taskchef 6.1.3 → 7.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 +1 -1
- package/BACKLOG.md +1 -1
- package/README.md +96 -454
- package/docs/firstmate-taskchef-comparison.md +277 -0
- package/docs/spec.md +246 -0
- package/docs/workflows.md +236 -0
- package/index.js +0 -2
- package/package.json +4 -3
- package/scripts/benchmark-dispatch-prepare.js +7 -7
- package/skills/taskchef-bootstrap/SKILL.md +7 -8
- package/skills/taskchef-delegate/SKILL.md +2 -9
- package/skills/taskchef-report/SKILL.md +4 -8
- package/src/cli.js +0 -21
- package/src/dashboard.js +1 -1
- package/src/delegation.js +4 -8
- package/src/github.js +0 -4
- package/src/mcp.js +5 -5
- package/src/workspace.js +26 -233
- package/SPEC.md +0 -107
- package/docs/delegation-design.md +0 -227
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
[README](../README.md) owns user operation. The
|
|
6
|
+
[FirstMate comparison](firstmate-taskchef-comparison.md) is non-normative
|
|
7
|
+
research.
|
|
8
|
+
|
|
9
|
+
## Implementation map
|
|
10
|
+
|
|
11
|
+
| Surface | Responsibility |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `skills/taskchef-delegate/SKILL.md` | Split, route, record-before-create, create, return. |
|
|
14
|
+
| `skills/taskchef-bootstrap/SKILL.md` | Initialize current workspace and configure projects. |
|
|
15
|
+
| `skills/taskchef-report/SKILL.md` | Select cached tasks and perform bounded live checks. |
|
|
16
|
+
| `src/mcp.js` | Four structured lifecycle tools and MCP annotations. |
|
|
17
|
+
| `src/delegation.js` | UUID marker, executor contract paragraphs, and creation-failure handling. |
|
|
18
|
+
| `src/workspace.js` | Current schemas, validation, locking, atomic JSONL writes, linking, and result freshness. |
|
|
19
|
+
| `src/cli.js` | Administration, inspection, diagnostics, and dashboard startup. |
|
|
20
|
+
| `src/dashboard.js` | Validated snapshots, SSE fan-out, and bounded open actions. |
|
|
21
|
+
|
|
22
|
+
The MCP process resolves `TASKCHEF_WORKSPACE` once and never accepts a model
|
|
23
|
+
supplied path. The CLI resolves `--workspace`, then the environment, then the
|
|
24
|
+
per-user default.
|
|
25
|
+
|
|
26
|
+
## Normal delegation and self-linking
|
|
27
|
+
|
|
28
|
+
The dispatcher uses native Codex project discovery for routing and MCP for
|
|
29
|
+
TaskChef state. It never supplies executor identity.
|
|
30
|
+
|
|
31
|
+
```mermaid
|
|
32
|
+
sequenceDiagram
|
|
33
|
+
autonumber
|
|
34
|
+
actor U as User
|
|
35
|
+
participant D as Dispatcher skill
|
|
36
|
+
participant M as TaskChef MCP
|
|
37
|
+
participant W as workspace.js
|
|
38
|
+
participant C as Native Codex
|
|
39
|
+
participant E as Executor
|
|
40
|
+
U->>D: Request outcome
|
|
41
|
+
par Route once
|
|
42
|
+
D->>C: List native projects
|
|
43
|
+
and Prepare each outcome
|
|
44
|
+
D->>M: prepare_dispatch()
|
|
45
|
+
M->>W: prepareDispatch()
|
|
46
|
+
W-->>M: UUID, marker, projects
|
|
47
|
+
M-->>D: preparation
|
|
48
|
+
end
|
|
49
|
+
D->>D: Choose one configured and native project
|
|
50
|
+
D->>M: record_task(id, project, title, instruction, null)
|
|
51
|
+
M->>W: recordTask()
|
|
52
|
+
W->>W: Lock, validate, append schema-4 snapshot
|
|
53
|
+
W-->>M: working link-pending task
|
|
54
|
+
M-->>D: task
|
|
55
|
+
D->>C: Create executor with marked instruction
|
|
56
|
+
C-->>D: Created-task reference
|
|
57
|
+
D-->>U: Return immediately
|
|
58
|
+
C->>E: Start executor
|
|
59
|
+
E->>E: Read own CODEX_THREAD_ID
|
|
60
|
+
E->>M: link_task(taskId, threadId)
|
|
61
|
+
M->>W: linkTask()
|
|
62
|
+
W->>W: Lock and replace null identity atomically
|
|
63
|
+
W-->>E: linked working task
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Record-before-create makes native creation failure observable. Executor
|
|
67
|
+
self-linking removes dispatcher-side polling, task search, title matching, and
|
|
68
|
+
parent/child identity inference.
|
|
69
|
+
|
|
70
|
+
## Result reporting
|
|
71
|
+
|
|
72
|
+
The executor obtains the turn identity from an exact native read of its own
|
|
73
|
+
linked task. `report_result` updates only the latest semantic snapshot.
|
|
74
|
+
|
|
75
|
+
```mermaid
|
|
76
|
+
sequenceDiagram
|
|
77
|
+
autonumber
|
|
78
|
+
participant E as Executor
|
|
79
|
+
participant C as Native Codex task API
|
|
80
|
+
participant M as TaskChef MCP
|
|
81
|
+
participant W as workspace.js
|
|
82
|
+
E->>E: Finish or reach semantic decision
|
|
83
|
+
E->>C: Exact read of linked executor
|
|
84
|
+
C-->>E: Current turn ID
|
|
85
|
+
E->>M: report_result(taskId, threadId, turnId, status, summary)
|
|
86
|
+
M->>W: reportTaskResult()
|
|
87
|
+
W->>W: Lock and validate identity and freshness
|
|
88
|
+
alt Fresh result
|
|
89
|
+
W->>W: Replace status, summary, turnId, updatedAt, updatedBy
|
|
90
|
+
W-->>M: Updated task
|
|
91
|
+
M-->>E: Recorded result
|
|
92
|
+
else Same turn and same result
|
|
93
|
+
W-->>M: Existing task
|
|
94
|
+
M-->>E: Idempotent success
|
|
95
|
+
else Conflict or stale turn
|
|
96
|
+
W-->>M: Tool error
|
|
97
|
+
M-->>E: Visible failure
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A native approval prompt is not a semantic result. `needs_input` is reserved
|
|
102
|
+
for a user decision or fact required to proceed.
|
|
103
|
+
|
|
104
|
+
## Follow-up turns
|
|
105
|
+
|
|
106
|
+
Turn IDs are freshness tokens for semantic callbacks. Lexical UUIDv7 order lets
|
|
107
|
+
the workspace reject a callback from an older executor turn.
|
|
108
|
+
|
|
109
|
+
```mermaid
|
|
110
|
+
sequenceDiagram
|
|
111
|
+
autonumber
|
|
112
|
+
actor U as User
|
|
113
|
+
participant E as Linked executor
|
|
114
|
+
participant C as Native Codex task API
|
|
115
|
+
participant M as TaskChef MCP
|
|
116
|
+
participant W as workspace.js
|
|
117
|
+
E->>M: report_result(..., turnA, needs_input, summaryA)
|
|
118
|
+
M->>W: Store turnA
|
|
119
|
+
W-->>E: needs_input snapshot
|
|
120
|
+
U->>E: Provide decision
|
|
121
|
+
E->>C: Read exact executor after follow-up
|
|
122
|
+
C-->>E: turnB
|
|
123
|
+
E->>M: report_result(..., turnB, completed, summaryB)
|
|
124
|
+
M->>W: Require turnB greater than turnA
|
|
125
|
+
W-->>E: completed snapshot
|
|
126
|
+
E->>M: report_result(..., turnA, completed, staleSummary)
|
|
127
|
+
M->>W: Validate freshness
|
|
128
|
+
W-->>E: Error: turn is not newer
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The executor contract therefore requires a new exact read on every follow-up;
|
|
132
|
+
cached or inherited turn IDs are invalid.
|
|
133
|
+
|
|
134
|
+
## Link-pending and failure paths
|
|
135
|
+
|
|
136
|
+
A failed or interrupted link never authorizes substantive work. The record
|
|
137
|
+
remains a visible retry point for the same executor.
|
|
138
|
+
|
|
139
|
+
```mermaid
|
|
140
|
+
sequenceDiagram
|
|
141
|
+
autonumber
|
|
142
|
+
participant E as Executor
|
|
143
|
+
participant M as TaskChef MCP
|
|
144
|
+
participant W as workspace.js
|
|
145
|
+
E->>M: link_task(taskId, assertedThreadId)
|
|
146
|
+
M->>W: linkTask()
|
|
147
|
+
alt Exact eligible record and unused canonical UUIDv7
|
|
148
|
+
W->>W: Atomic null-to-thread transition
|
|
149
|
+
W-->>E: Linked task
|
|
150
|
+
else MCP unavailable or call interrupted
|
|
151
|
+
M--xE: Visible failure
|
|
152
|
+
Note over E,W: Record remains link-pending
|
|
153
|
+
else Wrong task, marker, state, or identity
|
|
154
|
+
W-->>E: Validation error
|
|
155
|
+
Note over E,W: No mutation
|
|
156
|
+
end
|
|
157
|
+
E->>E: Retry linking on a later turn before work
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
If `CODEX_THREAD_ID` is missing, the executor reports the problem visibly and
|
|
161
|
+
does not substitute `CODEX_SESSION_ID`, a parent ID, or search results.
|
|
162
|
+
|
|
163
|
+
Native creation can fail after the durable record exists:
|
|
164
|
+
|
|
165
|
+
```mermaid
|
|
166
|
+
sequenceDiagram
|
|
167
|
+
autonumber
|
|
168
|
+
participant D as Dispatcher
|
|
169
|
+
participant M as TaskChef MCP
|
|
170
|
+
participant W as workspace.js
|
|
171
|
+
participant C as Native Codex
|
|
172
|
+
D->>M: record_task(..., threadId null)
|
|
173
|
+
M->>W: Append working record
|
|
174
|
+
W-->>D: Recorded task
|
|
175
|
+
D->>C: Create executor
|
|
176
|
+
C--xD: Creation error
|
|
177
|
+
D->>M: report_result(taskId, null, null, failed, boundedSummary)
|
|
178
|
+
M->>W: Lock and store creation failure
|
|
179
|
+
W-->>D: Failed task with null IDs
|
|
180
|
+
D-->>D: Preserve original creation error and task ID
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The summary is bounded and excludes secrets, transcripts, and raw command
|
|
184
|
+
output. A creation-failure record cannot later be linked.
|
|
185
|
+
|
|
186
|
+
## Dashboard update flow
|
|
187
|
+
|
|
188
|
+
Every mutation rewrites `tasks.jsonl` atomically under the workspace lock.
|
|
189
|
+
The monitor tolerates replacement races, validates a complete snapshot, and
|
|
190
|
+
publishes only the newest state to each SSE client.
|
|
191
|
+
|
|
192
|
+
```mermaid
|
|
193
|
+
sequenceDiagram
|
|
194
|
+
autonumber
|
|
195
|
+
participant M as MCP writer
|
|
196
|
+
participant W as workspace.js
|
|
197
|
+
participant F as tasks.jsonl
|
|
198
|
+
participant D as Dashboard monitor
|
|
199
|
+
participant B as Browser client
|
|
200
|
+
participant C as Native Codex
|
|
201
|
+
M->>W: link_task or report_result
|
|
202
|
+
W->>W: Acquire shared lock
|
|
203
|
+
W->>F: Atomic replacement
|
|
204
|
+
W-->>M: Updated task
|
|
205
|
+
F-->>D: Filesystem change
|
|
206
|
+
D->>F: Bounded read from one descriptor
|
|
207
|
+
D->>D: Validate current schema and sort
|
|
208
|
+
D-->>B: SSE snapshot
|
|
209
|
+
B->>D: Open task action
|
|
210
|
+
alt Canonical Codex UUIDv7
|
|
211
|
+
D->>C: Direct thread navigation
|
|
212
|
+
else Null or non-native durable ID
|
|
213
|
+
D->>D: Revalidate current project configuration
|
|
214
|
+
D->>C: Open configured project
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The dashboard binds to `127.0.0.1`, has no shared session state, limits
|
|
219
|
+
request bodies, and checks origin/authority for stateful local actions. Historical
|
|
220
|
+
project paths are untrusted until matched against current configuration.
|
|
221
|
+
|
|
222
|
+
## Concurrency and trust boundaries
|
|
223
|
+
|
|
224
|
+
Configuration and task mutations use the same `proper-lockfile` lock. ID and
|
|
225
|
+
identity uniqueness checks, link eligibility, and result freshness occur
|
|
226
|
+
inside the critical section. Writes use temporary files/hard links and atomic
|
|
227
|
+
rename so readers see complete snapshots.
|
|
228
|
+
|
|
229
|
+
The dispatcher controls routing and immutable intent. The executor controls its
|
|
230
|
+
cooperatively asserted identity and semantic result. Neither identity nor
|
|
231
|
+
summary is cryptographically authenticated; this is a local single-user trust
|
|
232
|
+
model. Managed files, instructions, project snapshots, MCP inputs, and dashboard
|
|
233
|
+
requests are validated at every action boundary.
|
|
234
|
+
|
|
235
|
+
Only current configuration schema 2 and task schema 4 are accepted.
|
|
236
|
+
Unsupported data is rejected without rewrite.
|
package/index.js
CHANGED
|
@@ -5,7 +5,6 @@ export {
|
|
|
5
5
|
canonicalGitRoot,
|
|
6
6
|
doctorWorkspace,
|
|
7
7
|
ensureWorkspaceInstructions,
|
|
8
|
-
ensureWorkspaceSkills,
|
|
9
8
|
filterTasks,
|
|
10
9
|
importProjects,
|
|
11
10
|
initializeWorkspace,
|
|
@@ -17,7 +16,6 @@ export {
|
|
|
17
16
|
readTask,
|
|
18
17
|
recordTask,
|
|
19
18
|
reportTaskResult,
|
|
20
|
-
resolveTask,
|
|
21
19
|
requireSafeId,
|
|
22
20
|
removeProject,
|
|
23
21
|
validateConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taskchef",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "A non-blocking interactive dispatcher for visible Codex tasks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Favo Yang",
|
|
@@ -23,10 +23,11 @@
|
|
|
23
23
|
"assets",
|
|
24
24
|
"BACKLOG.md",
|
|
25
25
|
"bin",
|
|
26
|
-
"docs/
|
|
26
|
+
"docs/spec.md",
|
|
27
|
+
"docs/workflows.md",
|
|
28
|
+
"docs/firstmate-taskchef-comparison.md",
|
|
27
29
|
"index.js",
|
|
28
30
|
"mcp",
|
|
29
|
-
"SPEC.md",
|
|
30
31
|
"scripts/benchmark-dispatch-prepare.js",
|
|
31
32
|
"scripts/e2e-benchmark.js",
|
|
32
33
|
"src",
|
|
@@ -33,10 +33,10 @@ function measure(operation) {
|
|
|
33
33
|
return performance.now() - startedAt;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const
|
|
36
|
+
const baseline = [];
|
|
37
37
|
const prepared = [];
|
|
38
38
|
for (let index = 0; index < sampleCount; index += 1) {
|
|
39
|
-
|
|
39
|
+
baseline.push(measure(() => {
|
|
40
40
|
runTaskChef(["workspace", "path", "--json", "--workspace", workspace]);
|
|
41
41
|
runTaskChef(["project", "list", "--json", "--workspace", workspace]);
|
|
42
42
|
execFileSync(process.execPath, [
|
|
@@ -66,15 +66,15 @@ function statistics(samples) {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const
|
|
69
|
+
const baselineStats = statistics(baseline);
|
|
70
70
|
const preparedStats = statistics(prepared);
|
|
71
71
|
process.stdout.write(`${JSON.stringify({
|
|
72
72
|
schemaVersion: 1,
|
|
73
73
|
comparison: {
|
|
74
|
-
|
|
74
|
+
baseline: {
|
|
75
75
|
description: "workspace path + project list + external UUID/timestamp process",
|
|
76
76
|
processCalls: 3,
|
|
77
|
-
...
|
|
77
|
+
...baselineStats,
|
|
78
78
|
},
|
|
79
79
|
dispatchPrepare: {
|
|
80
80
|
description: "dispatch prepare",
|
|
@@ -82,9 +82,9 @@ process.stdout.write(`${JSON.stringify({
|
|
|
82
82
|
...preparedStats,
|
|
83
83
|
},
|
|
84
84
|
savedProcessCalls: 2,
|
|
85
|
-
medianSpeedup: Number((
|
|
85
|
+
medianSpeedup: Number((baselineStats.medianMs / preparedStats.medianMs).toFixed(2)),
|
|
86
86
|
medianReductionPercent: Number(
|
|
87
|
-
((1 - preparedStats.medianMs /
|
|
87
|
+
((1 - preparedStats.medianMs / baselineStats.medianMs) * 100).toFixed(1),
|
|
88
88
|
),
|
|
89
89
|
},
|
|
90
90
|
}, null, 2)}\n`);
|
|
@@ -15,8 +15,8 @@ all deterministic workspace operations.
|
|
|
15
15
|
## Boundaries
|
|
16
16
|
|
|
17
17
|
- Keep implementation, tests, and reports in the TaskChef source repository.
|
|
18
|
-
-
|
|
19
|
-
workspace.
|
|
18
|
+
- Create and manage only `AGENTS.md`, `taskchef.json`, and `tasks.jsonl` in a
|
|
19
|
+
dispatcher workspace. Preserve unrelated user-owned paths.
|
|
20
20
|
- Do not dispatch tasks or report on executor threads during bootstrap unless
|
|
21
21
|
the user separately requests those actions.
|
|
22
22
|
- Never create hooks, schedules, polling, or daemons. TaskChef executors
|
|
@@ -37,9 +37,9 @@ all deterministic workspace operations.
|
|
|
37
37
|
CLI discovered from the current desktop environment; never invoke
|
|
38
38
|
`codex add` or hard-code an application bundle path.
|
|
39
39
|
3. `workspace init` takes no stdin, creates an empty
|
|
40
|
-
configuration when missing, creates the one-entry-per-task JSONL log,
|
|
41
|
-
managed instructions
|
|
42
|
-
|
|
40
|
+
configuration when missing, creates the one-entry-per-task JSONL log, and
|
|
41
|
+
refreshes managed instructions. The installed plugin provides all three
|
|
42
|
+
TaskChef skills outside the dispatcher workspace.
|
|
43
43
|
4. Run `doctor --json` after setup or when the user asks to diagnose the
|
|
44
44
|
workspace. Doctor is read-only. Rerun `workspace init --json` to repair the
|
|
45
45
|
managed scaffold.
|
|
@@ -86,6 +86,5 @@ Example managed-workspace import entry:
|
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`githubRepos: []`. All subsequent configuration writes use schema version 2.
|
|
89
|
+
TaskChef accepts configuration schema version 2 only. `githubRepos` is always
|
|
90
|
+
an array; unsupported configuration is rejected without being rewritten.
|
|
@@ -23,8 +23,8 @@ reloaded or installed.
|
|
|
23
23
|
|
|
24
24
|
## Boundaries
|
|
25
25
|
|
|
26
|
-
-
|
|
27
|
-
|
|
26
|
+
- Treat only `AGENTS.md`, `taskchef.json`, and `tasks.jsonl` as TaskChef-managed
|
|
27
|
+
dispatcher files. Preserve unrelated user-owned paths.
|
|
28
28
|
- Use real Codex tasks, never collaboration or subagent tools.
|
|
29
29
|
- Never use hooks, schedules, daemons, background monitors, recent-task
|
|
30
30
|
searches, transcripts, hidden reasoning, or polling for identity.
|
|
@@ -78,10 +78,3 @@ user decision, not live approval UI.
|
|
|
78
78
|
|
|
79
79
|
The filesystem watcher surfaces `link_task` and `report_result` writes to the
|
|
80
80
|
dashboard. The linked child ID drives the exact Codex deep link.
|
|
81
|
-
|
|
82
|
-
## Legacy recovery
|
|
83
|
-
|
|
84
|
-
`taskchef task resolve` exists only for unresolved records created before the
|
|
85
|
-
self-linking schema. Require an exact marker match and a unique durable child
|
|
86
|
-
ID. The command rejects new self-linking records. Never edit `tasks.jsonl`
|
|
87
|
-
directly.
|
|
@@ -29,8 +29,8 @@ all deterministic task-log operations.
|
|
|
29
29
|
- Use the full list only when the user asks for an overview of the task history.
|
|
30
30
|
2. For an overview, select only attention-worthy candidates before detailed
|
|
31
31
|
reads:
|
|
32
|
-
- always include `working`, `needs_input`,
|
|
33
|
-
|
|
32
|
+
- always include `working`, `needs_input`, and entries with a null
|
|
33
|
+
`threadId`;
|
|
34
34
|
- include `completed` and `failed` entries updated during the last seven
|
|
35
35
|
days;
|
|
36
36
|
- omit older terminal entries by default and report the omitted count;
|
|
@@ -75,12 +75,8 @@ all deterministic task-log operations.
|
|
|
75
75
|
completed, failed, unresolved, or unknown. Show the cached summary when it
|
|
76
76
|
remains fresh. If a newer turn exists without a callback, describe the live
|
|
77
77
|
state and label the cached result stale rather than overwriting it.
|
|
78
|
-
6. Never edit `tasks.jsonl` directly during reporting.
|
|
79
|
-
|
|
80
|
-
link-pending and must be retried by that executor, while a schema 1-3 null
|
|
81
|
-
identity is a legacy recovery candidate. Manual recovery may call
|
|
82
|
-
`task resolve` only for the latter after one exact structured marker match.
|
|
83
|
-
Never persist inferred status,
|
|
78
|
+
6. Never edit `tasks.jsonl` directly during reporting. A null identity is
|
|
79
|
+
executor link-pending and must be retried by that executor. Never persist inferred status,
|
|
84
80
|
transcripts, prose classifications, or hidden reasoning. Do not poll or wait.
|
|
85
81
|
|
|
86
82
|
If the task history is empty, say that TaskChef has not recorded any tasks. If
|
package/src/cli.js
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
recordTask,
|
|
19
19
|
removeProject,
|
|
20
20
|
requireSafeId,
|
|
21
|
-
resolveTask,
|
|
22
21
|
} from "./workspace.js";
|
|
23
22
|
|
|
24
23
|
const BLANK_TABLE_CELL = Symbol("blank table cell");
|
|
@@ -241,7 +240,6 @@ async function initialize(args) {
|
|
|
241
240
|
`Configuration: ${value.config.action}`,
|
|
242
241
|
`Task log: ${value.tasks.action}`,
|
|
243
242
|
`Instructions: ${value.instructions.action}`,
|
|
244
|
-
`Legacy skill links removed: ${value.legacySkills.removed.length}`,
|
|
245
243
|
...(value.registration ? [`Codex opening: ${value.registration.status}`] : []),
|
|
246
244
|
].join("\n"));
|
|
247
245
|
return registrationFailed ? 5 : 0;
|
|
@@ -353,22 +351,6 @@ async function taskRecord(args) {
|
|
|
353
351
|
return 0;
|
|
354
352
|
}
|
|
355
353
|
|
|
356
|
-
async function taskResolve(args) {
|
|
357
|
-
if (!args[2] || args[2].startsWith("--")) throw new Error("task resolve requires a task ID");
|
|
358
|
-
validateCommandArgs(args, 3, {
|
|
359
|
-
values: ["--thread-id", "--workspace"],
|
|
360
|
-
switches: ["--json"],
|
|
361
|
-
});
|
|
362
|
-
if (!args.includes("--thread-id")) throw new Error("task resolve requires --thread-id");
|
|
363
|
-
const task = await resolveTask(
|
|
364
|
-
workspaceRoot(args),
|
|
365
|
-
args[2],
|
|
366
|
-
option(args, "--thread-id"),
|
|
367
|
-
);
|
|
368
|
-
print(task, args, (value) => `Resolved ${value.id}: ${value.threadId}`);
|
|
369
|
-
return 0;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
354
|
async function taskShow(args) {
|
|
373
355
|
validateCommandArgs(args, 3, { values: ["--workspace"], switches: ["--json"] });
|
|
374
356
|
print(await readTaskForShow(workspaceRoot(args), args[2]), args, taskDetails);
|
|
@@ -464,13 +446,11 @@ Usage:
|
|
|
464
446
|
taskchef project remove <name> [--json] [--workspace <path>]
|
|
465
447
|
taskchef dispatch prepare [--json] [--workspace <path>]
|
|
466
448
|
taskchef task record [--json] [--workspace <path>]
|
|
467
|
-
taskchef task resolve <legacy-task-id> --thread-id <thread-id> [--json] [--workspace <path>]
|
|
468
449
|
taskchef task show <task-id-or-8-character-prefix> [--json] [--workspace <path>]
|
|
469
450
|
taskchef task list [--project <name-or-path>] [--ascending] [--full-id] [--json] [--workspace <path>]
|
|
470
451
|
taskchef task summary [--json] [--workspace <path>]
|
|
471
452
|
|
|
472
453
|
Task record reads one JSON value from closed, non-interactive standard input.
|
|
473
|
-
Task resolve is a legacy migration command and rejects self-linking task records.
|
|
474
454
|
Task show accepts a full task ID or the exact 8-character ID printed by task list.
|
|
475
455
|
Task show prints human-readable details by default; --json prints the complete task object.
|
|
476
456
|
Project import reads a JSON
|
|
@@ -497,7 +477,6 @@ export async function runCli(args) {
|
|
|
497
477
|
if (args[0] === "project" && args[1] === "remove") return projectRemove(args);
|
|
498
478
|
if (args[0] === "dispatch" && args[1] === "prepare") return dispatchPrepare(args);
|
|
499
479
|
if (args[0] === "task" && args[1] === "record") return taskRecord(args);
|
|
500
|
-
if (args[0] === "task" && args[1] === "resolve") return taskResolve(args);
|
|
501
480
|
if (args[0] === "task" && args[1] === "show" && args[2]) return taskShow(args);
|
|
502
481
|
if (args[0] === "task" && args[1] === "list") return taskList(args);
|
|
503
482
|
if (args[0] === "task" && args[1] === "summary") return taskSummary(args);
|
package/src/dashboard.js
CHANGED
|
@@ -521,7 +521,7 @@ export async function createDashboardServer({
|
|
|
521
521
|
else await openWorkspaceInCodex(canonicalProjectPath);
|
|
522
522
|
sendJson(response, 202, {
|
|
523
523
|
message: task.threadId
|
|
524
|
-
? "Opened the project in Codex; this
|
|
524
|
+
? "Opened the project in Codex; this thread ID cannot use direct navigation."
|
|
525
525
|
: "Opened the project in Codex; this task does not yet have a thread ID.",
|
|
526
526
|
});
|
|
527
527
|
} catch {
|
package/src/delegation.js
CHANGED
|
@@ -8,7 +8,6 @@ const UUID_SOURCE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12
|
|
|
8
8
|
const UUID_PATTERN = new RegExp(`^${UUID_SOURCE}$`);
|
|
9
9
|
const CODEX_UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
10
10
|
const TASKCHEF_MARKER_PATTERN = new RegExp(`^<!-- taskchef_id=(${UUID_SOURCE}) -->$`);
|
|
11
|
-
const LEGACY_TASKCHEF_MARKER_PATTERN = new RegExp(`^# taskchef_id=(${UUID_SOURCE})$`);
|
|
12
11
|
|
|
13
12
|
function requireObject(value, name) {
|
|
14
13
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${name} must be an object`);
|
|
@@ -82,16 +81,13 @@ export function taskChefMarker(taskId) {
|
|
|
82
81
|
return `<!-- taskchef_id=${requireUuid(taskId)} -->`;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
export function parseTaskChefMarker(instruction
|
|
84
|
+
export function parseTaskChefMarker(instruction) {
|
|
86
85
|
if (typeof instruction !== "string") return null;
|
|
87
86
|
const firstLine = instruction.split(/\r?\n/, 1)[0];
|
|
88
87
|
const currentMatch = firstLine.match(TASKCHEF_MARKER_PATTERN);
|
|
89
|
-
if (currentMatch
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
if (!allowLegacyHeading) return null;
|
|
94
|
-
return firstLine.match(LEGACY_TASKCHEF_MARKER_PATTERN)?.[1] ?? null;
|
|
88
|
+
if (currentMatch === null) return null;
|
|
89
|
+
const prefix = instruction.match(/^([^\r\n]*)(\r?\n)\2/);
|
|
90
|
+
return prefix === null ? null : currentMatch[1];
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
export function prepareDelegation(instruction, { taskId = randomUUID() } = {}) {
|
package/src/github.js
CHANGED
|
@@ -59,11 +59,7 @@ export function canonicalGithubRepository(value, name = "githubRepos") {
|
|
|
59
59
|
export function normalizeGithubRepositories(
|
|
60
60
|
value,
|
|
61
61
|
name = "githubRepos",
|
|
62
|
-
{ allowLegacyScalar = false } = {},
|
|
63
62
|
) {
|
|
64
|
-
if (allowLegacyScalar && (value === null || typeof value === "string")) {
|
|
65
|
-
value = value === null ? [] : [value];
|
|
66
|
-
}
|
|
67
63
|
if (!Array.isArray(value)) throw new Error(`${name} must be an array of GitHub repository URLs`);
|
|
68
64
|
const repositories = [];
|
|
69
65
|
const seen = new Set();
|
package/src/mcp.js
CHANGED
|
@@ -18,22 +18,22 @@ const projectSchema = z.object({
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
const taskSchema = z.object({
|
|
21
|
-
schemaVersion: z.
|
|
21
|
+
schemaVersion: z.literal(4),
|
|
22
22
|
id: z.string(),
|
|
23
23
|
project: projectSchema,
|
|
24
24
|
title: z.string(),
|
|
25
25
|
instruction: z.string(),
|
|
26
26
|
threadId: z.string().nullable(),
|
|
27
27
|
createdAt: z.string(),
|
|
28
|
-
status: z.enum(["working", "needs_input", "completed", "failed"])
|
|
28
|
+
status: z.enum(["working", "needs_input", "completed", "failed"]),
|
|
29
29
|
summary: z.string().nullable(),
|
|
30
30
|
turnId: z.string().nullable(),
|
|
31
|
-
updatedAt: z.string()
|
|
32
|
-
updatedBy: z.enum(["dispatcher", "
|
|
31
|
+
updatedAt: z.string(),
|
|
32
|
+
updatedBy: z.enum(["dispatcher", "mcp"]),
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
const preparationSchema = z.object({
|
|
36
|
-
schemaVersion: z.
|
|
36
|
+
schemaVersion: z.literal(1),
|
|
37
37
|
workspace: z.string(),
|
|
38
38
|
taskId: z.string(),
|
|
39
39
|
preparedAt: z.string(),
|