@abelxiaoxing/cadence 2.0.1 → 2.0.3

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 CHANGED
@@ -1,29 +1,24 @@
1
1
  # Cadence
2
2
 
3
3
  `@abelxiaoxing/cadence` is a shared Pi task workspace with two independently usable clients: native `/cadence` and a browser WebUI.
4
- One explicitly started service owns tasks, bounded subagent collaboration, real parent checks and retained history.
5
- Ordinary conversations, resource discovery, installation and opening history never start work or a service.
4
+ One persistent service owns tasks, bounded subagent collaboration, real parent checks and retained history.
5
+ Ordinary conversations, resource discovery and opening history never start work or a service.
6
+ On Linux, Pi's user-scope install registers that service as a systemd user unit and starts its WebUI on the default address.
6
7
 
7
8
  This is a trusted workflow assistant, **not a sandbox, certified executor or transactional application system**.
8
9
  Code workers edit the chosen working copy directly; recovery can require human work.
9
10
  Model prose, tool results and human Done do not certify checks or code.
10
11
 
11
- ## Upgrading from 1.x
12
-
13
- Version 2.0.0 replaces the four Abel workflow prompts (`/abel-init`, `/abel-design`, `/abel-implement`, `/abel-diagnose`) with native `/cadence` and the WebUI backed by one explicitly started service.
14
- This is a breaking change, not an in-place continuation of the 1.x workflow.
15
- Stop older Cadence sessions before switching; existing records and credentials are not imported, migrated, deleted or replayed automatically.
16
- Node `>=22.19.0` and the declared external peers, including Undici, are required; `ws` is now a production dependency.
17
- Installation still does not start the service or execute tasks; follow the loading and explicit startup instructions below.
18
-
19
12
  ## Load and start explicitly
20
13
 
21
14
  The package can be loaded as a reviewed local package, extracted tarball or installed npm package through Pi's package mechanism.
22
15
  For a local checkout, add its absolute or relative path to Pi's `packages` setting, or try `pi -e ./cadence`.
23
16
  Pi loads `src/index.ts`; the service, worker and readonly operator are ordinary JavaScript with no TypeScript/source-tree fallback.
24
- The manifest does not impose a Pi version range; public Pi peers stay external.
17
+ The manifest does not impose a Pi version range; inside Pi the extension uses Pi's own public packages.
25
18
  Development pins, declared Node baseline (`>=22.19.0`) and actual tested runtime identities are separate facts, not universal compatibility promises.
26
- Required installed peers include public Pi packages, Typebox and Undici; `ws` is the sole production dependency.
19
+ Required installed peers are public Pi packages and Typebox; `undici` and `ws` are exact production dependencies.
20
+ Pi's package installer omits peers and supplies only its own packages and Typebox to the extension, so any other extension import must be a production dependency.
21
+ The standalone owner and workers run outside Pi and import `pi-coding-agent`/`pi-ai` by bare name, so they load the exact tested copy (0.84.3) from optional dependencies; an install that omits optional dependencies cannot start an owner.
27
22
  Browser assets are prebuilt, not compiled by consumer postinstall.
28
23
 
29
24
  Before using the shared owner, stop older Cadence sessions and choose one coordination state-home for the checkout.
@@ -38,8 +33,23 @@ cadence-service token-link --workspace /absolute/project --reveal
38
33
  Use the installed `cadence-service` bin, or `node /absolute/cadence/src/service-cli.mjs` followed by those arguments.
39
34
  Default binding is numeric loopback `127.0.0.1:17423`; `--foreground` is available for an explicitly configured supervisor.
40
35
  Start reads the existing Pi agent directory (or an explicit private configuration), never creates another login/profile.
36
+ A directory that contains the private state home (the home directory and its ancestors, such as `C:\Users\<you>`) cannot be a workspace, so commands there use `~/cadence` instead of refusing; only `start` creates it, and `/cadence` started there attaches to that owner.
41
37
  See [service management](docs/service-management.md) for configuration, model/auth policy, Stop, diagnosis and informed recovery.
42
38
 
