swarm-engine 1.41.0 → 1.51.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 +145 -42
- package/agents/implementer.md +1 -14
- package/agents/orchestrator.md +127 -246
- package/agents/reviewer.md +3 -18
- package/dist/cli/commands/agents.js +13 -1
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/dashboard.d.ts +3 -0
- package/dist/cli/commands/dashboard.d.ts.map +1 -0
- package/dist/cli/commands/dashboard.js +43 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/orchestrate.d.ts.map +1 -1
- package/dist/cli/commands/orchestrate.js +17 -0
- package/dist/cli/commands/orchestrate.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +20 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +5 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +4 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/types.d.ts +26 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/index.js +2 -2
- package/dist/memory/index.js.map +1 -1
- package/dist/runtime/acon.d.ts +61 -0
- package/dist/runtime/acon.d.ts.map +1 -0
- package/dist/runtime/acon.js +266 -0
- package/dist/runtime/acon.js.map +1 -0
- package/dist/runtime/agent-runner.d.ts +8 -0
- package/dist/runtime/agent-runner.d.ts.map +1 -1
- package/dist/runtime/agent-runner.js +93 -4
- package/dist/runtime/agent-runner.js.map +1 -1
- package/dist/runtime/backends/claude.d.ts +1 -0
- package/dist/runtime/backends/claude.d.ts.map +1 -1
- package/dist/runtime/backends/claude.js +50 -2
- package/dist/runtime/backends/claude.js.map +1 -1
- package/dist/runtime/backends/codex.d.ts.map +1 -1
- package/dist/runtime/backends/codex.js +4 -0
- package/dist/runtime/backends/codex.js.map +1 -1
- package/dist/runtime/backends/gemini.d.ts.map +1 -1
- package/dist/runtime/backends/gemini.js +4 -0
- package/dist/runtime/backends/gemini.js.map +1 -1
- package/dist/runtime/benefits.d.ts +250 -0
- package/dist/runtime/benefits.d.ts.map +1 -0
- package/dist/runtime/benefits.js +775 -0
- package/dist/runtime/benefits.js.map +1 -0
- package/dist/runtime/cache-optimizer.d.ts +7 -3
- package/dist/runtime/cache-optimizer.d.ts.map +1 -1
- package/dist/runtime/cache-optimizer.js +11 -7
- package/dist/runtime/cache-optimizer.js.map +1 -1
- package/dist/runtime/compaction.d.ts +6 -1
- package/dist/runtime/compaction.d.ts.map +1 -1
- package/dist/runtime/compaction.js +39 -2
- package/dist/runtime/compaction.js.map +1 -1
- package/dist/runtime/compactor.d.ts +90 -0
- package/dist/runtime/compactor.d.ts.map +1 -0
- package/dist/runtime/compactor.js +418 -0
- package/dist/runtime/compactor.js.map +1 -0
- package/dist/runtime/context-decay.d.ts +45 -0
- package/dist/runtime/context-decay.d.ts.map +1 -0
- package/dist/runtime/context-decay.js +149 -0
- package/dist/runtime/context-decay.js.map +1 -0
- package/dist/runtime/cost-model.d.ts.map +1 -1
- package/dist/runtime/cost-model.js +20 -17
- package/dist/runtime/cost-model.js.map +1 -1
- package/dist/runtime/engine.d.ts +2 -0
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +113 -3
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/graph-discovery.js +2 -2
- package/dist/runtime/graph-discovery.js.map +1 -1
- package/dist/runtime/graph-trajectory.js +3 -3
- package/dist/runtime/graph-trajectory.js.map +1 -1
- package/dist/runtime/lsp.d.ts.map +1 -1
- package/dist/runtime/lsp.js +4 -0
- package/dist/runtime/lsp.js.map +1 -1
- package/dist/runtime/mcp.d.ts +1 -0
- package/dist/runtime/mcp.d.ts.map +1 -1
- package/dist/runtime/mcp.js +38 -0
- package/dist/runtime/mcp.js.map +1 -1
- package/dist/runtime/output-schemas.d.ts +21 -0
- package/dist/runtime/output-schemas.d.ts.map +1 -0
- package/dist/runtime/output-schemas.js +252 -0
- package/dist/runtime/output-schemas.js.map +1 -0
- package/dist/runtime/output-summarizer.d.ts +45 -0
- package/dist/runtime/output-summarizer.d.ts.map +1 -0
- package/dist/runtime/output-summarizer.js +171 -0
- package/dist/runtime/output-summarizer.js.map +1 -0
- package/dist/runtime/plugins.d.ts +5 -1
- package/dist/runtime/plugins.d.ts.map +1 -1
- package/dist/runtime/plugins.js +14 -2
- package/dist/runtime/plugins.js.map +1 -1
- package/dist/runtime/prompt-tier.d.ts +33 -0
- package/dist/runtime/prompt-tier.d.ts.map +1 -0
- package/dist/runtime/prompt-tier.js +105 -0
- package/dist/runtime/prompt-tier.js.map +1 -0
- package/dist/runtime/sharing.js +2 -1
- package/dist/runtime/sharing.js.map +1 -1
- package/dist/runtime/stats.d.ts +2 -0
- package/dist/runtime/stats.d.ts.map +1 -1
- package/dist/runtime/stats.js +17 -3
- package/dist/runtime/stats.js.map +1 -1
- package/dist/utils/project-config.d.ts +20 -0
- package/dist/utils/project-config.d.ts.map +1 -1
- package/dist/utils/project-config.js +46 -1
- package/dist/utils/project-config.js.map +1 -1
- package/dist/utils/redact.d.ts.map +1 -1
- package/dist/utils/redact.js +5 -1
- package/dist/utils/redact.js.map +1 -1
- package/dist/web/bridge.d.ts +47 -0
- package/dist/web/bridge.d.ts.map +1 -0
- package/dist/web/bridge.js +267 -0
- package/dist/web/bridge.js.map +1 -0
- package/dist/web/graph-api.d.ts +19 -0
- package/dist/web/graph-api.d.ts.map +1 -0
- package/dist/web/graph-api.js +157 -0
- package/dist/web/graph-api.js.map +1 -0
- package/dist/web/index.d.ts +21 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +38 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/public/index.html +1304 -0
- package/dist/web/public/public/index.html +1307 -0
- package/dist/web/server.d.ts +24 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +113 -0
- package/dist/web/server.js.map +1 -0
- package/dist/web/tray.d.ts +23 -0
- package/dist/web/tray.d.ts.map +1 -0
- package/dist/web/tray.js +205 -0
- package/dist/web/tray.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BenefitsCollector — aggregates optimization metrics during orchestration
|
|
3
|
+
* and produces a user-facing summary of what the engine did for them.
|
|
4
|
+
*/
|
|
5
|
+
// ─── Constants ────────────────────────────────────────────────
|
|
6
|
+
/** Approximate base payload loaded per Claude Code session spawn (system prompt + tool schemas + MCP + skills).
|
|
7
|
+
* Dropped from ~45k to ~31k in Wave 1 via ENABLE_TOOL_SEARCH (deferred tool loading). */
|
|
8
|
+
export const BASE_PAYLOAD_TOKENS_PER_AGENT = 31_000;
|
|
9
|
+
/** Cache-read pricing model: cache_read is billed at ~10% of regular input rate, so each
|
|
10
|
+
* cache_read token represents ~90% savings vs the equivalent regular input token.
|
|
11
|
+
* For Opus 4.6 we assume $5/MTok regular input → $4.5/MTok (90%) savings per cache_read token. */
|
|
12
|
+
export const CACHE_SAVINGS_RATE = 4.5 / 1_000_000;
|
|
13
|
+
/** Minimum cache-read ratio over the window for cliff detection to fire
|
|
14
|
+
* (if baseline is already low, the "cliff" isn't meaningful). */
|
|
15
|
+
const CLIFF_BASELINE_FLOOR = 0.2;
|
|
16
|
+
/** Current-ratio-vs-baseline drop threshold — >50% drop = cliff. */
|
|
17
|
+
const CLIFF_DROP_RATIO = 0.5;
|
|
18
|
+
/** Token floor for cliff detection — noise floor avoids flagging tiny requests. */
|
|
19
|
+
const CLIFF_NOISE_FLOOR_TOKENS = 1000;
|
|
20
|
+
/** Rolling history window size per agentType for baseline computation. */
|
|
21
|
+
const CLIFF_HISTORY_WINDOW = 10;
|
|
22
|
+
/** Minimum number of prior samples required before cliff detection activates. */
|
|
23
|
+
const CLIFF_MIN_HISTORY = 3;
|
|
24
|
+
// ─── Collector ────────────────────────────────────────────────
|
|
25
|
+
/**
|
|
26
|
+
* Collects benefits metrics during orchestration execution.
|
|
27
|
+
* Call increment methods as optimizations occur, then call `build()` at the end.
|
|
28
|
+
*/
|
|
29
|
+
export class BenefitsCollector {
|
|
30
|
+
// Token savings accumulators
|
|
31
|
+
_smartRoutingSaved = 0;
|
|
32
|
+
_promptCompressionSaved = 0;
|
|
33
|
+
_verbatimCompactionSaved = 0;
|
|
34
|
+
_contextDecaySaved = 0;
|
|
35
|
+
_contextFilteringSaved = 0;
|
|
36
|
+
/** Pre-computed estimate accumulated via {@link addCacheSavingsUsd} (caller-side guess
|
|
37
|
+
* from shared-prefix length × parallel agent count). Used as a fallback only when
|
|
38
|
+
* no real API cache_read data has been recorded. */
|
|
39
|
+
_cacheSavingsUsdEstimate = 0;
|
|
40
|
+
/** Real savings accumulated via {@link recordApiUsage} from API-reported cache_read
|
|
41
|
+
* tokens. Takes priority over {@link _cacheSavingsUsdEstimate} in {@link build}. */
|
|
42
|
+
_cacheSavingsUsdReal = 0;
|
|
43
|
+
// Graph counters
|
|
44
|
+
_contextRoutingDecisions = 0;
|
|
45
|
+
_coModHintsInjected = 0;
|
|
46
|
+
_confidenceGatesEvaluated = 0;
|
|
47
|
+
_gatesTriggered = 0;
|
|
48
|
+
_reviewFindingsRecorded = 0;
|
|
49
|
+
// ML counters
|
|
50
|
+
_gnnPredictionsMade = 0;
|
|
51
|
+
_highRiskNodesIdentified = 0;
|
|
52
|
+
_agentsDroppedML = 0;
|
|
53
|
+
_agentsDroppedRules = 0;
|
|
54
|
+
_dropoutTokensSaved = 0;
|
|
55
|
+
_affinityEscalations = 0;
|
|
56
|
+
_trajectoryPredictionsMade = 0;
|
|
57
|
+
// Adaptive counters
|
|
58
|
+
_phasesSkipped = 0;
|
|
59
|
+
_modelDowngrades = 0;
|
|
60
|
+
_modelUpgrades = 0;
|
|
61
|
+
_cascadeTierChanges = 0;
|
|
62
|
+
_budgetWarnings = 0;
|
|
63
|
+
_livingSpecUpdates = 0;
|
|
64
|
+
// Cache cliff detection state
|
|
65
|
+
_cacheCliffs = 0;
|
|
66
|
+
_agentCacheRatios = new Map(); // agentType → recent ratios (last CLIFF_HISTORY_WINDOW)
|
|
67
|
+
// Cold-wake counter (incremented via recordColdWake, typically wired to 'cache:cold-wake' events)
|
|
68
|
+
_coldWakes = 0;
|
|
69
|
+
bus = null;
|
|
70
|
+
constructor(bus) {
|
|
71
|
+
this.bus = bus ?? null;
|
|
72
|
+
}
|
|
73
|
+
// ─── Token savings methods ────────────────────────────────
|
|
74
|
+
addSmartRoutingSaved(tokens) {
|
|
75
|
+
this._smartRoutingSaved += tokens;
|
|
76
|
+
}
|
|
77
|
+
addPromptCompressionSaved(tokens) {
|
|
78
|
+
this._promptCompressionSaved += tokens;
|
|
79
|
+
}
|
|
80
|
+
addVerbatimCompactionSaved(tokens) {
|
|
81
|
+
this._verbatimCompactionSaved += tokens;
|
|
82
|
+
}
|
|
83
|
+
addContextDecaySaved(tokens) {
|
|
84
|
+
this._contextDecaySaved += tokens;
|
|
85
|
+
}
|
|
86
|
+
addContextFilteringSaved(tokens) {
|
|
87
|
+
this._contextFilteringSaved += tokens;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Add a pre-computed cache savings estimate (caller-side). Used by the engine
|
|
91
|
+
* for shared-prefix-across-parallel-agents projections before any real API data
|
|
92
|
+
* is available. This estimate is **superseded** by real data from
|
|
93
|
+
* {@link recordApiUsage} in {@link build} — the two are tracked separately to
|
|
94
|
+
* avoid double-counting.
|
|
95
|
+
*/
|
|
96
|
+
addCacheSavingsUsd(usd) {
|
|
97
|
+
this._cacheSavingsUsdEstimate += usd;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Record actual API usage and credit real cache savings based on cache_read tokens
|
|
101
|
+
* reported by the Claude API. When any real data is recorded, the value reported
|
|
102
|
+
* by {@link build} switches to the real total, ignoring the caller-side estimate
|
|
103
|
+
* accumulated via {@link addCacheSavingsUsd} (the two were previously double-counted).
|
|
104
|
+
*
|
|
105
|
+
* Pricing: see {@link CACHE_SAVINGS_RATE} ($4.5/MTok cache_read).
|
|
106
|
+
*
|
|
107
|
+
* Also performs **cross-phase** cache-cliff detection per agentType — when an agent's
|
|
108
|
+
* cache_read ratio drops >{@link CLIFF_DROP_RATIO} vs its rolling baseline (over
|
|
109
|
+
* {@link CLIFF_HISTORY_WINDOW} prior samples), emits `cache:cliff` to the EventBus
|
|
110
|
+
* (if any) and increments the cliff counter.
|
|
111
|
+
*
|
|
112
|
+
* **Limitation**: detection happens at agent-execution granularity (one call per
|
|
113
|
+
* AgentInstance.usage roll-up). Mid-conversation cache TTL expiry within a single
|
|
114
|
+
* agent run is NOT detected here — that would require a per-API-call hook in the
|
|
115
|
+
* backend (see BackendOptions for per-call observability options if/when added).
|
|
116
|
+
*/
|
|
117
|
+
recordApiUsage(usage, agentType) {
|
|
118
|
+
// ─── Real savings credit (separate accumulator from estimate) ─────────
|
|
119
|
+
this._cacheSavingsUsdReal += (usage.cacheReadInputTokens ?? 0) * CACHE_SAVINGS_RATE;
|
|
120
|
+
// ─── Cache cliff detection ────────────────────────────
|
|
121
|
+
// Only meaningful when we know which agentType this call belongs to.
|
|
122
|
+
if (!agentType)
|
|
123
|
+
return;
|
|
124
|
+
const cacheRead = usage.cacheReadInputTokens ?? 0;
|
|
125
|
+
const input = usage.inputTokens ?? 0;
|
|
126
|
+
const totalInput = cacheRead + input;
|
|
127
|
+
// Noise floor — avoid false positives on tiny requests where ratios swing wildly.
|
|
128
|
+
if (totalInput < CLIFF_NOISE_FLOOR_TOKENS)
|
|
129
|
+
return;
|
|
130
|
+
const ratio = cacheRead / totalInput;
|
|
131
|
+
const history = this._agentCacheRatios.get(agentType) ?? [];
|
|
132
|
+
if (history.length >= CLIFF_MIN_HISTORY) {
|
|
133
|
+
// Need at least CLIFF_MIN_HISTORY prior samples to establish a meaningful baseline.
|
|
134
|
+
const baseline = history.reduce((a, b) => a + b, 0) / history.length;
|
|
135
|
+
// Only flag when baseline was non-trivial AND dropped by more than the threshold.
|
|
136
|
+
if (baseline > CLIFF_BASELINE_FLOOR && ratio < baseline * CLIFF_DROP_RATIO) {
|
|
137
|
+
this._cacheCliffs++;
|
|
138
|
+
// Regular-vs-cache price delta: ($5 - $0.5) / 1M = $4.5/MTok extra cost per token
|
|
139
|
+
// moved from cache to regular input. Approximate cost of the missed cache.
|
|
140
|
+
const estimatedExtraCostUsd = (totalInput * (baseline - ratio) * (5 - 0.5)) / 1_000_000;
|
|
141
|
+
this.bus?.emit('cache:cliff', {
|
|
142
|
+
agentType,
|
|
143
|
+
previousRatio: baseline,
|
|
144
|
+
currentRatio: ratio,
|
|
145
|
+
estimatedExtraCostUsd,
|
|
146
|
+
}, 'benefits-collector');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
history.push(ratio);
|
|
150
|
+
if (history.length > CLIFF_HISTORY_WINDOW)
|
|
151
|
+
history.shift();
|
|
152
|
+
this._agentCacheRatios.set(agentType, history);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Record a cold-wake event — fired when an agent's idle gap exceeds the prompt cache
|
|
156
|
+
* TTL (typically 5 minutes), so the next invocation rebuilds the cache from cold and
|
|
157
|
+
* pays full input price. Typically wired to the `cache:cold-wake` event from
|
|
158
|
+
* AgentRunner via `bus.on(...)` in the engine.
|
|
159
|
+
*/
|
|
160
|
+
recordColdWake() {
|
|
161
|
+
this._coldWakes++;
|
|
162
|
+
}
|
|
163
|
+
// ─── Graph methods ────────────────────────────────────────
|
|
164
|
+
addContextRoutingDecision() {
|
|
165
|
+
this._contextRoutingDecisions++;
|
|
166
|
+
}
|
|
167
|
+
addCoModHintInjected() {
|
|
168
|
+
this._coModHintsInjected++;
|
|
169
|
+
}
|
|
170
|
+
addConfidenceGateEvaluated(triggered) {
|
|
171
|
+
this._confidenceGatesEvaluated++;
|
|
172
|
+
if (triggered)
|
|
173
|
+
this._gatesTriggered++;
|
|
174
|
+
}
|
|
175
|
+
addReviewFindingRecorded() {
|
|
176
|
+
this._reviewFindingsRecorded++;
|
|
177
|
+
}
|
|
178
|
+
// ─── ML methods ───────────────────────────────────────────
|
|
179
|
+
addGnnPrediction(highRiskCount) {
|
|
180
|
+
this._gnnPredictionsMade++;
|
|
181
|
+
this._highRiskNodesIdentified += highRiskCount;
|
|
182
|
+
}
|
|
183
|
+
addAgentDroppedML(estimatedTokensSaved) {
|
|
184
|
+
this._agentsDroppedML++;
|
|
185
|
+
this._dropoutTokensSaved += estimatedTokensSaved;
|
|
186
|
+
}
|
|
187
|
+
addAgentDroppedRules() {
|
|
188
|
+
this._agentsDroppedRules++;
|
|
189
|
+
}
|
|
190
|
+
addAffinityEscalation() {
|
|
191
|
+
this._affinityEscalations++;
|
|
192
|
+
}
|
|
193
|
+
addTrajectoryPrediction() {
|
|
194
|
+
this._trajectoryPredictionsMade++;
|
|
195
|
+
}
|
|
196
|
+
// ─── Adaptive methods ─────────────────────────────────────
|
|
197
|
+
addPhaseSkipped() {
|
|
198
|
+
this._phasesSkipped++;
|
|
199
|
+
}
|
|
200
|
+
addModelDowngrade() {
|
|
201
|
+
this._modelDowngrades++;
|
|
202
|
+
}
|
|
203
|
+
addModelUpgrade() {
|
|
204
|
+
this._modelUpgrades++;
|
|
205
|
+
}
|
|
206
|
+
addCascadeTierChange() {
|
|
207
|
+
this._cascadeTierChanges++;
|
|
208
|
+
}
|
|
209
|
+
addBudgetWarning() {
|
|
210
|
+
this._budgetWarnings++;
|
|
211
|
+
}
|
|
212
|
+
addLivingSpecUpdate() {
|
|
213
|
+
this._livingSpecUpdates++;
|
|
214
|
+
}
|
|
215
|
+
// ─── Build ────────────────────────────────────────────────
|
|
216
|
+
/**
|
|
217
|
+
* Build the final summary. Call at orchestration end.
|
|
218
|
+
*
|
|
219
|
+
* @param opts.actualTotalTokens — total input tokens actually consumed (used to compute savingsPercent)
|
|
220
|
+
* @param opts.actualCostUsd — total USD cost of the orchestration
|
|
221
|
+
* @param opts.durationMs — wall-clock duration in milliseconds
|
|
222
|
+
* @param opts.agentsExecuted — number of agents that ran
|
|
223
|
+
* @param opts.totalAgentTurns — sum of per-agent turns (each turn re-sends full context in stateless loop)
|
|
224
|
+
* @param opts.phasesExecuted — number of phases that ran
|
|
225
|
+
* @param opts.historicalAvgCostUsd — average cost for this pattern from history (null if none)
|
|
226
|
+
* @param opts.patternRunsRecorded — how many historical runs exist for this pattern
|
|
227
|
+
* @param opts.historicalSuccessRate — success rate from history (null if none)
|
|
228
|
+
*/
|
|
229
|
+
build(opts) {
|
|
230
|
+
const totalTokensSaved = this._smartRoutingSaved +
|
|
231
|
+
this._promptCompressionSaved +
|
|
232
|
+
this._verbatimCompactionSaved +
|
|
233
|
+
this._contextDecaySaved +
|
|
234
|
+
this._contextFilteringSaved +
|
|
235
|
+
this._dropoutTokensSaved;
|
|
236
|
+
// What would have been used without optimizations = actual + saved
|
|
237
|
+
const wouldHaveUsed = opts.actualTotalTokens + totalTokensSaved;
|
|
238
|
+
const savingsPercent = totalTokensSaved > 0 && wouldHaveUsed > 0 ? Math.round((totalTokensSaved / wouldHaveUsed) * 100) : 0;
|
|
239
|
+
const costDelta = opts.historicalAvgCostUsd != null ? opts.actualCostUsd - opts.historicalAvgCostUsd : null;
|
|
240
|
+
// Real API data takes priority over caller-side estimate to avoid double-counting.
|
|
241
|
+
// If any real data was recorded (recordApiUsage with cache_read), use that exclusively;
|
|
242
|
+
// otherwise fall back to the pre-computed estimate (addCacheSavingsUsd).
|
|
243
|
+
const cacheSavingsUsd = this._cacheSavingsUsdReal > 0 ? this._cacheSavingsUsdReal : this._cacheSavingsUsdEstimate;
|
|
244
|
+
const cacheSavingsMeasured = this._cacheSavingsUsdReal > 0;
|
|
245
|
+
// ─── Turn-based compounding ──────────────────────────────
|
|
246
|
+
// The stateless loop re-sends full context every turn, so per-prompt/per-context
|
|
247
|
+
// savings compound across turns. Use conservative floor of avgTurns.
|
|
248
|
+
const totalAgentTurns = opts.totalAgentTurns ?? opts.agentsExecuted;
|
|
249
|
+
const avgTurns = totalAgentTurns / Math.max(opts.agentsExecuted, 1);
|
|
250
|
+
// Only compound savings that are part of the persistent re-sent context
|
|
251
|
+
const compoundable = this._smartRoutingSaved + this._promptCompressionSaved + this._contextDecaySaved + this._contextFilteringSaved;
|
|
252
|
+
// Non-compounding savings (output-side compaction or spawn-avoidance)
|
|
253
|
+
const nonCompounding = this._verbatimCompactionSaved + this._dropoutTokensSaved;
|
|
254
|
+
// Conservative: use floor of avgTurns (under-estimate)
|
|
255
|
+
const compoundedTotalSaved = Math.floor(compoundable * Math.floor(avgTurns)) + nonCompounding;
|
|
256
|
+
return {
|
|
257
|
+
tokens: {
|
|
258
|
+
smartRoutingSaved: this._smartRoutingSaved,
|
|
259
|
+
promptCompressionSaved: this._promptCompressionSaved,
|
|
260
|
+
verbatimCompactionSaved: this._verbatimCompactionSaved,
|
|
261
|
+
contextDecaySaved: this._contextDecaySaved,
|
|
262
|
+
contextFilteringSaved: this._contextFilteringSaved,
|
|
263
|
+
cacheSavingsUsd: Math.round(cacheSavingsUsd * 10000) / 10000,
|
|
264
|
+
cacheSavingsMeasured,
|
|
265
|
+
totalTokensSaved,
|
|
266
|
+
compoundedTotalSaved,
|
|
267
|
+
savingsPercent,
|
|
268
|
+
},
|
|
269
|
+
graph: {
|
|
270
|
+
contextRoutingDecisions: this._contextRoutingDecisions,
|
|
271
|
+
coModHintsInjected: this._coModHintsInjected,
|
|
272
|
+
confidenceGatesEvaluated: this._confidenceGatesEvaluated,
|
|
273
|
+
gatesTriggered: this._gatesTriggered,
|
|
274
|
+
patternRunsRecorded: opts.patternRunsRecorded ?? 0,
|
|
275
|
+
historicalSuccessRate: opts.historicalSuccessRate ?? null,
|
|
276
|
+
reviewFindingsRecorded: this._reviewFindingsRecorded,
|
|
277
|
+
},
|
|
278
|
+
ml: {
|
|
279
|
+
gnnPredictionsMade: this._gnnPredictionsMade,
|
|
280
|
+
highRiskNodesIdentified: this._highRiskNodesIdentified,
|
|
281
|
+
agentsDroppedML: this._agentsDroppedML,
|
|
282
|
+
agentsDroppedRules: this._agentsDroppedRules,
|
|
283
|
+
dropoutTokensSaved: this._dropoutTokensSaved,
|
|
284
|
+
affinityEscalations: this._affinityEscalations,
|
|
285
|
+
trajectoryPredictionsMade: this._trajectoryPredictionsMade,
|
|
286
|
+
},
|
|
287
|
+
adaptive: {
|
|
288
|
+
phasesSkipped: this._phasesSkipped,
|
|
289
|
+
modelDowngrades: this._modelDowngrades,
|
|
290
|
+
modelUpgrades: this._modelUpgrades,
|
|
291
|
+
cascadeTierChanges: this._cascadeTierChanges,
|
|
292
|
+
budgetWarnings: this._budgetWarnings,
|
|
293
|
+
livingSpecUpdates: this._livingSpecUpdates,
|
|
294
|
+
},
|
|
295
|
+
actualCostUsd: Math.round(opts.actualCostUsd * 10000) / 10000,
|
|
296
|
+
historicalAvgCostUsd: opts.historicalAvgCostUsd ?? null,
|
|
297
|
+
costDeltaUsd: costDelta != null ? Math.round(costDelta * 10000) / 10000 : null,
|
|
298
|
+
durationMs: opts.durationMs,
|
|
299
|
+
agentsExecuted: opts.agentsExecuted,
|
|
300
|
+
phasesExecuted: opts.phasesExecuted,
|
|
301
|
+
totalAgentTurns,
|
|
302
|
+
avgTurnsPerAgent: Math.round(avgTurns * 100) / 100,
|
|
303
|
+
cacheCliffs: this._cacheCliffs,
|
|
304
|
+
coldWakes: this._coldWakes,
|
|
305
|
+
basePayloadEstimateTokens: totalAgentTurns * BASE_PAYLOAD_TOKENS_PER_AGENT,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/** Reset all counters */
|
|
309
|
+
reset() {
|
|
310
|
+
this._smartRoutingSaved = 0;
|
|
311
|
+
this._promptCompressionSaved = 0;
|
|
312
|
+
this._verbatimCompactionSaved = 0;
|
|
313
|
+
this._contextDecaySaved = 0;
|
|
314
|
+
this._contextFilteringSaved = 0;
|
|
315
|
+
this._cacheSavingsUsdEstimate = 0;
|
|
316
|
+
this._cacheSavingsUsdReal = 0;
|
|
317
|
+
this._contextRoutingDecisions = 0;
|
|
318
|
+
this._coModHintsInjected = 0;
|
|
319
|
+
this._confidenceGatesEvaluated = 0;
|
|
320
|
+
this._gatesTriggered = 0;
|
|
321
|
+
this._reviewFindingsRecorded = 0;
|
|
322
|
+
this._gnnPredictionsMade = 0;
|
|
323
|
+
this._highRiskNodesIdentified = 0;
|
|
324
|
+
this._agentsDroppedML = 0;
|
|
325
|
+
this._agentsDroppedRules = 0;
|
|
326
|
+
this._dropoutTokensSaved = 0;
|
|
327
|
+
this._affinityEscalations = 0;
|
|
328
|
+
this._trajectoryPredictionsMade = 0;
|
|
329
|
+
this._phasesSkipped = 0;
|
|
330
|
+
this._modelDowngrades = 0;
|
|
331
|
+
this._modelUpgrades = 0;
|
|
332
|
+
this._cascadeTierChanges = 0;
|
|
333
|
+
this._budgetWarnings = 0;
|
|
334
|
+
this._livingSpecUpdates = 0;
|
|
335
|
+
this._cacheCliffs = 0;
|
|
336
|
+
this._agentCacheRatios.clear();
|
|
337
|
+
this._coldWakes = 0;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// ─── Formatter ────────────────────────────────────────────────
|
|
341
|
+
/** Format a number with commas: 12345 → "12,345" */
|
|
342
|
+
function fmtNum(n) {
|
|
343
|
+
return n.toLocaleString('en-US');
|
|
344
|
+
}
|
|
345
|
+
/** Format a USD amount to 4 decimal places */
|
|
346
|
+
function fmtUsd(n) {
|
|
347
|
+
return `$${n.toFixed(4)}`;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Format a BenefitsSummary into a human-readable string.
|
|
351
|
+
* Only shows sections that have at least one non-zero metric.
|
|
352
|
+
* Uses plain text (no box-drawing characters).
|
|
353
|
+
*/
|
|
354
|
+
export function formatBenefitsSummary(summary) {
|
|
355
|
+
const sections = [];
|
|
356
|
+
// ─── Token Efficiency ───────────────────────────────────
|
|
357
|
+
const t = summary.tokens;
|
|
358
|
+
const tokenLines = [];
|
|
359
|
+
if (t.smartRoutingSaved > 0) {
|
|
360
|
+
tokenLines.push(` Smart routing saved ~${fmtNum(t.smartRoutingSaved)} tokens (filtered irrelevant phase outputs)`);
|
|
361
|
+
}
|
|
362
|
+
if (t.promptCompressionSaved > 0) {
|
|
363
|
+
tokenLines.push(` Prompt compression saved ~${fmtNum(t.promptCompressionSaved)} tokens (stripped markdown boilerplate)`);
|
|
364
|
+
}
|
|
365
|
+
if (t.verbatimCompactionSaved > 0) {
|
|
366
|
+
tokenLines.push(` Verbatim compaction saved ~${fmtNum(t.verbatimCompactionSaved)} tokens (replaced file reads with refs)`);
|
|
367
|
+
}
|
|
368
|
+
if (t.contextDecaySaved > 0) {
|
|
369
|
+
tokenLines.push(` Context decay saved ~${fmtNum(t.contextDecaySaved)} tokens (older phases summarized)`);
|
|
370
|
+
}
|
|
371
|
+
if (t.contextFilteringSaved > 0) {
|
|
372
|
+
tokenLines.push(` Context filtering saved ~${fmtNum(t.contextFilteringSaved)} tokens (per-agent file-scope filtering)`);
|
|
373
|
+
}
|
|
374
|
+
if (t.cacheSavingsUsd > 0) {
|
|
375
|
+
const cacheTag = t.cacheSavingsMeasured ? '(measured)' : '(est.)';
|
|
376
|
+
tokenLines.push(` Cache savings ${cacheTag}: ${fmtUsd(t.cacheSavingsUsd)} (shared prefix caching across parallel agents)`);
|
|
377
|
+
}
|
|
378
|
+
if (t.totalTokensSaved > 0) {
|
|
379
|
+
tokenLines.push(` Per-injection total: ~${fmtNum(t.totalTokensSaved)} tokens saved (${t.savingsPercent}% reduction)`);
|
|
380
|
+
}
|
|
381
|
+
if (t.compoundedTotalSaved > 0 && t.compoundedTotalSaved !== t.totalTokensSaved) {
|
|
382
|
+
tokenLines.push(` Compounded savings (conservative est.): ~${fmtNum(t.compoundedTotalSaved)} tokens (x avg ${fmtNum(summary.avgTurnsPerAgent)} turns/agent)`);
|
|
383
|
+
}
|
|
384
|
+
if (tokenLines.length > 0) {
|
|
385
|
+
sections.push(`TOKEN EFFICIENCY\n${tokenLines.join('\n')}`);
|
|
386
|
+
}
|
|
387
|
+
// ─── Knowledge Graph ────────────────────────────────────
|
|
388
|
+
const g = summary.graph;
|
|
389
|
+
const graphLines = [];
|
|
390
|
+
if (g.contextRoutingDecisions > 0) {
|
|
391
|
+
graphLines.push(` ${fmtNum(g.contextRoutingDecisions)} context routing decisions (graph-optimized context per agent)`);
|
|
392
|
+
}
|
|
393
|
+
if (g.coModHintsInjected > 0) {
|
|
394
|
+
graphLines.push(` ${fmtNum(g.coModHintsInjected)} co-modification hints (suggested related files from history)`);
|
|
395
|
+
}
|
|
396
|
+
if (g.confidenceGatesEvaluated > 0) {
|
|
397
|
+
const triggeredPart = g.gatesTriggered > 0
|
|
398
|
+
? `, ${fmtNum(g.gatesTriggered)} triggered (prevented low-quality handoff)`
|
|
399
|
+
: ', 0 triggered';
|
|
400
|
+
graphLines.push(` ${fmtNum(g.confidenceGatesEvaluated)} confidence gates evaluated${triggeredPart}`);
|
|
401
|
+
}
|
|
402
|
+
if (g.patternRunsRecorded > 0 && g.historicalSuccessRate != null) {
|
|
403
|
+
graphLines.push(` Pattern has ${g.historicalSuccessRate}% success rate across ${fmtNum(g.patternRunsRecorded)} historical runs`);
|
|
404
|
+
}
|
|
405
|
+
if (g.reviewFindingsRecorded > 0) {
|
|
406
|
+
graphLines.push(` ${fmtNum(g.reviewFindingsRecorded)} review findings recorded to graph`);
|
|
407
|
+
}
|
|
408
|
+
if (graphLines.length > 0) {
|
|
409
|
+
sections.push(`KNOWLEDGE GRAPH\n${graphLines.join('\n')}`);
|
|
410
|
+
}
|
|
411
|
+
// ─── ML/GNN Predictions ─────────────────────────────────
|
|
412
|
+
const m = summary.ml;
|
|
413
|
+
const mlLines = [];
|
|
414
|
+
if (m.gnnPredictionsMade > 0) {
|
|
415
|
+
mlLines.push(` ${fmtNum(m.gnnPredictionsMade)} GNN risk predictions, ${fmtNum(m.highRiskNodesIdentified)} high-risk nodes identified`);
|
|
416
|
+
}
|
|
417
|
+
if (m.agentsDroppedML > 0) {
|
|
418
|
+
mlLines.push(` ${fmtNum(m.agentsDroppedML)} agents dropped by predictive dropout (saved ~${fmtNum(m.dropoutTokensSaved)} tokens)`);
|
|
419
|
+
}
|
|
420
|
+
if (m.agentsDroppedRules > 0) {
|
|
421
|
+
mlLines.push(` ${fmtNum(m.agentsDroppedRules)} agents dropped by rule-based dropout`);
|
|
422
|
+
}
|
|
423
|
+
if (m.affinityEscalations > 0) {
|
|
424
|
+
mlLines.push(` ${fmtNum(m.affinityEscalations)} affinity escalation${m.affinityEscalations === 1 ? '' : 's'} (upgraded model for historically-difficult files)`);
|
|
425
|
+
}
|
|
426
|
+
if (m.trajectoryPredictionsMade > 0) {
|
|
427
|
+
mlLines.push(` ${fmtNum(m.trajectoryPredictionsMade)} trajectory predictions (success probability forecasts)`);
|
|
428
|
+
}
|
|
429
|
+
if (mlLines.length > 0) {
|
|
430
|
+
sections.push(`ML/GNN PREDICTIONS\n${mlLines.join('\n')}`);
|
|
431
|
+
}
|
|
432
|
+
// ─── Adaptive Optimization ──────────────────────────────
|
|
433
|
+
const a = summary.adaptive;
|
|
434
|
+
const adaptiveLines = [];
|
|
435
|
+
if (a.phasesSkipped > 0) {
|
|
436
|
+
adaptiveLines.push(` ${fmtNum(a.phasesSkipped)} phase${a.phasesSkipped === 1 ? '' : 's'} skipped (replanner determined ${a.phasesSkipped === 1 ? 'it was' : 'they were'} redundant)`);
|
|
437
|
+
}
|
|
438
|
+
if (a.modelDowngrades > 0) {
|
|
439
|
+
adaptiveLines.push(` ${fmtNum(a.modelDowngrades)} model downgrade${a.modelDowngrades === 1 ? '' : 's'} (used cheaper model where safe)`);
|
|
440
|
+
}
|
|
441
|
+
if (a.modelUpgrades > 0) {
|
|
442
|
+
adaptiveLines.push(` ${fmtNum(a.modelUpgrades)} model upgrade${a.modelUpgrades === 1 ? '' : 's'} (used more capable model where needed)`);
|
|
443
|
+
}
|
|
444
|
+
if (a.cascadeTierChanges > 0) {
|
|
445
|
+
adaptiveLines.push(` ${fmtNum(a.cascadeTierChanges)} cascade tier change${a.cascadeTierChanges === 1 ? '' : 's'} (started cheap, escalated on failure)`);
|
|
446
|
+
}
|
|
447
|
+
if (a.budgetWarnings > 0) {
|
|
448
|
+
adaptiveLines.push(` ${fmtNum(a.budgetWarnings)} budget warning${a.budgetWarnings === 1 ? '' : 's'} issued`);
|
|
449
|
+
}
|
|
450
|
+
if (a.livingSpecUpdates > 0) {
|
|
451
|
+
adaptiveLines.push(` Living spec refined ${fmtNum(a.livingSpecUpdates)} time${a.livingSpecUpdates === 1 ? '' : 's'} during execution`);
|
|
452
|
+
}
|
|
453
|
+
if (adaptiveLines.length > 0) {
|
|
454
|
+
sections.push(`ADAPTIVE OPTIMIZATION\n${adaptiveLines.join('\n')}`);
|
|
455
|
+
}
|
|
456
|
+
// ─── Cost line ──────────────────────────────────────────
|
|
457
|
+
let costLine = `COST: ${fmtUsd(summary.actualCostUsd)}`;
|
|
458
|
+
if (summary.historicalAvgCostUsd != null && summary.costDeltaUsd != null) {
|
|
459
|
+
const direction = summary.costDeltaUsd <= 0 ? 'saved' : 'over';
|
|
460
|
+
const delta = Math.abs(summary.costDeltaUsd);
|
|
461
|
+
costLine += ` (historical avg: ${fmtUsd(summary.historicalAvgCostUsd)}, ${direction} ${fmtUsd(delta)} vs baseline)`;
|
|
462
|
+
}
|
|
463
|
+
sections.push(costLine);
|
|
464
|
+
return sections.join('\n\n');
|
|
465
|
+
}
|
|
466
|
+
// ─── Styled Table Formatter ──────────────────────────────────
|
|
467
|
+
/** Measure display width in a monospace terminal (ASCII = 1 col) */
|
|
468
|
+
function displayWidth(s) {
|
|
469
|
+
let w = 0;
|
|
470
|
+
for (const ch of s) {
|
|
471
|
+
// East Asian wide characters take 2 columns; everything else takes 1.
|
|
472
|
+
// Simplified: codepoints above 0x1100 that are CJK/emoji get 2, rest get 1.
|
|
473
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
474
|
+
if ((cp >= 0x1100 && cp <= 0x115f) ||
|
|
475
|
+
(cp >= 0x2e80 && cp <= 0xa4cf) ||
|
|
476
|
+
(cp >= 0xac00 && cp <= 0xd7a3) ||
|
|
477
|
+
(cp >= 0xf900 && cp <= 0xfaff) ||
|
|
478
|
+
(cp >= 0xfe10 && cp <= 0xfe6f) ||
|
|
479
|
+
(cp >= 0xff01 && cp <= 0xff60) ||
|
|
480
|
+
(cp >= 0xffe0 && cp <= 0xffe6) ||
|
|
481
|
+
(cp >= 0x1f000 && cp <= 0x1faff) ||
|
|
482
|
+
(cp >= 0x20000 && cp <= 0x2fa1f)) {
|
|
483
|
+
w += 2;
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
w += 1;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return w;
|
|
490
|
+
}
|
|
491
|
+
/** Right-pad a string to a target display width */
|
|
492
|
+
function padTo(s, target) {
|
|
493
|
+
const gap = target - displayWidth(s);
|
|
494
|
+
return s + ' '.repeat(Math.max(gap, 0));
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Format a BenefitsSummary as a styled box-drawing table.
|
|
498
|
+
* Only shows sections that have at least one non-zero metric.
|
|
499
|
+
* No emojis inside the box — uses text labels to avoid alignment issues.
|
|
500
|
+
*/
|
|
501
|
+
export function formatBenefitsTable(summary) {
|
|
502
|
+
const sections = [];
|
|
503
|
+
// ─── Token Efficiency ───────────────────────────────────
|
|
504
|
+
const t = summary.tokens;
|
|
505
|
+
const tokenRows = [];
|
|
506
|
+
if (t.smartRoutingSaved > 0) {
|
|
507
|
+
tokenRows.push({
|
|
508
|
+
label: 'Smart routing',
|
|
509
|
+
value: `-${fmtNum(t.smartRoutingSaved)} tok`,
|
|
510
|
+
detail: 'filtered phase outputs',
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (t.promptCompressionSaved > 0) {
|
|
514
|
+
tokenRows.push({
|
|
515
|
+
label: 'Prompt compression',
|
|
516
|
+
value: `-${fmtNum(t.promptCompressionSaved)} tok`,
|
|
517
|
+
detail: 'stripped boilerplate',
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (t.verbatimCompactionSaved > 0) {
|
|
521
|
+
tokenRows.push({
|
|
522
|
+
label: 'Verbatim compaction',
|
|
523
|
+
value: `-${fmtNum(t.verbatimCompactionSaved)} tok`,
|
|
524
|
+
detail: 'replaced with refs',
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
if (t.contextDecaySaved > 0) {
|
|
528
|
+
tokenRows.push({
|
|
529
|
+
label: 'Context decay',
|
|
530
|
+
value: `-${fmtNum(t.contextDecaySaved)} tok`,
|
|
531
|
+
detail: 'older phases summarized',
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
if (t.contextFilteringSaved > 0) {
|
|
535
|
+
tokenRows.push({
|
|
536
|
+
label: 'Context filtering',
|
|
537
|
+
value: `-${fmtNum(t.contextFilteringSaved)} tok`,
|
|
538
|
+
detail: 'file-scope filtering',
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
if (t.cacheSavingsUsd > 0) {
|
|
542
|
+
const cacheLabel = t.cacheSavingsMeasured ? 'Cache savings (measured)' : 'Cache savings (est.)';
|
|
543
|
+
tokenRows.push({ label: cacheLabel, value: fmtUsd(t.cacheSavingsUsd), detail: 'prefix sharing' });
|
|
544
|
+
}
|
|
545
|
+
if (t.totalTokensSaved > 0) {
|
|
546
|
+
tokenRows.push({
|
|
547
|
+
label: 'Per-injection saved',
|
|
548
|
+
value: `-${fmtNum(t.totalTokensSaved)} tok`,
|
|
549
|
+
detail: `${t.savingsPercent}% reduction`,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (t.compoundedTotalSaved > 0 && t.compoundedTotalSaved !== t.totalTokensSaved) {
|
|
553
|
+
tokenRows.push({
|
|
554
|
+
label: 'Compounded (est.)',
|
|
555
|
+
value: `~${fmtNum(t.compoundedTotalSaved)} tok`,
|
|
556
|
+
detail: `x avg ${fmtNum(summary.avgTurnsPerAgent)} turns/agent (conservative)`,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (summary.basePayloadEstimateTokens > 0) {
|
|
560
|
+
const avgT = summary.avgTurnsPerAgent;
|
|
561
|
+
tokenRows.push({
|
|
562
|
+
label: 'Base-payload tax',
|
|
563
|
+
value: `+${fmtNum(summary.basePayloadEstimateTokens)} tok`,
|
|
564
|
+
detail: `${fmtNum(summary.totalAgentTurns)} turns x ~31k/turn (${fmtNum(summary.agentsExecuted)} agents, avg ${fmtNum(avgT)} turns)`,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
if (summary.cacheCliffs > 0) {
|
|
568
|
+
tokenRows.push({
|
|
569
|
+
label: 'Cache cliffs',
|
|
570
|
+
value: `${summary.cacheCliffs} detected`,
|
|
571
|
+
detail: 'cache_read ratio dropped >50%',
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
if (summary.coldWakes > 0) {
|
|
575
|
+
tokenRows.push({
|
|
576
|
+
label: 'Cold wakes',
|
|
577
|
+
value: `${summary.coldWakes} detected`,
|
|
578
|
+
detail: 'idle gap > prompt cache TTL',
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
if (tokenRows.length > 0) {
|
|
582
|
+
sections.push({ title: 'TOKEN EFFICIENCY', rows: tokenRows });
|
|
583
|
+
}
|
|
584
|
+
// ─── Knowledge Graph ────────────────────────────────────
|
|
585
|
+
const g = summary.graph;
|
|
586
|
+
const graphRows = [];
|
|
587
|
+
if (g.contextRoutingDecisions > 0) {
|
|
588
|
+
graphRows.push({
|
|
589
|
+
label: 'Context routing',
|
|
590
|
+
value: `${fmtNum(g.contextRoutingDecisions)}`,
|
|
591
|
+
detail: 'graph-optimized per agent',
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
if (g.coModHintsInjected > 0) {
|
|
595
|
+
graphRows.push({
|
|
596
|
+
label: 'Co-mod hints',
|
|
597
|
+
value: `${fmtNum(g.coModHintsInjected)}`,
|
|
598
|
+
detail: 'related files from history',
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
if (g.confidenceGatesEvaluated > 0) {
|
|
602
|
+
const detail = g.gatesTriggered > 0 ? `${g.gatesTriggered} blocked handoff` : 'all passed';
|
|
603
|
+
graphRows.push({ label: 'Confidence gates', value: `${fmtNum(g.confidenceGatesEvaluated)} eval`, detail });
|
|
604
|
+
}
|
|
605
|
+
if (g.patternRunsRecorded > 0 && g.historicalSuccessRate != null) {
|
|
606
|
+
graphRows.push({
|
|
607
|
+
label: 'Pattern history',
|
|
608
|
+
value: `${g.historicalSuccessRate}% success`,
|
|
609
|
+
detail: `${fmtNum(g.patternRunsRecorded)} runs`,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
if (g.reviewFindingsRecorded > 0) {
|
|
613
|
+
graphRows.push({
|
|
614
|
+
label: 'Review findings',
|
|
615
|
+
value: `${fmtNum(g.reviewFindingsRecorded)}`,
|
|
616
|
+
detail: 'recorded to graph',
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
if (graphRows.length > 0) {
|
|
620
|
+
sections.push({ title: 'KNOWLEDGE GRAPH', rows: graphRows });
|
|
621
|
+
}
|
|
622
|
+
// ─── ML/GNN ─────────────────────────────────────────────
|
|
623
|
+
const m = summary.ml;
|
|
624
|
+
const mlRows = [];
|
|
625
|
+
if (m.gnnPredictionsMade > 0) {
|
|
626
|
+
mlRows.push({
|
|
627
|
+
label: 'GNN risk scan',
|
|
628
|
+
value: `${fmtNum(m.highRiskNodesIdentified)} high-risk`,
|
|
629
|
+
detail: `${fmtNum(m.gnnPredictionsMade)} predictions`,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
if (m.agentsDroppedML > 0) {
|
|
633
|
+
mlRows.push({
|
|
634
|
+
label: 'Predictive dropout',
|
|
635
|
+
value: `${fmtNum(m.agentsDroppedML)} agents`,
|
|
636
|
+
detail: `-${fmtNum(m.dropoutTokensSaved)} tok saved`,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
if (m.agentsDroppedRules > 0) {
|
|
640
|
+
mlRows.push({
|
|
641
|
+
label: 'Rule-based dropout',
|
|
642
|
+
value: `${fmtNum(m.agentsDroppedRules)} agents`,
|
|
643
|
+
detail: 'redundant removed',
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
if (m.affinityEscalations > 0) {
|
|
647
|
+
mlRows.push({
|
|
648
|
+
label: 'Affinity escalation',
|
|
649
|
+
value: `${fmtNum(m.affinityEscalations)}`,
|
|
650
|
+
detail: 'model upgraded for hard files',
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
if (m.trajectoryPredictionsMade > 0) {
|
|
654
|
+
mlRows.push({
|
|
655
|
+
label: 'Trajectory forecast',
|
|
656
|
+
value: `${fmtNum(m.trajectoryPredictionsMade)}`,
|
|
657
|
+
detail: 'success probabilities',
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
if (mlRows.length > 0) {
|
|
661
|
+
sections.push({ title: 'ML / GNN', rows: mlRows });
|
|
662
|
+
}
|
|
663
|
+
// ─── Adaptive ───────────────────────────────────────────
|
|
664
|
+
const a = summary.adaptive;
|
|
665
|
+
const adaptiveRows = [];
|
|
666
|
+
if (a.phasesSkipped > 0) {
|
|
667
|
+
adaptiveRows.push({ label: 'Phases skipped', value: `${fmtNum(a.phasesSkipped)}`, detail: 'replanner: redundant' });
|
|
668
|
+
}
|
|
669
|
+
if (a.modelDowngrades > 0) {
|
|
670
|
+
adaptiveRows.push({
|
|
671
|
+
label: 'Model downgrades',
|
|
672
|
+
value: `${fmtNum(a.modelDowngrades)}`,
|
|
673
|
+
detail: 'cheaper where safe',
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
if (a.modelUpgrades > 0) {
|
|
677
|
+
adaptiveRows.push({
|
|
678
|
+
label: 'Model upgrades',
|
|
679
|
+
value: `${fmtNum(a.modelUpgrades)}`,
|
|
680
|
+
detail: 'stronger where needed',
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
if (a.cascadeTierChanges > 0) {
|
|
684
|
+
adaptiveRows.push({
|
|
685
|
+
label: 'Cascade changes',
|
|
686
|
+
value: `${fmtNum(a.cascadeTierChanges)}`,
|
|
687
|
+
detail: 'start cheap, escalate',
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (a.livingSpecUpdates > 0) {
|
|
691
|
+
adaptiveRows.push({
|
|
692
|
+
label: 'Living spec',
|
|
693
|
+
value: `${fmtNum(a.livingSpecUpdates)} updates`,
|
|
694
|
+
detail: 'refined during run',
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
if (adaptiveRows.length > 0) {
|
|
698
|
+
sections.push({ title: 'ADAPTIVE', rows: adaptiveRows });
|
|
699
|
+
}
|
|
700
|
+
// ─── Pre-compute cost bar to include in width calc ──────
|
|
701
|
+
const costParts = [`$${summary.actualCostUsd.toFixed(2)}`];
|
|
702
|
+
if (summary.historicalAvgCostUsd != null && summary.costDeltaUsd != null) {
|
|
703
|
+
const dir = summary.costDeltaUsd <= 0 ? 'saved' : 'over';
|
|
704
|
+
costParts.push(`${dir} $${Math.abs(summary.costDeltaUsd).toFixed(2)} vs avg`);
|
|
705
|
+
}
|
|
706
|
+
if (summary.tokens.savingsPercent > 0) {
|
|
707
|
+
costParts.push(`${summary.tokens.savingsPercent}% tokens saved`);
|
|
708
|
+
}
|
|
709
|
+
costParts.push(`${fmtNum(summary.agentsExecuted)} agents`);
|
|
710
|
+
costParts.push(`${fmtNum(summary.phasesExecuted)} phases`);
|
|
711
|
+
costParts.push(`${(summary.durationMs / 1000).toFixed(1)}s`);
|
|
712
|
+
let costText = costParts.join(' | ');
|
|
713
|
+
// ─── Compute column widths ──────────────────────────────
|
|
714
|
+
const allRows = sections.flatMap((s) => s.rows);
|
|
715
|
+
const treePrefix = 5; // " ├─ " or " └─ "
|
|
716
|
+
const labelW = Math.max(20, ...allRows.map((r) => displayWidth(r.label)));
|
|
717
|
+
const valueW = Math.max(14, ...allRows.map((r) => displayWidth(r.value)));
|
|
718
|
+
const detailW = Math.max(20, ...allRows.map((r) => displayWidth(r.detail)));
|
|
719
|
+
// Row content: " " + tree + label + " " + value + " " + detail + " "
|
|
720
|
+
const rowInnerW = 2 + treePrefix + labelW + 2 + valueW + 2 + detailW + 2;
|
|
721
|
+
// Cost bar wraps if needed — don't let it blow out the table width
|
|
722
|
+
const costInnerW = Math.min(2 + displayWidth(costText) + 2, rowInnerW + 10);
|
|
723
|
+
const innerW = Math.max(rowInnerW, costInnerW, 64);
|
|
724
|
+
// ─── Build table ────────────────────────────────────────
|
|
725
|
+
const H = '\u2500'; // ─
|
|
726
|
+
const V = '\u2502'; // │
|
|
727
|
+
const TL = '\u250c'; // ┌
|
|
728
|
+
const TR = '\u2510'; // ┐
|
|
729
|
+
const BL = '\u2514'; // └
|
|
730
|
+
const BR = '\u2518'; // ┘
|
|
731
|
+
const LT = '\u251c'; // ├
|
|
732
|
+
const RT = '\u2524'; // ┤
|
|
733
|
+
const bottomBorder = `${BL}${H.repeat(innerW)}${BR}`;
|
|
734
|
+
const separator = `${LT}${H.repeat(innerW)}${RT}`;
|
|
735
|
+
const emptyRow = `${V}${' '.repeat(innerW)}${V}`;
|
|
736
|
+
const lines = [];
|
|
737
|
+
// Title row
|
|
738
|
+
const title = ' Engine Benefits ';
|
|
739
|
+
const titlePad = innerW - displayWidth(title) - 1;
|
|
740
|
+
lines.push(`${TL}${H}${title}${H.repeat(Math.max(0, titlePad))}${TR}`);
|
|
741
|
+
// Cost bar at top — truncate if wider than table
|
|
742
|
+
const maxCostW = innerW - 4; // 2 padding each side
|
|
743
|
+
if (displayWidth(costText) > maxCostW) {
|
|
744
|
+
// Trim from the end (drop duration/phases if needed)
|
|
745
|
+
let trimmed = costText;
|
|
746
|
+
while (displayWidth(trimmed) > maxCostW - 3 && trimmed.length > 0) {
|
|
747
|
+
trimmed = trimmed.slice(0, -1);
|
|
748
|
+
}
|
|
749
|
+
costText = trimmed.trimEnd() + '...';
|
|
750
|
+
}
|
|
751
|
+
lines.push(`${V} ${padTo(costText, innerW - 2)}${V}`);
|
|
752
|
+
lines.push(separator);
|
|
753
|
+
for (let si = 0; si < sections.length; si++) {
|
|
754
|
+
const section = sections[si];
|
|
755
|
+
// Section header
|
|
756
|
+
lines.push(`${V} ${padTo(section.title, innerW - 2)}${V}`);
|
|
757
|
+
for (let ri = 0; ri < section.rows.length; ri++) {
|
|
758
|
+
const row = section.rows[ri];
|
|
759
|
+
const isLast = ri === section.rows.length - 1;
|
|
760
|
+
const tree = isLast ? '\u2514\u2500' : '\u251c\u2500'; // └─ or ├─
|
|
761
|
+
const labelPart = `${tree} ${padTo(row.label, labelW)}`;
|
|
762
|
+
const valuePart = padTo(row.value, valueW);
|
|
763
|
+
const detailPart = padTo(row.detail, detailW);
|
|
764
|
+
const content = `${labelPart} ${valuePart} ${detailPart}`;
|
|
765
|
+
lines.push(`${V} ${padTo(content, innerW - 2)}${V}`);
|
|
766
|
+
}
|
|
767
|
+
// Add separator between sections (not after last)
|
|
768
|
+
if (si < sections.length - 1) {
|
|
769
|
+
lines.push(emptyRow);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
lines.push(bottomBorder);
|
|
773
|
+
return lines.join('\n');
|
|
774
|
+
}
|
|
775
|
+
//# sourceMappingURL=benefits.js.map
|