// invoke.proto — the bn_invoke envelope, hook catalog, and the job/lifecycle // hook payloads (WO-WZ-001). // // Every host→guest call crosses the wasm boundary as one guest export: // // bn_invoke(hook_id, ptr, len) → packed(ptr, len) // // where (ptr, len) frames a serialized InvokeRequest and the packed return // frames a serialized InvokeResponse. Hook-specific payloads (render.proto, // http.proto, and the messages below) travel inside InvokeRequest.payload / // InvokeResponse.payload. See core/docs/wasm-abi.md. syntax = "proto3"; package abi.v1; import "v1/manifest.proto"; option go_package = "git.dev.alexdunmow.com/block/pluginsdk/abi/v1;abiv1"; // Hook identifies the guest entry point being invoked. enum Hook { HOOK_UNSPECIFIED = 0; // Render one block: payload = RenderBlockRequest / RenderBlockResponse. HOOK_RENDER_BLOCK = 1; // Render one template: payload = RenderTemplateRequest / RenderTemplateResponse. HOOK_RENDER_TEMPLATE = 2; // Forward a buffered HTTP request to the guest's mux: // payload = HttpRequest / HttpResponse. HOOK_HANDLE_HTTP = 3; // Run a background job handler: payload = JobRequest / JobResponse. HOOK_JOB = 4; // Plugin load lifecycle: payload = LoadRequest / LoadResponse. HOOK_LOAD = 5; // Plugin unload lifecycle: payload = UnloadRequest / UnloadResponse. HOOK_UNLOAD = 6; // Re-fetch content for RAG re-indexing: // payload = RagFetchRequest / RagFetchResponse. HOOK_RAG_FETCH = 7; // Media lifecycle event delivery: payload = MediaHookRequest / MediaHookResponse. HOOK_MEDIA_HOOK = 8; // Capture the static manifest at publish time: // payload = DescribeRequest / DescribeResponse. HOOK_DESCRIBE = 9; // Invoke a plugin-declared template tag (manifest.declared_tags) that the // host engine hit while rendering a powered block: // payload = RenderTagRequest / RenderTagResponse. HOOK_RENDER_TAG = 10; // Apply a plugin-declared template filter (manifest.declared_filters): // payload = ApplyFilterRequest / ApplyFilterResponse. HOOK_APPLY_FILTER = 11; // Execute a guest-registered AI tool handler (ai.tools.register): // payload = AiToolCallRequest / AiToolCallResponse. HOOK_AI_TOOL_CALL = 12; // Invoke a method on a bridge service this plugin registered // (bridge.register_service): payload = BridgeCallRequest / BridgeCallResponse. HOOK_BRIDGE_CALL = 13; // Render one directory panel section (DirectoryExtensions.PanelSections): // payload = DirectoryPanelSectionRequest / DirectoryPanelSectionResponse. HOOK_DIRECTORY_PANEL_SECTION = 14; // Run one directory pin decorator (DirectoryExtensions.PinDecorators): // payload = DirectoryPinDecoratorRequest / DirectoryPinDecoratorResponse. HOOK_DIRECTORY_PIN_DECORATOR = 15; } // InvokeRequest is the host→guest call envelope. message InvokeRequest { Hook hook = 1; // Serialized hook-specific request message (see Hook value comments). bytes payload = 2; // Milliseconds the guest has to answer; the host also enforces this // deadline on the wasm instance (WithCloseOnContextDone). int64 deadline_ms = 3; } // InvokeResponse is the guest→host return envelope. message InvokeResponse { // Serialized hook-specific response message; empty when error is set. bytes payload = 1; // Set when the hook failed; the host treats decode failures and traps as // implicit ABI_ERROR_CODE_INTERNAL. AbiError error = 2; } // AbiErrorCode classifies boundary-crossing failures. enum AbiErrorCode { ABI_ERROR_CODE_UNSPECIFIED = 0; // The handler ran and failed; message carries the Go error text. ABI_ERROR_CODE_INTERNAL = 1; // The payload could not be decoded. ABI_ERROR_CODE_DECODE = 2; // The hook/capability is not implemented by the callee. ABI_ERROR_CODE_UNIMPLEMENTED = 3; // The call exceeded its deadline; the instance is considered poisoned. ABI_ERROR_CODE_DEADLINE_EXCEEDED = 4; // The caller is not entitled to this capability. ABI_ERROR_CODE_PERMISSION_DENIED = 5; // A db.* call named a transaction handle the host has already expired // (dropped at the call-chain deadline, WO-WZ-007). Distinct from a real // fault: the unit of work is retryable in a fresh transaction. Emitted by // the cms dbexec side (adopted separately) and mapped guest-side to // bnwasm.ErrTxExpired. ABI_ERROR_CODE_TX_EXPIRED = 6; // An http.request egress call was refused by policy (host not granted, // grant revoked, platform-denylisted, or an off-allowlist redirect). // message names the offending host. Mapped guest-side to // egress.ErrDenied — deliberately distinct from a network failure // (ADR 0023, cms repo). ABI_ERROR_CODE_EGRESS_DENIED = 7; } // AbiError is the structured error carried by InvokeResponse and // HostCallResponse. message AbiError { AbiErrorCode code = 1; string message = 2; } // --- DESCRIBE --- // DescribeRequest asks the guest for its static manifest (publish time only). message DescribeRequest { // ABI major version of the calling host/publish tool. uint32 host_abi_version = 1; } // DescribeResponse returns the static manifest. message DescribeResponse { PluginManifest manifest = 1; } // --- LOAD / UNLOAD --- // LoadRequest carries the static host configuration the .so world exposed as // CoreServices.AppURL / CoreServices.MediaPath. message LoadRequest { HostConfig host_config = 1; } // HostConfig mirrors the plain-value CoreServices fields. message HostConfig { string app_url = 1; // CoreServices.AppURL string media_path = 2; // CoreServices.MediaPath } message LoadResponse {} message UnloadRequest {} message UnloadResponse {} // --- JOB --- // JobRequest dispatches one background job to the guest handler registered // for job_type (manifest.job_types). message JobRequest { string job_type = 1; // JSON job configuration (json.RawMessage in JobHandlerFunc). bytes config_json = 2; } // JobResponse returns the handler's JSON result. message JobResponse { bytes result_json = 1; } // --- RAG_FETCH --- // RagFetchRequest asks the guest's registered content fetcher // (manifest.rag_content_fetcher_types) for a content item's text. message RagFetchRequest { string content_type = 1; string content_id = 2; // UUID } // RagFetchResponse mirrors plugin.ContentFetcher's return values. message RagFetchResponse { string title = 1; string text = 2; } // --- MEDIA_HOOK --- // MediaHookRequest delivers one media lifecycle event // (plugin.MediaHooksProvider). message MediaHookRequest { oneof event { MediaAnalyzedEvent media_analyzed = 1; // OnMediaAnalyzed ModerationDecisionEvent moderation_decision = 2; // OnModerationDecision } } message MediaHookResponse {} // MediaAnalyzedEvent mirrors plugin.MediaAnalyzedEvent (UUIDs as strings). message MediaAnalyzedEvent { string media_id = 1; string analysis_id = 2; string content_hash = 3; string status = 4; string source_plugin = 5; string source_type = 6; string source_ref_id = 7; string safe_adult = 8; string safe_violence = 9; string safe_racy = 10; } // --- AI_TOOL_CALL --- // AiToolCallRequest executes the guest-side Handler of a tool the plugin // registered via ai.tools.register (ai.ToolHandler). message AiToolCallRequest { string slug = 1; // JSON encoding of the params map. bytes params_json = 2; } // AiToolCallResponse mirrors ai.ToolResult. A handler-level failure travels // in error_message (a normal tool outcome the model sees); AbiError stays // reserved for transport/decode/panic failures. message AiToolCallResponse { string content = 1; string error_message = 2; } // --- BRIDGE_CALL --- // BridgeCallRequest asks THIS plugin (the provider) to run a method on one of // its registered bridge services. The consumer side is the bridge.invoke // capability; payload encoding is agreed between the two plugins (JSON by // convention). message BridgeCallRequest { string service_name = 1; string method = 2; bytes payload = 3; } message BridgeCallResponse { bytes payload = 1; } // --- DIRECTORY_PANEL_SECTION / DIRECTORY_PIN_DECORATOR --- // DirectoryPanelSectionRequest renders the index-th registered panel section // (DirectoryExtensions.PanelSections[index], counted in // manifest.directory_extensions.panel_section_count). message DirectoryPanelSectionRequest { uint32 index = 1; // JSON encoding of the row data map. bytes data_json = 2; } message DirectoryPanelSectionResponse { string html = 1; } // DirectoryPinDecoratorRequest runs the index-th registered pin decorator, // which mutates the pin map in place; the mutated pin is returned. message DirectoryPinDecoratorRequest { uint32 index = 1; // JSON encoding of the pin map (mutated by the decorator). bytes pin_json = 2; // JSON encoding of the row data map (read-only input). bytes data_json = 3; } message DirectoryPinDecoratorResponse { // JSON encoding of the mutated pin map. bytes pin_json = 1; } // ModerationDecisionEvent mirrors plugin.ModerationDecisionEvent. message ModerationDecisionEvent { string media_id = 1; string analysis_id = 2; string status = 3; string previous_status = 4; string source_plugin = 5; string source_type = 6; string source_ref_id = 7; string moderated_by = 8; string note = 9; }