@tea-agent/loop-agent 0.37.0 → 0.37.1-beta.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 (192) hide show
  1. package/AGENTS.md +2 -6
  2. package/CHANGELOG.md +71 -144
  3. package/README.md +1 -1
  4. package/bin/loop-agent.js +1 -37
  5. package/dist/application/task-lifecycle/advance.js +0 -1
  6. package/dist/application/task-lifecycle/observe.js +0 -15
  7. package/dist/application/task-lifecycle/plan-transitions.js +0 -15
  8. package/dist/cli/command-definitions.js +0 -7
  9. package/dist/cli/program.js +3 -8
  10. package/dist/commands/init-upgrade.js +19 -351
  11. package/dist/commands/init.js +67 -14
  12. package/dist/commands/run-dag-progress.js +0 -14
  13. package/dist/commands/task-advance.js +3 -33
  14. package/dist/executors/dag-pi-executor.js +30 -47
  15. package/dist/executors/pi-sdk-executor.js +0 -34
  16. package/dist/executors/shell-executor.js +95 -6
  17. package/dist/shared/operator/capabilities.js +13 -200
  18. package/dist/shared/package-metadata.js +0 -42
  19. package/dist/task/contract/project.js +0 -4
  20. package/dist/task/source-prepare/completeness.js +0 -53
  21. package/dist/task/source-prepare/parse-intent.js +12 -82
  22. package/dist/task/source-prepare/prepare.js +5 -102
  23. package/dist/task/source-prepare/semantic-intake.js +23 -144
  24. package/dist/worker/console/chat/artifact-card.js +1 -8
  25. package/dist/worker/console/chat/chat-event-store.js +0 -152
  26. package/dist/worker/console/chat/human-gate-card.js +1 -9
  27. package/dist/worker/console/chat/operation-card.js +2 -71
  28. package/dist/worker/console/chat/pi-runtime.js +62 -111
  29. package/dist/worker/console/chat/routes.js +10 -43
  30. package/dist/worker/console/chat/session-store.js +1 -9
  31. package/dist/worker/console/chat/shortcuts.js +7 -9
  32. package/dist/worker/console/chat/turn-process.js +23 -204
  33. package/dist/worker/console/chat/workspace-landing.js +1 -2
  34. package/dist/worker/console/operation-runner.js +6 -155
  35. package/dist/worker/console/operator-actions.js +14 -391
  36. package/dist/worker/console/operator-user-error.js +0 -4
  37. package/dist/worker/console/recovery-cta.js +3 -50
  38. package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
  39. package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
  40. package/dist/worker/console/static/index.html +2 -2
  41. package/dist/worker/console/static-src/app/console-types.js +9 -13
  42. package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
  43. package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
  44. package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
  45. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
  46. package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
  47. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  48. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
  49. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
  50. package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
  51. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
  52. package/dist/worker/loop-agent/loop-agent-client.js +3 -17
  53. package/dist/worker/observability/read-model.js +0 -20
  54. package/dist/worker/observe/spec-evidence.js +8 -3
  55. package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
  56. package/dist/worker/observe/static/operator-chrome.js +3 -10
  57. package/dist/worker/observe/static/views/dag-inspector.js +71 -6
  58. package/dist/worker/observe/static/views/dag.js +0 -12
  59. package/dist/worker/preflight.js +1 -2
  60. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
  61. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  62. package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
  63. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  64. package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
  65. package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
  66. package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
  67. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  68. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
  69. package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
  70. package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
  71. package/dist/workflows/dag/frontend-repair.js +18 -219
  72. package/dist/workflows/dag/frontend-verification-trace.js +32 -47
  73. package/dist/workflows/dag/init-hybrid.js +82 -165
  74. package/dist/workflows/dag/node-execution.js +0 -89
  75. package/dist/workflows/dag/recovery-recommendation.js +0 -58
  76. package/dist/workflows/dag/runner.js +15 -333
  77. package/dist/workflows/dag/scheduler.js +3 -257
  78. package/dist/workflows/dag/types.js +9 -130
  79. package/docs/README.md +3 -3
  80. package/docs/architecture/evolution.md +67 -102
  81. package/docs/templates/backend-test-dag.json +50 -32
  82. package/docs/templates/init-managed-agents.md +2 -5
  83. package/harness.json +2 -2
  84. package/package.json +3 -9
  85. package/skills/loop-agent/SKILL.md +0 -1
  86. package/skills/loop-agent/references/command-reference.md +0 -4
  87. package/dist/build-stamp.json +0 -6
  88. package/dist/commands/dag-request-interrupt.js +0 -20
  89. package/dist/worker/console/chat/assistant-content.js +0 -121
  90. package/dist/worker/console/chat/semantic-activity.js +0 -477
  91. package/dist/worker/console/operation-run-facts.js +0 -190
  92. package/dist/worker/console/operation-wait.js +0 -355
  93. package/dist/worker/console/recovery-error-copy.js +0 -198
  94. package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
  95. package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
  96. package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
  97. package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
  98. package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
  99. package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
  100. package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
  101. package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
  102. package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
  103. package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
  104. package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
  105. package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
  106. package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
  107. package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
  108. package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
  109. package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
  110. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
  112. package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
  113. package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
  114. package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
  115. package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
  116. package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
  117. package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
  118. package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
  119. package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
  120. package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
  121. package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
  122. package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
  123. package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
  124. package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
  125. package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
  126. package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
  127. package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
  128. package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
  129. package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
  130. package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
  131. package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
  132. package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
  133. package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
  134. package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
  135. package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
  136. package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
  137. package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
  138. package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
  139. package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
  140. package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
  141. package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
  142. package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
  143. package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
  144. package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
  145. package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
  146. package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
  147. package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
  148. package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
  149. package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
  150. package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
  151. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
  152. package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
  153. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
  154. package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
  155. package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
  156. package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
  157. package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
  158. package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  159. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  160. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  161. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  162. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  163. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  164. package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  165. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  166. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  167. package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  168. package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  169. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  170. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  171. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  172. package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  173. package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  174. package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  175. package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  176. package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  177. package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  178. package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
  179. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  180. package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
  181. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
  182. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
  183. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
  184. package/dist/worker/observability/interrupt-eligibility.js +0 -264
  185. package/dist/workflows/dag/contract-output-registry.js +0 -14
  186. package/dist/workflows/dag/contract-validator-registrations.js +0 -8
  187. package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
  188. package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
  189. package/dist/workflows/dag/frontend-recovery-run.js +0 -539
  190. package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
  191. package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
  192. package/dist/workflows/dag/interrupt-request.js +0 -559
