taskchef 8.3.0 → 8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/spec.md CHANGED
@@ -59,15 +59,10 @@ is dated research, not contract.
59
59
 
60
60
  ## Workspace contract
61
61
 
62
- TaskChef MUST manage only `AGENTS.md`, `taskchef.json`, `tasks.jsonl`, the
63
- optional `.taskchef-usage.json` cache, and the optional dashboard lifecycle
64
- records `.taskchef-dashboard-owner.json` and
65
- `.taskchef-dashboard-handoff.json` inside the dispatcher workspace. It MUST
66
- preserve unrelated paths. The owner record MUST be a retained mode-`0600`
67
- current-listener identity and control credential that is atomically replaced by
68
- a new owner. The handoff record MUST be a retained mode-`0600`, secret-free,
69
- credential-signed final lease snapshot that is atomically overwritten by the
70
- next finalized handoff.
62
+ TaskChef MUST manage only `AGENTS.md`, `taskchef.json`, `tasks.jsonl`, and the
63
+ optional `.taskchef-usage.json` cache inside the dispatcher workspace. It MUST
64
+ preserve unrelated paths. Dashboard process identity and Codex-session leases
65
+ MUST remain in memory and MUST NOT create owner, secret, or handoff files.
71
66
 
72
67
  `taskchef.json` MUST have schema version 2, the following required fields, and
73
68
  an optional exact `dashboard` object:
@@ -343,11 +338,8 @@ object. Validation, marker, identity, uniqueness, freshness, or filesystem
343
338
  failures are surfaced as tool errors and MUST NOT partially mutate the log.
344
339
 
345
340
  ### `ensure_dashboard`
346
- **Caller:** dispatcher. **Mutation:** starts or reuses the authenticated
347
- Codex-session-scoped loopback dashboard. The first start writes a private
348
- dashboard ownership record in the canonical workspace. The record remains
349
- after shutdown and the next owner atomically replaces it before reporting
350
- startup success.
341
+ **Caller:** dispatcher. **Mutation:** starts or reuses the
342
+ Codex-session-scoped loopback dashboard.
351
343
 
352
344
  **Input:** empty object.
353
345
 
