taskchef 6.1.3 → 7.1.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/src/delegation.js CHANGED
@@ -2,13 +2,13 @@ import { randomUUID } from "node:crypto";
2
2
 
3
3
  export const EXECUTOR_OWNERSHIP_PARAGRAPH = "This task owns the delegated assignment. Execute it in this task; do not re-dispatch it merely because it concerns TaskChef or a configured project. Explicit requests to delegate separate work remain valid.";
4
4
  export const EXECUTOR_LINK_PARAGRAPH = "Before any other work, read this executor's own durable Codex thread ID from the current task's CODEX_THREAD_ID environment value and call the TaskChef link_task MCP tool with that thread ID and the marked TaskChef task ID. Never use CODEX_SESSION_ID or the parent or delegator thread ID. If linking fails, CODEX_THREAD_ID is unavailable, or the tool is unavailable, report the failure visibly and retry on a later turn; do not guess an identity or continue substantive work while the task is link-pending.";
5
- export const EXECUTOR_RESULT_PARAGRAPH = "Before ending, call the TaskChef report_result MCP tool with the marked task ID, this executor's self-linked thread ID, the current turn ID from an exact native read of that same thread, completed, needs_input, or failed, and a concise summary. Never reuse a prior turn ID after a follow-up. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.";
5
+ export const EXECUTOR_WORKING_PARAGRAPH = "After a successful initial link, and at the start of every follow-up turn before substantive work, read this exact Codex thread natively to obtain the current turn ID and call TaskChef report_state with the marked task ID, the self-linked thread ID, that current turn ID, status working, and summary omitted or null. link_task remains the first TaskChef action on the initial turn; do not report working before identity is linked. Never reuse a prior turn ID after a follow-up.";
6
+ export const EXECUTOR_RESULT_PARAGRAPH = "Before ending, read this exact Codex thread again and call TaskChef report_state for the same current working turn with status completed, needs_input, or failed and a concise summary. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.";
6
7
 
7
8
  const UUID_SOURCE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
8
9
  const UUID_PATTERN = new RegExp(`^${UUID_SOURCE}$`);
9
10
  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
11
  const TASKCHEF_MARKER_PATTERN = new RegExp(`^<!-- taskchef_id=(${UUID_SOURCE}) -->$`);
11
- const LEGACY_TASKCHEF_MARKER_PATTERN = new RegExp(`^# taskchef_id=(${UUID_SOURCE})$`);
12
12
 
