@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.
- package/.agents/plugins/marketplace.json +10 -0
- package/.claude-plugin/marketplace.json +12 -0
- package/.cursor-plugin/marketplace.json +12 -0
- package/README.md +166 -2
- package/STATUS.md +121 -0
- package/THIRD-PARTY-NOTICES.txt +1928 -0
- package/adapters/README.md +12 -0
- package/adapters/claude/mcp.mjs +7 -0
- package/adapters/codex/mcp.mjs +7 -0
- package/adapters/cursor/mcp.mjs +7 -0
- package/bin/wacando-agent.mjs +243 -0
- package/docs/AGENT_TOOLS.md +147 -0
- package/docs/CURSOR_IDENTITY_RESUME.md +579 -0
- package/docs/MCP-AUDIT.md +355 -0
- package/docs/MCP-CONTRACT.md +138 -0
- package/docs/MIGRATION-2.0.md +36 -0
- package/docs/OMEMO_PREKEY_INTEGRATION.md +125 -0
- package/docs/ONBOARDING-VALIDATION.md +88 -0
- package/docs/ONBOARDING.md +212 -0
- package/docs/ONBOARDING_PLUGIN_CONTRACT.md +156 -0
- package/docs/PLUGIN_HANDOFF_FOLLOWUP.md +178 -0
- package/docs/PUBLIC_ONBOARDING_READINESS.md +156 -0
- package/docs/RELEASE.md +52 -0
- package/docs/WACANDO_PACKAGES.md +26 -0
- package/package.json +66 -4
- package/packages/core/limits.mjs +24 -0
- package/packages/core/store.mjs +92 -0
- package/packages/core/validation.mjs +25 -0
- package/packages/mcp/agent-tools.mjs +82 -0
- package/packages/mcp/server.mjs +161 -0
- package/packages/mcp/stdio.mjs +42 -0
- package/packages/onboarding/network.mjs +60 -0
- package/packages/onboarding/service.mjs +235 -0
- package/packages/onboarding/storage.mjs +99 -0
- package/packages/onboarding/version.mjs +2 -0
- package/packages/runtime/conversations.mjs +433 -0
- package/packages/runtime/device.mjs +34 -0
- package/packages/runtime/index.mjs +8 -0
- package/packages/runtime/pairing.mjs +110 -0
- package/packages/runtime/recovery.mjs +49 -0
- package/packages/runtime/runtime.mjs +213 -0
- package/packages/runtime/session.mjs +52 -0
- package/packages/server/cli.mjs +18 -0
- package/packages/server/server.mjs +110 -0
- package/packages/server/stream.mjs +42 -0
- package/packages/web/app.js +85 -0
- package/packages/web/history.js +48 -0
- package/packages/web/index.html +17 -0
- package/packages/web/style.css +1 -0
- package/plugins/wacando/.codex-plugin/plugin.json +18 -0
- package/plugins/wacando/.mcp.json +8 -0
- package/plugins/wacando/skills/wacando/SKILL.md +33 -0
- package/plugins/wacando-claude/.claude-plugin/plugin.json +10 -0
- package/plugins/wacando-claude/.mcp.json +8 -0
- package/plugins/wacando-claude/README.md +88 -0
- package/plugins/wacando-claude/skills/open/SKILL.md +66 -0
- package/plugins/wacando-cursor/.cursor-plugin/plugin.json +11 -0
- package/plugins/wacando-cursor/README.md +48 -0
- package/plugins/wacando-cursor/commands/wacando-open.md +10 -0
- package/plugins/wacando-cursor/mcp.json +8 -0
- package/plugins/wacando-cursor/skills/wacando/SKILL.md +50 -0
- package/provenance.json +503 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
# Cursor identity resume — issue 2
|
|
2
|
+
|
|
3
|
+
Status: shutdown regression and **issue #2 runtime acceptance checks PASS**.
|
|
4
|
+
King's explicit decision, relayed in mission
|
|
5
|
+
`1dc1a826-eefb-4610-bdfa-5027f88ab4d0`, accepts identity resume and revocation
|
|
6
|
+
proved by all three runtime profiles against real API/Postgres/ejabberd/OMEMO.
|
|
7
|
+
**Actual Cursor/Codex/Claude CLIs are NOT QUALIFIED in this sandbox**; their
|
|
8
|
+
qualification is tracked in [Wacando #17](https://github.com/lbframe/wacando/issues/17).
|
|
9
|
+
The recorded Cursor/Codex EROFS failures remain residual limitations, never PASS.
|
|
10
|
+
This decision changes the acceptance scope, not the observed test results.
|
|
11
|
+
Independent review is pending. No package was published and no lock was removed
|
|
12
|
+
in order to obtain a successful resume.
|
|
13
|
+
|
|
14
|
+
## Demonstrated defect and scope
|
|
15
|
+
|
|
16
|
+
Base: `d0cf8b2` (`origin/main`). Linux, Node.js 22.22.0, npm 11.11.1.
|
|
17
|
+
Installed host versions observed: Cursor Agent CLI `2026.09.26-dd393fe`, Codex
|
|
18
|
+
CLI `0.162.1`. Neither host was used to claim an actual API/XMPP resume in this run.
|
|
19
|
+
|
|
20
|
+
The MCP server used to arm an unconditional `process.exit(0)` one second after
|
|
21
|
+
EOF/SIGTERM. `OnboardingService` releases a live session's exclusive `.lock` only
|
|
22
|
+
**after** `session.stop()` resolves. The checked-in `@xmpp/client` implementation
|
|
23
|
+
can wait two seconds closing the stream and another two closing the socket.
|
|
24
|
+
Thus normal shutdown can exit successfully before releasing its session lock.
|
|
25
|
+
The same deadline can interrupt saving an installation response after redemption.
|
|
26
|
+
|
|
27
|
+
The regression test holds session cleanup behind a controlled barrier for 1.2
|
|
28
|
+
seconds, crosses the old deadline, then allows the owner to release its own lock.
|
|
29
|
+
It generates real OMEMO local state, compares it byte for byte after cleanup, and
|
|
30
|
+
reacquires the released lock. A separate test holds an actual loopback HTTP
|
|
31
|
+
verification response until after the old deadline and checks it is saved before
|
|
32
|
+
exit. These are process/storage lifecycle tests. The session stop barrier does
|
|
33
|
+
not simulate or qualify the XMPP contract.
|
|
34
|
+
|
|
35
|
+
All three fail against an isolated archive of the base with exit code 1; both
|
|
36
|
+
session cases observe the child exiting with code 0 before cleanup. All pass in
|
|
37
|
+
the final full suite. This demonstrates a local shutdown defect consistent with
|
|
38
|
+
the production report, **not proof that it was the cause of that particular
|
|
39
|
+
Cursor host failure**. A held prior process, shutdown timing, or another host
|
|
40
|
+
issue must still be distinguished using the actual host checker and approved API.
|
|
41
|
+
|
|
42
|
+
## Change
|
|
43
|
+
|
|
44
|
+
- Wait for onboarding cleanup and any currently dispatched call before applying
|
|
45
|
+
the final one-second stdout deadline. A separate 45-second cap exits with code
|
|
46
|
+
1 if cleanup cannot finish. It never reaps or bypasses a lock.
|
|
47
|
+
- Wait for in-flight background recovery on close and avoid opening/publishing a
|
|
48
|
+
new session after close was requested. Keep the existing credentials, host/origin
|
|
49
|
+
partition, key paths and exclusive locking.
|
|
50
|
+
- Regenerate all three plugin runtimes with the build; generated files were not
|
|
51
|
+
edited directly.
|
|
52
|
+
- Bring the isolated host checker protections from the historical public fixture
|
|
53
|
+
into the normal loopback checker: isolated Cursor workspace alias, own process
|
|
54
|
+
group cleanup, parsed affirmative `verified: true` / `connected: true`, private
|
|
55
|
+
identity equality, and clear rejection after owner revocation. Cursor's outer
|
|
56
|
+
success/isError flag alone is insufficient. Raw host output is suppressed.
|
|
57
|
+
|
|
58
|
+
The checker fails if its own descendants cannot close within ten seconds. Any
|
|
59
|
+
forced termination is a failed qualification, never a successful retry with a
|
|
60
|
+
removed lock. The checker remains loopback-only; no public acceptance bypass was
|
|
61
|
+
added. Its event parser tests use explicit synthetic wrappers, not recorded host
|
|
62
|
+
payloads from a real acceptance run.
|
|
63
|
+
|
|
64
|
+
## Original implementation commands and real outputs
|
|
65
|
+
|
|
66
|
+
The repository contains no separate lint/typecheck scripts. `npm run check`
|
|
67
|
+
builds, checks generated output and runs all Node tests. Full suites were bounded
|
|
68
|
+
by `timeout 600s` and run in the foreground.
|
|
69
|
+
|
|
70
|
+
Baseline reproduction, from the plugin checkout:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
baseline_dir=$(mktemp -d /tmp/wacando-issue2-before.XXXXXX)
|
|
74
|
+
git archive origin/main | tar -x -C "$baseline_dir"
|
|
75
|
+
cp tests/onboarding-shutdown.test.mjs "$baseline_dir/tests/onboarding-shutdown.test.mjs"
|
|
76
|
+
cd "$baseline_dir"
|
|
77
|
+
timeout 600s node --test tests/onboarding-shutdown.test.mjs
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Actual isolated directory: `/tmp/wacando-issue2-before.56NFHw`. Exit 1.
|
|
81
|
+
[Complete baseline output](evidence/issue-2/baseline.txt):
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
# tests 3
|
|
85
|
+
# suites 0
|
|
86
|
+
# pass 0
|
|
87
|
+
# fail 3
|
|
88
|
+
# cancelled 0
|
|
89
|
+
# skipped 0
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Original full check before the approved API target, from the plugin checkout:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm run check
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Exit 0. [Complete output](evidence/issue-2/check.txt):
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
# tests 57
|
|
102
|
+
# suites 0
|
|
103
|
+
# pass 54
|
|
104
|
+
# fail 0
|
|
105
|
+
# cancelled 0
|
|
106
|
+
# skipped 3
|
|
107
|
+
# todo 0
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This includes Cursor/Codex cleanup and key preservation, in-flight verification
|
|
111
|
+
and background recovery cleanup, parsed result validation, and both packaged
|
|
112
|
+
MCP runtimes rejecting a locally revoked identity while still answering `ping`
|
|
113
|
+
and exiting with code 0. Existing injected control-plane rejection tests cover
|
|
114
|
+
HTTP 403/AGENT_REVOKED, but are not a real remote revocation check.
|
|
115
|
+
|
|
116
|
+
The first check attempt used the default npm cache and failed the packaging test:
|
|
117
|
+
`EROFS` opening `/home/kyle/.npm/_cacache/tmp/da3aeb1a`. No application test failure
|
|
118
|
+
was inferred from that sandbox restriction; the final run used the writable
|
|
119
|
+
cache above. An intermediate new background-recovery test also failed because
|
|
120
|
+
its simulated scheduler retained an already-executed timer in the test's Set;
|
|
121
|
+
the fixture now consumes it before calling the callback. The final output is
|
|
122
|
+
from the corrected test, with no retry of a live identity.
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm run check:mcp
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Exit 0. [Complete output](evidence/issue-2/check-mcp.txt):
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
Official Inspector 2.7.0 passed: wacando
|
|
132
|
+
Official Inspector 2.7.0 passed: wacando-cursor
|
|
133
|
+
Official Inspector 2.7.0 passed: wacando-claude
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
npm run build
|
|
138
|
+
npm run check:generated
|
|
139
|
+
git diff --check
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Exit 0 for each. Real output:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
Built self-contained codex plugin: wacando@1.0.0
|
|
146
|
+
Built self-contained cursor plugin: wacando-cursor@1.0.0
|
|
147
|
+
Built self-contained claude plugin: wacando-claude@1.0.0
|
|
148
|
+
Verified exact runtime: wacando
|
|
149
|
+
Verified exact runtime: wacando-cursor
|
|
150
|
+
Verified exact runtime: wacando-claude
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`git diff --check` produced no output for the then-uncommitted changes.
|
|
154
|
+
This historical working-tree check does not cover the cumulative PR diff;
|
|
155
|
+
see the whitespace scope correction in the documentary follow-up evidence.
|
|
156
|
+
|
|
157
|
+
## Approved local qualification — October 15, 2026
|
|
158
|
+
|
|
159
|
+
King authorized an ephemeral local development target, relayed by
|
|
160
|
+
`wacando.lead`. This run used Wacando `origin/main` at
|
|
161
|
+
`66c754756e83e8685d61e1c0312b690d77b3bdd3`, in the detached checkout
|
|
162
|
+
`/home/kyle/timon-projects/wacando/agent2-api`. No service source, frozen document,
|
|
163
|
+
production/public service, account credential or issue-10 service was changed.
|
|
164
|
+
The existing plugin branch and shutdown-lock regressions were retained.
|
|
165
|
+
|
|
166
|
+
The target derives from `docker-compose.dev.yml`, uses the repository's API,
|
|
167
|
+
policy and web Dockerfiles, and injects the official `ejabberd.ci.yml` and dev
|
|
168
|
+
seed. The seed's container selector was restricted to this project's service;
|
|
169
|
+
the original ancestor-image selector could otherwise select issue 10.
|
|
170
|
+
|
|
171
|
+
| Service | Loopback host port | Container port |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| Postgres | 25432 | 5432 |
|
|
174
|
+
| ejabberd c2s / WebSocket / component | 25222 / 25280 / 25347 | 5222 / 5280 / 5347 |
|
|
175
|
+
| API / policy / web | 23001 / 23002 / 23000 | 3001 / 3002 / 3000 |
|
|
176
|
+
|
|
177
|
+
Project, network and volumes use `wacando-agent2`. All bindings use 127.0.0.1;
|
|
178
|
+
host port 3000 remains reserved for Timon. Only inherited development secrets
|
|
179
|
+
were used. Generated compose credentials remain outside evidence in a mode-0600
|
|
180
|
+
temporary file and were removed after shutdown.
|
|
181
|
+
|
|
182
|
+
**Retirement dependency:** the controllers create synthetic owners through
|
|
183
|
+
`/api/auth/sign-up/email`, then use the existing v1 onboarding endpoints. The
|
|
184
|
+
CLI checker imports `apps/web/src/lib/onboarding-prompts.ts`. This qualification
|
|
185
|
+
depends on the web onboarding flow scheduled for retirement in Wacando #42; it
|
|
186
|
+
does not qualify the future app/pairing flow. The web endpoint was checked by
|
|
187
|
+
HTTP only, not by a browser onboarding session.
|
|
188
|
+
|
|
189
|
+
### Setup commands and observed results
|
|
190
|
+
|
|
191
|
+
From the main-service checkout:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
git fetch origin main
|
|
195
|
+
git worktree add --detach ../agent2-api origin/main
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
From the plugin checkout, the retained
|
|
199
|
+
[fixture preparation script](evidence/issue-2/prepare-local-stack.py) reproduces
|
|
200
|
+
the exact generated compose and seed adaptation (both SHA-256 hashes matched
|
|
201
|
+
the initially generated files):
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
python3 docs/evidence/issue-2/prepare-local-stack.py /home/kyle/timon-projects/wacando/agent2-api
|
|
205
|
+
timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d postgres ejabberd
|
|
206
|
+
timeout 180s bash /tmp/wacando-agent2-ci-up.sh
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Exit 0. [Seed output](evidence/issue-2/local-seed.txt) confirms real ejabberd
|
|
210
|
+
26.7.0, the six documented synthetic dev accounts and all three shifted ports.
|
|
211
|
+
|
|
212
|
+
From the isolated main checkout, final successful image builds:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
BUILDX_CONFIG=/tmp/wacando-agent2-buildx timeout 600s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json build api
|
|
216
|
+
BUILDX_CONFIG=/tmp/wacando-agent2-buildx timeout 600s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json build policy
|
|
217
|
+
BUILDX_CONFIG=/tmp/wacando-agent2-buildx timeout 600s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json build web
|
|
218
|
+
timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d --no-deps --no-build api
|
|
219
|
+
timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d --no-build policy
|
|
220
|
+
timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d --no-build web
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Exit 0 for the final builds and starts. Complete logs:
|
|
224
|
+
[API](evidence/issue-2/local-build-api.txt),
|
|
225
|
+
[policy](evidence/issue-2/local-build-policy.txt),
|
|
226
|
+
[web](evidence/issue-2/local-build-web.txt),
|
|
227
|
+
[full-stack start](evidence/issue-2/local-up.txt).
|
|
228
|
+
API/contracts/XMPP and policy typechecks passed inside their images; the web's
|
|
229
|
+
Next build and TypeScript check passed. All five services were healthy in the
|
|
230
|
+
[status output](evidence/issue-2/local-status.txt).
|
|
231
|
+
|
|
232
|
+
```sh
|
|
233
|
+
curl -fsS --max-time 5 http://localhost:23001/health
|
|
234
|
+
curl -fsS --max-time 5 http://localhost:23000/onboarding -o /dev/null -w 'Web onboarding HTTP %{http_code}\n'
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Exit 0: `{"ok":true}` and `Web onboarding HTTP 200`.
|
|
238
|
+
[Actual outputs](evidence/issue-2/local-health.txt).
|
|
239
|
+
|
|
240
|
+
Setup failures were infrastructure/dependency failures, not successful acceptance:
|
|
241
|
+
the default buildx cache was read-only; the first group build and first policy
|
|
242
|
+
build failed with `frontend grpc server closed unexpectedly`
|
|
243
|
+
([group](evidence/issue-2/local-build-first.txt),
|
|
244
|
+
[policy](evidence/issue-2/local-build-policy-first.txt)). An exploratory legacy
|
|
245
|
+
API build reached `corepack prepare` but hit its 180-second timeout; a bounded
|
|
246
|
+
`docker pull node:24-alpine` then succeeded. The host briefly showed load 52.65
|
|
247
|
+
and full I/O pressure averaging 62.33% over 60 seconds. Retrying serial builds
|
|
248
|
+
with the writable buildx cache succeeded. No daemon or shared service was
|
|
249
|
+
restarted. A separate source dependency preparation completed with:
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
COREPACK_HOME=/tmp/wacando-agent2-corepack timeout 600s pnpm install --frozen-lockfile --store-dir /tmp/wacando-agent2-pnpm-store
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The first default Corepack attempt failed on its read-only home cache. The final
|
|
256
|
+
install exited 0 with the frozen lockfile unchanged; API execution used the
|
|
257
|
+
Docker image, not these host dependencies.
|
|
258
|
+
|
|
259
|
+
### Actual API/runtime checks
|
|
260
|
+
|
|
261
|
+
From the plugin checkout:
|
|
262
|
+
|
|
263
|
+
```sh
|
|
264
|
+
npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm ci --include=dev
|
|
265
|
+
WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 timeout 600s node --test tests/onboarding-live.test.mjs
|
|
266
|
+
npm_config_cache=/tmp/wacando-issue2-npm-cache WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 timeout 600s npm run check
|
|
267
|
+
npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm run check:mcp
|
|
268
|
+
node --check scripts/check-host-onboarding.mjs
|
|
269
|
+
git diff --check
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
All exit 0 at that stage (`git diff --check` checked then-uncommitted changes,
|
|
273
|
+
not the cumulative PR diff containing preserved raw logs). The initial checker
|
|
274
|
+
could not import `esbuild`; `npm ci` without
|
|
275
|
+
`--include=dev` inherited production dependency selection and did not install
|
|
276
|
+
it. The explicit dev install added two packages and resolved that prerequisite.
|
|
277
|
+
|
|
278
|
+
- [Standalone live suite](evidence/issue-2/local-live.txt): **3 passed, 0 failed,
|
|
279
|
+
0 skipped**, with the installed Codex/Cursor/Claude runtimes and real
|
|
280
|
+
API/Postgres/ejabberd/OMEMO. It exercises official verification, enrollment,
|
|
281
|
+
public-bundle publication, a fresh MCP process resuming with consumed token,
|
|
282
|
+
byte-stable private identity, owner remote revocation and explicit rejection.
|
|
283
|
+
- [Full final check](evidence/issue-2/local-check.txt): **57 passed, 0 failed,
|
|
284
|
+
0 skipped**, including those three real integration cases and the existing
|
|
285
|
+
shutdown-lock regressions. Generated runtimes match shared sources. No
|
|
286
|
+
separate plugin lint/typecheck scripts exist.
|
|
287
|
+
- [MCP Inspector](evidence/issue-2/local-check-mcp.txt): official Inspector 2.7.0
|
|
288
|
+
passes all three hosts. No MCP wire or generated-runtime change was needed in
|
|
289
|
+
this follow-up; the checker itself is a development controller.
|
|
290
|
+
|
|
291
|
+
The direct runtime suite requires actual parsed `connected: true` and proves
|
|
292
|
+
issue #2's agreed runtime acceptance: identity resume and owner revocation in
|
|
293
|
+
all three profiles. It does not qualify any real CLI event stream or host UI;
|
|
294
|
+
that separate qualification belongs to Wacando #17. It does not mock the XMPP
|
|
295
|
+
contract or delete a lock.
|
|
296
|
+
|
|
297
|
+
### Actual CLI attempts and residual sandbox limitation
|
|
298
|
+
|
|
299
|
+
```sh
|
|
300
|
+
WACANDO_CHECKOUT=/home/kyle/timon-projects/wacando/agent2-api WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 WACANDO_TEST_HOST=cursor timeout 600s node scripts/check-host-onboarding.mjs
|
|
301
|
+
WACANDO_CHECKOUT=/home/kyle/timon-projects/wacando/agent2-api WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 WACANDO_TEST_HOST=codex timeout 600s node scripts/check-host-onboarding.mjs
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Both exit 1, **before any confirmed MCP call**. Outputs:
|
|
305
|
+
[Cursor](evidence/issue-2/local-cursor.txt),
|
|
306
|
+
[Codex](evidence/issue-2/local-codex.txt). Each reports `Host exit=1`, `locks=none`,
|
|
307
|
+
`calls=0`, with raw host output suppressed. No `verified: true` or
|
|
308
|
+
`connected: true` host payload was obtained; host enrollment, resume and remote
|
|
309
|
+
revocation stages were therefore not reached.
|
|
310
|
+
|
|
311
|
+
The initial Cursor attempt failed at `mcp enable`: the runner exposes a
|
|
312
|
+
read-only `/tmp/.git`, so Cursor resolved the ancestor as its project root and
|
|
313
|
+
missed the fixture's `.cursor/mcp.json`. The checker now creates its own Git
|
|
314
|
+
root, directs Cursor project data and compile cache into the fixture, and
|
|
315
|
+
disables Cursor debug logs. This fixes configuration lookup without editing
|
|
316
|
+
global host configuration or changing home/credential locations. The next
|
|
317
|
+
attempt passed enable and reached the CLI, which exited before tool invocation.
|
|
318
|
+
|
|
319
|
+
Minimal probes containing no onboarding secrets isolated the remaining errors:
|
|
320
|
+
|
|
321
|
+
```sh
|
|
322
|
+
git init --quiet /tmp/wacando-cursor-config-qGCIeU
|
|
323
|
+
CURSOR_DATA_DIR=/tmp/wacando-cursor-config-qGCIeU/cursor-data NODE_COMPILE_CACHE=/tmp/wacando-cursor-config-qGCIeU/cursor-cache CURSOR_AGENT_DISABLE_DEBUG_LOG=1 timeout 180s cursor-agent --print --output-format stream-json --auto-review --trust --workspace /tmp/wacando-cursor-config-qGCIeU 'Do not use any tools. Return only ready.'
|
|
324
|
+
timeout 180s codex exec --ephemeral --json --skip-git-repo-check --approve-for-me -C /tmp 'Do not use any tools. Return only ready.'
|
|
325
|
+
timeout 180s codex exec --ephemeral --json --skip-git-repo-check --approve-for-me -c 'sqlite_home="/tmp/wacando-agent2-codex-sqlite"' -c 'log_dir="/tmp/wacando-agent2-codex-logs"' -C /tmp 'Do not use any tools. Return only ready.'
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Each exits 1. Cursor's [actual diagnostic](evidence/issue-2/local-cursor-probe.txt):
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
Error: EROFS: read-only file system, open '/home/kyle/.cursor/sandbox-policies/sandbox-policy-ab97f73e2a601171'
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Codex's [initial](evidence/issue-2/local-codex-probe.txt) and
|
|
335
|
+
[isolated SQLite/log](evidence/issue-2/local-codex-probe2.txt) diagnostics:
|
|
336
|
+
|
|
337
|
+
```text
|
|
338
|
+
Error: failed to initialize in-process app-server client: Read-only file system (os error 30)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The launched mission's filesystem policy permits writes only in the project
|
|
342
|
+
and temporary roots; required host state locations are read-only. No policy
|
|
343
|
+
was disabled, no permissions widened, no home variable changed and no host
|
|
344
|
+
credentials copied or modified. Cursor/Codex CLI execution could not reach
|
|
345
|
+
MCP in this sandbox. No actual Claude CLI onboarding attempt is recorded here;
|
|
346
|
+
its passing runtime profile is not CLI evidence. All three CLIs remain
|
|
347
|
+
**NOT QUALIFIED in this sandbox**, with follow-up in Wacando #17 rather than
|
|
348
|
+
an additional acceptance blocker for issue #2. This documentary follow-up
|
|
349
|
+
requests no sandbox, HOME or credential access and changes no host installation
|
|
350
|
+
or configuration.
|
|
351
|
+
|
|
352
|
+
### Cleanup and residual risks
|
|
353
|
+
|
|
354
|
+
```sh
|
|
355
|
+
timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json down -v
|
|
356
|
+
docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json ps
|
|
357
|
+
docker volume ls --filter label=com.docker.compose.project=wacando-agent2
|
|
358
|
+
docker inspect wacando-issue10-postgres wacando-issue10-ejabberd --format '{{.Name}} status={{.State.Status}} started={{.State.StartedAt}} restarts={{.RestartCount}}'
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Exit 0. [Complete cleanup output](evidence/issue-2/local-down.txt) confirms all
|
|
362
|
+
five containers, the network and three named volumes removed, with no project
|
|
363
|
+
containers/volumes remaining. Issue-10 services were still running with zero
|
|
364
|
+
restarts and start times before this qualification. Synthetic identities,
|
|
365
|
+
owner sessions and server-side metadata disappeared with the isolated volumes;
|
|
366
|
+
fixture plugin state was cleaned normally. No lock was removed to manufacture
|
|
367
|
+
a success or retry the same identity.
|
|
368
|
+
|
|
369
|
+
- **HIGH, residual CLI qualification:** actual Cursor/Codex/Claude CLI
|
|
370
|
+
verification, identity resume after host restart, preserved keys and remote
|
|
371
|
+
revocation are **NOT QUALIFIED in this sandbox**. Cursor/Codex attempts failed
|
|
372
|
+
before MCP because of read-only host state; Claude CLI has no recorded attempt.
|
|
373
|
+
Host event-shape compatibility remains unproven. Wacando #17 owns this work;
|
|
374
|
+
the runtime PASS applies only to the agreed issue #2 acceptance scope.
|
|
375
|
+
- **MEDIUM, residual:** SIGKILL, a short host termination deadline or the
|
|
376
|
+
45-second cleanup cap may leave stale locks. The fix does not recover crash
|
|
377
|
+
locks; existing locks stay preserved.
|
|
378
|
+
- Cursor IDE/Desktop, Codex Desktop, Claude host UI, macOS/Windows, encrypted
|
|
379
|
+
conversation exchange, future pairing/app flow and production were not tested.
|
|
380
|
+
|
|
381
|
+
Implementation and issue #2 runtime acceptance checks: **PASS** (three profiles,
|
|
382
|
+
real Postgres/ejabberd/OMEMO; identity resume and revocation).
|
|
383
|
+
Actual CLI qualification: **NOT QUALIFIED in this sandbox**, tracked in Wacando #17.
|
|
384
|
+
Independent review: **PENDING**. [PR #9](https://github.com/lbframe/wacando-agent/pull/9)
|
|
385
|
+
remains draft; issue #2 stays open until review/merge. No merge or publication.
|
|
386
|
+
The [Wacando #42](https://github.com/lbframe/wacando/issues/42) retirement dependency
|
|
387
|
+
remains: this evidence uses v1 web onboarding and its prompt builder, and does
|
|
388
|
+
not qualify the replacement app/pairing flow.
|
|
389
|
+
|
|
390
|
+
The [documentary follow-up evidence](evidence/issue-2/DOCUMENTARY_FOLLOWUP.md)
|
|
391
|
+
records the scope decision and checks separately from the unchanged raw logs.
|
|
392
|
+
|
|
393
|
+
## Manual CLI qualification after host restart
|
|
394
|
+
|
|
395
|
+
**Procedure only, NOT EXECUTED by this documentary follow-up.** This is the
|
|
396
|
+
reproducible follow-up for [Wacando #17](https://github.com/lbframe/wacando/issues/17),
|
|
397
|
+
not additional evidence of a CLI PASS. Commands below are for an operator's
|
|
398
|
+
terminal on an already configured, authenticated host. The previous assertion
|
|
399
|
+
that all three CLIs' `--help` checks passed has no archived output and is
|
|
400
|
+
withdrawn, including the Claude claim. This follow-up separately archives
|
|
401
|
+
[Codex help](evidence/issue-2/codex-help.json),
|
|
402
|
+
[Codex exec help](evidence/issue-2/codex-exec-help.json) and
|
|
403
|
+
[Cursor help](evidence/issue-2/cursor-help.json), each exit 0; only their flags
|
|
404
|
+
are checked. No `claude --help` was run by this follow-up; Claude flags remain
|
|
405
|
+
unverified. Before using a launch command, verify its flags on that target
|
|
406
|
+
host; unavailable or incompatible commands leave that CLI NOT QUALIFIED.
|
|
407
|
+
Help success does not qualify MCP behavior.
|
|
408
|
+
|
|
409
|
+
The [original reviewed procedure snapshot](https://github.com/lbframe/wacando-agent/blob/abd3e52a285808df5b4cc3e1f53621421ef9b316/docs/CURSOR_IDENTITY_RESUME.md#manual-cli-qualification-after-host-restart)
|
|
410
|
+
is immutable and survives branch deletion. That snapshot predates the revocation
|
|
411
|
+
wording correction below; use the corrected acceptance in this document.
|
|
412
|
+
|
|
413
|
+
### Preconditions and baseline, separately for each CLI
|
|
414
|
+
|
|
415
|
+
Use a designated test identity and an approved service with real Postgres,
|
|
416
|
+
ejabberd and OMEMO. Record the service revision, plugin revision and CLI version.
|
|
417
|
+
The installed package must include PR #9's cleanup fix. Its existing MCP
|
|
418
|
+
configuration must use the exact same API origin and protected state directory
|
|
419
|
+
before and after restart. Keep each host's own installation/agent and origin/host
|
|
420
|
+
partition; do not transfer state between Cursor, Codex and Claude.
|
|
421
|
+
`qualification_origin` must equal `new URL(configuredApiUrl).origin` exactly,
|
|
422
|
+
including normalized scheme, hostname and port, with no trailing slash or path
|
|
423
|
+
(for example, `http://localhost:23001`). The service stores state under the hash
|
|
424
|
+
of `URL.origin + "\n" + host`; the fingerprint function hashes the supplied
|
|
425
|
+
string literally. A trailing slash selects a different partition and fails
|
|
426
|
+
closed (typically ENOENT); it is not an identity change or a PASS.
|
|
427
|
+
|
|
428
|
+
The owner first completes the official installation/enrollment flow for that
|
|
429
|
+
CLI: observe the actual MCP payloads `verified: true` and `connected: true`, plus
|
|
430
|
+
owner-visible verification/connection. A host's prose, exit code, or outer
|
|
431
|
+
`isError: false` is insufficient. This procedure starts with that already
|
|
432
|
+
enrolled identity; it installs nothing and changes no host configuration.
|
|
433
|
+
|
|
434
|
+
Replace the angle-bracket placeholders below with local paths and public IDs
|
|
435
|
+
from that identity's metadata. They are not authentication credentials. Use the
|
|
436
|
+
existing protected state directory; do not change HOME or read/copy any durable
|
|
437
|
+
credential. Use a trusted existing workspace. For Cursor/Claude, the package
|
|
438
|
+
paths are the existing installed plugin directories, not marketplace roots.
|
|
439
|
+
|
|
440
|
+
```sh
|
|
441
|
+
qualification_host='cursor' # Run separately with cursor, codex, then claude.
|
|
442
|
+
qualification_workspace='<absolute existing trusted workspace>'
|
|
443
|
+
qualification_origin='<exact URL.origin of configured API; no trailing slash>'
|
|
444
|
+
qualification_agent_id='<public agent ID>'
|
|
445
|
+
qualification_handle='<public @username/agent handle>'
|
|
446
|
+
qualification_installation_id='<public installation ID>'
|
|
447
|
+
qualification_state='<absolute existing protected state directory>'
|
|
448
|
+
cursor_package='<absolute installed wacando-cursor directory>'
|
|
449
|
+
claude_package='<absolute installed wacando-claude directory>'
|
|
450
|
+
|
|
451
|
+
codex --version
|
|
452
|
+
cursor-agent --version
|
|
453
|
+
claude --version
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
The following local operator function reads only the selected OMEMO state and
|
|
457
|
+
emits a SHA-256 fingerprint of `identityKeyPair`, never the private key material.
|
|
458
|
+
The baseline remains local. Do not attach state files or raw CLI transcripts to
|
|
459
|
+
issues. After normal closure of the initial enrolled host, check its processes
|
|
460
|
+
have exited; the command also refuses a remaining lock. Do not delete a lock or
|
|
461
|
+
regenerate keys to pass this procedure.
|
|
462
|
+
|
|
463
|
+
```sh
|
|
464
|
+
qualification_identity() {
|
|
465
|
+
node --input-type=module - "$qualification_state" "$qualification_origin" "$qualification_host" "$qualification_agent_id" <<'NODE'
|
|
466
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
467
|
+
import { createHash } from 'node:crypto';
|
|
468
|
+
import { join } from 'node:path';
|
|
469
|
+
const [root, origin, host, agentId] = process.argv.slice(2);
|
|
470
|
+
const hash = value => createHash('sha256').update(value).digest('hex');
|
|
471
|
+
const partition = join(root, hash(`${origin}\n${host}`));
|
|
472
|
+
if ((await readdir(partition)).some(name => name.endsWith('.lock'))) {
|
|
473
|
+
throw new Error('Host cleanup left a lock; qualification failed. Preserve state.');
|
|
474
|
+
}
|
|
475
|
+
const state = JSON.parse(await readFile(join(partition, `omemo-${hash(agentId)}`, 'classic-state.json'), 'utf8'));
|
|
476
|
+
if (!state.identityKeyPair) throw new Error('Missing identity; qualification failed.');
|
|
477
|
+
console.log(hash(JSON.stringify(state.identityKeyPair)));
|
|
478
|
+
NODE
|
|
479
|
+
}
|
|
480
|
+
qualification_evidence="$qualification_workspace/.wacando-qualification-$qualification_host"
|
|
481
|
+
(umask 077; mkdir "$qualification_evidence")
|
|
482
|
+
qualification_identity > "$qualification_evidence/identity-before.sha256"
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
Require exit 0 at every step; stop if baseline creation fails. Keep the baseline
|
|
486
|
+
directory across any machine reboot. The fingerprint is for local comparison;
|
|
487
|
+
record only the comparison result in shared evidence.
|
|
488
|
+
|
|
489
|
+
### Start a fresh host and resume, per CLI
|
|
490
|
+
|
|
491
|
+
Quit the initial CLI normally and wait for its MCP process to finish cleanup.
|
|
492
|
+
Start a new CLI process, not a resumed chat. For a machine-reboot run, shut down
|
|
493
|
+
normally, reboot through the host's usual controls, restore service availability,
|
|
494
|
+
and repeat the variable/function definitions above with exactly the same values;
|
|
495
|
+
keep the existing baseline directory and do not rerun `mkdir` or overwrite it.
|
|
496
|
+
Record which restart was exercised (CLI process or machine). No reboot was
|
|
497
|
+
performed in the sandbox evidence.
|
|
498
|
+
|
|
499
|
+
Build the same public-ID-only prompt before each launch:
|
|
500
|
+
|
|
501
|
+
```sh
|
|
502
|
+
resume_prompt="Invoke the installed Wacando MCP tool wacando_enroll_agent exactly once with {\"apiBaseUrl\":\"$qualification_origin\",\"agentId\":\"$qualification_agent_id\",\"handle\":\"$qualification_handle\",\"installationId\":\"$qualification_installation_id\",\"enrollmentToken\":\"already-consumed\"}. Return its actual safe tool payload. Use no shell, HTTP or transport substitute. Do not inspect credentials or record this prompt. If the tool is unavailable or fails, report the failure and stop."
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Run only the command for the selected host, with its normal MCP approvals:
|
|
506
|
+
|
|
507
|
+
| CLI | Exact launch command | Required observation |
|
|
508
|
+
| --- | --- | --- |
|
|
509
|
+
| Cursor | `cursor-agent --workspace "$qualification_workspace" --plugin-dir "$cursor_package" "$resume_prompt"` | Actual `wacando_enroll_agent` tool payload has `connected: true` and the same handle; expand the tool result instead of trusting Cursor's outer success wrapper. |
|
|
510
|
+
| Codex | `codex -C "$qualification_workspace" "$resume_prompt"` | The already activated Wacando MCP server returns `connected: true` and the same handle in the actual tool payload. |
|
|
511
|
+
| Claude Code | From `cd "$qualification_workspace"`, run `claude --plugin-dir "$claude_package" "$resume_prompt"` | Actual Wacando tool payload has `connected: true` and the same handle. The automated Codex/Cursor event parser does not support Claude; inspect its actual MCP tool result manually. |
|
|
512
|
+
|
|
513
|
+
Keep the host open long enough to observe the tool result and the owner-visible
|
|
514
|
+
connection. `already-consumed` is a deliberately unusable placeholder: successful
|
|
515
|
+
resume must use the saved durable identity without a fresh token, installation,
|
|
516
|
+
agent or keypair. Then quit normally, wait for this host's MCP process to exit,
|
|
517
|
+
and run:
|
|
518
|
+
|
|
519
|
+
```sh
|
|
520
|
+
qualification_identity > "$qualification_evidence/identity-after.sha256"
|
|
521
|
+
cmp "$qualification_evidence/identity-before.sha256" "$qualification_evidence/identity-after.sha256"
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Expected: both commands exit 0, `cmp` is silent, and no lock remains. Any missing
|
|
525
|
+
payload, timeout, forced termination, changed fingerprint or remaining lock is
|
|
526
|
+
FAIL for this CLI; preserve state and report the result without a retry that
|
|
527
|
+
removes a lock. Runtime PASS cannot turn that CLI result into PASS.
|
|
528
|
+
|
|
529
|
+
### Owner revocation and fresh-host rejection, per CLI
|
|
530
|
+
|
|
531
|
+
Using the official owner UI, revoke this same designated test agent and confirm
|
|
532
|
+
its revoked status. Start another fresh CLI with the same launch command and
|
|
533
|
+
`resume_prompt`. For this already-saved identity, not yet marked revoked
|
|
534
|
+
locally, `enroll()` skips token redemption and calls `open()`. The API's
|
|
535
|
+
403/`AGENT_REVOKED` rejection of durable access is handled there. Expected exact
|
|
536
|
+
MCP error text:
|
|
537
|
+
|
|
538
|
+
```text
|
|
539
|
+
Agent revoked or durable access rejected. Connection attempts have stopped. Check the identity on the site.
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
If background recovery has already persisted `revoked: true` before the call,
|
|
543
|
+
`enroll()` instead rejects with:
|
|
544
|
+
|
|
545
|
+
```text
|
|
546
|
+
Agent revoked. Connection attempts have stopped. Create a new identity on the site.
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
The shorter `Agent revoked. Connection attempts have stopped.` belongs to
|
|
550
|
+
initial token redemption when no agent is saved; it is not the expected path
|
|
551
|
+
for this procedure. These paths are in `packages/onboarding/service.mjs`
|
|
552
|
+
(`open()` catch, saved-revocation guard, and initial-enrollment catch).
|
|
553
|
+
|
|
554
|
+
Require exactly one actual `wacando_enroll_agent` result with `isError: true`,
|
|
555
|
+
rejection text matching the prefix `/^Agent revoked[. ]/`, and no
|
|
556
|
+
`connected: true` in that result. This accepts the relevant variants, consistent
|
|
557
|
+
with the live test's `/Agent revoked/` assertion and the stricter host parser's
|
|
558
|
+
prefix check. Host prose, an outer success/error wrapper, a missing tool result,
|
|
559
|
+
a generic error or a timeout cannot pass. The durable-access message also covers
|
|
560
|
+
other 401/403 rejections: the message alone does not prove owner revocation.
|
|
561
|
+
Require the earlier successful resume of the same identity and confirmed revoked
|
|
562
|
+
status in the official owner UI as well. The host must remain responsive.
|
|
563
|
+
|
|
564
|
+
Quit normally, then repeat the fingerprint comparison above: exit 0 and
|
|
565
|
+
identical identity, with no leftover lock. Revocation is the final step; do not
|
|
566
|
+
reuse that identity for a successful-resume scenario.
|
|
567
|
+
|
|
568
|
+
For each CLI, record the version, revisions, restart type, safe MCP result,
|
|
569
|
+
owner-visible status, normal shutdown, fingerprint equality, revocation result,
|
|
570
|
+
commands, exit codes, untested points, residual risks and PASS/FAIL. Never retain
|
|
571
|
+
temporary prompt tokens, cookies, credentials, private keys or message content.
|
|
572
|
+
Cursor/Codex EROFS before MCP means **NOT QUALIFIED**, never PASS; an unattempted
|
|
573
|
+
Claude CLI also stays **NOT QUALIFIED**. All three are currently **CLI not
|
|
574
|
+
qualified in the sandbox**.
|
|
575
|
+
|
|
576
|
+
**Wacando #42 dependency:** this v1 procedure's initial setup and historical
|
|
577
|
+
checker depend on the web onboarding flow and `onboarding-prompts.ts`. When
|
|
578
|
+
that flow is retired under [#42](https://github.com/lbframe/wacando/issues/42),
|
|
579
|
+
adapt initial setup to the approved replacement app/pairing flow for #17. Do not claim that this v1 evidence proves the future flow.
|