@wacando/agent 0.0.0-stage → 2.0.1

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 (62) hide show
  1. package/.agents/plugins/marketplace.json +10 -0
  2. package/.claude-plugin/marketplace.json +12 -0
  3. package/.cursor-plugin/marketplace.json +12 -0
  4. package/README.md +166 -2
  5. package/STATUS.md +121 -0
  6. package/THIRD-PARTY-NOTICES.txt +1928 -0
  7. package/adapters/README.md +12 -0
  8. package/adapters/claude/mcp.mjs +7 -0
  9. package/adapters/codex/mcp.mjs +7 -0
  10. package/adapters/cursor/mcp.mjs +7 -0
  11. package/bin/wacando-agent.mjs +243 -0
  12. package/docs/AGENT_TOOLS.md +147 -0
  13. package/docs/CURSOR_IDENTITY_RESUME.md +579 -0
  14. package/docs/MCP-AUDIT.md +355 -0
  15. package/docs/MCP-CONTRACT.md +138 -0
  16. package/docs/MIGRATION-2.0.md +36 -0
  17. package/docs/OMEMO_PREKEY_INTEGRATION.md +125 -0
  18. package/docs/ONBOARDING-VALIDATION.md +88 -0
  19. package/docs/ONBOARDING.md +212 -0
  20. package/docs/ONBOARDING_PLUGIN_CONTRACT.md +156 -0
  21. package/docs/PLUGIN_HANDOFF_FOLLOWUP.md +178 -0
  22. package/docs/PUBLIC_ONBOARDING_READINESS.md +156 -0
  23. package/docs/RELEASE.md +52 -0
  24. package/docs/WACANDO_PACKAGES.md +26 -0
  25. package/package.json +66 -4
  26. package/packages/core/limits.mjs +24 -0
  27. package/packages/core/store.mjs +92 -0
  28. package/packages/core/validation.mjs +25 -0
  29. package/packages/mcp/agent-tools.mjs +82 -0
  30. package/packages/mcp/server.mjs +161 -0
  31. package/packages/mcp/stdio.mjs +42 -0
  32. package/packages/onboarding/network.mjs +60 -0
  33. package/packages/onboarding/service.mjs +235 -0
  34. package/packages/onboarding/storage.mjs +99 -0
  35. package/packages/onboarding/version.mjs +2 -0
  36. package/packages/runtime/conversations.mjs +433 -0
  37. package/packages/runtime/device.mjs +34 -0
  38. package/packages/runtime/index.mjs +8 -0
  39. package/packages/runtime/pairing.mjs +110 -0
  40. package/packages/runtime/recovery.mjs +49 -0
  41. package/packages/runtime/runtime.mjs +213 -0
  42. package/packages/runtime/session.mjs +52 -0
  43. package/packages/server/cli.mjs +18 -0
  44. package/packages/server/server.mjs +110 -0
  45. package/packages/server/stream.mjs +42 -0
  46. package/packages/web/app.js +85 -0
  47. package/packages/web/history.js +48 -0
  48. package/packages/web/index.html +17 -0
  49. package/packages/web/style.css +1 -0
  50. package/plugins/wacando/.codex-plugin/plugin.json +18 -0
  51. package/plugins/wacando/.mcp.json +8 -0
  52. package/plugins/wacando/skills/wacando/SKILL.md +33 -0
  53. package/plugins/wacando-claude/.claude-plugin/plugin.json +10 -0
  54. package/plugins/wacando-claude/.mcp.json +8 -0
  55. package/plugins/wacando-claude/README.md +88 -0
  56. package/plugins/wacando-claude/skills/open/SKILL.md +66 -0
  57. package/plugins/wacando-cursor/.cursor-plugin/plugin.json +11 -0
  58. package/plugins/wacando-cursor/README.md +48 -0
  59. package/plugins/wacando-cursor/commands/wacando-open.md +10 -0
  60. package/plugins/wacando-cursor/mcp.json +8 -0
  61. package/plugins/wacando-cursor/skills/wacando/SKILL.md +50 -0
  62. package/provenance.json +503 -0