13
13
  function requireObject(value, name) {
14
14
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${name} must be an object`);
@@ -82,16 +82,13 @@ export function taskChefMarker(taskId) {
82
82
  return `<!-- taskchef_id=${requireUuid(taskId)} -->`;
83
83
  }
84
84
 
85
- export function parseTaskChefMarker(instruction, { allowLegacyHeading = false } = {}) {
85
+ export function parseTaskChefMarker(instruction) {
86
86
  if (typeof instruction !== "string") return null;
87
87
  const firstLine = instruction.split(/\r?\n/, 1)[0];
88
88
  const currentMatch = firstLine.match(TASKCHEF_MARKER_PATTERN);
89
- if (currentMatch !== null) {
90
- const prefix = instruction.match(/^([^\r\n]*)(\r?\n)\2/);
91
- return prefix === null ? null : currentMatch[1];
92
- }
93
- if (!allowLegacyHeading) return null;
94
- return firstLine.match(LEGACY_TASKCHEF_MARKER_PATTERN)?.[1] ?? null;
89
+ if (currentMatch === null) return null;
90
+ const prefix = instruction.match(/^([^\r\n]*)(\r?\n)\2/);
91
+ return prefix === null ? null : currentMatch[1];
95
92
  }
96
93
 
97
94
  export function prepareDelegation(instruction, { taskId = randomUUID() } = {}) {
@@ -100,7 +97,7 @@ export function prepareDelegation(instruction, { taskId = randomUUID() } = {}) {
100
97
  const id = requireUuid(taskId);
101
98
  return {
102
99
  id,
103
- instruction: `${taskChefMarker(id)}\n\n${EXECUTOR_OWNERSHIP_PARAGRAPH}\n\n${EXECUTOR_LINK_PARAGRAPH}\n\n${EXECUTOR_RESULT_PARAGRAPH}\n\n${instruction}`,
100
+ instruction: `${taskChefMarker(id)}\n\n${EXECUTOR_OWNERSHIP_PARAGRAPH}\n\n${EXECUTOR_LINK_PARAGRAPH}\n\n${EXECUTOR_WORKING_PARAGRAPH}\n\n${EXECUTOR_RESULT_PARAGRAPH}\n\n${instruction}`,
104
101
  };
105
102
  }
106
103
 
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
@@ -4,6 +4,7 @@ import {
4
4
  prepareDispatch,
5
5
  linkTask,
6
6
  recordTask,
7
+ reportTaskState,
7
8
  reportTaskResult,
8
9
  } from "./workspace.js";
9
10
  import { parseTaskChefMarker } from "./delegation.js";
@@ -18,22 +19,28 @@ const projectSchema = z.object({
18
19
  });
19
20
 
20
21
  const taskSchema = z.object({
21
- schemaVersion: z.number(),
22
+ schemaVersion: z.union([z.literal(4), z.literal(5)]),
22
23
  id: z.string(),
23
24
  project: projectSchema,
24
25
  title: z.string(),
25
26
  instruction: z.string(),
26
27
  threadId: z.string().nullable(),
27
28
  createdAt: z.string(),
28
- status: z.enum(["working", "needs_input", "completed", "failed"]).nullable(),
29
+ status: z.enum(["working", "needs_input", "completed", "failed"]),
29
30
  summary: z.string().nullable(),
30
31
  turnId: z.string().nullable(),
31
- updatedAt: z.string().nullable(),
32
- updatedBy: z.enum(["dispatcher", "hook", "mcp"]).nullable(),
32
+ updatedAt: z.string(),
33
+ updatedBy: z.enum(["dispatcher", "mcp"]),
34
+ lastResult: z.object({
35
+ status: z.enum(["needs_input", "completed", "failed"]),
36
+ summary: z.string(),
37
+ turnId: z.string().nullable(),
38
+ updatedAt: z.string(),
39
+ }).nullable(),
33
40
  });
34
41
 
35
42
  const preparationSchema = z.object({
36
- schemaVersion: z.number(),
43
+ schemaVersion: z.literal(1),
37
44
  workspace: z.string(),
38
45
  taskId: z.string(),
39
46
  preparedAt: z.string(),
@@ -54,13 +61,14 @@ export function createTaskChefMcpServer({
54
61
  prepare = prepareDispatch,
55
62
  record = recordTask,
56
63
  reportResult = reportTaskResult,
64
+ reportState = reportTaskState,
57
65
  link = linkTask,
58
66
  } = {}) {
59
67
  const server = new McpServer(
60
68
  { name: "taskchef", version: "1.0.0" },
61
69
  {
62
70
  instructions:
63
- "Prepare with prepare_dispatch, call record_task before creating the Codex task, then create it natively and return immediately. The executor must call link_task before other work and report_result before ending.",
71
+ "Prepare with prepare_dispatch, call record_task before creating the Codex task, then create it natively and return immediately. The executor must call link_task first, report_state working at the start of each execution turn, and report_state with a semantic outcome before ending.",
64
72
  },
65
73
  );
66
74
 
@@ -136,12 +144,38 @@ export function createTaskChefMcpServer({
136
144
  },
137
145
  );
138
146
 
147
+ server.registerTool(
148
+ "report_state",
149
+ {
150
+ title: "Report TaskChef state",
151
+ description:
152
+ "Report this self-linked executor turn's lifecycle state. Use working before substantive work in a newly linked or follow-up turn, with summary omitted or null. Before ending the same turn, report needs_input, completed, or failed with a concise semantic summary. Exact retries are idempotent; stale or mismatched turns are rejected.",
153
+ inputSchema: {
154
+ taskId: z.string().min(1),
155
+ threadId: z.string().min(1).nullable(),
156
+ turnId: z.string().min(1).max(256).nullable(),
157
+ status: z.enum(["working", "needs_input", "completed", "failed"]),
158
+ summary: z.string().min(1).max(2_000).nullable().optional(),
159
+ },
160
+ outputSchema: { task: taskSchema },
161
+ annotations: {
162
+ readOnlyHint: false,
163
+ destructiveHint: true,
164
+ openWorldHint: false,
165
+ },
166
+ },
167
+ async (input) => {
168
+ const task = await reportState(workspace, input);
169
+ return toolResult("task", task, `Recorded ${task.status} state for TaskChef task ${task.id}.`);
170
+ },
171
+ );
172
+
139
173
  server.registerTool(
140
174
  "report_result",
141
175
  {
142
- title: "Report TaskChef result",
176
+ title: "Report TaskChef result (deprecated)",
143
177
  description:
144
- "Store the executor's latest semantic outcome for one recorded TaskChef task. A linked executor must supply its matching durable thread ID and current turn ID. Null IDs are accepted only for a failed executor creation before a thread exists. Use needs_input only for a semantic user decision, not a transient native approval prompt. Summaries must omit secrets, transcripts, and raw command output.",
178
+ "Deprecated compatibility alias for semantic results. New executors must use report_state working at turn start and report_state again with needs_input, completed, or failed before ending. This alias preserves legacy callers by implicitly starting the supplied newer turn before storing its result.",
145
179
  inputSchema: {
146
180
  taskId: z.string().min(1),
147
181
  threadId: z.string().min(1).nullable(),