@abelxiaoxing/cadence 2.0.15 → 2.0.17
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/README.md +90 -20
- package/docs/service-management.md +58 -5
- package/package.json +2 -2
- package/src/service/coordination.ts +26 -9
- package/src/service-cli.mjs +1326 -922
- package/src/service-host.mjs +326 -254
- package/web/dist/assets/cadence.css +1 -1
- package/web/dist/assets/cadence.js +11 -11
- package/web/dist/manifest.json +6 -6
package/README.md
CHANGED
|
@@ -137,24 +137,30 @@ For manual checks, `pi -e ./src/index.ts -e ./scripts/agent-panel-demo.ts` adds
|
|
|
137
137
|
|
|
138
138
|
Open the private link that `cadence server install` printed, or that `cadence token-link --reveal` reveals.
|
|
139
139
|
Valid bootstrap redirects to a token-free URL and uses an HttpOnly SameSite cookie for subsequent HTTP/WebSocket access; no localStorage token copy.
|
|
140
|
-
The unified browser entry
|
|
140
|
+
The unified browser entry opens on the conversation shell, without an execution cwd, SDK session or default workspace.
|
|
141
|
+
Choose a working folder from the lower-right button; before a folder is selected, New conversation offers the same picker and Send stays disabled.
|
|
141
142
|
Only explicit Open starts or reuses a project owner.
|
|
142
143
|
Within that project, the browser opens on an ordinary conversation with a real message composer, retained multi-turn context and standard read/write/edit/bash tools; no Cadence task is created by Send.
|
|
143
|
-
Use `cadence <goal>` or `/cadence <goal>` at the beginning of the input, or select Cadence task, to
|
|
144
|
+
Use `cadence <goal>` or `/cadence <goal>` at the beginning of the input, or select Cadence task, then Send to create a task in the background and begin readonly analysis.
|
|
145
|
+
New task opens the same composer in task mode; there is no task-kind picker or creation dialog.
|
|
144
146
|
Mentions, quotations and fenced code do not select task mode.
|
|
145
|
-
|
|
147
|
+
Analysis appears in the task transcript.
|
|
148
|
+
Review implementation plan opens the retained proposal for one explicit Confirm plan and execute approval: save those exact bytes, then start only the matching graph under a fresh grant.
|
|
149
|
+
Changed context, failed publication or an uncertain reply stops the sequence without automatic retry.
|
|
150
|
+
Configured checks and human review remain separate.
|
|
146
151
|
A task created from an existing ordinary conversation retains a navigation link back; the old conversation is not converted and its history is not silently injected into the task.
|
|
147
152
|
All of this works without a live Pi terminal.
|
|
148
153
|
A token grants workspace access, not action consent.
|
|
149
|
-
|
|
154
|
+
Task Send authorizes only creation and readonly analysis.
|
|
155
|
+
Editing requires context-bound plan confirmation; the advanced inspector retains independent Save/Start controls.
|
|
150
156
|
Ordinary Send binds its exact text, conversation head, epoch, access and selected model; stale/duplicate/lost replies do not silently replay work.
|
|
151
157
|
The WebUI provides task/run/assignment navigation, long retained messages and tool detail, exact literal search, timeline and complete scoped NDJSON/Markdown downloads.
|
|
152
158
|
Desktop panes and phone drawers retain cancellation and keyboard controls.
|
|
153
159
|
Its chrome follows pi-web-ui: a compact topbar with view tabs and icon actions, a sectioned task sidebar, pill inspector tabs, transcript/tool cards and a bottom statusbar; phones wrap the topbar into an action row and an icon tab row.
|
|
154
|
-
The left sidebar
|
|
160
|
+
The left sidebar provides New conversation and New task alongside running/retained conversations; a separate lower-left dock observes Cadence subagents and opens their retained transcripts.
|
|
155
161
|
It is not pi-web-ui's subagent engine.
|
|
156
162
|
The right sidebar defaults to readonly files, bounded text previews and path-only references; task plan/check/change/review controls occupy its Task details tab.
|
|
157
|
-
The lower-right folder button browses directories on the service host and explicitly opens another independent project owner.
|
|
163
|
+
The lower-right folder button contains recent projects, browses directories on the service host and explicitly opens another independent project owner.
|
|
158
164
|
The unified entry switches project clients on the same origin, including an explicitly configured HTTPS proxy; it never changes a running task's cwd, registers a boot service or cancels the project left behind.
|
|
159
165
|
Backend cookies and native capabilities never reach the browser; proxied actions remain browser-authority actions, with owner-side consent/build/CSRF checks.
|
|
160
166
|
The gateway refuses the legacy project-level Open command: all project openings use its own association route, without returning an independent backend token link.
|
|
@@ -235,7 +241,9 @@ Storage exhaustion refuses new work rather than silently evicting history.
|
|
|
235
241
|
Workbench state lives separately under `<state-home>/cadence-assistant-v1/workbenches-v1/<configuration-hash>/`, not in a hidden substitute workspace.
|
|
236
242
|
Whole-workbench Stop joins its accepted openings and associated exact-epoch project owners, serializing their final registry releases; unrelated, unassociated owners are not stopped.
|
|
237
243
|
A changed epoch, uncertain launch or unconfirmed Stop keeps the workbench fenced, but does not prevent Stop from reaching its other known associated owners.
|
|
238
|
-
Workbenches never repair missing private state or erase uncertain locks.
|
|
244
|
+
Workbenches never repair missing private state or automatically erase uncertain locks.
|
|
245
|
+
`cadence workbench recover inspect|resolve` offers explicit local recovery after a dead gateway, with exact resources, durable evidence and fresh terminal acknowledgments; it preserves project fences/history and retires only the old workbench associations until another explicit Open.
|
|
246
|
+
Registrations from older versions without a hostname additionally require independent same-host attestation; unknown provenance is not recoverable by guessing a PID.
|
|
239
247
|
Service restart never resumes the old queue.
|
|
240
248
|
Uncertain process/auth/storage settlement keeps fences; use documented local informed recovery, not PID-only deletion or automatic cleanup.
|
|
241
249
|
|
|
@@ -276,8 +284,9 @@ bun pm pack --destination /absolute/private-temp
|
|
|
276
284
|
|
|
277
285
|
Build/verification use a fresh private directory with `TMPDIR`, `TEMP` and `TMP` set explicitly, on the checkout's disk filesystem rather than a nearly full tmpfs.
|
|
278
286
|
`verify` includes native/unit tests, web tests, required real-browser scenarios and exact real-pack auditing; missing required browser is non-green, never an automatic download.
|
|
279
|
-
It needs a Node with TypeScript type stripping (official builds; a distro Node built without it fails `build:web:check`)
|
|
280
|
-
|
|
287
|
+
It needs a Node with TypeScript type stripping (official builds; a distro Node built without it fails `build:web:check`).
|
|
288
|
+
`verify` keeps short regressions and loopback browser scenarios; it runs no real PTY workflow, HTTPS-proxy browser lane or multi-MiB cold-history scan.
|
|
289
|
+
Fabricated loopback providers are distinct from physical humans/devices, paid accounts, package-manager installation and native Windows/macOS/ConPTY qualification.
|
|
281
290
|
Those additional lanes remain unqualified unless their own receipts exist.
|
|
282
291
|
No passing gate authorizes commit, push, publication, installation, deployment or cleanup.
|
|
283
292
|
|
|
@@ -310,7 +319,7 @@ Historical logs are not runtime dependencies, and removing development tooling m
|
|
|
310
319
|
|
|
311
320
|
`src/service/{gateway,gateway-store,gateway-cli}.ts` owns the no-project entry, private workbench coordination and same-origin project routing, not task execution.
|
|
312
321
|
A root access rotation retires its proxy attachment IDs and detaches those exact browser viewers at the owners, without cancelling admitted work.
|
|
313
|
-
The first version requires a selected project for ordinary Send/tools; the empty workbench
|
|
322
|
+
The first version requires a selected project for ordinary Send/tools; the empty workbench presents the conversation shell with Send disabled, not an unbound chat executor.
|
|
314
323
|
`src/service/{workbench,conversations,workbench-files}.ts` owns ordinary conversations, their private retained turns, readonly files and explicit project navigation; `src/service/contracts/workbench.ts` is the portable closed DTO boundary.
|
|
315
324
|
Conversation auth intents and original settlements use their own flushed private journal, never fabricated task/history receipts.
|
|
316
325
|
Their original Stop lifetime and exclusive checkout barrier belong to the existing service owner and scheduler.
|
|
@@ -337,6 +346,14 @@ Use their current declarations rather than copying DTO parsing, reducers, a seco
|
|
|
337
346
|
Concurrent or interrupted resolvers cannot remove a successor lock; all project reservations, locks and history remain intact.
|
|
338
347
|
Unreserved partial release, unsafe/corrupt registry or receipt bytes, live/remote/ambiguous holders and changed resources stay fenced.
|
|
339
348
|
Readonly diagnosis and project Open name `registry-locked`; see [global registry recovery](docs/service-management.md#residual-global-registry-lock).
|
|
349
|
+
Missing retained project directories do not invalidate the global registry: their exact reservations and private fences remain, while canonical existing ancestors, aliases and overlaps are still checked.
|
|
350
|
+
Unbound `recover` addresses account/workbench state, not cwd; project recovery requires explicit `--workspace`.
|
|
351
|
+
- Workbench recovery is local and interactive, never acquire-on-dead-PID.
|
|
352
|
+
A bounded flushed receipt precedes exact gateway fence release; project registrations, locks, incomplete opening receipts and history are untouched.
|
|
353
|
+
The receipt retires the exact old project-reference incarnations from proxy/Stop association until explicit Open republishes them, preventing a deleted old directory from re-quarantining the next orderly Stop.
|
|
354
|
+
New workbench registrations record their host; legacy hostless registrations require an extra fresh same-host attestation, never automatic host inference.
|
|
355
|
+
Live/foreign/ambiguous owners and changed or unsafe resources refuse.
|
|
356
|
+
Generated Linux units exclude deterministic foreground refusal exit78 from restart; manager acceptance without authenticated readiness is not reported as successful startup.
|
|
340
357
|
- Native entry requires fresh raw Enter within1500ms, trusted/idle TUI, no pending modal/messages and no child guard.
|
|
341
358
|
Browser authentication grants access, not task consent; normal mutation ingress also requires independently built compatible client/service/assets identities.
|
|
342
359
|
- Boot model choice is absent until explicitly selected.
|
|
@@ -499,10 +516,10 @@ Correct: expose bounded continuations/full exports and report the exact executed
|
|
|
499
516
|
Read affected source and adjacent tests, identify the actual owner of a contract, then make the smallest coherent change.
|
|
500
517
|
Use ordinary source editing and directly affected checks while developing; independently review complete capabilities and run full `verify` at stable integration boundaries.
|
|
501
518
|
Do not run the entire suite after every local edit, but do not replace final integration with unit mocks or citation counts.
|
|
502
|
-
`bun run test` runs
|
|
503
|
-
|
|
504
|
-
`test/test-selection.test.ts` compares the runner's actual file discovery across all three configurations, rejecting duplicates, overlap, omissions and moved serial files; new tests belong to integration until explicitly promoted.
|
|
519
|
+
`bun run test` runs every root file in one parallel group; there are no serial groups or separate fast/integration lanes.
|
|
520
|
+
Its workers load `test/no-fsync.mjs`, which makes fsync a no-op inside them (spawned owners and CLIs still flush): crash tests kill processes and no test simulates power loss.
|
|
505
521
|
Create a fresh 0700 root on the checkout's disk filesystem and explicitly set TMPDIR/TEMP/TMP to it; a nearly full tmpfs `/tmp` makes owners refuse at their free-space floor (`receipt-inconclusive`) and `web/dist` publication fail with EXDEV.
|
|
522
|
+
Keep that root's absolute path short: Chromium appends fixture and singleton-socket components, and an overly long path makes it abort before browser scenarios begin.
|
|
506
523
|
A test waits for the event it needs (both providers contacted, a row settled), not a wall-clock guess at how long process starts take; fixed waits stay only where time is the contract.
|
|
507
524
|
Use fabricated credentials, isolated browser/profile/state roots and local fixtures; never personal credentials, automatic downloads or unknown process cleanup.
|
|
508
525
|
Outer command budgets must cover whole suites and preserve original runner waits; existing SDK20s/child15s, PTY90s/interaction15s and browser120s/action15s fixture budgets are not increased to obtain green.
|
|
@@ -511,27 +528,39 @@ Preserve failed attempts; correct a demonstrated cause before rerunning, and nev
|
|
|
511
528
|
Regression owners:
|
|
512
529
|
|
|
513
530
|
- `service-contract`, `service-current`, `service-model-choice`, `service-auth`: closed DTOs, exact source/choice/current authority, security and compatibility.
|
|
514
|
-
- `service-owner`, `service-accepted-work`, `service-
|
|
515
|
-
- `service-scheduler`, `service-checks`, `service-human-review`, `service-accepting-host`: real SDK overlap/exclusion, native checks, review and publication.
|
|
531
|
+
- `service-owner`, `service-accepted-work`, `service-recovery`, `service-registry-recovery`: original operations, overlap, Stop, unknown fences and informed recovery.
|
|
516
532
|
- `worker-*`, `lightweight-sdk`, `lightweight-process-diff`: genuine SDK/tool capture, split redaction, no-spill bounds and original native facts.
|
|
517
533
|
- `service-history-*`: durable ordering, cold/no-index/restarted proof/search/export, corruption/torn/quota and expected-byte publication.
|
|
518
|
-
- `lightweight-shell`, `native-client`, `native-history`, `terminal-integration`: native consent, shared service, Unicode/paging
|
|
534
|
+
- `lightweight-shell`, `native-client`, `native-history`, `terminal-integration`: native consent, shared service, Unicode/paging and detach/cancel.
|
|
519
535
|
- `agent-panel`, `agent-panel-model`, `agent-panel-cadence`: panel keys/rendering and readonly owner observation that never starts, cancels or replays work.
|
|
520
|
-
- `test/web/`: adapted pure/SSR tests separately from actual built-browser contexts,
|
|
536
|
+
- `test/web/`: adapted pure/SSR tests separately from actual built-browser contexts; `accepting-browser` drives one real SDK Start, check, review and departure-without-cancel workflow with mixed clients, focus/viewports and complete downloads.
|
|
521
537
|
- `package-*`, `distribution`, `service-package`, `independence`: exact tar/member/body/import/notice closure and nonactivating discovery.
|
|
522
538
|
- `service-autostart`, `project-start`, `install-config`: explicit `cadence server` registration on systemd (root system scope without linger, legacy user scope), launchd and the Windows Run key, separate named service definitions and directory owners, definition escaping, restart/disable/mask/foreign handling, orderly Stop before every change, the `.env` port and token lines and a package without install scripts.
|
|
523
539
|
- `service-cli`: real owners started from the `.env` port and token, reselection of a stopped workspace, edits while running and rotation of the token line.
|
|
524
540
|
- `workspace-landing`: no-default-workspace startup, explicit root refusal and noncreating native/readonly entry.
|
|
525
|
-
- `
|
|
541
|
+
- `gateway-browser`: real same-origin project routing, browser-only authority, retirement on token rotation, continuing ordinary work and genuine multi-owner Stop over loopback.
|
|
542
|
+
- `gateway-recovery`: real crashed owners/resolvers, exact gateway-only release, preserved project fences and unfinished openings, retired/reopened associations, live/remote/changed/linked refusals, generated CLI diagnostics and native-TTY host/process/credential/storage acknowledgments.
|
|
526
543
|
|
|
527
544
|
Regenerate operator/service/native/browser outputs only through their canonical builders; `--check` variants compare new private output without overwriting canonical bytes.
|
|
528
545
|
WebUI text is keyed by its English source: fixed text goes through `useT().t`, whose type requires a `web/src/i18n/zh.ts` entry with the same `{name}` marks, and run-time status words or kept notices through `tx`, which passes unknown text and codes through.
|
|
529
546
|
The timeline client takes the chosen language from its bridge and is remounted on a switch; browser scenarios pin `locale: "en-US"` so English assertions do not follow the host's `LANG`.
|
|
530
|
-
The browser is conversation-first: ordinary chat and explicit Cadence task conversations share its sidebar, while a lower-left dock observes actual Cadence child identities.
|
|
547
|
+
The browser is conversation-first, including before any project is selected: ordinary chat and explicit Cadence task conversations share its sidebar, while a lower-left dock observes actual Cadence child identities.
|
|
548
|
+
The empty entry has no project welcome screen or central Create shortcut; New conversation offers folder selection until a project is explicitly opened.
|
|
549
|
+
The bottom-right folder control is always present, and its picker lists recent projects.
|
|
550
|
+
Conversation history belongs to each project owner, so the unselected entry's history section says so and lists the recent projects; choosing one only preselects its folder in the picker.
|
|
551
|
+
An unselected first entry returns to the last selected project, remembered only in that browser as its opaque key, after one readonly conversation listing proves that owner still runs in its associated epoch; this is navigation, never Open.
|
|
552
|
+
A stopped, changed or unknown owner stays stopped: the entry remains and names the last project as not running, and any navigation, Open or folder choice during the check wins.
|
|
553
|
+
Browsing or dismissing the picker never opens a project or sends a message.
|
|
554
|
+
The picker has no separate Browse step: its location trail, folder list and Enter in the path entry navigate, and the footer names the exact Open target.
|
|
555
|
+
Open of a typed or recent path first makes one fresh readonly observation and opens only that observation's canonical folder; a failed read opens nothing and keeps the typed path and previous listing in view.
|
|
556
|
+
The footer Open is disabled while any observation is pending, including the first one; a recent entry still opens, because its own fresh observation supersedes the pending read.
|
|
557
|
+
Every navigation makes a fresh readonly observation, including retrying the same path after a read failure; it never replays Open.
|
|
558
|
+
Folder browsing (not Open) accepts `~` for the service account's home and reports a missing folder as `invalid`, a file or linked component as `forbidden` and a read failure as `unavailable`; the gateway passes these public codes on so the picker can explain them.
|
|
531
559
|
One service still owns one checkout.
|
|
532
560
|
The recent-project directory records only explicit project openings in the same private namespace; navigation opens that project's independent authenticated entry rather than changing global cwd, importing Pi sessions or adopting another account's state.
|
|
533
561
|
The center shows ordinary streamed messages or retained task text as Markdown.
|
|
534
|
-
Task tools/context/timeline/
|
|
562
|
+
Task tools/context/timeline/runs and export/link actions live under Details, shown only for a task or child scope; an ordinary conversation has no view switch and folds its tool calls into the transcript.
|
|
563
|
+
Runs puts the current snapshot's owner projection above the retained run pages.
|
|
535
564
|
The right panel defaults to files; its Task details inspector remains bound to the task's current plan/check/change/review context even when viewing a child.
|
|
536
565
|
Historical child links bind exact scope and watermark.
|
|
537
566
|
Bounded history and Back never silently rebase; running conversations offer an explicit new-activity update, and source limitations/progress stay visible even when complete-record metadata is folded.
|
|
@@ -542,6 +571,47 @@ Review the independently enumerated package member list and every emitted body,
|
|
|
542
571
|
Keep public Pi peers external for the extension, the exact Undici/ws runtime edges, the exact optional SDK pins for the standalone owner and all upstream/font/vendor notices.
|
|
543
572
|
Preserve original license bytes through their precise Git attributes; line-ending normalization must not invalidate audited source/notice hashes.
|
|
544
573
|
|
|
574
|
+
### Release 2.0.17 acceptance snapshot
|
|
575
|
+
|
|
576
|
+
Full `verify` passed on the release candidate in about four minutes: 1908 root tests in 71 files (27 platform skips on Linux), 16 task-entry node tests, 136 web tests, 5 real-browser scenarios in 4 files and the 135-member real-pack audit.
|
|
577
|
+
Observed environment: Linux x86_64, official Node 22.22.1, Bun 1.3.14, Pi SDK 0.84.3 and Chrome for Testing 153.0.8010.12, with four root workers and unchanged test deadlines.
|
|
578
|
+
`verify` no longer runs the slow real-process, PTY, HTTPS-proxy browser and multi-MiB cold-scan suites; those behaviors are not regression-gated in this release.
|
|
579
|
+
Task Send creates a task and starts readonly analysis, and one Confirm plan and execute saves and starts the reviewed plan.
|
|
580
|
+
Fixes in that flow: a fenced receipt read is observed again instead of ending the sequence, a transient context gap no longer aborts an in-flight approval, and a failed observation is re-read after 2s rather than leaving actions disabled until the 30s reconcile.
|
|
581
|
+
Ordinary conversations show no recorded-view switch, and Runs includes the current snapshot's owner projection.
|
|
582
|
+
The first merged attempts failed a time-budgeted search page and the new plan-approval browser step, which also failed on its original commit; both causes were fixed, not retried around.
|
|
583
|
+
No consumer service was installed or restarted, no paid provider was used and no native Windows/macOS qualification is claimed; the `ci/windows` matrix still names the removed vitest projects and was not run.
|
|
584
|
+
|
|
585
|
+
### Release 2.0.16 acceptance snapshot
|
|
586
|
+
|
|
587
|
+
The final code candidate passed all 2070 root tests in 86 files (27 platform skips on Linux), 133 web tests, all 9 real-browser scenarios and the 135-member exact real-pack audit.
|
|
588
|
+
Source/type/build/lint/Agent checks and a separate Bun pack/body audit also passed; this is separate-lane evidence, not a claim that one `verify` invocation was entirely green.
|
|
589
|
+
Observed environment: Linux x86_64, Node26.7.0, Bun1.3.14, Pi SDK0.84.3 and existing Chrome for Testing151.0.7922.34, with `VITEST_MAX_WORKERS=1` and unchanged test deadlines.
|
|
590
|
+
Review added failing-then-passing regressions for a successor lock borrowing a stopped predecessor's PID, unexpected foreground management exceptions being misclassified as exit78, and an accepted Stop abandoning its exact-epoch wait after an inconclusive registration read.
|
|
591
|
+
The stopped-row recovery bridge must belong to the exact current lock; Stop never resends its request or infers closure from a read failure or successor epoch.
|
|
592
|
+
The first full attempt had three pre-existing test deadlines and a workbench Stop with empty CLI output; isolated diagnostics passed the timeout files, and the original timing and empty-output causes remain unconfirmed.
|
|
593
|
+
After the independently reproduced Stop race was fixed, the second full attempt passed root/web checks but every browser launch hit Chromium's explicit singleton-socket path-length refusal because the private temporary root was too long.
|
|
594
|
+
Using a fresh short private root on the same disk, all browser scenarios and the pack audit passed without code changes, larger budgets, skipped tests or downloads.
|
|
595
|
+
Failure logs and isolated receipts remain outside the checkout; no consumer service was installed/restarted, uncertain state cleaned, paid provider used or new native Windows/macOS qualification claimed.
|
|
596
|
+
|
|
597
|
+
### Workbench recovery review snapshot
|
|
598
|
+
|
|
599
|
+
The recovery review reproduces the still-reserved deleted-project failure, adds gateway-only native recovery (including legacy host attestation), and exercises restart/Stop after retiring old project associations without deleting references or project fences.
|
|
600
|
+
The new gateway recovery file passes 30 tests, including actual owner/resolver exits, concurrent resolvers, interrupted release, unsafe/changed resources, real generated startup refusal/restart/Stop and genuine POSIX TTY confirmation; global registry recovery passes 34 tests.
|
|
601
|
+
Full `verify` passed static/build/lint/Agent checks and reached 2064 passing root tests with 27 platform skips, but one unchanged `service-history-projection` dense-message test exceeded its 15s deadline.
|
|
602
|
+
Its complete 24-test file passed in a subsequent isolated diagnostic run (the affected case took 1.83s); the original timing cause is unconfirmed, not retrospectively successful.
|
|
603
|
+
After the final startup/Stop diagnostic wording correction, 121 targeted CLI/recovery tests, source/generated checks, 133 web tests, all 9 browser scenarios and the 135-member real-pack audit passed.
|
|
604
|
+
A separate Bun pack/body audit also passed; failures and isolated fixture receipts remain outside the checkout, with no increased test budgets, downloaded dependencies, consumer lock cleanup or service installation/restart.
|
|
605
|
+
Actual WSL shutdown, real service-manager installation and new native Windows/macOS recovery qualification were not executed; this is not a full green `verify` claim.
|
|
606
|
+
|
|
607
|
+
### Conversation-first entry review snapshot
|
|
608
|
+
|
|
609
|
+
The project-less conversation shell and folder-picker review passed 133 web tests, all 9 real-browser scenarios and the 135-member real-pack audit, plus a separate Bun pack/body audit.
|
|
610
|
+
New loopback and HTTPS regressions refuse Open after an unbrowsed path edit, retry the same readonly directory observation after failure, and fence Open while a refresh is pending; the two original picker defects were observed failing before their fixes.
|
|
611
|
+
Full `verify` passed its static/build/lint/Agent checks but stopped in the root suite: 2033 tests passed, 27 platform tests skipped, and the unchanged `service-history-search-export` field-kind/snapshot case exceeded its 5s deadline.
|
|
612
|
+
That complete 16-test file passed in a subsequent isolated diagnostic run (the affected case took 1.16s); the original timing cause is unconfirmed, and this is not a full green `verify` claim.
|
|
613
|
+
Failed logs were retained outside the checkout; no deadline was increased, test skipped to obtain green, dependency downloaded or consumer service installed/restarted.
|
|
614
|
+
|
|
545
615
|
### Release 2.0.15 acceptance snapshot
|
|
546
616
|
|
|
547
617
|
The release candidate passed the complete root suite: 2034 tests in 85 files, with 27 platform skips on Linux.
|
|
@@ -21,7 +21,8 @@ Defaults are loopback `127.0.0.1:17423`, normal Pi agent configuration, and deta
|
|
|
21
21
|
An explicit port0 selects and persists a positive port once; restart uses the selected configuration, not a fallback endpoint.
|
|
22
22
|
Missing/corrupt existing setup is preserved, not filled in or reset.
|
|
23
23
|
Commands without `--workspace` address the unified workbench, which has no execution cwd and never creates `~/cadence`.
|
|
24
|
-
Use `cadence workbench start|status|diagnose|stop|token-link|token-rotate` to name that target explicitly.
|
|
24
|
+
Use `cadence workbench start|status|diagnose|stop|token-link|token-rotate|recover inspect|recover resolve` to name that target explicitly.
|
|
25
|
+
`recover` without `--workspace` also addresses account/workbench state, never the current directory; project-owner recovery requires an explicit `--workspace`.
|
|
25
26
|
Its start accepts `--foreground`, `--state-home`, `--agent-dir`, `--server-config` and a private `--config` containing an `HttpPolicy` object (the schema used below for project HTTP policy).
|
|
26
27
|
The configuration's port/token come from the `.env`; a selected HTTP policy is retained across orderly restarts.
|
|
27
28
|
A project root containing the private state home is refused, with no alternate directory creation; native `/cadence` also requires a real project selection.
|
|
@@ -47,7 +48,7 @@ Keep that link private.
|
|
|
47
48
|
Open Pi in the same project and use `/cadence` for the native client; explicit `cadence stop --workspace <path>` stops only that project.
|
|
48
49
|
Opening another project never changes the cwd or cancels work in the former one.
|
|
49
50
|
The default workbench routes all associated projects through one origin, including a configured HTTPS proxy.
|
|
50
|
-
Its project picker browses directory names readonly; only **Open this project
|
|
51
|
+
Its project picker browses directory names readonly (`~` names the service account's home); only **Open this folder**, or one click on a recent project, starts or reuses an owner, without admitting a task or model call.
|
|
51
52
|
Project navigation remounts only the browser client; the original owner's work continues.
|
|
52
53
|
Browser proxy traffic keeps browser authority and the owner's own attachments, CSRF/build checks, grants and history proofs; native management routes and local-capability headers are not forwarded.
|
|
53
54
|
The gateway also refuses the legacy project-level Open command, so every opening is associated through the workbench instead of returning an independent backend token link.
|
|
@@ -91,6 +92,8 @@ A reinstall without `--workspace` explicitly selects the unified entry after ord
|
|
|
91
92
|
A plain `restart` retains the definition's existing target, including an older explicit project service, rather than silently migrating it.
|
|
92
93
|
The `.env` [port and token](#port-and-token) select the endpoint (17423 by default) and the token; an occupied endpoint fails instead of falling back.
|
|
93
94
|
Install and restart print the private entry link once the owner answers; the JSON result on stdout never carries the token.
|
|
95
|
+
A successful manager start request is not owner readiness: an unready owner reports `quarantined` or `unconfirmed`, retains the separate `managerStatus`, exits nonzero and names the recovery inspection command.
|
|
96
|
+
Status also retains the manager's `active`/`activating`/`failed` observation separately when the workbench itself is quarantined.
|
|
94
97
|
`restart` rewrites an existing definition with the current Node and package paths, keeping its workspace and the user's disable choices, and starts it; run it after an npm upgrade, a Node change or a `models.json` edit.
|
|
95
98
|
`stop` joins the workbench's accepted openings and associated exact-epoch project owners, then closes the entry; unrelated project owners stay independent.
|
|
96
99
|
If one associated owner refuses or has a changed epoch, Stop still reaches the other known owners serially, while the workbench remains fenced rather than reporting a successful Stop.
|
|
@@ -200,7 +203,10 @@ It carries `ConditionPathExists=` for the CLI; explicit single-project definitio
|
|
|
200
203
|
`ExecStop` is the authenticated orderly Stop, so admitted work is stopped, not detached; `stop` and `uninstall` (`disable --now`) use it too.
|
|
201
204
|
`KillMode=mixed` sends systemd's SIGTERM only to the management client.
|
|
202
205
|
An owner still settling after `TimeoutStopSec=90` (for example a quarantined one) is SIGKILLed, which is not orderly Stop and can leave recovery fences.
|
|
203
|
-
`Restart=on-failure` is bounded to three starts in five minutes
|
|
206
|
+
`Restart=on-failure` is bounded to three starts in five minutes.
|
|
207
|
+
Deterministic foreground startup refusals exit78 (`EX_CONFIG`), and newly generated units use `RestartPreventExitStatus=78` so these refusals do not loop.
|
|
208
|
+
Ordinary unexpected process failures retain the bounded restart policy; a restart never removes fences.
|
|
209
|
+
Existing generated units get this setting on the next successful explicit install/restart, not by package installation or recovery.
|
|
204
210
|
Without linger, user units start at login and stop at logout through the same Stop.
|
|
205
211
|
Linger starts the user manager, with every enabled user unit, at boot and keeps it running after logout.
|
|
206
212
|
`status` reports `linger` as `on` or `off`, and Pi's startup card names `loginctl enable-linger` while an enabled unit is login-only; a disabled or masked unit (also `mask --runtime`) keeps it quiet.
|
|
@@ -296,11 +302,18 @@ Local fabricated-provider plain/gzip routing tests are not paid-provider, all-ad
|
|
|
296
302
|
## Informed local recovery
|
|
297
303
|
|
|
298
304
|
```sh
|
|
305
|
+
# Account/workbench recovery (no cwd or project required):
|
|
306
|
+
cadence workbench recover inspect
|
|
307
|
+
cadence workbench recover resolve
|
|
308
|
+
# A specific project owner:
|
|
299
309
|
cadence recover inspect --workspace /absolute/project
|
|
300
310
|
cadence recover resolve --workspace /absolute/project
|
|
301
311
|
```
|
|
302
312
|
|
|
303
|
-
Inspection is readonly and handles the global registry lock before project-owner recovery.
|
|
313
|
+
Inspection is readonly and handles the global registry lock before the selected workbench or project-owner recovery.
|
|
314
|
+
Use the same account and state home as the service; workbench recovery also accepts `--server-config <path>` for a named or custom configuration.
|
|
315
|
+
A recovery refusal reports a fixed code/stage or `blocked-preserve-state`, never raw state/configuration bytes.
|
|
316
|
+
Do not delete state files or move to a different state home to bypass a refusal.
|
|
304
317
|
|
|
305
318
|
### Residual global registry lock
|
|
306
319
|
|
|
@@ -315,12 +328,16 @@ cadence recover resolve --workspace /absolute/project --state-home /absolute/sta
|
|
|
315
328
|
```
|
|
316
329
|
|
|
317
330
|
With a registry lock present, inspection reports `scope: "global-registry"`, the exact lock-incarnation digest, holder PID/host, reservation count and any previous resolver's liveness.
|
|
318
|
-
A project with no selected owner can be used to address this global operation;
|
|
331
|
+
A project with no selected owner can be used to address this global operation; explicit `--workspace` must still name an existing suitable directory.
|
|
332
|
+
Alternatively, `cadence workbench recover inspect|resolve --state-home <path>` addresses the same global lock without any project directory or workbench registration, including from outside a project or when every old project directory is gone.
|
|
319
333
|
Resolve requires the same four fresh local-terminal acknowledgments described below, with the displayed lock-incarnation digest instead of a service epoch.
|
|
320
334
|
The holder must be on this host and its PID probe must specifically report absence; age, a remote host, permission denial or a reused live PID never authorizes release.
|
|
321
335
|
|
|
322
336
|
Recovery validates the registry and checks that unreserved project selections have no remaining owner/checkout fences.
|
|
323
337
|
A normally stopped project whose working directory was later removed is checked through its retained private metadata; recovery neither requires nor reconstructs that directory.
|
|
338
|
+
A still-reserved project whose working directory was removed also remains a valid retained reservation: canonical existing ancestors and overlap/endpoint constraints are checked without requiring the missing tail.
|
|
339
|
+
Its reservation, owner/checkout locks and history remain fenced and unchanged; releasing the global lock only restores access to independent projects.
|
|
340
|
+
Dangling/rebound aliases, permission errors and corrupt entries are not treated as absent directories.
|
|
324
341
|
An interrupted reservation remains reserved even when its project initialization is incomplete: unrelated projects can reopen, but that project's uncertain resources are not repaired.
|
|
325
342
|
An older release interrupted after removing a reservation but before releasing its project locks is unsafe and remains blocked, as do corrupt/partial registry bytes, linked files and changed resources.
|
|
326
343
|
Inspection reports `blocked-preserve-state` when it cannot establish those conditions; preserve that state for manual diagnosis rather than deleting locks.
|
|
@@ -337,6 +354,42 @@ No Open/Start is replayed, no process is signaled, and no service is installed o
|
|
|
337
354
|
After success, explicitly open the desired project again.
|
|
338
355
|
If its own owner is still fenced, run `recover inspect` again and follow project-owner recovery separately; restoring global registry access does not certify or release that owner's work.
|
|
339
356
|
|
|
357
|
+
### Workbench recovery
|
|
358
|
+
|
|
359
|
+
After a crash or failed Stop, an `alive`, `stopping` or `quarantined` registration may outlive its gateway process.
|
|
360
|
+
Do not keep restarting it or delete `workbenches-v1`; inspect the exact selection instead:
|
|
361
|
+
|
|
362
|
+
```sh
|
|
363
|
+
cadence workbench recover inspect --server-config /absolute/cadence.env --state-home /absolute/state-home
|
|
364
|
+
cadence workbench recover resolve --server-config /absolute/cadence.env --state-home /absolute/state-home
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
If the first inspection reports `scope: "global-registry"`, resolve that lock first, then inspect again for `scope: "workbench"`.
|
|
368
|
+
Prevent the supervisor from repeatedly launching the old process while recovering; use its original account/scope and inspect processes externally.
|
|
369
|
+
A live owner, a known remote host, permission-denied PID probe, changed bytes/incarnations, linked or partial state, or a live/ambiguous resolver refuses release.
|
|
370
|
+
PID absence alone never grants recovery; the same four fresh terminal acknowledgments bind the exact workbench epoch and selected resources.
|
|
371
|
+
|
|
372
|
+
New registrations record their hostname.
|
|
373
|
+
Older registrations did not: inspection explicitly reports `legacyHost: true`, `host: null` and `local-host-confirmation-required`, not proven same-host absence.
|
|
374
|
+
Resolve then additionally requires typing `local-host` after independently establishing that this state originated on this host and was not copied/shared from another host.
|
|
375
|
+
If that provenance cannot be established, do not confirm; preserve the state for manual diagnosis.
|
|
376
|
+
This attestation cannot override a recorded foreign hostname or a live/ambiguous local PID.
|
|
377
|
+
No `--yes`, piped or pasted answers, browser recovery or implicit takeover is supported.
|
|
378
|
+
|
|
379
|
+
Before release, an exclusive immutable receipt under `workbenches-v1/<configuration-hash>/recovery-v1/<lock-incarnation>/<attempt>.json` is flushed and closed.
|
|
380
|
+
The resolver then rechecks the resources, receipt, owner absence and fresh terminal authority, marks only the gateway registration stopped and releases only its exact gateway lock.
|
|
381
|
+
Concurrent resolvers cannot release a successor lock; a resolver that crashed after its receipt can be superseded only after its same-host PID is absent and another fresh confirmation.
|
|
382
|
+
A stopped registration is eligible only when its pinned recovery receipt belongs to that exact lock incarnation; an interrupted new startup cannot borrow the predecessor's dead PID to release its new fence.
|
|
383
|
+
Partial/corrupt receipts remain preserved; there are at most128attempts per lock incarnation, with no evidence eviction.
|
|
384
|
+
|
|
385
|
+
Recovery also acknowledges and retires the gateway's **exact old project associations**, recorded by their reference-file incarnations in that receipt.
|
|
386
|
+
It does not delete those recent-project references or settle/stop their owners: all project registrations, reservations, locks, opening receipts and history remain unchanged, including unknown outcomes and unfinished Open operations.
|
|
387
|
+
This prevents a deleted old project from immediately quarantining the recovered workbench again on its next Stop.
|
|
388
|
+
Old references require a new explicit **Open** before proxy access or whole-workbench Stop associates them again; merely restarting, viewing the recent list or recovering never replays an Open.
|
|
389
|
+
After recovery, explicitly start/restart the workbench.
|
|
390
|
+
Separately inspect/recover any still-fenced project owner before opening it.
|
|
391
|
+
The receipt is operator acknowledgment, not proof of native process closure or credential/storage correctness.
|
|
392
|
+
|
|
340
393
|
### Project-owner recovery
|
|
341
394
|
|
|
342
395
|
With no registry lock present, inspection displays the epoch, exact coordination/history digests, owner PID/host observation and independent process/credential/storage uncertainty.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abelxiaoxing/cadence",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.17",
|
|
4
4
|
"description": "Shared Pi task workspace: native terminal and WebUI, persistent service, checks and human review.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"build:operator": "bun scripts/build-operator.mjs",
|
|
88
88
|
"build:service": "bun scripts/build-service.mjs",
|
|
89
89
|
"check:web": "tsc -p web/tsconfig.json && tsc -p web/tsconfig.config.json",
|
|
90
|
-
"test:web": "vitest run --config web/vitest.config.ts",
|
|
90
|
+
"test:web": "node --test test/web/task-entry.node.mjs && vitest run --config web/vitest.config.ts",
|
|
91
91
|
"build:web": "node scripts/build-web.mjs",
|
|
92
92
|
"build:web:check": "node scripts/build-web.mjs --check",
|
|
93
93
|
"test:web:assets-browser": "bun run test:browser",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
|
-
import { opendirSync
|
|
2
|
+
import { opendirSync } from "node:fs";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
5
5
|
import {
|
|
6
6
|
type CheckoutLock,
|
|
7
|
+
canonicalAmbientRoot,
|
|
8
|
+
defaultStateHome,
|
|
7
9
|
resolveWorkspace,
|
|
8
10
|
TaskStore,
|
|
9
11
|
} from "../task-store.ts";
|
|
@@ -245,19 +247,30 @@ export type CoordinationStatus =
|
|
|
245
247
|
* task creation, stale reclaim or accepting execution route lives here.
|
|
246
248
|
*/
|
|
247
249
|
export class ServiceCoordination {
|
|
248
|
-
readonly
|
|
250
|
+
private readonly project?: TaskStore;
|
|
249
251
|
readonly files: PrivateFiles;
|
|
250
|
-
readonly directory: string;
|
|
251
252
|
readonly registryDirectory: string;
|
|
252
253
|
readonly stateHome: string;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
/** null addresses only account-wide registry recovery, never an implicit cwd. */
|
|
255
|
+
constructor(workspace: string | null, stateHome?: string) {
|
|
256
|
+
this.project =
|
|
257
|
+
workspace === null
|
|
258
|
+
? undefined
|
|
259
|
+
: new TaskStore(resolveWorkspace(workspace).root, stateHome);
|
|
260
|
+
this.stateHome = this.project
|
|
261
|
+
? dirname(dirname(this.project.privateRoot))
|
|
262
|
+
: canonicalAmbientRoot(stateHome ?? defaultStateHome());
|
|
263
|
+
const namespace = join(this.stateHome, "cadence-assistant-v1");
|
|
256
264
|
this.files = new PrivateFiles(namespace);
|
|
257
|
-
this.stateHome = dirname(namespace);
|
|
258
|
-
this.directory = join(this.store.privateRoot, "service-v1");
|
|
259
265
|
this.registryDirectory = join(namespace, "service-registry-v1");
|
|
260
266
|
}
|
|
267
|
+
get store(): TaskStore {
|
|
268
|
+
if (!this.project) throw new ContractError("forbidden");
|
|
269
|
+
return this.project;
|
|
270
|
+
}
|
|
271
|
+
get directory(): string {
|
|
272
|
+
return join(this.store.privateRoot, "service-v1");
|
|
273
|
+
}
|
|
261
274
|
path(
|
|
262
275
|
name:
|
|
263
276
|
| "format.json"
|
|
@@ -289,9 +302,12 @@ export class ServiceCoordination {
|
|
|
289
302
|
const o = closed(json(snapshot, REGISTRY_BYTES), ["schema", "entries"]);
|
|
290
303
|
const entries = list(o.entries, REGISTRY_ENTRIES, decodeReservation);
|
|
291
304
|
for (const [i, row] of entries.entries()) {
|
|
305
|
+
// A missing consumer directory does not erase its reservation. Validate
|
|
306
|
+
// its canonical existing ancestors without recreating the missing tail;
|
|
307
|
+
// aliases (including dangling ones), permissions and overlaps still refuse.
|
|
292
308
|
if (
|
|
293
309
|
row.stateHome !== this.stateHome ||
|
|
294
|
-
|
|
310
|
+
canonicalAmbientRoot(row.root) !== row.root
|
|
295
311
|
)
|
|
296
312
|
refused();
|
|
297
313
|
// An endpoint cannot be shared by two supported owners, even for disjoint roots.
|
|
@@ -561,6 +577,7 @@ export class ServiceCoordination {
|
|
|
561
577
|
* selected host-config snapshot); only then may the identity change. */
|
|
562
578
|
reselectExisting?: (hostConfig: FileSnapshot) => void,
|
|
563
579
|
): OwnerLease {
|
|
580
|
+
void this.store; // An account-only recovery target cannot acquire an owner.
|
|
564
581
|
const config = decodeOwnerConfig(input);
|
|
565
582
|
const bound = endpoint(resolvedEndpoint);
|
|
566
583
|
if (!endpointMatches(config.endpoint, bound))
|