@@ -1,7 +1,7 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
2
  import { buildOperatorCapabilitiesDocument } from "../../shared/operator/capabilities.js";
3
3
  import { buildOperatorResult, operatorFailed, operatorSucceeded, } from "../../shared/operator/envelope.js";
4
- import { CANONICAL_TASK_ID_PATTERN, formatLocalBusinessDate, getTaskPaths, loadTaskConfig, } from "../../task/runtime.js";
4
+ import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
5
5
  import { diffTaskContract, validateDraftForTask, } from "../../task/contract/index.js";
6
6
  import { stageUtf8Text } from "./app-data.js";
7
7
  import { ALL_CONFIRMATION_CHALLENGES, hashDagBytes, } from "./dag-confirmation.js";
@@ -18,23 +18,18 @@ import { normalizeConsoleWorkflowKind } from "./workflow-kinds.js";
18
18
  import { deriveTaskIdentityFromPrd, nextTaskIdRevision, } from "./prd-identity.js";
19
19
  import { appendEngineeringCliArgs, buildDraftFromTaskIntake, parseEngineeringBoundaryFromParams, } from "./prd-intake-bridge.js";
20
20
  import { discoverPrdReferences } from "./prd-reference-discovery.js";
21
- import { clearSemanticIntakeAttempt, SEMANTIC_INTAKE_CLI_TIMEOUT_MS, } from "../../task/source-prepare/semantic-intake.js";
21
+ import { clearSemanticIntakeAttempt } from "../../task/source-prepare/semantic-intake.js";
22
22
  import nodePath from "node:path";
23
23
  import { resolveSiblingAgentWorkerBin } from "./sibling-controller.js";
24
24
  import { projectOperationEventSummary, projectOperationForChat, } from "./chat/chat-event-store.js";
25
- import { OperationWaitError, waitForOperationChange, } from "./operation-wait.js";
26
25
  import { MutationGateReceiptStore } from "./mutation-gate-receipt-store.js";
27
- import { formatIneligibleRerunMessage } from "./recovery-error-copy.js";
28
- import { evaluateConsoleDagInterruptEligibility } from "../observability/interrupt-eligibility.js";
29
- import { INTERRUPT_ERROR_MESSAGES, isInterruptReasonCode, normalizeInterruptReasonDetail, } from "../observability/interrupt-eligibility.js";
30
26
  import { assessAutonomousExecutionEligibility, deriveEligibilityFactsFromDagSpec, } from "./dag-execution-receipt.js";
