@sema-agent/core 5.7.0 → 5.9.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 +79 -0
- package/dist/agents/cascade.d.ts +1 -1
- package/dist/agents/cascade.js +24 -0
- package/dist/agents/subagent.d.ts +7 -7
- package/dist/agents/subagent.js +74 -33
- package/dist/agents/teacher.js +3 -3
- package/dist/agents/team.d.ts +1 -1
- package/dist/agents/team.js +1 -1
- package/dist/agents/verify.js +1 -1
- package/dist/brain/anthropic.js +24 -35
- package/dist/brain/open-responses.d.ts +11 -0
- package/dist/brain/open-responses.js +721 -0
- package/dist/brain/openai.js +22 -33
- package/dist/brain/request-params.d.ts +1 -0
- package/dist/brain/request-params.js +16 -0
- package/dist/brain/stream-engine.d.ts +0 -2
- package/dist/brain/stream-engine.js +5 -47
- package/dist/brain/stream-shared.d.ts +0 -10
- package/dist/brain/stream-shared.js +0 -23
- package/dist/brain/terminal-cause.d.ts +0 -1
- package/dist/brain/terminal-cause.js +0 -3
- package/dist/brain/timeout.d.ts +4 -2
- package/dist/brain/timeout.js +4 -14
- package/dist/config/catalog.d.ts +3 -11
- package/dist/config/catalog.js +30 -69
- package/dist/config/defaults.d.ts +0 -3
- package/dist/config/defaults.js +0 -3
- package/dist/core/auto-compaction.d.ts +2 -11
- package/dist/core/auto-compaction.js +3 -67
- package/dist/core/checkpoint-store.d.ts +10 -8
- package/dist/core/checkpoint-store.js +7 -7
- package/dist/core/fs-write-gate-policy.js +2 -2
- package/dist/core/lsp-diagnostics.d.ts +3 -2
- package/dist/core/lsp-diagnostics.js +20 -7
- package/dist/core/memory-engine/file-backend.js +20 -17
- package/dist/core/memory-engine/layout.d.ts +5 -0
- package/dist/core/memory-engine/layout.js +72 -47
- package/dist/core/runner/assemble-result.d.ts +3 -8
- package/dist/core/runner/assemble-result.js +24 -18
- package/dist/core/runner/prepare-task.d.ts +27 -6
- package/dist/core/runner/prepare-task.js +209 -144
- package/dist/core/runner/runtask.d.ts +0 -2
- package/dist/core/runner/runtask.js +228 -266
- package/dist/core/runner/session-rule-policy.d.ts +1 -0
- package/dist/core/runner/session-rule-policy.js +4 -3
- package/dist/core/session-policy-store.d.ts +5 -0
- package/dist/core/session-policy-store.js +3 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/task-registry-agent.d.ts +2 -0
- package/dist/core/task-registry-agent.js +33 -1
- package/dist/core/task-registry-shared.d.ts +0 -1
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/task-registry.js +9 -1
- package/dist/core/tool-errors.js +10 -2
- package/dist/core/tool-policy.d.ts +8 -0
- package/dist/core/tool-policy.js +11 -0
- package/dist/core/trace.d.ts +1 -10
- package/dist/core/types.d.ts +27 -30
- package/dist/core/usage-window-store.d.ts +39 -0
- package/dist/core/usage-window-store.js +115 -0
- package/dist/engine/execution-env/node-execution-env.js +0 -22
- package/dist/engine/harness/agent-harness.d.ts +1 -7
- package/dist/engine/harness/agent-harness.js +16 -53
- package/dist/engine/harness/types.d.ts +3 -15
- package/dist/engine/llm/types.d.ts +3 -75
- package/dist/engine/loop/agent-loop.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +7 -173
- package/dist/engine/loop/types.d.ts +1 -18
- package/dist/engine/session/repo-utils.d.ts +1 -2
- package/dist/engine/session/repo-utils.js +0 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +10 -5
- package/dist/internal/llm.d.ts +1 -1
- package/dist/orchestration/goal.js +1 -5
- package/dist/orchestration/run-workflow-tool.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow-governance.d.ts +6 -6
- package/dist/orchestration/workflow-governance.js +33 -47
- package/dist/orchestration/workflow.js +14 -5
- package/dist/prompt-assembly/event-registry.js +2 -3
- package/dist/stores/cc/lockfile.js +9 -11
- package/dist/stores/cc/mailbox-store.js +49 -38
- package/dist/stores/file/index.d.ts +3 -0
- package/dist/stores/file/index.js +4 -0
- package/dist/stores/file/usage-window-store.d.ts +9 -0
- package/dist/stores/file/usage-window-store.js +86 -0
- package/dist/tools/fs/fs-bash.d.ts +1 -10
- package/dist/tools/fs/fs-bash.js +20 -60
- package/dist/tools/fs/index.d.ts +0 -2
- package/dist/tools/fs/index.js +1 -2
- package/dist/tools/web.js +20 -19
- package/package.json +4 -5
- package/dist/bin/sema-tb.d.ts +0 -31
- package/dist/bin/sema-tb.js +0 -448
- package/dist/bin/tb-env.d.ts +0 -2
- package/dist/bin/tb-env.js +0 -17
- package/dist/brain/walltime.d.ts +0 -1
- package/dist/brain/walltime.js +0 -1
- package/dist/core/runner/call-cap.d.ts +0 -67
- package/dist/core/runner/call-cap.js +0 -145
- package/dist/core/runner/cut-kill.d.ts +0 -10
- package/dist/core/runner/cut-kill.js +0 -37
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, uuidv7 } from "../../internal/harness.js";
|
|
2
|
-
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, MAX_SUPPORTED_CHECKPOINT_VERSION, remainingBudgetMicroUsd,
|
|
3
|
-
import { effectiveCushionMs, estimateCycleMs, hasRealContent, minLiveWriteoutWindowMs, recordCallSample, recordToolCycleSample, recordToolSample, recordTtftSample, softExecDeadlineMs, writeoutCushionMs } from "./call-cap.js";
|
|
2
|
+
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, MAX_SUPPORTED_CHECKPOINT_VERSION, remainingBudgetMicroUsd, remainingTokens, validatePendingSteer, winnerFromOutcome, } from "../checkpoint-store.js";
|
|
4
3
|
import { engineVersion } from "../version.js";
|
|
5
4
|
import { CONFIG_CATALOG_VERSION, declarationReasons, resolveEffectiveConfig } from "../../config/catalog.js";
|
|
6
5
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
7
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, isCompactionManualCancel,
|
|
6
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, isCompactionManualCancel, maybeCompact, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
8
7
|
import { ASK_USER_QUESTION_TOOL_NAME, QUESTION_AWAITS_RESUME } from "../ask-question.js";
|
|
9
8
|
import { computeCostMicroUsd, modelCostToPricing } from "../pricing.js";
|
|
10
9
|
import { emitTrace } from "../trace.js";
|
|
11
10
|
import { emitTaskOutcome } from "../task-outcome.js";
|
|
12
|
-
import { isDegenerateCutMessage
|
|
11
|
+
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
13
12
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
14
13
|
import { resolveReasoning } from "../../brain/reasoning.js";
|
|
15
14
|
import { readDegradation } from "../../brain/degrading.js";
|
|
@@ -41,17 +40,15 @@ import { defaultTaskRegistry } from "../task-registry.js";
|
|
|
41
40
|
import { discloseDroppedPending, isDelegatedAgentTerminal, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
42
41
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
43
42
|
import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-size-guideline.js";
|
|
44
|
-
import { DEFAULT_MAX_TURNS } from "../../config/defaults.js";
|
|
45
43
|
import { structuredFrom, toolOutputFrom } from "./tool-output-projection.js";
|
|
46
|
-
export { DEFAULT_MAX_TURNS };
|
|
47
44
|
function createRunState() {
|
|
48
45
|
return {
|
|
49
46
|
telemetry: { cacheFamily: "input-excludes-cached", pricing: { inputPer1M: 0, outputPer1M: 0 }, pricingConfigured: true, unpricedSpend: false, tracer: undefined, taskId: "", taskStart: 0, taskStartMonotonic: 0, cacheBreakReported: false },
|
|
50
47
|
degrade: { degradeToModel: undefined, degraded: undefined, outputErrorStreak: 0, outputInvalid: false, recordDegraded: () => { } },
|
|
51
|
-
limits: { turnsExceeded: false, budgetHit: undefined, outputRetryCap: 0, effectiveMaxTurns: undefined },
|
|
52
|
-
budget: { remainingMicroUsd: undefined, maxCostMicroUsd: undefined, overBudget: () =>
|
|
48
|
+
limits: { turnsExceeded: false, budgetHit: undefined, budgetAxis: undefined, platformTerminal: undefined, outputRetryCap: 0, effectiveMaxTurns: undefined },
|
|
49
|
+
budget: { remainingMicroUsd: undefined, maxCostMicroUsd: undefined, remainingTokens: undefined, maxTokensWindow: undefined, overBudget: () => undefined, streamCancel: false, callOutputChars: 0, lastStreamBudgetCheck: 0, projectedOverBudget: () => undefined },
|
|
53
50
|
turn: { callStartAt: undefined, firstTokenAt: undefined, turnUsage: undefined, turnUsageMissing: false, turnStopReason: undefined, lastTurnHadToolCalls: false, toolBatch: [] },
|
|
54
|
-
counters: {
|
|
51
|
+
counters: { approachNoticesSent: 0, walltimeSyncBackstopFired: false, compactionFloor: 0, trimForceBackoff: false, repetitionCuts: 0, repetitionSpared: 0, repetitionEvents: [], REPETITION_EVENTS_CAP: 0, preemptIgnoredReported: false, wroteThisRun: false, finalVerifyInjections: 0, groundingSignalPreR9: false, groundingSignalPostR9: false, cadenceTurns: 0 },
|
|
55
52
|
attach: { attachmentsCfg: undefined, agentListingOn: false, skillsListingOn: false, attachState: undefined, dateState: undefined, instrProbe: undefined, instrState: undefined, sizeGuidelineState: undefined, attachmentsInjected: 0 },
|
|
56
53
|
};
|
|
57
54
|
}
|
|
@@ -96,14 +93,19 @@ const SUGGESTIONS_TIMEOUT_MS = 30_000;
|
|
|
96
93
|
const SUGGESTIONS_TRANSCRIPT_MESSAGES = 12;
|
|
97
94
|
const SNAPSHOT_TIMEOUT_MS = 30_000;
|
|
98
95
|
const SNAPSHOT_TOO_LARGE_TTL_MS = 30 * 60_000;
|
|
96
|
+
const LIMIT_APPROACH_DEFAULT_THRESHOLDS = [0.8, 0.95];
|
|
97
|
+
const DEFAULT_PRECALL_OUTPUT_TOKENS = 4096;
|
|
98
|
+
function limitApproachFrames(index, axis, percent) {
|
|
99
|
+
return index === 0
|
|
100
|
+
? `<system-reminder>[limit approach] This task has used about ${percent}% of its ${axis}. ` +
|
|
101
|
+
`Start converging: finish the step you are on, drop work that is not required for the deliverable, and ` +
|
|
102
|
+
`avoid starting anything you cannot complete within what is left.</system-reminder>`
|
|
103
|
+
: `<system-reminder>[limit approach] About ${percent}% of the ${axis} is gone. Deliver now: ` +
|
|
104
|
+
`stop exploring, write up what you have — including what is incomplete and what you would do next — ` +
|
|
105
|
+
`and end the task. Work that is not reported before the allowance runs out is lost.</system-reminder>`;
|
|
106
|
+
}
|
|
99
107
|
function resolveMaxTurns(limits) {
|
|
100
|
-
|
|
101
|
-
return limits.maxTurns;
|
|
102
|
-
}
|
|
103
|
-
if (limits?.timeoutSec !== undefined && limits.timeoutSec > 0) {
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
return DEFAULT_MAX_TURNS;
|
|
108
|
+
return limits?.maxTurns;
|
|
107
109
|
}
|
|
108
110
|
function resumeDecisionWasNegative(resume) {
|
|
109
111
|
const o = resume.outcome;
|
|
@@ -111,6 +113,16 @@ function resumeDecisionWasNegative(resume) {
|
|
|
111
113
|
}
|
|
112
114
|
const DEFERRED_REISSUE = "[DEFERRED] This tool call shared a batch with a call that suspended for durable approval, so it was " +
|
|
113
115
|
"NOT executed on resume. If you still need it, issue it again now.";
|
|
116
|
+
function toolEndBodyFrom(result, isError) {
|
|
117
|
+
const o = toolOutputFrom(result);
|
|
118
|
+
const st = structuredFrom(result);
|
|
119
|
+
const code = isError ? result?.details?.code : undefined;
|
|
120
|
+
return {
|
|
121
|
+
...(o !== undefined ? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) } : {}),
|
|
122
|
+
...(st !== undefined ? { structured: st } : {}),
|
|
123
|
+
...(typeof code === "string" ? { errorCode: code } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
114
126
|
function deepJsonEqual(a, b) {
|
|
115
127
|
if (a === b)
|
|
116
128
|
return true;
|
|
@@ -228,62 +240,85 @@ function resumeContinuation(resume) {
|
|
|
228
240
|
`conversation above. Do NOT restart the task or re-run any tool you already ran; continue from this ` +
|
|
229
241
|
`exact point, building on the existing results, and finish the remaining work.`);
|
|
230
242
|
}
|
|
243
|
+
function platformLimitTerminal(reason, retryAfterMs, moment = "turn_boundary") {
|
|
244
|
+
const message = moment === "entry"
|
|
245
|
+
? `a deployment usage window is exhausted (RunnerDeps.usageWindows), so the task was refused before its first model call — nothing ran and nothing was spent. There is no checkpoint to suspend into at this point, so the caller re-submits after the window frees, in ${String(retryAfterMs ?? 0)}ms.`
|
|
246
|
+
: reason === "env_lifetime"
|
|
247
|
+
? "the execution environment's declared lifetime is expiring and the run could not be suspended durably (not eligible, or the suspend was refused — see remoteEnvFailures / onError). It was stopped at a clean turn boundary instead of being killed mid-turn when the platform reclaims the environment."
|
|
248
|
+
: `a deployment usage window is exhausted (RunnerDeps.usageWindows) and the run could not be suspended durably, so it was stopped at a clean turn boundary. The window frees in ${String(retryAfterMs ?? 0)}ms.`;
|
|
249
|
+
const e = new Error(message);
|
|
250
|
+
e.code = reason === "env_lifetime" ? "env.lifetime_expired" : "usage.window_exhausted";
|
|
251
|
+
if (retryAfterMs !== undefined)
|
|
252
|
+
e.retryAfterMs = retryAfterMs;
|
|
253
|
+
return e;
|
|
254
|
+
}
|
|
231
255
|
function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
232
|
-
const { spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted,
|
|
256
|
+
const { spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted, walltimeMonotonicDeadline, timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact, runnerHooks } = deps;
|
|
233
257
|
let finalVerifySeenAtLastBoundary = 0;
|
|
234
258
|
const onTurnBoundary = async (event) => {
|
|
235
|
-
if (prepared.callCapRef && turnToolSpan.startMin !== undefined && turnToolSpan.endMax !== undefined) {
|
|
236
|
-
recordToolCycleSample(prepared.callCapRef.state, turnToolSpan.endMax - turnToolSpan.startMin);
|
|
237
|
-
}
|
|
238
|
-
turnToolSpan.startMin = undefined;
|
|
239
|
-
turnToolSpan.endMax = undefined;
|
|
240
259
|
let boundarySteered = false;
|
|
241
|
-
if (prepared.suspendForResource === undefined) {
|
|
242
|
-
const nowMs = Date.now();
|
|
243
|
-
if (!rs.counters.finalizeInjected &&
|
|
244
|
-
spec.limits?.gracefulFinalize !== false &&
|
|
245
|
-
walltimeDeadlineMs !== undefined &&
|
|
246
|
-
prepared.callCapRef !== undefined) {
|
|
247
|
-
const remainingMs = walltimeDeadlineMs - nowMs;
|
|
248
|
-
if (remainingMs > 0 && remainingMs < estimateCycleMs(prepared.callCapRef.state) + (effectiveCushionMs(prepared.callCapRef) ?? 0)) {
|
|
249
|
-
rs.counters.finalizeInjected = true;
|
|
250
|
-
prepared.callCapRef.finalizeMode = true;
|
|
251
|
-
void prepared.harness.setThinkingLevel("off").catch(() => { });
|
|
252
|
-
prepared.turnSnapshot?.refreshRequestPolicy({ thinkingLevel: "off" });
|
|
253
|
-
rs.counters.nudgeIdx = nudgeSchedule.length;
|
|
254
|
-
const finalizeText = buildFinalizeText();
|
|
255
|
-
void prepared.harness.steer(finalizeText, { engineMinted: true }).catch(() => { });
|
|
256
|
-
boundarySteered = true;
|
|
257
|
-
queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
let due;
|
|
261
|
-
while (rs.counters.nudgeIdx < nudgeSchedule.length && nowMs >= nudgeSchedule[rs.counters.nudgeIdx].atMs) {
|
|
262
|
-
due = nudgeSchedule[rs.counters.nudgeIdx++];
|
|
263
|
-
}
|
|
264
|
-
if (due) {
|
|
265
|
-
void prepared.harness.steer(due.text, { engineMinted: true }).catch(() => { });
|
|
266
|
-
rs.counters.nudgesSent += 1;
|
|
267
|
-
boundarySteered = true;
|
|
268
|
-
queue.push({ type: "steering_injected", source: "deadline_nudge", preview: due.text.slice(0, 220), ...ident() });
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
260
|
const walltimeHit = walltimeMonotonicDeadline !== undefined && performance.now() >= walltimeMonotonicDeadline;
|
|
272
261
|
const preemptHit = spec.preemptSignal?.aborted === true;
|
|
273
262
|
if (preemptHit && !prepared.suspendForResource && !rs.counters.preemptIgnoredReported) {
|
|
274
263
|
rs.counters.preemptIgnoredReported = true;
|
|
275
264
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "preempt.ignored", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
276
265
|
}
|
|
266
|
+
let platformCause;
|
|
267
|
+
let platformRetryAfterMs;
|
|
268
|
+
const preemptWillSuspend = preemptHit && prepared.suspendForResource !== undefined;
|
|
269
|
+
if (!preemptWillSuspend && prepared.suspendRef.token === undefined && !prepared.abortController.signal.aborted) {
|
|
270
|
+
if (prepared.envLifetimeSuspendAt !== undefined && Date.now() >= prepared.envLifetimeSuspendAt) {
|
|
271
|
+
platformCause = "env_lifetime";
|
|
272
|
+
}
|
|
273
|
+
else if (prepared.usageGovernance !== undefined) {
|
|
274
|
+
const governance = prepared.usageGovernance;
|
|
275
|
+
try {
|
|
276
|
+
const at = Date.now();
|
|
277
|
+
await governance.commit(stats.tokens, at);
|
|
278
|
+
platformRetryAfterMs = await governance.check(at);
|
|
279
|
+
if (platformRetryAfterMs !== undefined)
|
|
280
|
+
platformCause = "usage_window";
|
|
281
|
+
}
|
|
282
|
+
catch (govErr) {
|
|
283
|
+
rs.limits.platformTerminal = govErr instanceof Error ? govErr : new Error(String(govErr));
|
|
284
|
+
void prepared.harness.abort();
|
|
285
|
+
prepared.abortController.abort();
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (platformCause !== undefined) {
|
|
291
|
+
const platformSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) };
|
|
292
|
+
const committed = prepared.suspendForPlatformLimit !== undefined &&
|
|
293
|
+
(await prepared.suspendForPlatformLimit(platformCause, platformSpend, platformRetryAfterMs !== undefined ? { resumeAfterMs: platformRetryAfterMs } : undefined));
|
|
294
|
+
if (committed) {
|
|
295
|
+
rs.limits.turnsExceeded = false;
|
|
296
|
+
rs.limits.budgetHit = undefined;
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
const terminal = platformLimitTerminal(platformCause, platformRetryAfterMs);
|
|
300
|
+
rs.limits.platformTerminal = terminal;
|
|
301
|
+
try {
|
|
302
|
+
runnerHooks.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
}
|
|
306
|
+
void prepared.harness.abort();
|
|
307
|
+
prepared.abortController.abort();
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
277
310
|
if (prepared.suspendForResource &&
|
|
278
311
|
(rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || walltimeHit || preemptHit) &&
|
|
279
312
|
prepared.suspendRef.token === undefined) {
|
|
280
313
|
const reason = preemptHit
|
|
281
314
|
? "preempt"
|
|
282
315
|
: rs.limits.budgetHit !== undefined
|
|
283
|
-
? "
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
316
|
+
? rs.limits.budgetAxis === "tokens"
|
|
317
|
+
? "budget_tokens"
|
|
318
|
+
: "budget_cost"
|
|
319
|
+
: rs.limits.turnsExceeded
|
|
320
|
+
? "turns"
|
|
321
|
+
: "walltime";
|
|
287
322
|
const sliceSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) };
|
|
288
323
|
if (await prepared.suspendForResource(reason, sliceSpend)) {
|
|
289
324
|
rs.limits.turnsExceeded = false;
|
|
@@ -308,6 +343,39 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
308
343
|
}
|
|
309
344
|
return undefined;
|
|
310
345
|
}
|
|
346
|
+
const approachCfg = spec.limits?.approachNotice;
|
|
347
|
+
if (approachCfg !== false &&
|
|
348
|
+
rs.counters.approachNoticesSent < LIMIT_APPROACH_DEFAULT_THRESHOLDS.length &&
|
|
349
|
+
prepared.suspendForResource === undefined &&
|
|
350
|
+
rs.turn.lastTurnHadToolCalls &&
|
|
351
|
+
!boundarySteered &&
|
|
352
|
+
!prepared.abortController.signal.aborted) {
|
|
353
|
+
const thresholds = approachCfg?.at ?? LIMIT_APPROACH_DEFAULT_THRESHOLDS;
|
|
354
|
+
const ratios = [];
|
|
355
|
+
if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow > 0) {
|
|
356
|
+
ratios.push({ axis: "token budget", ratio: stats.tokens / rs.budget.maxTokensWindow });
|
|
357
|
+
}
|
|
358
|
+
if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd > 0) {
|
|
359
|
+
ratios.push({ axis: "cost budget", ratio: stats.costMicroUsd / rs.budget.maxCostMicroUsd });
|
|
360
|
+
}
|
|
361
|
+
const declaredMaxTurns = spec.limits?.maxTurns;
|
|
362
|
+
if (declaredMaxTurns !== undefined && declaredMaxTurns > 0) {
|
|
363
|
+
ratios.push({ axis: "turn budget", ratio: (stats.turns + 1) / declaredMaxTurns });
|
|
364
|
+
}
|
|
365
|
+
let tightest;
|
|
366
|
+
for (const r of ratios)
|
|
367
|
+
if (tightest === undefined || r.ratio > tightest.ratio)
|
|
368
|
+
tightest = r;
|
|
369
|
+
const index = rs.counters.approachNoticesSent;
|
|
370
|
+
if (tightest !== undefined && tightest.ratio >= thresholds[index]) {
|
|
371
|
+
rs.counters.approachNoticesSent += 1;
|
|
372
|
+
const percent = Math.min(100, Math.round(tightest.ratio * 100));
|
|
373
|
+
const body = limitApproachFrames(index === 0 ? 0 : 1, tightest.axis, percent);
|
|
374
|
+
void prepared.harness.steer(body, { engineMinted: true }).catch(() => undefined);
|
|
375
|
+
queue.push({ type: "steering_injected", source: "limit_approach", preview: body.slice(0, 220), ...ident() });
|
|
376
|
+
boundarySteered = true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
311
379
|
if (prepared.reviewRequestRef.pending !== undefined) {
|
|
312
380
|
const { reason } = prepared.reviewRequestRef.pending;
|
|
313
381
|
prepared.reviewRequestRef.pending = undefined;
|
|
@@ -322,7 +390,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
322
390
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "review.dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
323
391
|
}
|
|
324
392
|
if (prepared.lspDiagnostics && !prepared.lspDiagnostics.registry.isEmpty() && !prepared.abortController.signal.aborted) {
|
|
325
|
-
const files = prepared.lspDiagnostics.registry.drain();
|
|
393
|
+
const files = prepared.lspDiagnostics.registry.drain(prepared.lspDiagnostics.runIdent);
|
|
326
394
|
if (files.length > 0) {
|
|
327
395
|
queue.push({ type: "diagnostics", files, isNew: true, ...ident() });
|
|
328
396
|
const block = formatDiagnosticsBlock(files);
|
|
@@ -337,7 +405,6 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
337
405
|
rs.attach.sizeGuidelineState !== undefined ||
|
|
338
406
|
rs.attach.attachState !== undefined) &&
|
|
339
407
|
!boundarySteered &&
|
|
340
|
-
!rs.counters.finalizeInjected &&
|
|
341
408
|
rs.counters.finalVerifyInjections === 0 &&
|
|
342
409
|
!prepared.abortController.signal.aborted) {
|
|
343
410
|
const due = [];
|
|
@@ -525,7 +592,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
525
592
|
const batch = rs.turn.toolBatch;
|
|
526
593
|
rs.turn.toolBatch = [];
|
|
527
594
|
batchArgs?.clear();
|
|
528
|
-
const injectedThisTurn = finalVerifyInjectedThisTurn ? "final_verification" :
|
|
595
|
+
const injectedThisTurn = finalVerifyInjectedThisTurn ? "final_verification" : undefined;
|
|
529
596
|
if (!boundarySteered && !prepared.abortController.signal.aborted) {
|
|
530
597
|
try {
|
|
531
598
|
const r = await postToolBatchHook(batch, injectedThisTurn !== undefined ? { injectedThisTurn } : undefined);
|
|
@@ -555,7 +622,6 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
555
622
|
}
|
|
556
623
|
const forceManual = manualCompactRef.requested;
|
|
557
624
|
if ((!withinTaskCompaction && !forceManual) ||
|
|
558
|
-
(rs.counters.finalizeInjected && !forceManual) ||
|
|
559
625
|
prepared.abortController.signal.aborted ||
|
|
560
626
|
prepared.suspendRef.token !== undefined ||
|
|
561
627
|
prepared.reviewRef.token !== undefined) {
|
|
@@ -617,9 +683,6 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
617
683
|
const trimPressure = trimPressureArmed && !rs.counters.trimForceBackoff;
|
|
618
684
|
const passTrigger = trimPressure ? "forced" : forceManual ? "manual" : "auto";
|
|
619
685
|
let manualOutcome = "failed";
|
|
620
|
-
if (rs.counters.finalizeInjected && forceManual) {
|
|
621
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.manual_in_finalize", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
622
|
-
}
|
|
623
686
|
const manualInstructions = claimedManual?.instructions;
|
|
624
687
|
try {
|
|
625
688
|
const comp = await maybeCompact({
|
|
@@ -744,16 +807,10 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
744
807
|
manualOutcome = "mooted";
|
|
745
808
|
}
|
|
746
809
|
else if (!prepared.abortController.signal.aborted) {
|
|
747
|
-
|
|
748
|
-
if (!walltimeAbort)
|
|
749
|
-
compactionBreaker.failures += 1;
|
|
810
|
+
compactionBreaker.failures += 1;
|
|
750
811
|
runnerHooks.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
751
812
|
const msg = String(err instanceof Error ? err.message : err);
|
|
752
|
-
const failReason =
|
|
753
|
-
? "walltime soft-deadline abort (engine-initiated write-out protection; not counted toward the breaker)"
|
|
754
|
-
: msg.length > 512
|
|
755
|
-
? `${msg.slice(0, 512)}…`
|
|
756
|
-
: msg;
|
|
813
|
+
const failReason = msg.length > 512 ? `${msg.slice(0, 512)}…` : msg;
|
|
757
814
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
758
815
|
kind: "compaction.failed",
|
|
759
816
|
version: 1,
|
|
@@ -776,7 +833,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
776
833
|
return onTurnBoundary;
|
|
777
834
|
}
|
|
778
835
|
function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
779
|
-
const { spec, queue, internals,
|
|
836
|
+
const { spec, queue, internals, ident, parentToolCallId, subagentName, pushContent, emitCommitted, startedToolCallIds, toolStartAt, writeFamilyOf, toolLabels, postToolBatchHook, batchArgs } = deps;
|
|
780
837
|
let lastWorkspaceCwd = prepared.cwdRef?.current;
|
|
781
838
|
const announceWorkspaceMove = () => {
|
|
782
839
|
const cwdNow = prepared.cwdRef?.current;
|
|
@@ -801,8 +858,10 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
801
858
|
rs.budget.callOutputChars += ev.delta.length;
|
|
802
859
|
if (rs.budget.callOutputChars - rs.budget.lastStreamBudgetCheck >= 256) {
|
|
803
860
|
rs.budget.lastStreamBudgetCheck = rs.budget.callOutputChars;
|
|
804
|
-
|
|
861
|
+
const projectedAxis = rs.budget.projectedOverBudget();
|
|
862
|
+
if (projectedAxis !== undefined) {
|
|
805
863
|
rs.limits.budgetHit = "exceeded";
|
|
864
|
+
rs.limits.budgetAxis = projectedAxis;
|
|
806
865
|
void prepared.harness.abort();
|
|
807
866
|
prepared.abortController.abort();
|
|
808
867
|
}
|
|
@@ -841,19 +900,8 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
841
900
|
rs.turn.turnUsage.cacheWriteTokens += u.cacheWrite || 0;
|
|
842
901
|
rs.turn.turnUsage.costMicroUsd += turnCostMicroUsd;
|
|
843
902
|
const callEndAt = Date.now();
|
|
844
|
-
const callIssuedAt = prepared.callIssuedAtRef
|
|
845
|
-
|
|
846
|
-
prepared.callIssuedAtRef.current = undefined;
|
|
847
|
-
const capLast = prepared.callCapRef?.state.last;
|
|
848
|
-
if (prepared.callCapRef) {
|
|
849
|
-
recordCallSample(prepared.callCapRef.state, u.output || 0, rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0, {
|
|
850
|
-
contentValid: m.usageMissing === true && hasRealContent(m.content),
|
|
851
|
-
});
|
|
852
|
-
if (rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined) {
|
|
853
|
-
recordTtftSample(prepared.callCapRef.state, rs.turn.firstTokenAt - rs.turn.callStartAt);
|
|
854
|
-
}
|
|
855
|
-
prepared.callCapRef.state.last = undefined;
|
|
856
|
-
}
|
|
903
|
+
const callIssuedAt = prepared.callIssuedAtRef.current;
|
|
904
|
+
prepared.callIssuedAtRef.current = undefined;
|
|
857
905
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
858
906
|
kind: "brain.call",
|
|
859
907
|
version: 1,
|
|
@@ -870,25 +918,11 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
870
918
|
firstTokenMs: rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined ? rs.turn.firstTokenAt - rs.turn.callStartAt : undefined,
|
|
871
919
|
...(callIssuedAt !== undefined ? { callStartedAt: callIssuedAt } : {}),
|
|
872
920
|
...(rs.telemetry.pricingConfigured ? { costMicroUsd: turnCostMicroUsd } : {}),
|
|
873
|
-
...(capLast !== undefined ? { callCap: capLast.cap, capThinkingSkipped: capLast.thinkingSkipped } : {}),
|
|
874
921
|
...(m.role === "assistant" && typeof m.stopReason === "string"
|
|
875
922
|
? { stopReason: m.stopReason }
|
|
876
923
|
: {}),
|
|
877
924
|
ts: callEndAt,
|
|
878
925
|
}));
|
|
879
|
-
if (prepared.callCapRef &&
|
|
880
|
-
prepared.callCapRef.reasoningObserved !== true &&
|
|
881
|
-
capLast !== undefined &&
|
|
882
|
-
m.role === "assistant" &&
|
|
883
|
-
m.stopReason === "length") {
|
|
884
|
-
const blocks = Array.isArray(m.content)
|
|
885
|
-
? (m.content)
|
|
886
|
-
: [];
|
|
887
|
-
const productive = blocks.some((c) => c?.type === "toolCall") ||
|
|
888
|
-
blocks.some((c) => c?.type === "text" && typeof c.text === "string" && c.text.trim() !== "");
|
|
889
|
-
if (!productive)
|
|
890
|
-
prepared.callCapRef.reasoningObserved = true;
|
|
891
|
-
}
|
|
892
926
|
if (prepared.cacheBreakDetector && prepared.cacheFingerprint) {
|
|
893
927
|
const finding = prepared.cacheBreakDetector.observe({
|
|
894
928
|
turn: stats.turns + 1,
|
|
@@ -906,20 +940,6 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
906
940
|
if (m.role === "assistant" && typeof m.stopReason === "string") {
|
|
907
941
|
rs.turn.turnStopReason = m.stopReason;
|
|
908
942
|
}
|
|
909
|
-
if (m.role === "assistant" && isWalltimeCutMessage(m)) {
|
|
910
|
-
rs.counters.callCutoffs += 1;
|
|
911
|
-
if (prepared.callCapRef)
|
|
912
|
-
prepared.callCapRef.finalizeMode = true;
|
|
913
|
-
if (!rs.counters.finalizeInjected) {
|
|
914
|
-
const finalizeText = buildFinalizeText();
|
|
915
|
-
void prepared.harness.steer(finalizeText, { engineMinted: true }).catch(() => { });
|
|
916
|
-
void prepared.harness.setThinkingLevel("off").catch(() => { });
|
|
917
|
-
prepared.turnSnapshot?.refreshRequestPolicy({ thinkingLevel: "off" });
|
|
918
|
-
rs.counters.finalizeInjected = true;
|
|
919
|
-
rs.counters.nudgeIdx = nudgeSchedule.length;
|
|
920
|
-
queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
943
|
if (m.role === "assistant") {
|
|
924
944
|
const rep = m.repetition;
|
|
925
945
|
if (rep !== undefined) {
|
|
@@ -950,13 +970,6 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
950
970
|
}
|
|
951
971
|
}
|
|
952
972
|
}
|
|
953
|
-
if (prepared.callCapRef &&
|
|
954
|
-
prepared.callCapRef.reasoningObserved !== true &&
|
|
955
|
-
m.role === "assistant" &&
|
|
956
|
-
Array.isArray(m.content) &&
|
|
957
|
-
m.content.some((c) => c?.type === "thinking")) {
|
|
958
|
-
prepared.callCapRef.reasoningObserved = true;
|
|
959
|
-
}
|
|
960
973
|
if (m.role === "assistant") {
|
|
961
974
|
const content = m.content;
|
|
962
975
|
if (rs.attach.attachState !== undefined)
|
|
@@ -995,11 +1008,6 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
995
1008
|
const toolStarted = toolStartAt.get(event.toolCallId);
|
|
996
1009
|
toolStartAt.delete(event.toolCallId);
|
|
997
1010
|
const toolNow = Date.now();
|
|
998
|
-
if (prepared.callCapRef && toolStarted !== undefined) {
|
|
999
|
-
recordToolSample(prepared.callCapRef.state, toolNow - toolStarted);
|
|
1000
|
-
turnToolSpan.startMin = turnToolSpan.startMin === undefined ? toolStarted : Math.min(turnToolSpan.startMin, toolStarted);
|
|
1001
|
-
turnToolSpan.endMax = turnToolSpan.endMax === undefined ? toolNow : Math.max(turnToolSpan.endMax, toolNow);
|
|
1002
|
-
}
|
|
1003
1011
|
if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
|
|
1004
1012
|
rs.counters.wroteThisRun = true;
|
|
1005
1013
|
}
|
|
@@ -1051,16 +1059,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1051
1059
|
toolName: event.toolName,
|
|
1052
1060
|
...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
|
|
1053
1061
|
isError: event.isError,
|
|
1054
|
-
...(
|
|
1055
|
-
const o = toolOutputFrom(event.result);
|
|
1056
|
-
return o !== undefined
|
|
1057
|
-
? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) }
|
|
1058
|
-
: {};
|
|
1059
|
-
})(),
|
|
1060
|
-
...(() => {
|
|
1061
|
-
const st = structuredFrom(event.result);
|
|
1062
|
-
return st !== undefined ? { structured: st } : {};
|
|
1063
|
-
})(),
|
|
1062
|
+
...toolEndBodyFrom(event.result, event.isError),
|
|
1064
1063
|
...ident(),
|
|
1065
1064
|
});
|
|
1066
1065
|
announceWorkspaceMove();
|
|
@@ -1119,21 +1118,17 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1119
1118
|
}
|
|
1120
1119
|
else {
|
|
1121
1120
|
if (rs.degrade.degraded === undefined &&
|
|
1122
|
-
spec.degrade &&
|
|
1121
|
+
spec.limits?.degrade &&
|
|
1123
1122
|
rs.degrade.degradeToModel &&
|
|
1124
1123
|
rs.budget.maxCostMicroUsd !== undefined &&
|
|
1125
|
-
stats.costMicroUsd >= spec.degrade.atCostFraction * rs.budget.maxCostMicroUsd) {
|
|
1124
|
+
stats.costMicroUsd >= spec.limits.degrade.atCostFraction * rs.budget.maxCostMicroUsd) {
|
|
1126
1125
|
void prepared.harness.setModel(rs.degrade.degradeToModel);
|
|
1127
1126
|
rs.degrade.recordDegraded({ from: prepared.model.id, to: rs.degrade.degradeToModel.id, reason: "budget", atTurn: stats.turns }, rs.degrade.degradeToModel);
|
|
1128
|
-
if (prepared.callCapRef &&
|
|
1129
|
-
spec.limits?.maxOutputTokens === undefined &&
|
|
1130
|
-
rs.degrade.degradeToModel.maxTokens !== undefined &&
|
|
1131
|
-
(prepared.callCapRef.staticCapTokens === undefined || rs.degrade.degradeToModel.maxTokens < prepared.callCapRef.staticCapTokens)) {
|
|
1132
|
-
prepared.callCapRef.staticCapTokens = rs.degrade.degradeToModel.maxTokens;
|
|
1133
|
-
}
|
|
1134
1127
|
}
|
|
1135
|
-
|
|
1128
|
+
const overAxis = rs.limits.budgetHit ? undefined : rs.budget.overBudget();
|
|
1129
|
+
if (overAxis !== undefined) {
|
|
1136
1130
|
rs.limits.budgetHit = "exceeded";
|
|
1131
|
+
rs.limits.budgetAxis = overAxis;
|
|
1137
1132
|
if (prepared.suspendForResource === undefined) {
|
|
1138
1133
|
void prepared.harness.abort();
|
|
1139
1134
|
prepared.abortController.abort();
|
|
@@ -1702,9 +1697,9 @@ export class Runner {
|
|
|
1702
1697
|
rs.telemetry.cacheFamily = cacheFamilyOf(prepared.model);
|
|
1703
1698
|
rs.telemetry.pricing = this.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
|
|
1704
1699
|
rs.telemetry.pricingConfigured = this.deps.pricing?.[prepared.model.id] !== undefined || prepared.model.cost !== undefined;
|
|
1705
|
-
if (spec.degrade) {
|
|
1700
|
+
if (spec.limits?.degrade) {
|
|
1706
1701
|
try {
|
|
1707
|
-
rs.degrade.degradeToModel = resolveModel(spec.degrade.to, this.deps.models);
|
|
1702
|
+
rs.degrade.degradeToModel = resolveModel(spec.limits.degrade.to, this.deps.models);
|
|
1708
1703
|
}
|
|
1709
1704
|
catch (e) {
|
|
1710
1705
|
this.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId });
|
|
@@ -1776,7 +1771,7 @@ export class Runner {
|
|
|
1776
1771
|
catch {
|
|
1777
1772
|
}
|
|
1778
1773
|
};
|
|
1779
|
-
const sliceWindowMicroUsd = spec.maxCostUsd !== undefined ? Math.round(spec.maxCostUsd * 1e6) : undefined;
|
|
1774
|
+
const sliceWindowMicroUsd = spec.limits?.maxCostUsd !== undefined ? Math.round(spec.limits.maxCostUsd * 1e6) : undefined;
|
|
1780
1775
|
rs.budget.remainingMicroUsd = prepared.resourceLedger
|
|
1781
1776
|
? remainingBudgetMicroUsd(prepared.resourceLedger)
|
|
1782
1777
|
: spec.resourceSuspend?.totalBudgetUsd !== undefined
|
|
@@ -1786,24 +1781,36 @@ export class Runner {
|
|
|
1786
1781
|
sliceWindowMicroUsd !== undefined && rs.budget.remainingMicroUsd !== undefined
|
|
1787
1782
|
? Math.min(sliceWindowMicroUsd, rs.budget.remainingMicroUsd)
|
|
1788
1783
|
: (sliceWindowMicroUsd ?? rs.budget.remainingMicroUsd);
|
|
1789
|
-
rs.budget.
|
|
1790
|
-
(
|
|
1784
|
+
rs.budget.remainingTokens = prepared.resourceLedger
|
|
1785
|
+
? remainingTokens(prepared.resourceLedger)
|
|
1786
|
+
: spec.resourceSuspend?.totalTokens;
|
|
1787
|
+
rs.budget.maxTokensWindow =
|
|
1788
|
+
spec.limits?.maxTokens !== undefined && rs.budget.remainingTokens !== undefined
|
|
1789
|
+
? Math.min(spec.limits.maxTokens, rs.budget.remainingTokens)
|
|
1790
|
+
: (spec.limits?.maxTokens ?? rs.budget.remainingTokens);
|
|
1791
|
+
rs.budget.overBudget = () => {
|
|
1792
|
+
if (rs.budget.maxTokensWindow !== undefined && stats.tokens > rs.budget.maxTokensWindow)
|
|
1793
|
+
return "tokens";
|
|
1794
|
+
if (rs.budget.maxCostMicroUsd !== undefined && stats.costMicroUsd > rs.budget.maxCostMicroUsd)
|
|
1795
|
+
return "cost";
|
|
1796
|
+
return undefined;
|
|
1797
|
+
};
|
|
1791
1798
|
rs.budget.streamCancel =
|
|
1792
|
-
(spec.budgetStreamCancel ?? rs.budget.maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
|
|
1799
|
+
(spec.limits?.budgetStreamCancel ?? rs.budget.maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
|
|
1793
1800
|
rs.budget.callOutputChars = 0;
|
|
1794
1801
|
rs.budget.lastStreamBudgetCheck = 0;
|
|
1795
1802
|
rs.budget.projectedOverBudget = () => {
|
|
1796
1803
|
const estOut = Math.ceil(rs.budget.callOutputChars / 4);
|
|
1804
|
+
if (rs.budget.maxTokensWindow !== undefined && stats.tokens + estOut > rs.budget.maxTokensWindow)
|
|
1805
|
+
return "tokens";
|
|
1797
1806
|
if (rs.budget.maxCostMicroUsd !== undefined) {
|
|
1798
1807
|
const estOutMicro = computeCostMicroUsd({ totalInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: estOut }, rs.telemetry.pricing);
|
|
1799
1808
|
if (stats.costMicroUsd + estOutMicro > rs.budget.maxCostMicroUsd)
|
|
1800
|
-
return
|
|
1809
|
+
return "cost";
|
|
1801
1810
|
}
|
|
1802
|
-
return
|
|
1811
|
+
return undefined;
|
|
1803
1812
|
};
|
|
1804
1813
|
rs.turn.turnUsageMissing = false;
|
|
1805
|
-
rs.counters.nudgesSent = 0;
|
|
1806
|
-
rs.counters.callCutoffs = 0;
|
|
1807
1814
|
rs.counters.repetitionCuts = 0;
|
|
1808
1815
|
rs.counters.repetitionSpared = 0;
|
|
1809
1816
|
rs.counters.repetitionEvents = [];
|
|
@@ -1999,7 +2006,6 @@ export class Runner {
|
|
|
1999
2006
|
}
|
|
2000
2007
|
const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(name);
|
|
2001
2008
|
const toolStartAt = new Map();
|
|
2002
|
-
const turnToolSpan = {};
|
|
2003
2009
|
const startedToolCallIds = new Set();
|
|
2004
2010
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "task.start", version: 1, taskId: rs.telemetry.taskId, model: prepared.model.id, engineVersion: engineVersion(), ts: rs.telemetry.taskStart }));
|
|
2005
2011
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
@@ -2054,61 +2060,9 @@ export class Runner {
|
|
|
2054
2060
|
};
|
|
2055
2061
|
});
|
|
2056
2062
|
}
|
|
2057
|
-
const
|
|
2058
|
-
const remainingWalltimeBudgetMs = prepared.resourceLedger?.totalWalltimeSec !== undefined
|
|
2059
|
-
? remainingWalltimeMs(prepared.resourceLedger)
|
|
2060
|
-
: spec.resourceSuspend?.totalWalltimeSec !== undefined
|
|
2061
|
-
? Math.max(0, spec.resourceSuspend.totalWalltimeSec * 1000 - (prepared.resourceLedger?.spentWalltimeMs ?? 0))
|
|
2062
|
-
: undefined;
|
|
2063
|
-
const effectiveTimeoutMs = sliceWindowWalltimeMs !== undefined && remainingWalltimeBudgetMs !== undefined
|
|
2064
|
-
? Math.min(sliceWindowWalltimeMs, remainingWalltimeBudgetMs)
|
|
2065
|
-
: (sliceWindowWalltimeMs ?? remainingWalltimeBudgetMs);
|
|
2066
|
-
const walltimeDeadlineMs = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStart + effectiveTimeoutMs : undefined;
|
|
2063
|
+
const effectiveTimeoutMs = spec.limits?.maxWalltimeMs;
|
|
2067
2064
|
const walltimeMonotonicDeadline = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStartMonotonic + effectiveTimeoutMs : undefined;
|
|
2068
|
-
if (prepared.callCapRef && prepared.suspendForResource === undefined && walltimeDeadlineMs !== undefined) {
|
|
2069
|
-
prepared.callCapRef.deadlineMs = walltimeDeadlineMs;
|
|
2070
|
-
if (spec.limits?.gracefulFinalize !== false) {
|
|
2071
|
-
prepared.callCapRef.cushionMs = writeoutCushionMs(effectiveTimeoutMs);
|
|
2072
|
-
prepared.callCapRef.budgetMs = effectiveTimeoutMs;
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
2075
|
-
rs.counters.finalizeInjected = false;
|
|
2076
2065
|
rs.counters.walltimeSyncBackstopFired = false;
|
|
2077
|
-
const nudgeSchedule = [];
|
|
2078
|
-
{
|
|
2079
|
-
const conf = spec.limits?.deadlineNudge;
|
|
2080
|
-
const budgetSec = effectiveTimeoutMs !== undefined ? effectiveTimeoutMs / 1000 : undefined;
|
|
2081
|
-
if (conf !== false && budgetSec && budgetSec > 0) {
|
|
2082
|
-
const [a, b] = conf?.at ?? [0.8, 0.95];
|
|
2083
|
-
if (a > 0 && a < 1) {
|
|
2084
|
-
const t1 = rs.telemetry.taskStart + a * budgetSec * 1000;
|
|
2085
|
-
const remainMin = Math.max(1, Math.round((budgetSec * 1000 * (1 - a)) / 60_000));
|
|
2086
|
-
const budgetMin = Math.max(1, Math.round(budgetSec / 60));
|
|
2087
|
-
nudgeSchedule.push({
|
|
2088
|
-
atMs: t1,
|
|
2089
|
-
text: `<system-reminder>[deadline] ~${remainMin} min of the ~${budgetMin}-min wall-clock budget remain. ` +
|
|
2090
|
-
`Start converging: persist finished work (write files / commit), prefer completing in-flight items over opening new ones.</system-reminder>`,
|
|
2091
|
-
});
|
|
2092
|
-
const t2 = rs.telemetry.taskStart + Math.max(Math.min(b * budgetSec * 1000, budgetSec * 1000 - 120_000), a * budgetSec * 1000 + 1);
|
|
2093
|
-
if (b > a && b < 1) {
|
|
2094
|
-
nudgeSchedule.push({
|
|
2095
|
-
atMs: t2,
|
|
2096
|
-
text: `<system-reminder>[deadline] the wall-clock budget is nearly exhausted. STOP new work — ` +
|
|
2097
|
-
`write out deliverables and give your final answer NOW; an unfinished answer beats a hard timeout.</system-reminder>`,
|
|
2098
|
-
});
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
rs.counters.nudgeIdx = 0;
|
|
2104
|
-
const buildFinalizeText = () => `<system-reminder>[deadline] the wall-clock budget is exhausted — this is the FINAL turn. ` +
|
|
2105
|
-
`Write your best current work to the deliverable path NOW, even if incomplete — persist to disk FIRST; everything else comes after. ` +
|
|
2106
|
-
`If a better version exists anywhere else (a draft, scratch or temp file), copy it to the deliverable path now. ` +
|
|
2107
|
-
`Do NOT open new work, new investigations, or long commands; write out and stop.` +
|
|
2108
|
-
(finalVerificationOn
|
|
2109
|
-
? ` After writing, if seconds permit, confirm the deliverable file actually EXISTS at the required path with your latest content.`
|
|
2110
|
-
: "") +
|
|
2111
|
-
`</system-reminder>`;
|
|
2112
2066
|
const timeout = startTimeout(prepared.harness, prepared.abortController, walltimeMonotonicDeadline !== undefined ? walltimeMonotonicDeadline - performance.now() : undefined, prepared.suspendForResource !== undefined);
|
|
2113
2067
|
const parentToolCallId = internals?.parentToolCallId;
|
|
2114
2068
|
const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: rs.telemetry.taskId } : { eventId: uuidv7() };
|
|
@@ -2164,8 +2118,8 @@ export class Runner {
|
|
|
2164
2118
|
const batchArgs = postToolBatchHook ? new Map() : undefined;
|
|
2165
2119
|
rs.turn.toolBatch = [];
|
|
2166
2120
|
const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove } = makeHarnessHandlers(prepared, stats, rs, {
|
|
2167
|
-
spec, queue, internals,
|
|
2168
|
-
pushContent, emitCommitted, startedToolCallIds, toolStartAt,
|
|
2121
|
+
spec, queue, internals, ident, parentToolCallId, subagentName,
|
|
2122
|
+
pushContent, emitCommitted, startedToolCallIds, toolStartAt, writeFamilyOf,
|
|
2169
2123
|
toolLabels, postToolBatchHook, batchArgs,
|
|
2170
2124
|
runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
|
|
2171
2125
|
});
|
|
@@ -2206,7 +2160,6 @@ export class Runner {
|
|
|
2206
2160
|
};
|
|
2207
2161
|
const withinTaskCompaction = (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true);
|
|
2208
2162
|
const compactionBreaker = { failures: 0 };
|
|
2209
|
-
const compactionThroughputRef = {};
|
|
2210
2163
|
const windowSafetyOptions = (mainModel) => ({
|
|
2211
2164
|
...(rs.budget.maxCostMicroUsd !== undefined
|
|
2212
2165
|
? {
|
|
@@ -2217,15 +2170,6 @@ export class Runner {
|
|
|
2217
2170
|
},
|
|
2218
2171
|
}
|
|
2219
2172
|
: {}),
|
|
2220
|
-
...(walltimeDeadlineMs !== undefined && prepared.callCapRef !== undefined
|
|
2221
|
-
? {
|
|
2222
|
-
walltime: {
|
|
2223
|
-
deadlineMs: walltimeDeadlineMs,
|
|
2224
|
-
cushionMs: () => effectiveCushionMs(prepared.callCapRef) ?? 0,
|
|
2225
|
-
throughputRef: compactionThroughputRef,
|
|
2226
|
-
},
|
|
2227
|
-
}
|
|
2228
|
-
: {}),
|
|
2229
2173
|
onWindowSafety: (info) => {
|
|
2230
2174
|
if (info.kind === "fallback") {
|
|
2231
2175
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
@@ -2263,7 +2207,6 @@ export class Runner {
|
|
|
2263
2207
|
(rs.counters.finalVerifyInjections === 0 || (rs.counters.finalVerifyInjections === 1 && rs.counters.groundingSignalPreR9 && !rs.counters.groundingSignalPostR9)) &&
|
|
2264
2208
|
rs.counters.wroteThisRun &&
|
|
2265
2209
|
prepared.outputRef.set !== true &&
|
|
2266
|
-
!rs.counters.finalizeInjected &&
|
|
2267
2210
|
!(rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns - 1)) {
|
|
2268
2211
|
rs.counters.finalVerifyInjections += 1;
|
|
2269
2212
|
if (rs.counters.finalVerifyInjections === 2) {
|
|
@@ -2371,18 +2314,6 @@ export class Runner {
|
|
|
2371
2314
|
malformedToolUse: {},
|
|
2372
2315
|
thinkingOnly: {},
|
|
2373
2316
|
degenerateOutput: { detect: (m) => isDegenerateCutMessage(m) },
|
|
2374
|
-
walltimeCutoff: {
|
|
2375
|
-
detect: (m) => isWalltimeCutMessage(m),
|
|
2376
|
-
hasLiveWriteoutWindow: () => {
|
|
2377
|
-
const ref = prepared.callCapRef;
|
|
2378
|
-
if (ref === undefined)
|
|
2379
|
-
return true;
|
|
2380
|
-
const soft = softExecDeadlineMs(ref);
|
|
2381
|
-
if (soft === undefined)
|
|
2382
|
-
return true;
|
|
2383
|
-
return soft - Date.now() >= minLiveWriteoutWindowMs(ref.state);
|
|
2384
|
-
},
|
|
2385
|
-
},
|
|
2386
2317
|
promptTooLong: {
|
|
2387
2318
|
recover: async () => {
|
|
2388
2319
|
if (!(spec.compaction?.enabled ?? true))
|
|
@@ -2456,8 +2387,7 @@ export class Runner {
|
|
|
2456
2387
|
return comp.compacted === true;
|
|
2457
2388
|
}
|
|
2458
2389
|
catch (err) {
|
|
2459
|
-
|
|
2460
|
-
compactionBreaker.failures += 1;
|
|
2390
|
+
compactionBreaker.failures += 1;
|
|
2461
2391
|
const msg = String(err instanceof Error ? err.message : err);
|
|
2462
2392
|
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
|
|
2463
2393
|
return false;
|
|
@@ -2472,8 +2402,8 @@ export class Runner {
|
|
|
2472
2402
|
w.resolve(outcome);
|
|
2473
2403
|
};
|
|
2474
2404
|
const onTurnBoundary = makeTurnBoundary(prepared, stats, rs, {
|
|
2475
|
-
spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted,
|
|
2476
|
-
|
|
2405
|
+
spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted,
|
|
2406
|
+
walltimeMonotonicDeadline,
|
|
2477
2407
|
timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction,
|
|
2478
2408
|
compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact,
|
|
2479
2409
|
runnerHooks: {
|
|
@@ -2517,7 +2447,7 @@ export class Runner {
|
|
|
2517
2447
|
if (resume) {
|
|
2518
2448
|
const walltimeExhaustedResume = effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0;
|
|
2519
2449
|
if (!walltimeExhaustedResume && resume.outcome.gate !== "wake") {
|
|
2520
|
-
await this.applyResumeDecision(prepared, resume, (e) =>
|
|
2450
|
+
await this.applyResumeDecision(prepared, resume, (e) => pushContent({ ...e, ...ident() }), emitCommitted, (toolName, details) => {
|
|
2521
2451
|
if (rs.attach.attachState === undefined)
|
|
2522
2452
|
return;
|
|
2523
2453
|
const family = writeFamilyOf(toolName);
|
|
@@ -2541,8 +2471,13 @@ export class Runner {
|
|
|
2541
2471
|
void prepared.harness.abort();
|
|
2542
2472
|
prepared.abortController.abort();
|
|
2543
2473
|
}
|
|
2474
|
+
else if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow <= 0) {
|
|
2475
|
+
rs.limits.budgetHit = "exceeded";
|
|
2476
|
+
rs.limits.budgetAxis = "tokens";
|
|
2477
|
+
}
|
|
2544
2478
|
else if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd <= 0) {
|
|
2545
2479
|
rs.limits.budgetHit = "exceeded";
|
|
2480
|
+
rs.limits.budgetAxis = "cost";
|
|
2546
2481
|
}
|
|
2547
2482
|
else {
|
|
2548
2483
|
let continuation = resumeContinuation(resume);
|
|
@@ -2641,13 +2576,30 @@ export class Runner {
|
|
|
2641
2576
|
cacheReadTokens: 0,
|
|
2642
2577
|
cacheWriteTokens: 0,
|
|
2643
2578
|
cacheWriteTokensLong: 0,
|
|
2644
|
-
outputTokens: prepared.model.maxTokens ??
|
|
2579
|
+
outputTokens: prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS,
|
|
2645
2580
|
}, rs.telemetry.pricing);
|
|
2646
2581
|
const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? rs.budget.remainingMicroUsd : rs.budget.maxCostMicroUsd;
|
|
2582
|
+
const precallTokens = Math.ceil(effectiveObjective.length / 4) + (prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS);
|
|
2583
|
+
const precallCeilingTokens = prepared.suspendForResource !== undefined ? rs.budget.remainingTokens : rs.budget.maxTokensWindow;
|
|
2584
|
+
const entryUsageRetryAfterMs = prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
|
|
2647
2585
|
if (promptBlocked) {
|
|
2648
2586
|
}
|
|
2587
|
+
else if (entryUsageRetryAfterMs !== undefined) {
|
|
2588
|
+
const terminal = platformLimitTerminal("usage_window", entryUsageRetryAfterMs, "entry");
|
|
2589
|
+
rs.limits.platformTerminal = terminal;
|
|
2590
|
+
try {
|
|
2591
|
+
this.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
|
|
2592
|
+
}
|
|
2593
|
+
catch {
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
else if (precallCeilingTokens !== undefined && precallTokens > precallCeilingTokens) {
|
|
2597
|
+
rs.limits.budgetHit = "precall";
|
|
2598
|
+
rs.limits.budgetAxis = "tokens";
|
|
2599
|
+
}
|
|
2649
2600
|
else if (precallCeilingMicroUsd !== undefined && precallMicroUsd > precallCeilingMicroUsd) {
|
|
2650
2601
|
rs.limits.budgetHit = "precall";
|
|
2602
|
+
rs.limits.budgetAxis = "cost";
|
|
2651
2603
|
}
|
|
2652
2604
|
else if (effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0) {
|
|
2653
2605
|
timeout.fired = true;
|
|
@@ -2687,6 +2639,7 @@ export class Runner {
|
|
|
2687
2639
|
}
|
|
2688
2640
|
finally {
|
|
2689
2641
|
timeout.clear();
|
|
2642
|
+
prepared.lspDiagnostics?.registry.releaseRun(prepared.lspDiagnostics.runIdent);
|
|
2690
2643
|
prepared.abortController.abort();
|
|
2691
2644
|
prepared.releaseSignal();
|
|
2692
2645
|
try {
|
|
@@ -2702,6 +2655,17 @@ export class Runner {
|
|
|
2702
2655
|
if (threw === undefined && prepared.brainCallGuardrailRef.timedOut !== undefined) {
|
|
2703
2656
|
threw = prepared.brainCallGuardrailRef.timedOut;
|
|
2704
2657
|
}
|
|
2658
|
+
if (rs.limits.platformTerminal !== undefined) {
|
|
2659
|
+
threw = rs.limits.platformTerminal;
|
|
2660
|
+
}
|
|
2661
|
+
if (prepared.usageGovernance !== undefined) {
|
|
2662
|
+
try {
|
|
2663
|
+
await prepared.usageGovernance.commit(stats.tokens, Date.now());
|
|
2664
|
+
}
|
|
2665
|
+
catch (flushErr) {
|
|
2666
|
+
this.deps.onError?.(flushErr, { phase: "config", sessionId: prepared.sessionId });
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2705
2669
|
const suspended = prepared.suspendRef.token !== undefined && prepared.suspendRef.gate !== undefined;
|
|
2706
2670
|
const reviewPaused = prepared.reviewRef.token !== undefined && prepared.reviewRef.gate !== undefined;
|
|
2707
2671
|
const durablyPaused = suspended || reviewPaused;
|
|
@@ -2800,24 +2764,12 @@ export class Runner {
|
|
|
2800
2764
|
gates: [...prepared.humanReviewRef.gates],
|
|
2801
2765
|
};
|
|
2802
2766
|
}
|
|
2803
|
-
|
|
2804
|
-
const toolClamps = prepared.callCapRef?.toolClamps ?? 0;
|
|
2805
|
-
const toolCuts = prepared.callCapRef?.toolCuts ?? 0;
|
|
2806
|
-
const pendingCutTools = prepared.callCapRef?.pendingCutTools ?? 0;
|
|
2807
|
-
const toolCutKills = prepared.cutKills?.kills ?? 0;
|
|
2808
|
-
if (rs.counters.finalizeInjected || rs.counters.nudgesSent > 0 || capShrinks > 0 || rs.counters.callCutoffs > 0 || toolClamps > 0 || toolCuts > 0 || toolCutKills > 0 || pendingCutTools > 0 || rs.counters.finalVerifyInjections > 0 || rs.attach.attachmentsInjected > 0 || rs.counters.repetitionCuts > 0 || rs.counters.repetitionSpared > 0) {
|
|
2767
|
+
if (rs.counters.finalVerifyInjections > 0 || rs.attach.attachmentsInjected > 0 || rs.counters.repetitionCuts > 0 || rs.counters.repetitionSpared > 0 || rs.counters.approachNoticesSent > 0) {
|
|
2809
2768
|
stats.mechanisms = {
|
|
2810
|
-
...(rs.counters.finalizeInjected ? { finalizeInjected: true } : {}),
|
|
2811
|
-
...(rs.counters.nudgesSent > 0 ? { nudgesSent: rs.counters.nudgesSent } : {}),
|
|
2812
|
-
...(capShrinks > 0 ? { capShrinks } : {}),
|
|
2813
|
-
...(rs.counters.callCutoffs > 0 ? { callCutoffs: rs.counters.callCutoffs } : {}),
|
|
2814
|
-
...(toolClamps > 0 ? { toolClamps } : {}),
|
|
2815
|
-
...(toolCuts > 0 ? { toolCuts } : {}),
|
|
2816
|
-
...(toolCutKills > 0 ? { toolCutKills } : {}),
|
|
2817
|
-
...(pendingCutTools > 0 ? { pendingCutTools } : {}),
|
|
2818
2769
|
...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
|
|
2819
2770
|
...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjections: rs.counters.finalVerifyInjections } : {}),
|
|
2820
2771
|
...(rs.attach.attachmentsInjected > 0 ? { attachmentsInjected: rs.attach.attachmentsInjected } : {}),
|
|
2772
|
+
...(rs.counters.approachNoticesSent > 0 ? { approachNoticesSent: rs.counters.approachNoticesSent } : {}),
|
|
2821
2773
|
...(rs.counters.repetitionCuts > 0 ? { repetitionCuts: rs.counters.repetitionCuts } : {}),
|
|
2822
2774
|
...(rs.counters.repetitionSpared > 0 ? { repetitionSpared: rs.counters.repetitionSpared } : {}),
|
|
2823
2775
|
...(rs.counters.repetitionEvents.length > 0 ? { repetitionEvents: rs.counters.repetitionEvents } : {}),
|
|
@@ -2829,10 +2781,12 @@ export class Runner {
|
|
|
2829
2781
|
unpricedSpend: rs.telemetry.unpricedSpend,
|
|
2830
2782
|
rewindNotes: prepared.rewindNotes,
|
|
2831
2783
|
remoteEnvFailures: prepared.remoteEnvFailures,
|
|
2784
|
+
retryAfterMs: rs.limits.platformTerminal?.retryAfterMs,
|
|
2832
2785
|
abortedForTimeout: timeout.fired,
|
|
2833
2786
|
abortedForTurns: rs.limits.turnsExceeded,
|
|
2834
2787
|
abortedLive,
|
|
2835
2788
|
budgetHit: rs.limits.budgetHit,
|
|
2789
|
+
budgetAxis: rs.limits.budgetAxis,
|
|
2836
2790
|
blockedReason: prepared.blockedRef.reason,
|
|
2837
2791
|
conflict: prepared.conflictRef.hit,
|
|
2838
2792
|
outputInvalid: rs.degrade.outputInvalid,
|
|
@@ -2891,7 +2845,6 @@ export class Runner {
|
|
|
2891
2845
|
result.status === "failed" &&
|
|
2892
2846
|
final?.stopReason === "error" &&
|
|
2893
2847
|
!isDegenerateCutMessage(final) &&
|
|
2894
|
-
!isWalltimeCutMessage(final) &&
|
|
2895
2848
|
threw === undefined &&
|
|
2896
2849
|
rs.limits.budgetHit === undefined &&
|
|
2897
2850
|
!rs.degrade.outputInvalid &&
|
|
@@ -2959,7 +2912,7 @@ export class Runner {
|
|
|
2959
2912
|
ts: Date.now(),
|
|
2960
2913
|
}));
|
|
2961
2914
|
setResult(result);
|
|
2962
|
-
if (parentToolCallId !== undefined && (result.status === "completed" || result.status === "failed" || result.status === "
|
|
2915
|
+
if (parentToolCallId !== undefined && (result.status === "completed" || result.status === "failed" || result.status === "blocked")) {
|
|
2963
2916
|
const terminalTick = {
|
|
2964
2917
|
type: "task_progress",
|
|
2965
2918
|
taskId: rs.telemetry.taskId,
|
|
@@ -3298,6 +3251,12 @@ export class Runner {
|
|
|
3298
3251
|
if (checkpointVersionOf(cp) > MAX_SUPPORTED_CHECKPOINT_VERSION) {
|
|
3299
3252
|
throw new CheckpointError("checkpoint.unsupported_version", `checkpoint version ${checkpointVersionOf(cp)} is newer than this worker supports (max ${MAX_SUPPORTED_CHECKPOINT_VERSION})`);
|
|
3300
3253
|
}
|
|
3254
|
+
const retiredWalltimeTotal = cp.resourceLedger?.totalWalltimeSec;
|
|
3255
|
+
if (retiredWalltimeTotal !== undefined) {
|
|
3256
|
+
throw new CheckpointError("checkpoint.walltime_axis_retired", "this checkpoint's ledger carries a cross-slice WALL-CLOCK allocation (resourceLedger.totalWalltimeSec), an axis this engine version retired — " +
|
|
3257
|
+
"resuming here would run the leg with that ceiling unenforced. Rejected pre-CAS (the checkpoint stays pending): finish it on a worker of the " +
|
|
3258
|
+
"previous release, or start a fresh task bounded by resourceSuspend.totalTokens / totalBudgetUsd.");
|
|
3259
|
+
}
|
|
3301
3260
|
if (taskConfig.signal?.aborted === true && cp.status === "pending") {
|
|
3302
3261
|
throw new CheckpointError("checkpoint.resume_aborted", "the resume was handed an ALREADY-ABORTED signal — refusing to consume the approval on a leg that cannot run it (the checkpoint stays pending and is resumable with a live signal)");
|
|
3303
3262
|
}
|
|
@@ -3410,7 +3369,7 @@ export class Runner {
|
|
|
3410
3369
|
}
|
|
3411
3370
|
else if (!completed.has(id)) {
|
|
3412
3371
|
const name = names.get(id) ?? "unknown";
|
|
3413
|
-
emit({ type: "tool_end", toolCallId: id, toolName: name, ...((() => { const l = prepared.tools.find((t) => t.name === name)?.label; return l !== undefined && l !== name ? { label: l } : {}; })()), isError: true });
|
|
3372
|
+
emit({ type: "tool_end", toolCallId: id, toolName: name, ...((() => { const l = prepared.tools.find((t) => t.name === name)?.label; return l !== undefined && l !== name ? { label: l } : {}; })()), isError: true, ...toolEndBodyFrom({ content: formatHookFeedback(DEFERRED_REISSUE) }, true) });
|
|
3414
3373
|
const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE), true));
|
|
3415
3374
|
emitCommitted(eid, "toolResult", id);
|
|
3416
3375
|
}
|
|
@@ -3424,10 +3383,10 @@ export class Runner {
|
|
|
3424
3383
|
const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput ?? pendingAction.args);
|
|
3425
3384
|
const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
|
|
3426
3385
|
emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs });
|
|
3427
|
-
const emitEnd = (isError) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError });
|
|
3386
|
+
const emitEnd = (isError, result) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError) });
|
|
3428
3387
|
if (outcome.decision === "deny") {
|
|
3429
|
-
emitEnd(true);
|
|
3430
3388
|
const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason) : `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
|
|
3389
|
+
emitEnd(true, { content: formatHookFeedback(reason) });
|
|
3431
3390
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason), true));
|
|
3432
3391
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
3433
3392
|
return;
|
|
@@ -3435,8 +3394,9 @@ export class Runner {
|
|
|
3435
3394
|
if (outcome.decision === "allow" && outcome.updatedInput !== undefined && prepared.basePolicyForResumeEdit) {
|
|
3436
3395
|
const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal));
|
|
3437
3396
|
if (rechecked.action === "deny") {
|
|
3438
|
-
|
|
3439
|
-
|
|
3397
|
+
const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is denied by the deployment's tool policy and was not executed${rechecked.message ? `: ${rechecked.message}` : ""}.`);
|
|
3398
|
+
emitEnd(true, { content: editedDenial });
|
|
3399
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
|
|
3440
3400
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
3441
3401
|
return;
|
|
3442
3402
|
}
|
|
@@ -3444,8 +3404,9 @@ export class Runner {
|
|
|
3444
3404
|
if (prepared.denyNarrowingPolicy) {
|
|
3445
3405
|
const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal));
|
|
3446
3406
|
if (narrowed.action === "deny") {
|
|
3447
|
-
|
|
3448
|
-
|
|
3407
|
+
const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`);
|
|
3408
|
+
emitEnd(true, { content: narrowedDenial });
|
|
3409
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
|
|
3449
3410
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
3450
3411
|
return;
|
|
3451
3412
|
}
|
|
@@ -3463,13 +3424,14 @@ export class Runner {
|
|
|
3463
3424
|
res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
|
|
3464
3425
|
}
|
|
3465
3426
|
catch (err) {
|
|
3466
|
-
|
|
3467
|
-
|
|
3427
|
+
const execError = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
3428
|
+
emitEnd(true, { content: execError });
|
|
3429
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, execError, true));
|
|
3468
3430
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
3469
3431
|
return;
|
|
3470
3432
|
}
|
|
3471
3433
|
const executedIsError = res.isError === true;
|
|
3472
|
-
emitEnd(executedIsError);
|
|
3434
|
+
emitEnd(executedIsError, res);
|
|
3473
3435
|
onResolvedToolSuccess?.(pendingAction.toolName, executedIsError ? undefined : res.details);
|
|
3474
3436
|
const eid = await prepared.session.appendMessage({
|
|
3475
3437
|
role: "toolResult",
|