@matuto-ai/opencode-psst-connector 0.0.0-stage → 0.7.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 (52) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/LICENSE +21 -0
  3. package/README.md +182 -2
  4. package/docs/active-conversation.md +68 -0
  5. package/docs/message-lifecycle.md +31 -0
  6. package/docs/migration-0.6.0.md +21 -0
  7. package/docs/native-automation.md +37 -0
  8. package/docs/presence-notices.md +21 -0
  9. package/docs/publishing.md +19 -0
  10. package/docs/reference-snapshot.sha256 +7 -0
  11. package/docs/setup-acceptance.md +96 -0
  12. package/docs/setup-context-recovery.md +55 -0
  13. package/docs/verification.md +39 -0
  14. package/package.json +42 -4
  15. package/plugin/index.mjs +18 -0
  16. package/plugin/tui.mjs +188 -0
  17. package/src/active-presence.mjs +97 -0
  18. package/src/approval-helper.mjs +11 -0
  19. package/src/config.mjs +50 -0
  20. package/src/enrollment.mjs +59 -0
  21. package/src/identity-recovery.mjs +94 -0
  22. package/src/installation.mjs +127 -0
  23. package/src/message-format.mjs +26 -0
  24. package/src/message-preview.mjs +29 -0
  25. package/src/native-delivery.mjs +169 -0
  26. package/src/native-inbox.mjs +37 -0
  27. package/src/native-incarnation.mjs +67 -0
  28. package/src/native-lock.mjs +17 -0
  29. package/src/native-metadata.mjs +15 -0
  30. package/src/native-plugin.mjs +336 -0
  31. package/src/native-setup.mjs +249 -0
  32. package/src/opencode-adapter.mjs +223 -0
  33. package/src/presence-notices.mjs +90 -0
  34. package/src/project-metadata.mjs +21 -0
  35. package/src/reference-pin.mjs +21 -0
  36. package/src/reference.mjs +20 -0
  37. package/src/setup-rpc.mjs +16 -0
  38. package/src/tool-schema.mjs +5 -0
  39. package/src/tool-scope.mjs +26 -0
  40. package/vendor/broker/LICENSE +21 -0
  41. package/vendor/broker/ORIGIN.md +12 -0
  42. package/vendor/broker/reference/client.mjs +439 -0
  43. package/vendor/broker/reference/errors.mjs +43 -0
  44. package/vendor/broker/reference/inbox.mjs +334 -0
  45. package/vendor/broker/reference/mcp-stdio.mjs +160 -0
  46. package/vendor/broker/reference/runtime.mjs +329 -0
  47. package/vendor/broker/reference/tools.mjs +88 -0
  48. package/vendor/broker/src/protocol.mjs +325 -0
  49. package/vendor/enrollment/LICENSE +21 -0
  50. package/vendor/enrollment/ORIGIN.md +20 -0
  51. package/vendor/enrollment/approval.mjs +181 -0
  52. package/vendor/enrollment/enrollment.mjs +137 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ ## 0.7.0 — unreleased source candidate
