@sema-agent/core 3.0.0 → 4.0.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/CHANGELOG.md +2345 -0
- package/dist/agents/cascade.d.ts +2 -1
- package/dist/agents/cascade.js +31 -32
- package/dist/agents/cumulative-stats.d.ts +29 -0
- package/dist/agents/cumulative-stats.js +29 -0
- package/dist/agents/observer.d.ts +4 -0
- package/dist/agents/observer.js +11 -10
- package/dist/agents/repair-loop.js +27 -19
- package/dist/agents/retain-ledger.d.ts +2 -0
- package/dist/agents/retain-ledger.js +6 -5
- package/dist/agents/roster-store.js +3 -5
- package/dist/agents/subagent.js +66 -64
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +21 -24
- package/dist/agents/verify.js +15 -14
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/openai.js +24 -21
- package/dist/brain/stream-engine.js +5 -2
- package/dist/brain/stream-shared.d.ts +1 -0
- package/dist/brain/stream-shared.js +14 -0
- package/dist/brain/terminal-cause.d.ts +4 -0
- package/dist/brain/terminal-cause.js +12 -0
- package/dist/brain/tool-call-id.d.ts +1 -0
- package/dist/brain/tool-call-id.js +3 -0
- package/dist/brain/tool-call-repair.js +2 -1
- package/dist/core/auto-compaction.js +16 -25
- package/dist/core/background-agent-store.d.ts +22 -22
- package/dist/core/background-agent-store.js +19 -23
- package/dist/core/checkpoint-store.d.ts +6 -13
- package/dist/core/checkpoint-store.js +11 -5
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/hooks.d.ts +3 -1
- package/dist/core/hooks.js +8 -18
- package/dist/core/lsp.js +4 -3
- package/dist/core/mailbox-store.d.ts +7 -10
- package/dist/core/mcp.d.ts +7 -10
- package/dist/core/mcp.js +7 -3
- package/dist/core/memory-engine/engine.d.ts +0 -1
- package/dist/core/memory-engine/engine.js +24 -34
- package/dist/core/memory-engine/file-backend.d.ts +1 -2
- package/dist/core/memory-engine/file-backend.js +25 -36
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +60 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +1 -15
- package/dist/core/runner/assemble-result.d.ts +2 -7
- package/dist/core/runner/assemble-result.js +3 -4
- package/dist/core/runner/compaction-call-options.d.ts +7 -0
- package/dist/core/runner/compaction-call-options.js +30 -0
- package/dist/core/runner/prepare-task.d.ts +6 -23
- package/dist/core/runner/prepare-task.js +20 -53
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +65 -156
- package/dist/core/runner/session-rule-policy.js +4 -1
- package/dist/core/runner/teardown-bounded.d.ts +7 -0
- package/dist/core/runner/teardown-bounded.js +36 -0
- package/dist/core/runner/tool-disclosure.d.ts +2 -5
- package/dist/core/runner/turn-attachments.d.ts +5 -15
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/sensitive-path-policy.js +2 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/stub-env.d.ts +2 -6
- package/dist/core/surrogate-safe-slice.d.ts +4 -0
- package/dist/core/surrogate-safe-slice.js +18 -0
- package/dist/core/task-registry-monitor.js +8 -13
- package/dist/core/task-registry-shared.d.ts +22 -0
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +40 -57
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.js +2 -1
- package/dist/core/tool-policy.js +10 -5
- package/dist/core/tool-result-store.d.ts +1 -0
- package/dist/core/tool-result-store.js +17 -1
- package/dist/core/trace.d.ts +2 -9
- package/dist/core/types.d.ts +15 -12
- package/dist/core/untrusted-egress.js +7 -5
- package/dist/core/workflow-journal-store.d.ts +9 -20
- package/dist/core/workflow-run-store.d.ts +11 -10
- package/dist/core/workflow-run-store.js +22 -16
- package/dist/engine/compaction/utils.js +4 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +13 -33
- package/dist/engine/harness/types.d.ts +29 -67
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +11 -4
- package/dist/engine/loop/types.d.ts +11 -9
- package/dist/engine/session/memory-repo.d.ts +2 -6
- package/dist/engine/session/session.d.ts +5 -30
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/orchestration/run-spec.js +7 -4
- package/dist/orchestration/run-workflow-tool.js +3 -35
- package/dist/orchestration/workflow.d.ts +3 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/stores/file/background-agent-store.d.ts +3 -12
- package/dist/stores/file/background-agent-store.js +3 -41
- package/dist/stores/file/checkpoint-store.js +6 -6
- package/dist/stores/file/mailbox-store.d.ts +2 -6
- package/dist/stores/file/session-policy-store.js +15 -1
- package/dist/stores/file/session-store.d.ts +2 -6
- package/dist/stores/file/workflow-journal-store.d.ts +3 -11
- package/dist/stores/file/workflow-run-store.d.ts +3 -7
- package/dist/stores/file/workflow-run-store.js +4 -17
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
- package/dist/tools/fs/bash-readonly-classifier.js +60 -25
- package/dist/tools/fs/fs-bash.js +324 -96
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/notebook.d.ts +2 -5
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +43 -14
- package/dist/tools/fs/search.js +5 -5
- package/dist/tools/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +7 -5
- package/dist/tools/worktree.d.ts +2 -7
- package/dist/tools/worktree.js +3 -1
- package/package.json +6 -2
package/dist/agents/subagent.js
CHANGED
|
@@ -11,6 +11,7 @@ import { SUBAGENT_PROMPT } from "../prompts/default.js";
|
|
|
11
11
|
import { hasSessionFork } from "../core/session.js";
|
|
12
12
|
import { isDurablePause } from "./suspend-guard.js";
|
|
13
13
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
14
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
14
15
|
import { boundedRedactedSummary } from "../core/untrusted-egress.js";
|
|
15
16
|
import { uuidv7 } from "../internal/harness.js";
|
|
16
17
|
import { addWorktree } from "../core/git-worktree-env.js";
|
|
@@ -18,6 +19,17 @@ import { shellQuote } from "../tools/fs/search.js";
|
|
|
18
19
|
import { BG_AGENT_REAP_STOP_ERROR } from "../core/task-registry.js";
|
|
19
20
|
import { extractErrorCode } from "../brain/errors.js";
|
|
20
21
|
import { FORK_DEFAULT_MAX_TURNS, SESSION_BG_DEFAULT_TIMEOUT_SEC, RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, RUNNING_AGENT_OBSERVE_EVERY_BEATS } from "../config/defaults.js";
|
|
22
|
+
function rollupDelegatedCost(stats, nested) {
|
|
23
|
+
if (stats.costMicroUsd === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (nested !== undefined && nested.costMicroUsd === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
return stats.costMicroUsd + (nested?.costMicroUsd ?? 0);
|
|
28
|
+
}
|
|
29
|
+
function delegatedCostField(stats) {
|
|
30
|
+
const total = rollupDelegatedCost(stats, stats.nested);
|
|
31
|
+
return total !== undefined ? { costMicroUsd: total } : {};
|
|
32
|
+
}
|
|
21
33
|
export { FORK_DEFAULT_MAX_TURNS, SESSION_BG_DEFAULT_TIMEOUT_SEC, RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX };
|
|
22
34
|
import { SUBAGENT_RESUME_CAP, SubagentRetainLedger, getOrCreateSessionRetainLedger, ensureSessionReapHook, createResumePrompt, } from "./retain-ledger.js";
|
|
23
35
|
import { recordRosterSpawn } from "./roster-store.js";
|
|
@@ -264,6 +276,7 @@ function createSteerHandle(stream, parentToolCallId, agentName, settled, retain)
|
|
|
264
276
|
};
|
|
265
277
|
}
|
|
266
278
|
export function createSubagentResume(deps) {
|
|
279
|
+
const notifier = createSafeNotifier();
|
|
267
280
|
const resume = async (content) => {
|
|
268
281
|
const ledger = deps.ledger;
|
|
269
282
|
if (!ledger) {
|
|
@@ -403,6 +416,7 @@ export function createSubagentResume(deps) {
|
|
|
403
416
|
taskId: deps.taskId,
|
|
404
417
|
sessionScoped: deps.sessionScoped === true,
|
|
405
418
|
...(deps.rowAgentType !== undefined ? { agentType: deps.rowAgentType } : {}),
|
|
419
|
+
sessionId: entry.childSessionId,
|
|
406
420
|
transcriptId: entry.childSessionId,
|
|
407
421
|
...(deps.parentToolCallId !== undefined ? { parentToolCallId: deps.parentToolCallId } : {}),
|
|
408
422
|
progressTaskId: e.taskId,
|
|
@@ -442,11 +456,7 @@ export function createSubagentResume(deps) {
|
|
|
442
456
|
settleResolve = r;
|
|
443
457
|
});
|
|
444
458
|
if (deps.sink) {
|
|
445
|
-
|
|
446
|
-
deps.sink(createSteerHandle(stream, deps.parentToolCallId, entry.agentName, settled, { resume, childSessionId: entry.childSessionId }));
|
|
447
|
-
}
|
|
448
|
-
catch {
|
|
449
|
-
}
|
|
459
|
+
notifier.notify(() => deps.sink?.(createSteerHandle(stream, deps.parentToolCallId, entry.agentName, settled, { resume, childSessionId: entry.childSessionId })), "subagent.resume.sink");
|
|
450
460
|
}
|
|
451
461
|
void (async () => {
|
|
452
462
|
try {
|
|
@@ -493,8 +503,9 @@ export function createSubagentResume(deps) {
|
|
|
493
503
|
seq: entry.cycleSeq,
|
|
494
504
|
...(stoppedByRevive !== undefined ? { stoppedBy: stoppedByRevive } : {}),
|
|
495
505
|
summary: reviveTerminalSummary,
|
|
506
|
+
...resumeResidual(),
|
|
496
507
|
resumable: status !== "killed",
|
|
497
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns,
|
|
508
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
498
509
|
...(completionIdRevive !== undefined ? { completionId: completionIdRevive } : {}),
|
|
499
510
|
});
|
|
500
511
|
const resumeFrame = {
|
|
@@ -512,7 +523,7 @@ export function createSubagentResume(deps) {
|
|
|
512
523
|
...(status === "killed" && child.result ? { partial: true } : {}),
|
|
513
524
|
...resumeResidual(),
|
|
514
525
|
resumable: status !== "killed",
|
|
515
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns,
|
|
526
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
516
527
|
...(completionIdRevive !== undefined ? { completionId: completionIdRevive } : {}),
|
|
517
528
|
};
|
|
518
529
|
const gateOwner = ownsTerminalFacesRevive && status === "completed" && deps.notify !== undefined && deps.registry !== undefined && ledger.get(deps.parentToolCallId) === entry
|
|
@@ -777,6 +788,7 @@ export function agentWhenToUseText(def, lean = true) {
|
|
|
777
788
|
function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
778
789
|
const maxDepth = opts.maxDepth ?? DEFAULT_SUBAGENT_MAX_DEPTH;
|
|
779
790
|
const toolName = opts.name ?? DEFAULT_SUBAGENT_TOOL_NAME;
|
|
791
|
+
const notifier = createSafeNotifier();
|
|
780
792
|
const deploymentNames = new Set((opts.agents ?? []).map((a) => a.name));
|
|
781
793
|
const builtins = opts.builtinAgents === false ? [] : builtinAgentDefinitions(toolName).filter((d) => !deploymentNames.has(d.name));
|
|
782
794
|
const available = [...(opts.agents ?? []), ...builtins].filter((a) => !excluded.has(a.name));
|
|
@@ -1153,8 +1165,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1153
1165
|
: perCallModel ?? (def ? (def.model ?? ctx.model ?? opts.model) : opts.model);
|
|
1154
1166
|
const childSystemPrompt = def?.systemPrompt ?? opts.systemPrompt;
|
|
1155
1167
|
const childDefaultPersona = childSystemPrompt === undefined && !wantsFork ? SUBAGENT_PROMPT : undefined;
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1168
|
+
const defMaxTurns = typeof def?.maxTurns === "number" && Number.isFinite(def.maxTurns) && def.maxTurns > 0 ? def.maxTurns : undefined;
|
|
1169
|
+
const childLimits = defMaxTurns !== undefined
|
|
1170
|
+
? { ...opts.limits, maxTurns: defMaxTurns }
|
|
1158
1171
|
: wantsFork && opts.limits?.maxTurns === undefined
|
|
1159
1172
|
? { ...opts.limits, maxTurns: FORK_DEFAULT_MAX_TURNS }
|
|
1160
1173
|
: opts.limits;
|
|
@@ -1188,14 +1201,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1188
1201
|
else if (observerFailure === undefined) {
|
|
1189
1202
|
observerFailure = msg;
|
|
1190
1203
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
catch {
|
|
1198
|
-
}
|
|
1204
|
+
notifier.notify(() => opts.onObserverError?.(err, {
|
|
1205
|
+
...(def !== undefined ? { observedAgent: def.name } : {}),
|
|
1206
|
+
...(observerArm ? { observerAgent: observerArm.observerDefinition.name } : {}),
|
|
1207
|
+
}), "subagent.onObserverError");
|
|
1199
1208
|
};
|
|
1200
1209
|
const observedSteerRef = {};
|
|
1201
1210
|
let pendingTrigger = [];
|
|
@@ -1239,7 +1248,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1239
1248
|
...(ctx.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...ctx.alwaysLoadTools] } : {}),
|
|
1240
1249
|
...(ctx.promptProfile !== undefined ? { promptProfile: ctx.promptProfile } : {}),
|
|
1241
1250
|
enableBlockedReport: false,
|
|
1242
|
-
limits: {
|
|
1251
|
+
limits: {
|
|
1252
|
+
maxTurns: typeof observerDef.maxTurns === "number" && Number.isFinite(observerDef.maxTurns) && observerDef.maxTurns > 0 ? observerDef.maxTurns : 8,
|
|
1253
|
+
timeoutSec: 120,
|
|
1254
|
+
},
|
|
1243
1255
|
});
|
|
1244
1256
|
const runObserverLeg = async (spec) => {
|
|
1245
1257
|
if (!isObserverTaskId(sid))
|
|
@@ -1683,21 +1695,17 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1683
1695
|
resumable: false,
|
|
1684
1696
|
...(completionIdReap !== undefined ? { completionId: completionIdReap } : {}),
|
|
1685
1697
|
});
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
}, { priority: "later" });
|
|
1698
|
-
}
|
|
1699
|
-
catch {
|
|
1700
|
-
}
|
|
1698
|
+
notifier.notify(() => notify?.({
|
|
1699
|
+
task_id: taskId,
|
|
1700
|
+
task_type: "background_agent",
|
|
1701
|
+
toolUseId: ctx.toolCallId,
|
|
1702
|
+
status: "killed",
|
|
1703
|
+
stoppedBy: stoppedByReap,
|
|
1704
|
+
summary: summaryReap,
|
|
1705
|
+
sessionId: forkedId,
|
|
1706
|
+
resumable: false,
|
|
1707
|
+
...(completionIdReap !== undefined ? { completionId: completionIdReap } : {}),
|
|
1708
|
+
}, { priority: "later" }), "subagent.reapTerminalNotify");
|
|
1701
1709
|
});
|
|
1702
1710
|
if (agentName !== undefined) {
|
|
1703
1711
|
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, sessionId: forkedId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...((typeof childModel === "string" ? resolveModelDisplayLabel(childModel) : childModel?.id) !== undefined ? { model: typeof childModel === "string" ? childModel : childModel?.id } : {}), ...((ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: Date.now() });
|
|
@@ -1770,6 +1778,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1770
1778
|
kind: "tick",
|
|
1771
1779
|
taskId,
|
|
1772
1780
|
sessionScoped: sessionScopedBg === true,
|
|
1781
|
+
sessionId: forkedId,
|
|
1773
1782
|
transcriptId: forkedId,
|
|
1774
1783
|
parentToolCallId: ctx.toolCallId,
|
|
1775
1784
|
progressTaskId: e.taskId,
|
|
@@ -1805,7 +1814,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1805
1814
|
tokens: child.stats.tokens + (nsb?.tokens ?? 0),
|
|
1806
1815
|
turns: child.stats.turns + (nsb?.turns ?? 0),
|
|
1807
1816
|
tasks: 1 + (nsb?.tasks ?? 0),
|
|
1808
|
-
costMicroUsd: (child.stats
|
|
1817
|
+
costMicroUsd: rollupDelegatedCost(child.stats, nsb),
|
|
1809
1818
|
});
|
|
1810
1819
|
}
|
|
1811
1820
|
catch {
|
|
@@ -1857,7 +1866,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1857
1866
|
summary: forkTerminalSummary,
|
|
1858
1867
|
...residualFork,
|
|
1859
1868
|
resumable: resumableFork,
|
|
1860
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns,
|
|
1869
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
1861
1870
|
...(completionIdFork !== undefined ? { completionId: completionIdFork } : {}),
|
|
1862
1871
|
});
|
|
1863
1872
|
try {
|
|
@@ -1876,7 +1885,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1876
1885
|
...(settledBg === "killed" && child.result ? { partial: true } : {}),
|
|
1877
1886
|
...residualFork,
|
|
1878
1887
|
resumable: resumableFork,
|
|
1879
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns,
|
|
1888
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
1880
1889
|
...(completionIdFork !== undefined ? { completionId: completionIdFork } : {}),
|
|
1881
1890
|
}, { priority: "later" });
|
|
1882
1891
|
}
|
|
@@ -1980,7 +1989,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1980
1989
|
tokens: forkChild.stats.tokens + (nsf?.tokens ?? 0),
|
|
1981
1990
|
turns: forkChild.stats.turns + (nsf?.turns ?? 0),
|
|
1982
1991
|
tasks: 1 + (nsf?.tasks ?? 0),
|
|
1983
|
-
costMicroUsd: (forkChild.stats
|
|
1992
|
+
costMicroUsd: rollupDelegatedCost(forkChild.stats, nsf),
|
|
1984
1993
|
});
|
|
1985
1994
|
if (isDurablePause(forkChild.status)) {
|
|
1986
1995
|
return {
|
|
@@ -2131,21 +2140,17 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2131
2140
|
resumable: false,
|
|
2132
2141
|
...(completionIdReap !== undefined ? { completionId: completionIdReap } : {}),
|
|
2133
2142
|
});
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
}, { priority: "later" });
|
|
2146
|
-
}
|
|
2147
|
-
catch {
|
|
2148
|
-
}
|
|
2143
|
+
notifier.notify(() => notify?.({
|
|
2144
|
+
task_id: taskId,
|
|
2145
|
+
task_type: "background_agent",
|
|
2146
|
+
toolUseId: ctx.toolCallId,
|
|
2147
|
+
status: "killed",
|
|
2148
|
+
stoppedBy: stoppedByReap,
|
|
2149
|
+
summary: summaryReap,
|
|
2150
|
+
sessionId: bgChildSessionId,
|
|
2151
|
+
resumable: false,
|
|
2152
|
+
...(completionIdReap !== undefined ? { completionId: completionIdReap } : {}),
|
|
2153
|
+
}, { priority: "later" }), "subagent.reapTerminalNotify");
|
|
2149
2154
|
});
|
|
2150
2155
|
const bgDurableProbe = bg.registry.durableAgentRowProbe(taskId);
|
|
2151
2156
|
const bgRowConfirmed = async () => (bgDurableProbe ? await bgDurableProbe().catch(() => false) : false);
|
|
@@ -2249,6 +2254,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2249
2254
|
kind: "tick",
|
|
2250
2255
|
taskId,
|
|
2251
2256
|
sessionScoped: sessionScopedBg === true,
|
|
2257
|
+
sessionId: bgChildSessionId,
|
|
2252
2258
|
transcriptId: bgChildSessionId,
|
|
2253
2259
|
parentToolCallId: ctx.toolCallId,
|
|
2254
2260
|
progressTaskId: e.taskId,
|
|
@@ -2405,7 +2411,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2405
2411
|
tokens: child.stats.tokens + (ns2?.tokens ?? 0),
|
|
2406
2412
|
turns: child.stats.turns + (ns2?.turns ?? 0),
|
|
2407
2413
|
tasks: 1 + (ns2?.tasks ?? 0),
|
|
2408
|
-
costMicroUsd: (child.stats
|
|
2414
|
+
costMicroUsd: rollupDelegatedCost(child.stats, ns2),
|
|
2409
2415
|
});
|
|
2410
2416
|
}
|
|
2411
2417
|
catch {
|
|
@@ -2515,7 +2521,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2515
2521
|
summary: bgTerminalSummary,
|
|
2516
2522
|
...residual,
|
|
2517
2523
|
resumable: resumableBg,
|
|
2518
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns,
|
|
2524
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
2519
2525
|
...(completionIdBg !== undefined ? { completionId: completionIdBg } : {}),
|
|
2520
2526
|
});
|
|
2521
2527
|
const completionFrame = {
|
|
@@ -2533,7 +2539,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2533
2539
|
...(settled === "killed" && child.result ? { partial: true } : {}),
|
|
2534
2540
|
...residual,
|
|
2535
2541
|
resumable: resumableBg,
|
|
2536
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns,
|
|
2542
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
2537
2543
|
...(completionIdBg !== undefined ? { completionId: completionIdBg } : {}),
|
|
2538
2544
|
};
|
|
2539
2545
|
const gateOwner = ownsTerminalFaces && settled === "completed" && notify !== undefined ? bgRetain?.childSessionId : undefined;
|
|
@@ -2685,14 +2691,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2685
2691
|
sink: ctx.onSubagentSpawn,
|
|
2686
2692
|
...(opts.background ? { registry: opts.background.registry } : {}),
|
|
2687
2693
|
});
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
}));
|
|
2693
|
-
}
|
|
2694
|
-
catch {
|
|
2695
|
-
}
|
|
2694
|
+
notifier.notify(() => ctx.onSubagentSpawn?.(createSteerHandle(stream, ctx.toolCallId, childAgentName, settled, {
|
|
2695
|
+
resume,
|
|
2696
|
+
...(retainEntry ? { childSessionId: retainEntry.childSessionId } : {}),
|
|
2697
|
+
})), "subagent.onSubagentSpawn");
|
|
2696
2698
|
}
|
|
2697
2699
|
try {
|
|
2698
2700
|
for await (const ev of stream)
|
|
@@ -2737,7 +2739,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2737
2739
|
tokens: child.stats.tokens + (ns?.tokens ?? 0),
|
|
2738
2740
|
turns: child.stats.turns + (ns?.turns ?? 0),
|
|
2739
2741
|
tasks: 1 + (ns?.tasks ?? 0),
|
|
2740
|
-
costMicroUsd: (child.stats
|
|
2742
|
+
costMicroUsd: rollupDelegatedCost(child.stats, ns),
|
|
2741
2743
|
});
|
|
2742
2744
|
if (isDurablePause(child.status)) {
|
|
2743
2745
|
if (retainEntry)
|
package/dist/agents/teacher.d.ts
CHANGED
|
@@ -53,15 +53,7 @@ export interface TeacherRunResult extends TaskResult {
|
|
|
53
53
|
tokens: number;
|
|
54
54
|
tasks: number;
|
|
55
55
|
costMicroUsd?: number;
|
|
56
|
-
humanReview?:
|
|
57
|
-
count: number;
|
|
58
|
-
totalWaitMs: number;
|
|
59
|
-
gates: Array<{
|
|
60
|
-
kind: string;
|
|
61
|
-
waitMs: number;
|
|
62
|
-
decision?: string;
|
|
63
|
-
}>;
|
|
64
|
-
};
|
|
56
|
+
humanReview?: NonNullable<TaskResult["stats"]["humanReview"]>;
|
|
65
57
|
};
|
|
66
58
|
}
|
|
67
59
|
export declare function parseTeacherAdvice(text: string): TeacherAdvice;
|
package/dist/agents/teacher.js
CHANGED
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
3
3
|
import { isDefineToolProduct, stampDefineToolBrand } from "../core/tools.js";
|
|
4
4
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
5
|
+
import { buildCumulativeStats } from "./cumulative-stats.js";
|
|
5
6
|
export const TEACHER_PROMPT = `You are an expert advisor to a less-capable "student" agent that got stuck.
|
|
6
7
|
You receive the task and the student's recent failed attempts. Your job is to help the student RECOVER,
|
|
7
8
|
not to do the work for it.
|
|
@@ -84,6 +85,7 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
84
85
|
let studentTokens = 0;
|
|
85
86
|
let studentTurns = 0;
|
|
86
87
|
let studentCostMicro = 0;
|
|
88
|
+
let anyStudentLegUnpriced = false;
|
|
87
89
|
let studentPrompt = 0;
|
|
88
90
|
let studentTotalInput = 0;
|
|
89
91
|
let studentCached = 0;
|
|
@@ -202,6 +204,8 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
202
204
|
studentTokens += s.tokens;
|
|
203
205
|
studentTurns += s.turns;
|
|
204
206
|
studentCostMicro += s.costMicroUsd ?? 0;
|
|
207
|
+
if (s.costMicroUsd === undefined)
|
|
208
|
+
anyStudentLegUnpriced = true;
|
|
205
209
|
studentPrompt += s.promptTokens ?? 0;
|
|
206
210
|
studentTotalInput += s.totalInputTokens ?? 0;
|
|
207
211
|
studentCached += s.cachedTokens ?? 0;
|
|
@@ -442,34 +446,27 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
442
446
|
function finalize(result) {
|
|
443
447
|
return {
|
|
444
448
|
...result,
|
|
445
|
-
stats: {
|
|
446
|
-
...result.stats,
|
|
449
|
+
stats: buildCumulativeStats(result.stats, {
|
|
447
450
|
tokens: studentTokens,
|
|
448
451
|
turns: studentTurns,
|
|
449
452
|
costMicroUsd: studentCostMicro || undefined,
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
: undefined,
|
|
465
|
-
costBreakdown: {
|
|
466
|
-
llmRootMicroUsd: studentLlmRootMicro,
|
|
467
|
-
nestedSubagentMicroUsd: studentNestedCostMicro,
|
|
468
|
-
memoryConsolidationMicroUsd: 0,
|
|
469
|
-
compactionMicroUsd: studentCompactionMicro,
|
|
453
|
+
anyLegUnpriced: anyStudentLegUnpriced,
|
|
454
|
+
promptTokens: studentPrompt,
|
|
455
|
+
totalInputTokens: studentTotalInput,
|
|
456
|
+
cachedTokens: studentCached,
|
|
457
|
+
outputTokens: studentOutput,
|
|
458
|
+
cacheWriteTokens: studentCacheWrite,
|
|
459
|
+
cacheWriteTokensLong: studentCacheWriteLong,
|
|
460
|
+
llmRootMicroUsd: studentLlmRootMicro,
|
|
461
|
+
compactionMicroUsd: studentCompactionMicro,
|
|
462
|
+
nested: {
|
|
463
|
+
tokens: studentNestedTokens,
|
|
464
|
+
turns: studentNestedTurns,
|
|
465
|
+
tasks: studentNestedTasks,
|
|
466
|
+
costMicroUsd: studentNestedCostMicro,
|
|
470
467
|
},
|
|
471
|
-
humanReview:
|
|
472
|
-
},
|
|
468
|
+
humanReview: { count: hrCount, totalWaitMs: hrWaitMs, gates: hrGates },
|
|
469
|
+
}),
|
|
473
470
|
escalations,
|
|
474
471
|
teacherStats: {
|
|
475
472
|
tokens: teacherTokens,
|
package/dist/agents/verify.js
CHANGED
|
@@ -3,6 +3,7 @@ import { CODE_AGENT_PROMPT } from "../prompts/default.js";
|
|
|
3
3
|
import { releaseSession } from "./session-util.js";
|
|
4
4
|
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
5
5
|
import { delimitUntrusted, sanitizeUntrustedText } from "../core/untrusted-text.js";
|
|
6
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
6
7
|
export const VERIFICATION_PROMPT = `You are a verification specialist. Your job is NOT to confirm the implementation works — it is to try to BREAK it.
|
|
7
8
|
|
|
8
9
|
You have two documented failure patterns. First, verification avoidance: faced with a check, you find reasons not to run it — you read code, narrate what you would test, declare "PASS," and move on. Second, being seduced by the first 80%: a polished result or a passing test suite makes you inclined to pass it, not noticing the edge that crashes, the state that vanishes, the bad input that is unhandled. The first 80% is the easy part. Your entire value is in finding the last 20%.
|
|
@@ -58,6 +59,8 @@ export const VerdictSchema = Type.Object({
|
|
|
58
59
|
}),
|
|
59
60
|
evidence: Type.Optional(Type.String({ description: "Commands run + observed output backing the verdict." })),
|
|
60
61
|
});
|
|
62
|
+
const VERIFY_ON_ROUND_SITE = "verify.onRound";
|
|
63
|
+
const VERIFY_ON_VERIFIER_PAUSE_SITE = "verify.onVerifierPause";
|
|
61
64
|
function fixObjective(findings) {
|
|
62
65
|
const list = findings.length
|
|
63
66
|
? findings.map((f) => `- ${sanitizeUntrustedText(f)}`).join("\n")
|
|
@@ -82,7 +85,8 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
82
85
|
if (result.status !== "completed") {
|
|
83
86
|
return { ...result, verification: { verdict: "unverified", unverifiedReason: "impl_incomplete", rounds: 0, findings: [] } };
|
|
84
87
|
}
|
|
85
|
-
const maxRounds = Math.max(1, Math.floor(config.maxRounds
|
|
88
|
+
const maxRounds = Number.isFinite(config.maxRounds) ? Math.max(1, Math.floor(config.maxRounds)) : 2;
|
|
89
|
+
const notifier = createSafeNotifier();
|
|
86
90
|
const verifierTools = config.verifierTools ?? (specBase.tools ?? []).filter((t) => t.effect === "read");
|
|
87
91
|
const evidenceMode = config.evidence != null && config.evidence.trim() !== "";
|
|
88
92
|
const verifierPrompt = config.verifierPrompt ?? (evidenceMode ? STATIC_VERIFICATION_PROMPT : VERIFICATION_PROMPT);
|
|
@@ -126,6 +130,7 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
126
130
|
spend += v.cost;
|
|
127
131
|
verifierSpend += v.cost;
|
|
128
132
|
if (v.paused) {
|
|
133
|
+
const paused = v.paused;
|
|
129
134
|
outcome = {
|
|
130
135
|
verdict: "unverified",
|
|
131
136
|
unverifiedReason: "verifier_paused",
|
|
@@ -139,18 +144,14 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
139
144
|
...(v.paused.checkpointGate ? { checkpointGate: v.paused.checkpointGate } : {}),
|
|
140
145
|
},
|
|
141
146
|
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
}
|
|
153
|
-
config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings });
|
|
147
|
+
notifier.notify(() => config.onVerifierPause?.({
|
|
148
|
+
round,
|
|
149
|
+
status: paused.status,
|
|
150
|
+
sessionId: paused.sessionId,
|
|
151
|
+
...(paused.checkpointToken ? { checkpointToken: paused.checkpointToken } : {}),
|
|
152
|
+
...(paused.checkpointGate ? { checkpointGate: paused.checkpointGate } : {}),
|
|
153
|
+
}), VERIFY_ON_VERIFIER_PAUSE_SITE);
|
|
154
|
+
notifier.notify(() => config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings }), VERIFY_ON_ROUND_SITE);
|
|
154
155
|
break;
|
|
155
156
|
}
|
|
156
157
|
const rawVerdict = v.verdict?.verdict ?? "unverified";
|
|
@@ -164,7 +165,7 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
164
165
|
evidence: v.verdict?.evidence,
|
|
165
166
|
verifierCost: verifierSpend,
|
|
166
167
|
};
|
|
167
|
-
config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings });
|
|
168
|
+
notifier.notify(() => config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings }), VERIFY_ON_ROUND_SITE);
|
|
168
169
|
if (outcome.verdict !== "FAIL")
|
|
169
170
|
break;
|
|
170
171
|
if (round >= maxRounds)
|
package/dist/brain/anthropic.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {} from "../internal/llm.js";
|
|
2
2
|
import { BrainError } from "./errors.js";
|
|
3
3
|
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
|
|
4
|
-
import { createRepetitionPoll, createWalltimeGate } from "./stream-shared.js";
|
|
4
|
+
import { createRepetitionPoll, createWalltimeGate, parseStreamedToolArgs } from "./stream-shared.js";
|
|
5
|
+
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
5
6
|
import { WALLTIME_CUTOFF_MESSAGE } from "./walltime.js";
|
|
6
7
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
8
|
import { ANTHROPIC_RESERVED, applyExtraBody, stripAuthHeaders } from "./request-params.js";
|
|
@@ -201,23 +202,11 @@ function closeToolUseBlock(acc) {
|
|
|
201
202
|
return acc.closedTc;
|
|
202
203
|
if (!acc.toolName)
|
|
203
204
|
return undefined;
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
const parsed = JSON.parse(acc.toolJson);
|
|
208
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
209
|
-
return undefined;
|
|
210
|
-
args = parsed;
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
return undefined;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
args = {};
|
|
218
|
-
}
|
|
205
|
+
const args = parseStreamedToolArgs(acc.toolJson);
|
|
206
|
+
if (args === undefined)
|
|
207
|
+
return undefined;
|
|
219
208
|
if (!acc.toolId)
|
|
220
|
-
acc.toolId =
|
|
209
|
+
acc.toolId = mintFallbackToolCallId("toolu_");
|
|
221
210
|
acc.closedTc = { type: "toolCall", id: acc.toolId, name: acc.toolName, arguments: args };
|
|
222
211
|
return acc.closedTc;
|
|
223
212
|
}
|
|
@@ -339,6 +328,8 @@ export function createAnthropicBrain(config = {}) {
|
|
|
339
328
|
let stopReason = null;
|
|
340
329
|
let stopDetails;
|
|
341
330
|
let nextContentIndex = 0;
|
|
331
|
+
let malformedFrames = 0;
|
|
332
|
+
let malformedSample;
|
|
342
333
|
let degenerate = false;
|
|
343
334
|
let degenTextBlock;
|
|
344
335
|
const rep = createRepetitionPoll();
|
|
@@ -512,6 +503,8 @@ export function createAnthropicBrain(config = {}) {
|
|
|
512
503
|
evt = JSON.parse(data);
|
|
513
504
|
}
|
|
514
505
|
catch {
|
|
506
|
+
malformedFrames++;
|
|
507
|
+
malformedSample ??= data.slice(0, 160);
|
|
515
508
|
return;
|
|
516
509
|
}
|
|
517
510
|
handleEvent(evt);
|
|
@@ -563,6 +556,12 @@ export function createAnthropicBrain(config = {}) {
|
|
|
563
556
|
text: `\n[note: ${malformed.length} tool call(s) were truncated (stop_reason="${stopReason ?? "?"}") and dropped — re-issue them next turn: ${malformed.join("; ")}]`,
|
|
564
557
|
});
|
|
565
558
|
}
|
|
559
|
+
if (malformedFrames > 0) {
|
|
560
|
+
finalContent.push({
|
|
561
|
+
type: "text",
|
|
562
|
+
text: `\n[note: ${malformedFrames} provider stream frame(s) were not valid JSON and were dropped — the streamed output may be missing content (first sample: ${JSON.stringify(malformedSample)})]`,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
566
565
|
const truncatedEmpty = stopReason === "max_tokens" && noUsableContent && !toolError;
|
|
567
566
|
const dynamicCut = truncatedEmpty && options?.maxTokensDynamic === true;
|
|
568
567
|
const staticReasoningCut = truncatedEmpty && !dynamicCut && reasoningSeen && options?.staticReasoningCutDowngrade === true;
|
|
@@ -586,7 +585,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
586
585
|
? `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}) — the model emitted reasoning although no thinking was requested (a budget-ignoring gateway?) and it consumed the entire output budget; raise max_tokens`
|
|
587
586
|
: `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}); raise max_tokens`
|
|
588
587
|
: emptyNoFinish
|
|
589
|
-
? `[stream_torn] model stream ended with no content and no stop_reason — the response was lost (a torn stream or an in-band provider error frame)`
|
|
588
|
+
? `[stream_torn] model stream ended with no content and no stop_reason — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
|
|
590
589
|
: undefined));
|
|
591
590
|
const errored = walltime.cut ||
|
|
592
591
|
degenerate ||
|
|
@@ -595,9 +594,13 @@ export function createAnthropicBrain(config = {}) {
|
|
|
595
594
|
emptyNoFinish ||
|
|
596
595
|
(toolError !== undefined && noUsableContent) ||
|
|
597
596
|
(truncatedEmpty && !dynamicCut && !staticReasoningCut);
|
|
598
|
-
const errorKind =
|
|
599
|
-
? "
|
|
600
|
-
:
|
|
597
|
+
const errorKind = walltime.cut
|
|
598
|
+
? "walltime_cutoff"
|
|
599
|
+
: degenerate
|
|
600
|
+
? "degenerate"
|
|
601
|
+
: !safetyCut && toolError === undefined && truncatedEmpty && !dynamicCut && !staticReasoningCut
|
|
602
|
+
? "length_empty"
|
|
603
|
+
: undefined;
|
|
601
604
|
const finalMessage = {
|
|
602
605
|
...partial,
|
|
603
606
|
content: finalContent.length > 0 ? finalContent : [{ type: "text", text: "" }],
|