@@ -356,7 +348,7 @@ startup success.
356
348
  ```text
357
349
  { dashboard: {
358
350
  action: "started" | "reused",
359
- launcher: "session",
351
+ launcher: "session" | "standalone" | "mcp",
360
352
  url: "http://127.0.0.1:3210/",
361
353
  workspace: string,
362
354
  taskchefVersion: string,
@@ -372,84 +364,65 @@ and concurrent calls are idempotent. The stable default MUST bind only to
372
364
  session PID, or credential.
373
365
 
374
366
  Exact reuse MUST require the fixed service/schema, TaskChef version,
375
- dashboard-server version, canonical workspace, and `session` launcher identity.
376
- It MUST additionally prove the private owner credential and use a fresh,
377
- single-use, action-bound HMAC to register the MCP's original Codex parent PID.
378
- The registration acknowledgement and every transferred lease set MUST carry a
379
- separate response HMAC bound to the request nonce and complete accepted result.
367
+ dashboard-server version, canonical workspace, and recognized launcher identity.
368
+ A `session` listener MUST register the MCP's original Codex parent PID in its
369
+ in-memory lease. Every launcher MUST complete another exact health probe before
370
+ ensure reports `reused`, and the response MUST report the observed launcher.
380
371
  The independent dashboard MUST survive closure of an individual MCP transport.
381
372
  The session manager MUST require an explicit nonzero port because a detached
382
373
  child cannot safely return an ephemeral bound port without an additional IPC
383
- ownership channel. Direct foreground server callers MAY continue to bind port
374
+ readiness channel. Direct foreground server callers MAY continue to bind port
384
375
  zero when they retain the returned listener.
385
376
 
386
- The dashboard MUST track only explicitly registered PIDs using non-signalling
377
+ The dashboard MUST track only same-version explicitly registered PIDs using non-signalling
387
378
  existence probes. Once every registered PID is absent for the grace period, it
388
- MUST gracefully close its HTTP listener and exit. A new authenticated live
379
+ MUST gracefully close its HTTP listener and exit. A new live
389
380
  registration during the grace period MUST cancel expiry. This is a local
390
381
  best-effort session guard, not a Codex restart guarantee.
391
382
 
392
- An older TaskChef version MAY be retired only when its exact health identity
393
- reports the same canonical workspace and an `mcp` or `session` launcher; a
394
- private regular mode-0600 owner record exactly matches that listener; and a
395
- fresh nonce-bound HMAC challenge proves control of the record's secret. Only
396
- then MAY the new MCP send an action- and nonce-bound authenticated loopback
397
- handoff. Before retiring a prior `session` listener, an idempotent prepare
398
- action MUST fence new registrations, register the activating Codex PID,
399
- validate and return the bounded live PID lease set while the old listener
400
- remains running. Only a separate authenticated commit after the caller verifies
401
- that result MAY schedule shutdown. A lost prepare response MUST be retryable,
402
- and an abandoned preparation MUST expire without shutting down the listener. A
403
- full, failed, or malformed transfer MUST leave the
404
- listener running and MUST NOT acknowledge a concurrent registration that will
405
- be absent from the returned set. A prior `mcp` listener MAY instead receive a
406
- separately authenticated graceful shutdown request.
407
- A commit MUST begin a short bounded finalization grace that allows concurrent
408
- authenticated activators to join. Its response MUST contain a credential-bound
409
- proof of the final immutable complete lease snapshot. The listener MUST remain
410
- available for a bounded commit-response retry window before shutdown, so a
411
- surviving activator can recover if the elected one exits before launch. An
412
- activator observing the authenticated old-owner listener gap MUST wait for the
413
- elected replacement and MUST NOT bind an older version into that gap.
414
- The finalized transferable snapshot MUST reserve one bounded lease slot for a
415
- distinct recovery activator. If that complete union cannot fit, commit MUST fail
416
- without stopping the old listener. When different compatible newer versions
417
- race, a higher version that loses the bind MUST authenticate and replace the
418
- lower winner rather than accepting it as final.
419
- Before acknowledging commit, the retiring session MUST atomically persist the
420
- credential-bound final snapshot in a private mode-`0600` same-workspace record
421
- that contains no secret. A replacement MAY use that record only after verifying
422
- its exact workspace, listener identity, handoff identifier when known, bounded
423
- PID set, and HMAC with the private owner credential. It MUST ignore the record
424
- once replacement ownership is published, but MUST NOT unlink it after that
425
- transition because a newer handoff may already have atomically replaced it. The
426
- single retained record MUST be overwritten only by a newer finalized handoff
427
- and MUST remain unusable against a different owner identity or credential.
428
- Owner and handoff publication MUST sync the completed private file, atomically
429
- rename it, and sync the containing canonical workspace directory before
430
- reporting success. Finalization MUST fence further handoff joins before taking
431
- and persisting the immutable snapshot.
432
- The new MCP MAY then start its current `session` version and MUST NOT downgrade
433
- a newer listener. Concurrent startup and handoff MUST converge on one
434
- authenticated listener. Single-use control nonces MUST be retained in a fixed,
435
- time-bounded replay cache that refuses overflow while entries remain valid.
436
-
437
- The credential MUST NOT appear in health responses, requests, diagnostics, or
438
- logs. A standalone, unknown, malformed, different-workspace, newer, unproven,
439
- spoofed, or legacy listener without valid ownership metadata MUST produce a
440
- concise actionable conflict and MUST NOT receive a shutdown request, signal,
441
- or process-level termination attempt. TaskChef MUST NOT discover or kill port
442
- owners. An invalid workspace, initial task log, or ownership write MUST leave
443
- no newly owned listener.
383
+ When exact structured health identifies an older TaskChef dashboard for the
384
+ same canonical workspace, the new MCP MUST request simple graceful shutdown,
385
+ wait a bounded time for port release, and start its installed `session` version
386
+ within the same ensure operation. It MUST NOT transfer in-memory PID leases
387
+ across versions. The replacement initially knows only the activating session;
388
+ a later ensure MAY register another live session or restart the dashboard after
389
+ the activating lease expires.
390
+
391
+ A recognized newer TaskChef listener MUST NOT be downgraded. Concurrent
392
+ activation and replacement MUST converge through health probing and exclusive
393
+ port binding. Unknown, malformed, different-workspace, or unrelated occupants
394
+ MUST receive no control request or process signal and MUST remain untouched.
395
+ TaskChef MUST NOT discover or kill port owners.
396
+
397
+ TaskChef's dashboard uses a trusted, single-user localhost threat model. Exact
398
+ bounded health identity is sufficient service recognition and MUST NOT be
399
+ described as cryptographic authentication. The dashboard MUST NOT create or use
400
+ an owner secret, HMAC challenge, durable ownership record, handoff record,
401
+ prepare/commit protocol, recovery lease, or cross-version lease transfer.
402
+
403
+ The session-registration and shutdown controls MUST remain loopback-only and
404
+ MUST require the exact listener Host and Origin, JSON content type, a non-simple
405
+ TaskChef control header, and an exact bounded request body containing the exact
406
+ structured health identity observed by the caller. The receiver MUST compare
407
+ that expected identity with its own identity before registering a session or
408
+ accepting shutdown. It MUST refuse a mismatch, and the manager MUST probe again
409
+ before applying lifecycle rules to the current occupant. These request-shape
410
+ checks protect against ordinary cross-site browser requests; they MUST NOT be
411
+ described as authentication. Future LAN access is outside this contract and
412
+ requires a separate opt-in design with password-backed user authentication and
413
+ suitable transport protection.
414
+
415
+ A listener using the former HMAC control protocol will refuse the new simple
416
+ shutdown request. It MUST be reported as a recognized older TaskChef conflict
417
+ and left running. Crossing that one-time compatibility boundary requires the
418
+ verified legacy listener to stop through its existing lifecycle before ensure
419
+ is retried; TaskChef MUST NOT add a process-kill fallback.
444
420
 
445
421
  Detached session startup MUST use a private child readiness acknowledgement
446
- sent only after listener bind and atomic ownership publication. Startup errors
447
- MUST propagate through that channel. A bounded readiness timeout MUST
448
- cooperatively cancel the exact child so late initialization cannot strand an
449
- unreported listener. Ownership publication MUST check cancellation before its
450
- atomic replacement, and the listener MUST retain the port until that write
451
- commits or aborts on every shutdown path so an older writer cannot overwrite a
452
- successor's credential.
422
+ sent only after listener bind, initialization, and initial in-memory lease
423
+ creation. Startup errors MUST propagate through that channel. A bounded
424
+ readiness timeout MUST cooperatively cancel the exact child so late
425
+ initialization cannot strand an unreported listener.
453
426
 
454
427
  MCP EOF, explicit close, protocol close, SIGINT, SIGTERM, transport-start
455
428
  failure, and MCP-parent loss MUST still bound MCP shutdown. They MUST close the
@@ -768,15 +741,16 @@ permissions for dashboard availability. Availability is best-effort while a
768
741
  registered Codex session process is alive and is not guaranteed while Codex is
769
742
  closed.
770
743
 
771
- Releases predating authenticated dashboard handoff are legacy listeners. A
772
- verified legacy listener MUST be reported distinctly from an unknown port
773
- conflict but MUST remain untouched; one final manual cleanup MAY be required
774
- when upgrading from such a release. Once both sides implement the same control
775
- protocol, compatible prior-version handoff is automatic.
744
+ Releases using the former HMAC dashboard control protocol are legacy listeners.
745
+ A verified legacy listener MUST be reported distinctly from an unknown port
746
+ conflict and MUST remain running when it refuses simple graceful shutdown. One
747
+ final stop through that listener's existing lifecycle is required when crossing
748
+ this boundary. Releases implementing simple localhost control replace older
749
+ recognized versions automatically.
776
750
 
777
751
  Installing or replacing plugin files MUST NOT be described as activating the
778
752
  new MCP code. Release verification MUST install the plugin, activate or reload
779
753
  the new MCP process, ensure the dashboard, and verify the expected TaskChef
780
- version, dashboard protocol `serverVersion`, `session` launcher, canonical workspace,
754
+ version, dashboard protocol `serverVersion`, reported launcher, canonical workspace,
781
755
  and canonical URL. Exact-compatible session-listener reuse remains valid; installation MUST NOT be
782
756
  claimed to reload Codex automatically.
package/docs/workflows.md CHANGED
@@ -24,8 +24,8 @@ start, upgrade, shutdown, and security rationale.
24
24
  | `src/config-audit.js` | Durable prepared/terminal configuration-mutation journal. |
25
25
  | `src/cli.js` | Administration, normalized cached briefs, inspection, diagnostics, and dashboard startup. |
26
26
  | `src/dashboard.js` | Versioned health identity, validated compact snapshots, SSE fan-out, on-demand details, and bounded open actions. |
27
- | `src/dashboard-manager.js` | Concurrent session ensure, authenticated reuse and upgrade handoff, and safe conflicts. |
28
- | `src/dashboard-session-process.js` | Independent loopback server, private ownership publication, signals, and Codex-session lease. |
27
+ | `src/dashboard-manager.js` | Concurrent session ensure, exact health reuse, bounded version replacement, and safe conflicts. |
28
+ | `src/dashboard-session-process.js` | Independent loopback server, signals, and in-memory Codex-session lease. |
29
29
  | `src/dashboard-session.js` | Exact registered-PID liveness checks and grace-period expiry. |
30
30
  | `src/usage.js` | Optional bounded ccusage execution, exact primary-thread mapping, normalized aggregation, and the private usage cache. |
31
31
  | `src/usage-tracker.js` | Deferred sampling, cumulative boundaries, historical availability, and per-turn deltas. |
@@ -46,31 +46,31 @@ sequenceDiagram
46
46
  participant D as Dispatcher
47
47
  participant M as TaskChef MCP
48
48
  participant H as Loopback health
49
- participant S as Dashboard session process
49
+ participant S as Dashboard process
50
50
  M->>M: Read dashboard.autostart (absent means true)
51
51
  M->>M: Best-effort ensure before MCP transport connects
52
52
  D->>M: ensure_dashboard()
53
53
  M->>M: Serialize concurrent ensure calls
54
54
  M->>H: GET 127.0.0.1:3210/api/health
55
- alt Exact service, versions, canonical workspace, and session launcher
55
+ alt Exact service, versions, canonical workspace, and recognized launcher
56
56
  H-->>M: Bounded compatible identity
57
- M->>S: Authenticate owner and register Codex parent PID
58
- M-->>D: reused, URL, workspace, versions
57
+ M->>S: Register Codex parent PID when launcher is session
58
+ M->>H: Verify exact health again
59
+ M-->>D: reused, URL, workspace, versions, actual launcher
59
60
  else No listener
60
61
  H--xM: Connection refused
61
62
  M->>S: Launch session process on 127.0.0.1:3210
62
- S-->>M: Private owner metadata and bounded identity
63
+ S-->>M: Ready after bind, initialization, and initial lease
63
64
  M-->>D: started, URL, workspace, versions
64
- else Verified older TaskChef listener for this workspace
65
- M->>S: Authenticated prepare, lease join, and commit
66
- S-->>M: Signed final leases and bounded retry before shutdown
65
+ else Recognized older TaskChef listener for this workspace
66
+ M->>S: Simple guarded graceful shutdown request
67
+ S-->>M: Port released within bounded wait
67
68
  M->>S: Launch installed session version
68
69
  M-->>D: started, URL, workspace, versions
69
- else Standalone, unknown, stale, or different workspace
70
+ else Newer, unknown, malformed, or different workspace
70
71
  H-->>M: Missing or incompatible identity
71
72
  M-->>D: Actionable conflict, listener untouched
72
73
  end
73
- Note over D: Continue even when ensure failed
74
74
  D-->>D: Answer, report, or dispatch
75
75
  Note over D: Created-thread directive, when any, precedes final dashboard link
76
76
  ```
