@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.3

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 (187) hide show
  1. package/CHANGELOG.md +9 -140
  2. package/dist/application/task-lifecycle/advance.js +4 -17
  3. package/dist/application/task-lifecycle/observe.js +29 -43
  4. package/dist/application/task-lifecycle/plan-transitions.js +4 -5
  5. package/dist/build-stamp.json +3 -3
  6. package/dist/commands/client-recovery.js +55 -20
  7. package/dist/commands/new-task.js +5 -0
  8. package/dist/commands/status.js +57 -0
  9. package/dist/commands/task-contract.js +269 -0
  10. package/dist/executors/dag-pi-executor.js +230 -269
  11. package/dist/executors/pi-executor.js +0 -2
  12. package/dist/executors/pi-sdk-executor.js +39 -77
  13. package/dist/executors/shell-executor.js +2 -3
  14. package/dist/executors/shell-write-guard.js +32 -27
  15. package/dist/shared/pi-context-pressure/extension.js +16 -26
  16. package/dist/shared/pi-retry-settings.js +0 -40
  17. package/dist/shared/prompts.js +26 -0
  18. package/dist/task/config.js +2 -0
  19. package/dist/task/contract/validate-draft.js +106 -0
  20. package/dist/task/goal.js +3 -0
  21. package/dist/task/index.js +12 -0
  22. package/dist/task/lifecycle.js +1 -0
  23. package/dist/task/operator/capabilities.js +6 -0
  24. package/dist/task/operator/envelope.js +2 -0
  25. package/dist/task/operator/index.js +5 -0
  26. package/dist/task/operator/registry.js +2 -0
  27. package/dist/task/operator/types.js +1 -0
  28. package/dist/task/paths.js +1 -0
  29. package/dist/task/source-prepare/source-fidelity-pi.js +2 -2
  30. package/dist/task/source-references.js +23 -48
  31. package/dist/task/source-state.js +1 -0
  32. package/dist/task/subagent-guidance.js +1 -0
  33. package/dist/worker/console/chat/assistant-content.js +2 -23
  34. package/dist/worker/console/chat/explore-tools.js +0 -13
  35. package/dist/worker/console/chat/pi-runtime.js +1182 -221
  36. package/dist/worker/console/chat/routes.js +71 -213
  37. package/dist/worker/console/chat/sdd-data-alignment.js +9 -65
  38. package/dist/worker/console/chat/session-catalog.js +0 -32
  39. package/dist/worker/console/chat/session-store.js +6 -22
  40. package/dist/worker/console/chat/shortcuts.js +0 -6
  41. package/dist/worker/console/chat/sift-bridge.js +1 -0
  42. package/dist/worker/console/chat/tool-preview.js +0 -11
  43. package/dist/worker/console/chat/tools.js +0 -6
  44. package/dist/worker/console/console-update-and-init.js +19 -1
  45. package/dist/worker/console/console-update-runtime.js +20 -5
  46. package/dist/worker/console/index.js +20 -0
  47. package/dist/worker/console/operator-actions.js +1 -13
  48. package/dist/worker/console/routes.js +0 -8
  49. package/dist/worker/console/server.js +29 -11
  50. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-hUmM_GRf.js} +1 -1
  51. package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-Bt-xuieW.js} +1 -1
  52. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-DTqkL4DJ.js} +1 -1
  53. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-Cy0MNnzV.js} +1 -1
  54. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-DDdlT0Pd.js} +1 -1
  55. package/dist/worker/console/static/assets/channel-BpzRnZmU.js +1 -0
  56. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-3JawwNZ6.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-DoTp7Cri.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-CcirhZaS.js} +1 -1
  59. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-VrG21gnP.js} +1 -1
  60. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DDOgIS70.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CBjmgvJ_.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-CCQSht8M.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-BpGMkUUV.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-DNDT41QL.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-euXnDFbn.js} +1 -1
  66. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlMVqCc.js +1 -0
  67. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlMVqCc.js +1 -0
  68. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-BJ6NAeql.js} +1 -1
  69. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-F5JKIvIq.js} +1 -1
  70. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-Cwmkl_Gi.js} +1 -1
  71. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-BmCZ8815.js} +1 -1
  72. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-zWA3hUnX.js} +1 -1
  73. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-BzA-888F.js} +1 -1
  74. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-tPNheoch.js} +1 -1
  75. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-Cdi7Fyqp.js} +1 -1
  76. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-DFK9VBf1.js} +1 -1
  77. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-xCUr1kpg.js} +1 -1
  78. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-BANQHY_A.js} +1 -1
  79. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-VHJYEMkP.js} +1 -1
  80. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-DiyZQ0C6.js} +1 -1
  81. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-CZtIeBBa.js} +1 -1
  82. package/dist/worker/console/static/assets/index-BlN6ytLr.css +1 -0
  83. package/dist/worker/console/static/assets/index-CA-jfWlE.js +449 -0
  84. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-BuSspC0X.js} +1 -1
  85. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DRJZyPFy.js} +1 -1
  86. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-DIfXIVuE.js} +1 -1
  87. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-CT10_7Lt.js} +1 -1
  88. package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-ufemj74H.js} +1 -1
  89. package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-DEQe8-_m.js} +5 -5
  90. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-BRLUrbrC.js} +1 -1
  91. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BgibKtiF.js} +1 -1
  92. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-D3eyfdV9.js} +1 -1
  93. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DXeTk5Px.js} +1 -1
  94. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-BgXdFEe7.js} +1 -1
  95. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-CseC2A7R.js} +1 -1
  96. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-D80PFzfJ.js} +1 -1
  97. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BWzHW7LS.js} +1 -1
  98. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-xWQYTLAn.js} +1 -1
  99. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-CLxtpZeL.js} +1 -1
  100. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CiVoq4G-.js +1 -0
  101. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-DwugtmWV.js} +2 -2
  102. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BkdgdpfR.js +8 -0
  103. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-DHEOwHIe.js} +1 -1
  104. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-pmbZKlBK.js} +1 -1
  105. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-CivHoM5q.js} +1 -1
  106. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BfHIE7o6.js} +1 -1
  107. package/dist/worker/console/static/index.html +2 -2
  108. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +10 -122
  109. package/dist/worker/console/static-src/operator-chat/compaction-message.js +0 -7
  110. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +15 -35
  111. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +62 -136
  112. package/dist/worker/console/static-src/operator-chat/useChatStream.js +68 -159
  113. package/dist/worker/console/static-src/operator-chat/useChatThread.js +56 -75
  114. package/dist/worker/console/static-src/operator-chat/useComposer.js +78 -206
  115. package/dist/worker/console/workspace-context.js +1 -21
  116. package/dist/worker/observe/static/router.js +1 -1
  117. package/dist/worker/observe/static/styles.css +4 -7
  118. package/dist/worker/observe/static/views/dag-inspector.js +19 -12
  119. package/dist/worker/observe/static/views/dag.js +2 -2
  120. package/dist/worker/scheduler/index.js +25 -0
  121. package/dist/workflows/dag/facts.js +4 -0
  122. package/dist/workflows/dag/frontend-closeout.js +1 -1
  123. package/dist/workflows/dag/frontend-design-policy.js +1 -2
  124. package/dist/workflows/dag/frontend-implementation-contract.js +4 -8
  125. package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
  126. package/dist/workflows/dag/frontend-repair.js +76 -55
  127. package/dist/workflows/dag/frontend-review-context.js +1 -2
  128. package/dist/workflows/dag/frontend-review-scopes.js +12 -34
  129. package/dist/workflows/dag/frontend-shape-facts.js +2 -12
  130. package/dist/workflows/dag/frontend-verification-trace.js +42 -8
  131. package/dist/workflows/dag/index.js +6 -0
  132. package/dist/workflows/dag/init-hybrid.js +160 -97
  133. package/dist/workflows/dynamic/index.js +7 -0
  134. package/docs/architecture/runtime-boundaries.md +3 -3
  135. package/docs/templates/frontend-implementation-dag.json +8 -9
  136. package/package.json +3 -7
  137. package/skills/frontend-bounded-implement/SKILL.md +3 -4
  138. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  139. package/skills/frontend-contract/references/contract-protocol.md +1 -3
  140. package/skills/frontend-design-review/SKILL.md +7 -12
  141. package/skills/frontend-design-review/references/review-checklist.md +7 -7
  142. package/skills/frontend-plan/SKILL.md +0 -19
  143. package/skills/frontend-plan/references/decision-contract.md +19 -26
  144. package/skills/frontend-review/SKILL.md +18 -12
  145. package/skills/frontend-review/references/review-findings.md +43 -24
  146. package/dist/shared/pi-provider-recovery.js +0 -182
  147. package/dist/worker/console/chat/browser-automation.js +0 -363
  148. package/dist/worker/console/chat/browser-policy.js +0 -113
  149. package/dist/worker/console/chat/browser-routes.js +0 -162
  150. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +0 -44
  151. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +0 -89
  152. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +0 -202
  153. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +0 -79
  154. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +0 -86
  155. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +0 -38
  156. package/dist/worker/console/chat/pi-runtime/inspection-types.js +0 -28
  157. package/dist/worker/console/chat/pi-runtime/inspection.js +0 -439
  158. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +0 -153
  159. package/dist/worker/console/chat/pi-runtime/sdk-session.js +0 -5
  160. package/dist/worker/console/chat/scm-routes.js +0 -217
  161. package/dist/worker/console/chat/scm-service.js +0 -283
  162. package/dist/worker/console/chat/scm-tools.js +0 -111
  163. package/dist/worker/console/chat/session-mode-view.js +0 -49
  164. package/dist/worker/console/chat/session-mode.js +0 -220
  165. package/dist/worker/console/chat/terminal-routes.js +0 -216
  166. package/dist/worker/console/chat/terminal-sessions.js +0 -348
  167. package/dist/worker/console/chat/terminal-tools.js +0 -211
  168. package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
  169. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
  170. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
  171. package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
  172. package/dist/worker/console/static/assets/index-wXlQohNs.js +0 -469
  173. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
  174. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
  175. package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +0 -10
  176. package/dist/worker/console/static-src/operator-chat/chat-link.js +0 -94
  177. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +0 -24
  178. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +0 -192
  179. package/dist/worker/console/static-src/operator-chat/process-label.js +0 -41
  180. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +0 -13
  181. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +0 -10
  182. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +0 -51
  183. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +0 -21
  184. package/dist/worker/console/workspace-initialization.js +0 -209
  185. package/dist/workflows/dag/frontend-capacity.js +0 -9
  186. package/dist/workflows/dag/frontend-plan-completeness.js +0 -186
  187. package/dist/workflows/dag/frontend-plan-recovery-policy.js +0 -18