31
27
  import { issueHumanGateToken, mutationGatePayloadHash, verifyHumanGateToken, } from "./human-gate-token.js";
32
28
  const MUTATION_GATE_ACTIONS = new Set([
33
29
  // 2026-08-11: standaloneTaskRerun / workerTaskRetry are autonomous (server
34
- // run-facts checks). Destructive DAG reconciliation, live cooperative
35
- // interrupt, and Night Scheduler mutations still need the browser Human Gate.
30
+ // run-facts checks). Destructive DAG reconciliation and Night Scheduler
31
+ // mutations still need the browser Human Gate.
36
32
  "dagReconcileRun",
37
- "dagRequestInterrupt",
38
33
  "workerAdmissionPrepare",
39
34
  "workerSchedulerAdd",
40
35
  "workerSchedulerCancel",
@@ -47,12 +42,10 @@ const MUTATION_OR_LONG = new Set([
47
42
  "contractApply",
48
43
  "contractAdopt",
49
44
  "contractRecover",
50
- "taskAdvance",
51
45
  "dagRunTask",
52
46
  "runDag",
53
47
  "dagRerun",
54
48
  "dagReconcileRun",
55
- "dagRequestInterrupt",
56
49
  "standaloneTaskRerun",
57
50
  "workerTaskRetry",
58
51
  ]);
@@ -62,15 +55,6 @@ function paramsOf(req) {
62
55
  function str(v) {
63
56
  return typeof v === "string" && v.trim() ? v.trim() : undefined;
64
57
  }
65
- const TASK_ID_PLACEHOLDER_RE = /[<>]|^(?:id|task-?id|your-task-id|todo|tbd)$/i;
66
- /** Mirror of runtime GENERIC_TASK_SLUGS (not exported there); keep in sync. */
67
- const GENERIC_SLUG_SET = new Set(["test", "fix", "tmp", "new-task"]);
68
- const TASK_ADVANCE_ALIAS_ACTIONS = new Set(["promoteRun", "closeoutTask"]);
69
- function taskIdInputError(taskId) {
70
- if (!TASK_ID_PLACEHOLDER_RE.test(taskId))
71
- return undefined;
72
- return `taskId must be a concrete canonical id; placeholder values are not allowed: ${JSON.stringify(taskId)}`;
73
- }
74
58
  /** Extract write-set gate token from task advance operator JSON. */
75
59
  function extractWriteSetGateTokenFromCliJson(json, taskId) {
76
60
  if (!json || typeof json !== "object")
@@ -145,46 +129,13 @@ function gateError(action, code, message, details) {
145
129
  }),
146
130
  };
147
131
  }
148
- /**
149
- * `dag report --json` is `{ schemaVersion, runs: [...] }` (no Operator `ok`).
150
- * `runReadCli` used to wrap that as `{ stdout, raw }`, which made the
151
- * autonomous rerun gate miss `primaryFailure` even after the UI showed the
152
- * report (2026-08-16). Accept both the native report shape and the wrap.
153
- */
154
- function extractDagReportRunFacts(reportResult, runId) {
155
- const asRecord = (value) => value && typeof value === "object" && !Array.isArray(value)
156
- ? value
157
- : undefined;
158
- const result = asRecord(reportResult);
159
- const envelope = Array.isArray(result?.runs)
160
- ? result
161
- : asRecord(result?.raw);
162
- if (!envelope)
163
- return undefined;
164
- const runs = Array.isArray(envelope.runs) ? envelope.runs : [];
165
- const matched = runs.find((entry) => asRecord(entry)?.runId === runId) ?? runs[0];
166
- const matchedRec = asRecord(matched);
167
- if (matchedRec?.primaryFailure) {
168
- return {
169
- primaryFailure: asRecord(matchedRec.primaryFailure),
170
- primaryRecovery: asRecord(matchedRec.primaryRecovery),
171
- };
172
- }
173
- if (envelope.primaryFailure) {
174
- return {
175
- primaryFailure: asRecord(envelope.primaryFailure),
176
- primaryRecovery: asRecord(envelope.primaryRecovery),
177
- };
178
- }
179
- return undefined;
180
- }
181
132
  /**
182
133
  * 2026-08-11 R2 run-facts check for standaloneTaskRerun (read-only CLI
183
134
  * evidence only — no browser Human Gate). Fails closed when facts are missing.
184
135
  */
185
136
  async function assertAutonomousRerunEligible(ctx, runId, taskId) {
186
137
  const report = await runReadCli(ctx, ["dag", "report", "--run-id", runId, "--json"], "dag report");
187
- const reportResult = extractDagReportRunFacts(report.ok ? report.result : undefined, runId);
138
+ const reportResult = (report.ok ? report.result : undefined);
188
139
  const recovery = reportResult?.primaryRecovery;
189
140
  if (!report.ok || !reportResult?.primaryFailure || !recovery) {
190
141
  return gateError("standaloneTaskRerun", "INVALID_INPUT", "cannot read dagReport run facts; standaloneTaskRerun requires server-side run evidence", { runId });
@@ -330,9 +281,6 @@ async function runReadCli(ctx, args, command) {
330
281
  if (env.schemaVersion === 1 && typeof env.ok === "boolean") {
331
282
  return env;
332
283
  }
333
- if (raw.schemaVersion === 1 && Array.isArray(raw.runs)) {
334
- return operatorSucceeded(command, raw);
335
- }
336
284
  if (command === "dag rerun plan" &&
337
285
  raw.plan &&
338
286
  typeof raw.plan === "object") {
@@ -344,8 +292,9 @@ async function runReadCli(ctx, args, command) {
344
292
  ? plan.blockedReasons.map(String)
345
293
  : [];
346
294
  const blockingNodes = Array.isArray(plan.blockingNodes)
347
- ? plan.blockingNodes
295
+ ? plan.blockingNodes.map((n) => [n.nodeId, n.reasonCode].filter(Boolean).join(":"))
348
296
  : [];
297
+ const reasons = [...blockedReasons, ...blockingNodes].filter(Boolean);
349
298
  return buildOperatorResult({
350
299
  command,
351
300
  ok: false,
@@ -353,10 +302,9 @@ async function runReadCli(ctx, args, command) {
353
302
  result: plan,
354
303
  error: {
355
304
  code: "DAG_RERUN_INELIGIBLE",
356
- message: formatIneligibleRerunMessage({
357
- blockedReasons,
358
- blockingNodes,
359
- }),
305
+ message: reasons.length > 0
306
+ ? `从节点重跑不具备安全资格:${reasons.slice(0, 6).join("; ")}${reasons.length > 6 ? "…" : ""}`
307
+ : "从节点重跑不具备安全资格(eligible=false)",
360
308
  details: {
361
309
  eligible: false,
362
310
  planHash: typeof plan.planHash === "string" ? plan.planHash : undefined,
@@ -571,90 +519,6 @@ export async function dispatchOperatorAction(ctx, req) {
571
519
  }),
572
520
  };
573
521
  }
574
- case "operationWait": {
575
- const operationId = str(p.operationId);
576
- if (!operationId)
577
- return invalid(action, "operationId is required");
578
- const rawAfterSeq = p.afterSeq ?? 0;
579
- const afterSeq = typeof rawAfterSeq === "number" ? rawAfterSeq : Number(rawAfterSeq);
580
- const rawMaxWaitMs = p.maxWaitMs;
581
- const maxWaitMs = typeof rawMaxWaitMs === "number"
582
- ? rawMaxWaitMs
583
- : rawMaxWaitMs === undefined
584
- ? undefined
585
- : Number(rawMaxWaitMs);
586
- const rawWakeOn = str(p.wakeOn);
587
- const wakeOn = rawWakeOn === "all" || rawWakeOn === "meaningful" ? rawWakeOn : undefined;
588
- try {
589
- const result = await waitForOperationChange({
590
- operations: ctx.operations,
591
- events: ctx.events,
592
- operationId,
593
- afterSeq,
594
- maxWaitMs,
595
- ...(wakeOn ? { wakeOn } : {}),
596
- poll: async () => {
597
- const current = await ctx.operations.get(operationId);
598
- const runId = current?.dagRunId ??
599
- (typeof current?.actionParams?.runId === "string"
600
- ? current.actionParams.runId
601
- : undefined);
602
- if (!current || !runId)
603
- return false;
604
- const eligibility = await evaluateConsoleDagInterruptEligibility({
605
- repoRoot: ctx.repoRoot,
606
- operations: ctx.operations,
607
- runId,
608
- });
609
- if (!eligibility.interrupt)
610
- return false;
611
- const targetId = eligibility.targetOperationId ?? current.operationId;
612
- return syncTargetInterruptProjection({
613
- operations: ctx.operations,
614
- events: ctx.events,
615
- operationId: targetId,
616
- runId,
617
- interrupt: eligibility.interrupt,
618
- });
619
- },
620
- });
621
- return {
622
- kind: "sync",
623
- status: 200,
624
- body: operatorSucceeded(action, result),
625
- };
626
- }
627
- catch (error) {
628
- if (error instanceof OperationWaitError) {
629
- if (error.code === "NOT_FOUND") {
630
- return {
631
- kind: "error",
632
- status: 404,
633
- body: operatorFailed({
634
- command: action,
635
- outcome: "not-found",
636
- code: "NOT_FOUND",
637
- message: error.message,
638
- }),
639
- };
640
- }
641
- if (error.code === "EVENT_CURSOR_EXPIRED") {
642
- return {
643
- kind: "error",
644
- status: 410,
645
- body: operatorFailed({
646
- command: action,
647
- outcome: "blocked",
648
- code: "EVENT_CURSOR_EXPIRED",
649
- message: error.message,
650
- }),
651
- };
652
- }
653
- return invalid(action, error.message);
654
- }
655
- throw error;
656
- }
657
- }
658
522
  case "inspect": {
659
523
  const body = await runReadCli(ctx, ["inspect", "--json"], "inspect");
660
524
  return { kind: "sync", status: body.ok ? 200 : 400, body };
@@ -1322,24 +1186,8 @@ export async function dispatchOperatorAction(ctx, req) {
1322
1186
  };
1323
1187
  }
1324
1188
  const reviewPacket = extractReviewPacketFromCliJson(gen.json) ?? {};
1325
- let parsedDagSpec;
1326
- try {
1327
- parsedDagSpec = JSON.parse(dagText);
1328
- }
1329
- catch (error) {
1330
- return {
1331
- kind: "error",
1332
- status: 409,
1333
- body: operatorFailed({
1334
- command: action,
1335
- outcome: "blocked",
1336
- code: "DAG_GENERATION_FAILED",
1337
- message: `generated DAG is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
1338
- }),
1339
- };
1340
- }
1341
1189
  const derivedFacts = deriveEligibilityFactsFromDagSpec({
1342
- dagSpec: parsedDagSpec,
1190
+ dagSpec: JSON.parse(dagText),
1343
1191
  allowedPaths,
1344
1192
  forbiddenPaths,
1345
1193
  });
@@ -1501,24 +1349,6 @@ export async function dispatchOperatorAction(ctx, req) {
1501
1349
  if (!taskId || !clientRequestId) {
1502
1350
  return invalid(action, "taskId and clientRequestId are required");
1503
1351
  }
1504
- const taskIdError = taskIdInputError(taskId);
1505
- if (taskIdError)
1506
- return invalid(action, taskIdError);
1507
- // Canonical id gate at admission (2026-08-16): non-canonical ids were
1508
- // accepted here (validateNewTaskId only warns) and every downstream
1509
- // advance/import then flailed on the malformed task directory. Fail
1510
- // fast with a concrete suggested id instead.
1511
- if (!CANONICAL_TASK_ID_PATTERN.test(taskId)) {
1512
- const slug = taskId
1513
- .toLowerCase()
1514
- .replace(/[^a-z0-9]+/g, "-")
1515
- .replace(/^-+|-+$/g, "")
1516
- .slice(0, 48);
1517
- const suggested = slug && !GENERIC_SLUG_SET.has(slug)
1518
- ? `${formatLocalBusinessDate()}-${slug}`
1519
- : `${formatLocalBusinessDate()}-${slug || "task"}-r2`;
1520
- return invalid(action, `taskId must be canonical YYYY-MM-DD-<kebab-slug> (local business date); got "${taskId}" — suggested id: "${suggested}" (e.g. 2026-08-16-tool-preview-edge-test); retry newTask with that id`);
1521
- }
1522
1352
  return acceptOperation(ctx, {
1523
1353
  action,
1524
1354
  actionParams: p,
@@ -1527,38 +1357,6 @@ export async function dispatchOperatorAction(ctx, req) {
1527
1357
  cliArgs: ["task", "advance", taskId, title, "--json"],
1528
1358
  });
1529
1359
  }
1530
- case "taskAdvance": {
1531
- const taskId = str(p.taskId);
1532
- const clientRequestId = str(req.clientRequestId);
1533
- if (!taskId || !clientRequestId) {
1534
- return invalid(action, "taskId and clientRequestId are required");
1535
- }
1536
- const taskIdError = taskIdInputError(taskId);
1537
- if (taskIdError)
1538
- return invalid(action, taskIdError);
1539
- const args = ["task", "advance", taskId];
1540
- const title = str(p.title);
1541
- if (title)
1542
- args.push(title);
1543
- const taskKind = str(p.taskKind);
1544
- if (taskKind)
1545
- args.push("--task-kind", taskKind);
1546
- const featureId = str(p.featureId);
1547
- if (featureId)
1548
- args.push("--feature-id", featureId);
1549
- const profile = str(p.profile);
1550
- if (profile)
1551
- args.push("--profile", profile);
1552
- appendEngineeringCliArgs(args, parseEngineeringBoundaryFromParams(p));
1553
- args.push("--json");
1554
- return acceptOperation(ctx, {
1555
- action,
1556
- actionParams: p,
1557
- clientRequestId,
1558
- taskId,
1559
- cliArgs: args,
1560
- });
1561
- }
1562
1360
  case "bootstrapFromPrd":
1563
1361
  return dispatchBootstrapFromPrd(ctx, p);
1564
1362
  case "importPrd": {
@@ -1569,9 +1367,6 @@ export async function dispatchOperatorAction(ctx, req) {
1569
1367
  if (!taskId || !content) {
1570
1368
  return invalid(action, "taskId and content are required");
1571
1369
  }
1572
- const taskIdError = taskIdInputError(taskId);
1573
- if (taskIdError)
1574
- return invalid(action, taskIdError);
1575
1370
  const taskKindRaw = str(p.taskKind)?.trim();
1576
1371
  const taskKind = normalizeConsoleWorkflowKind(taskKindRaw, "standard");
1577
1372
  const taskKindExplicit = Boolean(taskKindRaw);
@@ -1625,7 +1420,7 @@ export async function dispatchOperatorAction(ctx, req) {
1625
1420
  cwd: ctx.repoRoot,
1626
1421
  artifactName: "import-prd-task-advance-intake",
1627
1422
  expectJson: true,
1628
- timeoutMs: SEMANTIC_INTAKE_CLI_TIMEOUT_MS,
1423
+ timeoutMs: 300_000,
1629
1424
  });
1630
1425
  const intakeJson = intake.json ?? null;
1631
1426
  const intakeOk = intake.ok && intake.exitCode === 0 && !intake.timedOut;
@@ -1766,9 +1561,6 @@ export async function dispatchOperatorAction(ctx, req) {
1766
1561
  if (!taskId || !clientRequestId) {
1767
1562
  return invalid(action, "taskId and clientRequestId are required");
1768
1563
  }
1769
- const taskIdError = taskIdInputError(taskId);
1770
- if (taskIdError)
1771
- return invalid(action, taskIdError);
1772
1564
  const out = await stageUtf8Text(ctx.appData, "", { extension: ".json" });
1773
1565
  return acceptOperation(ctx, {
1774
1566
  action,
@@ -2051,99 +1843,6 @@ export async function dispatchOperatorAction(ctx, req) {
2051
1843
  ],
2052
1844
  });
2053
1845
  }
2054
- case "dagInterruptEligibility": {
2055
- const runId = str(p.runId) ?? str(p.dagRunId);
2056
- if (!runId)
2057
- return invalid(action, "runId is required");
2058
- const eligibility = await evaluateConsoleDagInterruptEligibility({
2059
- repoRoot: ctx.repoRoot,
2060
- operations: ctx.operations,
2061
- runId,
2062
- });
2063
- if (eligibility.interrupt && eligibility.targetOperationId) {
2064
- await syncTargetInterruptProjection({
2065
- operations: ctx.operations,
2066
- events: ctx.events,
2067
- operationId: eligibility.targetOperationId,
2068
- runId,
2069
- interrupt: eligibility.interrupt,
2070
- });
2071
- }
2072
- return {
2073
- kind: "sync",
2074
- status: 200,
2075
- body: operatorSucceeded(action, eligibility),
2076
- };
2077
- }
2078
- case "dagRequestInterrupt": {
2079
- const runId = str(p.runId);
2080
- const reasonCode = str(p.reasonCode);
2081
- const reasonDetail = str(p.reasonDetail) ?? str(p.reason);
2082
- const clientRequestId = str(req.clientRequestId);
2083
- if (!runId || !reasonCode || !reasonDetail || !clientRequestId) {
2084
- return invalid(action, "runId, reasonCode, reasonDetail, and clientRequestId are required");
2085
- }
2086
- if (!isInterruptReasonCode(reasonCode)) {
2087
- return invalid(action, `reasonCode must be one of requirements-changed | suspected-runaway | resource-protection | operator-request | other`);
2088
- }
2089
- try {
2090
- normalizeInterruptReasonDetail(reasonDetail);
2091
- }
2092
- catch (error) {
2093
- return invalid(action, error instanceof Error ? error.message : String(error));
2094
- }
2095
- const gated = await consumeMutationGateReceipt(ctx, action, p);
2096
- if (gated.kind === "error")
2097
- return gated;
2098
- if (gated.kind === "idempotent")
2099
- return gated.result;
2100
- const eligibility = await evaluateConsoleDagInterruptEligibility({
2101
- repoRoot: ctx.repoRoot,
2102
- operations: ctx.operations,
2103
- runId,
2104
- });
2105
- if (!eligibility.eligible || !eligibility.targetOperationId || !eligibility.expectedRunnerIdentity) {
2106
- const failed = interruptActionFailed(action, eligibility.reasonCodes[0] ?? "RUN_NOT_ACTIVE", eligibility.message);
2107
- return failed;
2108
- }
2109
- const expected = eligibility.expectedRunnerIdentity;
2110
- const accepted = await acceptOperation(ctx, {
2111
- action,
2112
- actionParams: {
2113
- runId,
2114
- reasonCode,
2115
- reasonDetail,
2116
- targetOperationId: eligibility.targetOperationId,
2117
- expectedRunnerIdentity: expected,
2118
- },
2119
- clientRequestId,
2120
- cliArgs: [
2121
- "dag",
2122
- "request-interrupt",
2123
- "--run-id",
2124
- runId,
2125
- "--request-id",
2126
- clientRequestId,
2127
- "--target-operation-id",
2128
- eligibility.targetOperationId,
2129
- "--reason-code",
2130
- reasonCode,
2131
- "--reason",
2132
- reasonDetail,
2133
- "--expected-runner-pid",
2134
- String(expected.pid),
2135
- "--expected-runner-hostname",
2136
- expected.hostname,
2137
- "--expected-runner-started-at",
2138
- expected.startedAt,
2139
- "--expected-controller-fingerprint",
2140
- expected.controllerFingerprint,
2141
- "--json",
2142
- ],
2143
- });
2144
- await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
2145
- return accepted;
2146
- }
2147
1846
  case "dagReconcileRun": {
2148
1847
  // S6 Recovery: orphan/interrupted runs — structured reason is the human
2149
1848
  // confirmation surface (CLI eligibility still fail-closed).
@@ -2437,7 +2136,7 @@ export async function dispatchOperatorAction(ctx, req) {
2437
2136
  return invalid(action, "action and actionParams are required");
2438
2137
  }
2439
2138
  if (!MUTATION_GATE_ACTIONS.has(targetAction)) {
2440
- return invalid(action, "action must be a mutation-gate target (dagReconcileRun | dagRequestInterrupt | workerAdmissionPrepare | workerSchedulerAdd | workerSchedulerCancel | workerSchedulerHarvest | workerSchedulerDiscard)");
2139
+ return invalid(action, "action must be a mutation-gate target (dagReconcileRun | workerAdmissionPrepare | workerSchedulerAdd | workerSchedulerCancel | workerSchedulerHarvest | workerSchedulerDiscard)");
2441
2140
  }
2442
2141
  const secret = ctx.humanGateSecret;
2443
2142
  if (!secret) {
@@ -2496,14 +2195,6 @@ export async function dispatchOperatorAction(ctx, req) {
2496
2195
  rawArgs.some((value) => typeof value !== "string"))) {
2497
2196
  return invalid(action, "args must be an array of strings");
2498
2197
  }
2499
- if (TASK_ADVANCE_ALIAS_ACTIONS.has(action) && Array.isArray(rawArgs)) {
2500
- const aliasTaskId = str(rawArgs[0]);
2501
- const taskIdError = aliasTaskId
2502
- ? taskIdInputError(aliasTaskId)
2503
- : undefined;
2504
- if (taskIdError)
2505
- return invalid(action, taskIdError);
2506
- }
2507
2198
  if (capability.humanConfirmation === "required") {
2508
2199
  const confirmationId = str(p.confirmationId);
2509
2200
  if (!confirmationId)
@@ -2583,9 +2274,6 @@ export async function dispatchOperatorAction(ctx, req) {
2583
2274
  .replace(/^(loop-agent|agent-worker)\s+/, "")
2584
2275
  .split(/\s+/);
2585
2276
  const args = [...prefix, ...(rawArgs ?? [])];
2586
- if (TASK_ADVANCE_ALIAS_ACTIONS.has(action) && !args.includes("--json")) {
2587
- args.push("--json");
2588
- }
2589
2277
  if (capability.kind === "read") {
2590
2278
  if (!args.includes("--json"))
2591
2279
  args.push("--json");
@@ -3008,7 +2696,7 @@ async function dispatchBootstrapFromPrd(ctx, p) {
3008
2696
  cwd: ctx.repoRoot,
3009
2697
  artifactName: "bootstrap-task-advance-intake",
3010
2698
  expectJson: true,
3011
- timeoutMs: SEMANTIC_INTAKE_CLI_TIMEOUT_MS,
2699
+ timeoutMs: 300_000,
3012
2700
  });
3013
2701
  const intakeJson = intake.json ?? null;
3014
2702
  const intakeOk = intake.ok && intake.exitCode === 0 && !intake.timedOut;
@@ -3482,71 +3170,6 @@ function invalid(action, message) {
3482
3170
  }),
3483
3171
  };
3484
3172
  }
3485
- function interruptActionFailed(action, code, message) {
3486
- const blocked = code === "INTERRUPT_ALREADY_PENDING" ||
3487
- code === "INTERRUPT_NEEDS_RECONCILE" ||
3488
- code === "RUN_ALREADY_TERMINAL" ||
3489
- code === "RUN_NOT_CONSOLE_OWNED" ||
3490
- code === "RUNNER_IDENTITY_MISMATCH" ||
3491
- code === "RUN_NOT_ACTIVE";
3492
- return {
3493
- kind: "error",
3494
- status: code === "INVALID_INPUT" ? 400 : 409,
3495
- body: operatorFailed({
3496
- command: action,
3497
- outcome: blocked ? "blocked" : "invalid",
3498
- code,
3499
- message: message || INTERRUPT_ERROR_MESSAGES[code] || message,
3500
- }),
3501
- };
3502
- }
3503
- async function syncTargetInterruptProjection(input) {
3504
- const current = await input.operations.get(input.operationId);
3505
- if (!current)
3506
- return false;
3507
- if (current.interrupt?.status === input.interrupt.status &&
3508
- current.interrupt?.requestId === input.interrupt.requestId) {
3509
- return false;
3510
- }
3511
- const nextStatus = input.interrupt.status;
3512
- if (nextStatus !== "requested" &&
3513
- nextStatus !== "acknowledged" &&
3514
- nextStatus !== "settled" &&
3515
- nextStatus !== "needs-reconcile") {
3516
- return false;
3517
- }
3518
- await input.operations.update(input.operationId, {
3519
- dagRunId: current.dagRunId ?? input.runId,
3520
- interrupt: {
3521
- status: nextStatus,
3522
- ...(input.interrupt.requestId
3523
- ? { requestId: input.interrupt.requestId }
3524
- : {}),
3525
- ...(input.interrupt.reasonCode
3526
- ? { reasonCode: input.interrupt.reasonCode }
3527
- : {}),
3528
- ...(input.interrupt.requestedAt
3529
- ? { requestedAt: input.interrupt.requestedAt }
3530
- : {}),
3531
- ...(input.interrupt.acknowledgedAt
3532
- ? { acknowledgedAt: input.interrupt.acknowledgedAt }
3533
- : {}),
3534
- ...(input.interrupt.settledAt
3535
- ? { settledAt: input.interrupt.settledAt }
3536
- : {}),
3537
- ...(input.interrupt.outcome ? { outcome: input.interrupt.outcome } : {}),
3538
- },
3539
- });
3540
- input.events.append(input.operationId, {
3541
- kind: "state",
3542
- message: `interrupt ${nextStatus}`,
3543
- data: {
3544
- interruptStatus: nextStatus,
3545
- runId: input.runId,
3546
- },
3547
- });
3548
- return true;
3549
- }
3550
3173
  function confirmationErrorStatus(code) {
3551
3174
  if (code === "NOT_FOUND")
3552
3175
  return 404;
@@ -1,12 +1,8 @@
1
- import { rewriteRecoveryOperatorError } from "./recovery-error-copy.js";
2
1
  /** Map internal spawn/controller errors to actionable Chinese copy. */
3
2
  export function formatOperatorUserError(message) {
4
3
  const raw = (message ?? "").trim();
5
4
  if (!raw)
6
5
  return "操作失败";
7
- const recovery = rewriteRecoveryOperatorError(raw);
8
- if (recovery)
9
- return recovery;
10
6
  if (/controller identity changed|package fingerprint changed|entry binary sha256 changed/i.test(raw)) {
11
7
  return "控制器版本已变更(Console 启动后代码被重新编译)。请重启 Console 后再试,一次会话中途不要升级/重建控制器。";
12
8
  }