@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
@@ -0,0 +1,420 @@
1
+ {
2
+ "name": "@matuto-ai/opencode-broker-connector-v2",
3
+ "version": "0.4.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@matuto-ai/opencode-broker-connector-v2",
9
+ "version": "0.4.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "@matuto-ai/opencode-broker-connector": "git+https://github.com/matuto-ai/opencode-broker-connector.git#81bab6924d0f7193b8b3f32318ea4cbc1d23a17f"
13
+ }
14
+ },
15
+ "node_modules/@ai-sdk/provider": {
16
+ "version": "3.0.8",
17
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz",
18
+ "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==",
19
+ "license": "Apache-2.0",
20
+ "dependencies": {
21
+ "json-schema": "^0.4.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=18"
25
+ }
26
+ },
27
+ "node_modules/@matuto-ai/opencode-broker-connector": {
28
+ "version": "0.4.0",
29
+ "resolved": "git+https://github.com/matuto-ai/opencode-broker-connector.git#81bab6924d0f7193b8b3f32318ea4cbc1d23a17f",
30
+ "integrity": "sha512-T3G2NRNRT2QpXjmkxqlMPI1ew/mc4jsXVccnMWVbv4IAvGz/lKOgeRLBF6CDAChBqIiON/j40gEgiFQ/B0qqaQ==",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@opencode-ai/plugin": "1.19.4"
34
+ },
35
+ "bin": {
36
+ "psst-opencode": "bin/doctor.mjs"
37
+ },
38
+ "engines": {
39
+ "node": ">=24.19.0 <25"
40
+ }
41
+ },
42
+ "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
43
+ "version": "3.0.4",
44
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz",
45
+ "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==",
46
+ "cpu": [
47
+ "arm64"
48
+ ],
49
+ "license": "MIT",
50
+ "optional": true,
51
+ "os": [
52
+ "darwin"
53
+ ]
54
+ },
55
+ "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
56
+ "version": "3.0.4",
57
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz",
58
+ "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==",
59
+ "cpu": [
60
+ "x64"
61
+ ],
62
+ "license": "MIT",
63
+ "optional": true,
64
+ "os": [
65
+ "darwin"
66
+ ]
67
+ },
68
+ "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
69
+ "version": "3.0.4",
70
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz",
71
+ "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==",
72
+ "cpu": [
73
+ "arm"
74
+ ],
75
+ "license": "MIT",
76
+ "optional": true,
77
+ "os": [
78
+ "linux"
79
+ ]
80
+ },
81
+ "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
82
+ "version": "3.0.4",
83
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz",
84
+ "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==",
85
+ "cpu": [
86
+ "arm64"
87
+ ],
88
+ "license": "MIT",
89
+ "optional": true,
90
+ "os": [
91
+ "linux"
92
+ ]
93
+ },
94
+ "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
95
+ "version": "3.0.4",
96
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz",
97
+ "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==",
98
+ "cpu": [
99
+ "x64"
100
+ ],
101
+ "license": "MIT",
102
+ "optional": true,
103
+ "os": [
104
+ "linux"
105
+ ]
106
+ },
107
+ "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
108
+ "version": "3.0.4",
109
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz",
110
+ "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==",
111
+ "cpu": [
112
+ "x64"
113
+ ],
114
+ "license": "MIT",
115
+ "optional": true,
116
+ "os": [
117
+ "win32"
118
+ ]
119
+ },
120
+ "node_modules/@opencode-ai/plugin": {
121
+ "version": "1.19.4",
122
+ "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.19.4.tgz",
123
+ "integrity": "sha512-VN71q/OmfggxIhiDbuzYDYmZcQMsNyVSw1o91e7LD6WUn/9jx4usI9ZpoPbC5p/Prpo/Umzpd+38+EuLoKRr+A==",
124
+ "license": "MIT",
125
+ "dependencies": {
126
+ "@ai-sdk/provider": "3.0.8",
127
+ "@opencode-ai/sdk": "1.19.4",
128
+ "effect": "4.0.0-beta.83",
129
+ "zod": "4.1.8"
130
+ },
131
+ "peerDependencies": {
132
+ "@opentui/core": ">=0.4.5",
133
+ "@opentui/keymap": ">=0.4.5",
134
+ "@opentui/solid": ">=0.4.5"
135
+ },
136
+ "peerDependenciesMeta": {
137
+ "@opentui/core": {
138
+ "optional": true
139
+ },
140
+ "@opentui/keymap": {
141
+ "optional": true
142
+ },
143
+ "@opentui/solid": {
144
+ "optional": true
145
+ }
146
+ }
147
+ },
148
+ "node_modules/@opencode-ai/sdk": {
149
+ "version": "1.19.4",
150
+ "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.19.4.tgz",
151
+ "integrity": "sha512-3Wuc4t8gEuWKHbX1Na9oDK6PgOveid70l4Ks5RCjRIo2RcP8FQN3NS6Mg3XWc2+WiUfpfrmvBWlh5u1zeQfDQw==",
152
+ "license": "MIT",
153
+ "dependencies": {
154
+ "cross-spawn": "7.0.6"
155
+ }
156
+ },
157
+ "node_modules/@standard-schema/spec": {
158
+ "version": "1.1.0",
159
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
160
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
161
+ "license": "MIT"
162
+ },
163
+ "node_modules/cross-spawn": {
164
+ "version": "7.0.6",
165
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
166
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
167
+ "license": "MIT",
168
+ "dependencies": {
169
+ "path-key": "^3.1.0",
170
+ "shebang-command": "^2.0.0",
171
+ "which": "^2.0.1"
172
+ },
173
+ "engines": {
174
+ "node": ">= 8"
175
+ }
176
+ },
177
+ "node_modules/detect-libc": {
178
+ "version": "2.1.2",
179
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
180
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
181
+ "license": "Apache-2.0",
182
+ "optional": true,
183
+ "engines": {
184
+ "node": ">=8"
185
+ }
186
+ },
187
+ "node_modules/effect": {
188
+ "version": "4.0.0-beta.83",
189
+ "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.83.tgz",
190
+ "integrity": "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w==",
191
+ "license": "MIT",
192
+ "dependencies": {
193
+ "@standard-schema/spec": "^1.1.0",
194
+ "fast-check": "^4.8.0",
195
+ "find-my-way-ts": "^0.1.6",
196
+ "ini": "^7.0.0",
197
+ "kubernetes-types": "^1.30.0",
198
+ "msgpackr": "^2.0.1",
199
+ "multipasta": "^0.2.7",
200
+ "toml": "^4.1.1",
201
+ "uuid": "^14.0.0",
202
+ "yaml": "^2.9.0"
203
+ }
204
+ },
205
+ "node_modules/fast-check": {
206
+ "version": "4.10.2",
207
+ "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.10.2.tgz",
208
+ "integrity": "sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==",
209
+ "funding": [
210
+ {
211
+ "type": "individual",
212
+ "url": "https://github.com/sponsors/dubzzz"
213
+ },
214
+ {
215
+ "type": "opencollective",
216
+ "url": "https://opencollective.com/fast-check"
217
+ }
218
+ ],
219
+ "license": "MIT",
220
+ "dependencies": {
221
+ "pure-rand": "^8.0.0"
222
+ },
223
+ "engines": {
224
+ "node": ">=12.17.0"
225
+ }
226
+ },
227
+ "node_modules/find-my-way-ts": {
228
+ "version": "0.1.6",
229
+ "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz",
230
+ "integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==",
231
+ "license": "MIT"
232
+ },
233
+ "node_modules/ini": {
234
+ "version": "7.0.0",
235
+ "resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz",
236
+ "integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==",
237
+ "license": "ISC",
238
+ "engines": {
239
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
240
+ }
241
+ },
242
+ "node_modules/isexe": {
243
+ "version": "2.0.0",
244
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
245
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
246
+ "license": "ISC"
247
+ },
248
+ "node_modules/json-schema": {
249
+ "version": "0.4.0",
250
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
251
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
252
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
253
+ },
254
+ "node_modules/kubernetes-types": {
255
+ "version": "1.30.0",
256
+ "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz",
257
+ "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==",
258
+ "license": "Apache-2.0"
259
+ },
260
+ "node_modules/msgpackr": {
261
+ "version": "2.1.3",
262
+ "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.1.3.tgz",
263
+ "integrity": "sha512-C8pQ8SJjwH9C9JiCe5asXWvJC3melOxMuBj7vpTY95096ay6r1wPXG0XN56wMaRpzRPE3hdf4ufukia/j+beKQ==",
264
+ "license": "MIT",
265
+ "optionalDependencies": {
266
+ "msgpackr-extract": "^3.0.4"
267
+ }
268
+ },
269
+ "node_modules/msgpackr-extract": {
270
+ "version": "3.0.4",
271
+ "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz",
272
+ "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==",
273
+ "hasInstallScript": true,
274
+ "license": "MIT",
275
+ "optional": true,
276
+ "dependencies": {
277
+ "node-gyp-build-optional-packages": "5.2.2"
278
+ },
279
+ "bin": {
280
+ "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
281
+ },
282
+ "optionalDependencies": {
283
+ "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4",
284
+ "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4",
285
+ "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4",
286
+ "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4",
287
+ "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4",
288
+ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4"
289
+ }
290
+ },
291
+ "node_modules/multipasta": {
292
+ "version": "0.2.9",
293
+ "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.9.tgz",
294
+ "integrity": "sha512-kAovZHgxryqb0xTFz7OTCPxbas8UBTTwCDhfrlaNecp1/z6xcMfp5PVhDxsW7jONHyDF10/ccp30MxA2+66GJw==",
295
+ "license": "MIT"
296
+ },
297
+ "node_modules/node-gyp-build-optional-packages": {
298
+ "version": "5.2.2",
299
+ "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
300
+ "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
301
+ "license": "MIT",
302
+ "optional": true,
303
+ "dependencies": {
304
+ "detect-libc": "^2.0.1"
305
+ },
306
+ "bin": {
307
+ "node-gyp-build-optional-packages": "bin.js",
308
+ "node-gyp-build-optional-packages-optional": "optional.js",
309
+ "node-gyp-build-optional-packages-test": "build-test.js"
310
+ }
311
+ },
312
+ "node_modules/path-key": {
313
+ "version": "3.1.1",
314
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
315
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
316
+ "license": "MIT",
317
+ "engines": {
318
+ "node": ">=8"
319
+ }
320
+ },
321
+ "node_modules/pure-rand": {
322
+ "version": "8.4.2",
323
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz",
324
+ "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==",
325
+ "funding": [
326
+ {
327
+ "type": "individual",
328
+ "url": "https://github.com/sponsors/dubzzz"
329
+ },
330
+ {
331
+ "type": "opencollective",
332
+ "url": "https://opencollective.com/fast-check"
333
+ }
334
+ ],
335
+ "license": "MIT"
336
+ },
337
+ "node_modules/shebang-command": {
338
+ "version": "2.0.0",
339
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
340
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
341
+ "license": "MIT",
342
+ "dependencies": {
343
+ "shebang-regex": "^3.0.0"
344
+ },
345
+ "engines": {
346
+ "node": ">=8"
347
+ }
348
+ },
349
+ "node_modules/shebang-regex": {
350
+ "version": "3.0.0",
351
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
352
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
353
+ "license": "MIT",
354
+ "engines": {
355
+ "node": ">=8"
356
+ }
357
+ },
358
+ "node_modules/toml": {
359
+ "version": "4.3.0",
360
+ "resolved": "https://registry.npmjs.org/toml/-/toml-4.3.0.tgz",
361
+ "integrity": "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==",
362
+ "license": "MIT",
363
+ "engines": {
364
+ "node": ">=20"
365
+ }
366
+ },
367
+ "node_modules/uuid": {
368
+ "version": "14.0.3",
369
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz",
370
+ "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==",
371
+ "funding": [
372
+ "https://github.com/sponsors/broofa",
373
+ "https://github.com/sponsors/ctavan"
374
+ ],
375
+ "license": "MIT",
376
+ "bin": {
377
+ "uuid": "dist-node/bin/uuid"
378
+ }
379
+ },
380
+ "node_modules/which": {
381
+ "version": "2.0.2",
382
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
383
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
384
+ "license": "ISC",
385
+ "dependencies": {
386
+ "isexe": "^2.0.0"
387
+ },
388
+ "bin": {
389
+ "node-which": "bin/node-which"
390
+ },
391
+ "engines": {
392
+ "node": ">= 8"
393
+ }
394
+ },
395
+ "node_modules/yaml": {
396
+ "version": "2.9.1",
397
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
398
+ "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
399
+ "license": "ISC",
400
+ "bin": {
401
+ "yaml": "bin.mjs"
402
+ },
403
+ "engines": {
404
+ "node": ">= 14.6"
405
+ },
406
+ "funding": {
407
+ "url": "https://github.com/sponsors/eemeli"
408
+ }
409
+ },
410
+ "node_modules/zod": {
411
+ "version": "4.1.8",
412
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
413
+ "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
414
+ "license": "MIT",
415
+ "funding": {
416
+ "url": "https://github.com/sponsors/colinhacks"
417
+ }
418
+ }
419
+ }
420
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@matuto-ai/opencode-broker-connector-v2",
3
+ "version": "0.4.0",
4
+ "description": "Native OpenCode V2 server-only Psst connector candidate",
5
+ "type": "module",
6
+ "private": true,
7
+ "license": "MIT",
8
+ "exports": { ".": "./index.mjs" },
9
+ "files": ["index.mjs", "LICENSE", "README.md"],
10
+ "dependencies": {
11
+ "@matuto-ai/opencode-broker-connector": "git+https://github.com/matuto-ai/opencode-broker-connector.git#81bab6924d0f7193b8b3f32318ea4cbc1d23a17f"
12
+ }
13
+ }
@@ -1,5 +1,12 @@
1
- # Vendored runtime
1
+ # Pinned Psst runtime candidate
2
2
 
