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/.codex-plugin/plugin.json +1 -1
- package/README.md +45 -49
- package/docs/dashboard-lifecycle.md +108 -194
- package/docs/spec.md +62 -88
- package/docs/workflows.md +29 -22
- package/package.json +1 -1
- package/src/dashboard-control.js +34 -0
- package/src/dashboard-manager.js +183 -842
- package/src/dashboard-session-process.js +35 -126
- package/src/dashboard-session.js +0 -5
- package/src/dashboard.js +28 -281
- package/src/mcp.js +3 -3
- package/src/dashboard-ownership.js +0 -251
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
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
347
|
-
Codex-session-scoped loopback dashboard.
|
|
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
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
|
447
|
-
MUST propagate through that channel. A bounded
|
|
448
|
-
cooperatively cancel the exact child so late
|
|
449
|
-
|
|
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
|
|
772
|
-
verified legacy listener MUST be reported distinctly from an unknown port
|
|
773
|
-
conflict
|
|
774
|
-
|
|
775
|
-
|
|
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`,
|
|
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,
|
|
28
|
-
| `src/dashboard-session-process.js` | Independent loopback server,
|
|
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
|
|
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
|
|
55
|
+
alt Exact service, versions, canonical workspace, and recognized launcher
|
|
56
56
|
H-->>M: Bounded compatible identity
|
|
57
|
-
M->>S:
|
|
58
|
-
M
|
|
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:
|
|
63
|
+
S-->>M: Ready after bind, initialization, and initial lease
|
|
63
64
|
M-->>D: started, URL, workspace, versions
|
|
64
|
-
else
|
|
65
|
-
M->>S:
|
|
66
|
-
S-->>M:
|
|
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
|
|
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
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
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
|
|
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`,
|
|
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
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
@@ -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
|
+
}
|