39
+ ### Linux autostart after `pi install`
40
+
41
+ `pi install npm:@abelxiaoxing/cadence` (user scope) writes `~/.local/share/systemd/user/cadence.service`, enables it and starts the owner for `~/cadence` on `http://127.0.0.1:17423`.
42
+ Set an existing absolute `CADENCE_WORKSPACE` at install to choose another workspace, or `CADENCE_AUTOSTART=0` to skip registration.
43
+ That first registration also enables linger when logind allows it without any authorization prompt, so the service starts at boot instead of at login; `CADENCE_LINGER=0` skips it, and later updates keep `loginctl disable-linger`.
44
+ Enter the WebUI once with the link from `cadence-service token-link --workspace ~/cadence --reveal`; installation prints that command, not the token.
45
+ Each `pi update` restarts the unit through orderly Stop, so running work is stopped rather than detached; `systemctl --user disable --now cadence` or `mask` is kept across upgrades.
46
+ Run `cadence-service autostart remove` before `pi remove`; after `remove`, a later `pi update` registers the unit again as a first registration, so keep it off with `disable` or `mask` instead.
47
+ The npm client hides dependency install-script output, and with `strict-allow-scripts` it skips those scripts entirely.
48
+ Approve Cadence once with `npm install-scripts approve @abelxiaoxing/cadence` inside `~/.pi/agent/npm` (or `npm config set allow-scripts=@abelxiaoxing/cadence --location=user`), or run `cadence-service autostart install --linger`, which may ask for your password.
49
+ Pi's session-start card names that command (without `--linger` under `CADENCE_LINGER=0`) while Pi's copy has no unit on a systemd machine, and `loginctl enable-linger` while the enabled unit only starts at login.
50
+ Until the optional SDK and Pi's `auth.json` exist, registration or start waits instead of quarantining an owner.
51
+ See [the systemd user unit](docs/service-management.md#linux-systemd-user-unit) for exact behavior and limits.
52
+
43
53
  ### Native terminal
44
54
 
45
55
  In a trusted, idle Pi TUI, type `/cadence [goal]` using fresh native Enter.
@@ -54,6 +64,27 @@ Return, session departure and Pi quit **detach; admitted work continues** while
54
64
  Reattach to observe the same IDs without replay.
55
65
  Explicit Cancel requests bounded stop; it is not proof that every escaped descendant stopped.
56
66
 
67
+ ### Background agent panel
68
+
69
+ A readonly panel in the Pi TUI follows background agents: the child runs of Cadence's running owner and any agents another extension announces on Pi's shared event bus.
70
+ Every TUI session, not only `/cadence`, looks for the owner already running for its workspace half a second after start and every 15s while none answers; it never starts, recovers or selects one, skips an incompatible owner and only detaches on departure.
71
+ That observation holds one of the owner's viewer attachments, so departure (including Pi quit) waits up to 1.5s for the detach to reach the owner rather than leaving the slot to its idle expiry; `/cadence` departs the same way.
72
+ `CADENCE_AGENT_PANEL=off` turns that observation off.
73
+ It reads the active roster every 5s, and every 2s together with the task's snapshot while it follows an episode, announcing each child of the active episodes: its assignment ID as label, purpose as kind, lifecycle or latest tool as activity, and completed, canceled or any other outcome (with its reason) as done, cancelled or error.
74
+ A child whose outcome the session can no longer observe (no compatible owner a whole retry after losing it, or no settled row five looks after its run left the roster) ends as an error that says so; `/cadence` History keeps the actual outcome.
75
+ Elapsed time counts from when the session first observed a child (`—` for one first seen settled); no tokens are shown because the owner records no usage.
76
+ Opening a child reads its retained messages, tool calls and results through the history readers, up to 64KiB per field; complete fields, search and export stay in `/cadence` History.
77
+ A retained `edit` call shows its call and result without Pi's diff preview, which would compare against the file as it is now.
78
+ Producers emit `subagent:start {id,label,kind,startedAt}`, `subagent:message {id,message,streaming?}`, `subagent:activity {id,activity,tokensIn?,tokensOut?}`, `subagent:stats {id,tokensIn,tokensOut?}` and `subagent:end {id,status,endedAt,error?}` with status `done`, `error` or `cancelled`; `startedAt: null` marks an unobserved start.
79
+ A message has the main session's message shape; `streaming: true` marks a snapshot that the next message replaces.
80
+ The panel emits `subagent:view {id}` (`id: null` when none) so a producer loads only the shown agent's messages.
81
+ Nothing is mounted until the first agent arrives (`CADENCE_AGENT_PANEL=always` keeps `● main` visible).
82
+ Then a launched/finished block sits above the input and one row per agent below it: at most six recently active rows plus `+K more`.
83
+ With an empty input, ↓ moves focus to the rows; ↑/↓ or Ctrl+P/N select, Enter shows that agent's transcript in place of the main view, PgUp/PgDn, Ctrl+U/D and Home/End scroll it, and Esc returns to the main view and then to the input.
84
+ Every other input key, including Enter, Shift+Tab and Ctrl+J, stays Pi's; each agent view remembers its scroll position.
85
+ The panel never starts, cancels or keeps an agent or Pi alive, and keeps nothing across restarts.
86
+ For manual checks, `pi -e ./src/index.ts -e ./scripts/agent-panel-demo.ts` adds `/agents-demo [count]`, which plays fake agents (the second fails) and runs nothing.
87
+
57
88
  ### Browser
58
89
 
59
90
  Open the locally revealed token link.
@@ -63,6 +94,7 @@ A token grants workspace access, not action consent.
63
94
  Every mutation needs its own context-bound preview/confirmation; stale/duplicate/lost replies do not silently replay work.
64
95
  The WebUI provides task/run/assignment navigation, long retained messages and tool detail, exact literal search, timeline and complete scoped NDJSON/Markdown downloads.
65
96
  Desktop panes and phone drawers retain cancellation and keyboard controls.
97
+ 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.
66
98
 
67
99
  ## Execution, checks and review
68
100
 
@@ -108,30 +140,27 @@ Outbound provider proxy policy is independent of inbound HTTPS and refuses unsup
108
140
  - `skills/git-commit` requires an explicit commit request and deterministic audit; task review never commits.
109
141
  - Three pinned professional profiles in `agents/` provide readonly design/diagnosis and direct-edit implementation instructions, not execution authority.
110
142
  - `config/` contains optional research/check examples.
111
- Missing-only user `.env` template creation is the sole postinstall behavior; no service/build/Git/provider operation runs.
143
+ Missing-only user `.env` template creation and the Linux autostart refresh above are the only postinstall behaviors; no build/Git/provider operation runs.
112
144
  - `node /absolute/cadence/src/operator-cli.mjs doctor|runs [workspace]` remains readonly and independent of service startup.
113
145
 
114
146
  ## Development and evidence
115
147
 
116
148
  ```sh
117
- bun run verify # default fast verification; no browser
118
- bun run test # reviewed fast root regressions
119
- bun run test:target test/service-contract.test.ts
149
+ bun run check
150
+ bun run check:web
151
+ bun run build:check
152
+ bun run lint
153
+ bun run check:agents
154
+ bun run test:service
120
155
  bun run test:web
121
- bun run test:integration # explicit slow SDK/PTY/history lane
122
- CADENCE_BROWSER_PATH=/absolute/existing/chromium bun run verify:full
156
+ CADENCE_BROWSER_PATH=/absolute/existing/chromium bun run test:browser
157
+ CADENCE_BROWSER_PATH=/absolute/existing/chromium bun run verify
123
158
  bun pm pack --destination /absolute/private-temp
124
159
  ```
125
160
 
126
- Build/verification use a fresh private directory with `TMPDIR`, `TEMP` and `TMP` set explicitly.
127
- `verify` runs root/Web type checks, exact generated-byte checks, lint, Agent pins, fast root regressions, Web unit tests and the real pack audit.
128
- The fast root lane uses an explicit reviewed list in `test/fast.config.ts`, with at most four parallel workers; no test is selected from mutable timing caches.
129
- SDK/provider cycles, PTYs, large-history/native stress scenarios and real browsers are not part of the default command or fast CI matrix.
130
- `test:integration` selects the remaining root tests serially; new root tests stay there until explicitly promoted to the fast list.
131
- `verify:full` runs fast verification, those remaining tests and real-browser scenarios once each; it can take tens of minutes and must be requested explicitly.
132
- Fast verification is not full integration/browser qualification.
133
- Missing required browser in the full lane is non-green, never an automatic download.
134
- For a focused slow regression, pass its file to `test:target`; `test:service` and `test:browser` remain separate explicit lanes.
161
+ 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.
162
+ `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.
163
+ It needs a Node with TypeScript type stripping (official builds; a distro Node built without it fails `build:web:check`) and NSS `certutil` for the isolated HTTPS browser scenario.
135
164
  Source/extracted PTYs, fabricated loopback providers and isolated HTTPS are distinct from physical humans/devices, paid accounts, package-manager installation and native Windows/macOS/ConPTY qualification.
136
165
  Those additional lanes remain unqualified unless their own receipts exist.
137
166
  No passing gate authorizes commit, push, publication, installation, deployment or cleanup.
@@ -149,7 +178,8 @@ Historical logs are not runtime dependencies, and removing development tooling m
149
178
  | Owner | Responsibility |
150
179
  | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
151
180
  | `src/index.ts`, `src/task-shell.ts`, `src/native-*.ts` | Nonactivating Pi entry, raw native consent, typed service attachment, paging and verified export; no embedded executor |
152
- | `src/service/host.ts`, `src/service/cli.ts` | Real single-owner composition and explicit management; generated management entry stays SDK-free |
181
+ | `src/agent-panel.ts`, `src/agent-panel-model.ts`, `src/agent-panel-cadence.ts` | Readonly agent panel over Pi's `subagent:*` bus, fed with a running owner's children; never starts or cancels work |
182
+ | `src/service/host.ts`, `src/service/cli.ts`, `src/service/autostart.ts` | Real single-owner composition, explicit management and the generated Linux user unit; management entry stays SDK-free |
153
183
  | `src/service/{coordination,private-files,owner,recovery}.ts` | Canonical overlap exclusion, exact locks, original-operation lifetime and informed local recovery |
154
184
  | `src/service/{admission,task-actions,graph,scheduler}.ts` | Context-bound actions, actual current views, finite graph scheduling, check/review barriers |
155
185
  | `src/service/{model-config,model-context,model-choice,auth-network}.ts`, `src/sdk-network.ts` | Existing Pi sources, explicit exact model/thinking, original OAuth writeback and strict shared network policy |
@@ -186,6 +216,7 @@ Use their current declarations rather than copying DTO parsing, reducers, a seco
186
216
  - Independent readonly assignments may overlap; writers and native parent checks exclude readers and other writers across tasks.
187
217
  Reserve bounded slots/bytes before copying/auth/effects; dependencies use exact committed predecessor content only after actual native and recording settlement.
188
218
  Overflow, stale dependencies, first failure or wait expiry closes admission without retry; independent already-running siblings retain their own outcomes.
219
+ Each assignment or check observes the working copy at its baseline, immediately before its native launch and after exit; the steps between recheck ownership, sources and definitions without another Git/file capture.
189
220
  - Register the original accepted control flow before its first effect, in addition to separate native/auth/storage originals.
190
221
  Reporting timeout, a caught promise, PID absence or an empty roster cannot release resources.
191
222
  Stop publishes one shared attempt, quiesces all admissions, requests stops before awaiting children and joins permitted original continuation/safety writes.
@@ -209,6 +240,7 @@ A fresh check may account for exactly one owned generation advance bound to its
209
240
  Do not replace this proof with a general before≤after comparison.
210
241
  Changes to files, definitions, epoch/cwd/environment or a foreign generation stale the current obligation without rewriting historical native exit or PASS.
211
242
  Human Review binds exact selected run/graph/check/change identities and recaptures under a short exclusive barrier only after confirmation, not throughout the human modal.
243
+ One accepted Review folds its fixed committed snapshot (plan proof, selected run, check results) once; every barrier validation still rechecks raw task bytes, changes, configuration, choice and active work.
212
244
  Acknowledgment permits reviewed-with-issues, never changes failed/partial/canceled/native/recording/check facts or revives blocked execution.
213
245
  Unavailable concurrent views may display conservative stale status but cannot advance authoritative review revisions and invalidate an otherwise unchanged accepted Review.
214
246
 
@@ -235,6 +267,13 @@ Only the serialized history writer assigns committed sequence and persisted cont
235
267
  Commit actual admission/auth/launch/result facts in order; an index, observer callback or candidate reducer cannot authorize effects.
236
268
  Quota/exhaustion/refusal stops unsafe work without eviction; torn tails retain a verified prefix and visible gaps, while interior corruption refuses affected ranges.
237
269
  Derived indexes are optional accelerators: readers validate original batches/artifacts, use bounded fallback and never repair on view.
270
+ Current/plan/review reads replay a task's batches, so per-file cost bounds them: on a slow host (Windows Node 22) that replay once exceeded the 100ms read quantum after review, leaving the task `unavailable`.
271
+ `PrivateFiles` reuses a directory walk only within one synchronous `DirectoryWalks` scope (a read budget or lease check), after re-observing that directory's identity; an await or later call walks again, and the quantum is never raised instead.
272
+ When a batch settles a task's publication, the owner writer remembers in process memory that task's fold state (check facts, latest run, plan/lineage progress with original admission locators) under its chain head's sequence and hash, a bounded number of tasks and states.
273
+ Readers in that process resume from the newest remembered state at or before the snapshot whose chain link still matches and fold only newer original batches; an evicted, restarted, foreign-process or diverged miss folds the original chain, and nothing is persisted, authenticated or repaired.
274
+ A quantum that makes no semantic progress because its wall-clock time ran out (a host stall: GC, scheduling, scanning) is repeated with a fresh quantum at most twice before its refusal stands; byte exhaustion and every other refusal stay final, and no progress is ever faked.
275
+ The wall-clock quantum belongs only to client-facing cooperative reads: owner-internal authority reads (current views, admission, publication, Review and receipt facts) are bounded by the same bytes but never by time, so host speed cannot turn an owner's own decision into a refusal.
276
+ Plan proofs and Review selections read their small plan text through its content/part indexes under the validated snapshot, verifying the original chunk batches, artifacts and aggregate hash; absent or faulty indexes fall back to the complete validated-prefix scan, which no longer runs per current view.
238
277
  Prepared journal heads avoid redundant mutable legacy hints; no-root compatibility and its original integrity checks remain.
239
278
 
240
279
  New history uses private format3 at `service-v1/history-v2/`, with exclusive private read-proof key creation before the final format marker.
@@ -276,16 +315,23 @@ Byte values are UTF-8/encoded bounds where specified; they are not targets to si
276
315
 
277
316
  ### Validation and regression lessons
278
317
 
279
- | Condition | Required behavior |
280
- | -------------------------------------------------------------- | --------------------------------------------------------------------------------- |
281
- | Missing/unsafe/corrupt/partial selected state | Absent or fixed refusal; no bootstrap, adoption, repair or cleanup on observation |
282
- | Native exit0 with recording/stdin/final/cleanup failure | Preserve native zero independently; no workflow success |
283
- | Stale grant, selected task bytes or model/source identity | Refuse and require fresh observation/confirmation |
284
- | Unknown original stop/auth/storage or changed lease | Retain fences/charges; no guessed process signaling or automatic release |
285
- | Failed/unrun/stale check, partial child or incomplete coverage | Explicit issues review; preserve independent recorded outcomes |
286
- | Lost reply or detached view | Observe original receipt/run; no mutation replay or implicit Cancel |
287
- | Invalid read proof or changed source/key | Refuse; never rebase or interpret budget exhaustion as absence |
288
- | Dirty/partial/extra compiled assets or unsafe archive | Refuse in place; no source refill, wildcard serving or fallback build |
318
+ | Condition | Required behavior |
319
+ | -------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
320
+ | Missing/unsafe/corrupt/partial selected state | Absent or fixed refusal; no bootstrap, adoption, repair or cleanup on observation |
321
+ | Native exit0 with recording/stdin/final/cleanup failure | Preserve native zero independently; no workflow success |
322
+ | Stale grant, selected task bytes or model/source identity | Refuse and require fresh observation/confirmation |
323
+ | Unknown original stop/auth/storage or changed lease | Retain fences/charges; no guessed process signaling or automatic release |
324
+ | Failed/unrun/stale check, partial child or incomplete coverage | Explicit issues review; preserve independent recorded outcomes |
325
+ | Lost reply or detached view | Observe original receipt/run; no mutation replay or implicit Cancel |
326
+ | Invalid read proof or changed source/key | Refuse; never rebase or interpret budget exhaustion as absence |
327
+ | Dirty/partial/extra compiled assets or unsafe archive | Refuse in place; no source refill, wildcard serving or fallback build |
328
+ | Extension import outside Pi aliases declared only as a peer | Exact production dependency; Pi's install omits peers and Pi fails to start |
329
+ | Install without the optional SDK or Pi `auth.json` | No unit registration or start; Pi sources refuse unregistered, no SDK quarantines |
330
+ | Package files hard-linked into a package manager's store (Bun) | Packaged reads ignore link count, bind exact bodies; other files stay single-link |
331
+ | Host refuses during startup | Fixed code/stage only; unregistered exits `refused`, registered stays `quarantined` |
332
+ | Workspace containing the private state home | Land on `~/cadence` (only start creates it); never move state into the working copy |
333
+ | Upgrade changes the unit workspace or Node path | Stop with the loaded unit definition before rewriting, not only SIGTERM |
334
+ | Install hook cannot prompt and npm hides its output | Only a first registration tries passwordless linger; the startup card names the gap |
289
335
 
290
336
  Good: current context → explicit grant → durable acceptance → genuine original native/storage settlement → exact retained dependency → real checks → human review.
291
337
  Base: a client leaves and another reattaches to the same accepted run without launching it again.
@@ -299,10 +345,11 @@ Correct: expose bounded continuations/full exports and report the exact executed
299
345
  ### Development and verification practice
300
346
 
301
347
  Read affected source and adjacent tests, identify the actual owner of a contract, then make the smallest coherent change.
302
- Use ordinary source editing and directly affected checks while developing; run the fast `verify` command at stable boundaries.
303
- Request the explicit integration/browser lanes when those capabilities need qualification; do not silently start the entire suite after a local edit or a publication request.
304
- Keep fast-check results separate from real native/SDK/browser evidence, and never replace a required integration result with unit mocks or citation counts.
305
- On this development host create fresh0700 disk roots under `/home/mirako/abel` and explicitly set TMPDIR/TEMP/TMP; `/tmp` is inode-exhausted.
348
+ Use ordinary source editing and directly affected checks while developing; independently review complete capabilities and run full `verify` at stable integration boundaries.
349
+ Do not run the entire suite after every local edit, but do not replace final integration with unit mocks or citation counts.
350
+ `bun run test` runs unit files in parallel, then `serialProcessFiles` (real PTY/SDK process flows) and `serialScanFiles` (>8MiB cold scans) from `vitest.config.ts` one at a time; a file belongs there only when it measurably exceeds its own waits under shared CPU.
351
+ 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.
352
+ 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.
306
353
  Use fabricated credentials, isolated browser/profile/state roots and local fixtures; never personal credentials, automatic downloads or unknown process cleanup.
307
354
  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.
308
355
  Preserve failed attempts; correct a demonstrated cause before rerunning, and never assign old failures a retrospective cause without evidence.
@@ -315,14 +362,27 @@ Regression owners:
315
362
  - `worker-*`, `lightweight-sdk`, `lightweight-process-diff`: genuine SDK/tool capture, split redaction, no-spill bounds and original native facts.
316
363
  - `service-history-*`: durable ordering, cold/no-index/restarted proof/search/export, corruption/torn/quota and expected-byte publication.
317
364
  - `lightweight-shell`, `native-client`, `native-history`, `terminal-integration`: native consent, shared service, Unicode/paging, detach/cancel and actual source/extracted PTYs.
365
+ - `agent-panel`, `agent-panel-model`, `agent-panel-cadence`: panel keys/rendering and readonly owner observation that never starts, cancels or replays work.
318
366
  - `test/web/`: adapted pure/SSR tests separately from actual built-browser contexts, HTTPS, mixed clients, focus/viewports and complete downloads.
319
- - `package-*`, `distribution`, `service-package`, `independence`: exact tar/member/body/import/notice closure and nonactivating installation/discovery.
367
+ - `package-*`, `distribution`, `service-package`, `independence`: exact tar/member/body/import/notice closure and nonactivating discovery.
368
+ - `service-autostart`, `install-config`: Pi user-scope gating, unit escaping, upgrade/disable/mask/foreign handling and orderly removal.
369
+ - `workspace-landing`: home-directory redirection to `~/cadence` across management, native attach and observation.
320
370
 
321
371
  Regenerate operator/service/native/browser outputs only through their canonical builders; `--check` variants compare new private output without overwriting canonical bytes.
372
+ Publish `web/dist` from Node: Vite run under Bun lays out its preload helper differently, so a Bun build fails a Node `build:web:check`.
322
373
  Review the independently enumerated package member list and every emitted body, not a count or an expected list generated from the tar under test.
323
- Keep public Pi peers external, the exact ws runtime edge and all upstream/font/vendor notices.
374
+ 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.
324
375
  Preserve original license bytes through their precise Git attributes; line-ending normalization must not invalidate audited source/notice hashes.
325
376
 
377
+ ### Release 2.0.3 acceptance snapshot
378
+
379
+ Full `verify` passed on `c27b8ca`: 1924 root tests in 78 files (27 Windows-only controls skipped on Linux), 111 web tests in 8 files, 5 real-browser scenarios in 3 files and the 132-member real pack audit.
380
+ Observed environment: Linux x86_64, official Node 22.22.1, Bun 1.3.14, the pinned Pi SDK 0.84.3 and Chrome for Testing 153.0.8010.12; later commits change only tests and this section.
381
+ The GitHub matrix (Windows and Ubuntu × Node 22/24 × `parallel`, `serial-process`, `serial-scan`) runs on the separate CI branch.
382
+ A real Pi 1.4.0 user-scope install of the packed candidate, installed into Pi's own npm layout, registered and enabled the unit, enabled linger without a prompt and, on upgrade, stopped the old owner in order before starting the new one.
383
+ That owner started with the agent directory Pi had created at 0775 and served the built WebUI to Chromium at 1440, 390 and 320px widths.
384
+ No model-backed task ran there (no Pi provider login); macOS, physical phones, paid accounts and every adapter, and a second machine remain unqualified.
385
+
326
386
  ### Prior integrated acceptance snapshot
327
387
 
328
388
  Before removal of development-workflow tooling, the integrated redesign passed full `verify`:1839 root tests/69 files,110 web tests/8 files and5 real-browser scenarios/3 files, with27 existing Windows-only controls skipped on Linux.
@@ -8,7 +8,7 @@ Repository `provenance/webui-reuse.json` records source/destination hashes and e
8
8
 
9
9
  | Source family | Package adaptation |
10
10
  | ------------------------------------ | ------------------------------------------------------------------------ |
11
- | App, panels, layout, drawers | Three-pane task workspace and responsive phone navigation |
11
+ | App, TopBar, panels, layout, drawers | Three-pane workspace, pi topbar/statusbar chrome and phone navigation |
12
12
  | conv-groups, PlanBoard | Task/run/child tree, editable finite graph and separate confirmations |
13
13
  | MessageList, Message, ToolCallBlock | Paged transcript, stable tool associations and retained detail |
14
14
  | Markdown, StreamMarkdown, copy | Sanitized Markdown/math/code, no script/external-image execution |
@@ -35,7 +35,8 @@ Provisioned official notice bodies were reused locally; builds never download li
35
35
  ## Runtime/package closure
36
36
 
37
37
  Only the native extension's explicit import closure, maintained SDK worker/shared JS capture modules, generated management/host/operator executables, Agents, independent skills/configuration, documentation, notices and complete built assets ship.
38
- Service/worker execution uses ordinary Node with declared external Pi/Typebox/Undici peers and exact production `ws`; no source-tree or TypeScript fallback, SDK bundle or consumer compilation.
38
+ Service/worker execution uses ordinary Node with exact production `undici` and `ws` and the Pi SDK (`pi-coding-agent`, `pi-ai`) from exact 0.84.3 optional dependencies, because Pi's installer omits peers; no source-tree or TypeScript fallback, SDK bundle or consumer compilation.
39
+ Pi's package installer omits peers and aliases only its own packages and Typebox, so every other runtime import reachable from `src/index.ts` must be a production dependency; otherwise Pi fails to load the extension at startup.
39
40
  The retired shell-local controller is not a shipped competing executor.
40
41
 
41
42
  `provenance/package-members.json` is a reviewed source/import/asset policy, not generated from the archive being tested.
@@ -1,7 +1,7 @@
1
1
  # Explicit service management
2
2
 
3
- The native `/cadence` view and browser are clients of the same explicitly started owner.
4
- Opening either view, extension discovery, installation and login do not start it.
3
+ The native `/cadence` view and browser are clients of the same owner, started explicitly or by the [Linux systemd user unit](#linux-systemd-user-unit).
4
+ Opening either view, extension discovery and login do not start it.
5
5
  Use one coordination state-home for the checkout; a different state-home is not a recovery route.
6
6
  Stop older Cadence sessions before starting the shared service.
7
7
 
@@ -20,7 +20,58 @@ The equivalent package-relative command is `node <cadence>/src/service-cli.mjs .
20
20
  Defaults are loopback `127.0.0.1:17423`, normal Pi agent configuration, and detached service lifetime.
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
- There is no login autostart, supervisor installation, provider login or credential migration.
23
+ A workspace that contains the private state home (the home directory and its ancestors, such as `C:\Users\<you>` with `%LOCALAPPDATA%` or `~` with `~/.local/state`) would expose private history and locks to change observation, so every command there uses `~/cadence` (`%USERPROFILE%\cadence`) instead of refusing.
24
+ Only `start` creates it; `status` reports `absent` without creating it, an explicit `--workspace` adds one informational line, and JSON output names the effective `workspace` only when it differs.
25
+ `/cadence` started in such a directory attaches to the same owner, and real project directories are never redirected.
26
+ Apart from the Linux user unit below, there is no supervisor installation, provider login or credential migration.
27
+
28
+ ## Linux systemd user unit
29
+
30
+ On Linux, Pi's user-scope install (`pi install npm:@abelxiaoxing/cadence`, npm in `<agent-dir>/npm`) and every `pi update` run the package postinstall, which calls `cadence-service autostart refresh`.
31
+ Refresh acts only when the package is exactly Pi's managed user copy; checkouts, project (`-l`), temporary and global installs, other platforms and `CADENCE_AUTOSTART=0` are left alone.
32
+
33
+ The first refresh writes `~/.local/share/systemd/user/cadence.service`, enables it for `default.target` and starts it.
34
+ A user unit runs with the user manager, which starts at login unless logind lingers the user, so that first registration also asks logind to enable linger when it is off; `CADENCE_LINGER=0` at install skips it.
35
+ It never prompts: it calls `SetUserLinger` through `busctl` with `interactive=false` and no interactive authorization (bounded to 15s), because `loginctl enable-linger` always requests an interactive check that a desktop polkit agent can turn into a password dialog even with `--no-ask-password`.
36
+ Where polkit requires authorization for `set-self-linger` (older systemd defaults), the unit stays login-only and the result reports `"linger":"refused"`.
37
+ Later refreshes do not change linger, so `loginctl disable-linger` is kept across upgrades.
38
+ The workspace is `~/cadence` (created if missing) or an existing absolute `CADENCE_WORKSPACE`, which lands on `~/cadence` with a note when it contains the unit's state home; later refreshes keep the unit's workspace unless that variable is set again.
39
+ The unit runs `start --foreground --workspace` without `--port`, so a new workspace selects `http://127.0.0.1:17423`.
40
+ A port saved by an earlier start of that workspace still wins, and an occupied endpoint fails the unit instead of falling back.
41
+ It bakes the installer's PATH (minus npm lifecycle entries), LANG/LC_ALL, an absolute XDG_STATE_HOME and a custom PI_CODING_AGENT_DIR, so shell management sees the same state home and Pi sources.
42
+ Proxy variables are not baked; add a validated pair with `systemctl --user edit cadence`.
43
+ Browser entry still requires the explicitly revealed token link; installation prints the `token-link --reveal` command, never the token.
44
+
45
+ An upgrade refresh stops an active unit with its loaded definition, rewrites the file and starts it again.
46
+ `ExecStop` is the authenticated orderly Stop, so admitted work is stopped, not detached.
47
+ A disabled unit is refreshed but not re-enabled, and restarted only if it was running; a masked unit is never enabled or started; a unit file Cadence did not generate is left unchanged.
48
+ `KillMode=mixed` sends systemd's SIGTERM only to the management client.
49
+ An owner still settling after `TimeoutStopSec=90` (for example a quarantined one) is SIGKILLed, which is not orderly Stop and can leave recovery fences.
50
+ `Restart=on-failure` is bounded to three starts in five minutes; a restart never removes fences, so a crashed owner keeps refusing until informed recovery.
51
+
52
+ Pi's installer omits peers, so the standalone owner and workers load the Pi SDK (`pi-coding-agent`, `pi-ai`) from exact 0.84.3 optional dependencies.
53
+ Package files may be hard links into the package manager's store (Bun's default install); Cadence binds their exact content, not their link count.
54
+ Missing or unusable Pi sources refuse before an owner registers; a new owner without the SDK quarantines.
55
+ Refresh therefore does not register the unit while the SDK is unresolvable (for example, optional dependencies omitted), and enables without starting it while `auth.json` is missing; the unit carries `ConditionPathExists=` for all three files.
56
+ After logging in to Pi, start it with `systemctl --user start cadence`.
57
+ Without linger, user units start at login and stop at logout through the same Stop.
58
+ Linger starts the user manager, with every enabled user unit, at boot and keeps it running after logout.
59
+ Because npm hides the install guidance, Pi's session-start card names `autostart install --linger` while Pi's managed copy has no unit, and `loginctl enable-linger` while the enabled unit is login-only.
60
+ A machine not booted with systemd (no `/run/systemd/system`), a disabled or masked unit (also `mask --runtime`) and `CADENCE_AUTOSTART=0` keep the card quiet; with `CADENCE_LINGER=0` in Pi's environment it never suggests linger and names `autostart install` without `--linger`.
61
+
62
+ ```sh
63
+ cadence-service autostart install [--workspace /absolute/project] [--linger]
64
+ cadence-service autostart status
65
+ cadence-service autostart remove
66
+ ```
67
+
68
+ `install` registers, enables and starts the unit from any package location unless it is masked.
69
+ With `--linger` it also runs `loginctl enable-linger`, which may ask for your password; it asks only after the unit has started again, so an interrupted prompt never leaves the owner stopped.
70
+ `status` reports `linger` as `on` or `off`.
71
+ `remove` disables it through `ExecStop` and deletes only the generated file; workspace, state home, history and Pi sources stay.
72
+ Because npm runs no uninstall script, run `remove` before `pi remove`; a left-behind unit skips start once its CLI path disappears.
73
+ `remove` is for uninstalling: without the generated file the next `pi update` is a first registration again (unit and linger).
74
+ To keep the service off while Cadence stays installed, use `systemctl --user disable --now cadence` or `mask`.
24
75
 
