@pellux/goodvibes-sdk 0.34.2 → 0.36.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 (156) hide show
  1. package/README.md +11 -1
  2. package/dist/contracts/artifacts/operator-contract.json +4 -1
  3. package/dist/events/agents.d.ts +14 -0
  4. package/dist/events/agents.d.ts.map +1 -1
  5. package/dist/events/index.d.ts +1 -1
  6. package/dist/events/index.d.ts.map +1 -1
  7. package/dist/events/turn.d.ts +17 -0
  8. package/dist/events/turn.d.ts.map +1 -1
  9. package/dist/events/workflows.d.ts +8 -0
  10. package/dist/events/workflows.d.ts.map +1 -1
  11. package/dist/platform/acp/connection.d.ts +16 -0
  12. package/dist/platform/acp/connection.d.ts.map +1 -1
  13. package/dist/platform/acp/connection.js +17 -0
  14. package/dist/platform/agents/orchestrator-runner.d.ts +1 -1
  15. package/dist/platform/agents/orchestrator-runner.d.ts.map +1 -1
  16. package/dist/platform/agents/orchestrator-runner.js +20 -24
  17. package/dist/platform/agents/orchestrator.d.ts.map +1 -1
  18. package/dist/platform/agents/orchestrator.js +3 -2
  19. package/dist/platform/agents/worktree.d.ts +11 -1
  20. package/dist/platform/agents/worktree.d.ts.map +1 -1
  21. package/dist/platform/agents/worktree.js +48 -14
  22. package/dist/platform/agents/wrfc-config.d.ts +23 -0
  23. package/dist/platform/agents/wrfc-config.d.ts.map +1 -1
  24. package/dist/platform/agents/wrfc-config.js +24 -0
  25. package/dist/platform/agents/wrfc-controller.d.ts +49 -1
  26. package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
  27. package/dist/platform/agents/wrfc-controller.js +271 -16
  28. package/dist/platform/agents/wrfc-types.d.ts +48 -1
  29. package/dist/platform/agents/wrfc-types.d.ts.map +1 -1
  30. package/dist/platform/automation/manager-runtime-delivery.d.ts +1 -1
  31. package/dist/platform/automation/manager-runtime-delivery.d.ts.map +1 -1
  32. package/dist/platform/automation/manager-runtime-delivery.js +4 -3
  33. package/dist/platform/channels/policy-manager.d.ts +8 -0
  34. package/dist/platform/channels/policy-manager.d.ts.map +1 -1
  35. package/dist/platform/channels/policy-manager.js +43 -1
  36. package/dist/platform/companion/companion-chat-manager.d.ts +1 -0
  37. package/dist/platform/companion/companion-chat-manager.d.ts.map +1 -1
  38. package/dist/platform/companion/companion-chat-manager.js +10 -0
  39. package/dist/platform/companion/companion-chat-routes.js +1 -0
  40. package/dist/platform/companion/companion-chat-types.d.ts +1 -0
  41. package/dist/platform/companion/companion-chat-types.d.ts.map +1 -1
  42. package/dist/platform/config/schema-domain-core.d.ts +3 -0
  43. package/dist/platform/config/schema-domain-core.d.ts.map +1 -1
  44. package/dist/platform/config/schema-domain-core.js +24 -0
  45. package/dist/platform/config/schema-types.d.ts +6 -2
  46. package/dist/platform/config/schema-types.d.ts.map +1 -1
  47. package/dist/platform/control-plane/approval-broker.d.ts +7 -0
  48. package/dist/platform/control-plane/approval-broker.d.ts.map +1 -1
  49. package/dist/platform/control-plane/approval-broker.js +30 -0
  50. package/dist/platform/control-plane/method-catalog-channels.d.ts.map +1 -1
  51. package/dist/platform/control-plane/method-catalog-channels.js +2 -0
  52. package/dist/platform/control-plane/method-catalog-email.d.ts.map +1 -1
  53. package/dist/platform/control-plane/method-catalog-email.js +1 -0
  54. package/dist/platform/core/compaction-sections.d.ts.map +1 -1
  55. package/dist/platform/core/compaction-sections.js +16 -5
  56. package/dist/platform/core/compaction-types.d.ts +7 -1
  57. package/dist/platform/core/compaction-types.d.ts.map +1 -1
  58. package/dist/platform/core/compaction-types.js +6 -0
  59. package/dist/platform/core/context-compaction.d.ts +20 -4
  60. package/dist/platform/core/context-compaction.d.ts.map +1 -1
  61. package/dist/platform/core/context-compaction.js +34 -7
  62. package/dist/platform/core/conversation-utils.js +1 -1
  63. package/dist/platform/core/conversation.d.ts.map +1 -1
  64. package/dist/platform/core/conversation.js +9 -4
  65. package/dist/platform/core/orchestrator-turn-loop.d.ts.map +1 -1
  66. package/dist/platform/core/orchestrator-turn-loop.js +22 -3
  67. package/dist/platform/core/orchestrator.d.ts +12 -0
  68. package/dist/platform/core/orchestrator.d.ts.map +1 -1
  69. package/dist/platform/core/orchestrator.js +36 -8
  70. package/dist/platform/daemon/http/home-graph-routes.d.ts.map +1 -1
  71. package/dist/platform/daemon/http/home-graph-routes.js +2 -1
  72. package/dist/platform/daemon/http/project-planning-routes.d.ts.map +1 -1
  73. package/dist/platform/daemon/http/project-planning-routes.js +2 -1
  74. package/dist/platform/hooks/dispatcher.d.ts.map +1 -1
  75. package/dist/platform/hooks/dispatcher.js +4 -0
  76. package/dist/platform/hooks/runners/command.d.ts.map +1 -1
  77. package/dist/platform/hooks/runners/command.js +11 -2
  78. package/dist/platform/integrations/delivery.d.ts.map +1 -1
  79. package/dist/platform/integrations/delivery.js +0 -2
  80. package/dist/platform/intelligence/import-graph.d.ts.map +1 -1
  81. package/dist/platform/intelligence/import-graph.js +8 -0
  82. package/dist/platform/knowledge/extractors.d.ts.map +1 -1
  83. package/dist/platform/knowledge/extractors.js +30 -4
  84. package/dist/platform/mcp/client.d.ts +2 -0
  85. package/dist/platform/mcp/client.d.ts.map +1 -1
  86. package/dist/platform/mcp/client.js +17 -3
  87. package/dist/platform/mcp/registry.d.ts.map +1 -1
  88. package/dist/platform/mcp/registry.js +4 -0
  89. package/dist/platform/media/builtin-generation-providers.d.ts.map +1 -1
  90. package/dist/platform/media/builtin-generation-providers.js +2 -3
  91. package/dist/platform/media/provider-registry.d.ts.map +1 -1
  92. package/dist/platform/media/provider-registry.js +3 -1
  93. package/dist/platform/multimodal/service.d.ts.map +1 -1
  94. package/dist/platform/multimodal/service.js +18 -3
  95. package/dist/platform/providers/anthropic-compat.js +2 -2
  96. package/dist/platform/providers/anthropic-sdk-provider.js +1 -1
  97. package/dist/platform/providers/anthropic-sse-assembler.d.ts.map +1 -1
  98. package/dist/platform/providers/anthropic-sse-assembler.js +24 -0
  99. package/dist/platform/providers/anthropic.js +2 -2
  100. package/dist/platform/providers/capabilities.d.ts +6 -0
  101. package/dist/platform/providers/capabilities.d.ts.map +1 -1
  102. package/dist/platform/providers/capabilities.js +20 -1
  103. package/dist/platform/providers/gemini.d.ts.map +1 -1
  104. package/dist/platform/providers/gemini.js +57 -48
  105. package/dist/platform/providers/index.d.ts +1 -0
  106. package/dist/platform/providers/index.d.ts.map +1 -1
  107. package/dist/platform/providers/index.js +1 -0
  108. package/dist/platform/providers/interface.d.ts +9 -1
  109. package/dist/platform/providers/interface.d.ts.map +1 -1
  110. package/dist/platform/providers/llama-cpp.js +1 -1
  111. package/dist/platform/providers/lm-studio.js +1 -1
  112. package/dist/platform/providers/model-limits.d.ts.map +1 -1
  113. package/dist/platform/providers/model-limits.js +14 -2
  114. package/dist/platform/providers/ollama.js +1 -1
  115. package/dist/platform/providers/openai-codex.js +1 -1
  116. package/dist/platform/providers/openai-compat.d.ts.map +1 -1
  117. package/dist/platform/providers/openai-compat.js +2 -2
  118. package/dist/platform/providers/openai.js +2 -2
  119. package/dist/platform/runtime/emitters/agents.d.ts +2 -0
  120. package/dist/platform/runtime/emitters/agents.d.ts.map +1 -1
  121. package/dist/platform/runtime/emitters/turn.d.ts +9 -0
  122. package/dist/platform/runtime/emitters/turn.d.ts.map +1 -1
  123. package/dist/platform/runtime/emitters/turn.js +4 -0
  124. package/dist/platform/runtime/emitters/workflows.d.ts +1 -0
  125. package/dist/platform/runtime/emitters/workflows.d.ts.map +1 -1
  126. package/dist/platform/runtime/store/domains/agents.d.ts +7 -0
  127. package/dist/platform/runtime/store/domains/agents.d.ts.map +1 -1
  128. package/dist/platform/runtime/store/domains/conversation.d.ts +0 -2
  129. package/dist/platform/runtime/store/domains/conversation.d.ts.map +1 -1
  130. package/dist/platform/runtime/store/domains/conversation.js +0 -1
  131. package/dist/platform/runtime/store/helpers/reducers/conversation.d.ts.map +1 -1
  132. package/dist/platform/runtime/store/helpers/reducers/conversation.js +20 -2
  133. package/dist/platform/runtime/store/helpers/reducers/lifecycle.d.ts.map +1 -1
  134. package/dist/platform/runtime/store/helpers/reducers/lifecycle.js +6 -0
  135. package/dist/platform/runtime/store/helpers/reducers/sync.d.ts.map +1 -1
  136. package/dist/platform/runtime/store/helpers/reducers/sync.js +7 -1
  137. package/dist/platform/runtime/tools/phases/execute.d.ts.map +1 -1
  138. package/dist/platform/runtime/tools/phases/execute.js +11 -1
  139. package/dist/platform/runtime/ui-read-models-core.d.ts.map +1 -1
  140. package/dist/platform/runtime/ui-read-models-core.js +4 -1
  141. package/dist/platform/tools/exec/runtime.d.ts.map +1 -1
  142. package/dist/platform/tools/exec/runtime.js +9 -0
  143. package/dist/platform/tools/shared/overflow.d.ts.map +1 -1
  144. package/dist/platform/tools/shared/overflow.js +21 -2
  145. package/dist/platform/types/errors.d.ts +26 -0
  146. package/dist/platform/types/errors.d.ts.map +1 -1
  147. package/dist/platform/types/errors.js +63 -1
  148. package/dist/platform/utils/error-display.d.ts.map +1 -1
  149. package/dist/platform/utils/error-display.js +14 -6
  150. package/dist/platform/utils/retry.d.ts +4 -2
  151. package/dist/platform/utils/retry.d.ts.map +1 -1
  152. package/dist/platform/utils/retry.js +4 -2
  153. package/dist/platform/version.js +1 -1
  154. package/dist/platform/voice/providers/shared.d.ts.map +1 -1
  155. package/dist/platform/voice/providers/shared.js +2 -3
  156. package/package.json +9 -9
