@tea-agent/loop-agent 0.26.5-beta.2 → 0.27.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.
Files changed (71) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +29 -0
  3. package/README.md +13 -23
  4. package/dist/application/dag/args.js +7 -7
  5. package/dist/application/dag/generate-task-dag.js +2 -2
  6. package/dist/application/dag/run-dag.js +3 -4
  7. package/dist/application/dag/validate-dag.js +1 -1
  8. package/dist/application/task-lifecycle/advance.js +845 -0
  9. package/dist/application/task-lifecycle/gates.js +80 -0
  10. package/dist/application/task-lifecycle/index.js +7 -0
  11. package/dist/application/task-lifecycle/observe.js +395 -0
  12. package/dist/application/task-lifecycle/plan-transitions.js +224 -0
  13. package/dist/application/task-lifecycle/recommendations.js +180 -0
  14. package/dist/application/task-lifecycle/record.js +73 -0
  15. package/dist/application/task-lifecycle/types.js +1 -0
  16. package/dist/cli/command-definitions.js +14 -111
  17. package/dist/cli/help.js +6 -7
  18. package/dist/cli/program.js +26 -90
  19. package/dist/cli/update/policy.js +0 -1
  20. package/dist/commands/dag-final-verification.js +1 -1
  21. package/dist/commands/dag-init-hybrid.js +1 -1
  22. package/dist/commands/delegate.js +2 -2
  23. package/dist/commands/init.js +21 -19
  24. package/dist/commands/run-dag-progress.js +1 -1
  25. package/dist/commands/status.js +18 -17
  26. package/dist/commands/study-init.js +2 -2
  27. package/dist/commands/task-advance.js +335 -0
  28. package/dist/commands/task-contract.js +3 -5
  29. package/dist/commands/task-source-prepare.js +9 -4
  30. package/dist/commands/task-status.js +133 -0
  31. package/dist/governance/manifest-types.js +2 -2
  32. package/dist/shared/operator/capabilities.js +1358 -243
  33. package/dist/task/contract/adopt.js +1 -1
  34. package/dist/task/contract/apply.js +1 -1
  35. package/dist/task/contract/import-revision.js +1 -1
  36. package/dist/task/contract/recover.js +2 -2
  37. package/dist/task/read-model.js +18 -23
  38. package/dist/task/runtime.js +1 -1
  39. package/dist/task/source-prepare/completeness.js +1 -1
  40. package/dist/task/source-prepare/parse-intent.js +6 -1
  41. package/dist/task/source-prepare/prepare.js +23 -20
  42. package/dist/worker/console/operator-actions.js +288 -95
  43. package/dist/worker/console/recovery-cta.js +4 -4
  44. package/dist/worker/console/static/assets/{index-CSRIhuzh.js → index-CNO7n6qB.js} +1 -1
  45. package/dist/worker/console/static/index.html +1 -1
  46. package/dist/worker/materialize/harness-task-materializer.js +6 -5
  47. package/dist/worker/run-task/run-task.js +204 -112
  48. package/dist/worker/runner/run-ready.js +1 -1
  49. package/dist/workflows/dag/frontend-implementation-contract.js +6 -72
  50. package/dist/workflows/dag/frontend-prewrite-gate.js +4 -17
  51. package/dist/workflows/dag/init-hybrid.js +11 -66
  52. package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
  53. package/docs/templates/evaluation/agents-map-verbose-v0.md +3 -3
  54. package/docs/templates/harness.schema.json +2 -2
  55. package/docs/templates/init-managed-agents.md +13 -16
  56. package/docs/templates/production-readiness-checklist.md +3 -3
  57. package/harness.json +4 -4
  58. package/package.json +1 -1
  59. package/scripts/kb-bootstrap-init-skeleton.sh +2 -1
  60. package/scripts/kb-graph-incremental-prepare.mjs +2 -2
  61. package/skills/loop-agent/SKILL.md +18 -13
  62. package/skills/loop-agent/references/README.md +1 -1
  63. package/skills/loop-agent/references/command-reference.md +55 -84
  64. package/skills/loop-agent/references/harness-policy.md +19 -23
  65. package/skills/loop-agent/references/hybrid-dag.md +31 -33
  66. package/skills/loop-agent/references/long-running-loop.md +2 -2
  67. package/skills/loop-agent/references/one-shot-runs.md +4 -5
  68. package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
  69. package/skills/loop-agent/references/post-implementation-and-patterns.md +4 -4
  70. package/skills/loop-agent/references/source-and-plan-practice.md +45 -51
  71. package/skills/loop-agent/references/task-workflow.md +14 -15
