@sema-agent/core 5.12.0 → 5.14.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 +320 -0
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +4 -0
- package/dist/agents/subagent.js +137 -43
- package/dist/brain/anthropic.js +33 -10
- package/dist/brain/context-overflow.d.ts +20 -0
- package/dist/brain/context-overflow.js +58 -0
- package/dist/brain/open-responses.js +24 -10
- package/dist/brain/openai.js +29 -11
- package/dist/brain/request-params.d.ts +2 -0
- package/dist/brain/request-params.js +16 -0
- package/dist/brain/stream-engine.d.ts +9 -1
- package/dist/brain/stream-engine.js +256 -27
- package/dist/brain/timeout.d.ts +1 -0
- package/dist/brain/timeout.js +1 -0
- package/dist/core/a2a.d.ts +2 -2
- package/dist/core/a2a.js +3 -3
- package/dist/core/ask-question.d.ts +47 -2
- package/dist/core/ask-question.js +209 -28
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/checkpoint-store.d.ts +49 -18
- package/dist/core/checkpoint-store.js +117 -4
- package/dist/core/compliance.d.ts +11 -0
- package/dist/core/compliance.js +34 -0
- package/dist/core/governance-codes.d.ts +12 -0
- package/dist/core/governance-codes.js +24 -0
- package/dist/core/hooks.d.ts +24 -2
- package/dist/core/hooks.js +97 -10
- package/dist/core/human-input-projection.d.ts +12 -0
- package/dist/core/human-input-projection.js +27 -0
- package/dist/core/locked-config.d.ts +27 -0
- package/dist/core/locked-config.js +42 -0
- package/dist/core/mcp.d.ts +7 -2
- package/dist/core/mcp.js +7 -7
- package/dist/core/memory-admission.d.ts +51 -0
- package/dist/core/memory-admission.js +159 -0
- package/dist/core/memory.d.ts +2 -0
- package/dist/core/memory.js +3 -2
- package/dist/core/retention.d.ts +36 -0
- package/dist/core/retention.js +31 -0
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +2 -2
- package/dist/core/runner/prepare-memory.d.ts +9 -0
- package/dist/core/runner/prepare-memory.js +28 -2
- package/dist/core/runner/prepare-task.d.ts +18 -6
- package/dist/core/runner/prepare-task.js +428 -38
- package/dist/core/runner/runtask.d.ts +3 -6
- package/dist/core/runner/runtask.js +258 -77
- package/dist/core/runner/tool-output-projection.js +1 -0
- package/dist/core/session-store.d.ts +4 -0
- package/dist/core/session-store.js +5 -0
- package/dist/core/session.d.ts +1 -0
- package/dist/core/store-contracts/background-agent-store-contract.js +19 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +62 -3
- package/dist/core/task-notification.d.ts +2 -0
- package/dist/core/task-notification.js +5 -3
- package/dist/core/task-registry-agent.d.ts +1 -0
- package/dist/core/task-registry-agent.js +6 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/tool-policy.d.ts +5 -0
- package/dist/core/tool-policy.js +2 -1
- package/dist/core/tool-result-store.d.ts +2 -0
- package/dist/core/tool-result-store.js +1 -0
- package/dist/core/types.d.ts +38 -2
- package/dist/core/wiring-manifest.d.ts +97 -0
- package/dist/core/wiring-manifest.js +186 -0
- package/dist/engine/compaction/compaction.js +2 -2
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +19 -2
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +7 -0
- package/dist/engine/llm/types.js +8 -1
- package/dist/engine/llm/validation.js +11 -1
- package/dist/engine/session/import-validate.d.ts +6 -1
- package/dist/engine/session/import-validate.js +29 -6
- package/dist/engine/session/memory-repo.d.ts +3 -1
- package/dist/engine/session/memory-repo.js +2 -2
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -4
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +4 -0
- package/dist/orchestration/run-workflow-tool.js +3 -0
- package/dist/orchestration/workflow-types.d.ts +8 -0
- package/dist/orchestration/workflow-types.js +14 -0
- package/dist/orchestration/workflow.d.ts +4 -0
- package/dist/orchestration/workflow.js +134 -5
- package/dist/prompts/default.js +1 -1
- package/dist/stores/file/checkpoint-store.d.ts +3 -5
- package/dist/stores/file/checkpoint-store.js +31 -2
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/session-store.d.ts +3 -1
- package/dist/stores/file/session-store.js +2 -2
- package/dist/stores/file/shared-ledger.js +8 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -0
- package/dist/tools/fs/bash-readonly-classifier.js +94 -0
- package/dist/tools/fs/fs-bash.js +31 -12
- package/dist/tools/fs/safety.js +34 -10
- package/package.json +1 -1
package/dist/agents/subagent.js
CHANGED
|
@@ -17,7 +17,9 @@ import { uuidv7 } from "../internal/harness.js";
|
|
|
17
17
|
import { addWorktree } from "../core/git-worktree-env.js";
|
|
18
18
|
import { shellQuote } from "../tools/fs/search.js";
|
|
19
19
|
import { BG_AGENT_REAP_STOP_ERROR } from "../core/task-registry.js";
|
|
20
|
+
import { readDurableOrgAdmission } from "../core/memory-admission.js";
|
|
20
21
|
import { extractErrorCode } from "../brain/errors.js";
|
|
22
|
+
import { governanceRetryClass } from "../core/governance-codes.js";
|
|
21
23
|
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, RUNNING_AGENT_OBSERVE_EVERY_BEATS } from "../config/defaults.js";
|
|
22
24
|
function rollupDelegatedCost(stats, nested) {
|
|
23
25
|
if (stats.costMicroUsd === undefined)
|
|
@@ -52,6 +54,39 @@ export function notifyResultField(result) {
|
|
|
52
54
|
? `${result.slice(0, BG_AGENT_NOTIFY_RESULT_MAX)}\n[result truncated: ${result.length} chars total — call TaskOutput for the full text]`
|
|
53
55
|
: result;
|
|
54
56
|
}
|
|
57
|
+
const HANDBACK_ASK_MESSAGE = "Subagent has finished and is handing back control to the main agent. Review the subagent's work and flag if any action may violate security policy.";
|
|
58
|
+
async function reviewHandback(opts) {
|
|
59
|
+
const { review, evidence, signal } = opts;
|
|
60
|
+
if (review === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (signal?.aborted === true)
|
|
63
|
+
return undefined;
|
|
64
|
+
if (!evidence.result && !evidence.steps && !evidence.edits && !evidence.partialText)
|
|
65
|
+
return undefined;
|
|
66
|
+
const verdict = await review.decider
|
|
67
|
+
.decide({
|
|
68
|
+
req: {
|
|
69
|
+
toolName: opts.toolName,
|
|
70
|
+
args: {
|
|
71
|
+
result: evidence.result?.slice(0, 2_000),
|
|
72
|
+
toolSteps: evidence.steps,
|
|
73
|
+
editedFiles: evidence.edits,
|
|
74
|
+
...(evidence.partialText ? { partialFindings: evidence.partialText.slice(0, 2_000) } : {}),
|
|
75
|
+
},
|
|
76
|
+
toolCallId: opts.toolCallId,
|
|
77
|
+
},
|
|
78
|
+
askMessage: HANDBACK_ASK_MESSAGE,
|
|
79
|
+
}, signal)
|
|
80
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
81
|
+
if (verdict.kind !== "block")
|
|
82
|
+
return undefined;
|
|
83
|
+
return `SECURITY WARNING: This subagent performed actions that may violate security policy. Reason: ${inlineUntrusted(verdict.reason ?? "(no reason given)")}. Review the subagent's actions carefully before acting on its output.`;
|
|
84
|
+
}
|
|
85
|
+
function withHandbackWarning(warning, result) {
|
|
86
|
+
if (warning === undefined)
|
|
87
|
+
return result;
|
|
88
|
+
return result ? `${warning}\n\n${result}` : warning;
|
|
89
|
+
}
|
|
55
90
|
export function inheritedManifestScopeFor(snapshot) {
|
|
56
91
|
if (!snapshot || snapshot.length === 0)
|
|
57
92
|
return undefined;
|
|
@@ -221,6 +256,7 @@ export function classifySubagentError(child) {
|
|
|
221
256
|
return undefined;
|
|
222
257
|
const code = child.errorCode ?? extractErrorCode(child.errorMessage);
|
|
223
258
|
const msg = (child.errorMessage ?? "").toLowerCase();
|
|
259
|
+
const governanceClass = code !== undefined ? governanceRetryClass(code) : undefined;
|
|
224
260
|
const errorKind = code === "rate_limit"
|
|
225
261
|
? "rate_limit"
|
|
226
262
|
: code === "server" && (msg.includes("overloaded") || msg.includes("529"))
|
|
@@ -229,10 +265,10 @@ export function classifySubagentError(child) {
|
|
|
229
265
|
? "timeout"
|
|
230
266
|
: code === "network" || code === "server"
|
|
231
267
|
? "network"
|
|
232
|
-
:
|
|
268
|
+
: governanceClass !== undefined
|
|
233
269
|
? "governance"
|
|
234
270
|
: "logic";
|
|
235
|
-
return { errorKind, retryable: errorKind
|
|
271
|
+
return { errorKind, retryable: errorKind === "governance" ? governanceClass === "transient" : errorKind !== "logic" };
|
|
236
272
|
}
|
|
237
273
|
export function completedAgentCard(child, extras) {
|
|
238
274
|
return {
|
|
@@ -249,6 +285,7 @@ export function completedAgentCard(child, extras) {
|
|
|
249
285
|
...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}),
|
|
250
286
|
...(child.retryAfterMs !== undefined ? { retryAfterMs: child.retryAfterMs } : {}),
|
|
251
287
|
...(child.degraded !== undefined ? { degraded: child.degraded } : {}),
|
|
288
|
+
...(extras.handbackWarning !== undefined ? { handbackWarning: extras.handbackWarning } : {}),
|
|
252
289
|
...(extras.error ? { error_kind: extras.error.errorKind, retryable: extras.error.retryable } : {}),
|
|
253
290
|
stats: child.stats,
|
|
254
291
|
...(child.model !== undefined ? { model: child.model, resolvedModel: child.model } : {}),
|
|
@@ -284,7 +321,7 @@ function durablePauseDelegationReport(reportLabel, child) {
|
|
|
284
321
|
function unparkedDurablePauseReason(d) {
|
|
285
322
|
return d.kind === "no_park_lane"
|
|
286
323
|
? `the agent hit an approval gate and paused durably, but this delegation cannot be parked ` +
|
|
287
|
-
`(parking needs
|
|
324
|
+
`(parking needs a durable agent row, a checkpoint store and the ` +
|
|
288
325
|
`durable-session capability). Its checkpoint ${d.checkpointToken} is still committed and the ` +
|
|
289
326
|
`approval must be resolved through the deployment's approval channel; the gated action never ran.`
|
|
290
327
|
: `the agent hit an approval gate and paused durably, but the park did not take (` +
|
|
@@ -295,6 +332,10 @@ function unparkedDurablePauseReason(d) {
|
|
|
295
332
|
function errorKindClause(c) {
|
|
296
333
|
return c !== undefined ? ` (error_kind: ${c.errorKind}, retryable: ${c.retryable})` : "";
|
|
297
334
|
}
|
|
335
|
+
function stripSpawnTurnQuestionFace(spec) {
|
|
336
|
+
const { onQuestion: spawnTurnQuestionFace, ...rest } = spec;
|
|
337
|
+
return { spec: rest, internalsFlag: spawnTurnQuestionFace !== undefined ? { questionFaceStripped: true } : {} };
|
|
338
|
+
}
|
|
298
339
|
const FAILED_SESSION_RETAIN_TTL_MS = 15 * 60 * 1000;
|
|
299
340
|
const PARTIAL_FINDINGS_MAX_CHARS = 1200;
|
|
300
341
|
const BG_NOTIFY_DRAIN_WINDOW_MS = 2_000;
|
|
@@ -501,6 +542,13 @@ export function createSubagentResume(deps) {
|
|
|
501
542
|
}
|
|
502
543
|
catch {
|
|
503
544
|
}
|
|
545
|
+
try {
|
|
546
|
+
const resumedOrgVerdict = entry.internalsSnapshot.ownOrgAdmissionRef?.current;
|
|
547
|
+
if (resumedOrgVerdict !== undefined)
|
|
548
|
+
deps.registry.recordBackgroundAgentOrgAdmission(deps.taskId, resumedOrgVerdict);
|
|
549
|
+
}
|
|
550
|
+
catch {
|
|
551
|
+
}
|
|
504
552
|
},
|
|
505
553
|
}
|
|
506
554
|
: {}),
|
|
@@ -528,6 +576,14 @@ export function createSubagentResume(deps) {
|
|
|
528
576
|
void _;
|
|
529
577
|
const child = await stream.result();
|
|
530
578
|
let status = child.status === "completed" ? "completed" : abort.signal.aborted ? "killed" : "failed";
|
|
579
|
+
const resumeHandbackWarning = await reviewHandback({
|
|
580
|
+
review: deps.currentAutoModeReview,
|
|
581
|
+
toolCallId: deps.parentToolCallId,
|
|
582
|
+
toolName: "Agent(handback)",
|
|
583
|
+
evidence: { result: child.result, steps: resumeStepRecorder.recentSteps(), edits: resumeStepRecorder.editedFiles() },
|
|
584
|
+
signal: abort.signal,
|
|
585
|
+
});
|
|
586
|
+
const resumeHandbackResult = withHandbackWarning(resumeHandbackWarning, child.result);
|
|
531
587
|
const errCodeRevive = status === "failed" ? (child.errorCode ?? extractErrorCode(child.errorMessage)) : undefined;
|
|
532
588
|
const errClassRevive = status === "failed"
|
|
533
589
|
? classifySubagentError({ status: "failed", ...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}), ...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}) })
|
|
@@ -536,7 +592,7 @@ export function createSubagentResume(deps) {
|
|
|
536
592
|
try {
|
|
537
593
|
const winner = deps.registry.settleRevivedAgent(deps.taskId, reviveCycle, {
|
|
538
594
|
status,
|
|
539
|
-
...resultSettleFields(
|
|
595
|
+
...resultSettleFields(resumeHandbackResult),
|
|
540
596
|
...(status !== "completed" ? { error: (child.errorMessage ?? `resumed run ${status}`).slice(0, 500) } : {}),
|
|
541
597
|
...(errCodeRevive !== undefined ? { errorCode: errCodeRevive } : {}),
|
|
542
598
|
...(errClassRevive !== undefined ? { retryable: errClassRevive.retryable, errorKind: errClassRevive.errorKind } : {}),
|
|
@@ -583,7 +639,7 @@ export function createSubagentResume(deps) {
|
|
|
583
639
|
summary: reviveTerminalSummary,
|
|
584
640
|
...(failReasonRevive !== undefined ? { error: failReasonRevive.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
585
641
|
...(status === "failed" && errCodeRevive !== undefined ? { errorCode: errCodeRevive } : {}),
|
|
586
|
-
...(
|
|
642
|
+
...(resumeHandbackResult ? { result: notifyResultField(resumeHandbackResult) } : {}),
|
|
587
643
|
...(status === "killed" && child.result ? { partial: true } : {}),
|
|
588
644
|
...resumeResidual(),
|
|
589
645
|
resumable: status !== "killed",
|
|
@@ -1302,7 +1358,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1302
1358
|
const runObserverLeg = async (spec) => {
|
|
1303
1359
|
if (!isObserverTaskId(sid))
|
|
1304
1360
|
throw new ObserverStoppedByUserError("observer identity revoked — delivery refused");
|
|
1305
|
-
const r = await opts.runner.runTask(spec);
|
|
1361
|
+
const r = await opts.runner.runTask(spec, ctx.interactionPosture !== undefined ? { parentInteractionPosture: ctx.interactionPosture } : undefined);
|
|
1306
1362
|
if (r.status !== "completed") {
|
|
1307
1363
|
if (r.errorCode === "resume.session_not_found") {
|
|
1308
1364
|
throw new ObserverResumeStateError(`observer session no longer exists${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
|
|
@@ -1441,8 +1497,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1441
1497
|
...(inheritedManifestScope ? { inheritedManifestScope } : {}),
|
|
1442
1498
|
...(treeScope !== undefined ? { registryScope: treeScope } : {}),
|
|
1443
1499
|
...(ctx.inheritedGateForChildren ? { inheritedGate: ctx.inheritedGateForChildren() } : {}),
|
|
1500
|
+
ownOrgAdmissionRef: { current: reviveClaim !== undefined ? readDurableOrgAdmission(reviveClaim.row) : undefined },
|
|
1444
1501
|
...(childDefaultPersona !== undefined ? { defaultSystemPrompt: childDefaultPersona } : {}),
|
|
1445
1502
|
isDelegatedChild: true,
|
|
1503
|
+
...(ctx.interactionPosture !== undefined ? { parentInteractionPosture: ctx.interactionPosture } : {}),
|
|
1446
1504
|
parentToolCallId: ctx.toolCallId,
|
|
1447
1505
|
...(childAgentName ? { agentName: childAgentName } : {}),
|
|
1448
1506
|
...(agentName !== undefined ? { explicitAgentName: agentName } : {}),
|
|
@@ -1511,11 +1569,12 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1511
1569
|
limits: childLimits,
|
|
1512
1570
|
signal,
|
|
1513
1571
|
});
|
|
1514
|
-
const tryRetainChild = async (ledger, entryTtlMs, preMintedSessionId, durableProbe, releaseAnchor) => {
|
|
1572
|
+
const tryRetainChild = async (ledger, entryTtlMs, preMintedSessionId, durableProbe, releaseAnchor, internalsExtra) => {
|
|
1515
1573
|
if (!ledger)
|
|
1516
1574
|
return undefined;
|
|
1517
1575
|
const childSessionId = preMintedSessionId ?? uuidv7();
|
|
1518
|
-
const
|
|
1576
|
+
const retainStrip = stripSpawnTurnQuestionFace(buildChildSpec(undefined));
|
|
1577
|
+
const { signal: _spawnSignal, ...plainSpec } = retainStrip.spec;
|
|
1519
1578
|
let entry = ledger.register(ctx.toolCallId, {
|
|
1520
1579
|
childSessionId,
|
|
1521
1580
|
runner: opts.runner,
|
|
@@ -1532,7 +1591,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1532
1591
|
...(plainSpec.deferTools !== undefined ? { deferTools: [...plainSpec.deferTools] } : {}),
|
|
1533
1592
|
...(plainSpec.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...plainSpec.alwaysLoadTools] } : {}),
|
|
1534
1593
|
}),
|
|
1535
|
-
internalsSnapshot: { ...childInternals },
|
|
1594
|
+
internalsSnapshot: { ...childInternals, ...retainStrip.internalsFlag, ...(internalsExtra ?? {}) },
|
|
1536
1595
|
release: async () => {
|
|
1537
1596
|
try {
|
|
1538
1597
|
await opts.runner.sessions.unpin?.(childSessionId);
|
|
@@ -1595,14 +1654,14 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1595
1654
|
if (ledger !== undefined && !ledger.disposed) {
|
|
1596
1655
|
if (opts.background)
|
|
1597
1656
|
ensureSessionReapHook(opts.background.registry);
|
|
1598
|
-
const
|
|
1657
|
+
const failedRetainStrip = stripSpawnTurnQuestionFace(buildChildSpec(undefined));
|
|
1658
|
+
const { signal: _spawnSignal, ...plainSpec } = failedRetainStrip.spec;
|
|
1599
1659
|
const entry = ledger.register(ctx.toolCallId, {
|
|
1600
1660
|
childSessionId,
|
|
1601
1661
|
runner: opts.runner,
|
|
1602
1662
|
ttlMs: FAILED_SESSION_RETAIN_TTL_MS,
|
|
1603
1663
|
...(childAgentName !== undefined ? { agentName: childAgentName } : {}),
|
|
1604
1664
|
...(agentName !== undefined ? { explicitAgentName: agentName } : {}),
|
|
1605
|
-
...(agentName !== undefined ? { explicitAgentName: agentName } : {}),
|
|
1606
1665
|
specSnapshot: Object.freeze({
|
|
1607
1666
|
...plainSpec,
|
|
1608
1667
|
tools: [...(plainSpec.tools ?? [])],
|
|
@@ -1613,7 +1672,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1613
1672
|
...(plainSpec.deferTools !== undefined ? { deferTools: [...plainSpec.deferTools] } : {}),
|
|
1614
1673
|
...(plainSpec.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...plainSpec.alwaysLoadTools] } : {}),
|
|
1615
1674
|
}),
|
|
1616
|
-
internalsSnapshot: { ...childInternals },
|
|
1675
|
+
internalsSnapshot: { ...childInternals, ...failedRetainStrip.internalsFlag },
|
|
1617
1676
|
release: releaseChild,
|
|
1618
1677
|
});
|
|
1619
1678
|
if (entry) {
|
|
@@ -1713,7 +1772,6 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1713
1772
|
else if (!sessionScopedBg)
|
|
1714
1773
|
ctx.signal.addEventListener("abort", onHostAbort, { once: true });
|
|
1715
1774
|
}
|
|
1716
|
-
const dropTurnBoundQuestionFace = sessionScopedBg ? { onQuestion: undefined } : {};
|
|
1717
1775
|
const shortDesc = `fork: ${(typeof a.description === "string" && a.description.trim() ? a.description.trim() : prompt).slice(0, 180)}`;
|
|
1718
1776
|
const bgOwner = sessionScopedBg ? ctx.sessionId : ctx.taskId ?? bg.owner;
|
|
1719
1777
|
const bgScope = treeScope;
|
|
@@ -1806,7 +1864,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1806
1864
|
startedAt: Date.now(),
|
|
1807
1865
|
});
|
|
1808
1866
|
const forkBgStartedAt = Date.now();
|
|
1809
|
-
const
|
|
1867
|
+
const bgForkSpecBase = { ...buildChildSpec(abort.signal), sessionId: forkedId, requireExistingSession: true, objective: forkObjective };
|
|
1868
|
+
const forkQuestionStrip = sessionScopedBg ? stripSpawnTurnQuestionFace(bgForkSpecBase) : undefined;
|
|
1869
|
+
const bgForkSpec = forkQuestionStrip !== undefined ? forkQuestionStrip.spec : bgForkSpecBase;
|
|
1810
1870
|
const s2ForkNotifyReady = (inject) => {
|
|
1811
1871
|
bg.registry.attachAgentNotify(taskId, inject);
|
|
1812
1872
|
};
|
|
@@ -1830,7 +1890,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1830
1890
|
const bgForkInternals = bgSink
|
|
1831
1891
|
? {
|
|
1832
1892
|
...forkInternals,
|
|
1893
|
+
...(forkQuestionStrip !== undefined ? forkQuestionStrip.internalsFlag : {}),
|
|
1833
1894
|
onNotifyInjectorReady: s2ForkNotifyReady,
|
|
1895
|
+
delegationTaskType: "background_agent",
|
|
1834
1896
|
onForwardEvent: (e) => {
|
|
1835
1897
|
try {
|
|
1836
1898
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -1861,7 +1923,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1861
1923
|
}
|
|
1862
1924
|
: {
|
|
1863
1925
|
...forkInternals,
|
|
1926
|
+
...(forkQuestionStrip !== undefined ? forkQuestionStrip.internalsFlag : {}),
|
|
1864
1927
|
onNotifyInjectorReady: s2ForkNotifyReady,
|
|
1928
|
+
delegationTaskType: "background_agent",
|
|
1865
1929
|
onForwardEvent: (e) => {
|
|
1866
1930
|
try {
|
|
1867
1931
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -1886,6 +1950,14 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1886
1950
|
}
|
|
1887
1951
|
catch {
|
|
1888
1952
|
}
|
|
1953
|
+
const forkBgWarning = await reviewHandback({
|
|
1954
|
+
review: ctx.autoModeReview,
|
|
1955
|
+
toolCallId: ctx.toolCallId,
|
|
1956
|
+
toolName: "Agent(fork handback)",
|
|
1957
|
+
evidence: { result: child.result, steps: stepRecorder.recentSteps(), edits: stepRecorder.editedFiles() },
|
|
1958
|
+
signal: abort.signal,
|
|
1959
|
+
});
|
|
1960
|
+
const forkBgResult = withHandbackWarning(forkBgWarning, child.result);
|
|
1889
1961
|
const okBg = child.status === "completed";
|
|
1890
1962
|
const reapedBg = !okBg && abort.signal.aborted;
|
|
1891
1963
|
const collateralBg = reapedBg && bg.registry.getAccessibleTask(taskId, { ...(bgOwner !== undefined ? { owner: bgOwner } : {}), ...(bgScope !== undefined ? { scope: bgScope } : {}) })?.status === "running";
|
|
@@ -1897,7 +1969,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1897
1969
|
const settledBg = bg.registry.settleBackgroundAgent(taskId, {
|
|
1898
1970
|
cycle: 0,
|
|
1899
1971
|
status: okBg ? "completed" : reapedBg ? "killed" : "failed",
|
|
1900
|
-
...resultSettleFields(
|
|
1972
|
+
...resultSettleFields(forkBgResult),
|
|
1901
1973
|
...(!okBg ? { error: reapedBg ? (collateralBg ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : child.errorMessage ?? String(child.status) } : {}),
|
|
1902
1974
|
...(errCodeFork !== undefined ? { errorCode: errCodeFork } : {}),
|
|
1903
1975
|
...(errClassFork !== undefined ? { retryable: errClassFork.retryable, errorKind: errClassFork.errorKind } : {}),
|
|
@@ -1948,7 +2020,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1948
2020
|
...(failReasonFork !== undefined ? { error: failReasonFork.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
1949
2021
|
...(settledBg === "failed" && errCodeFork !== undefined ? { errorCode: errCodeFork } : {}),
|
|
1950
2022
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
1951
|
-
...(
|
|
2023
|
+
...(forkBgResult ? { result: notifyResultField(forkBgResult) } : {}),
|
|
1952
2024
|
...(settledBg === "killed" && child.result ? { partial: true } : {}),
|
|
1953
2025
|
...residualFork,
|
|
1954
2026
|
resumable: resumableFork,
|
|
@@ -2051,6 +2123,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2051
2123
|
const wt = await finishWorktree();
|
|
2052
2124
|
return { isError: true, content: `Fork failed: the forked run threw (${e instanceof Error ? e.message : String(e)}).${wt ? `\n${wt}` : ""}`, details: { error: "fork run failed" } };
|
|
2053
2125
|
}
|
|
2126
|
+
const forkHandbackWarning = await reviewHandback({
|
|
2127
|
+
review: ctx.autoModeReview,
|
|
2128
|
+
toolCallId: ctx.toolCallId,
|
|
2129
|
+
toolName: "Agent(fork handback)",
|
|
2130
|
+
evidence: { result: forkChild.result, steps: stepRecorder.recentSteps(), edits: stepRecorder.editedFiles() },
|
|
2131
|
+
signal: ctx.signal,
|
|
2132
|
+
});
|
|
2054
2133
|
const nsf = forkChild.stats.nested;
|
|
2055
2134
|
ctx.reportUsage?.({
|
|
2056
2135
|
tokens: forkChild.stats.tokens + (nsf?.tokens ?? 0),
|
|
@@ -2064,6 +2143,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2064
2143
|
const wtLine = await finishWorktree();
|
|
2065
2144
|
const forkErr = classifySubagentError(forkChild);
|
|
2066
2145
|
const forkLines = [
|
|
2146
|
+
...(forkHandbackWarning ? [forkHandbackWarning, ""] : []),
|
|
2067
2147
|
`[Sub-agent report · ${FORK_SUBAGENT_TYPE}${a.description ? ` · ${String(a.description)}` : ""}] (forked session …${forkedId.slice(-8)})`,
|
|
2068
2148
|
`status: ${forkChild.status}`,
|
|
2069
2149
|
modelNote ?? "",
|
|
@@ -2085,6 +2165,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2085
2165
|
details: completedAgentCard(forkChild, {
|
|
2086
2166
|
subagentType: FORK_SUBAGENT_TYPE,
|
|
2087
2167
|
error: forkErr,
|
|
2168
|
+
...(forkHandbackWarning !== undefined ? { handbackWarning: forkHandbackWarning } : {}),
|
|
2088
2169
|
...(worktreeKeptPath !== undefined ? { worktreePath: worktreeKeptPath } : {}),
|
|
2089
2170
|
...(() => {
|
|
2090
2171
|
const ts = toolStatsCounter.snapshot();
|
|
@@ -2253,7 +2334,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2253
2334
|
: ctx.subagentRetain;
|
|
2254
2335
|
if (sessionScopedBg && bgRetainLedger)
|
|
2255
2336
|
ensureSessionReapHook(bg.registry);
|
|
2256
|
-
const bgRetain = await tryRetainChild(bgRetainLedger, sessionScopedBg && ctx.subagentRetain ? ctx.subagentRetain.ttlMs : undefined, bgChildSessionId, bgDurableProbe, () => bg.registry.releaseDurableTranscriptAnchor(taskId)
|
|
2337
|
+
const bgRetain = await tryRetainChild(bgRetainLedger, sessionScopedBg && ctx.subagentRetain ? ctx.subagentRetain.ttlMs : undefined, bgChildSessionId, bgDurableProbe, () => bg.registry.releaseDurableTranscriptAnchor(taskId), {
|
|
2338
|
+
delegationTaskType: "background_agent",
|
|
2339
|
+
});
|
|
2257
2340
|
if (bgRetain) {
|
|
2258
2341
|
bg.registry.markRetainedContinuation(taskId);
|
|
2259
2342
|
const origRelease = bgRetain.release;
|
|
@@ -2263,23 +2346,26 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2263
2346
|
};
|
|
2264
2347
|
}
|
|
2265
2348
|
const forwardDurableApproval = ctx.durableApprovalForChildren !== undefined &&
|
|
2266
|
-
agentName !== undefined &&
|
|
2267
2349
|
bg.agentStore !== undefined &&
|
|
2268
2350
|
bg.checkpointStore !== undefined &&
|
|
2269
2351
|
bg.ensureChildSessionDurable !== undefined;
|
|
2270
|
-
const
|
|
2352
|
+
const bgSpecBase = {
|
|
2271
2353
|
...buildChildSpec(abort.signal),
|
|
2272
2354
|
sessionId: bgChildSessionId,
|
|
2273
2355
|
...(forwardDurableApproval ? { durableApproval: { ...ctx.durableApprovalForChildren } } : {}),
|
|
2274
2356
|
...(reviveRow !== undefined ? { requireExistingSession: true } : {}),
|
|
2275
|
-
...(outlivesThisRequest ? { onQuestion: undefined } : {}),
|
|
2276
2357
|
};
|
|
2358
|
+
const bgQuestionStrip = outlivesThisRequest ? stripSpawnTurnQuestionFace(bgSpecBase) : undefined;
|
|
2359
|
+
const bgSpec = bgQuestionStrip !== undefined ? bgQuestionStrip.spec : bgSpecBase;
|
|
2277
2360
|
let reviveAttachedResolve;
|
|
2278
2361
|
const reviveAttached = reviveRow !== undefined ? new Promise((r) => (reviveAttachedResolve = r)) : undefined;
|
|
2279
2362
|
const s2NotifyReady = (inject) => {
|
|
2280
2363
|
bg.registry.attachAgentNotify(taskId, inject);
|
|
2281
2364
|
if (parkedResume !== undefined)
|
|
2282
2365
|
bg.registry.finalizeParkedResume(taskId);
|
|
2366
|
+
const legOrgVerdict = childInternals.ownOrgAdmissionRef?.current;
|
|
2367
|
+
if (legOrgVerdict !== undefined)
|
|
2368
|
+
bg.registry.recordBackgroundAgentOrgAdmission(taskId, legOrgVerdict);
|
|
2283
2369
|
reviveAttachedResolve?.();
|
|
2284
2370
|
};
|
|
2285
2371
|
const bgActivityBeat = createBgActivityBeat(ctx.toolCallId, (toolStarts) => {
|
|
@@ -2302,7 +2388,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2302
2388
|
const bgInternals = bgSink
|
|
2303
2389
|
? {
|
|
2304
2390
|
...childInternals,
|
|
2391
|
+
...(bgQuestionStrip !== undefined ? bgQuestionStrip.internalsFlag : {}),
|
|
2305
2392
|
onNotifyInjectorReady: s2NotifyReady,
|
|
2393
|
+
delegationTaskType: "background_agent",
|
|
2306
2394
|
onForwardEvent: (e) => {
|
|
2307
2395
|
try {
|
|
2308
2396
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -2333,7 +2421,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2333
2421
|
}
|
|
2334
2422
|
: {
|
|
2335
2423
|
...childInternals,
|
|
2424
|
+
...(bgQuestionStrip !== undefined ? bgQuestionStrip.internalsFlag : {}),
|
|
2336
2425
|
onNotifyInjectorReady: s2NotifyReady,
|
|
2426
|
+
delegationTaskType: "background_agent",
|
|
2337
2427
|
onForwardEvent: (e) => {
|
|
2338
2428
|
try {
|
|
2339
2429
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -2485,6 +2575,16 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2485
2575
|
catch {
|
|
2486
2576
|
}
|
|
2487
2577
|
const seqAtSettle = bgRetain?.cycleSeq ?? (parkedResume !== undefined ? (reviveRow?.seq ?? 1) + 1 : reviveRow?.seq);
|
|
2578
|
+
const bgHandbackWarning = isDurablePause(child.status)
|
|
2579
|
+
? undefined
|
|
2580
|
+
: await reviewHandback({
|
|
2581
|
+
review: ctx.autoModeReview,
|
|
2582
|
+
toolCallId: ctx.toolCallId,
|
|
2583
|
+
toolName: "Agent(handback)",
|
|
2584
|
+
evidence: { result: child.result, steps: stepRecorder.recentSteps(), edits: stepRecorder.editedFiles() },
|
|
2585
|
+
signal: abort.signal,
|
|
2586
|
+
});
|
|
2587
|
+
const bgHandbackResult = withHandbackWarning(bgHandbackWarning, child.result);
|
|
2488
2588
|
if (bgRetain) {
|
|
2489
2589
|
if (isDurablePause(child.status))
|
|
2490
2590
|
bgRetainLedger?.abandon(ctx.toolCallId);
|
|
@@ -2507,7 +2607,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2507
2607
|
return false;
|
|
2508
2608
|
}
|
|
2509
2609
|
};
|
|
2510
|
-
const wired =
|
|
2610
|
+
const wired = bg.agentStore !== undefined && cpStore !== undefined && bg.ensureChildSessionDurable !== undefined;
|
|
2511
2611
|
if (wired) {
|
|
2512
2612
|
const token = child.checkpointToken;
|
|
2513
2613
|
if (abort.signal.aborted) {
|
|
@@ -2566,7 +2666,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2566
2666
|
cycle: 0,
|
|
2567
2667
|
status: ok ? "completed" : reaped ? "killed" : "failed",
|
|
2568
2668
|
seq: seqAtSettle ?? 1,
|
|
2569
|
-
...resultSettleFields(
|
|
2669
|
+
...resultSettleFields(bgHandbackResult),
|
|
2570
2670
|
...(!ok ? { error: reaped ? (collateral ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : unparkedPauseReason ?? child.errorMessage ?? String(child.status) } : {}),
|
|
2571
2671
|
...(errCodeBg !== undefined ? { errorCode: errCodeBg } : {}),
|
|
2572
2672
|
...(errClassBg !== undefined ? { retryable: errClassBg.retryable, errorKind: errClassBg.errorKind } : {}),
|
|
@@ -2618,7 +2718,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2618
2718
|
...(failReasonBg !== undefined ? { error: failReasonBg.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
2619
2719
|
...(settled === "failed" && errCodeBg !== undefined ? { errorCode: errCodeBg } : {}),
|
|
2620
2720
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
2621
|
-
...(
|
|
2721
|
+
...(bgHandbackResult ? { result: notifyResultField(bgHandbackResult) } : {}),
|
|
2622
2722
|
...(settled === "killed" && child.result ? { partial: true } : {}),
|
|
2623
2723
|
...residual,
|
|
2624
2724
|
resumable: resumableBg,
|
|
@@ -2775,6 +2875,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2775
2875
|
...(opts.background ? { registry: opts.background.registry } : {}),
|
|
2776
2876
|
...(opts.onObserverError !== undefined ? { onNotifyError: (f) => opts.onObserverError?.(f.error, { site: f.site }) } : {}),
|
|
2777
2877
|
...(ctx.onQuestion !== undefined ? { currentOnQuestion: ctx.onQuestion } : {}),
|
|
2878
|
+
...(ctx.autoModeReview !== undefined ? { currentAutoModeReview: ctx.autoModeReview } : {}),
|
|
2778
2879
|
});
|
|
2779
2880
|
notifier.notify(() => ctx.onSubagentSpawn?.(createSteerHandle(stream, ctx.toolCallId, childAgentName, settled, {
|
|
2780
2881
|
resume,
|
|
@@ -2799,26 +2900,18 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2799
2900
|
child = await opts.runner.runTask(buildChildSpec(ctx.signal), childInternals);
|
|
2800
2901
|
}
|
|
2801
2902
|
await settleObserver(child.status);
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
askMessage: "Subagent has finished and is handing back control to the main agent. Review the subagent's work and flag if any action may violate security policy.",
|
|
2815
|
-
}, ctx.signal)
|
|
2816
|
-
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
2817
|
-
if (handbackVerdict.kind === "block") {
|
|
2818
|
-
handbackWarning = `SECURITY WARNING: This subagent performed actions that may violate security policy. Reason: ${inlineUntrusted(handbackVerdict.reason ?? "(no reason given)")}. Review the subagent's actions carefully before acting on its output.`;
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2903
|
+
const handbackWarning = await reviewHandback({
|
|
2904
|
+
review: ctx.autoModeReview,
|
|
2905
|
+
toolCallId: ctx.toolCallId,
|
|
2906
|
+
toolName: "Agent(handback)",
|
|
2907
|
+
evidence: {
|
|
2908
|
+
result: child.result,
|
|
2909
|
+
steps: stepRecorder.recentSteps(),
|
|
2910
|
+
edits: stepRecorder.editedFiles(),
|
|
2911
|
+
...(child.result || child.status === "completed" ? {} : { partialText: assistantTextTail.trim() }),
|
|
2912
|
+
},
|
|
2913
|
+
signal: ctx.signal,
|
|
2914
|
+
});
|
|
2822
2915
|
const ns = child.stats.nested;
|
|
2823
2916
|
ctx.reportUsage?.({
|
|
2824
2917
|
tokens: child.stats.tokens + (ns?.tokens ?? 0),
|
|
@@ -2903,6 +2996,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2903
2996
|
details: completedAgentCard(child, {
|
|
2904
2997
|
...(def !== undefined ? { subagentType: def.name } : {}),
|
|
2905
2998
|
error: errClass,
|
|
2999
|
+
...(handbackWarning !== undefined ? { handbackWarning } : {}),
|
|
2906
3000
|
...(modelFallback !== undefined ? { modelFallback } : {}),
|
|
2907
3001
|
...(worktreeKeptPath !== undefined ? { worktreePath: worktreeKeptPath } : {}),
|
|
2908
3002
|
...(() => {
|
package/dist/brain/anthropic.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js"
|
|
|
5
5
|
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
6
6
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
7
|
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, modelSupportsVision, sendableImages } from "./media-degrade.js";
|
|
8
|
-
import { ANTHROPIC_RESERVED, applyExtraBody, stripAuthHeaders } from "./request-params.js";
|
|
8
|
+
import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, stripAuthHeaders } from "./request-params.js";
|
|
9
9
|
import { isThinkingLevel, reasoningBudgetShare, resolveEffort } from "./reasoning.js";
|
|
10
10
|
import { runStreamingBrain } from "./stream-engine.js";
|
|
11
11
|
const MIN_THINKING_TOKENS = 1024;
|
|
@@ -233,7 +233,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
233
233
|
config,
|
|
234
234
|
httpLabel: "anthropic",
|
|
235
235
|
stallTimeouts: options?.stallTimeouts,
|
|
236
|
-
buildRequest: () => {
|
|
236
|
+
buildRequest: (overrides) => {
|
|
237
237
|
const apiKey = options?.apiKey ?? config.apiKey;
|
|
238
238
|
const root = (model.baseUrl || config.baseUrl || "https://api.anthropic.com").replace(/\/+$/, "");
|
|
239
239
|
const nonEmptyBlocks = context.systemBlocks?.filter((b) => b.text.length > 0);
|
|
@@ -248,7 +248,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
248
248
|
: undefined;
|
|
249
249
|
const body = {
|
|
250
250
|
model: model.id,
|
|
251
|
-
max_tokens: options?.maxTokens ?? model.maxTokens ?? config.defaultMaxTokens ?? 4096,
|
|
251
|
+
max_tokens: overrides?.maxOutputTokens ?? options?.maxTokens ?? model.maxTokens ?? config.defaultMaxTokens ?? 4096,
|
|
252
252
|
messages: toAnthropicMessages(context, model),
|
|
253
253
|
stream: true,
|
|
254
254
|
};
|
|
@@ -266,7 +266,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
266
266
|
body.thinking = { type: "adaptive" };
|
|
267
267
|
}
|
|
268
268
|
else {
|
|
269
|
-
const hardCap = options?.maxTokens !== undefined;
|
|
269
|
+
const hardCap = overrides?.maxOutputTokens !== undefined || options?.maxTokens !== undefined;
|
|
270
270
|
if (hardCap && body.max_tokens < MIN_THINKING_TOKENS * 2) {
|
|
271
271
|
body.thinking = undefined;
|
|
272
272
|
delete body.thinking;
|
|
@@ -322,11 +322,28 @@ export function createAnthropicBrain(config = {}) {
|
|
|
322
322
|
headers["anthropic-version"] = config.version ?? "2023-06-01";
|
|
323
323
|
if (apiKey)
|
|
324
324
|
headers["x-api-key"] = apiKey;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
const builtThinkingRequested = body.thinking !== undefined;
|
|
326
|
+
const builtMaxTokens = body.max_tokens;
|
|
327
|
+
const builtMaxTokensLane = overrides?.maxOutputTokens !== undefined
|
|
328
|
+
? "engine"
|
|
329
|
+
: options?.maxTokens !== undefined
|
|
330
|
+
? "options"
|
|
331
|
+
: model.maxTokens !== undefined
|
|
332
|
+
? "model"
|
|
333
|
+
: "config-default";
|
|
334
|
+
const wire = applyExtraBody(body, model.extraBody, ANTHROPIC_RESERVED);
|
|
335
|
+
return {
|
|
336
|
+
url: `${root}/v1/messages`,
|
|
337
|
+
headers,
|
|
338
|
+
body: JSON.stringify(wire),
|
|
339
|
+
outputCapTokens: effectiveOutputCap(wire, OUTPUT_CAP_KEYS.anthropic),
|
|
340
|
+
capDerivedPaths: [...OUTPUT_CAP_KEYS.anthropic, "thinking.budget_tokens"],
|
|
341
|
+
onCommitted: () => {
|
|
342
|
+
thinkingRequested = builtThinkingRequested;
|
|
343
|
+
sentMaxTokens = builtMaxTokens;
|
|
344
|
+
sentMaxTokensLane = builtMaxTokensLane;
|
|
345
|
+
},
|
|
346
|
+
};
|
|
330
347
|
},
|
|
331
348
|
makeParser: (ctrl) => {
|
|
332
349
|
const { out, partial } = ctrl;
|
|
@@ -588,7 +605,13 @@ export function createAnthropicBrain(config = {}) {
|
|
|
588
605
|
const safetyCut = stopReason === "refusal";
|
|
589
606
|
const contextWindowExceeded = stopReason === "model_context_window_exceeded";
|
|
590
607
|
const emptyNoFinish = noUsableContent && stopReason == null && !degenerate;
|
|
591
|
-
const maxTokensNote = `effective max_tokens=${sentMaxTokens ?? "?"} (from ${sentMaxTokensLane === "options"
|
|
608
|
+
const maxTokensNote = `effective max_tokens=${sentMaxTokens ?? "?"} (from ${sentMaxTokensLane === "options"
|
|
609
|
+
? "options.maxTokens"
|
|
610
|
+
: sentMaxTokensLane === "engine"
|
|
611
|
+
? "the engine's context-overflow recovery"
|
|
612
|
+
: sentMaxTokensLane === "model"
|
|
613
|
+
? "model.maxTokens"
|
|
614
|
+
: "config default"})`;
|
|
592
615
|
const errorMessage = degenerate
|
|
593
616
|
? DEGENERATE_MESSAGE
|
|
594
617
|
: safetyCut
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const FLOOR_OUTPUT_TOKENS = 3000;
|
|
2
|
+
export interface ContextOverflowReport {
|
|
3
|
+
inputTokens: number;
|
|
4
|
+
maxTokens: number;
|
|
5
|
+
contextLimit: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function parseContextOverflow(status: number, body: string): ContextOverflowReport | undefined;
|
|
8
|
+
export type OutputCapAdjustment = {
|
|
9
|
+
outcome: "adjust";
|
|
10
|
+
maxOutputTokens: number;
|
|
11
|
+
current: number;
|
|
12
|
+
} | {
|
|
13
|
+
outcome: "below_floor";
|
|
14
|
+
available: number;
|
|
15
|
+
} | {
|
|
16
|
+
outcome: "no_progress";
|
|
17
|
+
available: number;
|
|
18
|
+
current: number;
|
|
19
|
+
};
|
|
20
|
+
export declare function planOutputCapAdjustment(report: ContextOverflowReport, otherCapsInEffect?: readonly (number | undefined)[]): OutputCapAdjustment;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const FLOOR_OUTPUT_TOKENS = 3000;
|
|
2
|
+
const CONTEXT_HEADROOM_TOKENS = 1000;
|
|
3
|
+
const CONTEXT_OVERFLOW_RE = /^input length and `max_tokens` exceed context limit: (\d+) \+ (\d+) > (\d+)(?!\w|[.,]\d|\s\d|[+\-*/])/;
|
|
4
|
+
const CONTEXT_OVERFLOW_PREFIX = "input length and `max_tokens` exceed context limit:";
|
|
5
|
+
function providerDiagnostic(body) {
|
|
6
|
+
const trimmed = body.trim();
|
|
7
|
+
if (!trimmed.startsWith("{"))
|
|
8
|
+
return undefined;
|
|
9
|
+
let parsed;
|
|
10
|
+
try {
|
|
11
|
+
parsed = JSON.parse(trimmed);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
17
|
+
return undefined;
|
|
18
|
+
const err = parsed.error;
|
|
19
|
+
if (typeof err !== "object" || err === null || Array.isArray(err))
|
|
20
|
+
return undefined;
|
|
21
|
+
const message = err.message;
|
|
22
|
+
return typeof message === "string" ? message : undefined;
|
|
23
|
+
}
|
|
24
|
+
function reportFrom(diagnostic) {
|
|
25
|
+
const message = diagnostic.trimStart();
|
|
26
|
+
const m = CONTEXT_OVERFLOW_RE.exec(message);
|
|
27
|
+
if (m === null)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (message.slice(m[0].length).includes(CONTEXT_OVERFLOW_PREFIX))
|
|
30
|
+
return undefined;
|
|
31
|
+
const [, rawInput, rawMax, rawLimit] = m;
|
|
32
|
+
if (rawInput === undefined || rawMax === undefined || rawLimit === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const inputTokens = Number.parseInt(rawInput, 10);
|
|
35
|
+
const maxTokens = Number.parseInt(rawMax, 10);
|
|
36
|
+
const contextLimit = Number.parseInt(rawLimit, 10);
|
|
37
|
+
if (!Number.isSafeInteger(inputTokens) || !Number.isSafeInteger(maxTokens) || !Number.isSafeInteger(contextLimit)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (inputTokens <= contextLimit - maxTokens)
|
|
41
|
+
return undefined;
|
|
42
|
+
return { inputTokens, maxTokens, contextLimit };
|
|
43
|
+
}
|
|
44
|
+
export function parseContextOverflow(status, body) {
|
|
45
|
+
if (status !== 400)
|
|
46
|
+
return undefined;
|
|
47
|
+
const diagnostic = providerDiagnostic(body);
|
|
48
|
+
return diagnostic === undefined ? undefined : reportFrom(diagnostic);
|
|
49
|
+
}
|
|
50
|
+
export function planOutputCapAdjustment(report, otherCapsInEffect = []) {
|
|
51
|
+
const available = Math.max(0, report.contextLimit - report.inputTokens - CONTEXT_HEADROOM_TOKENS);
|
|
52
|
+
if (available < FLOOR_OUTPUT_TOKENS)
|
|
53
|
+
return { outcome: "below_floor", available };
|
|
54
|
+
const current = Math.min(report.maxTokens, ...otherCapsInEffect.filter((c) => c !== undefined));
|
|
55
|
+
if (available >= current)
|
|
56
|
+
return { outcome: "no_progress", available, current };
|
|
57
|
+
return { outcome: "adjust", maxOutputTokens: available, current };
|
|
58
|
+
}
|