@@ -0,0 +1,355 @@
1
+ # MCP implementation audit
2
+
3
+ Date: September 22, 2026. Baseline: `60e06c39dac8bf7dcad4e08cd804a70591ce5915`.
4
+ Environment: Node.js `v22.22.0`, Linux. Official MCP Inspector: `2.7.0`.
5
+
6
+ ## Remediation status
7
+
8
+ All seven findings below have been corrected in the shared sources and regenerated
9
+ in the Codex, Cursor and Claude plugin runtimes. The original observations are
10
+ preserved below as baseline evidence; their line numbers refer to the audited
11
+ commit, not the current files. The [MCP contract](MCP-CONTRACT.md) describes the
12
+ resulting behavior, limits, and supported revision.
13
+
14
+ | Finding | Correction | Regression coverage |
15
+ | --- | --- | --- |
16
+ | F1 | Invalid HTTP request targets return 400; unexpected handler failures are contained | [HTTP survival and retained-event test](../tests/http-hardening.test.mjs) |
17
+ | F2 | Validate request IDs/envelopes and method parameters; ignore responses and notifications without replying | [Protocol negative tests](../tests/mcp-protocol.test.mjs) |
18
+ | F3 | Share tool schemas and strict validation, reject extra/provider arguments, count Unicode code points consistently | [Schema and Unicode tests](../tests/mcp-protocol.test.mjs) |
19
+ | F4 | Bound input/output frames, rates, store bytes, pages, connections and SSE queues; await output writes | [Protocol/resource tests](../tests/mcp-protocol.test.mjs), [HTTP/SSE tests](../tests/http-hardening.test.mjs), [viewer history tests](../tests/viewer-history.test.mjs) |
20
+ | F5 | Complete handshakes in test clients and enforce initialization state in the server | [Protocol tests](../tests/mcp-protocol.test.mjs), [relocated plugin tests](../tests/plugins.test.mjs) |
21
+ | F6 | HTTP sockets have a shutdown grace period; MCP also has a final exit deadline for unread stdout pipes | [HTTP shutdown tests](../tests/http-hardening.test.mjs), [stalled stdout test](../tests/mcp-protocol.test.mjs) |
22
+ | F7 | Build into clean staging directories, preserve old runtimes on preparation failure, compare exact generated inventories and bytes | [Build regression](../tests/build.test.mjs), [generated-file check](../scripts/check-generated.mjs) |
23
+
24
+ `open_viewer` now reports a visibility preference and an unknown browser state.
25
+ All host skills identify recorded text as untrusted historical data and explain
26
+ pagination, rate-limit retries, and the 8 MiB retention budget. The viewer merges
27
+ live events with paginated history without duplicates or resurrecting evictions.
28
+
29
+ The supported protocol remains `2024-11-05`, explicitly documented and tested.
30
+ Migration to the modern revision and optional structured results are versioned
31
+ feature work, not unresolved defects in this legacy contract. Runtime dependencies
32
+ remain empty, and existing host installations have not been overwritten.
33
+
34
+ Remediation verification: `npm run check` passes all **29 tests** and the exact
35
+ runtime inventory/content checks. `npm run check:mcp` passes official Inspector
36
+ tool discovery and synthetic recording for all three generated adapters. Native
37
+ host browser behavior and Windows/macOS remain outside this verification.
38
+
39
+ ## Initial assessment (baseline)
40
+
41
+ The server interoperates with the official Inspector through the legacy MCP
42
+ protocol, but it is not ready for a blanket claim of protocol compliance or
43
+ robustness. A malformed local HTTP request terminates the MCP process. JSON-RPC
44
+ envelopes and tool inputs are insufficiently validated, resource limits are
45
+ incomplete, and the automated tests do not exercise a complete MCP handshake.
46
+
47
+ The initial audit changed documentation only. Its probes used disposable processes
48
+ and synthetic fixtures, not real user conversations or installed host instances.
49
+ The subsequent fixes are summarized in the remediation table above.
50
+
51
+ ## Scope and authoritative references
52
+
53
+ Reviewed the shared MCP server, event store, HTTP/SSE viewer service, browser-side
54
+ data handling, all three adapters, plugin manifests and MCP configurations,
55
+ skills and browser instructions, package generation, installers, and tests.
56
+
57
+ The protocol reference is **https://modelcontextprotocol.io**, using explicit
58
+ revision URLs. The implementation announces `2024-11-05`; that revision is the
59
+ baseline for its existing wire contract. The current official documentation also
60
+ describes `2026-07-28`, which has substantially different discovery and versioning.
61
+
62
+ The local HTTP service is a **viewer API**, not an MCP HTTP transport. Its SSE
63
+ events are application events. Missing MCP HTTP headers, OAuth discovery, an MCP
64
+ POST endpoint, or MCP HTTP session management are therefore not findings here.
65
+ The authorization specification distinguishes stdio from HTTP authorization.
66
+ See [MCP authorization](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization).
67
+
68
+ The official protocol does not make using an SDK a prerequisite for conformance.
69
+ The repository's zero-runtime-dependency constraint can be preserved, but the
70
+ manual transport needs stronger validation and independent interoperability tests.
71
+
72
+ ## Findings
73
+
74
+ ### F1 — High: malformed viewer URLs terminate the MCP process
75
+
76
+ Location: [HTTP handler](../packages/server/server.mjs), line 28.
77
+
78
+ `new URL(req.url, origin)` is outside the handler's exception boundary. Sending
79
+ `GET //[ HTTP/1.1` with the correct local Host header causes `ERR_INVALID_URL`.
80
+ Because the asynchronous HTTP callback rejects without a handler, Node exits.
81
+
82
+ Reproduced against a disposable Codex MCP process after valid initialization:
83
+
84
+ ```text
85
+ HTTP client: ECONNRESET
86
+ Server: TypeError: Invalid URL
87
+ Location: packages/server/server.mjs:28:22
88
+ Process exit: 1
89
+ ```
90
+
91
+ The viewer and MCP share this process, so the failure disconnects the host and
92
+ discards its entire in-memory store. All packaged adapters share the affected
93
+ code. The demonstrated attack surface is local HTTP access with an accepted Host;
94
+ this is not evidence of a remote cross-origin exploit.
95
+
96
+ Correction: catch malformed request-target parsing and return HTTP 400. Contain
97
+ unexpected handler failures without terminating the process, and add a regression
98
+ test that confirms MCP calls and previously recorded events survive the request.
99
+ This is a service robustness defect, not a missing MCP HTTP feature.
100
+
101
+ ### F2 — Medium: invalid JSON-RPC envelopes produce invalid successful replies
102
+
103
+ Location: [MCP dispatch](../packages/mcp/server.mjs), lines 29–37.
104
+
105
+ Only `jsonrpc` and `method` are checked. Requests with IDs `null`, `true`, `{}`,
106
+ `[]`, or `1.5` receive successful `ping` results echoing those IDs. Scalar
107
+ `params`, such as `"invalid"`, are also accepted. A response-shaped input is
108
+ misclassified as a request and receives an error response.
109
+
110
+ For example:
111
+
112
+ ```json
113
+ {"jsonrpc":"2.0","id":true,"method":"ping"}
114
+ ```
115
+
116
+ Observed reply:
117
+
118
+ ```json
119
+ {"jsonrpc":"2.0","id":true,"result":{}}
120
+ ```
121
+
122
+ MCP specifies string or integer request IDs, forbids null request IDs, and
123
+ distinguishes requests, responses, and notifications.
124
+ See [MCP messages](https://modelcontextprotocol.io/specification/2024-11-05/basic/messages).
125
+
126
+ Correction: validate the envelope before dispatch; classify message kinds;
127
+ reject malformed requests without echoing invalid IDs into success responses;
128
+ validate method parameters; and avoid replying to valid notifications or
129
+ unsolicited responses. Add malformed-message tests alongside valid string and
130
+ integer ID cases. Error responses to unidentifiable malformed input should be
131
+ treated separately from successful responses to valid MCP requests.
132
+
133
+ ### F3 — Medium: tool implementations do not enforce their advertised schemas
134
+
135
+ Locations: [tool schemas and dispatch](../packages/mcp/server.mjs), lines 7–20
136
+ and 39–45; [event validation](../packages/core/store.mjs), lines 14–25.
137
+
138
+ Reproduced after calling the server:
139
+
140
+ | Input | Observed behavior | Advertised contract |
141
+ | --- | --- | --- |
142
+ | Valid `record_event` fields plus `unexpected: true` | Event recorded | Additional properties forbidden |
143
+ | `record_event` with `type: null` | Recorded as `message` | Type must be an enumerated string |
144
+ | `open_viewer` with `arguments: 42` | Success | Arguments must be an object |
145
+ | `get_events` with an unsupported conversation filter | Entire snapshot returned | No arguments accepted |
146
+
147
+ The store enforces nonblank required strings and length limits, but those limits
148
+ are absent from the published input schema. The packaging test even sends a
149
+ `provider` argument that the schema forbids, then expects success.
150
+
151
+ MCP requires validation of tool inputs.
152
+ See [tool security requirements](https://modelcontextprotocol.io/specification/2024-11-05/server/tools#security-considerations).
153
+
154
+ Correction: enforce object shape, allowed properties, types, enum values, and
155
+ length constraints at the tool boundary. Keep store validation as a second
156
+ layer. Publish the actual constraints and test schema/implementation agreement,
157
+ including Unicode length semantics. Reject a client-supplied provider while
158
+ continuing to assign provider identity from the adapter.
159
+
160
+ Distinguish malformed `tools/call` envelopes from domain validation failures.
161
+ Do not mechanically replace every `isError: true` with a JSON-RPC error: the
162
+ official guidance explicitly allows tool-level input validation errors.
163
+ See [tool error handling](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#error-handling).
164
+
165
+ ### F4 — Medium: framing, invocation rates, and result sizes need bounds
166
+
167
+ Locations: [stdio input/output](../packages/mcp/server.mjs), lines 22–25;
168
+ [get_events](../packages/mcp/server.mjs), line 42;
169
+ [HTTP snapshots and streams](../packages/server/server.mjs), lines 30–51.
170
+
171
+ There is no application-level invocation rate limit, no maximum incoming stdio
172
+ line size, and no handling of stdout backpressure. `readline` can accumulate an
173
+ arbitrarily long unfinished line before store validation is reached. A probe
174
+ sent a one-megabyte unused argument to `open_viewer` and received success.
175
+
176
+ `get_events` always returns the full store. With 1,000 valid events of 32,000
177
+ ASCII characters each, serialization through the current result shape produced
178
+ **32,274,017 bytes**. HTTP snapshots and SSE reconnection also copy the full
179
+ history. Bounded event count does not provide a small response or bounded
180
+ transport queues. This can overwhelm host tool-result/context limits and cause
181
+ large memory spikes. Actual out-of-memory failure was not induced.
182
+
183
+ MCP's tool security section explicitly requires rate limiting.
184
+ See [tool security requirements](https://modelcontextprotocol.io/specification/2024-11-05/server/tools#security-considerations).
185
+ The particular byte budgets below are implementation recommendations, not
186
+ protocol-prescribed constants.
187
+
188
+ Correction: bound input bytes before newline parsing, rate-limit calls, handle
189
+ stdout backpressure with bounded queues, and cap response bytes. Give
190
+ `get_events` a bounded page size and conversation/sequence selection with a
191
+ continuation mechanism. Review SSE client limits, initial snapshots, and slow
192
+ readers under the same memory budget. This is pagination of a tool's data;
193
+ pagination of the three-item `tools/list` is not required to fix it.
194
+
195
+ ### F5 — Medium: current tests give incomplete lifecycle assurance
196
+
197
+ Locations: [runtime MCP test](../tests/runtime.test.mjs), lines 52–72;
198
+ [plugin tests](../tests/plugins.test.mjs), lines 31–56;
199
+ [MCP test helper](../tests/helpers/mcp-client.mjs);
200
+ [MCP lifecycle handling](../packages/mcp/server.mjs), lines 32–38.
201
+
202
+ The runtime test sends `initialize` without the required parameters. Plugin
203
+ tests do not send `notifications/initialized` or validate the negotiated
204
+ protocol version. The store-isolation test invokes tools without initialization.
205
+ The server accepts all these sequences and ignores every notification. A direct
206
+ probe successfully recorded an event before initialization and successfully
207
+ initialized with an empty object.
208
+
209
+ For the announced legacy revision, initialization precedes normal operation,
210
+ and the client sends the initialized notification after negotiation.
211
+ See [legacy lifecycle](https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle).
212
+
213
+ Correction: make test clients perform a complete handshake, check a supported
214
+ negotiated version, and cover invalid initialization, notifications, framing,
215
+ error replies, and shutdown. Define server handling of premature calls and
216
+ repeated initialization explicitly. Treat pre-initialization rejection as
217
+ defensive hardening; the legacy specification's client sequencing obligations
218
+ do not themselves mandate a particular server rejection code.
219
+
220
+ The official Inspector checks below improve confidence for normal clients, but
221
+ they do not replace negative tests or establish complete conformance.
222
+
223
+ ### F6 — Medium: shutdown has no application-controlled deadline
224
+
225
+ Locations: [MCP stop handler](../packages/mcp/server.mjs), lines 50–54;
226
+ [HTTP close](../packages/server/server.mjs), lines 62–66.
227
+
228
+ An accepted TCP socket with unfinished HTTP headers can keep `server.close()`
229
+ pending. The code tracks SSE responses but not all sockets, and has no deadline
230
+ for finishing shutdown. Once stopping begins, the stop guard also makes a
231
+ subsequent SIGTERM ineffective at advancing shutdown.
232
+
233
+ A disposable-process probe opened a socket, sent `GET / HTTP/1.1\r\n`, and closed
234
+ MCP stdin. The process was still running after 1.5 seconds and after another
235
+ second following SIGTERM. Closing the HTTP socket allowed a clean exit. This
236
+ demonstrates a peer-dependent shutdown delay; the probe did not wait for Node's
237
+ HTTP timeout and does not assert that the delay is infinite.
238
+
239
+ Correction: stop accepting work, finish active responses, then destroy remaining
240
+ connections after a short grace period. Test EOF and SIGTERM with an active SSE
241
+ stream and incomplete HTTP requests. Legacy MCP discusses shutdown timeouts,
242
+ and current stdio guidance recommends prompt exit on EOF.
243
+ See [legacy lifecycle](https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle)
244
+ and [current stdio shutdown](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio#shutdown).
245
+
246
+ ### F7 — Low: package builds retain deleted source files
247
+
248
+ Location: [package builder](../scripts/build.mjs), lines 7–11.
249
+
250
+ The build copies source trees into existing runtime directories without removing
251
+ obsolete files. In an isolated copy of the repository, a synthetic source file
252
+ was built, deleted from source, and built again. All three plugin runtimes still
253
+ contained it. This can retain retired code or assets in distributed packages.
254
+ No obsolete sensitive file was found in the current packages.
255
+
256
+ Correction: generate each runtime into a clean staging directory and replace
257
+ only the build-owned output, then check exact source/output inventories. In CI,
258
+ verify that rebuilding does not leave uncommitted runtime changes. This is a
259
+ packaging defect, not a protocol violation.
260
+
261
+ ## Compatibility and secondary recommendations
262
+
263
+ - **Legacy-only support is a limitation, not automatically a negotiation bug.**
264
+ Always returning `2024-11-05` is allowed if that is the only supported legacy
265
+ revision. The Inspector successfully accepts that fallback. A newer requested
266
+ version does not have to be echoed unless the server actually supports it.
267
+ See [legacy version negotiation](https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle#version-negotiation).
268
+ - **Do not update just the version string.** The official `2026-07-28` revision
269
+ replaces the initialization handshake with per-request metadata and adds
270
+ `server/discover`, result discriminators, and cache metadata. The current
271
+ server returns `-32601` for discovery. Document a supported-version policy and
272
+ test target hosts before choosing a migration or support for both eras.
273
+ See [official changes](https://modelcontextprotocol.io/specification/2026-07-28/changelog)
274
+ and [discovery](https://modelcontextprotocol.io/specification/2026-07-28/server/discover).
275
+ - **Browser status should be unambiguous.** `open_viewer` returns `visible: true`
276
+ without performing or observing any browser action. Its description correctly
277
+ says it only returns a URL. Rename that field to an explicit visibility
278
+ preference, or report browser state as unknown until the host verifies it.
279
+ - **Structured results are optional modernization.** With an appropriate
280
+ supported revision, `structuredContent` and `outputSchema` could avoid manual
281
+ JSON text parsing. Their absence is not a defect in the announced legacy
282
+ protocol. See [structured tool results](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#structured-content).
283
+ - **Recorded text remains untrusted data.** The viewer uses `textContent`, which
284
+ avoids interpreting messages as HTML. Host instructions should also make clear
285
+ that text returned by `get_events` is recorded content, not fresh operational
286
+ instructions. No prompt-injection exploit was established during this audit.
287
+
288
+ ## Controls already present
289
+
290
+ - MCP adapters emit newline-delimited JSON and do not print startup banners to
291
+ stdout. Standalone CLI logging is separate. This follows the
292
+ [stdio transport contract](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#stdio).
293
+ - The server advertises only its implemented tools capability. Tool names are
294
+ stable, and the list has deterministic order.
295
+ - Unknown methods return `-32601`; unknown tool names return `-32602`.
296
+ Store validation failures are presented as tool errors.
297
+ - HTTP listens on `127.0.0.1`, checks Host/Origin and cross-site fetch metadata,
298
+ rejects writes, and serves a fixed asset allowlist. It sets CSP, no-store,
299
+ no-referrer, and nosniff headers.
300
+ - User-controlled messages become DOM text. No HTML interpretation or dynamic
301
+ code evaluation was found in the viewer's message handling.
302
+ - Stores are separate per MCP process and keep the latest 1,000 events.
303
+ Provider identity is assigned by the adapter. Capture is explicit, and the
304
+ reviewed runtime contains no credential or global-session-history reader.
305
+ - Plugins run from copied, self-contained packages, including paths with spaces.
306
+ The Cursor installer refuses an existing destination. Browser instructions
307
+ preserve host permission flows and describe availability limitations.
308
+ - Local processes able to reach the viewer can read events. The README explicitly
309
+ documents that this is not an isolation boundary between users on one machine.
310
+ Authentication would be a separate requirement if that trust model changes.
311
+
312
+ ## Original verification and remaining limits
313
+
314
+ `npm run check` passed: all three runtimes rebuilt, and **9/9 tests** passed.
315
+ The rebuild left tracked runtime files unchanged.
316
+
317
+ The following commands used the official Inspector. All exited successfully:
318
+
319
+ ```sh
320
+ npx --yes @modelcontextprotocol/inspector@2.7.0 --cli node adapters/codex/mcp.mjs --method tools/list
321
+ npx --yes @modelcontextprotocol/inspector@2.7.0 --cli node plugins/wacando/runtime/adapters/codex/mcp.mjs --method initialize
322
+ npx --yes @modelcontextprotocol/inspector@2.7.0 --cli node plugins/wacando-cursor/runtime/adapters/cursor/mcp.mjs --method initialize
323
+ npx --yes @modelcontextprotocol/inspector@2.7.0 --cli node plugins/wacando-claude/runtime/adapters/claude/mcp.mjs --method tools/call --tool-name record_event --tool-arg conversationId=audit-synthetic --tool-arg agentId=audit-probe --tool-arg 'text=Synthetic audit fixture; not a real agent message.'
324
+ ```
325
+
326
+ These are the documented [Inspector CLI](https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector/cli)
327
+ workflow, pinned for reproducibility. Each command creates its own short-lived
328
+ server; the returned viewer URL is not a persistent preview. Inspector was not
329
+ added as a runtime dependency. An npm warning concerned Inspector's own legacy
330
+ transport dependency, not a dependency of Wacando.
331
+
332
+ Additional direct Node probes established F1–F3, the oversized input and result
333
+ measurements in F4, premature calls in F5, the shutdown delay in F6, and stale
334
+ build output in F7. Large Unicode events were also delivered in an SSE probe;
335
+ no claim of reproducible SSE message loss is made.
336
+
337
+ No host was reinstalled, no host browser was opened, and no marketplace was
338
+ modified. Current Codex/Cursor/Claude UI loading, native browser behavior,
339
+ Windows/macOS behavior, and remote/container port access were not verified.
340
+ The earlier host observations in [STATUS.md](../STATUS.md) remain historical
341
+ evidence, not fresh end-to-end validation. Stress exhaustion and an exhaustive
342
+ protocol conformance suite were outside this audit.
343
+
344
+ ## Original recommended order
345
+
346
+ 1. Fix F1 and add its process-survival regression test.
347
+ 2. Fix F2–F3 and repair the lifecycle test client in F5.
348
+ 3. Bound transport/resource usage and shutdown in F4/F6.
349
+ 4. Make generated packages exact in F7 and clarify browser result semantics.
350
+ 5. Choose and document the supported MCP revision set, then add a pinned
351
+ Inspector interoperability check for each packaged adapter.
352
+
353
+ Implement fixes in shared sources, run `npm run check`, and regenerate all three
354
+ plugin runtimes through the builder. Existing host installations require their
355
+ normal update flow; changing this checkout alone does not update them.
@@ -0,0 +1,138 @@
1
+ # MCP contract and verification
2
+
3
+ The authoritative protocol reference is [modelcontextprotocol.io](https://modelcontextprotocol.io).
4
+ Protocol changes must identify the supported revision and update interoperability
5
+ and negative tests. Do not infer requirements for the viewer API from the MCP HTTP
6
+ transport specification.
7
+
8
+ ## Supported revision and lifecycle
9
+
10
+ Wacando implements **MCP `2024-11-05` over stdio**, with the `tools` capability.
11
+ It supports `open_viewer`, `record_event`, `get_events`,
12
+ `wacando_verify_installation`, and `wacando_enroll_agent`.
13
+ The viewer needs no credentials. Onboarding manages protected local credentials
14
+ and bundled messaging components; see [onboarding v1](ONBOARDING.md).
15
+ No prompts, resources, sampling, server-initiated requests or task execution
16
+ capabilities are advertised.
17
+
18
+ The client sends valid `initialize` parameters, accepts the returned revision,
19
+ then sends `notifications/initialized`. Only then are tools available. Ping is
20
+ allowed during initialization. Invalid initialization does not advance state;
21
+ repeated initialization is rejected. Unknown methods return `-32601`, including
22
+ the modern `server/discover` probe, so clients supporting both eras can fall back
23
+ to the legacy handshake. A newer offered version receives the supported legacy
24
+ revision. See the [official lifecycle and version negotiation](https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle).
25
+
26
+ The server does not claim support for the current `2026-07-28` revision. Adding
27
+ it requires per-request metadata, discovery and the associated response contract,
28
+ not just a version-string change. Keep legacy support until the target hosts
29
+ have been checked against the proposed replacement. See the
30
+ [official revision changes](https://modelcontextprotocol.io/specification/2026-07-28/changelog).
31
+
32
+ Requests are newline-delimited UTF-8 JSON, with CRLF also accepted. Text newlines
33
+ inside JSON must be escaped. Request IDs are strings of at most 512 UTF-16 code
34
+ units or JavaScript-safe integers; null, fractional and other IDs are rejected.
35
+ Clients must use unique IDs. Notifications and unsolicited responses receive no
36
+ reply and cannot mutate the store. Calls are dispatched serially; onboarding
37
+ awaits bounded outbound requests. Cancellation does not roll back a completed
38
+ append or token redemption. See [MCP messages](https://modelcontextprotocol.io/specification/2024-11-05/basic/messages)
39
+ and [stdio framing](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#stdio).
40
+
41
+ ## Validation and data boundaries
42
+
43
+ Tool schemas and validation share the same definitions. Unknown arguments are
44
+ rejected, and `provider` is assigned only by the host adapter. Required strings
45
+ must contain non-whitespace characters. Identifier lengths are limited to 200
46
+ Unicode code points; message text to 32,000. Optional fields can be omitted but
47
+ cannot be null unless explicitly advertised by a future schema.
48
+
49
+ Malformed JSON-RPC or method parameters produce protocol errors. Invalid tool
50
+ arguments and rate limiting return `isError: true` with actionable text. This
51
+ preserves the distinction described in [official tool error handling](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#error-handling).
52
+
53
+ Messages are untrusted historical data. Neither returned messages nor browser
54
+ content constitute new instructions or authorization. The viewer renders text,
55
+ and recording does not scan credentials, global histories, or private reasoning.
56
+
57
+ `open_viewer` returns `requestedVisible: true` and `browserState: "unknown"`.
58
+ The host must open and verify the browser; the MCP process cannot attest to UI
59
+ visibility. Host skills preserve the existing browser and approval requirements.
60
+
61
+ ## Resource budgets
62
+
63
+ These are implementation limits, not constants prescribed by MCP. Tool rate
64
+ limiting follows the [official tool security requirements](https://modelcontextprotocol.io/specification/2024-11-05/server/tools#security-considerations).
65
+
66
+ | Resource | Limit and behavior |
67
+ | --- | --- |
68
+ | Incoming stdio frame | 256 KiB before the newline; oversized, invalid UTF-8 or incomplete EOF frames terminate the transport with a stderr diagnostic |
69
+ | Outgoing MCP frame | 512 KiB; writes are awaited individually |
70
+ | Stalled write | 1 second; terminate the affected MCP transport or SSE client |
71
+ | Tool invocation rate | Burst of 100, replenished at 25 calls/second; rejected calls have no side effects |
72
+ | Stored history | Latest 1,000 events or 8 MiB of serialized event bytes, whichever is reached first |
73
+ | Event data page | At most 256 KiB; default 50 events, maximum 100 |
74
+ | HTTP request rate | Burst of 100, replenished at 50 requests/second, per process; excess requests receive 429 |
75
+ | HTTP connections | At most 32 simultaneous connections, including incomplete requests |
76
+ | SSE clients | At most 8; excess streams receive 503 |
77
+ | SSE output queue | 512 KiB per client, including the active write; close a client that exceeds it |
78
+ | HTTP shutdown grace | 250 ms; remaining sockets are destroyed after the grace period |
79
+ | MCP shutdown deadline | Allow protected onboarding cleanup (including an in-flight call/retry) to finish, bounded by 45 seconds with exit 1 on timeout; then allow 1 second for an unread stdout pipe before forcing exit |
80
+
81
+ The byte cap describes serialized data, not total process heap usage. Live stream
82
+ consumers are individually bounded; a slow viewer does not queue unlimited data
83
+ or interrupt other viewers. Host processes and browsers should reconnect after a
84
+ transport failure. Normal stdin EOF and SIGTERM close the viewer, including active
85
+ streams and incomplete HTTP requests.
86
+
87
+ ## Reading events
88
+
89
+ `get_events` and `GET /api/events` accept:
90
+
91
+ - `conversationId`: optional exact conversation filter.
92
+ - `afterSequence`: exclusive cursor, default zero.
93
+ - `throughSequence`: inclusive upper bound, default the current sequence.
94
+ - `limit`: maximum events, from 1 to 100, default 50.
95
+
96
+ Each response includes `events`, `sequence`, `oldestSequence`, `retention`,
97
+ `byteLimit`, `retainedBytes`, `historyTruncated`, and `nextAfterSequence`.
98
+ Keep the first page's `sequence` as `throughSequence` and follow
99
+ `nextAfterSequence` until it is null. A page may be shorter than `limit` because
100
+ of its byte budget. An unknown conversation produces an empty page. Evicted
101
+ history cannot be recovered; `historyTruncated` indicates the requested cursor
102
+ predates retained history, independently of the conversation filter.
103
+
104
+ Example tool arguments:
105
+
106
+ ```json
107
+ {"conversationId":"task-1","afterSequence":50,"throughSequence":120,"limit":50}
108
+ ```
109
+
110
+ SSE sends a bounded `snapshot` page first. The viewer fetches remaining history
111
+ over paginated HTTP while live `message` events continue. Each live payload is
112
+ `{event, oldestSequence}`. The viewer merges by ID, sorts by sequence, respects
113
+ eviction, and ignores history responses from superseded connections. Export is
114
+ disabled while the snapshot is incomplete. This SSE format belongs to the viewer
115
+ application, not the MCP transport.
116
+
117
+ ## Builds and checks
118
+
119
+ ```sh
120
+ npm run check
121
+ npm run check:mcp
122
+ ```
123
+
124
+ `check` regenerates runtimes from clean staging directories, compares every
125
+ generated file against its source, and runs the regression suite. Preparation
126
+ failures preserve the previous runtime. Only build-owned runtime directories are
127
+ replaced; plugin manifests, skills and existing host installations are preserved.
128
+
129
+ `check:mcp` runs the official Inspector pinned to `2.7.0` against each generated
130
+ adapter, checking discovery through `tools/list` and a synthetic recording. It
131
+ uses npm's executable cache and may need network access on its first run. The
132
+ Inspector is development verification tooling, not a runtime dependency. See the
133
+ [official Inspector CLI](https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector/cli).
134
+
135
+ For CI, run both commands and `git diff --exit-code -- plugins/*/runtime` to ensure
136
+ the checked-in packages match regenerated output. Commit runtime changes alongside
137
+ their shared sources. Host installation and native-browser tests are separate;
138
+ a successful CLI check does not verify a host's browser UI.
@@ -0,0 +1,36 @@
1
+ # Migrating to runtime 2.0 (official libsignal OMEMO)
2
+
3
+ Runtime/plugin 1.x encrypted with the **classic** OMEMO backend
4
+ (`@privacyresearch/libsignal-protocol-typescript`, GPL-3.0, no PQXDH). The
5
+ Wacando desktop apps use only the official `@signalapp/libsignal-client` and
6
+ cannot decrypt classic sessions. Runtime 2.0 ships **only** the official
7
+ backend (`@wacando/omemo`): bundles carry Kyber prekeys and sessions
8
+ use PQXDH.
9
+
10
+ There is no in-place key conversion: an identity key is bound to its device,
11
+ its published bundle and its peers' trust pins.
12
+
13
+ ## What happens to an existing 1.x identity
14
+
15
+ On connect, the runtime finds `classic-state.json` in the identity's protected
16
+ state directory and stops with:
17
+
18
+ > This identity was created by plugin 1.x with the retired classic OMEMO format
19
+ > ... revoke it on the site and pair a new identity with a different address
20
+
21
+ Nothing is deleted, rewritten or published: the classic keys stay on disk, no
22
+ native device is created next to them, and no plaintext path exists.
23
+
24
+ ## Re-enrollment
25
+
26
+ 1. Stop every host or daemon using the old identity.
27
+ 2. In the Wacando app, revoke the old agent.
28
+ 3. Pair a new identity (new address) with this runtime: `wacando-agent pair
29
+ <code>` or the `wacando_pair` MCP tool ([pairing](AGENT_TOOLS.md)). The runtime generates a new
30
+ native device locally.
31
+ 4. Optionally archive, then delete, the old protected state directory once you
32
+ no longer need it (`~/.wacando-plugin/<partition>/omemo-<digest>/`). The
33
+ runtime never does this for you.
34
+
35
+ Conversations of the old identity are not migrated; observers keep their own
36
+ history.
@@ -0,0 +1,125 @@
1
+ # OMEMO prekey integration — September 23, 2026
2
+
3
+ The upstream corrections described in
4
+ `/home/kyle/work/wacando/docs/OMEMO_PREKEY_REVIEW.md` are now incorporated into
5
+ this plugin checkout. The shared messaging bundle, preserved upstream sources,
6
+ provenance and all three generated runtimes were regenerated. No backend or API
7
+ contract changes, publication, deployment or push were performed here.
8
+
9
+ ## Included corrections and provenance
10
+
11
+ - Publication replenishes the backend that owns the advertised prekeys; the
12
+ classic bridge cannot shadow them with different private material.
13
+ - Classic consumption removes and persists the canonical native prekey.
14
+ - Session initiation selects a sender-specific slot.
15
+ - Replenishment allocates monotonically increasing, persisted prekey IDs.
16
+
17
+ The upstream revision recorded in provenance is
18
+ `babaa329fe288aa56cfb5e6b6b3c89a2d9aad86c`. The upstream fixes were uncommitted:
19
+ that revision alone does **not** identify the imported code. All 154 input hashes
20
+ in `packages/onboarding/vendor/provenance.json` were checked against the upstream
21
+ working tree. Bundle and provenance copies match in all three runtimes.
22
+
23
+ SHA-256 of `packages/onboarding/vendor/messaging.cjs`:
24
+ `f316806787afce10d6df0a499a2e6b2b2ed5899fb47898b9e31024dffe08e5b3`.
25
+
26
+ Existing identity keys, signed prekeys and ratchet state are preserved. There is
27
+ no automatic identity reset or plaintext fallback. Restart the MCP host to load
28
+ an updated installed package; keep its existing protected state directory.
29
+ The installer refuses to overwrite an existing installation, so stage the new
30
+ package separately and update the host registration using the
31
+ [installation guide](ONBOARDING.md).
32
+
33
+ ## Independently repeated in the actual plugin checkout
34
+
35
+ Environment: Linux, Node.js 22.22.0, npm 10.9.4, local service
36
+ `http://localhost:3005`, actual ejabberd and a sandboxed headless system Chrome
37
+ observer. The fixtures create synthetic accounts and revoke their test agents.
38
+
39
+ ```sh
40
+ node scripts/vendor-messaging.mjs /home/kyle/work/wacando
41
+ npm run check
42
+ npm run check:mcp
43
+ WACANDO_LIVE_TEST_ORIGIN=http://localhost:3005 node --test tests/onboarding-live.test.mjs
44
+
45
+ WACANDO_CHECKOUT=/home/kyle/work/wacando \
46
+ /home/kyle/work/wacando/apps/adapter/node_modules/.bin/tsx \
47
+ scripts/check-crypto-interop.mts --after-publication --two-senders
48
+
49
+ WACANDO_CHECKOUT=/home/kyle/work/wacando \
50
+ WACANDO_LIVE_TEST_ORIGIN=http://localhost:3005 \
51
+ WACANDO_CHROMIUM_EXECUTABLE=/usr/bin/google-chrome \
52
+ /home/kyle/work/wacando/apps/adapter/node_modules/.bin/tsx \
53
+ scripts/check-encrypted-interop.mts
54
+ ```
55
+
56
+ | Check | Result |
57
+ | --- | --- |
58
+ | Build and exact generated-runtime verification | PASS, all three runtimes |
59
+ | `npm run check` final run | 43 PASS, three opt-in live tests skipped |
60
+ | Official MCP Inspector 2.7.0 | PASS, all three runtimes |
61
+ | Explicit live onboarding suite | 3/3 PASS: verification, enrollment, OMEMO, restart and revocation |
62
+ | Offline crypto regression | 4/4 PASS: both directions and two observer senders after publication preparation |
63
+ | Packaged plugin → native adapter and real browser | PASS |
64
+ | Native adapter → plugin and real browser | PASS |
65
+ | Injected heartbeat outage and recovery | PASS, one XMPP connection and one grant; subsequent encrypted traffic received |
66
+ | Browser STOP | PASS, subsequent encrypted plugin stanza rejected by ejabberd |
67
+ | Server canary scans | PASS, zero occurrences for all three prefixes in fresh dumps, logs and spool |
68
+ | `git diff --check` | PASS |
69
+
70
+ The first `npm run check`, overlapping the crypto/network checks, had five
71
+ deadline failures: three MCP request timeouts and two relocated-package test
72
+ timeouts (38 passed, three failed, two cancelled, three skipped). An unchanged
73
+ rerun passed all 43 tests in 10.5 seconds. No timeout threshold or product code
74
+ was changed to obtain the passing run. Scheduling contention is a possible
75
+ explanation, not a proven root cause; these failures do not explain the earlier
76
+ transient enrollment failure recorded in the baseline.
77
+
78
+ Server scans were repeated from `/home/kyle/work/wacando` for each of
79
+ `SYNTHETIC_PLUGIN_`, `SYNTHETIC_ADAPTER_`, `SYNTHETIC_AFTER_HEARTBEAT_`:
80
+
81
+ ```sh
82
+ EJABBERD_CONTAINER=wacando-preview-xmpp \
83
+ bash packages/omemo/scripts/canary-scan.sh SYNTHETIC_PLUGIN_
84
+ ```
85
+
86
+ The upstream 49/49 OMEMO unit-test result is reported in its review; it was not
87
+ rerun here. The plugin regression and full encrypted network path above were
88
+ rerun against this checkout's regenerated bundle. The older failing results in
89
+ [the handoff follow-up](PLUGIN_HANDOFF_FOLLOWUP.md) are historical evidence.
90
+
91
+ ## Remaining limits and resumption
92
+
93
+ - A cached bundle can still select an already consumed prekey, including a
94
+ collision between devices belonging to one sender. Such a session is rejected.
95
+ Automatic bundle refresh and complete recovery of old broken sessions are not
96
+ implemented. Do not delete identity files or claim that lost private prekeys
97
+ can be reconstructed. The successful two-sender run does not prove that all
98
+ possible slot collisions are avoided.
99
+ - Production onboarding's reported 404 remains unresolved. HTTPS/WSS production
100
+ onboarding was not qualified by these local checks.
101
+ - Actual Codex CLI 0.153.4 tool invocation and literal site prompts were repeated
102
+ against the final bundle after this vendor refresh: verification, enrollment
103
+ and fresh-host resume all passed using `WACANDO_LIVE_TEST_ORIGIN=http://localhost:3005
104
+ WACANDO_CHECKOUT=/home/kyle/work/wacando node scripts/check-host-onboarding.mjs`.
105
+ No durable credential markers appeared in host output. Runtime tests cover all three MCP
106
+ runtimes. Cursor Agent CLI 2026.08.25-3e8eec8 subsequently passed verification,
107
+ enrollment and fresh-host resume using the final copied plugin; see the public
108
+ readiness record for the command and scope. Claude and graphical host UIs,
109
+ Codex Desktop installation UI, Windows and long outage campaigns remain to
110
+ be qualified. The browser here is a Playwright-controlled web observer, not
111
+ Codex's in-app browser.
112
+ - The encrypted-message fixture uses the packaged session's internal provider
113
+ and client. It does not add or imply a public messaging MCP tool.
114
+ - Preserve saved credentials and keys when resuming a completed enrollment.
115
+ Reinvoke enrollment with the same origin, installation and identity; do not
116
+ redeem the consumed temporary token again. The separate API limitation when
117
+ enrollment consumes a token but its durable response is lost remains unchanged;
118
+ follow the recovery guide rather than claiming enrollment succeeded.
119
+ - No npm package name availability is asserted. The versioned npx command in
120
+ the installation guide remains a publication placeholder. Nothing was
121
+ published or deployed.
122
+
123
+ The [public onboarding readiness record](PUBLIC_ONBOARDING_READINESS.md) tracks
124
+ deployment preparation, public probes, remaining host work and the proposed
125
+ consumed-prekey recovery design. Initial deadline failures remain unexplained.