@@ -1,3 +1,7 @@
1
+ const WRFC_COMMIT_SCOPES = ['off', 'scoped', 'all'];
2
+ function isWrfcCommitScope(value) {
3
+ return typeof value === 'string' && WRFC_COMMIT_SCOPES.includes(value);
4
+ }
1
5
  export function readWrfcConfig(configManager) {
2
6
  const wrfcConfig = configManager.getCategory('wrfc');
3
7
  // Number.isFinite (not typeof === 'number') so a NaN/Infinity config value is
@@ -6,6 +10,8 @@ export function readWrfcConfig(configManager) {
6
10
  const rawScore = configManager.get('wrfc.scoreThreshold');
7
11
  const rawMax = configManager.get('wrfc.maxFixAttempts');
8
12
  const rawHeartbeat = configManager.get('wrfc.agentHeartbeatTimeoutMs');
13
+ const rawTransportRetryLimit = configManager.get('wrfc.transportRetryLimit');
14
+ const rawTransportRetryDelayMs = configManager.get('wrfc.transportRetryDelayMs');
9
15
  return {
10
16
  scoreThreshold: Number.isFinite(rawScore)
11
17
  ? rawScore
@@ -16,15 +22,30 @@ export function readWrfcConfig(configManager) {
16
22
  autoCommit: typeof configManager.get('wrfc.autoCommit') === 'boolean'
17
23
  ? configManager.get('wrfc.autoCommit')
18
24
  : wrfcConfig?.autoCommit ?? false,
25
+ commitScope: isWrfcCommitScope(configManager.get('wrfc.commitScope'))
26
+ ? configManager.get('wrfc.commitScope')
27
+ : isWrfcCommitScope(wrfcConfig?.commitScope) ? wrfcConfig.commitScope : 'scoped',
19
28
  gates: Array.isArray(wrfcConfig?.gates) ? wrfcConfig.gates : [],
20
29
  agentHeartbeatTimeoutMs: Number.isFinite(rawHeartbeat)
21
30
  ? rawHeartbeat
22
31
  : Number.isFinite(wrfcConfig?.agentHeartbeatTimeoutMs) ? wrfcConfig?.agentHeartbeatTimeoutMs : 0,
32
+ transportRetryLimit: Number.isFinite(rawTransportRetryLimit)
33
+ ? rawTransportRetryLimit
34
+ : Number.isFinite(wrfcConfig?.transportRetryLimit) ? wrfcConfig?.transportRetryLimit : 1,
35
+ transportRetryDelayMs: Number.isFinite(rawTransportRetryDelayMs)
36
+ ? rawTransportRetryDelayMs
37
+ : Number.isFinite(wrfcConfig?.transportRetryDelayMs) ? wrfcConfig?.transportRetryDelayMs : 5_000,
23
38
  };
24
39
  }
25
40
  export function getWrfcAgentHeartbeatTimeoutMs(configManager) {
26
41
  return readWrfcConfig(configManager).agentHeartbeatTimeoutMs ?? 0;
27
42
  }
43
+ export function getWrfcTransportRetryLimit(configManager) {
44
+ return readWrfcConfig(configManager).transportRetryLimit ?? 1;
45
+ }
46
+ export function getWrfcTransportRetryDelayMs(configManager) {
47
+ return readWrfcConfig(configManager).transportRetryDelayMs ?? 5_000;
48
+ }
28
49
  export function getWrfcScoreThreshold(configManager) {
29
50
  return readWrfcConfig(configManager).scoreThreshold ?? 9.9;
30
51
  }
@@ -34,6 +55,9 @@ export function getWrfcMaxFixAttempts(configManager) {
34
55
  export function getWrfcAutoCommit(configManager) {
35
56
  return readWrfcConfig(configManager).autoCommit ?? false;
36
57
  }
58
+ export function getWrfcCommitScope(configManager) {
59
+ return readWrfcConfig(configManager).commitScope ?? 'scoped';
60
+ }
37
61
  export function getEnabledWrfcGates(configManager) {
38
62
  return readWrfcConfig(configManager).gates.filter((gate) => gate.enabled);
39
63
  }
@@ -139,6 +139,16 @@ export declare class WrfcController {
139
139
  private activeChildAgentId;
140
140
  private onAgentComplete;
141
141
  private onAgentFailed;
142
+ /**
143
+ * Respawn the most recently spawned child agent after a transport-classified
144
+ * failure, instead of failing the chain immediately. Bounded by
145
+ * wrfc.transportRetryLimit (default 1) and tracked via chain.transportRetryCount,
146
+ * kept separate from fixAttempts/reviewCycles so a transport blip never counts
147
+ * against the ordinary fix-cycle budget.
148
+ */
149
+ private retryTransportFailure;
150
+ /** Point the chain's role-specific agent-id field at a freshly (re)spawned child. */
151
+ private rewireChainChildAgentId;
142
152
  private onAgentCancelled;
143
153
  private startReview;
144
154
  private processReview;
@@ -152,7 +162,21 @@ export declare class WrfcController {
152
162
  private checkAndRunGatesForAll;
153
163
  private autoCommit;
154
164
  private autoCommitCandidateAgentIds;
155
- private autoCommitMessage;
165
+ /**
166
+ * Chain-wide "own edit ledger": every path self-reported as created/modified/deleted by
167
+ * any engineer/fixer/integrator completion on this chain (including subtask completions),
168
+ * deduplicated. Primary source is chain.touchedPaths, an incremental accumulator appended
169
+ * to on every completion (see recordTouchedPaths) so fixer/re-fix passes and resumed
170
+ * chains are represented, not just the first pass. Falls back to deriving from the
171
+ * last-stored report slots (chain.engineerReport / chain.integratorReport /
172
+ * subtask.engineerReport) for chains serialized before touchedPaths existed.
173
+ *
174
+ * Self-reported, not ground truth — same accuracy ceiling as verifyEngineerClaims. Per-agent
175
+ * worktree isolation (AgentWorktree.create) is not wired up in this controller today, so
176
+ * there is no git-branch-diff signal to corroborate against.
177
+ */
178
+ private collectChainTouchedPaths;
179
+ private buildAutoCommitMessage;
156
180
  private failChain;
157
181
  private cancelRunningChildren;
158
182
  private hasRunningChild;
@@ -168,6 +192,16 @@ export declare class WrfcController {
168
192
  private createBaseChain;
169
193
  private startEngineeringChain;
170
194
  private startCompoundEngineeringChain;
195
+ /**
196
+ * Appends a completion report's self-reported filesCreated/filesModified/filesDeleted
197
+ * into the chain's running edit ledger (chain.touchedPaths). Called for every engineer,
198
+ * fixer, and integrator completion — not just the first pass — so a chain that goes
199
+ * through gate-fix or review-fix cycles still has the fixer's edits represented. This is
200
+ * why it is a standalone accumulator rather than reading the last-stored report field:
201
+ * chain.engineerReport / subtask.engineerReport are last-write slots that do not reliably
202
+ * retain every fixer pass (see collectChainTouchedPaths for the consuming side).
203
+ */
204
+ private recordTouchedPaths;
171
205
  private handleEngineerCompletion;
172
206
  private buildSubtaskEngineerTask;
173
207
  private buildCompoundIntegrationTask;
@@ -184,6 +218,20 @@ export declare class WrfcController {
184
218
  private withRouteReason;
185
219
  private completeChainAsPassed;
186
220
  private completeOwnerAgent;
221
+ /**
222
+ * Roll up token usage across every agent that has ever run under this
223
+ * chain (the owner plus all phase/subtask children, across every review
224
+ * and fix cycle — `chain.allAgentIds` already tracks the full roster for
225
+ * worktree cleanup, so it doubles as the usage-aggregation source). Each
226
+ * contributor's usage is added in, including the owner's own (normally
227
+ * zero, but summed rather than ignored in case it is ever populated
228
+ * directly). Optional fields (reasoningTokens/reasoningSummaryCount) are
229
+ * only included in the result if at least one contributor reported them,
230
+ * matching AgentUsage's undefined-means-no-data convention for those.
231
+ */
232
+ private aggregateChainUsage;
233
+ /** Roll up tool-call counts across every agent that has ever run under this chain. */
234
+ private aggregateChainToolCallCount;
187
235
  private upsertWrfcWorkPlanTask;
188
236
  private setWrfcWorkPlanTaskStatus;
189
237
  private enqueueWrfcWorkPlanTaskOperation;
@@ -1 +1 @@
1
- {"version":3,"file":"wrfc-controller.d.ts","sourceRoot":"","sources":["../../../src/platform/agents/wrfc-controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUnD,OAAO,KAAK,EAGV,SAAS,EACT,sBAAsB,EAMvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,KAAK,EACV,8BAA8B,EAE9B,8BAA8B,EAC/B,MAAM,wCAAwC,CAAC;AAUhD,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAoB1B,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEzG;;;GAGG;AACH,eAAO,MAAM,iCAAiC,IAAI,CAAC;AA4BnD,KAAK,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,mBAAmB,GAAG,aAAa,CAAC,CAAC,CAAC;AACpI,KAAK,mBAAmB,GAAG;IACzB,kBAAkB,CAAC,KAAK,EAAE,8BAA8B,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5E,kBAAkB,CAAC,KAAK,EAAE,8BAA8B,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7E,CAAC;AASF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAChD,yGAAyG;IACzG,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAU;IACtD,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyC;IACpE,OAAO,CAAC,WAAW,CAAuE;IAC1F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6C;IAC3E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgC;IACjE,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IACvE,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,4CAA4C;IAC5C,OAAO,CAAC,aAAa,CAA+C;gBAGlE,UAAU,EAAE,eAAe,EAC3B,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC,EAClD,IAAI,EAAE;QACJ,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;QACxC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,aAAa,CAAC,CAAC;QACnE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1C,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,eAAe,CAAC,GAAG,SAAS,CAAC;QAC9D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;QAC/D;;;;;WAKG;QACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;KAC1C;IAkBH,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS;IAyBhD,YAAY,IAAI,MAAM;IAEtB,UAAU,IAAI,WAAW;IAEzB,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,oBAAoB,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG,IAAI;IAIzF,aAAa,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAOhD,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI;IAIzE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAE3C,UAAU,IAAI,SAAS,EAAE;IAEzB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IA8ErC,qBAAqB,IAAI,MAAM;IAQ/B;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAW9C;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IA+ChD;;;;;;;;;OASG;IACH,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,UAAQ,GAAG,OAAO;IAkBrD,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,sBAAsB;IA2B9B;;gFAE4E;IAC5E;uGACmG;IACnG,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,oBAAoB;IAiC5B,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,cAAc;IAmDtB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAgBrB,2FAA2F;IAC3F,OAAO,CAAC,YAAY;IA0BpB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,2BAA2B;IAInC,wFAAwF;IACxF,OAAO,CAAC,kBAAkB;YAQZ,eAAe;IAiF7B,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,WAAW;YA8CL,aAAa;IA6G3B,OAAO,CAAC,QAAQ;YAiEF,QAAQ;IAuBtB,OAAO,CAAC,qBAAqB;IA4C7B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,0BAA0B;YAIpB,kBAAkB;IA8GhC,OAAO,CAAC,oBAAoB;YASd,sBAAsB;YAqDtB,UAAU;IA0DxB,OAAO,CAAC,2BAA2B;IA6BnC,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,SAAS;IA0CjB,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,WAAW;YAqCL,WAAW;IAyBzB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,cAAc;IAEtB,OAAO,CAAC,kBAAkB;IAE1B,OAAO,CAAC,mBAAmB;IA0C3B,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,eAAe;IA8CvB,OAAO,CAAC,qBAAqB;IAwC7B,OAAO,CAAC,6BAA6B;IA8CrC,OAAO,CAAC,wBAAwB;IAiIhC,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,4BAA4B;IA0BpC,OAAO,CAAC,oBAAoB;YAad,8BAA8B;IAsB5C,OAAO,CAAC,gCAAgC;IA6GxC,OAAO,CAAC,0BAA0B;YAqCpB,4BAA4B;IAiF1C,OAAO,CAAC,uBAAuB;IAiD/B,OAAO,CAAC,gBAAgB;IA+BxB,OAAO,CAAC,0BAA0B;IAalC,OAAO,CAAC,kCAAkC;IAiB1C,OAAO,CAAC,cAAc;IAuCtB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,sBAAsB;IAqD9B,OAAO,CAAC,yBAAyB;IA6BjC,OAAO,CAAC,gCAAgC;IAgCxC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,eAAe;CAKxB"}
1
+ {"version":3,"file":"wrfc-controller.d.ts","sourceRoot":"","sources":["../../../src/platform/agents/wrfc-controller.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,8 @@ import { completePlanItemsForAgent } from './wrfc-plan-sync.js';
9
9
  import { logger } from '../utils/logger.js';
10
10
  import { summarizeError } from '../utils/error-display.js';
11
11
  import { emitAgentCompleted, emitAgentFailed, emitAgentProgress, emitAgentRunning, emitWorkflowChainFailed, emitWorkflowFixAttempted, emitWorkflowReviewCompleted, } from '../runtime/emitters/index.js';
12
- import { getWrfcAutoCommit, getWrfcMaxFixAttempts, getWrfcScoreThreshold, getWrfcAgentHeartbeatTimeoutMs, } from './wrfc-config.js';
12
+ import { getWrfcAutoCommit, getWrfcCommitScope, getWrfcMaxFixAttempts, getWrfcScoreThreshold, getWrfcAgentHeartbeatTimeoutMs, getWrfcTransportRetryLimit, getWrfcTransportRetryDelayMs, } from './wrfc-config.js';
13
+ import { isTransportFailureMessage } from '../types/errors.js';
13
14
  import { buildEngineerConstraintAddendum, } from './wrfc-prompt-addenda.js';
14
15
  import { completeWrfcOrchestrationNode, createWrfcWorkflowContext, emitWrfcAutoCommitted, emitWrfcCascadeAbort, emitWrfcChainCreated, emitWrfcChainPassed, emitWrfcConstraintsEnumerated, emitWrfcGraphCreated, emitWrfcScoreRegression, emitWrfcStateChanged, failWrfcOrchestrationNode, startWrfcOrchestrationNode, } from './wrfc-runtime-events.js';
15
16
  import { runWrfcGateChecks } from './wrfc-gate-runtime.js';
@@ -622,7 +623,10 @@ export class WrfcController {
622
623
  const chain = this.findChainByAgentId(agentId);
623
624
  if (!chain)
624
625
  return;
625
- if (agentId === chain.ownerAgentId && isChainTerminal(chain.state))
626
+ // A non-owner child failure on an already-terminal chain must be a no-op:
627
+ // mirrors onAgentCancelled and prevents duplicate WORKFLOW_CHAIN_FAILED events
628
+ // and passed→failed flips when a late/second child failure arrives.
629
+ if (isChainTerminal(chain.state))
626
630
  return;
627
631
  if (agentId === chain.ownerAgentId) {
628
632
  this.keepOwnerAgentActive(chain, 'Ignored premature owner failure event; WRFC lifecycle is still active');
@@ -637,8 +641,69 @@ export class WrfcController {
637
641
  });
638
642
  return;
639
643
  }
640
- this.setWrfcWorkPlanTaskStatus(chain, agentId, 'failed', errorMessage ?? `Agent ${agentId} failed`);
641
- this.failChain(chain, errorMessage ?? `Agent ${agentId} failed`);
644
+ const reason = errorMessage ?? `Agent ${agentId} failed`;
645
+ // A transport-classified failure of the most recently spawned child gets one
646
+ // bounded automatic retry (respawn same role/task) before the chain is failed
647
+ // outright — see retryTransportFailure. Guarding on lastChildSpawn.agentId
648
+ // matching this exact agentId avoids acting on a stale/duplicate failure event
649
+ // for an agent that isn't the chain's current active child.
650
+ if (chain.lastChildSpawn?.agentId === agentId &&
651
+ isTransportFailureMessage(reason) &&
652
+ (chain.transportRetryCount ?? 0) < getWrfcTransportRetryLimit(this.configManager)) {
653
+ this.retryTransportFailure(chain, chain.lastChildSpawn, reason);
654
+ return;
655
+ }
656
+ this.setWrfcWorkPlanTaskStatus(chain, agentId, 'failed', reason);
657
+ this.failChain(chain, reason, isTransportFailureMessage(reason) ? 'transport' : 'other');
658
+ }
659
+ /**
660
+ * Respawn the most recently spawned child agent after a transport-classified
661
+ * failure, instead of failing the chain immediately. Bounded by
662
+ * wrfc.transportRetryLimit (default 1) and tracked via chain.transportRetryCount,
663
+ * kept separate from fixAttempts/reviewCycles so a transport blip never counts
664
+ * against the ordinary fix-cycle budget.
665
+ */
666
+ retryTransportFailure(chain, spawn, reason) {
667
+ const limit = getWrfcTransportRetryLimit(this.configManager);
668
+ const delayMs = getWrfcTransportRetryDelayMs(this.configManager);
669
+ chain.transportRetryCount = (chain.transportRetryCount ?? 0) + 1;
670
+ const attempt = chain.transportRetryCount;
671
+ this.appendOwnerDecision(chain, 'transport_retry', `Transport failure on ${spawn.role} (attempt ${attempt}/${limit}): ${reason}. Retrying in ${Math.round(delayMs / 1000)}s.`, { agentId: spawn.agentId, role: spawn.role });
672
+ logger.warn('WrfcController: transport failure, retrying child agent', {
673
+ chainId: chain.id,
674
+ agentId: spawn.agentId,
675
+ role: spawn.role,
676
+ attempt,
677
+ limit,
678
+ reason,
679
+ });
680
+ const timer = setTimeout(() => {
681
+ // The chain may have reached a terminal state while this retry was waiting
682
+ // (e.g. cancelled) — don't resurrect it.
683
+ if (isChainTerminal(chain.state))
684
+ return;
685
+ const record = this.spawnWrfcAgent(chain, spawn.role, spawn.template, spawn.task, spawn.dangerouslyDisableWrfc, spawn.subtaskId);
686
+ this.registerSpawnedChild(chain, record, spawn.role, spawn.subtaskId);
687
+ this.rewireChainChildAgentId(chain, spawn.role, record.id);
688
+ }, delayMs);
689
+ timer.unref?.();
690
+ }
691
+ /** Point the chain's role-specific agent-id field at a freshly (re)spawned child. */
692
+ rewireChainChildAgentId(chain, role, agentId) {
693
+ switch (role) {
694
+ case 'engineer':
695
+ chain.engineerAgentId = agentId;
696
+ break;
697
+ case 'reviewer':
698
+ chain.reviewerAgentId = agentId;
699
+ break;
700
+ case 'fixer':
701
+ chain.fixerAgentId = agentId;
702
+ break;
703
+ case 'integrator':
704
+ chain.integratorAgentId = agentId;
705
+ break;
706
+ }
642
707
  }
643
708
  onAgentCancelled(agentId, reason) {
644
709
  const chain = this.findChainByAgentId(agentId);
@@ -749,9 +814,12 @@ export class WrfcController {
749
814
  }
750
815
  const maxFixAttempts = getWrfcMaxFixAttempts(this.configManager);
751
816
  if (chain.fixAttempts >= maxFixAttempts) {
752
- const failureReason = constraintFailure && review.score >= threshold
753
- ? `Unsatisfied constraints [${unsatisfiedConstraintIds.join(',')}] after ${chain.fixAttempts} fix attempt${chain.fixAttempts !== 1 ? 's' : ''}`
754
- : `Score ${review.score}/10 below threshold ${threshold}/10 after ${chain.fixAttempts} fix attempt${chain.fixAttempts !== 1 ? 's' : ''} — below threshold`;
817
+ const attemptsLabel = `${chain.fixAttempts} fix attempt${chain.fixAttempts !== 1 ? 's' : ''}`;
818
+ const failureReason = chain.claimsVerified === false && review.score >= threshold && !constraintFailure
819
+ ? `Engineer/fixer claims could not be verified on disk (suspected phantom work) after ${attemptsLabel}`
820
+ : constraintFailure && review.score >= threshold
821
+ ? `Unsatisfied constraints [${unsatisfiedConstraintIds.join(',')}] after ${attemptsLabel}`
822
+ : `Score ${review.score}/10 below threshold ${threshold}/10 after ${attemptsLabel} — below threshold`;
755
823
  this.failChain(chain, failureReason);
756
824
  return;
757
825
  }
@@ -988,6 +1056,14 @@ export class WrfcController {
988
1056
  }
989
1057
  async autoCommit(chain) {
990
1058
  this.transition(chain, 'committing');
1059
+ const commitScope = getWrfcCommitScope(this.configManager);
1060
+ if (commitScope === 'off') {
1061
+ logger.debug('WrfcController.autoCommit: wrfc.commitScope is off, skipping commit and merge entirely', {
1062
+ chainId: chain.id,
1063
+ });
1064
+ this.completeChainAsPassed(chain);
1065
+ return;
1066
+ }
991
1067
  const commitCandidateIds = this.autoCommitCandidateAgentIds(chain);
992
1068
  if (commitCandidateIds.length === 0) {
993
1069
  this.failChain(chain, 'autoCommit: no write-capable WRFC agent found on chain');
@@ -1001,10 +1077,28 @@ export class WrfcController {
1001
1077
  const worktree = this.createWorktree();
1002
1078
  let completed = false;
1003
1079
  try {
1004
- const commitMessage = this.autoCommitMessage(chain);
1005
- const directCommitHash = worktree.commitWorkingTree
1006
- ? await worktree.commitWorkingTree(commitMessage)
1007
- : null;
1080
+ const commitMessage = this.buildAutoCommitMessage(chain, commitScope);
1081
+ let directCommitHash = null;
1082
+ if (worktree.commitWorkingTree) {
1083
+ if (commitScope === 'scoped') {
1084
+ const touchedPaths = this.collectChainTouchedPaths(chain);
1085
+ if (touchedPaths.length === 0) {
1086
+ // Do NOT fall back to a full-tree `--all` sweep here — an empty self-reported
1087
+ // ledger means we genuinely don't know what this chain touched, and committing
1088
+ // everything dirty in the working tree is exactly the trust bug this fixes.
1089
+ logger.warn('WrfcController.autoCommit: commitScope is scoped but the chain edit ledger is empty; skipping commit rather than falling back to a full-tree sweep', {
1090
+ chainId: chain.id,
1091
+ });
1092
+ }
1093
+ else {
1094
+ directCommitHash = await worktree.commitWorkingTree(commitMessage, touchedPaths);
1095
+ }
1096
+ }
1097
+ else {
1098
+ // commitScope === 'all': legacy full-tree sweep, no paths argument.
1099
+ directCommitHash = await worktree.commitWorkingTree(commitMessage);
1100
+ }
1101
+ }
1008
1102
  let mergedCount = 0;
1009
1103
  for (const agentId of commitCandidateIds) {
1010
1104
  if (await worktree.merge(agentId)) {
@@ -1068,11 +1162,62 @@ export class WrfcController {
1068
1162
  }
1069
1163
  return candidates;
1070
1164
  }
1071
- autoCommitMessage(chain) {
1072
- const firstLine = chain.task.trim().replace(/\s+/g, ' ').slice(0, 72) || chain.id;
1073
- return `WRFC: ${firstLine}`;
1165
+ /**
1166
+ * Chain-wide "own edit ledger": every path self-reported as created/modified/deleted by
1167
+ * any engineer/fixer/integrator completion on this chain (including subtask completions),
1168
+ * deduplicated. Primary source is chain.touchedPaths, an incremental accumulator appended
1169
+ * to on every completion (see recordTouchedPaths) so fixer/re-fix passes and resumed
1170
+ * chains are represented, not just the first pass. Falls back to deriving from the
1171
+ * last-stored report slots (chain.engineerReport / chain.integratorReport /
1172
+ * subtask.engineerReport) for chains serialized before touchedPaths existed.
1173
+ *
1174
+ * Self-reported, not ground truth — same accuracy ceiling as verifyEngineerClaims. Per-agent
1175
+ * worktree isolation (AgentWorktree.create) is not wired up in this controller today, so
1176
+ * there is no git-branch-diff signal to corroborate against.
1177
+ */
1178
+ collectChainTouchedPaths(chain) {
1179
+ const paths = new Set(chain.touchedPaths ?? []);
1180
+ const fallbackReports = [
1181
+ chain.engineerReport,
1182
+ chain.integratorReport,
1183
+ ...(chain.subtasks ?? []).map((subtask) => subtask.engineerReport),
1184
+ ];
1185
+ for (const report of fallbackReports) {
1186
+ if (report && report.archetype === 'engineer') {
1187
+ const engineerReport = report;
1188
+ for (const path of [...engineerReport.filesCreated, ...engineerReport.filesModified, ...engineerReport.filesDeleted]) {
1189
+ paths.add(path);
1190
+ }
1191
+ }
1192
+ }
1193
+ return Array.from(paths);
1194
+ }
1195
+ buildAutoCommitMessage(chain, commitScope) {
1196
+ const fullTask = chain.task.trim();
1197
+ const firstLine = fullTask.replace(/\s+/g, ' ').slice(0, 72) || chain.id;
1198
+ const subject = `WRFC: ${firstLine}`;
1199
+ // Subject is length-capped for git log readability; the body below is never truncated —
1200
+ // this is the fix for the "anything past 72 characters is silently discarded" bug.
1201
+ const bodyLines = ['', fullTask || chain.id];
1202
+ if (chain.constraints.length > 0) {
1203
+ bodyLines.push('', `Constraints: ${chain.constraints.length}`);
1204
+ }
1205
+ const gateNames = (chain.gateResults ?? []).map((result) => result.gate);
1206
+ if (gateNames.length > 0) {
1207
+ bodyLines.push(`Gates: ${gateNames.join(', ')}`);
1208
+ }
1209
+ if (chain.subtasks && chain.subtasks.length > 0) {
1210
+ bodyLines.push(`Subtasks: ${chain.subtasks.map((subtask) => subtask.title).join('; ')}`);
1211
+ }
1212
+ if (commitScope === 'scoped') {
1213
+ const touchedPaths = this.collectChainTouchedPaths(chain);
1214
+ bodyLines.push(touchedPaths.length > 0
1215
+ ? `Staged paths (${touchedPaths.length}): ${touchedPaths.join(', ')}`
1216
+ : 'Staged paths: (none — chain edit ledger empty)');
1217
+ }
1218
+ return [subject, ...bodyLines].join('\n');
1074
1219
  }
1075
- failChain(chain, reason) {
1220
+ failChain(chain, reason, failureKind = 'other') {
1076
1221
  if (chain.state === 'pending') {
1077
1222
  this.chainQueue = this.chainQueue.filter((queued) => queued.record.id !== chain.ownerAgentId);
1078
1223
  }
@@ -1094,6 +1239,7 @@ export class WrfcController {
1094
1239
  this.activeChainCount = Math.max(0, this.activeChainCount - 1);
1095
1240
  }
1096
1241
  chain.error = reason;
1242
+ chain.failureKind = failureKind;
1097
1243
  chain.completedAt = Date.now();
1098
1244
  this.setWrfcWorkPlanTaskStatus(chain, chain.ownerAgentId, 'failed', reason);
1099
1245
  this.cancelRunningChildren(chain);
@@ -1102,7 +1248,7 @@ export class WrfcController {
1102
1248
  });
1103
1249
  this.completeOwnerAgent(chain, 'failed', reason);
1104
1250
  this.workmap.append({ ts: new Date().toISOString(), wrfcId: chain.id, event: 'chain_failed', reason });
1105
- emitWorkflowChainFailed(this.runtimeBus, createWrfcWorkflowContext(this.sessionId, chain.id), { chainId: chain.id, reason });
1251
+ emitWorkflowChainFailed(this.runtimeBus, createWrfcWorkflowContext(this.sessionId, chain.id), { chainId: chain.id, reason, failureKind });
1106
1252
  logger.error('WrfcController.failChain', { chainId: chain.id, reason });
1107
1253
  this.scheduleChainCleanup(chain);
1108
1254
  this.safeDequeueNext();
@@ -1271,7 +1417,9 @@ export class WrfcController {
1271
1417
  ownerTerminalEmitted: false,
1272
1418
  constraints: [],
1273
1419
  constraintsEnumerated: false,
1420
+ touchedPaths: [],
1274
1421
  createdAt: Date.now(),
1422
+ transportRetryCount: 0,
1275
1423
  ...(subtasks.length > 1 ? { subtasks } : {}),
1276
1424
  };
1277
1425
  this.chains.set(chain.id, chain);
@@ -1333,9 +1481,39 @@ export class WrfcController {
1333
1481
  this.upsertWrfcWorkPlanTask(chain, 'engineer', engineerRecord, 'in_progress', subtask.id);
1334
1482
  }
1335
1483
  }
1484
+ /**
1485
+ * Appends a completion report's self-reported filesCreated/filesModified/filesDeleted
1486
+ * into the chain's running edit ledger (chain.touchedPaths). Called for every engineer,
1487
+ * fixer, and integrator completion — not just the first pass — so a chain that goes
1488
+ * through gate-fix or review-fix cycles still has the fixer's edits represented. This is
1489
+ * why it is a standalone accumulator rather than reading the last-stored report field:
1490
+ * chain.engineerReport / subtask.engineerReport are last-write slots that do not reliably
1491
+ * retain every fixer pass (see collectChainTouchedPaths for the consuming side).
1492
+ */
1493
+ recordTouchedPaths(chain, report) {
1494
+ if (report.archetype !== 'engineer')
1495
+ return;
1496
+ const engineerReport = report;
1497
+ const claimed = [
1498
+ ...engineerReport.filesCreated,
1499
+ ...engineerReport.filesModified,
1500
+ ...engineerReport.filesDeleted,
1501
+ ];
1502
+ if (claimed.length === 0)
1503
+ return;
1504
+ chain.touchedPaths ??= [];
1505
+ const seen = new Set(chain.touchedPaths);
1506
+ for (const path of claimed) {
1507
+ if (!seen.has(path)) {
1508
+ seen.add(path);
1509
+ chain.touchedPaths.push(path);
1510
+ }
1511
+ }
1512
+ }
1336
1513
  handleEngineerCompletion(chain, agentId, report) {
1337
1514
  let reportForReview = report;
1338
1515
  this.completeCurrentNode(chain, report.summary);
1516
+ this.recordTouchedPaths(chain, report);
1339
1517
  if (chain.state === 'engineering') {
1340
1518
  chain.engineerReport = report;
1341
1519
  this.workmap.append({
@@ -1529,6 +1707,7 @@ export class WrfcController {
1529
1707
  handleCompoundEngineerCompletion(chain, subtask, agentId, report) {
1530
1708
  let reportForReview = report;
1531
1709
  this.completeSubtaskNode(chain, subtask, report.summary);
1710
+ this.recordTouchedPaths(chain, report);
1532
1711
  if (subtask.state === 'engineering') {
1533
1712
  subtask.engineerReport = report;
1534
1713
  this.workmap.append({
@@ -1749,6 +1928,7 @@ export class WrfcController {
1749
1928
  }
1750
1929
  handleIntegratorCompletion(chain, agentId, report) {
1751
1930
  chain.integratorReport = report;
1931
+ this.recordTouchedPaths(chain, report);
1752
1932
  this.completeCurrentNode(chain, report.summary);
1753
1933
  this.workmap.append({
1754
1934
  ts: new Date().toISOString(),
@@ -1804,6 +1984,11 @@ export class WrfcController {
1804
1984
  if (selectedRoute?.reason) {
1805
1985
  record.wrfcRouteReason = selectedRoute.reason;
1806
1986
  }
1987
+ // Remember how this child was spawned so a transport-classified failure of
1988
+ // this exact agent can be retried later by respawning with identical inputs
1989
+ // (see retryTransportFailure). Overwritten on every spawn — only ever
1990
+ // describes the most recent child.
1991
+ chain.lastChildSpawn = { agentId: record.id, role, template, task, dangerouslyDisableWrfc, subtaskId };
1807
1992
  return record;
1808
1993
  }
1809
1994
  withRouteReason(baseReason, record) {
@@ -1833,6 +2018,15 @@ export class WrfcController {
1833
2018
  owner.completedAt = Date.now();
1834
2019
  owner.progress = message;
1835
2020
  owner.fullOutput = message;
2021
+ // The owner never runs an LLM turn itself (it only supervises phase
2022
+ // children), so its own usage/toolCallCount stay at the spawn-time zero
2023
+ // default forever unless rolled up here from the real numbers its phase
2024
+ // agents accumulated. This is what makes AgentManager.getStatus()/list()
2025
+ // — the read path TUI per-agent surfaces actually use — return real data
2026
+ // for the owner instead of the never-updated zeros (WO-305 wired the
2027
+ // AGENT_COMPLETED.usage forwarding but nothing populated the source).
2028
+ owner.usage = this.aggregateChainUsage(chain);
2029
+ owner.toolCallCount = this.aggregateChainToolCallCount(chain);
1836
2030
  chain.ownerTerminalEmitted = true;
1837
2031
  const context = {
1838
2032
  sessionId: this.sessionId,
@@ -1846,6 +2040,7 @@ export class WrfcController {
1846
2040
  durationMs: Math.max(0, owner.completedAt - owner.startedAt),
1847
2041
  output: message,
1848
2042
  toolCallsMade: owner.toolCallCount,
2043
+ usage: owner.usage,
1849
2044
  });
1850
2045
  }
1851
2046
  else {
@@ -1857,6 +2052,66 @@ export class WrfcController {
1857
2052
  });
1858
2053
  }
1859
2054
  }
2055
+ /**
2056
+ * Roll up token usage across every agent that has ever run under this
2057
+ * chain (the owner plus all phase/subtask children, across every review
2058
+ * and fix cycle — `chain.allAgentIds` already tracks the full roster for
2059
+ * worktree cleanup, so it doubles as the usage-aggregation source). Each
2060
+ * contributor's usage is added in, including the owner's own (normally
2061
+ * zero, but summed rather than ignored in case it is ever populated
2062
+ * directly). Optional fields (reasoningTokens/reasoningSummaryCount) are
2063
+ * only included in the result if at least one contributor reported them,
2064
+ * matching AgentUsage's undefined-means-no-data convention for those.
2065
+ */
2066
+ aggregateChainUsage(chain) {
2067
+ let inputTokens = 0;
2068
+ let outputTokens = 0;
2069
+ let cacheReadTokens = 0;
2070
+ let cacheWriteTokens = 0;
2071
+ let llmCallCount = 0;
2072
+ let turnCount = 0;
2073
+ let reasoningTokens = 0;
2074
+ let hasReasoningTokens = false;
2075
+ let reasoningSummaryCount = 0;
2076
+ let hasReasoningSummaryCount = false;
2077
+ for (const agentId of chain.allAgentIds) {
2078
+ const usage = this.agentManager.getStatus(agentId)?.usage;
2079
+ if (!usage)
2080
+ continue;
2081
+ inputTokens += usage.inputTokens;
2082
+ outputTokens += usage.outputTokens;
2083
+ cacheReadTokens += usage.cacheReadTokens;
2084
+ cacheWriteTokens += usage.cacheWriteTokens;
2085
+ llmCallCount += usage.llmCallCount;
2086
+ turnCount += usage.turnCount;
2087
+ if (usage.reasoningTokens !== undefined) {
2088
+ hasReasoningTokens = true;
2089
+ reasoningTokens += usage.reasoningTokens;
2090
+ }
2091
+ if (usage.reasoningSummaryCount !== undefined) {
2092
+ hasReasoningSummaryCount = true;
2093
+ reasoningSummaryCount += usage.reasoningSummaryCount;
2094
+ }
2095
+ }
2096
+ return {
2097
+ inputTokens,
2098
+ outputTokens,
2099
+ cacheReadTokens,
2100
+ cacheWriteTokens,
2101
+ ...(hasReasoningTokens ? { reasoningTokens } : {}),
2102
+ llmCallCount,
2103
+ turnCount,
2104
+ ...(hasReasoningSummaryCount ? { reasoningSummaryCount } : {}),
2105
+ };
2106
+ }
2107
+ /** Roll up tool-call counts across every agent that has ever run under this chain. */
2108
+ aggregateChainToolCallCount(chain) {
2109
+ let total = 0;
2110
+ for (const agentId of chain.allAgentIds) {
2111
+ total += this.agentManager.getStatus(agentId)?.toolCallCount ?? 0;
2112
+ }
2113
+ return total;
2114
+ }
1860
2115
  upsertWrfcWorkPlanTask(chain, role, record, status, subtaskId) {
1861
2116
  if (!this.workPlanService)
1862
2117
  return;
@@ -10,7 +10,14 @@ export type WrfcState = 'pending' | 'engineering' | 'integrating' | 'reviewing'
10
10
  /** Agent role within a WRFC chain. The owner is the durable chain orchestrator. */
11
11
  export type WrfcAgentRole = 'owner' | 'orchestrator' | 'engineer' | 'reviewer' | 'fixer' | 'integrator' | 'verifier';
12
12
  export type WrfcSubtaskState = 'pending' | 'engineering' | 'reviewing' | 'fixing' | 'passed' | 'failed';
13
- export type WrfcOwnerDecisionAction = 'chain_created' | 'compound_started' | 'spawn_engineer' | 'spawn_reviewer' | 'spawn_fixer' | 'spawn_integrator' | 'spawn_gate_fixer' | 'subtask_review_passed' | 'subtask_review_failed' | 'review_passed' | 'review_failed' | 'gate_passed' | 'gate_failed' | 'chain_passed' | 'chain_failed' | 'chain_cancelled' | 'owner_completion_ignored' | 'owner_failure_ignored' | 'resume_skipped' | 'resume_started';
13
+ export type WrfcOwnerDecisionAction = 'chain_created' | 'compound_started' | 'spawn_engineer' | 'spawn_reviewer' | 'spawn_fixer' | 'spawn_integrator' | 'spawn_gate_fixer' | 'subtask_review_passed' | 'subtask_review_failed' | 'review_passed' | 'review_failed' | 'gate_passed' | 'gate_failed' | 'chain_passed' | 'chain_failed' | 'chain_cancelled' | 'owner_completion_ignored' | 'owner_failure_ignored' | 'resume_skipped' | 'resume_started' | 'transport_retry';
14
+ /**
15
+ * Why a chain reached the terminal 'failed' state. Distinguishes a transport/network
16
+ * blip (which gets one automatic retry, see WrfcChain.transportRetryCount) from an
17
+ * ordinary review/gate rejection, so a consumer (e.g. the TUI) can render the two
18
+ * differently instead of showing every failure identically.
19
+ */
20
+ export type WrfcChainFailureKind = 'transport' | 'other';
14
21
  export interface WrfcOwnerDecision {
15
22
  id: string;
16
23
  ts: string;
@@ -94,6 +101,36 @@ export interface WrfcChain {
94
101
  /** Durable audit of owner orchestration choices. */
95
102
  ownerDecisions: WrfcOwnerDecision[];
96
103
  error?: string | undefined;
104
+ /**
105
+ * Why the chain failed. Only meaningful when state is 'failed'. Optional field —
106
+ * absent on chains persisted before this field was introduced (deserializeChain
107
+ * treats it as undefined rather than requiring a schema-version bump).
108
+ */
109
+ failureKind?: WrfcChainFailureKind | undefined;
110
+ /**
111
+ * Number of times this chain has auto-retried a transport-classified child-agent
112
+ * failure by respawning the same role (bounded by wrfc.transportRetryLimit).
113
+ * Kept separate from fixAttempts/reviewCycles: a transport retry is not a fix
114
+ * cycle and must not count against maxFixAttempts. Optional/defaults to 0 —
115
+ * absent on chains persisted before this field was introduced.
116
+ */
117
+ transportRetryCount?: number | undefined;
118
+ /**
119
+ * Parameters used for the most recent spawnWrfcAgent call, kept so a
120
+ * transport-classified failure of that same agent can be retried by respawning
121
+ * with identical inputs. Overwritten on every subsequent spawn, so it only ever
122
+ * describes the latest child. A chain resumed from persisted JSON that hasn't
123
+ * spawned anything since resume simply has no retry candidate here and fails
124
+ * closed via failChain, same as any other missing-optional-field case.
125
+ */
126
+ lastChildSpawn?: {
127
+ agentId: string;
128
+ role: 'engineer' | 'reviewer' | 'fixer' | 'integrator';
129
+ template: 'engineer' | 'reviewer' | 'integrator';
130
+ task: string;
131
+ dangerouslyDisableWrfc: boolean;
132
+ subtaskId?: string | undefined;
133
+ } | undefined;
97
134
  /** Buffered agent completion — set when agent finishes while chain is still queued/pending. */
98
135
  bufferedCompletion?: {
99
136
  agentId: string;
@@ -119,6 +156,16 @@ export interface WrfcChain {
119
156
  * false = verification ran and found missing claims (phantom work detected).
120
157
  */
121
158
  claimsVerified?: boolean | undefined;
159
+ /**
160
+ * Running ledger of paths (filesCreated/filesModified/filesDeleted) self-reported by every
161
+ * engineer/fixer/integrator completion across the chain's lifetime — including subtask
162
+ * completions on compound chains. Appended to incrementally (not derived from a single
163
+ * "latest report" field) so it still reflects fixer/re-fix passes after resume, and so a
164
+ * pre-interruption pass is never lost. Consumed by collectChainTouchedPaths() to scope
165
+ * the auto-commit `git add` when wrfc.commitScope is 'scoped'. Self-reported, not ground
166
+ * truth — see verifyEngineerClaims for the same accuracy caveat.
167
+ */
168
+ touchedPaths?: string[] | undefined;
122
169
  }
123
170
  /** Quality gate definition. */
124
171
  export interface QualityGate {