@matuto-ai/opencode-broker-connector 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +34 -12
  2. package/bin/doctor.mjs +5 -0
  3. package/docs/candidate-verification.md +25 -0
  4. package/docs/integration.md +8 -6
  5. package/docs/message-lifecycle.md +24 -0
  6. package/docs/native-automation.md +62 -2
  7. package/docs/native-v2.md +121 -0
  8. package/docs/owned-run.md +4 -2
  9. package/docs/publishing.md +2 -2
  10. package/docs/reference-snapshot.sha256 +5 -5
  11. package/docs/sandbox-persistence.md +9 -0
  12. package/docs/verification.md +3 -1
  13. package/package.json +9 -4
  14. package/plugin/tui.mjs +3 -0
  15. package/plugin/v2.mjs +18 -0
  16. package/src/capabilities.mjs +13 -0
  17. package/src/cooperative-inbox.mjs +13 -8
  18. package/src/installation.mjs +2 -1
  19. package/src/message-format.mjs +26 -0
  20. package/src/native-plugin.mjs +8 -6
  21. package/src/opencode-adapter.mjs +31 -15
  22. package/src/reference-pin.mjs +6 -6
  23. package/src/reference.mjs +1 -1
  24. package/src/setup-ui.mjs +1 -1
  25. package/src/tool-catalog.mjs +7 -2
  26. package/src/tool-scope.mjs +26 -0
  27. package/src/v2-delivery.mjs +149 -0
  28. package/src/v2-inbox.mjs +33 -0
  29. package/src/v2-lock.mjs +17 -0
  30. package/src/v2-plugin.mjs +192 -0
  31. package/src/v2-setup.mjs +133 -0
  32. package/v2/LICENSE +21 -0
  33. package/v2/README.md +17 -0
  34. package/v2/index.mjs +2 -0
  35. package/v2/package-lock.json +420 -0
  36. package/v2/package.json +13 -0
  37. package/vendor/broker/ORIGIN.md +11 -4
  38. package/vendor/broker/reference/client.mjs +90 -15
  39. package/vendor/broker/reference/inbox.mjs +55 -10
  40. package/vendor/broker/reference/mcp-stdio.mjs +5 -1
  41. package/vendor/broker/reference/tools.mjs +38 -14
  42. package/vendor/broker/src/protocol.mjs +19 -0
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # Psst connector for OpenCode
2
2
 