25
76
  ## Status, departure and Stop
26
77
 
@@ -34,6 +85,14 @@ There is no login autostart, supervisor installation, provider login or credenti
34
85
  - `unconfirmed` means startup/Stop acknowledgment or settlement was not established within its reporting bound.
35
86
  It is not permission to restart, kill a guessed PID or remove a lock.
36
87
  `quarantined` preserves uncertain process/auth/storage work and prevents unsafe writers.
88
+ - A host that refuses during `start` reports `{"status":"refused","error":<code>,"stage":<stage>}` and exits without registering an owner.
89
+ Stages are `package` (installed web assets or Agent profiles), `pi-agent-directory`, `pi-auth`, `pi-models` and `service`; stderr names the selected Pi source to fix.
90
+ `quarantined` with stage `pi-runtime` or `service` means the owner registered first; it keeps its fences for informed recovery.
91
+ Reports carry fixed codes and stages only, never paths, configuration bytes or OS error text.
92
+ - The selected Pi agent directory must be a real directory (not a symlink) owned by you; `auth.json` and `models.json` must be your own single-link regular files.
93
+ Their mode bits are not checked: Pi creates its agent directory with your umask (0755, or 0775 under umask 002), and Cadence's own state stays 0700 regardless.
94
+ `models.json` may use only fields the pinned SDK supports.
95
+ `supportsMidConvoSystemMessages` from newer Pi is accepted because the pinned SDK has no system-role conversation message; other unknown fields refuse rather than being silently ignored.
37
96
  - A foreground owner can be managed from another terminal.