@@ -78,32 +78,39 @@ sequenceDiagram
78
78
  When one MCP transport or plugin process closes, the dashboard remains while a
79
79
  registered Codex session PID is alive. After every registered PID disappears
80
80
  for the grace period, the dashboard closes and exits. A foreground
81
- `taskchef dashboard` listener identifies itself as standalone and is never
82
- reused as the canonical session dashboard. No TaskChef path terminates an
83
- incompatible listener or installs OS persistence.
81
+ `taskchef dashboard` listener identifies itself as `standalone`. An
82
+ exact-current, healthy same-workspace listener with a recognized `standalone`,
83
+ `mcp`, or `session` launcher may be reused; ensure reports its actual launcher,
84
+ and only `session` listeners register Codex PID leases. No TaskChef path
85
+ terminates an incompatible listener or installs OS persistence.
84
86
 
85
87
  Autostart and explicit ensures share the same manager promise, while
86
- cross-process port races converge through authenticated reuse. An
88
+ cross-process port races converge through exact health recognition and port binding. An
87
89
  explicit `dashboard.autostart: false` skips only activation-time ensure. Any
88
90
  failure is reduced to a fixed stderr diagnostic; tool registration and MCP
89
91
  availability continue. Activation never opens a browser.
90
92
 
91
93
  ## Release-install verification
