feat: provide v2 Plugin Component contracts and offline validation

Validate exact Component interfaces and static Tool/DCDL schemas without guest execution. Share canonical archive packing and independent package/artifact verification; build real pure and HTTPS PDK fixtures and retire v1/Service authoring stubs.
This commit is contained in:
2026-10-28 13:31:47 +09:00
parent 06883c51b7
commit 2d195f4058
40 changed files with 3056 additions and 1662 deletions
@@ -1,25 +0,0 @@
package yoi:host@1.0.0;
/// Grant-bound one-shot HTTP request host API. Importing this interface does not grant
/// authority; package grants are checked before registration/execution and on
/// every host call.
interface request {
request: func(request-json: string) -> string;
}
/// Grant-bound host-owned WebSocket API. Authority requires a manifest `host_api.websocket`
/// target and an enablement grant; messages are delivered only by explicit bounded recv calls.
/// v1 supports text messages only and rejects guest-supplied handshake headers.
interface websocket {
open: func(request-json: string) -> string;
send-text: func(handle: u32, text: string) -> string;
recv: func(handle: u32, timeout-ms: u32) -> string;
close: func(handle: u32) -> string;
}
/// Grant-bound filesystem host API. No ambient WASI filesystem is exposed.
interface fs {
read: func(request-json: string) -> string;
%list: func(request-json: string) -> string;
write: func(request-json: string) -> string;
}
@@ -0,0 +1,61 @@
package yoi:host@2.0.0;
interface errors {
enum code {
invalid-input, denied, not-found, conflict, unsupported,
quota-exceeded, deadline-exceeded, cancelled, unavailable,
indeterminate, receipt-expired,
}
enum effect-observation { not-dispatched, response-observed, indeterminate }
record host-error {
code: code,
message: string,
diagnostic-id: string,
effect: effect-observation,
}
}
interface connections {
use errors.{host-error};
/// Call-local resource. No serialization, persistence or cross-Worker transfer.
resource connection;
open: func(slot: string) -> result<connection, host-error>;
}
interface http {
use connections.{connection};
use errors.{host-error};
enum method { get, head, post, put, patch, delete, options }
record pair { name: string, value: string }
record request {
method: method,
path: string,
query: list<pair>,
headers: list<pair>,
body: list<u8>,
timeout-ms: u32,
effect-key: string,
}
record response {
status: u16,
headers: list<pair>,
body: list<u8>,
}
/// Host authorizes each operation and does not automatically retry or follow redirects.
send: func(target: borrow<connection>, input: request) -> result<response, host-error>;
}
interface logging {
use errors.{host-error};
enum level { trace, debug, info, warn, error }
variant scalar { text(string), boolean(bool), signed(s64), unsigned(u64), number(f64) }
record field { name: string, value: scalar }
record entry {
level: level,
event-code: string,
message: string,
fields: list<field>,
}
/// Bounded diagnostic data, not model context. Never include secrets or raw payloads.
log: func(input: entry) -> result<_, host-error>;
}
@@ -1,59 +0,0 @@
package yoi:plugin@1.0.0;
world instance {
import yoi:host/request@1.0.0;
import yoi:host/websocket@1.0.0;
import yoi:host/fs@1.0.0;
/// Start one host-managed Plugin instance. `config-json` is the opaque
/// enablement config copied from the Profile/plugin grant record. The return
/// string is PluginStatus JSON: `{ "state": "ready|running|stopped|...",
/// "data": <json> }`.
export start: func(config-json: string) -> string;
/// Execute a manifest-declared Tool on the shared instance. `input-json` is
/// ordinary Tool input JSON and the return string is ToolOutput JSON.
export handle-tool: func(name: string, input-json: string) -> string;
/// Handle one host-dispatched Service/Ingress event. `event-json` is an
/// ingress event envelope with at least:
///
/// ```json
/// {
/// "kind": "websocket_text|websocket_close|websocket_error|...",
/// "source": "websocket:wss://host/path|...",
/// "ingress_name": "manifest_ingress_name",
/// "payload": { "text": "..." },
/// "created_at": "RFC3339 timestamp",
/// "attempt": 1,
/// "correlation_id": "host event id"
/// }
/// ```
///
/// The return string is ServiceOutput JSON. To request host-mediated side
/// effects, return top-level `output_commands`, for example:
///
/// ```json
/// {
/// "accepted": true,
/// "output_commands": [{
/// "correlation_id": "command correlation id",
/// "source_event_id": "matching ingress correlation_id",
/// "command_id": "guest command id",
/// "kind": "websocket_send",
/// "payload": { "url": "wss://host/path", "text": "reply" },
/// "requested_at": "RFC3339 timestamp"
/// }]
/// }
/// ```
///
/// Output commands are parsed, bounded, grant-checked, and executed by the
/// host. They are not ordinary ToolOutput and do not inject hidden context.
export handle-ingress: func(name: string, event-json: string) -> string;
/// Return PluginStatus JSON for the shared host-managed instance.
export status: func() -> string;
/// Stop the shared instance and return final PluginStatus JSON.
export stop: func() -> string;
}
@@ -1,12 +0,0 @@
package yoi:plugin@1.0.0;
world tool {
import yoi:host/request@1.0.0;
import yoi:host/websocket@1.0.0;
import yoi:host/fs@1.0.0;
/// Execute a manifest-declared Tool. `input-json` is ordinary Tool input JSON
/// and the returned string is ToolOutput JSON accepted by the current
/// Component Model Plugin runtime.
export call: func(tool-name: string, input-json: string) -> string;
}
+49
View File
@@ -0,0 +1,49 @@
package yoi:plugin@2.0.0;
/// Only plugin-specific payloads use schema-validated JSON. Context is not authority.
interface tool {
record invocation {
invocation-id: string,
tool-name: string,
input-json: string,
config-json: string,
remaining-ms: u32,
}
record output {
summary: string,
content: option<string>,
structured-json: option<string>,
}
record tool-error {
code: string,
message: string,
}
invoke: func(input: invocation) -> result<output, tool-error>;
}
/// Pure computation: no ambient WASI or unused Host capabilities.
world tool-plugin {
export tool;
}
world https-tool-plugin {
import yoi:host/errors@2.0.0;
import yoi:host/connections@2.0.0;
import yoi:host/http@2.0.0;
export tool;
}
/// Logging is an explicit capability, never an ambient console.
world logged-tool-plugin {
import yoi:host/errors@2.0.0;
import yoi:host/logging@2.0.0;
export tool;
}
world https-logged-tool-plugin {
import yoi:host/errors@2.0.0;
import yoi:host/connections@2.0.0;
import yoi:host/http@2.0.0;
import yoi:host/logging@2.0.0;
export tool;
}