38
97
  Ctrl-C, SIGTERM, machine crash or terminal destruction is **not** the authenticated orderly Stop route and can leave recovery fences.
39
98
  A recovered `stopped` registration means acknowledged fence release, not retroactive native closure certification.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abelxiaoxing/cadence",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
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",
@@ -17,6 +17,9 @@
17
17
  "skills",
18
18
  "config",
19
19
  "agents",
20
+ "src/agent-panel-cadence.ts",
21
+ "src/agent-panel-model.ts",
22
+ "src/agent-panel.ts",
20
23
  "src/index.ts",
21
24
  "src/install-config.mjs",
22
25
  "src/native-build.ts",
@@ -71,12 +74,10 @@
71
74
  "postinstall": "node src/install-config.mjs",
72
75
  "pack:check": "node scripts/pack-check.mjs",
73
76
  "traceability:check": "node scripts/traceability-check.mjs",
74
- "verify": "bun run check && bun run check:web && bun run build:web:check && bun run lint && bun run check:agents && bun run test && bun run test:web && bun run pack:check",
75
- "verify:full": "bun run verify && bun run test:integration && bun run test:browser",
77
+ "verify": "bun run check && bun run check:web && bun run build:web:check && bun run lint && bun run check:agents && bun run test && bun run test:web && bun run test:browser && bun run pack:check",
76
78
  "check": "node scripts/check-syntax.mjs && bun scripts/build-operator.mjs --check && bun scripts/build-service.mjs --check && tsc --noEmit",
