@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.
- package/README.md +11 -1
- package/dist/contracts/artifacts/operator-contract.json +4 -1
- package/dist/events/agents.d.ts +14 -0
- package/dist/events/agents.d.ts.map +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/turn.d.ts +17 -0
- package/dist/events/turn.d.ts.map +1 -1
- package/dist/events/workflows.d.ts +8 -0
- package/dist/events/workflows.d.ts.map +1 -1
- package/dist/platform/acp/connection.d.ts +16 -0
- package/dist/platform/acp/connection.d.ts.map +1 -1
- package/dist/platform/acp/connection.js +17 -0
- package/dist/platform/agents/orchestrator-runner.d.ts +1 -1
- package/dist/platform/agents/orchestrator-runner.d.ts.map +1 -1
- package/dist/platform/agents/orchestrator-runner.js +20 -24
- package/dist/platform/agents/orchestrator.d.ts.map +1 -1
- package/dist/platform/agents/orchestrator.js +3 -2
- package/dist/platform/agents/worktree.d.ts +11 -1
- package/dist/platform/agents/worktree.d.ts.map +1 -1
- package/dist/platform/agents/worktree.js +48 -14
- package/dist/platform/agents/wrfc-config.d.ts +23 -0
- package/dist/platform/agents/wrfc-config.d.ts.map +1 -1
- package/dist/platform/agents/wrfc-config.js +24 -0
- package/dist/platform/agents/wrfc-controller.d.ts +49 -1
- package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
- package/dist/platform/agents/wrfc-controller.js +271 -16
- package/dist/platform/agents/wrfc-types.d.ts +48 -1
- package/dist/platform/agents/wrfc-types.d.ts.map +1 -1
- package/dist/platform/automation/manager-runtime-delivery.d.ts +1 -1
- package/dist/platform/automation/manager-runtime-delivery.d.ts.map +1 -1
- package/dist/platform/automation/manager-runtime-delivery.js +4 -3
- package/dist/platform/channels/policy-manager.d.ts +8 -0
- package/dist/platform/channels/policy-manager.d.ts.map +1 -1
- package/dist/platform/channels/policy-manager.js +43 -1
- package/dist/platform/companion/companion-chat-manager.d.ts +1 -0
- package/dist/platform/companion/companion-chat-manager.d.ts.map +1 -1
- package/dist/platform/companion/companion-chat-manager.js +10 -0
- package/dist/platform/companion/companion-chat-routes.js +1 -0
- package/dist/platform/companion/companion-chat-types.d.ts +1 -0
- package/dist/platform/companion/companion-chat-types.d.ts.map +1 -1
- package/dist/platform/config/schema-domain-core.d.ts +3 -0
- package/dist/platform/config/schema-domain-core.d.ts.map +1 -1
- package/dist/platform/config/schema-domain-core.js +24 -0
- package/dist/platform/config/schema-types.d.ts +6 -2
- package/dist/platform/config/schema-types.d.ts.map +1 -1
- package/dist/platform/control-plane/approval-broker.d.ts +7 -0
- package/dist/platform/control-plane/approval-broker.d.ts.map +1 -1
- package/dist/platform/control-plane/approval-broker.js +30 -0
- package/dist/platform/control-plane/method-catalog-channels.d.ts.map +1 -1
- package/dist/platform/control-plane/method-catalog-channels.js +2 -0
- package/dist/platform/control-plane/method-catalog-email.d.ts.map +1 -1
- package/dist/platform/control-plane/method-catalog-email.js +1 -0
- package/dist/platform/core/compaction-sections.d.ts.map +1 -1
- package/dist/platform/core/compaction-sections.js +16 -5
- package/dist/platform/core/compaction-types.d.ts +7 -1
- package/dist/platform/core/compaction-types.d.ts.map +1 -1
- package/dist/platform/core/compaction-types.js +6 -0
- package/dist/platform/core/context-compaction.d.ts +20 -4
- package/dist/platform/core/context-compaction.d.ts.map +1 -1
- package/dist/platform/core/context-compaction.js +34 -7
- package/dist/platform/core/conversation-utils.js +1 -1
- package/dist/platform/core/conversation.d.ts.map +1 -1
- package/dist/platform/core/conversation.js +9 -4
- package/dist/platform/core/orchestrator-turn-loop.d.ts.map +1 -1
- package/dist/platform/core/orchestrator-turn-loop.js +22 -3
- package/dist/platform/core/orchestrator.d.ts +12 -0
- package/dist/platform/core/orchestrator.d.ts.map +1 -1
- package/dist/platform/core/orchestrator.js +36 -8
- package/dist/platform/daemon/http/home-graph-routes.d.ts.map +1 -1
- package/dist/platform/daemon/http/home-graph-routes.js +2 -1
- package/dist/platform/daemon/http/project-planning-routes.d.ts.map +1 -1
- package/dist/platform/daemon/http/project-planning-routes.js +2 -1
- package/dist/platform/hooks/dispatcher.d.ts.map +1 -1
- package/dist/platform/hooks/dispatcher.js +4 -0
- package/dist/platform/hooks/runners/command.d.ts.map +1 -1
- package/dist/platform/hooks/runners/command.js +11 -2
- package/dist/platform/integrations/delivery.d.ts.map +1 -1
- package/dist/platform/integrations/delivery.js +0 -2
- package/dist/platform/intelligence/import-graph.d.ts.map +1 -1
- package/dist/platform/intelligence/import-graph.js +8 -0
- package/dist/platform/knowledge/extractors.d.ts.map +1 -1
- package/dist/platform/knowledge/extractors.js +30 -4
- package/dist/platform/mcp/client.d.ts +2 -0
- package/dist/platform/mcp/client.d.ts.map +1 -1
- package/dist/platform/mcp/client.js +17 -3
- package/dist/platform/mcp/registry.d.ts.map +1 -1
- package/dist/platform/mcp/registry.js +4 -0
- package/dist/platform/media/builtin-generation-providers.d.ts.map +1 -1
- package/dist/platform/media/builtin-generation-providers.js +2 -3
- package/dist/platform/media/provider-registry.d.ts.map +1 -1
- package/dist/platform/media/provider-registry.js +3 -1
- package/dist/platform/multimodal/service.d.ts.map +1 -1
- package/dist/platform/multimodal/service.js +18 -3
- package/dist/platform/providers/anthropic-compat.js +2 -2
- package/dist/platform/providers/anthropic-sdk-provider.js +1 -1
- package/dist/platform/providers/anthropic-sse-assembler.d.ts.map +1 -1
- package/dist/platform/providers/anthropic-sse-assembler.js +24 -0
- package/dist/platform/providers/anthropic.js +2 -2
- package/dist/platform/providers/capabilities.d.ts +6 -0
- package/dist/platform/providers/capabilities.d.ts.map +1 -1
- package/dist/platform/providers/capabilities.js +20 -1
- package/dist/platform/providers/gemini.d.ts.map +1 -1
- package/dist/platform/providers/gemini.js +57 -48
- package/dist/platform/providers/index.d.ts +1 -0
- package/dist/platform/providers/index.d.ts.map +1 -1
- package/dist/platform/providers/index.js +1 -0
- package/dist/platform/providers/interface.d.ts +9 -1
- package/dist/platform/providers/interface.d.ts.map +1 -1
- package/dist/platform/providers/llama-cpp.js +1 -1
- package/dist/platform/providers/lm-studio.js +1 -1
- package/dist/platform/providers/model-limits.d.ts.map +1 -1
- package/dist/platform/providers/model-limits.js +14 -2
- package/dist/platform/providers/ollama.js +1 -1
- package/dist/platform/providers/openai-codex.js +1 -1
- package/dist/platform/providers/openai-compat.d.ts.map +1 -1
- package/dist/platform/providers/openai-compat.js +2 -2
- package/dist/platform/providers/openai.js +2 -2
- package/dist/platform/runtime/emitters/agents.d.ts +2 -0
- package/dist/platform/runtime/emitters/agents.d.ts.map +1 -1
- package/dist/platform/runtime/emitters/turn.d.ts +9 -0
- package/dist/platform/runtime/emitters/turn.d.ts.map +1 -1
- package/dist/platform/runtime/emitters/turn.js +4 -0
- package/dist/platform/runtime/emitters/workflows.d.ts +1 -0
- package/dist/platform/runtime/emitters/workflows.d.ts.map +1 -1
- package/dist/platform/runtime/store/domains/agents.d.ts +7 -0
- package/dist/platform/runtime/store/domains/agents.d.ts.map +1 -1
- package/dist/platform/runtime/store/domains/conversation.d.ts +0 -2
- package/dist/platform/runtime/store/domains/conversation.d.ts.map +1 -1
- package/dist/platform/runtime/store/domains/conversation.js +0 -1
- package/dist/platform/runtime/store/helpers/reducers/conversation.d.ts.map +1 -1
- package/dist/platform/runtime/store/helpers/reducers/conversation.js +20 -2
- package/dist/platform/runtime/store/helpers/reducers/lifecycle.d.ts.map +1 -1
- package/dist/platform/runtime/store/helpers/reducers/lifecycle.js +6 -0
- package/dist/platform/runtime/store/helpers/reducers/sync.d.ts.map +1 -1
- package/dist/platform/runtime/store/helpers/reducers/sync.js +7 -1
- package/dist/platform/runtime/tools/phases/execute.d.ts.map +1 -1
- package/dist/platform/runtime/tools/phases/execute.js +11 -1
- package/dist/platform/runtime/ui-read-models-core.d.ts.map +1 -1
- package/dist/platform/runtime/ui-read-models-core.js +4 -1
- package/dist/platform/tools/exec/runtime.d.ts.map +1 -1
- package/dist/platform/tools/exec/runtime.js +9 -0
- package/dist/platform/tools/shared/overflow.d.ts.map +1 -1
- package/dist/platform/tools/shared/overflow.js +21 -2
- package/dist/platform/types/errors.d.ts +26 -0
- package/dist/platform/types/errors.d.ts.map +1 -1
- package/dist/platform/types/errors.js +63 -1
- package/dist/platform/utils/error-display.d.ts.map +1 -1
- package/dist/platform/utils/error-display.js +14 -6
- package/dist/platform/utils/retry.d.ts +4 -2
- package/dist/platform/utils/retry.d.ts.map +1 -1
- package/dist/platform/utils/retry.js +4 -2
- package/dist/platform/version.js +1 -1
- package/dist/platform/voice/providers/shared.d.ts.map +1 -1
- package/dist/platform/voice/providers/shared.js +2 -3
- 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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
641
|
-
|
|
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
|
|
753
|
-
|
|
754
|
-
|
|
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.
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
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 {
|