27 KiB
作成
LocalTicketBackend によって作成されました。
Intake summary
ユーザー指示により、この Ticket を Orchestrator が routing できる ready 状態にする。readiness は引き続き requirements_sync_needed であり、実装直行ではなく WebSocket capability の設計同期・仕様化として扱う。URL 権限を前提にし、host_api.request とは分離する binding decisions は Ticket body に記録済み。
State changed
ユーザーから「readyにして」と明示されたため planning から ready へ遷移する。未決定点は残っているため、後続 Orchestrator routing では requirements/design sync として扱う。
State changed
Ticket を workspace-panel が queued にしました。
Decision
Routing decision: return_to_planning_requirements_sync
Reason:
- Ticket frontmatter has
readiness: requirements_sync_needed。 - Intake summary explicitly says this should be handled as WebSocket capability design/spec sync, not implementation-direct routing。
- Ticket body still lists concrete open questions that must be resolved before implementation or worktree side effects:
- WebSocket connection ownership: Yoi host-owned vs Plugin-instance-owned host API。
- Incoming message dispatch route: Ingress vs Service event/status stream vs other host routing。
- Reconnect / backoff / heartbeat / shutdown / cancellation / restore scope for first slice。
- Auth/headers/secrets model and grant/display split。
- Whether this work item closes as design/spec only or includes a minimal implementation slice。
- Related Ticket
00001KVMG8FTWis now routed for implementation of thehost_api.requestURL permission model. WebSocket design should reuse or intentionally diverge from that resulting URL permission schema after it stabilizes。
Decision:
- Do not create implementation worktree or spawn Coder/Reviewer for this Ticket now。
- Return to
planningwith concrete missing requirements/design decisions。 - Record relation to
00001KVMG8FTWso WebSocket design can be resumed after request permission model lands。
Required next planning output before queueing again:
- Decide whether this is design/spec only or includes a minimal implementation slice。
- Decide connection ownership and lifecycle boundary。
- Decide incoming-message durable/visible dispatch path and no-hidden-context behavior。
- Decide WebSocket URL permission/grant schema relative to
host_api.requestrequest-target schema。 - Decide auth/secrets display/grant model and non-goals for reconnect/backoff/heartbeat in first slice。
State changed
Ticket remains requirements_sync_needed and has unresolved design questions around connection ownership, incoming-message dispatch path, lifecycle bounds, auth/secrets handling, and design-vs-implementation scope. Returning to planning rather than starting implementation side effects.
Intake summary
Marked ready by yoi ticket state.
State changed
Marked ready by yoi ticket state.
State changed
Ticket を workspace-panel が queued にしました。
Decision
Routing decision: implementation_ready
Reason:
- Ticket was previously returned to planning because it needed concrete WebSocket connection ownership / lifecycle / permission model decisions。
- Current Ticket body is now retitled and refined as implementation work:
Plugin: URL 権限ベースの WebSocket host API を実装する。 - It defines explicit
host_api.websocketsemantics, URL permission/grant model, lifecycle/bounds/non-goals, acceptance criteria, tests, docs, and validation。 - Recorded dependency
00001KVMGAEJN depends_on 00001KVMG8FTWis now satisfied because00001KVMG8FTWisclosedandhost_api.requestURL permission model is integrated。 - Relations / orchestration plan have no remaining blockers。
- Active
00001KVMT2J25work is protocol/pod/TUI reconnect snapshot and is semantically separate from Plugin WebSocket host API; parallel implementation is acceptable。 - Orchestrator worktree is clean on
orchestrationatd8027781before routing side effects; target worktree / branch is not present。
Evidence checked:
- Ticket body / thread / artifacts via
TicketShowand directitem.mdread。 TicketRelationQuery(00001KVMGAEJN): onedepends_on 00001KVMG8FTW, target Ticket isclosed。TicketOrchestrationPlanQuery(00001KVMGAEJN): no records。TicketList(state=queued): this Ticket is the only queued Ticket。ListPods: only active child for other work isyoi-reviewer-00001KVMT2J25-r1。- Orchestrator git state / worktree list / branch list checked from
/home/hare/Projects/yoi/.worktree/orchestrationonly。 - Bounded code map:
crates/manifest/src/plugin.rsnow hashost_api.request,PluginRequestGrant, and manifest request target schema。crates/pod/src/feature/plugin.rshasPluginRequestClient,validate_plugin_request_request, request allowlist inspection, and explicit WebSocket rejection in request path。- No existing tungstenite/tokio-tungstenite/websocket dependency found in Cargo manifests。
- Docs currently state WebSocket/persistent transports require a separate Plugin capability。
IntentPacket:
Intent:
- Add a separate URL-permission-based Plugin WebSocket host API, not an extension of
host_api.request, suitable as a foundation for Discord/gateway-like integrations without implementing Discord itself。
Binding decisions / invariants:
- API name is
host_api.websocket; do not fold WebSocket intohost_api.request。 - URL permission model should mirror/reuse the
host_api.requesttarget/grant review semantics where sensible, while keeping websocket-specific lifecycle/bounds explicit。 - Authority requires both manifest-declared WebSocket target and enablement grant before opening a connection。
- WebSocket connection is host-owned and Plugin-driven: guest requests open/send/recv/close via host API, but host enforces handles, bounds, timeouts, and shutdown cleanup。
- No ambient network/socket access, no raw WASI sockets, no arbitrary URL by default。
- Secrets/auth headers are not solved by guest-memory arbitrary credential headers; keep credential-bearing header policy conservative and explicit。
- Incoming messages from WebSocket are delivered to the guest through explicit host API return values or bounded polling/receive operations, not hidden model context injection。
- No direct model Tool calls, Ticket mutation, Dashboard UI channel, or hidden history/context mutation。
host_api.requestmust keep rejecting WebSocket/SSE/persistent connection attempts。- First slice should avoid full background daemon scheduler unless it is minimal and bounded; preserve instance lifecycle cleanup。
Requirements / acceptance criteria:
- Manifest can declare WebSocket targets independently from request targets。
- Enablement config can grant WebSocket targets independently from request grants。
- Static inspection /
yoi plugin showreports WebSocket requested/granted/missing/broad diagnostics separately from request。 - Runtime refuses connect unless manifest target and grant both allow the URL。
- URL checks cover scheme (
ws/wss), host, port, path prefix, and any method/protocol constraints chosen for handshake。 - Local/private/loopback WebSocket targets require explicit declaration+grant。
- WebSocket API has bounded handle lifetime, max frame/message size, max open connections per Plugin instance, timeout/cancellation behavior, and cleanup on instance stop/trap/drop。
- Send/receive operations are bounded and typed; binary/text behavior is documented。
- Credential-like headers are rejected or explicitly not supported until SecretRef/grants exist。
- Tests cover allow/deny, grant-only/missing-grant, loopback allow/deny, broad diagnostics, request API still rejecting WebSocket, bounds/cleanup, and no hidden context mutation。
Implementation latitude:
- Rust dependency choice is Coder’s decision, e.g.
tokio-tungsteniteif suitable, but dependency/package/Nix implications must be handled。 - WIT/API shape can be handle-based with
open,send_text/send_binary,recv,close, or similar. Keep it minimal and reviewable。 - If a fully live network integration test is hard, use local test server / mock client abstraction to validate runtime policy and handle lifecycle。
- Reuse request target/grant matching helpers where appropriate, but avoid overgeneralizing if it obscures WebSocket semantics。
Escalate if:
- Implementing safe host-owned connection handles requires a broader Plugin service/instance actor redesign than current runtime supports。
- SecretRef/auth header requirements become necessary for useful functionality。
- Background receive delivery requires hidden context/history mutation or direct model Tool invocation。
- Cargo/Nix dependency updates become blocked by unavailable crates or source filtering。
- Public API compatibility for new WIT world/import names becomes unclear。
Validation plan:
cargo fmt --check- Focused manifest tests for websocket permission/target/grant parsing and independence from request。
- Focused pod plugin tests for connection allow/deny policy, loopback explicit grants, grant-only/missing-grant, bounds, handle cleanup, and request API rejection of WebSocket。
- Focused yoi plugin CLI tests for WebSocket diagnostics。
cargo checkfor affected crates。git diff --check- stale/boundary grep to ensure request docs still reject WebSocket and WebSocket docs do not claim hidden event injection。
yoi ticket doctornix build .#yoi --no-linkif dependencies/package/source-filter changed。
State changed
Human authorized queue routing from Workspace Dashboard. Previously recorded dependency on 00001KVMG8FTW is now satisfied because host_api.request is closed/integrated, and this Ticket now has implementation-ready WebSocket host API requirements. Orchestrator accepts implementation.
Implementation report
Implementation start report:
- Created child implementation worktree:
/home/hare/Projects/yoi/.worktree/00001KVMGAEJN-plugin-websocket-host-api
- Created branch:
impl/00001KVMGAEJN-plugin-websocket-host-api
- Base commit:
3eac7f8e ticket: accept plugin websocket host api
- Confirmed tracked Ticket project records are visible in the child worktree。
- Confirmed
.yoi/memoryhas no tracked/untracked entries in the child worktree check。 - Spawned sibling Coder Pod:
yoi-coder-00001KVMGAEJN
- Coder scope:
- read:
/home/hare/Projects/yoi - write:
/home/hare/Projects/yoi/.worktree/00001KVMGAEJN-plugin-websocket-host-api
- read:
Parallelization note:
00001KVMT2J25remains active in review and targets protocol/pod/TUI reconnect snapshot semantics. This Ticket should stay limited to Plugin WebSocket host API / manifest / runtime / CLI / docs / tests。
Next action:
- Wait for Coder implementation report, then spawn Reviewer read-only for the implementation diff。
Implementation report
Coder implementation report received from yoi-coder-00001KVMGAEJN.
Implementation commit reported:
4c1b8c3d feat: add plugin websocket host api
Changed areas reported:
- Manifest/config schema:
- Added independent
host_api.websocketcapability。 - Added manifest
[[websocket]]targets and enablementgrants.websocket。
- Added independent
- Runtime:
- Added host-owned WebSocket handle lifecycle for raw Wasm and component imports。
- Added
open,send_text/send-text,recv, andclose。 - Enforces manifest+grant URL allowlist before network I/O。
- Bounds open descriptor size, text/message size, max open connections, recv timeout, handle lifetime, and cleanup。
- Rejects embedded credentials, guest handshake headers, unsupported subprotocols, non-
ws/wssschemes, and binary message receive。
- Static inspection / CLI:
- Reports WebSocket requested/granted/missing/grant-only/broad diagnostics separately from request diagnostics。
yoi plugin shownow includesconfigured_websocket_grants。
- Docs/WIT:
- Added
yoi:host/websocket@1.0.0WIT interface。 - Documented text-only API, explicit bounded receive, no hidden context/history injection, and future SecretRef credential follow-up。
- Added
- Dependency/package:
- Added
tungstenitetocrates/pod。 - Updated
Cargo.lock。 - Updated
package.nixcargo hash tosha256-TZrw6nJclXVRpFIUlYvimGTDXlxBMaQt6oM5C5DIGIU=。
- Added
Coder validation reported:
cargo fmt --check: passedcargo test -p manifest websocket: passedcargo test -p pod websocket: passedcargo test -p yoi render_show_distinguishes_request_grant_statuses_and_broad_targets: passedcargo test -p manifest request_host_api_manifest_and_grant_parse_with_request_names: passedcargo check -p manifest -p pod -p yoi: passedgit diff --check: passed- stale/boundary grep for request/WebSocket docs and hidden context claims: passed
cargo run -p yoi -- ticket doctor: passed (doctor: ok)
Nix status reported:
- First
nix build .#yoi --no-linkexposed expected cargo hash mismatch; Coder updatedpackage.nix。 - After hash update, build advanced into dependency compilation then failed in sandbox compiling
aws-lc-syswithNo space left on device。 - Coder treated this as environmental disk-space failure, not Rust/package hash failure。
Orchestrator evidence checked:
- Worktree clean at
4c1b8c3d。 - Diff
3eac7f8e..HEADincludes expected code/docs/package areas but also includes child worktree edits to.yoi/tickets/00001KVMGAEJN/{item.md,thread.md}。 - Because Orchestrator owns durable Ticket records in this workflow, Coder was asked to remove those
.yoi/ticketsedits from the implementation branch before Reviewer routing。
Next action:
- Wait for Coder hygiene fix, then route code-only implementation diff to Reviewer。
Implementation report
Coder hygiene fix received from yoi-coder-00001KVMGAEJN.
New commit:
ce62d235 chore: keep plugin websocket branch code-only
Result:
- Implementation branch now contains code/docs/package changes only; child-worktree Ticket record edits were removed from the branch diff。
git diff --name-status 3eac7f8e..HEAD -- .yoi/tickets/00001KVMGAEJN: no output。- Worktree clean at
ce62d235。 - Diff
3eac7f8e..HEADnow touches 11 expected files: Cargo/package, manifest/pod/yoi plugin CLI, docs, and WIT resources。 git diff --check 3eac7f8e..HEAD: passed。
Next action:
- Route to read-only Reviewer Pod for acceptance review against the Ticket。
Comment
Review routing:
Spawned read-only Reviewer Pod:
yoi-reviewer-00001KVMGAEJN-r1
Review target:
- Worktree:
/home/hare/Projects/yoi/.worktree/00001KVMGAEJN-plugin-websocket-host-api - Branch:
impl/00001KVMGAEJN-plugin-websocket-host-api - Baseline:
3eac7f8e - Implementation commits:
4c1b8c3d feat: add plugin websocket host apice62d235 chore: keep plugin websocket branch code-only
- Diff:
3eac7f8e..ce62d235
Reviewer scope:
- read-only
/home/hare/Projects/yoi
Reviewer focus:
host_api.websocketremains distinct fromhost_api.request。- Manifest target + enablement grant are both required before opening a connection。
- Host-owned WebSocket handle lifecycle is bounded and cleaned up。
- Credential/header, binary/text, local/private/loopback, broad diagnostics, and no-hidden-context semantics are safe。
- Request API still rejects WebSocket/SSE/persistent attempts。
- Cargo/package/Nix changes are sound; Coder reported Nix advanced past hash check then failed due
No space left on deviceinaws-lc-sysbuild。
Orchestrator will wait for reviewer verdict before integration。
Review: request changes
Verdict: request_changes
確認範囲:
- Diff
3eac7f8e..ce62d235in worktree/home/hare/Projects/yoi/.worktree/00001KVMGAEJN-plugin-websocket-host-api:Cargo.lockcrates/manifest/src/plugin.rscrates/pod/Cargo.tomlcrates/pod/src/feature/plugin.rscrates/pod/src/pod.rscrates/yoi/src/plugin_cli.rsdocs/development/plugin-development.mdpackage.nixresources/plugin/wit/deps/yoi-host/yoi-host-v1.witresources/plugin/wit/yoi-plugin-instance-v1.witresources/plugin/wit/yoi-plugin-tool-v1.wit
- Ticket authority:
.yoi/tickets/00001KVMGAEJN/item.md.yoi/tickets/00001KVMGAEJN/thread.md
Blocking issue:
- WebSocket open が実際には bounded / cancellable ではなく、open-connection limit も network I/O 後にしか適用されていない。
Evidence:
crates/pod/src/feature/plugin.rs:922-931execute_plugin_websocket_openは URL/grant validation 後にclient.open(...)を呼び、その後で handle table に insert している。
crates/pod/src/feature/plugin.rs:2536-2558TungstenitePluginWebSocketClient::openは最初にtungstenite::client::connect_with_config(...)を呼ぶ。set_system_websocket_timeouts(...)は connection established 後にしか呼ばれない。
crates/pod/src/feature/plugin.rs:2687-2698PLUGIN_WEBSOCKET_MAX_OPEN_CONNECTIONSはPluginWebSocketHandleTable::insert内で check されるため、client.open(...)完了後、つまり network I/O 後にしか適用されない。
Implications:
- DNS/TCP/TLS/WebSocket handshake が Yoi の WebSocket timeout 適用前に OS/library behavior に従って block し得る。
PLUGIN_WASM_TIMEOUTwrapper は plugin Tool execution に timeout を返すが、spawned blocking task の open 自体は cancel しないため background で継続し得る。- handle table が満杯でも、新しい outbound WebSocket connection を開いた後で reject するため、max open connections の resource-bound intent を破り、意図しない network side effect が発生し得る。
Why this blocks:
- Ticket は WebSocket API に bounded handle lifetime、max open connections、timeout/cancellation behavior、cleanup を要求している。
- capacity / timeout / cancellation の enforcement が network I/O 前に fail closed していないため、この acceptance criteria を満たしていない。
Required fix:
- Network I/O 前に capacity を reserve/check する。
- Open path を bounded/cancellable にする、または bound を保証できない場合は dialing 前に fail closed する。
- Timeout/cancellation/resource-bound semantics を tests で固定する。
Non-blocking concerns:
- WebSocket host API は component/tool WIT surface に追加され、raw helper path tests はあるが、「long-lived WebSocket connection を Tool call 内に隠さず Plugin instance/Service lifecycle として扱う」という Ticket invariant を pin する integration test/diagnostic は見当たらなかった。現行 Plugin instance が Tool/Service state を共有するなら許容可能だが、lifecycle boundary の regression test/diagnostic があるとより明確。
close_all/ drop cleanup と componentstopcleanup はあるが、active blocking host calls は blocking operation が戻るまで handle-table mutex を保持し得る。これは open timeout/cancellation blocker の副次的問題。
Reviewer validation:
cargo fmt --check: passedgit diff --check 3eac7f8e..HEAD: passedcargo test -p manifest websocket: passedcargo test -p pod websocket: passed, 6 focused pod WebSocket testscargo test -p yoi render_show_distinguishes_request_grant_statuses_and_broad_targets: passedcargo test -p manifest request_host_api_manifest_and_grant_parse_with_request_names: passedcargo check -p manifest -p pod -p yoi: passedcargo run -p yoi -- ticket doctor: passed (doctor: ok)nix build .#yoi --no-link: passed; reviewer did not reproduce earlier disk-space failure。- Boundary/stale grep over docs/resources/manifest/pod/yoi CLI for
host_api.request,host_api.websocket, WebSocket/SSE, hidden context/history, Dashboard/Ticket/Discord/reconnect terms found no hidden context injection, Ticket mutation, Dashboard channel, reconnect daemon, or Discord-specific overreach in inspected changes。
Worktree status:
- Clean on
impl/00001KVMGAEJN-plugin-websocket-host-api。
Implementation report
Coder r1-fix report received from yoi-coder-00001KVMGAEJN.
New fix commit:
a766048f fix: bound plugin websocket open
Branch commits now:
4c1b8c3d feat: add plugin websocket host apice62d235 chore: keep plugin websocket branch code-onlya766048f fix: bound plugin websocket open
Fix summary:
crates/pod/src/feature/plugin.rs- Added pre-dial WebSocket capacity reservation / permit flow。
- Max-open-connections now fails before invoking network client/open path。
- Reservation is released on open failure。
- Replaced sync
tungsteniteopen path with bounded asynctokio-tungsteniteopen undertokio::time::timeout。 - Added deterministic fail-closed abstraction for clients that cannot guarantee bounded/cancellable open。
- Added focused tests for max-open pre-dial rejection, open failure reservation cleanup, and unbounded open client fail-closed before dialing。
crates/pod/Cargo.toml/Cargo.lock- Added
tokio-tungsteniteandfutures-util。
- Added
package.nix- Updated
cargoHashtosha256-cZxkmM42kbDp1Rv9gn4sCD5WIQLc0wCbjj4GbKjuA9Q=。
- Updated
Coder validation reported:
cargo fmt --check: passedcargo test -p pod websocket: passedcargo test -p manifest websocket: passedcargo test -p yoi render_show_distinguishes_request_grant_statuses_and_broad_targets: passedcargo test -p manifest request_host_api_manifest_and_grant_parse_with_request_names: passedcargo check -p manifest -p pod -p yoi: passedgit diff --check 3eac7f8e..HEAD: passedcargo run -p yoi -- ticket doctor: passed (doctor: ok)nix build .#yoi --no-link: passed
Orchestrator evidence checked:
- Worktree clean on
impl/00001KVMGAEJN-plugin-websocket-host-apiata766048f。 - r1-fix diff
ce62d235..HEADtouches onlyCargo.lock,crates/pod/Cargo.toml,crates/pod/src/feature/plugin.rs, andpackage.nix。 git diff --name-status 3eac7f8e..HEAD -- .yoi/tickets/00001KVMGAEJN: no output。git diff --check 3eac7f8e..HEAD: passed。
Next action:
- Route back to Reviewer for r2 verification of WebSocket open capacity/timeout/cancellation bounds and final acceptance。
Review: approve
Verdict: approve
r1-fix diff inspected:
- Reviewed
ce62d235..a766048f:Cargo.lockcrates/pod/Cargo.tomlcrates/pod/src/feature/plugin.rspackage.nix
- Also re-checked overall implementation diff
3eac7f8e..a766048fagainst Ticket authority and previous r1 blocker。
Previous blocker verification:
- Resolved。
Evidence in crates/pod/src/feature/plugin.rs:
execute_plugin_websocket_opennow:- validates URL / manifest / grant before dialing。
- fails closed if selected client cannot guarantee bounded open。
- reserves WebSocket capacity before
client.open(...)。 - commits reservation only after successful open。
PluginWebSocketHandles/PluginWebSocketHandleTablenow track reserved slots separately from active handles, soPLUGIN_WEBSOCKET_MAX_OPEN_CONNECTIONSrejects before network I/O。- Reservation cleanup is RAII-style:
- failed open drops the reservation。
- failed commit also drops/releases reservation。
TungstenitePluginWebSocketClientnow usestokio-tungsteniteasync connect undertokio::time::timeout(...), replacing previous synchronoustungstenite::client::connect_with_config(...)path。- Focused tests cover:
- max-open rejecting before network open。
- open failure releasing reservation。
- unbounded clients failing closed before dialing。
Overall Ticket fit:
host_api.websocketremains separate fromhost_api.request。- Manifest
[[websocket]]targets andgrants.websocketremain independent from request targets/grants。 - Runtime connect requires both manifest target and grant before network I/O。
- URL matching covers scheme, host, port, and path prefix。
- WebSocket API is text-only, explicit receive only, and does not inject incoming messages into hidden context/history。
- Guest arbitrary handshake headers / embedded credentials remain rejected。
- Request API continues to reject WebSocket/SSE/persistent request attempts。
- Static inspection /
yoi plugin showdiagnostics distinguish request and WebSocket grant/target states。 - No Ticket mutation, Dashboard channel, scheduler/reconnect daemon, direct model Tool call, or Discord-specific overreach found in reviewed branch。
Blocking issues:
- None。
Non-blocking concerns:
- None requiring changes。
Reviewer validation:
cargo fmt --check: passedgit diff --check 3eac7f8e..HEAD: passedcargo test -p pod websocket: passedcargo test -p manifest websocket: passedcargo test -p yoi render_show_distinguishes_request_grant_statuses_and_broad_targets: passedcargo test -p manifest request_host_api_manifest_and_grant_parse_with_request_names: passedcargo check -p manifest -p pod -p yoi: passedcargo run -p yoi -- ticket doctor: passednix build .#yoi --no-link: passed; disk-space failure did not reproduce。
Worktree status:
- Clean on
impl/00001KVMGAEJN-plugin-websocket-host-apiata766048f。 - Branch diff no longer includes
.yoi/tickets/00001KVMGAEJN/*。