77
79
  "lint": "biome check . && rumdl check .",
78
- "test": "vitest run --config test/fast.config.ts --reporter=dot",
79
- "test:integration": "vitest run --config test/integration.config.ts --reporter=verbose",
80
+ "test": "vitest run",
80
81
  "test:target": "vitest run",
81
82
  "check:agents": "node scripts/check-agents.mjs",
82
83
  "doctor": "node src/operator-cli.mjs doctor",
@@ -101,13 +102,11 @@
101
102
  },
102
103
  "devDependencies": {
103
104
  "@biomejs/biome": "^2.4.16",
104
- "@earendil-works/pi-coding-agent": "0.84.3",
105
105
  "@earendil-works/pi-tui": "0.84.3",
106
106
  "@types/node": "^22.15.3",
107
107
  "rumdl": "^0.2.10",
108
108
  "typescript": "^6.0.3",
109
109
  "vitest": "^4.1.8",
110
- "@earendil-works/pi-ai": "0.84.3",
111
110
  "@types/ws": "8.18.1",
112
111
  "react": "19.3.0",
113
112
  "react-dom": "19.3.0",
@@ -146,6 +145,10 @@
146
145
  "undici": "8.9.0",
147
146
  "ws": "8.21.3"
148
147
  },
148
+ "optionalDependencies": {
149
+ "@earendil-works/pi-ai": "0.84.3",
150
+ "@earendil-works/pi-coding-agent": "0.84.3"
151
+ },
149
152
  "bin": {
150
153
  "cadence-service": "./src/service-cli.mjs"
151
154
  }