@sema-agent/core 5.13.0 → 5.15.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 +418 -0
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +4 -0
- package/dist/agents/subagent.js +133 -41
- 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/errors.js +21 -1
- 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/retry.d.ts +5 -0
- package/dist/brain/retry.js +16 -4
- package/dist/brain/stream-engine.d.ts +9 -1
- package/dist/brain/stream-engine.js +261 -28
- 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 +41 -17
- package/dist/core/checkpoint-store.js +137 -6
- 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/mcp.d.ts +7 -2
- package/dist/core/mcp.js +7 -7
- package/dist/core/memory-admission.d.ts +4 -0
- package/dist/core/memory-admission.js +3 -0
- package/dist/core/memory-engine/engine.d.ts +3 -1
- package/dist/core/memory-engine/engine.js +4 -3
- package/dist/core/memory-recall.d.ts +1 -1
- package/dist/core/memory-recall.js +3 -2
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +1 -0
- package/dist/core/runner/prepare-memory.js +3 -3
- package/dist/core/runner/prepare-task.d.ts +19 -6
- package/dist/core/runner/prepare-task.js +343 -35
- package/dist/core/runner/runtask.d.ts +3 -6
- package/dist/core/runner/runtask.js +290 -64
- package/dist/core/runner/tool-disclosure.d.ts +5 -0
- package/dist/core/runner/tool-disclosure.js +65 -16
- package/dist/core/runner/tool-output-projection.js +1 -0
- package/dist/core/runner/turn-attachments.d.ts +4 -0
- package/dist/core/runner/turn-attachments.js +15 -2
- package/dist/core/session-store.d.ts +3 -0
- package/dist/core/session-store.js +4 -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/task-tool-shape.js +4 -3
- package/dist/core/tool-policy.d.ts +5 -0
- package/dist/core/tool-policy.js +2 -1
- package/dist/core/trace.d.ts +6 -0
- package/dist/core/types.d.ts +32 -1
- 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 +8 -1
- 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/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 +10 -7
- package/dist/index.js +8 -5
- 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/prompts/supervisor.d.ts +2 -2
- package/dist/prompts/supervisor.js +5 -4
- 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.d.ts +1 -0
- package/dist/tools/fs/fs-bash.js +32 -13
- package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
- package/dist/tools/fs/gh-rate-limit.js +4 -3
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +1 -0
- package/dist/tools/fs/safety.js +34 -10
- package/package.json +1 -1
package/dist/agents/subagent.js
CHANGED
|
@@ -17,6 +17,7 @@ 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";
|
|
21
22
|
import { governanceRetryClass } from "../core/governance-codes.js";
|
|
22
23
|
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, RUNNING_AGENT_OBSERVE_EVERY_BEATS } from "../config/defaults.js";
|
|
@@ -53,6 +54,39 @@ export function notifyResultField(result) {
|
|
|
53
54
|
? `${result.slice(0, BG_AGENT_NOTIFY_RESULT_MAX)}\n[result truncated: ${result.length} chars total — call TaskOutput for the full text]`
|
|
54
55
|
: result;
|
|
55
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
|
+
}
|
|
56
90
|
export function inheritedManifestScopeFor(snapshot) {
|
|
57
91
|
if (!snapshot || snapshot.length === 0)
|
|
58
92
|
return undefined;
|
|
@@ -251,6 +285,7 @@ export function completedAgentCard(child, extras) {
|
|
|
251
285
|
...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}),
|
|
252
286
|
...(child.retryAfterMs !== undefined ? { retryAfterMs: child.retryAfterMs } : {}),
|
|
253
287
|
...(child.degraded !== undefined ? { degraded: child.degraded } : {}),
|
|
288
|
+
...(extras.handbackWarning !== undefined ? { handbackWarning: extras.handbackWarning } : {}),
|
|
254
289
|
...(extras.error ? { error_kind: extras.error.errorKind, retryable: extras.error.retryable } : {}),
|
|
255
290
|
stats: child.stats,
|
|
256
291
|
...(child.model !== undefined ? { model: child.model, resolvedModel: child.model } : {}),
|
|
@@ -286,7 +321,7 @@ function durablePauseDelegationReport(reportLabel, child) {
|
|
|
286
321
|
function unparkedDurablePauseReason(d) {
|
|
287
322
|
return d.kind === "no_park_lane"
|
|
288
323
|
? `the agent hit an approval gate and paused durably, but this delegation cannot be parked ` +
|
|
289
|
-
`(parking needs
|
|
324
|
+
`(parking needs a durable agent row, a checkpoint store and the ` +
|
|
290
325
|
`durable-session capability). Its checkpoint ${d.checkpointToken} is still committed and the ` +
|
|
291
326
|
`approval must be resolved through the deployment's approval channel; the gated action never ran.`
|
|
292
327
|
: `the agent hit an approval gate and paused durably, but the park did not take (` +
|
|
@@ -297,6 +332,10 @@ function unparkedDurablePauseReason(d) {
|
|
|
297
332
|
function errorKindClause(c) {
|
|
298
333
|
return c !== undefined ? ` (error_kind: ${c.errorKind}, retryable: ${c.retryable})` : "";
|
|
299
334
|
}
|
|
335
|
+
function stripSpawnTurnQuestionFace(spec) {
|
|
336
|
+
const { onQuestion: spawnTurnQuestionFace, ...rest } = spec;
|
|
337
|
+
return { spec: rest, internalsFlag: spawnTurnQuestionFace !== undefined ? { questionFaceStripped: true } : {} };
|
|
338
|
+
}
|
|
300
339
|
const FAILED_SESSION_RETAIN_TTL_MS = 15 * 60 * 1000;
|
|
301
340
|
const PARTIAL_FINDINGS_MAX_CHARS = 1200;
|
|
302
341
|
const BG_NOTIFY_DRAIN_WINDOW_MS = 2_000;
|
|
@@ -503,6 +542,13 @@ export function createSubagentResume(deps) {
|
|
|
503
542
|
}
|
|
504
543
|
catch {
|
|
505
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
|
+
}
|
|
506
552
|
},
|
|
507
553
|
}
|
|
508
554
|
: {}),
|
|
@@ -530,6 +576,14 @@ export function createSubagentResume(deps) {
|
|
|
530
576
|
void _;
|
|
531
577
|
const child = await stream.result();
|
|
532
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);
|
|
533
587
|
const errCodeRevive = status === "failed" ? (child.errorCode ?? extractErrorCode(child.errorMessage)) : undefined;
|
|
534
588
|
const errClassRevive = status === "failed"
|
|
535
589
|
? classifySubagentError({ status: "failed", ...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}), ...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}) })
|
|
@@ -538,7 +592,7 @@ export function createSubagentResume(deps) {
|
|
|
538
592
|
try {
|
|
539
593
|
const winner = deps.registry.settleRevivedAgent(deps.taskId, reviveCycle, {
|
|
540
594
|
status,
|
|
541
|
-
...resultSettleFields(
|
|
595
|
+
...resultSettleFields(resumeHandbackResult),
|
|
542
596
|
...(status !== "completed" ? { error: (child.errorMessage ?? `resumed run ${status}`).slice(0, 500) } : {}),
|
|
543
597
|
...(errCodeRevive !== undefined ? { errorCode: errCodeRevive } : {}),
|
|
544
598
|
...(errClassRevive !== undefined ? { retryable: errClassRevive.retryable, errorKind: errClassRevive.errorKind } : {}),
|
|
@@ -585,7 +639,7 @@ export function createSubagentResume(deps) {
|
|
|
585
639
|
summary: reviveTerminalSummary,
|
|
586
640
|
...(failReasonRevive !== undefined ? { error: failReasonRevive.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
587
641
|
...(status === "failed" && errCodeRevive !== undefined ? { errorCode: errCodeRevive } : {}),
|
|
588
|
-
...(
|
|
642
|
+
...(resumeHandbackResult ? { result: notifyResultField(resumeHandbackResult) } : {}),
|
|
589
643
|
...(status === "killed" && child.result ? { partial: true } : {}),
|
|
590
644
|
...resumeResidual(),
|
|
591
645
|
resumable: status !== "killed",
|
|
@@ -1304,7 +1358,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1304
1358
|
const runObserverLeg = async (spec) => {
|
|
1305
1359
|
if (!isObserverTaskId(sid))
|
|
1306
1360
|
throw new ObserverStoppedByUserError("observer identity revoked — delivery refused");
|
|
1307
|
-
const r = await opts.runner.runTask(spec);
|
|
1361
|
+
const r = await opts.runner.runTask(spec, ctx.interactionPosture !== undefined ? { parentInteractionPosture: ctx.interactionPosture } : undefined);
|
|
1308
1362
|
if (r.status !== "completed") {
|
|
1309
1363
|
if (r.errorCode === "resume.session_not_found") {
|
|
1310
1364
|
throw new ObserverResumeStateError(`observer session no longer exists${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
|
|
@@ -1443,8 +1497,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1443
1497
|
...(inheritedManifestScope ? { inheritedManifestScope } : {}),
|
|
1444
1498
|
...(treeScope !== undefined ? { registryScope: treeScope } : {}),
|
|
1445
1499
|
...(ctx.inheritedGateForChildren ? { inheritedGate: ctx.inheritedGateForChildren() } : {}),
|
|
1500
|
+
ownOrgAdmissionRef: { current: reviveClaim !== undefined ? readDurableOrgAdmission(reviveClaim.row) : undefined },
|
|
1446
1501
|
...(childDefaultPersona !== undefined ? { defaultSystemPrompt: childDefaultPersona } : {}),
|
|
1447
1502
|
isDelegatedChild: true,
|
|
1503
|
+
...(ctx.interactionPosture !== undefined ? { parentInteractionPosture: ctx.interactionPosture } : {}),
|
|
1448
1504
|
parentToolCallId: ctx.toolCallId,
|
|
1449
1505
|
...(childAgentName ? { agentName: childAgentName } : {}),
|
|
1450
1506
|
...(agentName !== undefined ? { explicitAgentName: agentName } : {}),
|
|
@@ -1513,11 +1569,12 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1513
1569
|
limits: childLimits,
|
|
1514
1570
|
signal,
|
|
1515
1571
|
});
|
|
1516
|
-
const tryRetainChild = async (ledger, entryTtlMs, preMintedSessionId, durableProbe, releaseAnchor) => {
|
|
1572
|
+
const tryRetainChild = async (ledger, entryTtlMs, preMintedSessionId, durableProbe, releaseAnchor, internalsExtra) => {
|
|
1517
1573
|
if (!ledger)
|
|
1518
1574
|
return undefined;
|
|
1519
1575
|
const childSessionId = preMintedSessionId ?? uuidv7();
|
|
1520
|
-
const
|
|
1576
|
+
const retainStrip = stripSpawnTurnQuestionFace(buildChildSpec(undefined));
|
|
1577
|
+
const { signal: _spawnSignal, ...plainSpec } = retainStrip.spec;
|
|
1521
1578
|
let entry = ledger.register(ctx.toolCallId, {
|
|
1522
1579
|
childSessionId,
|
|
1523
1580
|
runner: opts.runner,
|
|
@@ -1534,7 +1591,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1534
1591
|
...(plainSpec.deferTools !== undefined ? { deferTools: [...plainSpec.deferTools] } : {}),
|
|
1535
1592
|
...(plainSpec.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...plainSpec.alwaysLoadTools] } : {}),
|
|
1536
1593
|
}),
|
|
1537
|
-
internalsSnapshot: { ...childInternals },
|
|
1594
|
+
internalsSnapshot: { ...childInternals, ...retainStrip.internalsFlag, ...(internalsExtra ?? {}) },
|
|
1538
1595
|
release: async () => {
|
|
1539
1596
|
try {
|
|
1540
1597
|
await opts.runner.sessions.unpin?.(childSessionId);
|
|
@@ -1597,14 +1654,14 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1597
1654
|
if (ledger !== undefined && !ledger.disposed) {
|
|
1598
1655
|
if (opts.background)
|
|
1599
1656
|
ensureSessionReapHook(opts.background.registry);
|
|
1600
|
-
const
|
|
1657
|
+
const failedRetainStrip = stripSpawnTurnQuestionFace(buildChildSpec(undefined));
|
|
1658
|
+
const { signal: _spawnSignal, ...plainSpec } = failedRetainStrip.spec;
|
|
1601
1659
|
const entry = ledger.register(ctx.toolCallId, {
|
|
1602
1660
|
childSessionId,
|
|
1603
1661
|
runner: opts.runner,
|
|
1604
1662
|
ttlMs: FAILED_SESSION_RETAIN_TTL_MS,
|
|
1605
1663
|
...(childAgentName !== undefined ? { agentName: childAgentName } : {}),
|
|
1606
1664
|
...(agentName !== undefined ? { explicitAgentName: agentName } : {}),
|
|
1607
|
-
...(agentName !== undefined ? { explicitAgentName: agentName } : {}),
|
|
1608
1665
|
specSnapshot: Object.freeze({
|
|
1609
1666
|
...plainSpec,
|
|
1610
1667
|
tools: [...(plainSpec.tools ?? [])],
|
|
@@ -1615,7 +1672,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1615
1672
|
...(plainSpec.deferTools !== undefined ? { deferTools: [...plainSpec.deferTools] } : {}),
|
|
1616
1673
|
...(plainSpec.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...plainSpec.alwaysLoadTools] } : {}),
|
|
1617
1674
|
}),
|
|
1618
|
-
internalsSnapshot: { ...childInternals },
|
|
1675
|
+
internalsSnapshot: { ...childInternals, ...failedRetainStrip.internalsFlag },
|
|
1619
1676
|
release: releaseChild,
|
|
1620
1677
|
});
|
|
1621
1678
|
if (entry) {
|
|
@@ -1715,7 +1772,6 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1715
1772
|
else if (!sessionScopedBg)
|
|
1716
1773
|
ctx.signal.addEventListener("abort", onHostAbort, { once: true });
|
|
1717
1774
|
}
|
|
1718
|
-
const dropTurnBoundQuestionFace = sessionScopedBg ? { onQuestion: undefined } : {};
|
|
1719
1775
|
const shortDesc = `fork: ${(typeof a.description === "string" && a.description.trim() ? a.description.trim() : prompt).slice(0, 180)}`;
|
|
1720
1776
|
const bgOwner = sessionScopedBg ? ctx.sessionId : ctx.taskId ?? bg.owner;
|
|
1721
1777
|
const bgScope = treeScope;
|
|
@@ -1808,7 +1864,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1808
1864
|
startedAt: Date.now(),
|
|
1809
1865
|
});
|
|
1810
1866
|
const forkBgStartedAt = Date.now();
|
|
1811
|
-
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;
|
|
1812
1870
|
const s2ForkNotifyReady = (inject) => {
|
|
1813
1871
|
bg.registry.attachAgentNotify(taskId, inject);
|
|
1814
1872
|
};
|
|
@@ -1832,7 +1890,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1832
1890
|
const bgForkInternals = bgSink
|
|
1833
1891
|
? {
|
|
1834
1892
|
...forkInternals,
|
|
1893
|
+
...(forkQuestionStrip !== undefined ? forkQuestionStrip.internalsFlag : {}),
|
|
1835
1894
|
onNotifyInjectorReady: s2ForkNotifyReady,
|
|
1895
|
+
delegationTaskType: "background_agent",
|
|
1836
1896
|
onForwardEvent: (e) => {
|
|
1837
1897
|
try {
|
|
1838
1898
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -1863,7 +1923,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1863
1923
|
}
|
|
1864
1924
|
: {
|
|
1865
1925
|
...forkInternals,
|
|
1926
|
+
...(forkQuestionStrip !== undefined ? forkQuestionStrip.internalsFlag : {}),
|
|
1866
1927
|
onNotifyInjectorReady: s2ForkNotifyReady,
|
|
1928
|
+
delegationTaskType: "background_agent",
|
|
1867
1929
|
onForwardEvent: (e) => {
|
|
1868
1930
|
try {
|
|
1869
1931
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -1888,6 +1950,14 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1888
1950
|
}
|
|
1889
1951
|
catch {
|
|
1890
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);
|
|
1891
1961
|
const okBg = child.status === "completed";
|
|
1892
1962
|
const reapedBg = !okBg && abort.signal.aborted;
|
|
1893
1963
|
const collateralBg = reapedBg && bg.registry.getAccessibleTask(taskId, { ...(bgOwner !== undefined ? { owner: bgOwner } : {}), ...(bgScope !== undefined ? { scope: bgScope } : {}) })?.status === "running";
|
|
@@ -1899,7 +1969,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1899
1969
|
const settledBg = bg.registry.settleBackgroundAgent(taskId, {
|
|
1900
1970
|
cycle: 0,
|
|
1901
1971
|
status: okBg ? "completed" : reapedBg ? "killed" : "failed",
|
|
1902
|
-
...resultSettleFields(
|
|
1972
|
+
...resultSettleFields(forkBgResult),
|
|
1903
1973
|
...(!okBg ? { error: reapedBg ? (collateralBg ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : child.errorMessage ?? String(child.status) } : {}),
|
|
1904
1974
|
...(errCodeFork !== undefined ? { errorCode: errCodeFork } : {}),
|
|
1905
1975
|
...(errClassFork !== undefined ? { retryable: errClassFork.retryable, errorKind: errClassFork.errorKind } : {}),
|
|
@@ -1950,7 +2020,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1950
2020
|
...(failReasonFork !== undefined ? { error: failReasonFork.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
1951
2021
|
...(settledBg === "failed" && errCodeFork !== undefined ? { errorCode: errCodeFork } : {}),
|
|
1952
2022
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
1953
|
-
...(
|
|
2023
|
+
...(forkBgResult ? { result: notifyResultField(forkBgResult) } : {}),
|
|
1954
2024
|
...(settledBg === "killed" && child.result ? { partial: true } : {}),
|
|
1955
2025
|
...residualFork,
|
|
1956
2026
|
resumable: resumableFork,
|
|
@@ -2053,6 +2123,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2053
2123
|
const wt = await finishWorktree();
|
|
2054
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" } };
|
|
2055
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
|
+
});
|
|
2056
2133
|
const nsf = forkChild.stats.nested;
|
|
2057
2134
|
ctx.reportUsage?.({
|
|
2058
2135
|
tokens: forkChild.stats.tokens + (nsf?.tokens ?? 0),
|
|
@@ -2066,6 +2143,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2066
2143
|
const wtLine = await finishWorktree();
|
|
2067
2144
|
const forkErr = classifySubagentError(forkChild);
|
|
2068
2145
|
const forkLines = [
|
|
2146
|
+
...(forkHandbackWarning ? [forkHandbackWarning, ""] : []),
|
|
2069
2147
|
`[Sub-agent report · ${FORK_SUBAGENT_TYPE}${a.description ? ` · ${String(a.description)}` : ""}] (forked session …${forkedId.slice(-8)})`,
|
|
2070
2148
|
`status: ${forkChild.status}`,
|
|
2071
2149
|
modelNote ?? "",
|
|
@@ -2087,6 +2165,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2087
2165
|
details: completedAgentCard(forkChild, {
|
|
2088
2166
|
subagentType: FORK_SUBAGENT_TYPE,
|
|
2089
2167
|
error: forkErr,
|
|
2168
|
+
...(forkHandbackWarning !== undefined ? { handbackWarning: forkHandbackWarning } : {}),
|
|
2090
2169
|
...(worktreeKeptPath !== undefined ? { worktreePath: worktreeKeptPath } : {}),
|
|
2091
2170
|
...(() => {
|
|
2092
2171
|
const ts = toolStatsCounter.snapshot();
|
|
@@ -2255,7 +2334,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2255
2334
|
: ctx.subagentRetain;
|
|
2256
2335
|
if (sessionScopedBg && bgRetainLedger)
|
|
2257
2336
|
ensureSessionReapHook(bg.registry);
|
|
2258
|
-
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
|
+
});
|
|
2259
2340
|
if (bgRetain) {
|
|
2260
2341
|
bg.registry.markRetainedContinuation(taskId);
|
|
2261
2342
|
const origRelease = bgRetain.release;
|
|
@@ -2265,23 +2346,26 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2265
2346
|
};
|
|
2266
2347
|
}
|
|
2267
2348
|
const forwardDurableApproval = ctx.durableApprovalForChildren !== undefined &&
|
|
2268
|
-
agentName !== undefined &&
|
|
2269
2349
|
bg.agentStore !== undefined &&
|
|
2270
2350
|
bg.checkpointStore !== undefined &&
|
|
2271
2351
|
bg.ensureChildSessionDurable !== undefined;
|
|
2272
|
-
const
|
|
2352
|
+
const bgSpecBase = {
|
|
2273
2353
|
...buildChildSpec(abort.signal),
|
|
2274
2354
|
sessionId: bgChildSessionId,
|
|
2275
2355
|
...(forwardDurableApproval ? { durableApproval: { ...ctx.durableApprovalForChildren } } : {}),
|
|
2276
2356
|
...(reviveRow !== undefined ? { requireExistingSession: true } : {}),
|
|
2277
|
-
...(outlivesThisRequest ? { onQuestion: undefined } : {}),
|
|
2278
2357
|
};
|
|
2358
|
+
const bgQuestionStrip = outlivesThisRequest ? stripSpawnTurnQuestionFace(bgSpecBase) : undefined;
|
|
2359
|
+
const bgSpec = bgQuestionStrip !== undefined ? bgQuestionStrip.spec : bgSpecBase;
|
|
2279
2360
|
let reviveAttachedResolve;
|
|
2280
2361
|
const reviveAttached = reviveRow !== undefined ? new Promise((r) => (reviveAttachedResolve = r)) : undefined;
|
|
2281
2362
|
const s2NotifyReady = (inject) => {
|
|
2282
2363
|
bg.registry.attachAgentNotify(taskId, inject);
|
|
2283
2364
|
if (parkedResume !== undefined)
|
|
2284
2365
|
bg.registry.finalizeParkedResume(taskId);
|
|
2366
|
+
const legOrgVerdict = childInternals.ownOrgAdmissionRef?.current;
|
|
2367
|
+
if (legOrgVerdict !== undefined)
|
|
2368
|
+
bg.registry.recordBackgroundAgentOrgAdmission(taskId, legOrgVerdict);
|
|
2285
2369
|
reviveAttachedResolve?.();
|
|
2286
2370
|
};
|
|
2287
2371
|
const bgActivityBeat = createBgActivityBeat(ctx.toolCallId, (toolStarts) => {
|
|
@@ -2304,7 +2388,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2304
2388
|
const bgInternals = bgSink
|
|
2305
2389
|
? {
|
|
2306
2390
|
...childInternals,
|
|
2391
|
+
...(bgQuestionStrip !== undefined ? bgQuestionStrip.internalsFlag : {}),
|
|
2307
2392
|
onNotifyInjectorReady: s2NotifyReady,
|
|
2393
|
+
delegationTaskType: "background_agent",
|
|
2308
2394
|
onForwardEvent: (e) => {
|
|
2309
2395
|
try {
|
|
2310
2396
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -2335,7 +2421,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2335
2421
|
}
|
|
2336
2422
|
: {
|
|
2337
2423
|
...childInternals,
|
|
2424
|
+
...(bgQuestionStrip !== undefined ? bgQuestionStrip.internalsFlag : {}),
|
|
2338
2425
|
onNotifyInjectorReady: s2NotifyReady,
|
|
2426
|
+
delegationTaskType: "background_agent",
|
|
2339
2427
|
onForwardEvent: (e) => {
|
|
2340
2428
|
try {
|
|
2341
2429
|
recordingForward(e, { bgAgentId: taskId });
|
|
@@ -2487,6 +2575,16 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2487
2575
|
catch {
|
|
2488
2576
|
}
|
|
2489
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);
|
|
2490
2588
|
if (bgRetain) {
|
|
2491
2589
|
if (isDurablePause(child.status))
|
|
2492
2590
|
bgRetainLedger?.abandon(ctx.toolCallId);
|
|
@@ -2509,7 +2607,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2509
2607
|
return false;
|
|
2510
2608
|
}
|
|
2511
2609
|
};
|
|
2512
|
-
const wired =
|
|
2610
|
+
const wired = bg.agentStore !== undefined && cpStore !== undefined && bg.ensureChildSessionDurable !== undefined;
|
|
2513
2611
|
if (wired) {
|
|
2514
2612
|
const token = child.checkpointToken;
|
|
2515
2613
|
if (abort.signal.aborted) {
|
|
@@ -2568,7 +2666,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2568
2666
|
cycle: 0,
|
|
2569
2667
|
status: ok ? "completed" : reaped ? "killed" : "failed",
|
|
2570
2668
|
seq: seqAtSettle ?? 1,
|
|
2571
|
-
...resultSettleFields(
|
|
2669
|
+
...resultSettleFields(bgHandbackResult),
|
|
2572
2670
|
...(!ok ? { error: reaped ? (collateral ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : unparkedPauseReason ?? child.errorMessage ?? String(child.status) } : {}),
|
|
2573
2671
|
...(errCodeBg !== undefined ? { errorCode: errCodeBg } : {}),
|
|
2574
2672
|
...(errClassBg !== undefined ? { retryable: errClassBg.retryable, errorKind: errClassBg.errorKind } : {}),
|
|
@@ -2620,7 +2718,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2620
2718
|
...(failReasonBg !== undefined ? { error: failReasonBg.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
2621
2719
|
...(settled === "failed" && errCodeBg !== undefined ? { errorCode: errCodeBg } : {}),
|
|
2622
2720
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
2623
|
-
...(
|
|
2721
|
+
...(bgHandbackResult ? { result: notifyResultField(bgHandbackResult) } : {}),
|
|
2624
2722
|
...(settled === "killed" && child.result ? { partial: true } : {}),
|
|
2625
2723
|
...residual,
|
|
2626
2724
|
resumable: resumableBg,
|
|
@@ -2777,6 +2875,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2777
2875
|
...(opts.background ? { registry: opts.background.registry } : {}),
|
|
2778
2876
|
...(opts.onObserverError !== undefined ? { onNotifyError: (f) => opts.onObserverError?.(f.error, { site: f.site }) } : {}),
|
|
2779
2877
|
...(ctx.onQuestion !== undefined ? { currentOnQuestion: ctx.onQuestion } : {}),
|
|
2878
|
+
...(ctx.autoModeReview !== undefined ? { currentAutoModeReview: ctx.autoModeReview } : {}),
|
|
2780
2879
|
});
|
|
2781
2880
|
notifier.notify(() => ctx.onSubagentSpawn?.(createSteerHandle(stream, ctx.toolCallId, childAgentName, settled, {
|
|
2782
2881
|
resume,
|
|
@@ -2801,26 +2900,18 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2801
2900
|
child = await opts.runner.runTask(buildChildSpec(ctx.signal), childInternals);
|
|
2802
2901
|
}
|
|
2803
2902
|
await settleObserver(child.status);
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
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.",
|
|
2817
|
-
}, ctx.signal)
|
|
2818
|
-
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
2819
|
-
if (handbackVerdict.kind === "block") {
|
|
2820
|
-
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.`;
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
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
|
+
});
|
|
2824
2915
|
const ns = child.stats.nested;
|
|
2825
2916
|
ctx.reportUsage?.({
|
|
2826
2917
|
tokens: child.stats.tokens + (ns?.tokens ?? 0),
|
|
@@ -2905,6 +2996,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2905
2996
|
details: completedAgentCard(child, {
|
|
2906
2997
|
...(def !== undefined ? { subagentType: def.name } : {}),
|
|
2907
2998
|
error: errClass,
|
|
2999
|
+
...(handbackWarning !== undefined ? { handbackWarning } : {}),
|
|
2908
3000
|
...(modelFallback !== undefined ? { modelFallback } : {}),
|
|
2909
3001
|
...(worktreeKeptPath !== undefined ? { worktreePath: worktreeKeptPath } : {}),
|
|
2910
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
|
+
}
|
package/dist/brain/errors.js
CHANGED
|
@@ -42,6 +42,25 @@ export function extractErrorCode(errorMessage) {
|
|
|
42
42
|
export function stripErrorCodePrefix(errorMessage) {
|
|
43
43
|
return errorMessage.replace(CODE_PREFIX_RE, "");
|
|
44
44
|
}
|
|
45
|
+
const STALE_CONNECTION_CODES = new Set([
|
|
46
|
+
"ECONNRESET",
|
|
47
|
+
"EPIPE",
|
|
48
|
+
"ConnectionClosed",
|
|
49
|
+
"ETIMEDOUT",
|
|
50
|
+
"ECONNABORTED",
|
|
51
|
+
"ERR_SOCKET_CLOSED",
|
|
52
|
+
"StreamSuspended",
|
|
53
|
+
]);
|
|
54
|
+
function hasStaleConnectionCode(e) {
|
|
55
|
+
let cur = e;
|
|
56
|
+
for (let depth = 0; depth < 5 && cur !== undefined && cur !== null; depth++) {
|
|
57
|
+
const code = typeof cur.code === "string" ? cur.code : undefined;
|
|
58
|
+
if (code !== undefined && STALE_CONNECTION_CODES.has(code))
|
|
59
|
+
return true;
|
|
60
|
+
cur = cur instanceof Error ? cur.cause : undefined;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
45
64
|
export function describeNetworkError(e) {
|
|
46
65
|
const top = e instanceof Error ? e.message : String(e);
|
|
47
66
|
const parts = [];
|
|
@@ -54,5 +73,6 @@ export function describeNetworkError(e) {
|
|
|
54
73
|
parts.push(piece);
|
|
55
74
|
cur = cur instanceof Error ? cur.cause : undefined;
|
|
56
75
|
}
|
|
57
|
-
|
|
76
|
+
const body = parts.length > 0 ? `${top} (${parts.join(" ← ")})` : top;
|
|
77
|
+
return hasStaleConnectionCode(e) ? `${body} — possibly a stale connection; a fresh one may succeed` : body;
|
|
58
78
|
}
|