@@ -0,0 +1,335 @@
1
+ import { advanceTaskLifecycle, } from "../application/task-lifecycle/index.js";
2
+ import { buildOperatorResult, operatorFailed, processExitCodeForOutcome, writeOperatorJson, } from "../shared/operator/index.js";
3
+ const COMMAND = "task advance";
4
+ const USAGE = `usage:
5
+ task advance <task-id> [title]
6
+ [--prd <path>]...
7
+ [--role <role>]...
8
+ [--from-text <string>]
9
+ [--from-draft <TaskContractDraftV1.json>]
10
+ [--task-kind <kind>]
11
+ [--feature-id <id>]
12
+ [--allowed-path <glob>]...
13
+ [--forbidden-path <glob>]...
14
+ [--verify <label:command>]...
15
+ [--verify-timeout <label:timeoutMs>]...
16
+ [--accept-recommendations <digest>]
17
+ [--approve-gate <gate-id:digest>]
18
+ [--reject-gate <gate-id:digest> --reason <text>]
19
+ [--profile auto|minimal|standard|reviewed|supervised]
20
+ [--max-concurrent <n>]
21
+ [--run-id <id>]
22
+ [--events-jsonl <path>]
23
+ [--worker-association <json>]
24
+ [--dag-output <path>]
25
+ [--skip-finalize]
26
+ [--no-strict-models]
27
+ [--dry-run]
28
+ [--json]`;
29
+ function pushList(target, value) {
30
+ const list = target ?? [];
31
+ list.push(value);
32
+ return list;
33
+ }
34
+ function parseVerify(flag) {
35
+ const idx = flag.indexOf(":");
36
+ if (idx <= 0 || idx === flag.length - 1) {
37
+ throw new Error(`invalid --verify ${flag}; expected label:command`);
38
+ }
39
+ return { label: flag.slice(0, idx), command: flag.slice(idx + 1) };
40
+ }
41
+ function parseVerifyTimeout(flag) {
42
+ const idx = flag.indexOf(":");
43
+ if (idx <= 0 || idx === flag.length - 1) {
44
+ throw new Error(`invalid --verify-timeout ${flag}; expected label:timeoutMs`);
45
+ }
46
+ const timeoutMs = Number(flag.slice(idx + 1));
47
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
48
+ throw new Error(`invalid --verify-timeout ${flag}; timeoutMs must be > 0`);
49
+ }
50
+ return { label: flag.slice(0, idx), timeoutMs };
51
+ }
52
+ export function parseTaskAdvanceArgs(args) {
53
+ const options = {};
54
+ const positional = [];
55
+ for (let i = 0; i < args.length; i += 1) {
56
+ const token = args[i];
57
+ const next = () => {
58
+ const value = args[i + 1];
59
+ if (value === undefined || value.startsWith("-")) {
60
+ throw new Error(`missing value for ${token}\n${USAGE}`);
61
+ }
62
+ i += 1;
63
+ return value;
64
+ };
65
+ if (token === "--json") {
66
+ options.json = true;
67
+ continue;
68
+ }
69
+ if (token === "--dry-run") {
70
+ options.dryRun = true;
71
+ continue;
72
+ }
73
+ if (token === "--prd") {
74
+ options.prdPaths = pushList(options.prdPaths, next());
75
+ continue;
76
+ }
77
+ if (token === "--role") {
78
+ options.roles = pushList(options.roles, next());
79
+ continue;
80
+ }
81
+ if (token === "--from-text") {
82
+ options.fromText = next();
83
+ continue;
84
+ }
85
+ if (token === "--from-draft") {
86
+ options.fromDraft = next();
87
+ continue;
88
+ }
89
+ if (token === "--task-kind") {
90
+ options.taskKind = next();
91
+ continue;
92
+ }
93
+ if (token === "--feature-id") {
94
+ options.featureId = next();
95
+ continue;
96
+ }
97
+ if (token === "--allowed-path") {
98
+ options.allowedPaths = pushList(options.allowedPaths, next());
99
+ continue;
100
+ }
101
+ if (token === "--forbidden-path") {
102
+ options.forbiddenPaths = pushList(options.forbiddenPaths, next());
103
+ continue;
104
+ }
105
+ if (token === "--verify") {
106
+ options.verify = pushList(options.verify, next());
107
+ continue;
108
+ }
109
+ if (token === "--verify-timeout") {
110
+ options.verifyTimeout = pushList(options.verifyTimeout, next());
111
+ continue;
112
+ }
113
+ if (token === "--accept-recommendations") {
114
+ options.acceptRecommendations = next();
115
+ continue;
116
+ }
117
+ if (token === "--approve-gate") {
118
+ options.approveGate = next();
119
+ continue;
120
+ }
121
+ if (token === "--reject-gate") {
122
+ options.rejectGate = next();
123
+ continue;
124
+ }
125
+ if (token === "--reason") {
126
+ options.reason = next();
127
+ continue;
128
+ }
129
+ if (token === "--profile") {
130
+ const value = next();
131
+ if (value !== "auto" &&
132
+ value !== "minimal" &&
133
+ value !== "standard" &&
134
+ value !== "reviewed" &&
135
+ value !== "supervised") {
136
+ throw new Error(`--profile must be auto|minimal|standard|reviewed|supervised\n${USAGE}`);
137
+ }
138
+ options.profile = value;
139
+ continue;
140
+ }
141
+ if (token === "--max-concurrent") {
142
+ const value = Number(next());
143
+ if (!Number.isFinite(value) || value <= 0) {
144
+ throw new Error(`--max-concurrent must be a positive number\n${USAGE}`);
145
+ }
146
+ options.maxConcurrent = value;
147
+ continue;
148
+ }
149
+ if (token === "--run-id") {
150
+ options.runId = next();
151
+ continue;
152
+ }
153
+ if (token === "--events-jsonl") {
154
+ options.eventsJsonlPath = next();
155
+ continue;
156
+ }
157
+ if (token === "--worker-association") {
158
+ options.workerAssociation = next();
159
+ continue;
160
+ }
161
+ if (token === "--dag-output") {
162
+ options.dagOutputPath = next();
163
+ continue;
164
+ }
165
+ if (token === "--skip-finalize") {
166
+ options.skipFinalize = true;
167
+ continue;
168
+ }
169
+ if (token === "--no-strict-models") {
170
+ options.strictModels = false;
171
+ continue;
172
+ }
173
+ if (token === "--help" || token === "-h") {
174
+ throw new Error(USAGE);
175
+ }
176
+ if (token.startsWith("-")) {
177
+ throw new Error(`unknown flag ${token}\n${USAGE}`);
178
+ }
179
+ positional.push(token);
180
+ }
181
+ const taskId = positional[0];
182
+ if (!taskId)
183
+ throw new Error(USAGE);
184
+ if (positional.length > 1) {
185
+ options.title = positional.slice(1).join(" ");
186
+ }
187
+ return { taskId, options };
188
+ }
189
+ function mapOutcome(result) {
190
+ if (result.blockers.length > 0) {
191
+ const code = result.blockers[0]?.code ?? "";
192
+ if (code === "STALE_GATE" || code.includes("CONFLICT"))
193
+ return "conflict";
194
+ if (code === "INTAKE_REQUIRED" || code === "INVALID_DRAFT")
195
+ return "invalid";
196
+ if (code === "NOT_FOUND" || code === "TASK_NOT_FOUND")
197
+ return "not-found";
198
+ return "blocked";
199
+ }
200
+ if (result.lifecycleState === "awaiting-write-set-approval") {
201
+ // Shared operator outcomes do not include awaiting-approval; use blocked
202
+ // with result.gate so machine clients can branch on lifecycleState/gate.
203
+ return "blocked";
204
+ }
205
+ if (result.lifecycleState === "needs-attention")
206
+ return "needs-reconcile";
207
+ if (result.lifecycleState === "run-failed")
208
+ return "blocked";
209
+ return "succeeded";
210
+ }
211
+ function toUseCaseInput(repoRoot, taskId, options) {
212
+ const timeouts = new Map((options.verifyTimeout ?? []).map((entry) => {
213
+ const parsed = parseVerifyTimeout(entry);
214
+ return [parsed.label, parsed.timeoutMs];
215
+ }));
216
+ const verifyCommands = (options.verify ?? []).map((entry) => {
217
+ const parsed = parseVerify(entry);
218
+ const timeoutMs = timeouts.get(parsed.label);
219
+ return timeoutMs
220
+ ? { ...parsed, timeoutMs }
221
+ : { label: parsed.label, command: parsed.command };
222
+ });
223
+ const roles = options.roles ?? [];
224
+ const prdPaths = (options.prdPaths ?? []).map((filePath, index) => ({
225
+ filePath,
226
+ role: roles[index],
227
+ }));
228
+ let workerAssociation;
229
+ if (options.workerAssociation) {
230
+ let parsed;
231
+ try {
232
+ parsed = JSON.parse(options.workerAssociation);
233
+ }
234
+ catch {
235
+ throw new Error("--worker-association must be valid JSON {schemaVersion:1,workerRunId,featureId,taskId}");
236
+ }
237
+ if (!parsed ||
238
+ typeof parsed !== "object" ||
239
+ parsed.schemaVersion !== 1 ||
240
+ typeof parsed.workerRunId !== "string" ||
241
+ typeof parsed.featureId !== "string" ||
242
+ typeof parsed.taskId !== "string") {
243
+ throw new Error("--worker-association must be JSON {schemaVersion:1,workerRunId,featureId,taskId}");
244
+ }
245
+ workerAssociation = parsed;
246
+ }
247
+ return {
248
+ repoRoot,
249
+ taskId,
250
+ title: options.title,
251
+ prdPaths: prdPaths.length > 0 ? prdPaths : undefined,
252
+ fromText: options.fromText,
253
+ fromDraftPath: options.fromDraft,
254
+ taskKind: options.taskKind,
255
+ featureId: options.featureId,
256
+ allowedPaths: options.allowedPaths,
257
+ forbiddenPaths: options.forbiddenPaths,
258
+ verifyCommands: verifyCommands.length > 0 ? verifyCommands : undefined,
259
+ approveGateToken: options.approveGate,
260
+ rejectGateToken: options.rejectGate,
261
+ rejectReason: options.reason,
262
+ acceptRecommendationsDigest: options.acceptRecommendations,
263
+ dryRun: options.dryRun,
264
+ profile: options.profile,
265
+ profileExplicit: Boolean(options.profile),
266
+ maxConcurrent: options.maxConcurrent,
267
+ runId: options.runId,
268
+ eventsJsonlPath: options.eventsJsonlPath,
269
+ workerAssociation,
270
+ dagOutputPath: options.dagOutputPath,
271
+ skipFinalize: options.skipFinalize,
272
+ strictModels: options.strictModels,
273
+ onProgress: options.json
274
+ ? undefined
275
+ : (message) => {
276
+ process.stderr.write(`[task advance] ${message}\n`);
277
+ },
278
+ };
279
+ }
280
+ export async function runTaskAdvance(repoRoot, args) {
281
+ let taskId;
282
+ let options;
283
+ try {
284
+ ({ taskId, options } = parseTaskAdvanceArgs(args));
285
+ }
286
+ catch (error) {
287
+ const message = error instanceof Error ? error.message : String(error);
288
+ const envelope = operatorFailed({
289
+ command: COMMAND,
290
+ outcome: "invalid",
291
+ code: "INVALID_INPUT",
292
+ message,
293
+ });
294
+ writeOperatorJson(envelope);
295
+ process.exitCode = processExitCodeForOutcome(envelope.outcome);
296
+ return;
297
+ }
298
+ try {
299
+ const result = await advanceTaskLifecycle(toUseCaseInput(repoRoot, taskId, options));
300
+ const outcome = mapOutcome(result);
301
+ const gateStop = result.lifecycleState === "awaiting-write-set-approval" &&
302
+ result.blockers.length === 0;
303
+ const createOnlyStop = result.lifecycleState === "intake-incomplete" &&
304
+ result.blockers.length === 0 &&
305
+ (result.completedTransitions.includes("task-created") ||
306
+ result.completedTransitions.includes("prd-imported"));
307
+ const ok = outcome === "succeeded" || gateStop || createOnlyStop;
308
+ const envelope = buildOperatorResult({
309
+ command: COMMAND,
310
+ ok,
311
+ // awaiting write-set approval is a successful stop condition for
312
+ // the first advance call; keep outcome blocked for machine branch
313
+ // but ok=true when no blockers.
314
+ outcome: gateStop ? "blocked" : createOnlyStop ? "succeeded" : outcome,
315
+ result,
316
+ warnings: result.warnings,
317
+ });
318
+ writeOperatorJson(envelope);
319
+ process.exitCode =
320
+ envelope.ok && envelope.outcome === "blocked"
321
+ ? 0
322
+ : processExitCodeForOutcome(envelope.outcome);
323
+ }
324
+ catch (error) {
325
+ const message = error instanceof Error ? error.message : String(error);
326
+ const envelope = operatorFailed({
327
+ command: COMMAND,
328
+ outcome: "blocked",
329
+ code: "INTERNAL_ERROR",
330
+ message,
331
+ });
332
+ writeOperatorJson(envelope);
333
+ process.exitCode = processExitCodeForOutcome(envelope.outcome);
334
+ }
335
+ }
@@ -1,13 +1,11 @@
1
1
  import { ContractMutationError, adoptTaskContract, applyTaskContract, diffTaskContract, doctorTaskContract, loadDraftFromPath, observeTaskContractState, recoverTaskContract, validateDraftForTask, } from "../task/contract/index.js";
