dual-brain 0.2.30 → 0.3.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/.dual-brain/docs/claude-code-extension-points.md +32 -0
- package/.dual-brain/docs/data-tools-capabilities.md +181 -0
- package/.dual-brain/docs/ecosystem-tools.md +91 -0
- package/.dual-brain/docs/panel-handoff.md +124 -0
- package/.dual-brain/docs/ruflo-analysis.md +48 -0
- package/bin/dual-brain.mjs +56 -56
- package/dist/mcp-server/index.d.ts +27 -0
- package/dist/mcp-server/index.js +359 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/src/agent-protocol.d.ts +163 -0
- package/dist/src/agent-protocol.js +368 -0
- package/dist/src/agent-protocol.js.map +1 -0
- package/dist/src/agents/registry.d.ts +52 -0
- package/dist/src/agents/registry.js +393 -0
- package/dist/src/agents/registry.js.map +1 -0
- package/dist/src/awareness.d.ts +93 -0
- package/dist/src/awareness.js +406 -0
- package/dist/src/awareness.js.map +1 -0
- package/dist/src/brief.d.ts +48 -0
- package/dist/src/brief.js +179 -0
- package/dist/src/brief.js.map +1 -0
- package/dist/src/calibration.d.ts +32 -0
- package/dist/src/calibration.js +133 -0
- package/dist/src/calibration.js.map +1 -0
- package/dist/src/checkpoint.d.ts +33 -0
- package/dist/src/checkpoint.js +99 -0
- package/dist/src/checkpoint.js.map +1 -0
- package/dist/src/ci-triage.d.ts +33 -0
- package/dist/src/ci-triage.js +193 -0
- package/dist/src/ci-triage.js.map +1 -0
- package/dist/src/cognitive-loop.d.ts +56 -0
- package/dist/src/cognitive-loop.js +495 -0
- package/dist/src/cognitive-loop.js.map +1 -0
- package/dist/src/collaboration.d.ts +147 -0
- package/dist/src/collaboration.js +438 -0
- package/dist/src/collaboration.js.map +1 -0
- package/dist/src/context-intel.d.ts +47 -0
- package/dist/src/context-intel.js +156 -0
- package/dist/src/context-intel.js.map +1 -0
- package/dist/src/context.d.ts +53 -0
- package/dist/src/context.js +332 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/continuity.d.ts +89 -0
- package/dist/src/continuity.js +230 -0
- package/dist/src/continuity.js.map +1 -0
- package/dist/src/cost-tracker.d.ts +47 -0
- package/dist/src/cost-tracker.js +170 -0
- package/dist/src/cost-tracker.js.map +1 -0
- package/dist/src/debrief.d.ts +53 -0
- package/dist/src/debrief.js +222 -0
- package/dist/src/debrief.js.map +1 -0
- package/dist/src/decide.d.ts +96 -0
- package/dist/src/decide.js +744 -0
- package/dist/src/decide.js.map +1 -0
- package/dist/src/decompose.d.ts +39 -0
- package/dist/src/decompose.js +218 -0
- package/dist/src/decompose.js.map +1 -0
- package/dist/src/detect.d.ts +91 -0
- package/dist/src/detect.js +544 -0
- package/dist/src/detect.js.map +1 -0
- package/dist/src/dispatch.d.ts +154 -0
- package/dist/src/dispatch.js +1306 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/doctor.d.ts +421 -0
- package/dist/src/doctor.js +1689 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/engine.d.ts +70 -0
- package/dist/src/engine.js +155 -0
- package/dist/src/engine.js.map +1 -0
- package/dist/src/envelope.d.ts +36 -0
- package/dist/src/envelope.js +80 -0
- package/dist/src/envelope.js.map +1 -0
- package/dist/src/failure-memory.d.ts +55 -0
- package/dist/src/failure-memory.js +175 -0
- package/dist/src/failure-memory.js.map +1 -0
- package/dist/src/fx.d.ts +87 -0
- package/dist/src/fx.js +272 -0
- package/dist/src/fx.js.map +1 -0
- package/dist/src/governance.d.ts +93 -0
- package/dist/src/governance.js +261 -0
- package/dist/src/governance.js.map +1 -0
- package/dist/src/handoff.d.ts +11 -0
- package/dist/src/handoff.js +90 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/head-protocol.d.ts +76 -0
- package/dist/src/head-protocol.js +109 -0
- package/dist/src/head-protocol.js.map +1 -0
- package/dist/src/head.d.ts +222 -0
- package/dist/src/head.js +765 -0
- package/dist/src/head.js.map +1 -0
- package/dist/src/health.d.ts +132 -0
- package/dist/src/health.js +435 -0
- package/dist/src/health.js.map +1 -0
- package/dist/src/inbox.d.ts +70 -0
- package/dist/src/inbox.js +218 -0
- package/dist/src/inbox.js.map +1 -0
- package/dist/src/index.d.ts +33 -0
- package/dist/src/index.js +38 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/install-hooks.d.ts +13 -0
- package/dist/src/install-hooks.js +88 -0
- package/dist/src/install-hooks.js.map +1 -0
- package/dist/src/integrity.d.ts +59 -0
- package/dist/src/integrity.js +206 -0
- package/dist/src/integrity.js.map +1 -0
- package/dist/src/intelligence.d.ts +104 -0
- package/dist/src/intelligence.js +391 -0
- package/dist/src/intelligence.js.map +1 -0
- package/dist/src/ledger.d.ts +54 -0
- package/dist/src/ledger.js +179 -0
- package/dist/src/ledger.js.map +1 -0
- package/dist/src/living-docs.d.ts +14 -0
- package/dist/src/living-docs.js +197 -0
- package/dist/src/living-docs.js.map +1 -0
- package/dist/src/memory-tiers.d.ts +37 -0
- package/dist/src/memory-tiers.js +160 -0
- package/dist/src/memory-tiers.js.map +1 -0
- package/dist/src/model-profiles.d.ts +65 -0
- package/dist/src/model-profiles.js +568 -0
- package/dist/src/model-profiles.js.map +1 -0
- package/dist/src/models.d.ts +58 -0
- package/dist/src/models.js +327 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/narrative.d.ts +54 -0
- package/dist/src/narrative.js +163 -0
- package/dist/src/narrative.js.map +1 -0
- package/dist/src/nextstep.d.ts +16 -0
- package/dist/src/nextstep.js +103 -0
- package/dist/src/nextstep.js.map +1 -0
- package/dist/src/observer.d.ts +18 -0
- package/dist/src/observer.js +251 -0
- package/dist/src/observer.js.map +1 -0
- package/dist/src/outcome.d.ts +110 -0
- package/dist/src/outcome.js +377 -0
- package/dist/src/outcome.js.map +1 -0
- package/dist/src/pipeline.d.ts +167 -0
- package/dist/src/pipeline.js +1503 -0
- package/dist/src/pipeline.js.map +1 -0
- package/dist/src/playbook.d.ts +59 -0
- package/dist/src/playbook.js +238 -0
- package/dist/src/playbook.js.map +1 -0
- package/dist/src/pr-agent.d.ts +97 -0
- package/dist/src/pr-agent.js +195 -0
- package/dist/src/pr-agent.js.map +1 -0
- package/dist/src/predictive.d.ts +57 -0
- package/dist/src/predictive.js +230 -0
- package/dist/src/predictive.js.map +1 -0
- package/dist/src/profile.d.ts +294 -0
- package/dist/src/profile.js +1347 -0
- package/dist/src/profile.js.map +1 -0
- package/dist/src/prompt-audit.d.ts +22 -0
- package/dist/src/prompt-audit.js +194 -0
- package/dist/src/prompt-audit.js.map +1 -0
- package/dist/src/prompt-intel.d.ts +12 -0
- package/dist/src/prompt-intel.js +321 -0
- package/dist/src/prompt-intel.js.map +1 -0
- package/dist/src/provider-context.d.ts +121 -0
- package/dist/src/provider-context.js +222 -0
- package/dist/src/provider-context.js.map +1 -0
- package/dist/src/provider-manager.d.ts +92 -0
- package/dist/src/provider-manager.js +428 -0
- package/dist/src/provider-manager.js.map +1 -0
- package/dist/src/receipt.d.ts +87 -0
- package/dist/src/receipt.js +326 -0
- package/dist/src/receipt.js.map +1 -0
- package/dist/src/recommendations.d.ts +13 -0
- package/dist/src/recommendations.js +291 -0
- package/dist/src/recommendations.js.map +1 -0
- package/dist/src/redact.d.ts +15 -0
- package/dist/src/redact.js +129 -0
- package/dist/src/redact.js.map +1 -0
- package/dist/src/replit.d.ts +397 -0
- package/dist/src/replit.js +1160 -0
- package/dist/src/replit.js.map +1 -0
- package/dist/src/repo.d.ts +149 -0
- package/dist/src/repo.js +416 -0
- package/dist/src/repo.js.map +1 -0
- package/dist/src/revert.d.ts +30 -0
- package/dist/src/revert.js +166 -0
- package/dist/src/revert.js.map +1 -0
- package/dist/src/room.d.ts +102 -0
- package/dist/src/room.js +212 -0
- package/dist/src/room.js.map +1 -0
- package/dist/src/routing-advisor.d.ts +57 -0
- package/dist/src/routing-advisor.js +221 -0
- package/dist/src/routing-advisor.js.map +1 -0
- package/dist/src/self-correct.d.ts +40 -0
- package/dist/src/self-correct.js +137 -0
- package/dist/src/self-correct.js.map +1 -0
- package/dist/src/session-lock.d.ts +35 -0
- package/dist/src/session-lock.js +134 -0
- package/dist/src/session-lock.js.map +1 -0
- package/dist/src/session.d.ts +267 -0
- package/dist/src/session.js +1660 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/settings-tui.d.ts +5 -0
- package/dist/src/settings-tui.js +422 -0
- package/dist/src/settings-tui.js.map +1 -0
- package/dist/src/setup-flow.d.ts +63 -0
- package/dist/src/setup-flow.js +233 -0
- package/dist/src/setup-flow.js.map +1 -0
- package/dist/src/signal.d.ts +19 -0
- package/dist/src/signal.js +122 -0
- package/dist/src/signal.js.map +1 -0
- package/dist/src/simmer.d.ts +85 -0
- package/dist/src/simmer.js +224 -0
- package/dist/src/simmer.js.map +1 -0
- package/dist/src/state-export.d.ts +129 -0
- package/dist/src/state-export.js +233 -0
- package/dist/src/state-export.js.map +1 -0
- package/dist/src/strategy.d.ts +54 -0
- package/dist/src/strategy.js +95 -0
- package/dist/src/strategy.js.map +1 -0
- package/dist/src/subscription.d.ts +40 -0
- package/dist/src/subscription.js +189 -0
- package/dist/src/subscription.js.map +1 -0
- package/dist/src/templates.d.ts +208 -0
- package/dist/src/templates.js +238 -0
- package/dist/src/templates.js.map +1 -0
- package/dist/src/test.d.ts +9 -0
- package/dist/src/test.js +1173 -0
- package/dist/src/test.js.map +1 -0
- package/dist/src/think-engine.d.ts +67 -0
- package/dist/src/think-engine.js +412 -0
- package/dist/src/think-engine.js.map +1 -0
- package/dist/src/tui.d.ts +71 -0
- package/dist/src/tui.js +242 -0
- package/dist/src/tui.js.map +1 -0
- package/dist/src/types.d.ts +177 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/update-check.d.ts +7 -0
- package/dist/src/update-check.js +36 -0
- package/dist/src/update-check.js.map +1 -0
- package/dist/src/wave-planner.d.ts +30 -0
- package/dist/src/wave-planner.js +281 -0
- package/dist/src/wave-planner.js.map +1 -0
- package/hooks/head-guard.sh +41 -0
- package/hooks/task-classifier.mjs +328 -0
- package/hooks/vibe-router.mjs +387 -0
- package/package.json +29 -153
- package/src/agents/registry.mjs +0 -405
- package/src/awareness.mjs +0 -425
- package/src/brief.mjs +0 -266
- package/src/calibration.mjs +0 -148
- package/src/checkpoint.mjs +0 -109
- package/src/ci-triage.mjs +0 -191
- package/src/cognitive-loop.mjs +0 -562
- package/src/collaboration.mjs +0 -545
- package/src/context-intel.mjs +0 -158
- package/src/context.mjs +0 -389
- package/src/continuity.mjs +0 -298
- package/src/cost-tracker.mjs +0 -184
- package/src/debrief.mjs +0 -228
- package/src/decide.mjs +0 -1099
- package/src/decompose.mjs +0 -331
- package/src/detect.mjs +0 -702
- package/src/dispatch.mjs +0 -1447
- package/src/doctor.mjs +0 -1607
- package/src/envelope.mjs +0 -139
- package/src/failure-memory.mjs +0 -178
- package/src/fx.mjs +0 -276
- package/src/governance.mjs +0 -279
- package/src/handoff.mjs +0 -87
- package/src/head-protocol.mjs +0 -128
- package/src/head.mjs +0 -952
- package/src/health.mjs +0 -528
- package/src/inbox.mjs +0 -195
- package/src/index.mjs +0 -44
- package/src/install-hooks.mjs +0 -100
- package/src/integrity.mjs +0 -245
- package/src/intelligence.mjs +0 -447
- package/src/ledger.mjs +0 -196
- package/src/living-docs.mjs +0 -210
- package/src/memory-tiers.mjs +0 -193
- package/src/models.mjs +0 -363
- package/src/narrative.mjs +0 -169
- package/src/nextstep.mjs +0 -100
- package/src/observer.mjs +0 -241
- package/src/outcome.mjs +0 -400
- package/src/pipeline.mjs +0 -1711
- package/src/playbook.mjs +0 -257
- package/src/pr-agent.mjs +0 -214
- package/src/predictive.mjs +0 -250
- package/src/profile.mjs +0 -1411
- package/src/prompt-audit.mjs +0 -231
- package/src/prompt-intel.mjs +0 -325
- package/src/provider-context.mjs +0 -257
- package/src/receipt.mjs +0 -344
- package/src/recommendations.mjs +0 -296
- package/src/redact.mjs +0 -192
- package/src/replit.mjs +0 -1210
- package/src/repo.mjs +0 -445
- package/src/revert.mjs +0 -149
- package/src/routing-advisor.mjs +0 -204
- package/src/self-correct.mjs +0 -147
- package/src/session-lock.mjs +0 -160
- package/src/session.mjs +0 -1655
- package/src/settings-tui.mjs +0 -373
- package/src/setup-flow.mjs +0 -223
- package/src/signal.mjs +0 -115
- package/src/simmer.mjs +0 -241
- package/src/strategy.mjs +0 -235
- package/src/subscription.mjs +0 -212
- package/src/templates.mjs +0 -260
- package/src/think-engine.mjs +0 -428
- package/src/tui.mjs +0 -276
- package/src/update-check.mjs +0 -35
- package/src/wave-planner.mjs +0 -294
package/src/cost-tracker.mjs
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
// cost-tracker.mjs — Lightweight cost estimation and efficiency tracking for .dualbrain/costs.jsonl.
|
|
2
|
-
|
|
3
|
-
import { readFileSync, appendFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
|
|
6
|
-
const TOKEN_COSTS = {
|
|
7
|
-
'claude-opus-4-6': 0.03,
|
|
8
|
-
'claude-sonnet-4-6': 0.006,
|
|
9
|
-
'claude-haiku-4-5-20251001': 0.001,
|
|
10
|
-
'gpt-5.5': 0.04,
|
|
11
|
-
'o3': 0.03,
|
|
12
|
-
'gpt-4o': 0.005,
|
|
13
|
-
'gpt-4o-mini': 0.0003,
|
|
14
|
-
'default': 0.01,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export function estimateTokenCost(model, tokens) {
|
|
18
|
-
const rate = TOKEN_COSTS[model] ?? TOKEN_COSTS['default'];
|
|
19
|
-
return (tokens / 1000) * rate;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function trackCost(action, cwd = process.cwd()) {
|
|
23
|
-
try {
|
|
24
|
-
const dir = join(cwd, '.dualbrain');
|
|
25
|
-
mkdirSync(dir, { recursive: true });
|
|
26
|
-
const entry = {
|
|
27
|
-
timestamp: new Date().toISOString(),
|
|
28
|
-
action: action.action ?? 'execute',
|
|
29
|
-
model: action.model ?? 'default',
|
|
30
|
-
tokensEstimated: action.tokensEstimated ?? 0,
|
|
31
|
-
costEstimated: estimateTokenCost(action.model ?? 'default', action.tokensEstimated ?? 0),
|
|
32
|
-
tier: action.tier ?? 'standard',
|
|
33
|
-
wasCacheHit: action.wasCacheHit ?? false,
|
|
34
|
-
tokensSaved: action.tokensSaved ?? 0,
|
|
35
|
-
};
|
|
36
|
-
appendFileSync(join(dir, 'costs.jsonl'), JSON.stringify(entry) + '\n', 'utf8');
|
|
37
|
-
return entry;
|
|
38
|
-
} catch {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function readCostLines(cwd) {
|
|
44
|
-
const p = join(cwd, '.dualbrain', 'costs.jsonl');
|
|
45
|
-
if (!existsSync(p)) return [];
|
|
46
|
-
try {
|
|
47
|
-
return readFileSync(p, 'utf8').trim().split('\n').filter(Boolean).flatMap(line => {
|
|
48
|
-
try { return [JSON.parse(line)]; } catch { return []; }
|
|
49
|
-
});
|
|
50
|
-
} catch { return []; }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getCostSummary(cwd = process.cwd(), days = 7) {
|
|
54
|
-
const cutoff = new Date(Date.now() - days * 86400000).toISOString();
|
|
55
|
-
const all = readCostLines(cwd).filter(e => e.timestamp >= cutoff);
|
|
56
|
-
|
|
57
|
-
if (all.length === 0) {
|
|
58
|
-
return {
|
|
59
|
-
period: `${days} days`,
|
|
60
|
-
totalCost: 0, totalTokens: 0, totalActions: 0,
|
|
61
|
-
cacheHits: 0, tokensSaved: 0, costSaved: 0, savingsRate: 0,
|
|
62
|
-
byTier: {}, byModel: {}, trend: 'stable',
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let totalCost = 0, totalTokens = 0, cacheHits = 0, tokensSaved = 0;
|
|
67
|
-
const byTier = {};
|
|
68
|
-
const byModel = {};
|
|
69
|
-
|
|
70
|
-
for (const e of all) {
|
|
71
|
-
totalCost += e.costEstimated ?? 0;
|
|
72
|
-
totalTokens += e.tokensEstimated ?? 0;
|
|
73
|
-
if (e.wasCacheHit) { cacheHits++; tokensSaved += e.tokensSaved ?? 0; }
|
|
74
|
-
|
|
75
|
-
const tier = e.tier ?? 'standard';
|
|
76
|
-
if (!byTier[tier]) byTier[tier] = { count: 0, tokens: 0, cost: 0 };
|
|
77
|
-
byTier[tier].count += 1;
|
|
78
|
-
byTier[tier].tokens += e.tokensEstimated ?? 0;
|
|
79
|
-
byTier[tier].cost += e.costEstimated ?? 0;
|
|
80
|
-
|
|
81
|
-
const model = e.model ?? 'default';
|
|
82
|
-
if (!byModel[model]) byModel[model] = { count: 0, tokens: 0, cost: 0 };
|
|
83
|
-
byModel[model].count += 1;
|
|
84
|
-
byModel[model].tokens += e.tokensEstimated ?? 0;
|
|
85
|
-
byModel[model].cost += e.costEstimated ?? 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const costSaved = estimateTokenCost('default', tokensSaved);
|
|
89
|
-
const savingsRate = (tokensSaved + totalTokens) > 0
|
|
90
|
-
? tokensSaved / (tokensSaved + totalTokens)
|
|
91
|
-
: 0;
|
|
92
|
-
|
|
93
|
-
// Trend: compare first half vs second half savings rate
|
|
94
|
-
const mid = Math.floor(all.length / 2);
|
|
95
|
-
const first = all.slice(0, mid);
|
|
96
|
-
const second = all.slice(mid);
|
|
97
|
-
const halfSavings = (half) => {
|
|
98
|
-
const ts = half.reduce((s, e) => s + (e.tokensSaved ?? 0), 0);
|
|
99
|
-
const tt = half.reduce((s, e) => s + (e.tokensEstimated ?? 0), 0);
|
|
100
|
-
return (ts + tt) > 0 ? ts / (ts + tt) : 0;
|
|
101
|
-
};
|
|
102
|
-
let trend = 'stable';
|
|
103
|
-
if (all.length >= 4) {
|
|
104
|
-
const delta = halfSavings(second) - halfSavings(first);
|
|
105
|
-
if (delta > 0.05) trend = 'improving';
|
|
106
|
-
else if (delta < -0.05) trend = 'degrading';
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
period: `${days} days`,
|
|
111
|
-
totalCost, totalTokens, totalActions: all.length,
|
|
112
|
-
cacheHits, tokensSaved, costSaved, savingsRate,
|
|
113
|
-
byTier, byModel, trend,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function formatCostReport(summary) {
|
|
118
|
-
const {
|
|
119
|
-
period, totalCost, totalTokens, totalActions,
|
|
120
|
-
cacheHits, tokensSaved, costSaved, savingsRate,
|
|
121
|
-
byTier, byModel, trend,
|
|
122
|
-
} = summary;
|
|
123
|
-
|
|
124
|
-
const lines = [`COST EFFICIENCY (${period})`];
|
|
125
|
-
|
|
126
|
-
const fmtK = (n) => n >= 1000 ? `${Math.round(n / 1000)}K` : String(Math.round(n));
|
|
127
|
-
const fmtD = (n) => `~$${n.toFixed(2)}`;
|
|
128
|
-
|
|
129
|
-
lines.push(` Total: ${fmtD(totalCost)} (${fmtK(totalTokens)} tokens, ${totalActions} actions)`);
|
|
130
|
-
|
|
131
|
-
if (cacheHits > 0) {
|
|
132
|
-
const pct = Math.round(savingsRate * 100);
|
|
133
|
-
lines.push(` Saved: ${fmtD(costSaved)} (${fmtK(tokensSaved)} tokens from ${cacheHits} cache hits)`);
|
|
134
|
-
lines.push(` Savings rate: ${pct}%`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const tierOrder = ['recall', 'quick', 'standard', 'deep', 'ultra'];
|
|
138
|
-
const tierKeys = [...new Set([...tierOrder, ...Object.keys(byTier)])].filter(k => byTier[k]);
|
|
139
|
-
if (tierKeys.length > 0) {
|
|
140
|
-
lines.push('');
|
|
141
|
-
lines.push(' Tier breakdown:');
|
|
142
|
-
for (const tier of tierKeys) {
|
|
143
|
-
const t = byTier[tier];
|
|
144
|
-
const isRecall = tier === 'recall' && (t.cost < 0.001 || t.tokens === 0);
|
|
145
|
-
const costStr = isRecall ? '$0.00 (cache hits!)' : fmtD(t.cost);
|
|
146
|
-
lines.push(` ${tier.padEnd(10)}${String(t.count).padStart(4)} actions ${costStr}`);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const trendIcon = trend === 'improving' ? '↗' : trend === 'degrading' ? '↘' : '→';
|
|
151
|
-
lines.push('');
|
|
152
|
-
if (trend !== 'stable') {
|
|
153
|
-
const pct = Math.round(Math.abs(savingsRate) * 100);
|
|
154
|
-
lines.push(` Trend: ${trendIcon} ${trend} (savings rate ${trend === 'improving' ? 'up' : 'down'} vs last half)`);
|
|
155
|
-
} else {
|
|
156
|
-
lines.push(` Trend: ${trendIcon} stable`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return lines.join('\n');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function getEfficiencyScore(cwd = process.cwd()) {
|
|
163
|
-
const summary = getCostSummary(cwd, 7);
|
|
164
|
-
if (summary.totalActions === 0) return 50;
|
|
165
|
-
|
|
166
|
-
const TIER_WEIGHTS = { recall: 0, quick: 1, standard: 2, deep: 4, ultra: 6 };
|
|
167
|
-
const totalTierCost = Object.entries(summary.byTier).reduce((s, [tier, v]) => {
|
|
168
|
-
return s + (TIER_WEIGHTS[tier] ?? 2) * v.count;
|
|
169
|
-
}, 0);
|
|
170
|
-
const maxPossible = summary.totalActions * (TIER_WEIGHTS['ultra'] ?? 6);
|
|
171
|
-
const tierScore = maxPossible > 0 ? 1 - (totalTierCost / maxPossible) : 0.5;
|
|
172
|
-
|
|
173
|
-
const cacheScore = summary.savingsRate;
|
|
174
|
-
const trendBonus = summary.trend === 'improving' ? 10 : summary.trend === 'degrading' ? -10 : 0;
|
|
175
|
-
|
|
176
|
-
const raw = Math.round(
|
|
177
|
-
tierScore * 40 +
|
|
178
|
-
cacheScore * 40 +
|
|
179
|
-
20 +
|
|
180
|
-
trendBonus
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
return Math.max(1, Math.min(100, raw));
|
|
184
|
-
}
|
package/src/debrief.mjs
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
const STATUSES = ['success', 'partial', 'blocked', 'pivoted'];
|
|
2
|
-
|
|
3
|
-
function emptyDebrief() {
|
|
4
|
-
return {
|
|
5
|
-
status: 'partial',
|
|
6
|
-
findings: [],
|
|
7
|
-
blockers: [],
|
|
8
|
-
scopeChange: 'same',
|
|
9
|
-
confidence: 0.5,
|
|
10
|
-
recommendations: [],
|
|
11
|
-
artifacts: { filesChanged: [], filesRead: [], testsRun: 0 },
|
|
12
|
-
pivotReason: undefined,
|
|
13
|
-
unexpectedFindings: [],
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// ─── Parse debrief from raw agent output ─────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
const FILE_PATH_RE = /(?:^|\s)([\w./-]+\.(?:mjs|js|ts|tsx|json|md|py|sh|yaml|yml|css|html))/gm;
|
|
20
|
-
const TEST_COUNT_RE = /(\d+)\s*(?:tests?|specs?|assertions?)\s*(?:passed|ran|run|succeeded|ok)/i;
|
|
21
|
-
|
|
22
|
-
function extractByPatterns(text, patterns, minLen, maxLen, limit) {
|
|
23
|
-
const results = [];
|
|
24
|
-
for (const re of patterns) {
|
|
25
|
-
for (const m of text.matchAll(re)) {
|
|
26
|
-
const v = m[1].trim();
|
|
27
|
-
if (v.length > minLen && v.length < maxLen) results.push(v);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return [...new Set(results)].slice(0, limit);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const extractFindings = t => extractByPatterns(t, [
|
|
34
|
-
/(?:found|discovered|identified|noticed|see that|observed)\s+(?:that\s+)?(.+?)(?:\.|$)/gim,
|
|
35
|
-
/^[-•*]\s*(.+)$/gm,
|
|
36
|
-
], 10, 300, 20);
|
|
37
|
-
|
|
38
|
-
const extractBlockers = t => extractByPatterns(t, [
|
|
39
|
-
/(?:blocked by|couldn't|cannot|unable to|failed to|can't)\s+(.+?)(?:\.|$)/gim,
|
|
40
|
-
/(?:blocker|obstacle|issue|problem):\s*(.+?)(?:\.|$)/gim,
|
|
41
|
-
], 5, 200, 10);
|
|
42
|
-
|
|
43
|
-
const extractRecommendations = t => extractByPatterns(t, [
|
|
44
|
-
/(?:recommend|suggest|should|next step|consider)\s+(.+?)(?:\.|$)/gim,
|
|
45
|
-
/(?:TODO|ACTION):\s*(.+?)(?:\.|$)/gim,
|
|
46
|
-
], 5, 200, 10);
|
|
47
|
-
|
|
48
|
-
const extractUnexpected = t => extractByPatterns(t, [
|
|
49
|
-
/(?:also noticed|unexpected|surprisingly|interestingly|aside:)\s+(.+?)(?:\.|$)/gim,
|
|
50
|
-
], 10, 200, 5);
|
|
51
|
-
|
|
52
|
-
function inferStatus(text, blockers, findings) {
|
|
53
|
-
const lower = text.toLowerCase();
|
|
54
|
-
if (/pivoted|changed approach|switched to/i.test(lower)) return 'pivoted';
|
|
55
|
-
if (blockers.length > 0 && findings.length === 0) return 'blocked';
|
|
56
|
-
if (blockers.length > 0) return 'partial';
|
|
57
|
-
if (/complete|done|success|finished|all (?:tests )?pass/i.test(lower)) return 'success';
|
|
58
|
-
return 'partial';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function inferScopeChange(text) {
|
|
62
|
-
const lower = text.toLowerCase();
|
|
63
|
-
if (/scope.{0,20}(?:grew|larger|expanded|more than expected)/i.test(lower)) return 'larger';
|
|
64
|
-
if (/scope.{0,20}(?:smaller|reduced|simpler than)/i.test(lower)) return 'smaller';
|
|
65
|
-
if (/(?:different approach|completely different|pivoted to)/i.test(lower)) return 'different';
|
|
66
|
-
return 'same';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function inferConfidence(text, findings, blockers) {
|
|
70
|
-
if (/(?:very confident|high confidence|certain)/i.test(text)) return 0.9;
|
|
71
|
-
if (/(?:uncertain|not sure|low confidence|unsure)/i.test(text)) return 0.3;
|
|
72
|
-
if (blockers.length > 2) return 0.3;
|
|
73
|
-
if (blockers.length > 0) return 0.5;
|
|
74
|
-
if (findings.length > 3) return 0.8;
|
|
75
|
-
return 0.6;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function parseDebrief(rawOutput) {
|
|
79
|
-
if (!rawOutput || typeof rawOutput !== 'string') return emptyDebrief();
|
|
80
|
-
|
|
81
|
-
// Try JSON-structured debrief first
|
|
82
|
-
const jsonMatch = rawOutput.match(/```(?:json)?\s*(\{[\s\S]*?"status"[\s\S]*?\})\s*```/);
|
|
83
|
-
if (jsonMatch) {
|
|
84
|
-
try {
|
|
85
|
-
const parsed = JSON.parse(jsonMatch[1]);
|
|
86
|
-
if (STATUSES.includes(parsed.status)) {
|
|
87
|
-
const d = emptyDebrief();
|
|
88
|
-
return { ...d, ...parsed, artifacts: { ...d.artifacts, ...(parsed.artifacts || {}) } };
|
|
89
|
-
}
|
|
90
|
-
} catch {}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Best-effort extraction from prose
|
|
94
|
-
const findings = extractFindings(rawOutput);
|
|
95
|
-
const blockers = extractBlockers(rawOutput);
|
|
96
|
-
const recommendations = extractRecommendations(rawOutput);
|
|
97
|
-
const unexpectedFindings = extractUnexpected(rawOutput);
|
|
98
|
-
|
|
99
|
-
const filesAll = [...rawOutput.matchAll(FILE_PATH_RE)].map(m => m[1]);
|
|
100
|
-
const lower = rawOutput.toLowerCase();
|
|
101
|
-
const filesChanged = filesAll.filter(f => lower.includes(`wrote ${f.toLowerCase()}`) || lower.includes(`created ${f.toLowerCase()}`) || lower.includes(`modified ${f.toLowerCase()}`) || lower.includes(`edited ${f.toLowerCase()}`));
|
|
102
|
-
const filesRead = filesAll.filter(f => !filesChanged.includes(f));
|
|
103
|
-
|
|
104
|
-
const testMatch = rawOutput.match(TEST_COUNT_RE);
|
|
105
|
-
const testsRun = testMatch ? parseInt(testMatch[1], 10) : 0;
|
|
106
|
-
|
|
107
|
-
const pivotMatch = rawOutput.match(/pivoted?\s+(?:because|since|due to)\s+(.+?)(?:\.|$)/i);
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
status: inferStatus(rawOutput, blockers, findings),
|
|
111
|
-
findings,
|
|
112
|
-
blockers,
|
|
113
|
-
scopeChange: inferScopeChange(rawOutput),
|
|
114
|
-
confidence: inferConfidence(rawOutput, findings, blockers),
|
|
115
|
-
recommendations,
|
|
116
|
-
artifacts: { filesChanged, filesRead, testsRun },
|
|
117
|
-
pivotReason: pivotMatch ? pivotMatch[1].trim() : undefined,
|
|
118
|
-
unexpectedFindings,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ─── Generate debrief instruction for agent prompts ──────────────────────────
|
|
123
|
-
|
|
124
|
-
const TIER_EMPHASIS = {
|
|
125
|
-
search: 'Focus on: findings, confidence, unexpectedFindings.',
|
|
126
|
-
execute: 'Focus on: artifacts (filesChanged, testsRun), blockers, scopeChange.',
|
|
127
|
-
think: 'Focus on: recommendations, confidence, pivotReason.',
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
export function generateDebriefInstruction(tier, contract) {
|
|
131
|
-
const emphasis = TIER_EMPHASIS[tier] || TIER_EMPHASIS.execute;
|
|
132
|
-
const scope = contract?.scope ? ` Scope: ${contract.scope}.` : '';
|
|
133
|
-
return `\n---\nRESPONSE FORMAT: End your response with a JSON block:\n\`\`\`json\n{"status":"success|partial|blocked|pivoted","findings":[],"blockers":[],"scopeChange":"same|larger|smaller|different","confidence":0.0-1.0,"recommendations":[],"artifacts":{"filesChanged":[],"filesRead":[],"testsRun":0},"pivotReason":"...if pivoted","unexpectedFindings":[]}\n\`\`\`\n${emphasis}${scope}`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ─── Integrate debrief into HEAD's situation model ───────────────────────────
|
|
137
|
-
|
|
138
|
-
export function integrateDebrief(currentSituation, debrief) {
|
|
139
|
-
const sit = structuredClone(currentSituation);
|
|
140
|
-
|
|
141
|
-
// Update confidence on related ledger entries
|
|
142
|
-
if (!sit.ledger) sit.ledger = [];
|
|
143
|
-
for (const finding of debrief.findings) {
|
|
144
|
-
const existing = sit.ledger.find(e => e.topic && finding.toLowerCase().includes(e.topic.toLowerCase()));
|
|
145
|
-
if (existing) {
|
|
146
|
-
existing.confidence = Math.min(1, (existing.confidence || 0.5) + 0.1);
|
|
147
|
-
existing.resolved = true;
|
|
148
|
-
} else {
|
|
149
|
-
sit.ledger.push({ topic: finding.slice(0, 80), confidence: debrief.confidence, resolved: false });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Blockers create new uncertainty entries
|
|
154
|
-
for (const blocker of debrief.blockers) {
|
|
155
|
-
sit.ledger.push({ topic: blocker.slice(0, 80), confidence: 0.3, resolved: false, isBlocker: true });
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Scope delta
|
|
159
|
-
if (debrief.scopeChange !== 'same') {
|
|
160
|
-
if (!sit.material) sit.material = {};
|
|
161
|
-
sit.material.scopeTrend = debrief.scopeChange;
|
|
162
|
-
if (debrief.artifacts?.filesChanged) {
|
|
163
|
-
sit.material.touchedFiles = [
|
|
164
|
-
...new Set([...(sit.material.touchedFiles || []), ...debrief.artifacts.filesChanged]),
|
|
165
|
-
];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Recommendations → next wave planning
|
|
170
|
-
if (!sit.nextActions) sit.nextActions = [];
|
|
171
|
-
sit.nextActions.push(...debrief.recommendations);
|
|
172
|
-
|
|
173
|
-
// Unexpected findings → noticings
|
|
174
|
-
if (debrief.unexpectedFindings?.length) {
|
|
175
|
-
if (!sit.noticings) sit.noticings = [];
|
|
176
|
-
sit.noticings.push(...debrief.unexpectedFindings);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Overall confidence update
|
|
180
|
-
sit.confidence = sit.confidence
|
|
181
|
-
? sit.confidence * 0.7 + debrief.confidence * 0.3
|
|
182
|
-
: debrief.confidence;
|
|
183
|
-
|
|
184
|
-
return sit;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ─── Summarize a parallel wave of debriefs ───────────────────────────────────
|
|
188
|
-
|
|
189
|
-
export function summarizeWaveOutcome(debriefs) {
|
|
190
|
-
if (!debriefs || debriefs.length === 0) {
|
|
191
|
-
return {
|
|
192
|
-
overallStatus: 'blocked',
|
|
193
|
-
aggregateConfidence: 0,
|
|
194
|
-
allFindings: [],
|
|
195
|
-
allBlockers: [],
|
|
196
|
-
scopeDelta: 'same',
|
|
197
|
-
nextActions: [],
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const statusPriority = { blocked: 0, pivoted: 1, partial: 2, success: 3 };
|
|
202
|
-
const worstStatus = debriefs.reduce((worst, d) =>
|
|
203
|
-
(statusPriority[d.status] ?? 2) < (statusPriority[worst] ?? 2) ? d.status : worst,
|
|
204
|
-
'success'
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
const allFindings = [...new Set(debriefs.flatMap(d => d.findings || []))];
|
|
208
|
-
const allBlockers = [...new Set(debriefs.flatMap(d => d.blockers || []))];
|
|
209
|
-
const nextActions = [...new Set(debriefs.flatMap(d => d.recommendations || []))];
|
|
210
|
-
|
|
211
|
-
const aggregateConfidence = debriefs.reduce((sum, d) => sum + (d.confidence || 0.5), 0) / debriefs.length;
|
|
212
|
-
|
|
213
|
-
// Scope: any non-same scope wins, larger beats smaller, different beats all
|
|
214
|
-
const scopeDeltas = debriefs.map(d => d.scopeChange || 'same').filter(s => s !== 'same');
|
|
215
|
-
let scopeDelta = 'same';
|
|
216
|
-
if (scopeDeltas.includes('different')) scopeDelta = 'different';
|
|
217
|
-
else if (scopeDeltas.includes('larger')) scopeDelta = 'larger';
|
|
218
|
-
else if (scopeDeltas.includes('smaller')) scopeDelta = 'smaller';
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
overallStatus: worstStatus,
|
|
222
|
-
aggregateConfidence: Math.round(aggregateConfidence * 100) / 100,
|
|
223
|
-
allFindings,
|
|
224
|
-
allBlockers,
|
|
225
|
-
scopeDelta,
|
|
226
|
-
nextActions,
|
|
227
|
-
};
|
|
228
|
-
}
|