4
+
5
+ Publish the connector under `@matuto-ai/opencode-psst-connector` and rename the GitHub repository to `opencode-psst-connector`. Preserve native identity, saved installation credentials, journals and tool names. The old npm package remains available; migration requires an explicit package-source change. See [migration checklist](docs/migration-0.6.0.md).
6
+
7
+ ### Included source changes
8
+
9
+ - Add explicit native `/psst-reauthorize`, configured-origin reuse and exact-request/selection confirmation; replacement does not revoke the old credential.
10
+ - Preserve setup candidates across focus interruptions, reconcile durable outcomes, and isolate unrelated chats without requiring them to close.
11
+
12
+ - Add native URL-only setup/status/cancel dialogs, exact-request replacement confirmation, durable owner/status recovery and cancellation/CAS fencing.
13
+ - Attach approved replacement before selecting it while retaining the previous working authorization and private journals; preserve strict identity recovery for expired authorization so setup can repair it.
14
+ - Permit conclusively fresh native hosts to share installation authorization without adopting another mailbox; verify 30 same-name sessions and pinned previous-source upgrade.
15
+ - Expand official-host acceptance with native restart, unread delivery, independent sessions, pending replacement and native input/permission preservation. Authenticated model qualification remains explicit and separate.
16
+
17
+ - Limit receive and heartbeat to selected local TUI conversations, resuming the same identity without closing inactive sessions.
18
+ - Add exact native model and bounded configured project metadata; keep immutable retry payloads.
19
+ - Add explicit registration-v2 opt-in and confirmed-expiry incarnation recovery; preserve old journals and block uncertain storage. Broker retention remains a separate opt-in.
20
+
21
+ - Standardize new native notices as `Psst · new message · From: <name> · <type> · <preview>` in English; keep IDs and correlation out of the UI preview without changing full payloads or existing persisted notices.
22
+
23
+ ## 0.5.1 (unreleased)
24
+
25
+ - Make new native history notices show a bounded literal message preview and external-agent sender label, rather than only “Psst agent message”.
26
+ - Preserve full message bodies, machine provenance, receipt semantics and all existing persisted payloads byte-for-byte.
27
+
28
+ ## 0.5.0
29
+
30
+ - Correct the root npm package to the sole current native OpenCode plugin, registered as `matuto.psst`. Remove the classic/TUI/owned-run entries and Git-dependent wrapper. Public CLI is `opencode`.
31
+ - Keep CodeMode enabled while registering send/resend as supported direct native tools with individual durable IDs; other broker tools remain in CodeMode.
32
+ - Bundle the reviewed runtime directly; production dependencies are npm-registry only. Add root-tarball and official-loader regression gates.
33
+ - Preserve approved private authorization and recover an existing client only from a unique, exact native binding. Unprovable ownership and old unapproved setup-owner loss fail closed without replacing credentials or journals.
34
+
35
+ ## 0.4.0 — 2026-11-01
36
+
37
+ - Added native automatic delivery, durable setup and exact-session lifecycle corrections.
38
+ - Packaging still selected the old root integration and required a separate Git-dependent native wrapper. Version 0.5.0 replaces that unsupported installation layout.
39
+
40
+ ## 0.3.0 and earlier — historical integration
41
+
42
+ - Earlier prototypes used classic host/TUI and manual delivery integrations. They are no longer supported or shipped.
43
+ - Existing private authorization and journal files must be preserved during the current package correction; old installation commands and experimental flags are not a supported upgrade path.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bruno Orsolon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,183 @@
1
- # Temporary Holding Version
1
+ # Psst for OpenCode
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **0.7.0 source candidate — not published yet.**
4
+ One npm package, one current native plugin, registered as **`matuto.psst`**.
5
+ The same npm package provides official server and TUI entries. There is no separate installation, Git wrapper or legacy entry.
6
+
7
+ ## Install or update
8
+
9
+ After this version is published:
10
+
11
+ ```sh
12
+ opencode plugin add @matuto-ai/opencode-psst-connector@0.7.0
13
+ ```
14
+
15
+ For an existing installation, first inspect `opencode plugin list` and remove
16
+ only this connector's exact previous package entry with `opencode plugin remove
17
+ <old-entry>` before adding the new version. The manager matches exact package
18
+ specs; keep unrelated plugins and all private Psst directories. Do not install
19
+ both an old Git wrapper and this npm package.
20
+
21
+ Reopen OpenCode, select the intended conversation and run **`/psst-setup`**.
22
+ Enter the broker HTTPS URL in its single text field, then approve the displayed
23
+ request in the broker admin page. `/psst-status` shows progress;
24
+ `/psst-cancel-setup` pauses the local request without revoking existing access.
25
+ No token, connection-file handoff or host policy change is needed. Startup never
26
+ enrolls; an already approved installation is reused.
27
+
28
+ Running setup again resumes the same pending request. Replacing a terminal request
29
+ requires an explicit native confirmation tied to that exact request. The old
30
+ connection stays active until the approved candidate attaches and its selection
31
+ commits. Denial, cancellation or failed attachment retains the working connection.
32
+ A deliberate new authorization has new credential-bound addresses; it does not
33
+ adopt the previous authorization's mailbox. Old local data remains intact.
34
+ Ordinary upgrades retain the same authorization, addresses and unread mailbox.
35
+
36
+ ### Request a replacement authorization
37
+
38
+ Run **`/psst-reauthorize`** in the intended chat, or choose **Psst: Request
39
+ replacement authorization** in the palette. It reuses the configured HTTPS broker
40
+ origin (or asks for one if none exists), displays that target, and asks for native
41
+ confirmation before creating a new request. Approve the displayed public request
42
+ ID in the broker `/admin` page, then use `/psst-status` to distinguish pending,
43
+ approved but not connected, and connected. A pending candidate is resumed, not
44
+ replaced; an approved candidate whose attachment failed is retried intact.
45
+
46
+ This is deterministic native TUI-to-server dispatch, with no model conversation
47
+ or model/provider tokens required by the connector on supported OpenCode 2.2.5.
48
+ It is not reconnect: reopening the selected conversation reuses saved credentials
49
+ and existing addresses. Reauthorization creates new credential-bound addresses
50
+ only after approval, attachment and safe selection. Old unread data is preserved,
51
+ not replayed into the new identity. Existing access continues only while valid.
52
+
53
+ **Replacement does not revoke the old credential.** Revocation is a separate
54
+ human action in Psst admin; do not revoke working access first as a prerequisite.
55
+ Cancellation, focus loss or an uncertain response never authorizes another new
56
+ request: return to the original chat, inspect `/psst-status`, then repeat the
57
+ command to reconcile the same candidate. Do not delete private state or locks.
58
+
59
+ The URL-only `broker_setup` tool remains available for an explicitly requested
60
+ initial setup, but an agent tool cannot confirm replacement. Use `/psst-reauthorize`
61
+ for that decision. See [setup, persistence and acceptance](docs/setup-acceptance.md).
62
+
63
+ ## Messages
64
+
65
+ Only the conversation selected in a local TUI receives automatic messages and renews its heartbeat. Home, exit and stale TUI leases suspend transport without closing the identity; reopening reconnects that identity. Multiple CLIs are independent. No TUI signal means offline. See [active-conversation behavior and limits](docs/active-conversation.md).
66
+
67
+ Incoming messages use the native synthetic queue automatically, without changing
68
+ agent/model selection or the user's draft. New native history notices include a
69
+ compact literal preview, clearly labeled `Psst · new message`, with `From:` followed
70
+ by sender name and client type. Correlation and IDs are not shown in the notice.
71
+ Controls and bidi formatting are escaped; long text is truncated.
72
+ The full machine-origin body is unchanged. Existing persisted notices keep their
73
+ original payload, including their old label. There is no manual receive/ack tool.
74
+ Use `broker_list_clients`, `broker_send`, `broker_message_status`,
75
+ `broker_update_metadata`, `broker_sent_history`, `broker_message_content`,
76
+ `broker_resend` and `broker_cancel`. Native tool identity and original timestamp
77
+ scope private retry records; a new tool call is not semantically deduplicated.
78
+ With CodeMode enabled, `broker_send` and `broker_resend` are advertised by the
79
+ host as direct native tools alongside `execute`, so every outgoing call has its
80
+ own durable identity. Other broker tools remain available inside CodeMode. No
81
+ mode, permission or session change is required; sends are not nested under
82
+ `execute`, whose children share one outer call ID.
83
+
84
+ A broker receipt proves durable inbox storage; native admission proves persistence,
85
+ not model understanding, human approval or completed work. Sender/message IDs stay
86
+ in a compact machine-origin envelope. Uncertain/cancelled submissions are never
87
+ blindly retried or discarded. See [delivery](docs/native-automation.md).
88
+
89
+ ## Compatibility
90
+
91
+ OpenCode **2.x** native plugin APIs are the target; **2.2.5** is the validated
92
+ version and current runtime guard. This is not a promise that every 2.0+ build
93
+ has the required session admission, context, storage and lifecycle APIs. The
94
+ public executable is `opencode`; unsupported versions fail clearly rather than
95
+ falling back to another integration.
96
+
97
+ ## Existing authorization and private state
98
+
99
+ The installation path is unchanged: `PSST_OPENCODE_CONFIG_DIR`, otherwise
100
+ `$OPENCODE_CONFIG_DIR/psst`, otherwise `$XDG_CONFIG_HOME/opencode/psst` (default
101
+ `~/.config/opencode/psst`). Keep it private (directory 0700, files 0600), under the
102
+ same UID and absolute path. Existing approved credentials, receipts and inbox
103
+ journals are not deleted, rotated or rewritten. An intentional `PSST_CONFIG_FILE`
104
+ override remains respected and disables URL setup rather than secretly replacing it.
105
+
106
+ The old registered ID had a different host-storage namespace. The new plugin can
107
+ recover one unique private host anchor only from owned connector manifests and an
108
+ exact matching native session ID, creation time, project and location. The old
109
+ private format/path markers remain internal compatibility data, not plugin names.
110
+ Corrupt, ambiguous or changed live ownership stops with `identity_recovery_*`.
111
+ A fresh independent native host gets a new anchor only when all relevant previous
112
+ session IDs are conclusively absent, or belong to another directory/workspace;
113
+ it never adopts their journals. Existing live ownership still causes an
114
+ owner conflict. Preserve the files and report the error rather than resetting them.
115
+ Old classic journals without exact native bindings cannot be migrated automatically:
116
+ `identity_legacy_migration_required` preserves them and stops new identity creation.
117
+ This is a preservation limit, not a successful migration of that old layout.
118
+
119
+ An old **unapproved** setup request whose owner existed only in the old host
120
+ namespace cannot be resumed safely. It remains intact and inert with
121
+ `setup_owner_recovery_required`; existing approved authorization is unaffected.
122
+ New pending records also persist the exact owner binding privately, so they can
123
+ resume after host-storage namespace loss only when the native session still
124
+ matches. A crash after candidate persistence resumes that same request with GET,
125
+ never another startup enrollment. No direct host-database access is used.
126
+
127
+ ## Verification
128
+
129
+ ```sh
130
+ npm ci --ignore-scripts
131
+ npm run check
132
+ npm test
133
+ npm run pack:check
134
+ ```
135
+
136
+ The unit suite checks connector configuration, native session identity, setup state,
137
+ message presentation, scoped tool calls, delivery receipts, cancellation and recovery.
138
+ Host and broker boundaries use in-process fakes; persistence checks use disposable
139
+ local files and SQLite journals. No OpenCode binary, broker server, container,
140
+ network access or provider credentials are needed. See [verification](docs/verification.md).
141
+
142
+ CI runs source syntax and this same unit suite. Package file-list checks remain a
143
+ separate release safeguard, not an installed-host or end-to-end test. Unit results
144
+ do not prove authenticated native model execution.
145
+
146
+ MIT. Vendored Psst code keeps its license and exact integrity pins.
147
+
148
+ ## Migrating to the Psst package name
149
+
150
+ Version 0.7.0 uses a new npm package and GitHub repository name. Follow the [migration checklist](docs/migration-0.6.0.md) before publication or upgrading an existing installation. Preserve all private Psst state and do not load both old and new sources.
151
+
152
+ ### Opt-in registration v2 and retained native conversations
153
+
154
+ Registration remains v1 by default. With a compatible broker, an operator can set
155
+ `broker.registrationVersion: 2` in plain configuration, or `registration_version: 2`
156
+ in an existing `broker-client-bundle-v1` connection file. Only numeric1 or2 is
157
+ accepted. The bundle keeps its enrollment-receipt requirement, credentials and
158
+ paths; do not convert an enrolled bundle into plain configuration to opt in.
159
+ This does not enable server cleanup. The broker's retention/enforcement activation
160
+ is a separate operator decision after all connectors are compatible.
161
+
162
+ An inactive native conversation resumes the same identity before expiry. After an
163
+ authenticated lookup confirms its own broker session is gone, the connector
164
+ revalidates the exact native conversation and selected state, then creates a fresh
165
+ empty journal and identity. A private atomic `current-incarnation.json` pointer
166
+ selects that generation on reload. Old pending sends, claims, native admissions and
167
+ uncertain operations are never copied or replayed into the new generation. Old
168
+ local journals remain untouched; this is not local-history deletion.
169
+
170
+ The original root `owner.sqlite` lock remains held across rebuilding and child
171
+ journals, preventing another worker from concurrently adopting the old root.
172
+ Missing/corrupt pointed journals and v1 fallback after a pointer exists fail closed.
173
+ Storage uncertainty stops automatic restoration for that conversation during the
174
+ plugin lifetime; recovery requires a deliberate plugin reload that first reads the
175
+ durable pointer. Never delete journals or change identities to force a retry.
176
+
177
+ The reviewed SDK also contains two cooperative presence-event tools. This native
178
+ connector intentionally continues exposing its existing eight tools, not those two
179
+ new tools; unknown future SDK catalog changes still fail closed.
180
+
181
+ Native offline transport notices and their exact-context ACK semantics are documented in [presence-notices](docs/presence-notices.md).
182
+
183
+ For interrupted URL setup or multiple open chats, see [setup context and focus recovery](docs/setup-context-recovery.md). Keep unrelated chats open; return to the original chat and reconcile its saved request with `/psst-status`.
@@ -0,0 +1,68 @@
1
+ # Active conversation availability (Unreleased)
2
+
3
+ The same root npm package provides the official OpenCode 2.2.5 `./server` and
4
+ `./tui` entries. There is no second plugin installation or CLI wrapper.
5
+
6
+ Only the conversation currently selected in a local TUI receives automatic
7
+ messages and renews its broker heartbeat. Different TUI processes can select
8
+ different conversations. If two select the same conversation, it remains active
9
+ until the last owner leaves. Home and plugin pages select no conversation.
10
+
11
+ Changing selection suspends transport and native delivery without closing the
12
+ broker identity or deleting history. The broker may still show it connected until
13
+ its existing lease expires (normally three minutes). Reopening reconnects the
14
+ same persisted identity; heartbeat alone is not used to revive an expired lease.
15
+ A synthetic submission already in flight may finish: its original journal and
16
+ receipt are reconciled, not duplicated or rewritten.
17
+
18
+ No TUI signal means offline, including a headless server. A remote TUI without a
19
+ shared local private directory is unsupported and shows `Psst offline`; it does
20
+ not restore all historic conversations. A newly selected conversation does not
21
+ create a broker identity from this signal: its verified native prompt, tool or
22
+ approved setup establishes the binding first.
23
+
24
+ ## Local preference, not authority
25
+
26
+ TUI selection is an ephemeral file lease in the existing private installation
27
+ area, scoped to the native directory and workspace. Files are atomically replaced,
28
+ private (0600 in 0700 directories), bounded, and reject symlinks. A TUI-local
29
+ in-memory owner and increasing sequence survive plugin reload. Each generation
30
+ only removes its own last write. Updates are sampled every 250 ms and expire after
31
+ five seconds without renewal. Server reconciliation runs every 500 ms. On Linux,
32
+ PID start time and boot ID also reject dead or recycled owners; other supported
33
+ platforms use process liveness and the bounded TTL, not Linux-specific `/proc`.
34
+
35
+ **Processes with the same UID can forge this preference.** It is not an
36
+ anti-spoofing security boundary. A forged lease can change availability of an
37
+ already-authorized conversation (and cause its normal automatic wake/cost).
38
+ It cannot create an authorized native binding, supply credentials, change scopes,
39
+ redirect an inbox to another session, or grant tool access. The server continues
40
+ to own credentials and journals and independently verifies exact native bindings.
41
+ There is no public activate RPC or credential exchange. The native setup RPC
42
+ accepts only a selected session ID, broker URL and exact-request replacement
43
+ confirmation; it cannot supply credentials or bypass broker admin approval.
44
+
45
+ Archive suspension can resume after native unarchive. Explicit disabled records,
46
+ and older disabled records whose reason is unknown, remain disabled; selection
47
+ never overrides them. Existing unread and uncertain operations remain intact.
48
+
49
+ ## Validation limits
50
+
51
+ Unit tests exercise connector selection, identity, cancellation and recovery with
52
+ in-process native and transport fakes. They do not start OpenCode or a broker.
53
+ Authenticated model response and native-host compatibility remain separate product
54
+ acceptance concerns. See [setup acceptance](setup-acceptance.md).
55
+
56
+ ## Display metadata
57
+
58
+ Project labels resolve from the explicit local plugin/config `project`, then the
59
+ single variable named by `PSST_PROJECT_ENV`, local `projectEnv`, or the default
60
+ `AI_AGENTS_SANDBOX_PROJECT_NAME` (in that order), then the native directory's
61
+ basename. Only a bounded display label is used, not a full environment dump.
62
+
63
+ The exact native session `model.providerID` and `model.id` are preferred. If absent,
64
+ `OPENCODE_MANAGED_MODEL=provider/model` is an explicitly configured fallback,
65
+ reported as `configured`, never `observed`. Otherwise model remains `unknown`.
66
+ Initial registration and newly prepared outgoing sends observe this metadata;
67
+ retries retain their original immutable outgoing payload. Model selections and
68
+ permissions are not modified.
@@ -0,0 +1,31 @@
1
+ # Outgoing message lifecycle
2
+
3
+ `broker_send` takes recipient ID and text; retry keys are private.
4
+ `broker_sent_history` lists the exact sender session's outgoing metadata;
5
+ `broker_message_content` retrieves a retained original; `broker_resend` explicitly
6
+ creates a new delivery; `broker_cancel` cannot undo receipt or execution.
7
+
8
+ A native call's original assistant tool block supplies its stable ID and creation
9
+ time. The connector persists the operation before HTTP and preserves its key on
10
+ technical retry/recovery. A different native call is new intent even when text is
11
+ identical. Aged or missing original proof fails closed, including after ledger GC.
12
+
13
+ Inspect current status/history after uncertainty before requesting a new explicit
14
+ send. Recovery is bounded to 10 retained operations and 5 seconds. Unknown effects
15
+ remain unknown, never fabricated success. Broker 0.5.0 supplies the lifecycle APIs;
16
+ transport, native admission and model completion remain separate evidence.
17
+
18
+ ## CodeMode routing
19
+
20
+ OpenCode's CodeMode executor forwards one outer `execute` ID to all nested calls.
21
+ The plugin therefore registers only `broker_send` and `broker_resend` with the
22
+ supported `options.codemode:false`. The actual host catalog advertises those as
23
+ direct tools alongside `execute`; all other broker tools retain normal CodeMode
24
+ routing. Direct calls retain individually persisted native IDs and original
25
+ timestamps. No invented child index, random retry ID or content-based dedup is
26
+ used. An obsolete wrapper call remains fail-closed instead of sending ambiguously.
27
+
28
+ Unit tests exercise scoped outgoing calls, stable operation IDs, payload conflicts,
29
+ cancellation and recovery using an in-process transport and native API. They also
30
+ assert that send/resend bypass CodeMode while other tools retain default routing.
31
+ They do not run OpenCode's executor or prove an authenticated model turn.
@@ -0,0 +1,21 @@
1
+ # 0.7.0 package and repository migration
2
+
3
+ This is an unreleased source candidate, not proof of npm publication. The new package is `@matuto-ai/opencode-psst-connector@0.7.0`; the old package is `@matuto-ai/opencode-broker-connector`. No prerelease/RC package is required.
4
+
5
+ ## Owner publication order
6
+
7
+ 1. Review and merge the code in the existing repository; retain its history and branch protections. CI checks out this repository and runs its unit suite; it does not fetch a second repository.
8
+ 2. Rename the GitHub repository to `matuto-ai/opencode-psst-connector`. Do not create a replacement repository. Update local remotes and verify the `verify` check and repository settings after the rename. Package metadata already points at this final name.
9
+ 3. Establish the new public npm package through npm's supported owner/account setup. Configure its own trusted publisher for GitHub owner `matuto-ai`, repository `opencode-psst-connector`, workflow `npm-publish.yml`, allowing direct `npm publish`. The old package's trusted publisher does not transfer. Validate new configuration within npm's two-day first-publish window. Account and publishing actions belong to the owner, not this code change.
10
+ 4. Publish the GitHub release tagged `v0.7.0` only when the new package's publishing access is ready. The workflow checks tag/version parity and tests before publishing. Never publish the same package-name/version twice: if initial owner setup already published the final `0.7.0` artifact, do not trigger a second publish of it; reconcile the artifact and workflow plan first.
11
+ 5. Verify the new npm artifact, provenance, exact version and a clean native install plus upgrade. Only then deprecate the old package with a message naming its replacement. Keep old versions downloadable; do not unpublish them.
12
+
13
+ ## Existing installation migration
14
+
15
+ After the new npm version is confirmed, list the native manager's existing package sources and remove only this connector's exact old source. Keep unrelated plugins and every private Psst state/credential directory. Add the new exact package version using the README command, then reload the native host. Do not load old and new copies together. An ordinary update of the old npm name does not switch names automatically.
16
+
17
+ The change is packaging only: native commands, tool names, identities, protocol, saved authorization and durable journals stay unchanged. Do not delete state or request fresh credentials just to change the package name. Validate reconnect/resume and absence of duplicate delivery with the preserved installation, separately from clean-install tests. Unit test success is not acceptance in the user's authenticated environment.
18
+
19
+ Keep native plugin ID `matuto.psst`, exports `.` and `./server`, commands and tool names unchanged. Preserve `PSST_OPENCODE_CONFIG_DIR` or the current `~/.config/opencode/psst` location and host-owned plugin storage. Use `opencode plugin list` to find the exact old source and `opencode plugin remove` for that source only; then add the new npm package. Do not erase the old private plugin journals or add a second plugin identity.
20
+
21
+ References: [npm deprecation](https://docs.npmjs.com/deprecating-and-undeprecating-packages-or-package-versions/) · [trusted publishing](https://docs.npmjs.com/trusted-publishers/).
@@ -0,0 +1,37 @@
1
+ # Native delivery contract
2
+
3
+ The supported OpenCode 2.2.5 plugin uses public `session.get`, `synthetic`,
4
+ `context`, hooks, events, storage and tool transforms. The root npm package exposes
5
+ one server plugin, `matuto.psst`; no classic or optional alternate entry is shipped.
6
+
7
+ - A durable private host/native-session binding and exclusive SQLite ownership
8
+ keep inboxes isolated. Same-session reopen retains the original broker identity.
9
+ - Exact payload, native ID and injection ID commit before the single
10
+ `synthetic({delivery:"queue",resume:true})` effect. Receipt validation compares
11
+ session, ID, type, delivery and complete payload, not HTTP success alone.
12
+ - At most one native item is pending. Native cancellation removes the host's
13
+ idempotency lookup, so uncertain delivery never automatically retries synthetic
14
+ admission. Read-only context proof can reconcile promotion; missing, cancelled,
15
+ compacted or mismatched evidence remains blocked rather than reinjected.
16
+ - Interrupt/failure pauses delivery. Subsequent native execution can reopen the
17
+ event pause. Move/archive/delete/unload fence the exact owner; resumable unload
18
+ does not terminally close its surviving broker identity.
19
+ - Losing the native event subscription, including a clean stream end, stops
20
+ transport, delivery and tools. Reload the plugin to resume the same durable
21
+ identities. In-flight admissions remain uncertain until exact native context
22
+ proves acceptance; stream recovery never resubmits them automatically.
23
+ - Peer messages are native synthetic machine-origin input, not human authority.
24
+ Agent/model selection, drafts and native permissions remain unchanged.
25
+ - Private operation IDs include the verified native call identity and original
26
+ assistant tool timestamp. An aged replay after journal GC fails closed.
27
+
28
+ The public-ID correction preserves approved filesystem state. Identity recovery
29
+ requires a single proven private anchor; old unapproved setup-owner records cannot
30
+ be read from another host plugin namespace and remain inert. See the README.
31
+
32
+ Official contracts: [plugin manager](https://opencode.ai/v2/docs/plugins/),
33
+ [server plugins](https://opencode.ai/v2/docs/build/plugins/server),
34
+ [synthetic admission](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/session/session.ts),
35
+ [host loader](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/plugin/src/host.ts).
36
+
37
+ Unreleased: automatic transport is additionally limited by the local TUI selection preference described in [active-conversation.md](active-conversation.md). This does not grant authentication or tool authority.
@@ -0,0 +1,21 @@
1
+ # Native prolonged-offline notices
2
+
3
+ With a broker advertising `policy.presence_events: true`, selected OpenCode conversations poll one bounded page (10 events, at most 64 KiB) per minute. Empty continuation pages are followed on later polls; each completed scan restarts without a cursor. Older brokers are not polled. The broker chooses the prolonged-absence grace and which affected sender may see an event; this connector does not infer a reply timeout or override presence policy.
4
+
5
+ Notices enter the same bound native conversation as a synthetic machine message, without changing agent/model, granting permissions or treating peer data as human instructions. They contain only validated recipient/event/message IDs, timestamps and pending-delivery uncertainty. A leased transport is not proof of process or model responsiveness. Status is an observation, not a guarantee that the recipient remains offline. Messages are never automatically resent, cancelled or closed.
6
+
7
+ The inbox's private, exclusively owned SQLite journal stores event ID, native ID and immutable payload before submission. Queue acceptance alone does **not** ACK an event. ACK requires an exact matching synthetic entry in native context, and means verified native presentation/admission into context, not human/model reading or a response. The original payload is compared on recovery, even if the broker's pending-message sample or observation timestamp changes.
8
+
9
+ Lost responses remain pinned; no potentially invoked native submission is repeated, including after restart. Exact context can reconcile it later. ACK failures can retry after durable local presentation. If a broker suppresses an obsolete event before submission, no notice is submitted; once queued it cannot be silently removed, so its text explicitly states that current state may have changed. Durably presented notices retry ACK independently of polling results, including after a lost applied ACK. An exact broker `404 message_not_found` retires an obsolete presented notice; other errors remain unresolved.
10
+
11
+ Cancellation, interrupted/failed execution, archive/revert, foreign binding, deselection and disposal fence this path just as they fence normal delivery. Explicit host-started execution can resume a paused conversation. No fallback prompt, notification subprocess or model-selection override is used.
12
+
13
+ Confirmed/obsolete tombstones are retained eight days, then pruned; the broker already deduplicates their ACK. Uncertain/unconfirmed rows are never automatically pruned. The journal is capped at 10,000 events; the existing database/free-space bounds also apply. At capacity, new notices stop with `native_presence_capacity`, rather than evicting uncertainty and risking duplicate presentation. No credential changes, destructive journal cleanup or automatic reset is performed.
14
+
15
+ ## Validation scope
16
+
17
+ `test/presence-notices.test.mjs` covers context-only ACK, restart, unknown native
18
+ outcome, ACK failure, immutable payload, capability gating, cancellation, selection,
19
+ bounded pagination, invalid metadata and durable-write failure. Native and transport
20
+ APIs are in-process fakes. These unit checks do not prove broker grace-period behavior,
21
+ authenticated model response or actual interactive TUI acceptance.
@@ -0,0 +1,19 @@
1
+ # Publishing the current OpenCode plugin
2
+
3
+ The root npm package is `@matuto-ai/opencode-psst-connector`; the next version
4
+ is **0.7.0**. Both `.` and `./server` resolve to the same native server object.
5
+ The official ./tui entry is included in that same package. No alternate package, Git source dependency or host installer is included.
6
+
7
+ The owner publishes the matching GitHub release after review/merge. The release
8
+ workflow verifies tag/package parity, runs syntax, unit and package file-list
9
+ checks, then packs once and publishes that archive to npm with provenance. Code publication and a green workflow are not proof that a
10
+ release exists. Do not instruct installation of an unpublished version.
11
+
12
+ After npm publication is verified, the supported command is:
13
+
14
+ ```sh
15
+ opencode plugin add @matuto-ai/opencode-psst-connector@0.7.0
16
+ ```
17
+
18
+ Keep current 2.2.5 native-runtime acceptance separate from the unit and package file-list checks.
19
+ Do not use a Git URL, Git shorthand, source tarball workaround or policy bypass.
@@ -0,0 +1,7 @@
1
+ fbf0fa6096996cb9bc3f928e55f3eca496420880a03460e993dda3417b32ad88 reference/inbox.mjs
2
+ e8ab94e6de6b5afb17b1dded9839fefa26481fef87ed715b041c09d41dbc93d5 reference/client.mjs
3
+ 8fdaa105b2e81aeaabb628131671989f951148f602844bca6819af827269f2c3 reference/runtime.mjs
4
+ e1836d5c33ed80a9e15c1ca9d07888c9db3caf40aeb2a115c813f74a45077c6e reference/tools.mjs
5
+ 88c61c995f9285ef7e22c401d7f0cf50fefc7dd6c76ae79c6bcc94097a33a5c3 reference/mcp-stdio.mjs
6
+ 85b0d0736c5a439f7c66697c3b1ce6cee998cda744dd3169c856ebf2b82bd2a7 reference/errors.mjs
7
+ 651f7d99987b58621070c5f24107ac621a09e325880506bc2e73cfe998d5b6aa src/protocol.mjs
@@ -0,0 +1,96 @@
1
+ # URL-only setup and persistence
2
+
3
+ ## Install, configure, use
4
+
5
+ The scoped 0.7.0 package is an unreleased source candidate. After publication,
6
+ install it using the single native plugin-manager command in the README. The
7
+ same package loads its server and TUI entries; do not install a second wrapper.
8
+
9
+ 1. Select a conversation in the local OpenCode 2.2.5 TUI. Run `/psst-setup` and
10
+ enter only the HTTPS broker origin.
11
+ 2. Open the displayed broker `/admin` page. Its form login and exact-request
12
+ approval are human actions. Pending setup is not a connection.
13
+ 3. Once approved, use the existing send/client tools. Incoming peer data enters
14
+ the native synthetic queue automatically for the selected conversation.
15
+ `/psst-status` reports status; `/psst-cancel-setup` pauses a pending local flow.
16
+
17
+ Missing configuration never starts enrollment. Pending retries retain the same
18
+ candidate and request, including uncertain network results. Private candidate
19
+ persistence precedes the first enrollment POST. Restart only reads or polls an
20
+ already durable request; it cannot generate a request or rotate credentials.
21
+ If the server starts before the owner TUI, selecting that exact owner later
22
+ resumes GET-only polling. Fatal poll, attachment or selection failures pause the
23
+ flow; selection changes alone cannot retry them. Explicit setup is required.
24
+
25
+ ## Replacement and recovery
26
+
27
+ The native dialog asks before replacing a terminal request and fences that
28
+ confirmation to its exact request ID. Model tools cannot supply this confirmation.
29
+ The prior authorization remains selected and usable until the approved candidate
30
+ has attached and the original selection revision still matches. Cancellation,
31
+ late results, failed attachment or an intervening selection cannot promote it.
32
+ Retrying an approved but uncommitted candidate reuses its receipt and original
33
+ selection baseline rather than silently replacing a newer choice.
34
+
35
+ An explicit replacement creates a new credential-bound identity. It retains the
36
+ old authorization's local inbox and history, but does not move unread messages or
37
+ uncertain sends across authorities. Ordinary plugin upgrade or restart, without
38
+ replacement, retains the original credential, mailbox, addresses and pending data.
39
+
40
+ Denied, expired, paused and pending states are restored from private records.
41
+ An expired but fully validated receipt can be inspected only for exact identity
42
+ recovery, allowing the native setup UI to offer replacement. It is never accepted
43
+ for connection or selection. Wrong token hashes, origin/type mismatches, missing
44
+ receipts and corrupt files remain fatal. Broker rejection is reported separately
45
+ from validated expiry and from protocol/retry failures.
46
+
47
+ ## Paths for disposable runtimes
48
+
49
+ The shared installation directory is, in precedence order:
50
+
51
+ - `PSST_OPENCODE_CONFIG_DIR`;
52
+ - `$OPENCODE_CONFIG_DIR/psst`;
53
+ - `$XDG_CONFIG_HOME/opencode/psst`, default `~/.config/opencode/psst`.
54
+
55
+ Persist the complete directory, including selected and pending records, approval
56
+ receipts, credentials, binding manifests and inbox journals. Keep the same absolute
57
+ path and UID, directory permissions 0700 and file permissions 0600. Do not mount a
58
+ subdirectory that omits receipts or owner locks. TUI and server must share this
59
+ local private installation directory for active-conversation signaling.
60
+
61
+ Installation sharing does not recreate OpenCode conversation history. To reopen
62
+ an existing native conversation, also retain that host's OpenCode configuration,
63
+ native data and state roots under the same project/workspace location. The pinned
64
+ host uses `XDG_CONFIG_HOME/opencode`, `XDG_DATA_HOME/opencode` and
65
+ `XDG_STATE_HOME/opencode` (default `~/.config/opencode`, `~/.local/share/opencode`
66
+ and `~/.local/state/opencode`). An `OPENCODE_CONFIG_DIR` override must also remain
67
+ available. Keep native host data separate for intentionally independent hosts;
68
+ share the Psst installation, not their entire OpenCode database.
69
+
70
+ A new host receives a fresh anchor only after native lookup proves all relevant
71
+ previous sessions absent, or the persisted bindings belong elsewhere. Matching
72
+ native history recovers the existing anchor. Unknown lookups, changed live
73
+ bindings, multiple matching anchors or corruption do not permit a reset. Classic
74
+ journals without a current native binding are retained and require explicit
75
+ migration work; they are never silently adopted.
76
+
77
+ ## Test scope and remaining acceptance
78
+
79
+ The maintained suite covers connector behavior with in-process native/transport
80
+ fakes and temporary private storage: exact session bindings, setup cancellation,
81
+ late results, selection CAS, attachment rollback, malformed state, expiry handling,
82
+ identity separation, delivery receipts and uncertainty recovery.
83
+
84
+ Real broker/server, OpenCode TUI, package-install, container recreation and
85
+ end-to-end harnesses have been removed. There are no opt-in heavy test stages.
86
+ Historical results remain in the repository's pull-request history; they are not
87
+ current CI checks or proof of the current environment.
88
+
89
+ Unit success does not establish authenticated provider execution, active model
90
+ toolchain/abort behavior, release availability or native-host compatibility.
91
+ For issue #8, the owner explicitly accepted source implementation, focused unit
92
+ tests, independent review and CI as the current acceptance scope on 2026-11-04.
93
+ Real-environment and integrated native/provider/container tests are deferred to a
94
+ later run on the owner's machine, not current acceptance gates. Package
95
+ publication is handled separately by the owner. These changes do not provision
96
+ credentials or deploy anything.
@@ -0,0 +1,55 @@
1
+ # Setup context and focus recovery
2
+
3
+ Keep unrelated chats open. `/psst-setup` and `/psst-status` use the selected
4
+ conversation and location captured by that TUI, not the number of open chats.
5
+ Another TUI's selection or blur does not cancel this TUI's command.
6
+
7
+ The supported OpenCode 2.2.5 boundary supplies renderer blur and selection
8
+ events, but blur does not prove whether the user went to a browser, another
9
+ terminal, or another native dialog. The connector therefore suspends the current
10
+ action on blur instead of trusting a confirmation returned after losing focus.
11
+ It also fences actual conversation/location changes, including away-and-back.
12
+
13
+ - If you need a URL from another window, copy it first. Return to the intended
14
+ chat, dismiss the old dialog, and reopen `/psst-setup`.
15
+ - After a request is saved, opening the broker `/admin` page is expected. Return
16
+ to the original chat and run `/psst-status`; any interrupted call does not erase
17
+ the durable request. If paused, `/psst-setup` with the same URL reconciles/resumes
18
+ that request. A running owner retry returns its existing request without a
19
+ second enrollment call. Never replay an old replacement confirmation.
20
+ - `/psst-reauthorize` uses the same context guards and saved candidate. Its native
21
+ confirmation shows the broker origin and is bound to both the previous request
22
+ and selected-installation revision. Reconnect is automatic reuse of the saved
23
+ credential; reauthorization is a new human-approved candidate; revoke remains
24
+ a separate human action in Psst admin. Neither an agent nor a peer message may
25
+ silently substitute one action for another.
26
+ - `request=not_submitted` means this action never called setup. It does not mean
27
+ there was no earlier request. `saved` means a durable request identity exists,
28
+ not proof that the broker has received or approved it. `unknown` means a reply
29
+ was lost or persistence could not be established; inspect status before retry.
30
+ - Setup owned elsewhere must be resumed in its original chat. Pausing a pending
31
+ request does not authorize a different conversation to adopt it. Existing
32
+ explicit replacement of a terminal authorization remains available with a
33
+ fresh confirmation bound to its exact request.
34
+
35
+ Warnings include only fixed phase/cause/request-state enums. Record those fields,
36
+ host version, and whether the two chats were views of one TUI or independent
37
+ TUIs/servers. Do not include messages, tokens, private paths or credentials. Do
38
+ not delete locks, reset identity, terminate other CLIs or change permission policy.
39
+
40
+ ## Evidence and limits for issue #27
41
+
42
+ Native-boundary unit doubles reproduce cancellation when this renderer emits
43
+ blur during URL entry, preflight, replacement confirmation, setup, status or
44
+ pause. A second independent TUI sharing the project can change its selection
45
+ and emit blur without interrupting the stable first TUI. These tests establish
46
+ connector event handling, not the original incident's host topology or trigger.
47
+ The report that closing another chat helped remains a workaround observation,
48
+ not evidence that two chats cause cancellation. No real-host reproduction,
49
+ container/provider suite, live setup change or release is claimed.
50
+
51
+ Regressions also cover selection change after receipt persistence, lost setup
52
+ reply after replacement preflight, same-owner retry while polling, cancellation
53
+ before/after persistence, durable read-only status and cross-chat ownership.
54
+ The existing selection CAS, credentials, journal, native permission, active-chat
55
+ routing and model/agent selection paths remain in place.