92
94
 
93
- The practical release handoff ends in this order:
95
+ The practical release-install sequence ends in this order:
94
96
 
95
97
  1. Install the released plugin.
96
98
  2. Activate or reload its new TaskChef MCP process.
97
99
  3. Run `$taskchef-dashboard` or call `ensure_dashboard`.
98
- 4. Verify the expected TaskChef version, protocol `serverVersion`, `session`
100
+ 4. Verify the expected TaskChef version, protocol `serverVersion`, reported
99
101
  launcher, canonical workspace, and canonical URL returned by the dashboard identity.
100
102
 
101
103
  Replacing plugin files alone cannot execute autostart because old code remains
102
104
  in an already-running MCP process. Installation does not necessarily reload
103
- Codex. Activation of the installed MCP authenticates and retires a verified
104
- older TaskChef listener before starting the installed session version. An
105
- exact-compatible session dashboard may be reused; a newer, standalone, or
106
- unknown listener is never terminated or replaced.
105
+ Codex. Activation of the installed MCP asks a recognized older same-workspace
106
+ TaskChef listener to shut down gracefully. If the listener accepts, TaskChef
107
+ waits boundedly for its port and starts the installed session version. If the
108
+ listener lacks the compatible callback or refuses the request, it remains
109
+ running and ensure reports a conflict. An exact-compatible same-workspace
110
+ dashboard with any recognized launcher may be reused; a newer or unknown
111
+ listener is never terminated or replaced. A release using the former HMAC
112
+ control protocol needs one verified stop through its existing lifecycle before
113
+ the simple control protocol can take over.
107
114
 
