taskchef 8.7.1 → 9.0.0
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 +7 -10
- package/.mcp.json +3 -2
- package/README.md +31 -158
- package/docs/mcp-app.md +3 -3
- package/docs/spec.md +38 -746
- package/docs/taskchef-next-storage.md +2 -2
- package/index.js +2 -99
- package/package.json +12 -49
- package/skills/delegate/SKILL.md +44 -0
- package/skills/delegate/agents/openai.yaml +4 -0
- package/src/codex-app.js +3 -152
- package/src/codex-session-scanner.js +2 -2
- package/src/file-lock.js +25 -0
- package/src/mcp-app/react/NextTaskDetail.test.tsx +1 -1
- package/src/mcp-app/react/NextTaskDetail.tsx +8 -8
- package/src/mcp-app/react/main.test.tsx +3 -3
- package/src/mcp-app/react/main.tsx +7 -7
- package/src/mcp-app.js +2 -2
- package/src/mcp.js +4 -542
- package/src/next-cache.js +2 -2
- package/src/next-github.js +3 -3
- package/src/next-notifications.js +2 -2
- package/src/{dashboard/react → ui}/components/CardStatusIcons.tsx +1 -1
- package/src/{dashboard/react → ui}/components/GitHubLinks.tsx +1 -1
- package/src/{dashboard/react → ui}/components/LinkedText.tsx +1 -1
- package/src/{dashboard/react → ui}/components/RelativeTime.test.tsx +1 -1
- package/src/{dashboard/react → ui}/components/RelativeTime.tsx +1 -1
- package/src/{dashboard/react → ui}/components/ReplyImagePreview.test.tsx +1 -1
- package/src/{dashboard/react → ui}/components/TaskBoard.tsx +1 -1
- package/src/{dashboard/react → ui}/components/TaskCard.tsx +1 -1
- package/src/{dashboard/react → ui}/components/TurnContent.tsx +1 -1
- package/src/{dashboard/react → ui}/presentation.ts +1 -1
- package/src/version.js +0 -1
- package/BACKLOG.md +0 -48
- package/assets/e2e-benchmark-example.json +0 -50
- package/assets/taskchef-dispatcher-instructions.md +0 -44
- package/bin/taskchef.js +0 -20
- package/docs/advanced-guide.md +0 -497
- package/docs/dashboard-lifecycle.md +0 -149
- package/docs/dashboard-ui.md +0 -40
- package/docs/firstmate-taskchef-comparison.md +0 -283
- package/docs/images/ccusage-token-consumption.png +0 -0
- package/docs/images/dashboard-identity.jpg +0 -0
- package/docs/images/dashboard-token-pending.jpg +0 -0
- package/docs/images/interrupted-turn-recovery.jpg +0 -0
- package/docs/images/notification-event-snapshots.jpg +0 -0
- package/docs/images/readme-board.png +0 -0
- package/docs/images/readme-completed.png +0 -0
- package/docs/images/readme-list.png +0 -0
- package/docs/images/readme-needs-input.png +0 -0
- package/docs/images/readme-roles.png +0 -0
- package/docs/images/reported-work-duration-desktop.jpg +0 -0
- package/docs/images/reported-work-duration-mobile.jpg +0 -0
- package/docs/images/result-history-dashboard.jpg +0 -0
- package/docs/images/taskchef-pr-icon-comparison.jpg +0 -0
- package/docs/images/taskchef-unified-status-popup.jpg +0 -0
- package/docs/workflows.md +0 -476
- package/mcp/dashboard-session.js +0 -51
- package/scripts/benchmark-dispatch-prepare.js +0 -90
- package/scripts/e2e-benchmark.js +0 -206
- package/scripts/roles/resolve_roles.py +0 -293
- package/skills/taskchef-bootstrap/SKILL.md +0 -96
- package/skills/taskchef-bootstrap/agents/openai.yaml +0 -4
- package/skills/taskchef-bootstrap/references/project-index.md +0 -154
- package/skills/taskchef-copilot/SKILL.md +0 -75
- package/skills/taskchef-copilot/agents/openai.yaml +0 -4
- package/skills/taskchef-dashboard/SKILL.md +0 -24
- package/skills/taskchef-dashboard/agents/openai.yaml +0 -4
- package/skills/taskchef-delegate/SKILL.md +0 -102
- package/skills/taskchef-delegate/agents/openai.yaml +0 -4
- package/skills/taskchef-delegate/references/model-roles.md +0 -29
- package/skills/taskchef-executor/SKILL.md +0 -154
- package/skills/taskchef-executor/agents/openai.yaml +0 -4
- package/skills/taskchef-executor/references/compatibility.md +0 -22
- package/skills/taskchef-executor/references/ending-actions.md +0 -26
- package/src/backups.js +0 -359
- package/src/cli.js +0 -733
- package/src/config-audit.js +0 -99
- package/src/config-mutations.js +0 -106
- package/src/dashboard/actions.js +0 -136
- package/src/dashboard/app.js +0 -1172
- package/src/dashboard/dist/app.js +0 -269
- package/src/dashboard/dist/app.js.map +0 -1
- package/src/dashboard/dist/index.html +0 -16
- package/src/dashboard/dist/styles.css +0 -1
- package/src/dashboard/index.html +0 -148
- package/src/dashboard/react/App.test.tsx +0 -346
- package/src/dashboard/react/App.tsx +0 -515
- package/src/dashboard/react/api.test.ts +0 -40
- package/src/dashboard/react/api.ts +0 -103
- package/src/dashboard/react/components/ActivityTimeline.tsx +0 -118
- package/src/dashboard/react/components/ModelSettings.test.tsx +0 -180
- package/src/dashboard/react/components/ModelSettings.tsx +0 -106
- package/src/dashboard/react/components/NotificationCenter.test.tsx +0 -76
- package/src/dashboard/react/components/NotificationCenter.tsx +0 -111
- package/src/dashboard/react/components/PullRequestBadges.tsx +0 -18
- package/src/dashboard/react/components/TaskDetail.test.tsx +0 -255
- package/src/dashboard/react/components/TaskDetail.tsx +0 -271
- package/src/dashboard/react/components/UsagePanel.tsx +0 -96
- package/src/dashboard/react/index.html +0 -15
- package/src/dashboard/react/main.tsx +0 -12
- package/src/dashboard/react/overlay-state.test.ts +0 -110
- package/src/dashboard/react/overlay-state.ts +0 -60
- package/src/dashboard/react/presentation.test.tsx +0 -571
- package/src/dashboard/styles.css +0 -261
- package/src/dashboard-control.js +0 -34
- package/src/dashboard-manager.js +0 -339
- package/src/dashboard-session-process.js +0 -88
- package/src/dashboard-session.js +0 -86
- package/src/dashboard.js +0 -1303
- package/src/delegation.js +0 -277
- package/src/github.js +0 -107
- package/src/model-roles.js +0 -70
- package/src/recorded-usage-tracker.js +0 -92
- package/src/usage-tracker.js +0 -650
- package/src/usage.js +0 -367
- package/src/workspace-path.js +0 -48
- package/src/workspace.js +0 -3629
- /package/src/{dashboard/react → ui}/components/AnimatedUsage.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/CardStatusIcons.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ImageGallery.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/OpenChatButton.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/OpenChatButton.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/PrGlyph.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/QueuedPrompts.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/QueuedPrompts.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ReplyCover.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ReplyCover.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ReplyImagePreview.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ReplyMarkdown.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ReplyMarkdown.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/SavedUsageFormat.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/SavedUsageFormat.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/ShimmerText.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/StatsGrid.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/StatusBadge.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/TaskBoard.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/TaskCardStats.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/TaskCardStats.tsx +0 -0
- /package/src/{dashboard/react → ui}/components/TurnContent.test.tsx +0 -0
- /package/src/{dashboard/react → ui}/fixtures.ts +0 -0
- /package/src/{dashboard → ui}/github-links.js +0 -0
- /package/src/{dashboard/react → ui}/image-cache.test.ts +0 -0
- /package/src/{dashboard/react → ui}/image-cache.ts +0 -0
- /package/src/{dashboard → ui}/state.js +0 -0
- /package/src/{dashboard/react → ui}/styles.css +0 -0
- /package/src/{dashboard/react → ui}/test-setup.ts +0 -0
- /package/src/{dashboard → ui}/time.js +0 -0
- /package/src/{dashboard/react → ui}/types.ts +0 -0
package/docs/workflows.md
DELETED
|
@@ -1,476 +0,0 @@
|
|
|
1
|
-
# TaskChef workflows
|
|
2
|
-
|
|
3
|
-
This developer and advanced-agent guide explains how the current implementation
|
|
4
|
-
moves data through TaskChef. The [specification](spec.md) is normative; the
|
|
5
|
-
[advanced guide](advanced-guide.md) owns detailed user operation. The
|
|
6
|
-
[FirstMate comparison](firstmate-taskchef-comparison.md) is non-normative
|
|
7
|
-
research. The [dashboard lifecycle](dashboard-lifecycle.md) owns the focused
|
|
8
|
-
start, upgrade, shutdown, and security rationale.
|
|
9
|
-
|
|
10
|
-
## Implementation map
|
|
11
|
-
|
|
12
|
-
| Surface | Responsibility |
|
|
13
|
-
| --- | --- |
|
|
14
|
-
| `skills/taskchef-delegate/SKILL.md` | Split, route, record-before-create, create, return. |
|
|
15
|
-
| `skills/taskchef-executor/SKILL.md` | Own, self-link, execute, and report every executor turn. |
|
|
16
|
-
| `skills/taskchef-bootstrap/SKILL.md` | Initialize the workspace and maintain the Codex project index. |
|
|
17
|
-
| `skills/taskchef-dashboard/SKILL.md` | Ensure or recover the canonical dashboard and return its URL. |
|
|
18
|
-
| `skills/taskchef-copilot/SKILL.md` | Explain normalized cached briefs and coordinate safe next actions. |
|
|
19
|
-
| `src/mcp.js` | Dashboard ensure, four primary lifecycle tools, one deprecated alias, MCP cleanup, and annotations. |
|
|
20
|
-
| `src/delegation.js` | UUID marker, concise executor-skill invocation shape, and creation-failure handling. |
|
|
21
|
-
| `src/workspace.js` | Current schemas, validation, locking, atomic JSONL writes, linking, and result freshness. |
|
|
22
|
-
| `src/config-mutations.js` | Canonical hashes and exact field-level project diffs. |
|
|
23
|
-
| `src/backups.js` | Private snapshot creation, verification, retention, and restore inputs. |
|
|
24
|
-
| `src/config-audit.js` | Durable prepared/terminal configuration-mutation journal. |
|
|
25
|
-
| `src/cli.js` | Administration, normalized cached briefs, inspection, diagnostics, and dashboard startup. |
|
|
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, 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
|
-
| `src/dashboard-session.js` | Exact registered-PID liveness checks and grace-period expiry. |
|
|
30
|
-
| `src/usage.js` | Normalized private usage projections and historical cache compatibility. |
|
|
31
|
-
| `src/usage-tracker.js` | Usage-provider factory and compatibility with injected cumulative readers. |
|
|
32
|
-
| `src/recorded-usage-tracker.js` | Lazy native-turn projections, descendant totals, bounded requests, and persistent web usage. |
|
|
33
|
-
| `src/codex-session-usage.js` | Saved Codex accounting, incremental file reads, derived disk caches, and shared pricing. |
|
|
34
|
-
|
|
35
|
-
The MCP process resolves `TASKCHEF_WORKSPACE` once and never accepts a model
|
|
36
|
-
supplied path. The CLI resolves `--workspace`, then the environment, then the
|
|
37
|
-
per-user default.
|
|
38
|
-
|
|
39
|
-
## Dispatcher dashboard lifecycle
|
|
40
|
-
|
|
41
|
-
MCP activation makes dashboard maintenance a best-effort lifecycle action. The
|
|
42
|
-
generated managed `AGENTS.md` block also retries it before dispatcher turns and
|
|
43
|
-
keeps response ordering centralized instead of duplicating it across skills.
|
|
44
|
-
|
|
45
|
-
```mermaid
|
|
46
|
-
sequenceDiagram
|
|
47
|
-
autonumber
|
|
48
|
-
participant D as Dispatcher
|
|
49
|
-
participant M as TaskChef MCP
|
|
50
|
-
participant H as Loopback health
|
|
51
|
-
participant S as Dashboard process
|
|
52
|
-
M->>M: Read dashboard.autostart (absent means true)
|
|
53
|
-
M->>M: Best-effort ensure before MCP transport connects
|
|
54
|
-
D->>M: ensure_dashboard()
|
|
55
|
-
M->>M: Serialize concurrent ensure calls
|
|
56
|
-
M->>H: GET 127.0.0.1:3210/api/health
|
|
57
|
-
alt Exact service, versions, canonical workspace, and recognized launcher
|
|
58
|
-
H-->>M: Bounded compatible identity
|
|
59
|
-
M->>S: Register Codex parent PID when launcher is session
|
|
60
|
-
M->>H: Verify exact health again
|
|
61
|
-
M-->>D: reused, URL, workspace, versions, actual launcher
|
|
62
|
-
else No listener
|
|
63
|
-
H--xM: Connection refused
|
|
64
|
-
M->>S: Launch session process on 127.0.0.1:3210
|
|
65
|
-
S-->>M: Ready after bind, initialization, and initial lease
|
|
66
|
-
M-->>D: started, URL, workspace, versions
|
|
67
|
-
else Recognized older TaskChef listener for this workspace
|
|
68
|
-
M->>S: Simple guarded graceful shutdown request
|
|
69
|
-
S-->>M: Port released within bounded wait
|
|
70
|
-
M->>S: Launch installed session version
|
|
71
|
-
M-->>D: started, URL, workspace, versions
|
|
72
|
-
else Newer, unknown, malformed, or different workspace
|
|
73
|
-
H-->>M: Missing or incompatible identity
|
|
74
|
-
M-->>D: Actionable conflict, listener untouched
|
|
75
|
-
end
|
|
76
|
-
D-->>D: Answer, report, or dispatch
|
|
77
|
-
Note over D: Created-thread directive, when any, precedes final dashboard link
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
When one MCP transport or plugin process closes, the dashboard remains while a
|
|
81
|
-
registered Codex session PID is alive. After every registered PID disappears
|
|
82
|
-
for the grace period, the dashboard closes and exits. A foreground
|
|
83
|
-
`taskchef dashboard` listener identifies itself as `standalone`. An
|
|
84
|
-
exact-current, healthy same-workspace listener with a recognized `standalone`,
|
|
85
|
-
`mcp`, or `session` launcher may be reused; ensure reports its actual launcher,
|
|
86
|
-
and only `session` listeners register Codex PID leases. No TaskChef path
|
|
87
|
-
terminates an incompatible listener or installs OS persistence.
|
|
88
|
-
|
|
89
|
-
Autostart and explicit ensures share the same manager promise, while
|
|
90
|
-
cross-process port races converge through exact health recognition and port binding. An
|
|
91
|
-
explicit `dashboard.autostart: false` skips only activation-time ensure. Any
|
|
92
|
-
failure is reduced to a fixed stderr diagnostic; tool registration and MCP
|
|
93
|
-
availability continue. Activation never opens a browser.
|
|
94
|
-
|
|
95
|
-
## Release-install verification
|
|
96
|
-
|
|
97
|
-
The practical release-install sequence ends in this order:
|
|
98
|
-
|
|
99
|
-
1. Install the released plugin.
|
|
100
|
-
2. Activate or reload its new TaskChef MCP process.
|
|
101
|
-
3. Run `$taskchef-dashboard` or call `ensure_dashboard`.
|
|
102
|
-
4. Verify the expected TaskChef version, protocol `serverVersion`, reported
|
|
103
|
-
launcher, canonical workspace, and canonical URL returned by the dashboard identity.
|
|
104
|
-
|
|
105
|
-
Replacing plugin files alone cannot execute autostart because old code remains
|
|
106
|
-
in an already-running MCP process. Installation does not necessarily reload
|
|
107
|
-
Codex. Activation of the installed MCP asks a recognized older same-workspace
|
|
108
|
-
TaskChef listener to shut down gracefully. If the listener accepts, TaskChef
|
|
109
|
-
waits boundedly for its port and starts the installed session version. If the
|
|
110
|
-
listener lacks the compatible callback or refuses the request, it remains
|
|
111
|
-
running and ensure reports a conflict. An exact-compatible same-workspace
|
|
112
|
-
dashboard with any recognized launcher may be reused; a newer or unknown
|
|
113
|
-
listener is never terminated or replaced. A release using the former HMAC
|
|
114
|
-
control protocol needs one verified stop through its existing lifecycle before
|
|
115
|
-
the simple control protocol can take over.
|
|
116
|
-
|
|
117
|
-
## Normal delegation and self-linking
|
|
118
|
-
|
|
119
|
-
The dispatcher uses native Codex project discovery for routing and MCP for
|
|
120
|
-
TaskChef state. It never supplies executor identity.
|
|
121
|
-
|
|
122
|
-
```mermaid
|
|
123
|
-
sequenceDiagram
|
|
124
|
-
autonumber
|
|
125
|
-
actor U as User
|
|
126
|
-
participant D as Dispatcher skill
|
|
127
|
-
participant M as TaskChef MCP
|
|
128
|
-
participant W as workspace.js
|
|
129
|
-
participant C as Native Codex
|
|
130
|
-
participant E as Executor
|
|
131
|
-
U->>D: Request outcome
|
|
132
|
-
D->>C: List native projects once
|
|
133
|
-
D->>M: reconcile_projects(snapshot)
|
|
134
|
-
M-->>D: Counts, available targets, diagnostics
|
|
135
|
-
par Prepare each outcome
|
|
136
|
-
D->>M: prepare_dispatch()
|
|
137
|
-
M-->>D: UUID, marker, projects
|
|
138
|
-
end
|
|
139
|
-
D->>D: Choose one configured and native project
|
|
140
|
-
D->>M: record_task(id, project, title, instruction, null)
|
|
141
|
-
M->>W: recordTask()
|
|
142
|
-
W->>W: Lock, validate, append schema-10 snapshot
|
|
143
|
-
W-->>M: working link-pending task
|
|
144
|
-
M-->>D: task
|
|
145
|
-
D->>C: Create executor with marked instruction
|
|
146
|
-
C-->>D: Created-task reference
|
|
147
|
-
D-->>U: Return immediately
|
|
148
|
-
C->>E: Start executor and load $taskchef-executor
|
|
149
|
-
E->>E: Read own CODEX_THREAD_ID
|
|
150
|
-
E->>M: link_task(taskId, threadId)
|
|
151
|
-
M->>W: linkTask()
|
|
152
|
-
W->>W: Lock and replace null identity atomically
|
|
153
|
-
W-->>E: linked working task
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Record-before-create makes native creation failure observable. Executor
|
|
157
|
-
self-linking removes dispatcher-side polling, task search, title matching, and
|
|
158
|
-
parent/child identity inference.
|
|
159
|
-
|
|
160
|
-
The created executor is the stable visible owner. For repository work, an
|
|
161
|
-
explicit combined investigate-or-plan-and-implement request runs a fresh
|
|
162
|
-
Planner, Implementer, and Reviewer in that order. Direct implementation and a
|
|
163
|
-
later request to implement prior investigation skip the Planner and run a fresh
|
|
164
|
-
Implementer and Reviewer in the same visible task. Each role is resolved
|
|
165
|
-
immediately before use when agent TOML exists; absent configuration inherits the
|
|
166
|
-
parent's settings without requiring Python. Only one writer may be active.
|
|
167
|
-
TaskChef records only the executor's ordinary request/result timeline; it does
|
|
168
|
-
not track child phases or identities.
|
|
169
|
-
|
|
170
|
-
The generated task begins with the complete assignment, adds the local reporting
|
|
171
|
-
authorization paragraph separated by a blank line, leaves one blank line,
|
|
172
|
-
then places one explicit `$taskchef-executor` invocation immediately before its
|
|
173
|
-
final marker. Older
|
|
174
|
-
recorded tasks with first-line HTML or heading markers and former inline
|
|
175
|
-
protocol remain readable; the deprecated `report_result` alias preserves their
|
|
176
|
-
semantic callbacks.
|
|
177
|
-
|
|
178
|
-
## State reporting
|
|
179
|
-
|
|
180
|
-
The executor always establishes a `turnRef` before work. An exact native read
|
|
181
|
-
supplies the same value as optional `turnId` metadata when available; otherwise
|
|
182
|
-
the executor retains a fresh UUID and reports `turnId: null`. `report_state`
|
|
183
|
-
records live turn state as a paired request/result timeline.
|
|
184
|
-
|
|
185
|
-
That timeline is also the durable GitHub-link source. A working request names a
|
|
186
|
-
known selected repository with its canonical GitHub URL. A terminal result
|
|
187
|
-
keeps full issue and pull-request URLs, including separate child-repository and
|
|
188
|
-
workspace/root pull requests. The dashboard does not recover omitted links by
|
|
189
|
-
scanning the native Codex transcript, and the executor does not guess an
|
|
190
|
-
ambiguous repository.
|
|
191
|
-
|
|
192
|
-
```mermaid
|
|
193
|
-
sequenceDiagram
|
|
194
|
-
autonumber
|
|
195
|
-
participant E as Executor
|
|
196
|
-
participant C as Native Codex task API
|
|
197
|
-
participant M as TaskChef MCP
|
|
198
|
-
participant W as workspace.js
|
|
199
|
-
opt Native turn metadata available
|
|
200
|
-
E->>C: Exact read of linked executor
|
|
201
|
-
C-->>E: Current turn ID
|
|
202
|
-
end
|
|
203
|
-
E->>E: Retain native ID or generate fallback UUID turnRef
|
|
204
|
-
E->>M: report_state(..., turnRef, turnId, working, requestSummary)
|
|
205
|
-
M->>W: reportTaskState()
|
|
206
|
-
W->>W: Append turn with request and null result
|
|
207
|
-
E->>E: Work, finish, or reach semantic decision
|
|
208
|
-
E->>M: report_state(..., same turnRef, same turnId, semantic status, summary)
|
|
209
|
-
M->>W: reportTaskState()
|
|
210
|
-
W->>W: Lock and validate identity and freshness
|
|
211
|
-
alt Same current working turn
|
|
212
|
-
W->>W: Fill that turn's result and derive compatibility results
|
|
213
|
-
W-->>M: Updated task
|
|
214
|
-
M-->>E: Recorded result
|
|
215
|
-
else Same turn and same result
|
|
216
|
-
W-->>M: Existing task
|
|
217
|
-
M-->>E: Idempotent success
|
|
218
|
-
else Conflict or stale turn
|
|
219
|
-
W-->>M: Tool error
|
|
220
|
-
M-->>E: Visible failure
|
|
221
|
-
end
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
A native approval prompt is not a semantic result. `needs_input` is reserved
|
|
225
|
-
for a user decision or fact required to proceed.
|
|
226
|
-
|
|
227
|
-
Ordinary completion returns after the terminal callback; it does not archive,
|
|
228
|
-
hand off, close, navigate away from, or otherwise terminate the native Codex
|
|
229
|
-
task. An archive requires an explicit request in the current assignment or
|
|
230
|
-
follow-up for that exact task. `finish`, `complete`, `done`, `ship`, and cleanup
|
|
231
|
-
alone do not authorize it. Any explicitly requested action that could make the
|
|
232
|
-
executor unavailable must follow, never precede, an accepted terminal callback.
|
|
233
|
-
If reporting fails, the later action is forbidden and the executor remains
|
|
234
|
-
accessible. If the later action fails, its error does not reopen or replace the
|
|
235
|
-
accepted semantic state.
|
|
236
|
-
|
|
237
|
-
## Follow-up turns
|
|
238
|
-
|
|
239
|
-
Turn refs are freshness tokens for semantic callbacks. The workspace rejects a
|
|
240
|
-
callback whose ref belongs to a historical turn. Fallback UUID refs are opaque
|
|
241
|
-
and unordered; native-backed UUIDv7 refs retain native ordering so a delayed,
|
|
242
|
-
previously unseen older start cannot replace newer native state.
|
|
243
|
-
|
|
244
|
-
```mermaid
|
|
245
|
-
sequenceDiagram
|
|
246
|
-
autonumber
|
|
247
|
-
actor U as User
|
|
248
|
-
participant E as Linked executor
|
|
249
|
-
participant C as Native Codex task API
|
|
250
|
-
participant M as TaskChef MCP
|
|
251
|
-
participant W as workspace.js
|
|
252
|
-
E->>M: report_state(..., turnA, needs_input, summaryA)
|
|
253
|
-
M->>W: reportTaskState()
|
|
254
|
-
W-->>M: needs_input snapshot
|
|
255
|
-
M-->>E: needs_input snapshot
|
|
256
|
-
U->>E: Provide decision
|
|
257
|
-
E->>C: Read exact executor after follow-up
|
|
258
|
-
C-->>E: turnB
|
|
259
|
-
E->>M: report_state(..., turnB, working, requestB)
|
|
260
|
-
M->>W: reportTaskState()
|
|
261
|
-
W->>W: Require a fresh turnB ref and append request B
|
|
262
|
-
W-->>M: working snapshot plus paired timeline
|
|
263
|
-
M-->>E: working snapshot plus paired timeline
|
|
264
|
-
E->>M: report_state(..., turnB, completed, summaryB)
|
|
265
|
-
M->>W: reportTaskState()
|
|
266
|
-
W-->>M: completed snapshot plus result B
|
|
267
|
-
M-->>E: completed snapshot plus result B
|
|
268
|
-
E->>M: report_state(..., turnA, completed, staleSummary)
|
|
269
|
-
M->>W: Validate freshness
|
|
270
|
-
W-->>M: Error: stale turnRef
|
|
271
|
-
M-->>E: Visible tool error
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
The executor contract therefore requires a new `turnRef` on every follow-up.
|
|
275
|
-
Native turn reads are preferred metadata but optional; cached or inherited IDs
|
|
276
|
-
and replacement fallback UUIDs for the same prompt are invalid.
|
|
277
|
-
|
|
278
|
-
## Interrupted-turn recovery
|
|
279
|
-
|
|
280
|
-
A crash, MCP failure, app restart, or upgrade can leave the latest turn with a
|
|
281
|
-
null result. A newer valid `working` report is the durable recovery signal. The
|
|
282
|
-
workspace handles it inside the same lock and atomic replacement as every
|
|
283
|
-
other lifecycle mutation:
|
|
284
|
-
|
|
285
|
-
```mermaid
|
|
286
|
-
sequenceDiagram
|
|
287
|
-
autonumber
|
|
288
|
-
participant E as Resumed executor
|
|
289
|
-
participant M as TaskChef MCP
|
|
290
|
-
participant W as workspace.js
|
|
291
|
-
participant F as tasks.jsonl
|
|
292
|
-
E->>M: report_state(..., turnRefB, working, requestB)
|
|
293
|
-
M->>W: reportTaskState()
|
|
294
|
-
W->>W: Acquire workspace lock and validate turnRefB is new
|
|
295
|
-
W->>W: Close unfinished turnA as interrupted
|
|
296
|
-
W->>W: Append turnB with requestB and null result
|
|
297
|
-
W->>F: One atomic schema-10 replacement
|
|
298
|
-
W-->>M: working task projected from turnB
|
|
299
|
-
M-->>E: Idempotent recovery success
|
|
300
|
-
E->>M: late semantic result for turnA
|
|
301
|
-
M->>W: Validate active turn and historical outcome
|
|
302
|
-
W-->>M: Reject stale result
|
|
303
|
-
M-->>E: Visible tool error
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
The interrupted outcome uses only the fixed TaskChef-authored summary. It is
|
|
307
|
-
visible in CLI and detail timelines but excluded from semantic `results` and
|
|
308
|
-
`lastResult`. Compact dashboard cards therefore show request B with “In
|
|
309
|
-
progress.” Notification reconciliation observes one new working event and does
|
|
310
|
-
not manufacture a failed-result event. Exact retries of either working start
|
|
311
|
-
return the current snapshot without reopening or duplicating a turn. Concurrent
|
|
312
|
-
newer starts serialize under the lock, leaving one ordered timeline whose only
|
|
313
|
-
unfinished entry is the latest turn.
|
|
314
|
-
|
|
315
|
-
## Link-pending and failure paths
|
|
316
|
-
|
|
317
|
-
A failed or interrupted link never authorizes substantive work. The record
|
|
318
|
-
remains a visible retry point for the same executor.
|
|
319
|
-
|
|
320
|
-
```mermaid
|
|
321
|
-
sequenceDiagram
|
|
322
|
-
autonumber
|
|
323
|
-
participant E as Executor
|
|
324
|
-
participant M as TaskChef MCP
|
|
325
|
-
participant W as workspace.js
|
|
326
|
-
E->>M: link_task(taskId, assertedThreadId)
|
|
327
|
-
M->>W: linkTask()
|
|
328
|
-
alt Exact eligible record and unused canonical UUIDv7
|
|
329
|
-
W->>W: Atomic null-to-thread transition
|
|
330
|
-
W-->>E: Linked task
|
|
331
|
-
else MCP unavailable or call interrupted
|
|
332
|
-
M--xE: Visible failure
|
|
333
|
-
Note over E,W: Record remains link-pending
|
|
334
|
-
else Wrong task, marker, state, or identity
|
|
335
|
-
W-->>E: Validation error
|
|
336
|
-
Note over E,W: No mutation
|
|
337
|
-
end
|
|
338
|
-
E->>E: Retry linking on a later turn before work
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
If `CODEX_THREAD_ID` is missing, the executor reports the problem visibly and
|
|
342
|
-
does not substitute `CODEX_SESSION_ID`, a parent ID, or search results.
|
|
343
|
-
|
|
344
|
-
Native creation can fail after the durable record exists:
|
|
345
|
-
|
|
346
|
-
```mermaid
|
|
347
|
-
sequenceDiagram
|
|
348
|
-
autonumber
|
|
349
|
-
participant D as Dispatcher
|
|
350
|
-
participant M as TaskChef MCP
|
|
351
|
-
participant W as workspace.js
|
|
352
|
-
participant C as Native Codex
|
|
353
|
-
D->>M: record_task(..., threadId null)
|
|
354
|
-
M->>W: Append working record
|
|
355
|
-
W-->>D: Recorded task
|
|
356
|
-
D->>C: Create executor
|
|
357
|
-
C--xD: Creation error
|
|
358
|
-
D->>D: Generate and retain fallback UUID turnRef
|
|
359
|
-
D->>M: report_state(taskId, null, turnRef, null, failed, boundedSummary)
|
|
360
|
-
M->>W: Lock and store creation failure
|
|
361
|
-
W-->>D: Failed task with turnRef and null native IDs
|
|
362
|
-
D-->>D: Preserve original creation error, task ID, and turnRef
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
The summary is bounded and excludes secrets, transcripts, and raw command
|
|
366
|
-
output. A creation-failure record cannot later be linked.
|
|
367
|
-
|
|
368
|
-
## Dashboard update flow
|
|
369
|
-
|
|
370
|
-
Every mutation rewrites `tasks.jsonl` atomically under the workspace lock.
|
|
371
|
-
The monitor tolerates replacement races, validates a complete snapshot, and
|
|
372
|
-
publishes only the newest state to each SSE client.
|
|
373
|
-
|
|
374
|
-
```mermaid
|
|
375
|
-
sequenceDiagram
|
|
376
|
-
autonumber
|
|
377
|
-
participant M as MCP writer
|
|
378
|
-
participant W as workspace.js
|
|
379
|
-
participant F as tasks.jsonl
|
|
380
|
-
participant D as Dashboard monitor
|
|
381
|
-
participant B as Browser client
|
|
382
|
-
participant C as Native Codex
|
|
383
|
-
M->>W: link_task or report_state
|
|
384
|
-
W->>W: Acquire shared lock
|
|
385
|
-
W->>F: Atomic replacement
|
|
386
|
-
W-->>M: Updated task
|
|
387
|
-
F-->>D: Filesystem change
|
|
388
|
-
D->>F: Bounded read from one descriptor
|
|
389
|
-
D->>D: Validate current schema and sort
|
|
390
|
-
D-->>B: Compact SSE snapshot with latestTurn only
|
|
391
|
-
B->>D: GET task detail on demand
|
|
392
|
-
D-->>B: Full validated history
|
|
393
|
-
B->>D: Open task action
|
|
394
|
-
alt Canonical Codex UUIDv7
|
|
395
|
-
D->>C: Direct thread navigation
|
|
396
|
-
else Null or non-native durable ID
|
|
397
|
-
D->>D: Revalidate current project configuration
|
|
398
|
-
D->>C: Open configured project
|
|
399
|
-
end
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
The dashboard binds to `127.0.0.1`, has no shared session state, limits task
|
|
403
|
-
count, file size, result count, and display fields, and checks origin/authority
|
|
404
|
-
for stateful local actions. Its bounded identity endpoint contains no task data
|
|
405
|
-
or secrets. The monitor already validates the complete log, but
|
|
406
|
-
snapshot/SSE list projections omit `turns` and derived `results` so repeated
|
|
407
|
-
updates do not resend unnecessary history. A read-only per-task endpoint returns the full timeline only
|
|
408
|
-
when the dialog opens. Historical project paths are untrusted until matched
|
|
409
|
-
against current configuration.
|
|
410
|
-
|
|
411
|
-
The browser derives one immutable Updates-panel snapshot when it first observes
|
|
412
|
-
a semantic lifecycle transition. The snapshot keeps its event-time title,
|
|
413
|
-
state/event, turn ref, optional Codex turn ID, timestamp, and relevant summary.
|
|
414
|
-
Its identity combines task ID, turn ref, and event, with creation time as the fallback when creation
|
|
415
|
-
has no turn. A separate seen-identity set outlives the bounded visible notices,
|
|
416
|
-
individual dismissals, and Clear all, preventing replay after reconnect,
|
|
417
|
-
normalization, or disappearance and reappearance. Per-task semantic signatures
|
|
418
|
-
also remain as tombstones while a task is absent. When one compact snapshot
|
|
419
|
-
first reveals a latest semantic result plus a newer working turn, the browser
|
|
420
|
-
captures both. The first page snapshot is a quiet baseline so opening or
|
|
421
|
-
refreshing the dashboard does not replay existing history. If a task is first
|
|
422
|
-
observed in a progressed state after that baseline, creation is retained
|
|
423
|
-
alongside the observable result and working events. The current task list is
|
|
424
|
-
used only as the navigation target: if the task is gone, the notice stays
|
|
425
|
-
readable and selection reports that current details are unavailable.
|
|
426
|
-
|
|
427
|
-
The retained toast list is not live. Reconciliation passes only its newly added
|
|
428
|
-
snapshots to a separate polite status region, so reconnect, dismissal, Clear
|
|
429
|
-
all, and ordinary rerendering do not re-announce retained history. Toast action
|
|
430
|
-
labels remain concise while `aria-describedby` connects the visible summary,
|
|
431
|
-
event time, and missing-task explanation for assistive technology.
|
|
432
|
-
|
|
433
|
-
## Schema 4-9 migration
|
|
434
|
-
|
|
435
|
-
`taskchef workspace migrate` acquires the same workspace lock as lifecycle
|
|
436
|
-
writers, validates the complete legacy log, converts schema-4/5/6 results into
|
|
437
|
-
request-unknown completed turns and preserves a newer working turn, then validates the
|
|
438
|
-
complete candidate. Before replacement it writes and reads back an exclusive
|
|
439
|
-
`tasks.jsonl.pre-v10-*.bak` file. Schema-7/8 timelines gain a durable `turnRef`:
|
|
440
|
-
non-null `turnId` values are copied exactly and null IDs receive persisted UUIDs.
|
|
441
|
-
Task and turn counts plus all refs are validated before and after the atomic
|
|
442
|
-
replacement. Schema-9 turns gain explicit legacy or MCP provenance. A second
|
|
443
|
-
run sees only schema 10 and returns unchanged without
|
|
444
|
-
another backup. Unsupported or malformed input fails before backup/rewrite;
|
|
445
|
-
after a later filesystem failure, the reported backup is the recovery source.
|
|
446
|
-
|
|
447
|
-
## Concurrency and trust boundaries
|
|
448
|
-
|
|
449
|
-
Configuration and task mutations use the same `proper-lockfile` lock. ID and
|
|
450
|
-
identity uniqueness checks, link eligibility, and result freshness occur
|
|
451
|
-
inside the critical section. Writes use temporary files/hard links and atomic
|
|
452
|
-
rename so readers see complete snapshots. Configuration replacement syncs the
|
|
453
|
-
new file and workspace directory before its audit transaction is committed.
|
|
454
|
-
|
|
455
|
-
Project reconciliation consumes one agent-supplied native schema-2 snapshot;
|
|
456
|
-
the Node process never calls native Codex project tools. It filters to eligible
|
|
457
|
-
local-host targets, canonicalizes paths, preserves curated and missing entries,
|
|
458
|
-
and writes only when projects or identity bindings change. Explicit removal
|
|
459
|
-
stores an exclusion. Learned aliases, repository ownership, and responsibilities
|
|
460
|
-
stay in bounded configuration slots with compact evidence and never alter
|
|
461
|
-
historical task snapshots. Optional learning happens after an accepted terminal
|
|
462
|
-
report, so its failure cannot change that task result.
|
|
463
|
-
|
|
464
|
-
The dispatcher controls routing and immutable intent. The executor controls its
|
|
465
|
-
cooperatively asserted identity and semantic result. Neither identity nor
|
|
466
|
-
summary is cryptographically authenticated; this is a local single-user trust
|
|
467
|
-
model. Managed files, instructions, project snapshots, MCP inputs, and dashboard
|
|
468
|
-
requests are validated at every action boundary.
|
|
469
|
-
|
|
470
|
-
Configuration schema 2 and task schemas 4 through 10 are accepted. Schemas
|
|
471
|
-
4-9 are read/migration compatibility until an explicit migration or lifecycle
|
|
472
|
-
mutation upgrades each record to schema 10. Schema 10 persists `turns`, including
|
|
473
|
-
timeline-only interrupted outcomes, and derives semantic-only `results` and
|
|
474
|
-
`lastResult` plus `latestTurn` for compact compatibility. It also records turn
|
|
475
|
-
provenance and the optimistic preconditions for manual dashboard outcomes. Other schemas
|
|
476
|
-
are rejected without rewrite.
|
package/mcp/dashboard-session.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { runDashboardSessionProcess } from "../src/dashboard-session-process.js";
|
|
4
|
-
|
|
5
|
-
let cancelled = false;
|
|
6
|
-
let runtime = null;
|
|
7
|
-
let readySent = false;
|
|
8
|
-
const startupController = new AbortController();
|
|
9
|
-
process.on("message", (message) => {
|
|
10
|
-
if (message?.type !== "cancel") return;
|
|
11
|
-
cancelled = true;
|
|
12
|
-
startupController.abort();
|
|
13
|
-
if (runtime) void runtime.close().finally(() => { process.exitCode = 1; });
|
|
14
|
-
});
|
|
15
|
-
process.on("disconnect", () => {
|
|
16
|
-
if (readySent) return;
|
|
17
|
-
cancelled = true;
|
|
18
|
-
startupController.abort();
|
|
19
|
-
if (runtime) void runtime.close().finally(() => { process.exitCode = 1; });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
runtime = await runDashboardSessionProcess({ signal: startupController.signal });
|
|
24
|
-
if (cancelled) {
|
|
25
|
-
await runtime.close();
|
|
26
|
-
throw Object.assign(new Error("dashboard session startup was cancelled"), {
|
|
27
|
-
code: "TASKCHEF_DASHBOARD_START_TIMEOUT",
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
if (process.send && process.connected) {
|
|
31
|
-
readySent = true;
|
|
32
|
-
await new Promise((resolve, reject) => {
|
|
33
|
-
process.send({ type: "ready", port: runtime.server.port }, (error) =>
|
|
34
|
-
error ? reject(error) : resolve());
|
|
35
|
-
});
|
|
36
|
-
if (process.connected) process.disconnect();
|
|
37
|
-
}
|
|
38
|
-
} catch (error) {
|
|
39
|
-
readySent = false;
|
|
40
|
-
if (runtime) await runtime.close().catch(() => {});
|
|
41
|
-
if (process.send && process.connected) {
|
|
42
|
-
await new Promise((resolve) => {
|
|
43
|
-
process.send({
|
|
44
|
-
type: "error",
|
|
45
|
-
code: typeof error?.code === "string" ? error.code : "TASKCHEF_DASHBOARD_START_FAILED",
|
|
46
|
-
}, () => resolve());
|
|
47
|
-
}).catch(() => {});
|
|
48
|
-
if (process.connected) process.disconnect();
|
|
49
|
-
}
|
|
50
|
-
process.exitCode = 1;
|
|
51
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { execFileSync } from "node:child_process";
|
|
4
|
-
import { mkdtemp, mkdir } from "node:fs/promises";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { performance } from "node:perf_hooks";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
|
|
10
|
-
import { addProject, initializeWorkspace } from "../index.js";
|
|
11
|
-
|
|
12
|
-
const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
-
const cli = path.join(repositoryRoot, "bin", "taskchef.js");
|
|
14
|
-
const sampleCount = Number.parseInt(process.env.TASKCHEF_BENCHMARK_SAMPLES ?? "30", 10);
|
|
15
|
-
if (!Number.isInteger(sampleCount) || sampleCount < 1) {
|
|
16
|
-
throw new Error("TASKCHEF_BENCHMARK_SAMPLES must be a positive integer");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const root = await mkdtemp(path.join(os.tmpdir(), "taskchef-dispatch-benchmark-"));
|
|
20
|
-
const workspace = path.join(root, "workspace");
|
|
21
|
-
const project = path.join(root, "project");
|
|
22
|
-
await mkdir(project);
|
|
23
|
-
await initializeWorkspace(workspace);
|
|
24
|
-
await addProject(workspace, { name: "benchmark", path: project, githubRepos: [] });
|
|
25
|
-
|
|
26
|
-
function runTaskChef(args) {
|
|
27
|
-
return execFileSync(process.execPath, [cli, ...args], { encoding: "utf8" });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function measure(operation) {
|
|
31
|
-
const startedAt = performance.now();
|
|
32
|
-
operation();
|
|
33
|
-
return performance.now() - startedAt;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const baseline = [];
|
|
37
|
-
const prepared = [];
|
|
38
|
-
for (let index = 0; index < sampleCount; index += 1) {
|
|
39
|
-
baseline.push(measure(() => {
|
|
40
|
-
runTaskChef(["workspace", "path", "--json", "--workspace", workspace]);
|
|
41
|
-
runTaskChef(["project", "list", "--json", "--workspace", workspace]);
|
|
42
|
-
execFileSync(process.execPath, [
|
|
43
|
-
"-e",
|
|
44
|
-
"const { randomUUID } = require('node:crypto'); JSON.stringify({ taskId: randomUUID(), preparedAt: new Date().toISOString() });",
|
|
45
|
-
]);
|
|
46
|
-
}));
|
|
47
|
-
prepared.push(measure(() => {
|
|
48
|
-
const result = JSON.parse(runTaskChef([
|
|
49
|
-
"dispatch", "prepare", "--json", "--workspace", workspace,
|
|
50
|
-
]));
|
|
51
|
-
if (result.projectCount !== 1 || !result.marker.includes(result.taskId)) {
|
|
52
|
-
throw new Error("dispatch prepare returned an invalid benchmark result");
|
|
53
|
-
}
|
|
54
|
-
}));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function statistics(samples) {
|
|
58
|
-
const sorted = [...samples].sort((left, right) => left - right);
|
|
59
|
-
const percentile = (value) => sorted[Math.floor((sorted.length - 1) * value)];
|
|
60
|
-
return {
|
|
61
|
-
samples: sorted.length,
|
|
62
|
-
medianMs: Number(percentile(0.5).toFixed(2)),
|
|
63
|
-
p95Ms: Number(percentile(0.95).toFixed(2)),
|
|
64
|
-
minMs: Number(sorted[0].toFixed(2)),
|
|
65
|
-
maxMs: Number(sorted.at(-1).toFixed(2)),
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const baselineStats = statistics(baseline);
|
|
70
|
-
const preparedStats = statistics(prepared);
|
|
71
|
-
process.stdout.write(`${JSON.stringify({
|
|
72
|
-
schemaVersion: 1,
|
|
73
|
-
comparison: {
|
|
74
|
-
baseline: {
|
|
75
|
-
description: "workspace path + project list + external UUID/timestamp process",
|
|
76
|
-
processCalls: 3,
|
|
77
|
-
...baselineStats,
|
|
78
|
-
},
|
|
79
|
-
dispatchPrepare: {
|
|
80
|
-
description: "dispatch prepare",
|
|
81
|
-
processCalls: 1,
|
|
82
|
-
...preparedStats,
|
|
83
|
-
},
|
|
84
|
-
savedProcessCalls: 2,
|
|
85
|
-
medianSpeedup: Number((baselineStats.medianMs / preparedStats.medianMs).toFixed(2)),
|
|
86
|
-
medianReductionPercent: Number(
|
|
87
|
-
((1 - preparedStats.medianMs / baselineStats.medianMs) * 100).toFixed(1),
|
|
88
|
-
),
|
|
89
|
-
},
|
|
90
|
-
}, null, 2)}\n`);
|