3
- Source: https://github.com/matuto-ai/psst/tree/ba2219cd87a1feac23e01b23d2c2b3d3bdd65359
4
- Seven runtime/protocol files are copied byte-for-byte and verified by src/reference-pin.mjs before import. The MIT license is retained. Connector cooperative read acknowledgments are implemented separately in src/cooperative-inbox.mjs, not by changing the pinned runtime or inventing native receipts.
5
- Test-only broker fixtures are copied from the same commit into test/broker-fixture; they are excluded from the npm package. No broker daemon is bundled in the installable runtime.
3
+ Seven runtime/protocol files are copied byte-for-byte from Psst candidate commit
4
+ `2de9cc3758b9fc593f5afca4fc501fd013d416ec` and verified by `src/reference-pin.mjs`. This is a local, unreleased
5
+ candidate, not evidence of registry publication or deployed broker support.
6
+ Source repository: https://github.com/matuto-ai/psst . MIT license retained.
7
+
8
+ The new outgoing history/content/resend/cancel APIs require the matching broker
9
+ candidate. Existing protocol-v1 send/receive remains compatible. Older brokers
10
+ return an explicit unsupported HTTP error for new routes; no local success is fabricated.
11
+ Approval/enrollment helpers and test-only broker fixtures retain their separate
12
+ original pins. Connector display and read acknowledgment changes live in `src/`.
@@ -8,7 +8,7 @@ import { ApiError, canonical, validateMetadata, validateRegistration, validateMe
8
8
  import { LocalError } from './errors.mjs';
9
9
 
10
10
  const ALLOWED_ERROR_CODES = new Set(['invalid_request', 'unauthenticated', 'forbidden', 'recipient_not_found',
11
- 'message_not_found', 'identity_conflict', 'stale_lease_epoch', 'idempotency_conflict', 'metadata_conflict',
11
+ 'message_not_found', 'history_changed', 'content_unavailable', 'retry_window_elapsed', 'identity_conflict', 'stale_lease_epoch', 'idempotency_conflict', 'metadata_conflict',
12
12
  'claim_conflict', 'session_closed', 'message_expired', 'payload_too_large',
13
13
  'incompatible_receiver_transport', 'receiving_disabled', 'rate_limited', 'mailbox_full',
14
14
  'storage_unavailable', 'not_ready']);
@@ -95,12 +95,13 @@ export class BrokerClient {
95
95
  return token;
96
96
  }
97
97
 
98
- async request(method, path, { body, key, session = true, signal, idempotent = false, deadline = Infinity } = {}) {
98
+ async request(method, path, { body, key, session = true, signal, idempotent = false, deadline = Infinity, onError = () => {} } = {}) {
99
99
  if (!/^\/v1\/[A-Za-z0-9_/?=&.%+~-]+$/.test(path)) throw new LocalError('invalid_configuration');
100
100
  for (let attempt = 0; ; attempt++) {
101
101
  if (this.now() >= deadline) throw new LocalError('retry_window_elapsed', { effect: 'unknown' });
102
102
  try { return await this.requestOnce(method, path, { body, key, session, signal, timeoutMs: Math.min(this.requestTimeoutMs, deadline - this.now()) }); }
103
103
  catch (error) {
104
+ onError(error);
104
105
  if (signal?.aborted) throw new LocalError('request_aborted', { effect: 'unknown' });
105
106
  const retryable = error instanceof BrokerError ? error.retryable
106
107
  : error instanceof LocalError && error.code === 'request_failed';
@@ -155,6 +156,7 @@ export class BrokerClient {
155
156
  const retryAfter = /^\d+$/.test(String(response.headers['retry-after'] ?? '')) ? Math.min(3600, Number(response.headers['retry-after'])) : 0;
156
157
  finish(new BrokerError(status, value, retryAfter)); return;
157
158
  }
159
+ if(['true','false'].includes(response.headers['idempotency-replayed']))value.acceptance_replayed=response.headers['idempotency-replayed']==='true';
158
160
  const timestamp = value?.server_time;
159
161
  // Registration/send/completion retries may return their original
160
162
  // stored result. Historical server_time is not a current clock sample.
@@ -269,23 +271,96 @@ export class BrokerClient {
269
271
  return metadata;
270
272
  }
271
273
 
274
+ operationOptions(options = {}) {
275
+ const {operationStartedAt,...rest}=options,seconds=positiveSeconds(this.policy.send_retry_seconds,86400);
276
+ if(operationStartedAt!==undefined&&(!Number.isSafeInteger(operationStartedAt)||operationStartedAt>this.now()+300000||this.now()-operationStartedAt>=seconds*1000))throw new LocalError('retry_window_elapsed',{effect:'unknown'});
277
+ return {retryWindowSeconds:seconds,issuedAt:Math.trunc((operationStartedAt??this.now())+this.clockOffset),...(operationStartedAt===undefined?{}:{retryUntil:operationStartedAt+seconds*1000}),...rest};
278
+ }
279
+
272
280
  prepareSend(request, options = {}) {
273
- return this.inbox.prepareSend(request, { retryWindowSeconds: positiveSeconds(this.policy.send_retry_seconds, 86400), ...options });
281
+ return this.inbox.prepareSend(request,this.operationOptions(options));
282
+ }
283
+
284
+ async sendPrepared(key, options = {}) { return this.outgoingRequest(key,'send',options); }
285
+
286
+ prepareResend(messageId, input = {}, options = {}) {
287
+ return this.inbox.prepareResend(messageId,input,this.operationOptions(options));
288
+ }
289
+
290
+ async resendPrepared(key, options = {}) { return this.outgoingRequest(key,'resend',options); }
291
+
292
+ async outgoingRequest(key, kind, options) {
293
+ // Shared by clients using this exact journal: concurrent invocations cannot
294
+ // race a definite rejection against another still-running network attempt.
295
+ const inflight=this.inbox.outgoingRequests??=new Map();
296
+ if(inflight.has(key))return inflight.get(key);
297
+ const work=this.transmitOutgoing(key,kind,options);inflight.set(key,work);
298
+ try{return await work;}finally{inflight.delete(key);}
299
+ }
300
+
301
+ async transmitOutgoing(key, kind, { signal } = {}) {
302
+ const operation=this.inbox.outgoing(key);
303
+ if(!operation||operation.kind!==kind)throw new LocalError('invalid_configuration');
304
+ if(operation.result)return {...operation.result,acceptance_replayed:true,send_key:key};
305
+ if(operation.failure)throw new BrokerError(operation.failure.status,operation.failure.value);
306
+ let priorUnknown=!!operation.uncertain;
307
+ try{this.inbox.markOutgoingAttempt(key);}catch(error){
308
+ if(priorUnknown)throw new LocalError('storage_unavailable',{effect:'unknown'});
309
+ throw error; // a wholly new operation has not reached the network
310
+ } // crash before/after HTTP stays uncertain
311
+ const body=kind==='send'?operation.request:Object.fromEntries(Object.entries(operation.request).filter(([k])=>k!=='message_id'));
312
+ const path=kind==='send'?'/v1/messages':`/v1/messages/${validateUuid(operation.request.message_id)}/resend`;
313
+ let result;
314
+ try{
315
+ result=await this.request('POST',path,{body,key,idempotent:true,deadline:operation.retry_until,signal,onError:error=>{if(error.effect!=='not_applied')priorUnknown=true;}});
316
+ }catch(error){
317
+ // A rejection of a later retry is not evidence that an earlier unknown
318
+ // attempt was rejected. Only a first, wholly known-not-applied operation
319
+ // may become a durable terminal failure; other intents remain independent.
320
+ if(!priorUnknown&&error instanceof BrokerError&&error.effect==='not_applied'&&!error.retryable)this.inbox.recordSendFailure(key,{status:error.status,value:error.toJSON()});
321
+ throw error;
322
+ }
323
+ try{validateUuid(result?.message_id);}catch{throw new LocalError('invalid_response',{effect:'unknown'});}
324
+ try{
325
+ this.inbox.recordSendResult(key,result);
326
+ if(kind==='send'&&operation.request.metadata_patch)this.inbox.setState('metadata',{...this.inbox.getState('metadata'),...operation.request.metadata_patch});
327
+ }catch{
328
+ // The broker already accepted this operation. A failed local result or
329
+ // metadata commit describes only the journal, never remote non-delivery.
330
+ throw new LocalError('storage_unavailable',{effect:'unknown'});
331
+ }
332
+ return {...result,send_key:key};
333
+ }
334
+
335
+ async cancelMessage(messageId, { signal } = {}) {
336
+ return this.request('POST',`/v1/messages/${validateUuid(messageId)}/cancel`,{body:{},idempotent:true,signal});
274
337
  }
275
338
 
276
- async sendPrepared(key, { signal } = {}) {
277
- const operation = this.inbox.outgoing(key);
278
- if (!operation) throw new LocalError('invalid_configuration');
279
- if (operation.result) return { ...operation.result, send_key: key };
280
- const result = await this.request('POST', '/v1/messages', { body: operation.request, key, idempotent: true,
281
- deadline: operation.retry_until, signal });
282
- try { validateUuid(result?.message_id); } catch { throw new LocalError('invalid_response', { effect: 'unknown' }); }
283
- this.inbox.recordSendResult(key, result);
284
- if (operation.request.metadata_patch) {
285
- this.inbox.setState('metadata', { ...this.inbox.getState('metadata'), ...operation.request.metadata_patch });
286
- // Sender revision is not guessed; reconcile from owned state before a future CAS mutation.
339
+ async recoverOutgoing({ limit = 10, signal } = {}) {
340
+ if(!Number.isSafeInteger(limit)||limit<1||limit>100)throw new LocalError('invalid_configuration');
341
+ const records=[];
342
+ const {operations,expiredCount}=this.inbox.outgoingRecovery(limit,this.now());
343
+ for(const operation of operations){
344
+ try{
345
+ const pending=this.inbox.outgoing(operation.key);
346
+ const value=await (pending.kind==='resend'?this.resendPrepared(operation.key,{signal}):this.sendPrepared(operation.key,{signal}));
347
+ const {send_key,...result}=value;records.push({operation:pending.kind,result});
348
+ }catch(error){records.push({error:error instanceof LocalError||error instanceof BrokerError?error.toJSON().error:new LocalError('request_failed',{effect:'unknown'}).toJSON().error});}
287
349
  }
288
- return { ...result, send_key: key };
350
+ if(expiredCount)records.push({...new LocalError('retry_window_elapsed',{effect:'unknown'}).toJSON(),count:expiredCount});
351
+ return records;
352
+ }
353
+
354
+ async sentHistory(filters = {}, { signal } = {}) {
355
+ const allowed=['recipient_id','status','accepted_after','accepted_before','limit','cursor'];
356
+ if(!filters||typeof filters!=='object'||Array.isArray(filters)||Object.keys(filters).some(key=>!allowed.includes(key))) throw new LocalError('invalid_configuration');
357
+ const query=new URLSearchParams();
358
+ for(const [key,value] of Object.entries(filters)) query.set(key,String(value));
359
+ return this.request('GET',`/v1/messages${query.size?`?${query}`:''}`,{idempotent:true,signal});
360
+ }
361
+
362
+ async messageContent(messageId, { signal } = {}) {
363
+ return this.request('GET',`/v1/messages/${validateUuid(messageId)}/content`,{idempotent:true,signal});
289
364
  }
290
365
 
291
366
  async messageStatus(messageId, { signal } = {}) {