108
115
  ## Normal delegation and self-linking
109
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "8.3.0",
3
+ "version": "8.3.1",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -0,0 +1,34 @@
1
+ export const DASHBOARD_CONTROL_SHUTDOWN_PATH = "/api/control/shutdown";
2
+ export const DASHBOARD_CONTROL_SESSION_PATH = "/api/control/session";
3
+ export const DASHBOARD_CONTROL_HEADER = "x-taskchef-control";
4
+
5
+ const DASHBOARD_IDENTITY_KEYS = Object.freeze([
6
+ "launcher",
7
+ "schemaVersion",
8
+ "serverVersion",
9
+ "service",
10
+ "taskchefVersion",
11
+ "workspace",
12
+ ]);
13
+
14
+ export function exactDashboardIdentity(value, expected) {
15
+ if (!value || typeof value !== "object" || Array.isArray(value)
16
+ || !expected || typeof expected !== "object" || Array.isArray(expected)) return false;
17
+ const valueKeys = Object.keys(value).sort();
18
+ const expectedKeys = Object.keys(expected).sort();
19
+ return valueKeys.length === DASHBOARD_IDENTITY_KEYS.length
20
+ && expectedKeys.length === DASHBOARD_IDENTITY_KEYS.length
21
+ && DASHBOARD_IDENTITY_KEYS.every((key, index) => valueKeys[index] === key
22
+ && expectedKeys[index] === key && value[key] === expected[key]);
23
+ }
24
+
25
+ export function validDashboardControlRequest(request, {
26
+ authority,
27
+ origin,
28
+ action,
29
+ } = {}) {
30
+ return request.headers.host === authority
31
+ && request.headers.origin === origin
32
+ && request.headers[DASHBOARD_CONTROL_HEADER] === action
33
+ && request.headers["content-type"] === "application/json";
34
+ }