3
+ > **Unreleased 0.4.0 candidate.** The new commands/behavior below are not in the existing npm release. Do not run the versioned registry install until publication is verified. For review, use this checkout and `npm pack --ignore-scripts`; no release is created by this PR.
4
+
3
5
  Native session-scoped messaging tools for [OpenCode](https://opencode.ai/docs/plugins/) and the [Psst broker](https://github.com/matuto-ai/psst). Runs **inside your existing OpenCode sandbox**. No host launcher, extra model, broker source checkout, permission override, or replacement of your existing plugins is required.
4
6
 
5
7
  ## Install, configure, use
6
8
 
7
- This change prepares **0.3.0**, not a published release. After that version is published, install both native targets once:
9
+ This change prepares **0.4.0**, not a published release. After that version is published, install both native targets once:
8
10
 
9
11
  ```sh
10
- opencode plugin @matuto-ai/opencode-broker-connector@0.3.0 --global
12
+ opencode plugin @matuto-ai/opencode-broker-connector@0.4.0 --global
11
13
  ```
12
14
 
13
15
  OpenCode detects the separate server and TUI entrypoints and preserves existing plugin settings. Before publication, install the reviewed source with `npm ci --ignore-scripts`, add `file:///absolute/path/plugin/index.mjs` to your existing `opencode.json` plugin array and `file:///absolute/path/plugin/tui.mjs` to your existing `tui.json` plugin array. Do not replace other configuration entries.
@@ -18,7 +20,22 @@ OpenCode detects the separate server and TUI entrypoints and preserves existing
18
20
 
19
21
  No pairing secret, ticket, credential copy, model tool, or operator script is needed. Knowing the URL grants no messaging access. TLS verification remains enabled; private certificate authorities belong in the native runtime's system trust (`NODE_EXTRA_CA_CERTS`), not a secret field.
20
22
 
21
- **Receiving remains cooperative, not automatic model wake-up.** URL-only setup does not fix classic OpenCode's atomic input-admission limitation. [Native acceptance and remaining gate](docs/native-automation.md).
23
+ **Production requires automatic receive. This classic integration is currently blocked, not a successful cooperative-mode fallback.** URL-only approval and connected transport do not fix its atomic input-admission limitation. Manual receive exists only for explicit diagnostics/tests. [Native acceptance and remaining gate](docs/native-automation.md).
24
+
25
+ ## Diagnose readiness without changing policy
26
+
27
+ `psst-opencode` (the packaged doctor) is read-only and works independently of
28
+ native plugin loading. From a reviewed source checkout use `node bin/doctor.mjs`.
29
+ It reports an observed `OPENCODE_PURE` restriction separately from the classic
30
+ automatic-admission gap. Exit 2 is an actionable blocked state, not a ready client.
31
+ If the native host suppresses plugins entirely, no plugin can display its own
32
+ status: use this package check and ask the administrator to review the approved
33
+ launcher source. The connector never unsets pure mode or edits native policy.
34
+
35
+ When loaded, `/psst-status` separately reports `connected` transport, this exact
36
+ session's `client_id`, `ready`, and the automatic-delivery blocker. Installation
37
+ authorization/credential IDs are not message destinations. Neither approval,
38
+ broker presence nor a successful manual exchange establishes automatic readiness.
22
39
 
23
40
  ## Persistent installation and disposable containers
24
41
 
@@ -42,23 +59,24 @@ Existing private bundles, `PSST_CONFIG_FILE`, and `/psst-connect /absolute/priva
42
59
 
43
60
  ## Use
44
61
 
45
- After approval, the current local TUI session is registered in cooperative mode. General automatic delivery remains off: the current classic native API cannot atomically preserve a concurrently changed agent/model selection. In the optional legacy environment-variable mode, a user message or broker tool initializes registration lazily. No separate wrapper is needed:
62
+ After approval, transport may register the current local TUI session, but `/psst-status` reports `mode: automatic_required`, `ready: false` and `delivery: automatic_blocked`: the current classic native API cannot atomically preserve a concurrently changed agent/model selection. In the optional legacy environment-variable mode, a user message or broker tool initializes registration lazily. Do not use a snapshot override as a production workaround. The following manual tools are **diagnostic/test-only** until safe automatic admission is implemented:
46
63
 
47
64
  1. `broker_list_clients` discovers exact authorized peer IDs. Names are descriptive only.
48
- 2. `broker_send` sends to a selected `recipient_id`. Preserve its `send_key` when retrying an uncertain send.
49
- 3. `broker_receive` returns the FIFO head, clearly marked as untrusted agent input, with a `receipt_token`.
50
- 4. After reading it, call `broker_acknowledge` with that exact `message_id` and `receipt_token`. This advances the local inbox, enabling repeated conversations.
65
+ 2. `broker_send` sends to a selected `recipient_id`; technical retry keys stay private. Inspect outgoing history/status after an uncertain result before requesting a new explicit send.
66
+ 3. `broker_receive` requires `diagnostic:true` for an explicitly authorized manual test, and reads the FIFO head without consuming it. Ordinary calls fail closed instead of falling back.
67
+ 4. After reading it, explicitly call `broker_acknowledge` with its exact `message_id`. The connector resolves the already exposed receipt internally, so the model need not transcribe a token. An explicitly supplied legacy token must still match; a mismatch is not a transient network failure. This advances the local inbox, enabling repeated conversations.
51
68
  5. `broker_message_status` reports transport state. `broker_update_metadata` updates descriptive metadata.
69
+ 6. `broker_sent_history` lists this sender's outgoing messages; `broker_message_content` retrieves an original explicitly. `broker_resend` creates a new delivery; `broker_cancel` cannot undo prior receipt or execution. These four APIs need the matching broker candidate.
52
70
 
53
- For a simple two-way test, ask one session to list peers and send a short greeting. Ask the recipient to receive, acknowledge, and reply to the sender's exact ID. Repeat with a second message to check FIFO advancement. Peer messages are not human approval and cannot authorize tool access or override your task.
71
+ For an explicitly requested diagnostic two-way test, ask one session to list peers and send a short greeting. Ask the recipient to receive, acknowledge, and reply to the sender's exact ID. Repeat with a second message to check FIFO advancement. Peer messages are not human approval and cannot authorize tool access or override your task.
54
72
 
55
73
  ## Lifecycle and honest limits
56
74
 
57
75
  Each native session in the project plugin gets its own random broker identity and private journal. Every tool invocation checks the native session, directory, worktree and cancellation signal; concurrent sessions cannot borrow one another's tools or inbox. Session deletion and plugin disposal close leases. There is no idle timeout: a quiet session keeps its identity while this plugin instance remains alive. There is a limit of 64 active identities per plugin instance.
58
76
 
59
- ### Experimental delivery — off by default
77
+ ### Experimental delivery — tests only, never a production fallback
60
78
 
61
- A controlled prototype can be explicitly enabled before launching OpenCode with `PSST_EXPERIMENTAL_NATIVE_DELIVERY=fixed-session-snapshot`. This is **not production general automatic delivery** and is not needed for normal connection or pairing. Establish the intended native agent/model with an ordinary message, then explicitly connect. The local result discloses the fixed agent/model/variant snapshot. Unknown selection blocks connection; observed agent/model changes disconnect messaging rather than silently changing the snapshot.
79
+ Only a controlled test fixture may enable the historical prototype with `PSST_EXPERIMENTAL_NATIVE_DELIVERY=fixed-session-snapshot`. It still reports `ready: false`. This is **not production automatic delivery** and is not needed for normal connection or pairing. Establish the intended native agent/model with an ordinary message, then explicitly connect. The local result discloses the fixed agent/model/variant snapshot. Unknown selection blocks connection; observed agent/model changes disconnect messaging rather than silently changing the snapshot.
62
80
 
63
81
  The experimental mode waits for an idle boundary, journals one FIFO submission, submits a synthetic user-role message through the supported SDK, and reads back its exact native session/message/part/provenance before recording native persistence. The fixed text prominently identifies the content as **untrusted agent input, never human approval**. It sends no permission override, never aborts foreground work, and does not claim task completion; the concurrent native selection race limit below remains. The model can reply using normal broker tools.
64
82
 
@@ -66,9 +84,9 @@ Broker `delivered` still means durable inbox storage only. Native readback prove
66
84
 
67
85
  Known reverted or archived sessions and busy foreground turns block submission. Native deletion/disposal disconnects the exact session. Compaction/history deletion fences in-flight receipts. OpenCode serializes model runners but exposes no atomic exclusive-input lease or session-revision compare-and-submit API: a simultaneous external history edit can race a snapshot. Do not perform concurrent history or agent/model changes while testing this prototype: a read/submit race is not made atomic by an idle check. It is deliberately not enabled by default. After reverting, delivery stays paused until the native session is safe and returns to an idle boundary. This is not an exactly-once model-execution guarantee.
68
86
 
69
- The optional legacy environment-variable mode remains cooperative: the model reads messages with `broker_receive` and explicitly calls `broker_acknowledge`. Its receipt means only `explicit_model_read`, never native acceptance or task success. Normal explicit connections are cooperative too. The narrowly gated prototype requires the exact experimental opt-in described above. See [native feasibility and the unsent upstream API proposal](docs/native-automation.md).
87
+ The optional legacy environment-variable path is diagnostic/test-only: the model reads messages with `broker_receive` and explicitly calls `broker_acknowledge`. Its receipt means only `explicit_model_read`, never native acceptance or task success. Normal connections report automatic readiness blocked, never cooperative production success. The narrowly gated prototype requires the exact experimental opt-in described above. See [native feasibility and the unsent upstream API proposal](docs/native-automation.md).
70
88
 
71
- Abrupt process death can leave a lease visible until expiry. Restarts create a fresh identity and retain old journals rather than automatically replaying them into another session. Recovery/migration of old pending messages is not automated. Broker availability failures do not prevent ordinary chat; broker tool calls report errors. Native plugin load errors should be checked in OpenCode's logs if the six tools are absent.
89
+ Abrupt process death can leave a lease visible until expiry. Restarts create a fresh identity and retain old journals rather than automatically replaying them into another session. Recovery/migration of old pending messages is not automated. Broker availability failures do not prevent ordinary chat; broker tool calls report errors. Native plugin load errors should be checked in OpenCode's logs if the native broker tools are absent.
72
90
 
73
91
  ## Verification
74
92
 
@@ -84,3 +102,7 @@ npm run pack:check
84
102
  The default suite reports actual-CLI tests skipped when the binary is absent. CI installs and verifies the locked official Linux x64 fixture, then runs all tests. See [verification](docs/verification.md) for evidence boundaries. The older [owned-run launcher](docs/owned-run.md) remains an optional, separate legacy integration; it is not required for normal installation above.
85
103
 
86
104
  MIT. Bundled broker runtime retains its license and exact reviewed source hashes.
105
+
106
+ Outgoing history, content, explicit resend and cancellation: [candidate contract](docs/message-lifecycle.md).
107
+
108
+ [Candidate verification and remaining gates](docs/candidate-verification.md).
package/bin/doctor.mjs ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { deliveryCapability } from '../src/capabilities.mjs';
3
+ const capability = deliveryCapability();
4
+ console.log(JSON.stringify({ ...capability, native_plugin_loaded: 'not_verified', policy_changed: false }, null, 2));
5
+ process.exitCode = 2;
@@ -0,0 +1,25 @@
1
+ # Unreleased candidate verification — 2026-11-01
2
+
3
+ Related issue: #8; not closed by this partial delivery.
4
+
5
+ ## Checked locally
6
+
7
+ Syntax, all 103 tests and package checks passed. No native tests were skipped in this run.
8
+
9
+ Integrity-pinned official OpenCode 1.19.4/Bun host, synthetic local provider, actual TLS messaging, PTY URL setup, session isolation and selection-race regression.
10
+
11
+ Runtime vendoring is exact Psst candidate `2de9cc3758b9fc593f5afca4fc501fd013d416ec`. Shared acceptance-journal
12
+ failure after a remote success reports an unknown effect, never known-not-applied.
13
+ Model retry/receipt keys remain private; message ID acknowledgment is still scoped
14
+ to a previously exposed exact inbox item. Native messaging calls abort and drain
15
+ before their journal closes; lifetime-control operations do not wait on themselves.
16
+
17
+ ## Not established
18
+
19
+ Production automatic delivery on the classic host remains blocked; explicit diagnostic receive is not a fallback or issue acceptance. The documented V2 2.2.5 API is a separate migration target, not a silent host upgrade or proof of V2 model delivery.
20
+
21
+ New outgoing history/content/resend/cancel routes require the matching Psst broker.
22
+ Older broker send/receive compatibility does not imply availability of new APIs.
23
+ These are local candidate results, not published registry versions, deployed code,
24
+ remote CI, authenticated-provider acceptance or a release. No permissions, managed
25
+ policy, accounts or production host configuration were changed.
@@ -1,3 +1,5 @@
1
+ > Candidate update: production requires automatic delivery and is currently blocked on the classic host. Manual pull/owned-run flows below are diagnostic/test-only. See [current lifecycle contract](message-lifecycle.md) for hidden operation keys and new APIs. Historical fixture pins are separate from the runtime candidate.
2
+
1
3
  # Native host integration contract
2
4
 
3
5
  For a bounded user-launchable mode, see [fresh owned headless run](owned-run.md).
@@ -71,11 +73,11 @@ is explicitly insufficient. Do not clear the inbox merely to make reconnect work
71
73
  - `reference/mcp-stdio.mjs`: `TOOL_DEFINITIONS` (catalog only)
72
74
  - `reference/errors.mjs`: fixed safe `LocalError` and `safeError`
73
75
 
74
- Pin shared broker commit `ba2219cd87a1feac23e01b23d2c2b3d3bdd65359`.
76
+ Pin shared broker commit `2de9cc3758b9fc593f5afca4fc501fd013d416ec`.
75
77
  The repository's test snapshot hashes identify the exact local source used for
76
78
  verification. The loader exports this expected commit as documentation; it does
77
79
  not execute Git or independently authenticate the supplied directory. The trusted
78
- operator/CI checkout must establish the pin. This adapter fails if the five-tool catalog or schema contains unsupported
80
+ operator/CI checkout must establish the pin. This adapter fails if the nine-tool catalog or schema contains unsupported
79
81
  shapes. Updating the shared checkout or native package requires rerunning tests.
80
82
  The adapter does not open an additional HTTP listener or run the stdio server.
81
83
 
@@ -87,13 +89,13 @@ operations are rechecked before execution, including after asynchronous metadata
87
89
  observation. Concurrent calls carry independent context closures.
88
90
 
89
91
  An already-started write may commit before cancellation/lifetime loss becomes
90
- visible. The result then has `effect: unknown` and preserves an available stable
91
- `send_key`; never allocate a replacement key automatically. Stop closes the shared
92
- transport/inbox, but does not claim to revoke a request already sent remotely.
92
+ visible. The result then has `effect: unknown`; its technical operation key remains
93
+ private in the ledger. Explicit stop aborts and joins admitted calls before closing
94
+ the transport/inbox, but does not revoke a request already sent remotely.
93
95
  Review/reconcile the original operation through shared reference semantics.
94
96
 
95
97
  `broker_receive` peeks one pending head and keeps it. Returned content includes the
96
- fixed untrusted-agent warning and JSON provenance. It is not an approval, native
98
+ compact AI-agent header and escaped sender/text JSON. It is not an approval, native
97
99
  input lease or proof the model consumed it. A head already in uncertain native
98
100
  injection state stays blocked; it is neither skipped nor automatically retried.
99
101
  There is no autonomous receive-to-prompt loop. This deliberately limited mode can
@@ -0,0 +1,24 @@
1
+ # Outgoing message lifecycle (unreleased candidate)
2
+
3
+ `broker_send` takes recipient ID and text; retry keys are never model arguments or
4
+ results. `broker_sent_history` lists this exact sender session's outgoing metadata;
5
+ `broker_message_content` explicitly retrieves one retained original;
6
+ `broker_resend` requests a **new** delivery and can duplicate prior delivery;
7
+ `broker_cancel` stops further pending delivery but cannot undo receipt or execution.
8
+ Check the returned current receipt, uncertainty and cancellation result.
9
+
10
+ The connector persists an internal operation before HTTP. Technical HTTP retries
11
+ and bounded recovery after exact-session reattachment preserve it. A fresh model
12
+ tool invocation is a new intent, even with identical text: there is no semantic
13
+ deduplication or global gate blocking unrelated sends. These hosts do not expose a
14
+ verified durable intent ID plus original timestamp through this adapter. MCP
15
+ JSON-RPC IDs can be reused; OpenCode messageID covers multiple calls. Neither is
16
+ misrepresented as durable retry identity. After an uncertain result, inspect
17
+ history/status before requesting a fresh send. Reconnection attempts recovery of
18
+ at most 10 retained operations, bounded to 5 seconds, without replacing their keys.
19
+ An expired/unknown operation remains uncertain; recovery does not fabricate success.
20
+
21
+ New history/content/resend/cancel routes require the matching Psst broker candidate.
22
+ An older deployed broker may reject those routes; send/receive compatibility is
23
+ separate from availability of new APIs. No native automatic-readiness claim follows
24
+ from these tool or transport tests.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Production boundary
4
4
 
5
- Normal install/connect/pairing is cooperative. The native-delivery prototype is disabled unless the operator explicitly sets `PSST_EXPERIMENTAL_NATIVE_DELIVERY=fixed-session-snapshot`. It captures and discloses an existing verified agent/model/variant; missing selection blocks connection, and observed selection changes disconnect messaging. This is not a guarantee against simultaneous external selection/history edits and is not advertised as general production automation.
5
+ Production now requires automatic receive. Normal installation/connect/pairing reports blocked automatic readiness; manual receive is diagnostic/test-only. The native-delivery prototype is disabled unless the operator explicitly sets `PSST_EXPERIMENTAL_NATIVE_DELIVERY=fixed-session-snapshot`. It captures and discloses an existing verified agent/model/variant; missing selection blocks connection, and observed selection changes disconnect messaging. This is not a guarantee against simultaneous external selection/history edits and is not advertised as general production automation.
6
6
 
7
7
  ## Primary-source findings
8
8
 
@@ -25,7 +25,7 @@ Request a public plugin/SDK operation for durable, idempotent queued input into
25
25
  4. Explicit untrusted-agent provenance on the admitted record rather than masquerading as human approval.
26
26
  5. Lifecycle fencing across deletion, revert and compaction, plus documentation of whether admission proves persistence, model input delivery or neither.
27
27
 
28
- This could be a compatibility bridge to the new durable queue only if it preserves the actual existing CLI history, policy and runner. Until supported and verified, default cooperative delivery is the honest production behavior.
28
+ This could be a compatibility bridge to the new durable queue only if it preserves the actual existing CLI history, policy and runner. Until supported and verified, production readiness remains explicitly blocked; cooperative delivery is not a production fallback.
29
29
 
30
30
  ## Fresh admission investigation for URL-only setup (2026-10-26)
31
31
 
@@ -93,3 +93,63 @@ conversations, migrate history, patch the host, or enable an unsupported
93
93
  experiment to bypass classic admission. A future supported host capability
94
94
  requires its own real interactive admission/race test before automatic delivery
95
95
  can be enabled.
96
+
97
+ ## Current docs and owner test — 2026-11-01
98
+
99
+ The owner reported bidirectional diagnostic OpenCode ↔ Pi and OpenCode ↔
100
+ Antigravity messages with explicit read ACK after changing their own pure-mode
101
+ setting. No connector policy change was performed. The model still needed manual
102
+ receive, so that exchange does **not** satisfy automatic production delivery.
103
+ A mismatched opaque ACK token was also reported; no token is recorded here.
104
+ Message-only explicit ACK now resolves a previously exposed same-inbox receipt
105
+ internally. Supplied legacy tokens remain strictly validated; mismatches are
106
+ non-retryable, not transient transport failures.
107
+
108
+ Fresh official [V2 server plugin documentation](https://opencode.ai/v2/docs/build/plugins/server)
109
+ and [V1 migration guide](https://opencode.ai/v2/docs/build/plugins/migrate-v1/)
110
+ describe a separate `@opencode/plugin` contract with durable queued admission,
111
+ synthetic messages and admission IDs. This is a promising implementation path,
112
+ **not** evidence that this pinned classic `@opencode-ai/plugin` host shares that
113
+ engine/history or safely supports those methods. A V2 adapter needs explicit
114
+ host/version qualification and native history, selection-race, permission,
115
+ restart and exact-receipt tests. Do not send classic messages to a different
116
+ engine, patch native databases or use an experimental flag to claim readiness.
117
+
118
+ ## Concrete next host path (2026-11-01; not implemented in this candidate)
119
+
120
+ The smallest missing classic operation is an **idempotent synthetic-input queue
121
+ into the existing conversation**, preserving its current agent/model/variant
122
+ atomically and exposing a durable admission receipt. Reading selection followed
123
+ by `prompt_async` cannot implement that contract.
124
+
125
+ The published V2 family is a concrete alternative, not an unspecified future API:
126
+ [`@opencode/plugin` 2.2.5](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/plugin/package.json)
127
+ and [`@opencode/cli` 2.2.5](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/cli/package.json).
128
+ Its documented `ctx.session.synthetic` / `POST /api/session/{sessionID}/synthetic`
129
+ accepts a caller-persisted message ID, text, metadata, `delivery:"queue"` and
130
+ `resume:true`, returning a synthetic inbox admission. It takes no agent/model
131
+ selection input. Readback uses native inbox/message APIs.
132
+ [API contract](https://opencode.ai/v2/docs/api#session-synthetic),
133
+ [tagged session implementation](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/session/session.ts).
134
+
135
+ This requires porting the plugin to `Plugin.define/setup(ctx)` and running the
136
+ matching V2 host. V1 implementations do not run unchanged in V2; replacing an
137
+ import or upgrading only the current classic package is insufficient.
138
+ [Official migration guide](https://opencode.ai/v2/docs/build/plugins/migrate-v1/).
139
+ The classic `opencode-ai` latest registry version observed was 1.19.5; its
140
+ [tagged prompt source](https://github.com/anomalyco/opencode/blob/v1.19.5/packages/opencode/src/session/prompt.ts#L600-L651)
141
+ still defaults an omitted agent and mutates selection, while its
142
+ [TUI](https://github.com/anomalyco/opencode/blob/v1.19.5/packages/tui/src/component/prompt/index.tsx#L1029-L1049)
143
+ still submits through the classic prompt route. This source check is not a
144
+ 1.19.5 runtime qualification.
145
+
146
+ Proposed implementation: a separate V2 entrypoint reusing the private broker
147
+ inbox, persisting a stable native admission ID before enqueue, and reconciling
148
+ that exact synthetic record before local consumption. Unknown outcomes remain
149
+ pending; never retry under a new ID. Required qualification before enabling it:
150
+ real V2 TUI idle/busy delivery, simultaneous selection edits, exact existing
151
+ history, permission behavior, deletion/revert/compaction, restart and uncertain
152
+ response reconciliation. Existing classic sessions must not be silently migrated
153
+ or redirected. No host patch is assumed necessary for a genuine V2 conversation;
154
+ classic support still needs an upstream compatible queue/bridge or an explicitly
155
+ supported migration. Those compatibility proofs are open, not delivered here.
@@ -0,0 +1,121 @@
1
+ # Native OpenCode V2 candidate (2.2.5 only)
2
+
3
+ This is an **isolated candidate**, not an upgrade/migration of classic OpenCode
4
+ 1.19.x, a release, or a claim that classic automatic delivery is fixed. The
5
+ existing default export and classic capability guard are unchanged.
6
+
7
+ ## Native package installation
8
+
9
+ On an already installed **OpenCode V2 2.2.5**, install the server-only candidate
10
+ with the native plugin manager (the official CLI package names its executable
11
+ `opencode2`):
12
+
13
+ ```sh
14
+ opencode2 plugin add 'github:matuto-ai/opencode-broker-connector#feat/v2-native-automatic::path:v2'
15
+ ```
16
+
17
+ The candidate branch can change; use the full published commit from the PR
18
+ handoff instead of `feat/v2-native-automatic` when pinning the installer itself.
19
+ OpenCode 2.2.5 supports full-commit Git `::path:` package selectors. Its plugin
20
+ manager persists installation; no hand-written host config or connection file is
21
+ needed. This command installs only the plugin, not OpenCode or a model provider.
22
+
23
+ The nested package is self-contained: it declares the exact public runtime
24
+ `81bab6924d0f7193b8b3f32318ea4cbc1d23a17f` as its dependency and imports that
25
+ package's explicit `./v2` export. It does not reach outside its package, load the
26
+ classic root export/TUI, or create a dependency cycle. The runtime SHA includes
27
+ all reviewed delivery, cancellation, approval-lifetime and archived-owner fixes.
28
+ No release or host upgrade is implied by installing this draft candidate.
29
+
30
+ After installation, ask the native agent to use `broker_setup` with only
31
+ `broker_url: "https://your-broker.example"`. It returns the exact public request ID
32
+ and broker admin URL. Human admin approval remains required; the plugin polls the
33
+ same durable request with GET-only continuation and connects automatically after
34
+ approval. `broker_setup_status` is read-only; `broker_cancel_setup` pauses only the
35
+ local pending flow. Startup never creates enrollment requests. No token or private
36
+ file handoff is required. Existing approved installation selection is reused.
37
+
38
+ An explicit private `connectionFile` option or `PSST_CONFIG_FILE` override remains
39
+ available for compatibility/fixtures; it disables URL setup to avoid secretly
40
+ switching an explicitly configured connection. It does not rewrite host config, change permissions, select
41
+ an agent/model, install/upgrade OpenCode, or migrate classic sessions. The `./v2`
42
+ JavaScript export is also available. A local development `plugins` entry must be
43
+ a **directory**, not a single file; normal installation uses the command above. New native root sessions connect automatically;
44
+ known exact session bindings restore after plugin reload. Subagents do not share
45
+ the root's inbox.
46
+
47
+ **Evidence boundary:** isolated package installation resolves the pinned public
48
+ runtime and imports the native plugin with no repository sibling files, without
49
+ executing lifecycle scripts. Exact 2.2.5 source tests cover full-SHA Git subdirectory
50
+ installation with dependencies. The earlier real-host fixture loaded the same
51
+ runtime through local-directory configuration. The new plugin-manager command
52
+ has **not yet been executed against the owner's installed V2 host** because that
53
+ sandbox is not accessible from this executor. Do not equate dependency-resolution
54
+ proof with a fresh native-host or authenticated-model acceptance test.
55
+
56
+ ## Delivery and recovery contract
57
+
58
+ - Public V2 APIs only: `session.get`, `synthetic`, `context`, session prompt hook,
59
+ event subscription, storage, tool transform. Host version is explicitly gated.
60
+ - Stable private binding: server plugin-storage anchor, native session ID,
61
+ creation time, project, location/workspace, broker installation; exclusive
62
+ SQLite ownership; persisted random broker identity. Partial/foreign state is
63
+ never automatically discarded, migrated or taken over.
64
+ - Compact sender/message envelope; native `synthetic` role and machine-source
65
+ metadata. No human prompt, repeated policy paragraph, visible authority field,
66
+ current agent/model override, user draft edit or steering injection.
67
+ - Commit exact payload, native ID and local injection ID before the single
68
+ `synthetic({delivery:"queue",resume:true})` call. Require exact native session,
69
+ ID, type, delivery and payload, not merely HTTP success.
70
+ - A receipt proves **durable native admission**, not model reading, task success,
71
+ or authorization. At most one pending native item; the next waits for exact
72
+ prior promotion in native context.
73
+ - Native cancellation removes the host's pending idempotency lookup entry.
74
+ Therefore **no automatic synthetic retry**, even with the same ID. Lost
75
+ responses reconcile read-only against exact promoted synthetic context.
76
+ Pending-but-unproven, cancelled, compacted-away or mismatched receipts remain
77
+ pinned/blocked, never guessed, deleted or reinjected.
78
+ - Interrupted/failed host sessions and cancel/revert events pause delivery.
79
+ Only subsequent native execution can reopen the event pause. Session move,
80
+ archive, delete or plugin unload fences the owner. Unload is resumable and does
81
+ not terminally close a surviving broker identity; native disappearance is not
82
+ treated as permission to silently allocate a replacement. A queued item cancelled by
83
+ the user is not recreated.
84
+ - No cooperative receive/ack tools are registered in V2. The eight outgoing/
85
+ status/metadata lifecycle tools use the shared reviewed SDK. Native tool-call
86
+ identity scopes private durable send/retry keys. Send/resend additionally require
87
+ the actual matching native assistant tool block creation time, so an aged replay
88
+ cannot mint a new operation after journal GC. Missing/compacted proof fails closed.
89
+
90
+ ## Verification
91
+
92
+ ```sh
93
+ node --test test/v2-delivery.test.mjs test/v2-plugin.test.mjs test/v2-setup.test.mjs
94
+ OPENCODE_V2_BINARY=/path/to/official/opencode2 node --test test/v2-host.test.mjs
95
+ # Optional native installer proof: existing CLI, disposable HOME/XDG only.
96
+ OPENCODE_V2_BINARY=/path/to/official/opencode2 \
97
+ PSST_V2_PACKAGE_SPEC='github:matuto-ai/opencode-broker-connector#FULL_COMMIT::path:v2' \
98
+ node --test test/v2-installer.test.mjs
99
+ npm test
100
+ npm run check
101
+ npm run pack:check
102
+ ```
103
+
104
+ The real-host test packs/extracts the candidate, loads its nested V2 directory
105
+ through the native plugin manager in an isolated HOME/XDG tree with random local
106
+ HTTP Basic credentials, connects a real local TLS broker, sends a real envelope,
107
+ and verifies automatic native synthetic admission and Plan→Build preservation.
108
+ With `PSST_APPROVAL_BROKER_DIR` pointing to the approval-enabled broker checkout,
109
+ a second real-host test invokes the registered native setup executor through a
110
+ fixture command/public tool API, approves the real TLS request in the disposable
111
+ admin fixture, and verifies automatic connection/delivery. This is actual native
112
+ plugin/tool integration, not a model-issued tool-call proof.
113
+ The selected fixture provider deliberately does not exist: **no user credentials,
114
+ model API or authenticated LLM response is used or proved**. Unit mocks separately
115
+ exercise uncertain writes, exact receipts, cancellation, restart, owner isolation,
116
+ selection changes, FIFO bounds and stalled requests.
117
+
118
+ Official contracts: [plugin loading](https://opencode.ai/v2/docs/plugins/),
119
+ [server plugins](https://opencode.ai/v2/docs/build/plugins/server),
120
+ [native synthetic admission](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/session/session.ts#L280-L320),
121
+ [pending/reconciliation semantics](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/session/inbox.ts).
package/docs/owned-run.md CHANGED
@@ -1,13 +1,15 @@
1
+ > Candidate update: production requires automatic delivery and is currently blocked on the classic host. Manual pull/owned-run flows below are diagnostic/test-only. See [current lifecycle contract](message-lifecycle.md) for hidden operation keys and new APIs. Historical fixture pins are separate from the runtime candidate.
2
+
1
3
  # Owned fresh OpenCode run (candidate)
2
4
 
3
- This mode launches **one new headless `opencode run`**, rather than attaching to an existing TUI, server session, transcript or resumed conversation. It offers the same five cooperative broker tools as the adapter factory. It does not interrupt a task, inject a prompt automatically, turn another agent's text into approval, or mark a native tool result consumed.
5
+ This mode launches **one new headless `opencode run`**, rather than attaching to an existing TUI, server session, transcript or resumed conversation. It offers the same nine diagnostic broker tools as the adapter factory. It does not interrupt a task, inject a prompt automatically, turn another agent's text into approval, or mark a native tool result consumed.
4
6
 
5
7
  ## Supported pinned prerequisites
6
8
 
7
9
  - Linux x64 with `/proc` available; launcher Node **24.19.0**.
8
10
  - Official `opencode-linux-x64` **1.19.4** executable. Its SHA256 must be `538fcb1b6408c8d4608822acee19f7ff5b3a3eb3383c0633c4f06170857312a7`. Other builds/platforms are deliberately refused, not assumed compatible.
9
11
  - `npm ci --ignore-scripts` in this repository installs the locked official `@opencode-ai/plugin` **1.19.4** helper. The external native plugin requires this local dependency; OpenCode's separate config-directory dependency is not a substitute.
10
- - A local broker reference checkout at **`ba2219cd87a1feac23e01b23d2c2b3d3bdd65359`**. The loader verifies its runtime file hashes before importing it.
12
+ - A local broker reference checkout at **`2de9cc3758b9fc593f5afca4fc501fd013d416ec`**. The loader verifies its runtime file hashes before importing it.
11
13
  - An already configured, trusted HTTPS broker and private token file. This launcher does not create credentials, expose a server, alter TLS verification or configure a model provider.
12
14
 
13
15
  The pinned OpenCode executable embeds Bun **1.4.4**. Its `node:sqlite` compatibility was exercised with the actual shared Inbox; no separate Bun installation or global Node change is required.
@@ -5,7 +5,7 @@ Package name: `@matuto-ai/opencode-broker-connector`. The npm workflow follows t
5
5
  1. Review and merge the feature PR.
6
6
  2. Bootstrap the package under an npm account authorized for this package if it does not exist yet. Account creation, credentials and first publication are operator actions, not performed by the connector.
7
7
  3. Configure npm's GitHub Actions trusted publisher for owner `matuto-ai`, repository `opencode-broker-connector`, workflow `npm-publish.yml`. If the repository moves, update package metadata and trusted-publisher settings together.
8
- 4. Create a GitHub release whose tag exactly matches `v` + package.json version (next `v0.3.0`).
8
+ 4. Create a GitHub release whose tag exactly matches `v` + package.json version (next `v0.4.0`).
9
9
  5. The action runs syntax, tests including pinned actual CLI, package inspection, then `npm publish --access public --provenance` using OIDC. No long-lived npm token is stored in the workflow.
10
10
  6. Confirm npm has the exact version before recommending npm installation. Failed publishing is not repaired by silently changing a version or recreating a tag.
11
11
 
@@ -15,7 +15,7 @@ Before npm publication, the local-file installation in README works directly fro
15
15
 
16
16
  ## Minor release
17
17
 
18
- This PR prepares `0.3.0`; the intended GitHub release tag is `v0.3.0`.
18
+ This PR prepares `0.4.0`; the intended GitHub release tag is `v0.4.0`.
19
19
  After review and merge, the maintainer can publish that release from the merged
20
20
  commit to exercise npm Trusted Publishing. This code change does not itself
21
21
  create a tag, release, or npm publication. Verify the workflow succeeds and the
@@ -1,10 +1,10 @@
1
- 93f75786eab29429ace024974e0f61d6ad46d2c5fe065dae387e54fd2373b167 reference/inbox.mjs
2
- f661b45acba106be03d97dacba3cd90ac18b09e7ff9eb93b995d5036d9d1c785 reference/client.mjs
1
+ fbf0fa6096996cb9bc3f928e55f3eca496420880a03460e993dda3417b32ad88 reference/inbox.mjs
2
+ c4c313e937d1003f28928a672072b926022aa0e9fd65825bc5db1078eb90afa9 reference/client.mjs
3
3
  8733076b73a42ab8f12fe20ba9908acdd754d89734ffd335e84b49aee9d737ca reference/runtime.mjs
4
- 5e6355cef1bec56170dca14b93142a7da8c102f411c5dbf437d84acda7351acf reference/tools.mjs
5
- 10a93788af426ce898c398e38dd41ac77b6e0129a7697bdc36c8d4f6d6e20138 reference/mcp-stdio.mjs
4
+ 5d3b16212a048912395d74b95a8b590d278b8b0915bb0bd7a0c3d80daf353839 reference/tools.mjs
5
+ f34b0cdcda9c2d607815e4ef99f1ba80fbd8936ef8c2893a858b6d408bc0ff09 reference/mcp-stdio.mjs
6
6
  e33231d94f514b5a1b59fdd4184f5881dd3712bde725a1cf53352c26293452ac reference/errors.mjs
7
- f3ff24b4429dddc8a9c10513ab72dbc613c91244591132458c7866a812d6316b src/protocol.mjs
7
+ 2d88829971643d47430d6696d0a6ac61589ea4f9001c860c5c196ecc5ac21a6f src/protocol.mjs
8
8
  575ac7406ed9b8698d97ecdc2f093a99455fdd581f375c4611c844d4618095c8 src/store.mjs
9
9
  69d5130ce90e045d0cd66facdc4380ef03f50895c53997fe9a664814fcca0e7b src/server.mjs
10
10
  8d85ecd3c02142de87de34425b71daa35b7317bbe3cfd19865c43e7bb57e3785 src/auth.mjs
@@ -32,3 +32,12 @@ that policy. Preserve managed model/provider, permission and credential policy.
32
32
  This connector does not remove pure mode or edit the sandbox launcher itself.
33
33
  The current owner environment remains an acceptance gate until its effective
34
34
  bootstrap is inspected and the intended plugin installation is permitted.
35
+
36
+ ## Read-only capability diagnosis
37
+
38
+ The packaged `psst-opencode` doctor reports `OPENCODE_PURE` only as observed in
39
+ its own process environment; it never disables it or claims to inspect every
40
+ managed source. A host that skips the plugin cannot run the plugin's diagnostic
41
+ hook. Ask the administrator to review the actual approved launcher source.
42
+ Even with plugins enabled, the pinned classic host remains `automatic_blocked`;
43
+ transport presence/manual receive must not be reported as production readiness.
@@ -1,3 +1,5 @@
1
+ > Candidate update: production requires automatic delivery and is currently blocked on the classic host. Manual pull/owned-run flows below are diagnostic/test-only. See [current lifecycle contract](message-lifecycle.md) for hidden operation keys and new APIs. Historical fixture pins are separate from the runtime candidate.
2
+
1
3
  # Native plugin verification (October 24, 2026)
2
4
 
3
5
  The current default package adds native session-keyed cooperative tools and a self-contained pinned reference runtime. CI runs the full suite with the integrity-verified official OpenCode 1.19.4 Linux x64 fixture (embedded Bun 1.4.4).
@@ -123,7 +125,7 @@ The second actual-host regression loads the npm-packed production plugin into of
123
125
 
124
126
  A further actual OpenCode smoke loads the packed plugin, passes only a private pairing-file path to the native command, and exercises the unmodified shared enrollment helper over system-trusted HTTPS. A synthetic endpoint implementing the frozen broker contract records a hash-only credential and authorizes it in the real transport fixture; automatic peer messages then reach the deterministic local model. This checks native/helper/credential integration, not the production broker's operator issuance implementation (covered by upstream broker tests). Unit tests additionally cover marked bundle receipt deletion/type/origin/token mismatches, private permissions, per-native-session attempt separation, expiry shutdown, revoked authorization shutdown and no late connect after disconnect.
125
127
 
126
- The default connector remains cooperative. Additional pinned-host coverage proves that the official v2 session identity is visible while its context/history remain empty for a classic CLI conversation with real messages. The connector does not mix these engines. Unknown agent/model state blocks the experiment and observed changes disconnect it. No test establishes atomic admission against simultaneous external native selection edits.
128
+ The default connector reports automatic readiness blocked; manual receive requires an explicit diagnostic request. Additional pinned-host coverage proves that the official v2 session identity is visible while its context/history remain empty for a classic CLI conversation with real messages. The connector does not mix these engines. Unknown agent/model state blocks the experiment and observed changes disconnect it. No test establishes atomic admission against simultaneous external native selection edits.
127
129
 
128
130
  ## URL-only approval and persistent native configuration (0.3.0 candidate)
129
131
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@matuto-ai/opencode-broker-connector",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24.19.0 <25"
7
7
  },
8
8
  "scripts": {
9
9
  "test": "node scripts/test.mjs",
10
- "check": "find src bin plugin examples scripts test vendor \\( -name \"*.mjs\" -o -name \"*.ts\" \\) -not -path \"*/node_modules/*\" -print0 | xargs -0 -r -n1 node --check",
10
+ "check": "find src bin plugin v2 examples scripts test vendor \\( -name \"*.mjs\" -o -name \"*.ts\" \\) -not -path \"*/node_modules/*\" -print0 | xargs -0 -r -n1 node --check",
11
11
  "pack:check": "node scripts/pack-check.mjs"
12
12
  },
13
13
  "license": "MIT",
@@ -19,7 +19,8 @@
19
19
  "exports": {
20
20
  ".": "./plugin/index.mjs",
21
21
  "./server": "./plugin/index.mjs",
22
- "./tui": "./plugin/tui.mjs"
22
+ "./tui": "./plugin/tui.mjs",
23
+ "./v2": "./plugin/v2.mjs"
23
24
  },
24
25
  "files": [
25
26
  "plugin",
@@ -33,7 +34,8 @@
33
34
  "README.md",
34
35
  "vendor/broker/ORIGIN.md",
35
36
  "vendor/enrollment",
36
- "CHANGELOG.md"
37
+ "CHANGELOG.md",
38
+ "v2"
37
39
  ],
38
40
  "repository": {
39
41
  "type": "git",
@@ -41,5 +43,8 @@
41
43
  },
42
44
  "publishConfig": {
43
45
  "access": "public"
46
+ },
47
+ "bin": {
48
+ "psst-opencode": "bin/doctor.mjs"
44
49
  }
45
50
  }
package/plugin/tui.mjs CHANGED
@@ -1,9 +1,12 @@
1
+ import { deliveryCapability } from '../src/capabilities.mjs';
1
2
  import { installSetupUI } from '../src/setup-ui.mjs';
2
3
  import { readSelection, readPending } from '../src/installation.mjs';
3
4
  import { startContextPublisher } from '../src/native-context.mjs';
4
5
  export default { id: 'matuto.psst', tui: async api => {
5
6
  startContextPublisher(api);
6
7
  await installSetupUI(api);
8
+ const capability = deliveryCapability();
9
+ api.ui.toast({ title: 'Psst: automatic delivery blocked', variant: 'warning', duration: 10000, message: capability.next_action });
7
10
  try {
8
11
  if (!process.env.PSST_CONFIG_FILE && !readSelection()) {
9
12
  const pending = readPending();
package/plugin/v2.mjs ADDED
@@ -0,0 +1,18 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import { loadBrokerReference } from '../src/reference.mjs';
3
+ import { readExistingConnection } from '../src/enrollment.mjs';
4
+ import { readInstallation } from '../src/installation.mjs';
5
+ import { setupV2Plugin } from '../src/v2-plugin.mjs';
6
+
7
+ // Structurally the public @opencode/plugin Plugin.define({ id, setup }) contract.
8
+ // define is identity-only; no V2 host, runtime or CLI is installed by this package.
9
+ export default {
10
+ id: 'matuto.psst.v2',
11
+ async setup(ctx) {
12
+ const reference = await loadBrokerReference();
13
+ const connectionFile = ctx.options?.connectionFile ?? process.env.PSST_CONFIG_FILE;
14
+ const runtime = await setupV2Plugin({ ctx, reference, tool, setupEnabled: !connectionFile,
15
+ loadSettings: () => connectionFile ? readExistingConnection(connectionFile) : readInstallation() });
16
+ return runtime.dispose;
17
+ },
18
+ };
@@ -0,0 +1,13 @@
1
+ /** Capability statements are not broker presence and never change host policy. */
2
+ export function deliveryCapability(env = process.env, { experimental = false } = {}) {
3
+ const pure = ['1', 'true'].includes(String(env.OPENCODE_PURE).toLowerCase());
4
+ return {
5
+ mode: 'automatic_required', ready: false,
6
+ delivery: experimental ? 'experimental_test_only' : 'automatic_blocked',
7
+ automatic_receive: false,
8
+ blocker: pure ? 'opencode_pure_mode' : 'classic_atomic_admission_unavailable',
9
+ next_action: pure
10
+ ? 'OPENCODE_PURE is enabled in this process. Ask the sandbox administrator to review the approved launcher source; the connector does not disable policy. No loaded plugin can diagnose itself when the host skips it entirely.'
11
+ : 'This classic OpenCode integration lacks safe automatic admission preserving the current agent/model and conversation. Transport may connect, but production messaging is not ready. Manual receive is diagnostic/test-only; do not enable a snapshot override as a fix.',
12
+ };
13
+ }