@@ -707,8 +707,6 @@ function shouldFallbackSdkToCli(sdkResult, validationIssues, options) {
707
707
  return true;
708
708
  if (!sdkResult.ok &&
709
709
  (sdkResult.failureCategory === "auth" ||
710
- sdkResult.failureCategory === "output-limit" ||
711
- sdkResult.failureCategory === "cancelled" ||
712
710
  sdkResult.failureCategory === "timeout" ||
713
711
  sdkResult.failureCategory === "termination-unconfirmed")) {
714
712
  return false;
@@ -9,9 +9,6 @@ import { BoundedTextPreview, classifyPiFailure, createPiJsonlStreamCollector, DE
9
9
  import { serializeSessionEvent } from "./pi-event-serializer.js";
10
10
  import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
11
11
  import { createCompactionController, createContextPressureExtension, createSiftBridgeState, inMemoryCompactionSettings, isDagSiftEnabled, parseCompactionPolicy, parseSiftBridgeConfig, SIFT_RETRIEVE_TOOL_NAME, } from "../shared/pi-context-pressure/index.js";
12
- /** Maximum number of same-session continuations after provider output truncation. */
13
- export const PI_SDK_OUTPUT_LIMIT_CONTINUE_MAX = 3;
14
- export const PI_SDK_OUTPUT_LIMIT_CONTINUE_PROMPT = "继续。上一轮因输出长度上限(stopReason=length)截断;当前会话、工具与已完成工作仍保留。只完成尚未完成的部分,不重做已成功的写入或事实提交。";
15
12
  /** Wrap the actual stream seam: tool turns, retries and compaction all await it. */
16
13
  export async function installPiProviderRequestBudget(session, reserve, onFailure, capacity) {
17
14
  const agent = session.agent;
@@ -614,8 +611,6 @@ async function executeSingleSdkAttemptInternal(options) {
614
611
  const requestEnvelopeObservation = { peakBytes: 0, peakEstimatedTokens: 0, estimatorVersion: "utf8-bytes-div3-v1-estimate" };
615
612
  const stdoutPreview = new BoundedTextPreview("stdout");
616
613
  const stdoutCollector = createPiJsonlStreamCollector();
617
- let turnCollector = createPiJsonlStreamCollector();
618
- let committedTerminalObserved = false;
619
614
  const usageSamples = [];
620
615
  const observationEvents = [];
621
616
  const readObservation = createFrontendReadObservation();
@@ -627,9 +622,6 @@ async function executeSingleSdkAttemptInternal(options) {
627
622
  // length-stopped thinking-only attempt with no persisted write tool calls is
628
623
  // still distinguishable from generic empty output.
629
624
  let stopReason;
630
- let providerErrorMessage;
631
- let promptIndex = 0;
632
- let outputContinuesSent = 0;
633
625
  let thinkingObserved = false;
634
626
  let writeToolCallCount = 0;
635
627
  const observeWriterThinkingExhaustionEvidence = (event) => {
@@ -776,8 +768,6 @@ async function executeSingleSdkAttemptInternal(options) {
776
768
  resolveContextBudget?.("context-budget");
777
769
  };
778
770
  unsubscribe = session.subscribe((event) => {
779
- if (event.type === "tool_execution_end" && event.isError !== true && typeof event.toolName === "string" && options.outputLimitRecovery?.terminalToolNames.includes(event.toolName) && isRecord(event.result) && isRecord(event.result.details) && event.result.details.ok === true)
780
- committedTerminalObserved = true;
781
771
  if (event.type === "agent_settled" && !settled) {
782
772
  settled = true;
783
773
  resolveSettlement?.("settled");
@@ -798,8 +788,6 @@ async function executeSingleSdkAttemptInternal(options) {
798
788
  }
799
789
  }
800
790
  const observationMessage = isRecord(event.message) ? event.message : undefined;
801
- if (typeof observationMessage?.errorMessage === "string")
802
- providerErrorMessage = observationMessage.errorMessage;
803
791
  try {
804
792
  readObservation.observe(event);
805
793
  }
@@ -808,7 +796,7 @@ async function executeSingleSdkAttemptInternal(options) {
808
796
  observationEvents.push({ responseId: event.responseId, messageId: event.messageId, usage: event.usage, tokenUsage: event.tokenUsage, message: observationMessage ? { role: observationMessage.role, id: observationMessage.id, responseId: observationMessage.responseId, usage: observationMessage.usage } : undefined });
809
797
  const usageSample = extractSdkUsageSample(event);
810
798
  if (usageSample)
811
- usageSamples.push({ ...usageSample, responseKey: usageSample.responseKey ?? `turn-${promptIndex}` });
799
+ usageSamples.push(usageSample);
812
800
  if (event.type === "turn_end")
813
801
  observedTurns += 1;
814
802
  checkContextBudget();
@@ -822,7 +810,6 @@ async function executeSingleSdkAttemptInternal(options) {
822
810
  const line = serializeSessionEvent(event);
823
811
  stdoutPreview.append(`${line}\n`);
824
812
  stdoutCollector.append(`${line}\n`);
825
- turnCollector.append(`${line}\n`);
826
813
  sessionEventAppender?.append(line, event);
827
814
  });
828
815
  const timeoutPromise = timeoutMs > 0
@@ -833,9 +820,6 @@ async function executeSingleSdkAttemptInternal(options) {
833
820
  })
834
821
  : null;
835
822
  const runTurn = async (userMessage) => {
836
- promptIndex += 1;
837
- stopReason = undefined;
838
- turnCollector = createPiJsonlStreamCollector();
839
823
  const settlementPromise = resetSettlement();
840
824
  const stallPromise = resetStallPromise();
841
825
  const contextBudgetPromise = options.contextBudget
@@ -881,55 +865,44 @@ async function executeSingleSdkAttemptInternal(options) {
881
865
  : options.userMessage;
882
866
  let continuesSent = 0;
883
867
  let raced = await runTurn(promptMessage);
884
- while (true) {
885
- if (raced === "context-budget") {
886
- appendStderr(contextBudgetDetail);
887
- abortConfirmed = await runSessionActionWithGrace("abort", () => session.abort());
888
- await disposeSession();
889
- }
890
- else if (raced === "stall") {
891
- abortConfirmed = await runSessionActionWithGrace("abort", () => session.abort());
892
- const withinAbsoluteTimeout = timeoutMs <= 0 || Date.now() - startedAt < timeoutMs;
893
- const canContinue = abortConfirmed &&
894
- writeToolCallCount === 0 &&
895
- withinAbsoluteTimeout &&
896
- continuesSent < PI_SDK_STALL_CONTINUE_MAX;
897
- if (canContinue) {
898
- // The aborted turn's stopReason must not leak onto a recovered continue.
899
- stopReason = undefined;
900
- continuesSent += 1;
901
- persistRuntimeNote({
902
- type: "loop-agent-stall-continue",
903
- prompt: PI_SDK_STALL_CONTINUE_PROMPT,
904
- maxContinues: PI_SDK_STALL_CONTINUE_MAX,
905
- });
906
- raced = await runTurn(PI_SDK_STALL_CONTINUE_PROMPT);
907
- if (raced !== "done" && raced !== "settled") {
908
- await failClosedAfterWait(raced === "stall" ? "stall" : "absolute-timeout", raced === "stall"
909
- ? "pi SDK stall continue did not recover; treating as timeout"
910
- : "pi SDK stall continue hit the absolute timeout", true);
911
- }
912
- }
913
- else {
914
- await failClosedAfterWait("stall", !abortConfirmed
915
- ? undefined
916
- : writeToolCallCount > 0
917
- ? "pi SDK stall continue skipped because write tools already ran"
918
- : "pi SDK stall continue skipped because absolute timeout elapsed", false);
868
+ if (raced === "context-budget") {
869
+ appendStderr(contextBudgetDetail);
870
+ abortConfirmed = await runSessionActionWithGrace("abort", () => session.abort());
871
+ await disposeSession();
872
+ }
873
+ else if (raced === "stall") {
874
+ abortConfirmed = await runSessionActionWithGrace("abort", () => session.abort());
875
+ const withinAbsoluteTimeout = timeoutMs <= 0 || Date.now() - startedAt < timeoutMs;
876
+ const canContinue = abortConfirmed &&
877
+ writeToolCallCount === 0 &&
878
+ withinAbsoluteTimeout &&
879
+ continuesSent < PI_SDK_STALL_CONTINUE_MAX;
880
+ if (canContinue) {
881
+ // The aborted turn's stopReason must not leak onto a recovered continue.
882
+ stopReason = undefined;
883
+ continuesSent += 1;
884
+ persistRuntimeNote({
885
+ type: "loop-agent-stall-continue",
886
+ prompt: PI_SDK_STALL_CONTINUE_PROMPT,
887
+ maxContinues: PI_SDK_STALL_CONTINUE_MAX,
888
+ });
889
+ raced = await runTurn(PI_SDK_STALL_CONTINUE_PROMPT);
890
+ if (raced !== "done" && raced !== "settled") {
891
+ await failClosedAfterWait(raced === "stall" ? "stall" : "absolute-timeout", raced === "stall"
892
+ ? "pi SDK stall continue did not recover; treating as timeout"
893
+ : "pi SDK stall continue hit the absolute timeout", true);
919
894
  }
920
895
  }
921
- else if (raced === "absolute-timeout") {
922
- await failClosedAfterWait("absolute-timeout");
923
- }
924
- if (options.outputLimitRecovery && stopReason === "length" && !committedTerminalObserved && !stderr && outputContinuesSent < PI_SDK_OUTPUT_LIMIT_CONTINUE_MAX && !timedOut) {
925
- outputContinuesSent += 1;
926
- const note = serializeSessionEvent({ type: "loop-agent-output-limit-continue", continue: outputContinuesSent, maxContinues: PI_SDK_OUTPUT_LIMIT_CONTINUE_MAX, prompt: PI_SDK_OUTPUT_LIMIT_CONTINUE_PROMPT });
927
- stdoutPreview.append(`${note}\n`);
928
- stdoutCollector.append(`${note}\n`);
929
- raced = await runTurn(PI_SDK_OUTPUT_LIMIT_CONTINUE_PROMPT);
930
- continue;
896
+ else {
897
+ await failClosedAfterWait("stall", !abortConfirmed
898
+ ? undefined
899
+ : writeToolCallCount > 0
900
+ ? "pi SDK stall continue skipped because write tools already ran"
901
+ : "pi SDK stall continue skipped because absolute timeout elapsed", false);
931
902
  }
932
- break;
903
+ }
904
+ else if (raced === "absolute-timeout") {
905
+ await failClosedAfterWait("absolute-timeout");
933
906
  }
934
907
  }
935
908
  catch (error) {
@@ -958,29 +931,20 @@ async function executeSingleSdkAttemptInternal(options) {
958
931
  }
959
932
  }
960
933
  }
961
- if (providerErrorMessage && stopReason === "error" && !stderr)
962
- appendStderr(providerErrorMessage);
963
- if (options.outputLimitRecovery && stopReason === "length" && !committedTerminalObserved && outputContinuesSent >= PI_SDK_OUTPUT_LIMIT_CONTINUE_MAX) {
964
- const note = serializeSessionEvent({ type: "loop-agent-output-limit-exhausted", maxContinues: PI_SDK_OUTPUT_LIMIT_CONTINUE_MAX });
965
- stdoutPreview.append(`${note}\n`);
966
- }
967
934
  const stdout = stdoutPreview.text();
968
935
  const durationMs = Date.now() - startedAt;
969
936
  const collected = stdoutCollector.finish();
970
937
  const fallbackParsed = collected.parsedEvents > 0
971
938
  ? undefined
972
939
  : extractAssistantTextFromPiJson(stdout);
973
- const finalTurn = turnCollector.finish();
974
940
  const assistantText = collected.parsedEvents > 0
975
- ? finalTurn.assistantText
941
+ ? collected.assistantText
976
942
  : (fallbackParsed?.assistantText ?? "");
977
943
  const parsedEvents = collected.parsedEvents > 0
978
944
  ? collected.parsedEvents
979
945
  : (fallbackParsed?.parsedEvents ?? 0);
980
946
  const tokensUsed = aggregateSdkTokenUsage(usageSamples);
981
- const cancelled = stopReason === "aborted";
982
- const outputLimitExhausted = Boolean(options.outputLimitRecovery && stopReason === "length" && !committedTerminalObserved && !timedOut && !stderr);
983
- const failureCategory = !terminationConfirmed ? "termination-unconfirmed" : stopReason === "aborted" ? "cancelled" : outputLimitExhausted ? "output-limit" : providerBudgetFailure ? (providerBudgetFailure.includes("REQUEST_ABORTED") ? "interrupted" : providerBudgetFailure.startsWith("FRONTEND_CONTEXT_ESTIMATE_EXCEEDED") ? "context-budget-exhausted" : "budget_breach") : terminationConfirmed
947
+ const failureCategory = !terminationConfirmed ? "termination-unconfirmed" : providerBudgetFailure ? (providerBudgetFailure.includes("REQUEST_ABORTED") ? "interrupted" : providerBudgetFailure.startsWith("FRONTEND_CONTEXT_ESTIMATE_EXCEEDED") ? "context-budget-exhausted" : "budget_breach") : terminationConfirmed
984
948
  ? classifyPiFailure({
985
949
  assistantText,
986
950
  exitCode: timedOut ? 1 : stderr ? 1 : 0,
@@ -995,14 +959,12 @@ async function executeSingleSdkAttemptInternal(options) {
995
959
  backend: "sdk",
996
960
  command: ["pi-sdk", ...piSdkArgs],
997
961
  durationMs,
998
- exitCode: timedOut || stderr || outputLimitExhausted || cancelled ? 1 : 0,
962
+ exitCode: timedOut || stderr ? 1 : 0,
999
963
  failureCategory,
1000
964
  modelDisplay,
1001
965
  ok: terminationConfirmed &&
1002
966
  !timedOut &&
1003
967
  !stderr &&
1004
- !outputLimitExhausted &&
1005
- !cancelled &&
1006
968
  assistantText.length > 0 &&
1007
969
  !collected.outputTooLarge,
1008
970
  parsedEvents,
@@ -2329,7 +2329,7 @@ async function collectReviewTerminalKinds(runDir, reviewNodeId) {
2329
2329
  const kind = record.fact.kind;
2330
2330
  return typeof kind === "string" ? kind : "";
2331
2331
  })
2332
- .filter((kind) => kind === "approve_review" || kind === "request_review_changes");
2332
+ .filter(Boolean);
2333
2333
  }
2334
2334
  catch {
2335
2335
  return [];
@@ -2348,7 +2348,7 @@ async function collectDesignTerminalKinds(runDir, designNodeId) {
2348
2348
  const kind = record.fact.kind;
2349
2349
  return typeof kind === "string" ? kind : "";
2350
2350
  })
2351
- .filter((kind) => kind === "approve_design" || kind === "request_design_changes");
2351
+ .filter(Boolean);
2352
2352
  }
2353
2353
  catch {
2354
2354
  return [];
@@ -2974,7 +2974,6 @@ async function executeFrontendCloseout(input, meta) {
2974
2974
  const contract = frontendImplementationContractSchema.parse(JSON.parse(contractRaw));
2975
2975
  const trace = context.verificationTrace;
2976
2976
  const verificationTargets = (trace?.targets ?? []).map((target) => ({
2977
- mode: contract.verificationTargets.find((entry) => entry.id === target.id)?.mode ?? "static",
2978
2977
  status: target.status === "ok" ? "ok" : "failed",
2979
2978
  }));
2980
2979
  const mockEvidenceBound = contract.mockApi.strategy !== "not-needed" &&
@@ -94,43 +94,48 @@ const DAG_RUNS_PREFIX = ".harness/dag-runs/";
94
94
  * They are not product evidence and must not fail exclusive write guards.
95
95
  * Intentional report/product writes remain governed by writePolicy/writeSet.
96
96
  */
97
- const EPHEMERAL_CACHE_DIRS = [
97
+ export function isEphemeralToolCachePath(filePath) {
98
+ const normalized = normalizePath(filePath);
99
+ if (!normalized)
100
+ return false;
98
101
  // CodeGraph extension sync side effect (plan 2026-08-21 D7): a loaded
99
102
  // pi-codegraph extension keeps an existing .codegraph/ index fresh at
100
103
  // session start; those index files are extension-owned run state, not
101
104
  // writer output, so they never count as write-guard violations.
102
- ".codegraph",
103
- ".pytest_cache",
104
- ".mypy_cache",
105
- ".ruff_cache",
105
+ if (normalized === ".codegraph" || normalized.startsWith(".codegraph/")) {
106
+ return true;
107
+ }
108
+ if (normalized === ".pytest_cache" ||
109
+ normalized.startsWith(".pytest_cache/")) {
110
+ return true;
111
+ }
112
+ if (normalized === ".mypy_cache" || normalized.startsWith(".mypy_cache/")) {
113
+ return true;
114
+ }
115
+ if (normalized === ".ruff_cache" || normalized.startsWith(".ruff_cache/")) {
116
+ return true;
117
+ }
106
118
  // playwright-cli default session dumps (console/page/network) under repo cwd.
107
119
  // Real browser case evidence must still be written under testcase/** explicitly.
108
- ".playwright-cli",
109
- ];
110
- const EPHEMERAL_CACHE_DIR_PREFIXES = [
120
+ if (normalized === ".playwright-cli" ||
121
+ normalized.startsWith(".playwright-cli/")) {
122
+ return true;
123
+ }
124
+ if (normalized === ".coverage" || normalized.startsWith(".coverage.")) {
125
+ return true;
126
+ }
111
127
  // vite/vitest runtime caches live under node_modules/.vite (e.g.
112
128
  // node_modules/.vite/vitest/results.json). node_modules is the gitignored
113
129
  // dependency tree — tool cache writes there are never a writer's product
114
130
  // change and must not trip the write guard.
115
- "node_modules/.vite",
116
- ];
117
- const EPHEMERAL_CACHE_FILES = new Set([".coverage"]);
118
- const EPHEMERAL_CACHE_PATH_PATTERNS = [
119
- /(?:^|\/)__pycache__(?:\/|$)/,
120
- /\.(?:pyc|pyo)$/,
121
- ];
122
- function isExactOrChildPath(value, names) {
123
- return names.some((name) => value === name || value.startsWith(`${name}/`));
124
- }
125
- export function isEphemeralToolCachePath(filePath) {
126
- const normalized = normalizePath(filePath);
127
- if (!normalized)
128
- return false;
129
- return (isExactOrChildPath(normalized, EPHEMERAL_CACHE_DIRS) ||
130
- EPHEMERAL_CACHE_DIR_PREFIXES.some((prefix) => normalized.startsWith(`${prefix}/`)) ||
131
- EPHEMERAL_CACHE_FILES.has(normalized) ||
132
- normalized.startsWith(".coverage.") ||
133
- EPHEMERAL_CACHE_PATH_PATTERNS.some((pattern) => pattern.test(normalized)));
131
+ if (normalized.startsWith("node_modules/.vite/")) {
132
+ return true;
133
+ }
134
+ if (/(?:^|\/)__pycache__(?:\/|$)/.test(normalized))
135
+ return true;
136
+ if (/\.(?:pyc|pyo)$/.test(normalized))
137
+ return true;
138
+ return false;
134
139
  }
135
140
  /** MVP post-run guard: only paths that changed during the shell node are checked. */
136
141
  export function validateShellWriteGuard(input) {
@@ -1,4 +1,3 @@
1
- import { shouldRewriteAssistantTransientProviderError } from "../pi-provider-recovery.js";
2
1
  import { buildOverflowCheckpoint } from "./checkpoint.js";
3
2
  import { shouldRewriteAssistantOverflow } from "./overflow.js";
4
3
  import { clearRetrieveBypass, compressProviderPayload, createSiftRetrieveToolDefinition, SIFT_RETRIEVE_TOOL_NAME, } from "./sift-bridge.js";
@@ -12,44 +11,35 @@ export function createCompactionController() {
12
11
  export function createContextPressureExtension(options) {
13
12
  const l2 = options.policy.mode !== "off";
14
13
  const l1 = Boolean(options.sift);
15
- const transientRecovery = options.normalizeTransientProviderErrors === true;
16
- if (!l2 && !l1 && !transientRecovery)
14
+ if (!l2 && !l1)
17
15
  return undefined;
18
16
  return {
19
17
  name: options.name ?? "operator-chat-context-pressure",
20
18
  hidden: true,
21
19
  factory: (api) => {
22
- if (l2 || transientRecovery) {
20
+ if (l2) {
23
21
  api.on("message_end", (event) => {
24
22
  const message = event.message;
25
23
  if (!message || typeof message !== "object")
26
24
  return undefined;
27
25
  const rec = message;
28
- if (l2) {
29
- if (typeof rec.errorMessage === "string") {
30
- options.controller.lastError = rec.errorMessage;
31
- }
32
- if (rec.role === "assistant") {
33
- clearRetrieveBypassIfPresent(options.sift);
34
- }
35
- const rewritten = shouldRewriteAssistantOverflow(message);
36
- if (rewritten) {
37
- incrementCounter(options.controller.counters, "overflow");
38
- return {
39
- message: {
40
- ...rec,
41
- errorMessage: rewritten,
42
- },
43
- };
44
- }
26
+ if (typeof rec.errorMessage === "string") {
27
+ options.controller.lastError = rec.errorMessage;
45
28
  }
46
- if (transientRecovery) {
47
- return shouldRewriteAssistantTransientProviderError(message);
29
+ if (rec.role === "assistant") {
30
+ clearRetrieveBypassIfPresent(options.sift);
48
31
  }
49
- return undefined;
32
+ const rewritten = shouldRewriteAssistantOverflow(message);
33
+ if (!rewritten)
34
+ return undefined;
35
+ incrementCounter(options.controller.counters, "overflow");
36
+ return {
37
+ message: {
38
+ ...rec,
39
+ errorMessage: rewritten,
40
+ },
41
+ };
50
42
  });
51
- }
52
- if (l2) {
53
43
  api.on("session_before_compact", (event) => {
54
44
  const reason = event.reason;
55
45
  if (reason === "overflow") {
@@ -21,43 +21,3 @@ export const PI_RECOMMENDED_RETRY = {
21
21
  },
22
22
  };
23
23
  export const PI_RECOMMENDED_COMPACTION = { enabled: true };
24
- function validBoolean(value) {
25
- return typeof value === "boolean";
26
- }
27
- function validNonNegativeNumber(value) {
28
- return typeof value === "number" && Number.isFinite(value) && value >= 0;
29
- }
30
- function preferredOptionalNonNegativeNumber(...values) {
31
- return values.find(validNonNegativeNumber);
32
- }
33
- function preferredValue(projectValue, globalValue, fallback, isValid) {
34
- if (isValid(projectValue))
35
- return projectValue;
36
- if (isValid(globalValue))
37
- return globalValue;
38
- return fallback;
39
- }
40
- /**
41
- * Apply Console-owned retry defaults without writing settings files.
42
- * Explicit project fields win over global fields, which win over defaults.
43
- */
44
- export function applyProcessRetryOverride(manager) {
45
- if (!manager?.applyOverrides)
46
- return { applied: false };
47
- const project = manager.getProjectSettings?.()?.retry;
48
- const global = manager.getGlobalSettings?.()?.retry;
49
- const currentProvider = manager.getProviderRetrySettings?.();
50
- const timeoutMs = preferredOptionalNonNegativeNumber(project?.provider?.timeoutMs, global?.provider?.timeoutMs, currentProvider?.timeoutMs);
51
- const retry = {
52
- enabled: preferredValue(project?.enabled, global?.enabled, PI_RECOMMENDED_RETRY.enabled, validBoolean),
53
- maxRetries: preferredValue(project?.maxRetries, global?.maxRetries, PI_RECOMMENDED_RETRY.maxRetries, validNonNegativeNumber),
54
- baseDelayMs: preferredValue(project?.baseDelayMs, global?.baseDelayMs, PI_RECOMMENDED_RETRY.baseDelayMs, validNonNegativeNumber),
55
- provider: {
56
- ...(timeoutMs === undefined ? {} : { timeoutMs }),
57
- maxRetries: preferredValue(project?.provider?.maxRetries, global?.provider?.maxRetries, PI_RECOMMENDED_RETRY.provider.maxRetries, validNonNegativeNumber),
58
- maxRetryDelayMs: preferredValue(project?.provider?.maxRetryDelayMs, global?.provider?.maxRetryDelayMs, PI_RECOMMENDED_RETRY.provider.maxRetryDelayMs, validNonNegativeNumber),
59
- },
60
- };
61
- manager.applyOverrides({ retry });
62
- return { applied: true, retry };
63
- }
@@ -0,0 +1,26 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { isFeatureStudyTask } from './reference-context.js';
4
+ export async function loadPrompt(repoRoot, step, taskConfig) {
5
+ const filename = resolvePromptFilename(step, taskConfig);
6
+ const promptPath = path.join(repoRoot, '.harness', 'prompts', filename);
7
+ try {
8
+ return await readFile(promptPath, 'utf-8');
9
+ }
10
+ catch {
11
+ if (filename !== `${step}.md`) {
12
+ return readFile(path.join(repoRoot, '.harness', 'prompts', `${step}.md`), 'utf-8');
13
+ }
14
+ throw new Error(`prompt not found: ${promptPath}`);
15
+ }
16
+ }
17
+ function resolvePromptFilename(step, taskConfig) {
18
+ if (!taskConfig || !isFeatureStudyTask(taskConfig)) {
19
+ return `${step}.md`;
20
+ }
21
+ if (step === 'analyze')
22
+ return 'feature-study-analyze.md';
23
+ if (step === 'plan')
24
+ return 'feature-study-plan.md';
25
+ return `${step}.md`;
26
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./config-types.js";
2
+ export * from "./workflow-state-types.js";
@@ -0,0 +1,106 @@
1
+ import path from "node:path";
2
+ import { safeParseTaskContractDraft } from "./schema.js";
3
+ import { getTaskContractPaths } from "./paths.js";
4
+ import { readSourceManifest } from "../source-references.js";
5
+ function isPathOutsideRepoHint(p) {
6
+ if (path.isAbsolute(p))
7
+ return true;
8
+ if (p.includes("\0"))
9
+ return true;
10
+ const normalized = p.replace(/\\/g, "/");
11
+ if (normalized.startsWith("../") || normalized.includes("/../"))
12
+ return true;
13
+ return false;
14
+ }
15
+ export async function validateTaskContractDraft(input) {
16
+ const errors = [];
17
+ const warnings = [];
18
+ const parsed = safeParseTaskContractDraft(input.draft);
19
+ if (!parsed.success) {
20
+ for (const issue of parsed.error.issues) {
21
+ errors.push({
22
+ code: issue.message.includes("schemaVersion") ||
23
+ issue.path[0] === "schemaVersion"
24
+ ? "UNSUPPORTED_SCHEMA_VERSION"
25
+ : "INVALID_INPUT",
26
+ path: issue.path.join("."),
27
+ message: issue.message,
28
+ });
29
+ }
30
+ return {
31
+ taskId: input.taskId,
32
+ valid: false,
33
+ errors,
34
+ warnings,
35
+ };
36
+ }
37
+ const draft = parsed.data;
38
+ if (draft.taskId !== input.taskId) {
39
+ errors.push({
40
+ code: "INVALID_INPUT",
41
+ path: "taskId",
42
+ message: `draft.taskId (${draft.taskId}) does not match task (${input.taskId})`,
43
+ });
44
+ }
45
+ for (const p of draft.constraints.allowedPaths) {
46
+ if (isPathOutsideRepoHint(p) && !p.includes("*")) {
47
+ // globs may be relative; absolute is out of repo
48
+ if (path.isAbsolute(p)) {
49
+ errors.push({
50
+ code: "PATH_OUTSIDE_REPO",
51
+ path: "constraints.allowedPaths",
52
+ message: `absolute path not allowed: ${p}`,
53
+ });
54
+ }
55
+ }
56
+ }
57
+ for (const p of draft.constraints.forbiddenPaths) {
58
+ if (path.isAbsolute(p)) {
59
+ errors.push({
60
+ code: "PATH_OUTSIDE_REPO",
61
+ path: "constraints.forbiddenPaths",
62
+ message: `absolute path not allowed: ${p}`,
63
+ });
64
+ }
65
+ }
66
+ if (draft.references && draft.references.length > 0) {
67
+ const paths = getTaskContractPaths(input.repoRoot, input.taskId);
68
+ const manifest = await readSourceManifest(paths.sourceDir);
69
+ if (!manifest) {
70
+ warnings.push({
71
+ code: "MISSING_SOURCE_MANIFEST",
72
+ message: "draft references present but source-manifest.json is missing; import-prd first for immutable evidence",
73
+ });
74
+ }
75
+ else {
76
+ for (const ref of draft.references) {
77
+ const normalized = ref.ref.replace(/\\/g, "/");
78
+ const hit = manifest.documents.some((doc) => {
79
+ const mat = doc.materializedPath.replace(/\\/g, "/");
80
+ return (mat === normalized ||
81
+ `source/${mat}` === normalized ||
82
+ normalized.endsWith(mat));
83
+ });
84
+ if (!hit) {
85
+ warnings.push({
86
+ code: "REFERENCE_NOT_IN_MANIFEST",
87
+ message: `reference not found in source-manifest: ${ref.ref}`,
88
+ });
89
+ }
90
+ }
91
+ }
92
+ }
93
+ if (draft.sourceResolutions &&
94
+ draft.sourceResolutions.some((r) => r.decidedBy !== "human")) {
95
+ warnings.push({
96
+ code: "NON_HUMAN_SOURCE_RESOLUTION",
97
+ message: "one or more sourceResolutions are not decidedBy=human; blocking conflicts require human resolution before apply",
98
+ });
99
+ }
100
+ return {
101
+ taskId: input.taskId,
102
+ valid: errors.length === 0,
103
+ errors,
104
+ warnings,
105
+ };
106
+ }
@@ -0,0 +1,3 @@
1
+ export { clearTaskGoal, consumeTaskGoalNotices, getTaskGoal, incrementGoalContinuationRuns, markTaskGoalComplete, recordTaskGoalProgress, setTaskGoal, updateTaskGoalStatus, } from "./runtime.js";
2
+ export * from "./goal-audit.js";
3
+ export * from "./goal-policy.js";
@@ -0,0 +1,12 @@
1
+ export * from "./ids.js";
2
+ export * from "./paths.js";
3
+ export * from "./source-state.js";
4
+ export * from "./lifecycle.js";
5
+ export * from "./goal.js";
6
+ export * from "./subagent-guidance.js";
7
+ export * from "./runtime.js";
8
+ export * from "./config.js";
9
+ export * from "./worktree.js";
10
+ export * as taskContract from "./contract/index.js";
11
+ export * as taskOperator from "./operator/index.js";
12
+ export * from "./contract/index.js";
@@ -0,0 +1 @@
1
+ export { createTask, getNextStep, getSupportedNextCommand, getTaskStatus, markStepCompleted, markTaskCompleted, markTaskFailed, markTaskRunning, markVerifyGoalPending, resolveTaskFlow, shouldRunRetrospective, } from "./runtime.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Compatibility re-export — canonical capabilities document lives in
3
+ * src/shared/operator (includes Console negotiation `dag` block).
4
+ * No independent projection / dual implementation.
5
+ */
6
+ export { buildOperatorCapabilitiesDocument, } from "../../shared/operator/capabilities.js";
@@ -0,0 +1,2 @@
1
+ /** Compatibility re-export — canonical envelope lives in src/shared/operator. */
2
+ export { buildOperatorResult, operatorSucceeded, operatorFailed, writeOperatorJson, processExitCodeForOutcome, } from "../../shared/operator/envelope.js";
@@ -0,0 +1,5 @@
1
+ /** Compatibility barrel — re-exports src/shared/operator for task-adjacent imports. */
2
+ export * from "./types.js";
3
+ export * from "./envelope.js";
4
+ export * from "./registry.js";
5
+ export * from "./capabilities.js";
@@ -0,0 +1,2 @@
1
+ /** Compatibility re-export — canonical registry lives in src/shared/operator. */
2
+ export { OPERATOR_OUTCOMES_V1, OPERATOR_ERROR_CODES_V1, OPERATOR_ERROR_CODE_SET, isOperatorErrorCode, } from "../../shared/operator/registry.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export { getTaskDir, getTaskPaths, listTaskSourceFiles, } from "./runtime.js";
@@ -69,7 +69,7 @@ const reviewResponseSchema = z.object({
69
69
  disposition: z.enum(FRAGMENT_DISPOSITIONS).optional(),
70
70
  reason: z.string().min(1).optional(),
71
71
  })
72
- .nullish(),
72
+ .optional(),
73
73
  });
74
74
  /**
75
75
  * 模型队列构建镜像 semantic-intake.ts:MED→HIGH→LOW(harness executors.pi)+
@@ -134,7 +134,7 @@ function buildReviewPrompt(request) {
134
134
  "- approved: true only when the exclusion/conflict handling is justified.",
135
135
  "- fragmentId: MUST equal the requested fragment id exactly (echo the `fragment id:` line verbatim); identity mismatch is rejected.",
136
136
  "- reason: recommended; explain the verdict.",
137
- "- revision: omit or use null when no revision is proposed; otherwise one bounded revision {disposition, reason}. Never invent requirements or human resolutions.",
137
+ "- revision: optional single bounded revision {disposition, reason}; never invent requirements or human resolutions.",
138
138
  JSON.stringify({
139
139
  fragmentId: "REQ-SRC-AB12CD34EF56",
140
140
  approved: true,