2
2
  import { operatorFailed, operatorSucceeded, processExitCodeForOutcome, writeOperatorJson, } from "../shared/operator/index.js";
3
3
  const USAGE = `usage:
4
- task contract show <taskId> [--json]
5
4
  task contract validate --task <id> --input <path> [--json]
6
5
  task contract diff --task <id> --input <path> [--json]
7
- task contract apply --task <id> --input <path> --expected-revision <n> --expected-observed-hash <sha256> --request-id <id> --request-payload-sha256 <sha256> [--json]
8
- task contract adopt --task <id> --expected-revision <n> --expected-observed-hash <sha256> --request-id <id> --request-payload-sha256 <sha256> [--json]
9
- task contract doctor --task <id> [--json]
10
- task contract recover --task <id> [--tx-id <id>] [--json]`;
6
+
7
+ Advanced-only public leaves. Managed apply/show/adopt/doctor/recover are internal services
8
+ reached via task advance / task status (or application APIs), not public CLI subcommands.`;
11
9
  export function parseTaskContractArgs(args) {
12
10
  const options = {};
13
11
  const positional = [];
@@ -205,13 +205,13 @@ function validateOptionCombinations(options) {
205
205
  throw new Error(`need at least one intent source: --use-imported-prd, --from-text/--from-file/--from-stdin, --from-draft, or sufficient flags (title/objective + --ac + --allowed-path)\n${USAGE}`);
206
206
  }
207
207
  }
208
- async function readStdinText() {
209
- if (stdinStream.isTTY) {
208
+ export async function readTaskSourcePrepareStdin(stream = stdinStream) {
209
+ if (stream.isTTY) {
210
210
  throw new Error("--from-stdin refused on TTY; pipe input or use --from-file/--from-text");
211
211
  }
212
212
  const chunks = [];
213
213
  let total = 0;
214
- for await (const chunk of stdinStream) {
214
+ for await (const chunk of stream) {
215
215
  const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
216
216
  total += buf.byteLength;
217
217
  if (total > PREPARE_MAX_FILE_BYTES) {
@@ -239,7 +239,7 @@ async function loadTextInput(repoRoot, options) {
239
239
  return { text: raw.toString("utf-8"), label: options.fromFile };
240
240
  }
241
241
  if (options.fromStdin) {
242
- const text = await readStdinText();
242
+ const text = await readTaskSourcePrepareStdin();
243
243
  return { text, label: "stdin" };
244
244
  }
245
245
  return {};
@@ -386,6 +386,11 @@ function printHuman(result) {
386
386
  console.log(lines.join("\n"));
387
387
  }
388
388
  export async function runTaskSourcePrepare(repoRoot, args) {
389
+ if (args.includes("--help") || args.includes("-h")) {
390
+ console.log(USAGE);
391
+ process.exitCode = 0;
392
+ return;
393
+ }
389
394
  let taskId;
390
395
  let options = {};
391
396
  try {
@@ -0,0 +1,133 @@
1
+ import { observeTaskLifecycle } from "../application/task-lifecycle/index.js";
2
+ import { buildOperatorResult, operatorFailed, processExitCodeForOutcome, writeOperatorJson, } from "../shared/operator/index.js";
3
+ const COMMAND = "task status";
4
+ const USAGE = `usage:
5
+ task status <task-id> [--json|--markdown] [--details]`;
6
+ export function parseTaskStatusArgs(args) {
7
+ const options = {};
8
+ const positional = [];
9
+ for (const token of args) {
10
+ if (token === "--json") {
11
+ options.json = true;
12
+ continue;
13
+ }
14
+ if (token === "--markdown") {
15
+ options.markdown = true;
16
+ continue;
17
+ }
18
+ if (token === "--details") {
19
+ options.details = true;
20
+ continue;
21
+ }
22
+ if (token === "--help" || token === "-h") {
23
+ throw new Error(USAGE);
24
+ }
25
+ if (token.startsWith("-")) {
26
+ throw new Error(`unknown flag ${token}\n${USAGE}`);
27
+ }
28
+ positional.push(token);
29
+ }
30
+ const taskId = positional[0];
31
+ if (!taskId || positional.length > 1)
32
+ throw new Error(USAGE);
33
+ return { taskId, options };
34
+ }
35
+ function formatMarkdown(snapshot, details) {
36
+ const lines = [
37
+ `# Task ${snapshot.taskId}`,
38
+ "",
39
+ `- lifecycleState: \`${snapshot.lifecycleState}\``,
40
+ `- exists: ${snapshot.exists}`,
41
+ ];
42
+ if (snapshot.title)
43
+ lines.push(`- title: ${snapshot.title}`);
44
+ if (snapshot.contract) {
45
+ lines.push(`- contract: ${snapshot.contract.effectiveStatus} rev=${snapshot.contract.revision ?? "n/a"}`);
46
+ }
47
+ if (snapshot.dagDraft) {
48
+ lines.push(`- dagDraft: exists=${snapshot.dagDraft.exists} strictValidated=${snapshot.dagDraft.strictValidated}`);
49
+ if (details && snapshot.dagDraft.writeSet.length > 0) {
50
+ lines.push(`- writeSet: ${snapshot.dagDraft.writeSet.join(", ")}`);
51
+ }
52
+ }
53
+ if (snapshot.gate) {
54
+ lines.push(`- gate: ${snapshot.gate.id} digest=${snapshot.gate.digest}`);
55
+ lines.push(`- approve: \`${snapshot.gate.approveCommand}\``);
56
+ }
57
+ if (snapshot.activeRun) {
58
+ lines.push(`- activeRun: ${snapshot.activeRun.runId} (${snapshot.activeRun.lifecycle}/${snapshot.activeRun.status ?? "?"})`);
59
+ }
60
+ if (snapshot.latestRun) {
61
+ lines.push(`- latestRun: ${snapshot.latestRun.runId} (${snapshot.latestRun.lifecycle}/${snapshot.latestRun.status ?? "?"})`);
62
+ }
63
+ if (snapshot.next.kind === "none") {
64
+ lines.push("- next: none");
65
+ }
66
+ else if (snapshot.next.kind === "external-action") {
67
+ lines.push(`- next: ${snapshot.next.description}`);
68
+ }
69
+ else {
70
+ lines.push(`- next: \`${snapshot.next.command}\``);
71
+ }
72
+ if (details && snapshot.completedTransitions.length > 0) {
73
+ lines.push("", "## completedTransitions", "");
74
+ for (const transition of snapshot.completedTransitions) {
75
+ lines.push(`- ${transition}`);
76
+ }
77
+ }
78
+ if (snapshot.blockers.length > 0) {
79
+ lines.push("", "## blockers", "");
80
+ for (const blocker of snapshot.blockers) {
81
+ lines.push(`- ${blocker.code}: ${blocker.message}`);
82
+ }
83
+ }
84
+ return `${lines.join("\n")}\n`;
85
+ }
86
+ export async function runTaskStatus(repoRoot, args) {
87
+ let taskId;
88
+ let options;
89
+ try {
90
+ ({ taskId, options } = parseTaskStatusArgs(args));
91
+ }
92
+ catch (error) {
93
+ const message = error instanceof Error ? error.message : String(error);
94
+ const envelope = operatorFailed({
95
+ command: COMMAND,
96
+ outcome: "invalid",
97
+ code: "INVALID_INPUT",
98
+ message,
99
+ });
100
+ writeOperatorJson(envelope);
101
+ process.exitCode = processExitCodeForOutcome(envelope.outcome);
102
+ return;
103
+ }
104
+ try {
105
+ const snapshot = await observeTaskLifecycle(repoRoot, taskId);
106
+ if (options.markdown && !options.json) {
107
+ process.stdout.write(formatMarkdown(snapshot, Boolean(options.details)));
108
+ process.exitCode = snapshot.exists ? 0 : 2;
109
+ return;
110
+ }
111
+ const envelope = buildOperatorResult({
112
+ command: COMMAND,
113
+ ok: true,
114
+ outcome: snapshot.exists ? "succeeded" : "not-found",
115
+ result: snapshot,
116
+ });
117
+ // status is read-only; missing task is still a valid observation.
118
+ envelope.ok = true;
119
+ writeOperatorJson(envelope);
120
+ process.exitCode = snapshot.exists ? 0 : 2;
121
+ }
122
+ catch (error) {
123
+ const message = error instanceof Error ? error.message : String(error);
124
+ const envelope = operatorFailed({
125
+ command: COMMAND,
126
+ outcome: "blocked",
127
+ code: "INTERNAL_ERROR",
128
+ message,
129
+ });
130
+ writeOperatorJson(envelope);
131
+ process.exitCode = processExitCodeForOutcome(envelope.outcome);
132
+ }
133
+ }
@@ -90,9 +90,9 @@ export const workflowPolicySchema = z
90
90
  .optional()
91
91
  .default("zh-CN"),
92
92
  defaultEntry: z
93
- .enum(["dag run-task", "run-dag"])
93
+ .enum(["task advance", "dag execute"])
94
94
  .optional()
95
- .default("dag run-task"),
95
+ .default("task advance"),
96
96
  profileRouting: z
97
97
  .object({
98
98
  minimal: workflowPolicyDagTemplateSchema