@sema-agent/core 5.65.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/agents/subagent.d.ts +2 -2
- package/dist/agents/subagent.js +11 -0
- package/dist/agents/verify.d.ts +1 -1
- package/dist/brain/anthropic.js +1 -1
- package/dist/brain/errors.d.ts +29 -0
- package/dist/brain/errors.js +20 -0
- package/dist/brain/open-responses.js +2 -2
- package/dist/brain/route-adjudicator.d.ts +8 -1
- package/dist/brain/route-adjudicator.js +1 -0
- package/dist/brain/status-sink.js +12 -1
- package/dist/brain/stream-engine.js +17 -6
- package/dist/core/auto-compaction.d.ts +26 -0
- package/dist/core/auto-compaction.js +7 -2
- package/dist/core/auto-mode-arming.d.ts +138 -0
- package/dist/core/auto-mode-arming.js +181 -0
- package/dist/core/auto-mode-defaults.d.ts +13 -0
- package/dist/core/auto-mode-defaults.js +5 -0
- package/dist/core/auto-mode-prompt.d.ts +14 -3
- package/dist/core/auto-mode-prompt.js +10 -7
- package/dist/core/auto-mode-rebuild.d.ts +75 -0
- package/dist/core/auto-mode-rebuild.js +41 -0
- package/dist/core/auto-mode.d.ts +15 -0
- package/dist/core/auto-mode.js +4 -2
- package/dist/core/checkpoint-store.d.ts +18 -0
- package/dist/core/context-edit.d.ts +47 -5
- package/dist/core/context-guard.d.ts +1 -1
- package/dist/core/file-history-retention.d.ts +106 -0
- package/dist/core/file-history-retention.js +36 -0
- package/dist/core/file-history-store.d.ts +768 -0
- package/dist/core/file-history-store.js +880 -0
- package/dist/core/governance-codes.d.ts +2 -1
- package/dist/core/governance-codes.js +14 -0
- package/dist/core/hooks.d.ts +39 -7
- package/dist/core/hooks.js +38 -21
- package/dist/core/lsp.d.ts +2 -2
- package/dist/core/mcp.d.ts +29 -7
- package/dist/core/memory-engine/consolidation-driver.d.ts +11 -0
- package/dist/core/memory-engine/consolidation-driver.js +71 -4
- package/dist/core/memory-engine/consolidation.d.ts +25 -2
- package/dist/core/memory-engine/consolidation.js +4 -1
- package/dist/core/memory-engine/distiller.d.ts +84 -1
- package/dist/core/memory-engine/distiller.js +68 -0
- package/dist/core/memory-engine/dual-root.js +3 -0
- package/dist/core/memory-engine/engine.d.ts +328 -15
- package/dist/core/memory-engine/engine.js +355 -29
- package/dist/core/memory-engine/file-backend.d.ts +30 -0
- package/dist/core/memory-engine/file-backend.js +14 -13
- package/dist/core/memory-engine/frontmatter.d.ts +22 -1
- package/dist/core/memory-engine/frontmatter.js +3 -0
- package/dist/core/memory-engine/header-hints.d.ts +5 -0
- package/dist/core/memory-engine/index.d.ts +5 -4
- package/dist/core/memory-engine/index.js +5 -4
- package/dist/core/memory-engine/layout.d.ts +88 -2
- package/dist/core/memory-engine/layout.js +112 -3
- package/dist/core/memory-engine/provenance-wording.d.ts +7 -0
- package/dist/core/memory-engine/provenance-wording.js +3 -0
- package/dist/core/memory-engine/tools.d.ts +89 -8
- package/dist/core/memory-engine/tools.js +263 -22
- package/dist/core/memory-engine/types.d.ts +64 -1
- package/dist/core/memory-recall.d.ts +6 -0
- package/dist/core/memory.d.ts +27 -1
- package/dist/core/memory.js +16 -2
- package/dist/core/permission-rule-consent.d.ts +20 -0
- package/dist/core/permission-rule-consent.js +12 -3
- package/dist/core/permission-rule-model.d.ts +67 -7
- package/dist/core/permission-rule-model.js +53 -7
- package/dist/core/permission-rule-store.js +15 -10
- package/dist/core/permission-rule-sync.js +15 -11
- package/dist/core/retention-policy.d.ts +9 -0
- package/dist/core/retention-policy.js +5 -2
- package/dist/core/retention.d.ts +13 -2
- package/dist/core/runner/assemble-result.d.ts +19 -1
- package/dist/core/runner/assemble-result.js +17 -2
- package/dist/core/runner/compaction-call-options.d.ts +93 -0
- package/dist/core/runner/compaction-call-options.js +3 -0
- package/dist/core/runner/memory-capture-optout.d.ts +80 -0
- package/dist/core/runner/memory-capture-optout.js +53 -0
- package/dist/core/runner/prepare-config-doors.d.ts +5 -0
- package/dist/core/runner/prepare-config-doors.js +16 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +110 -5
- package/dist/core/runner/prepare-hands-readface.js +99 -7
- package/dist/core/runner/prepare-memory.d.ts +88 -0
- package/dist/core/runner/prepare-memory.js +305 -24
- package/dist/core/runner/prepare-task.d.ts +141 -1
- package/dist/core/runner/prepare-task.js +443 -79
- package/dist/core/runner/runtask.d.ts +9 -20
- package/dist/core/runner/runtask.js +133 -96
- package/dist/core/runner/session-file-state-replay.d.ts +18 -10
- package/dist/core/runner/session-file-state-replay.js +52 -1
- package/dist/core/runner/tool-disclosure.js +2 -1
- package/dist/core/runner/turn-attachments.d.ts +22 -12
- package/dist/core/session-store.d.ts +1 -1
- package/dist/core/session-store.js +6 -1
- package/dist/core/session.d.ts +34 -1
- package/dist/core/store-contracts/file-history-store-contract.d.ts +3 -0
- package/dist/core/store-contracts/file-history-store-contract.js +720 -0
- package/dist/core/task-registry-shared.js +11 -1
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +172 -1
- package/dist/core/tool-policy.js +32 -1
- package/dist/core/tool-result-store.js +2 -1
- package/dist/core/trace.d.ts +24 -0
- package/dist/core/types.d.ts +784 -89
- package/dist/core/types.js +4 -3
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/untrusted-text.js +8 -0
- package/dist/core/workflow-run-store-contract.js +6 -2
- package/dist/core/workflow-run-store.d.ts +4 -1
- package/dist/engine/compaction/compaction.d.ts +88 -10
- package/dist/engine/compaction/compaction.js +109 -30
- package/dist/engine/execution-env/node-execution-env.d.ts +9 -1
- package/dist/engine/execution-env/node-execution-env.js +28 -0
- package/dist/engine/harness/agent-harness.d.ts +52 -1
- package/dist/engine/harness/agent-harness.js +36 -1
- package/dist/engine/harness/types.d.ts +26 -1
- package/dist/engine/llm/types.d.ts +50 -4
- package/dist/engine/loop/agent-loop.d.ts +5 -1
- package/dist/engine/loop/agent-loop.js +25 -0
- package/dist/engine/loop/types.d.ts +19 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
- package/dist/engine/session/session.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +14 -6
- package/dist/orchestration/run-workflow-tool.d.ts +20 -2
- package/dist/orchestration/run-workflow-tool.js +22 -3
- package/dist/orchestration/workflow-governance.d.ts +59 -1
- package/dist/orchestration/workflow-governance.js +61 -8
- package/dist/orchestration/workflow-meta.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +56 -13
- package/dist/orchestration/workflow-types.d.ts +78 -2
- package/dist/orchestration/workflow.d.ts +20 -0
- package/dist/orchestration/workflow.js +163 -14
- package/dist/prompt-assembly/event-registry.js +1 -1
- package/dist/prompts/default.d.ts +7 -7
- package/dist/stores/file/file-history-store.d.ts +368 -0
- package/dist/stores/file/file-history-store.js +1248 -0
- package/dist/stores/file/index.d.ts +22 -13
- package/dist/stores/file/index.js +4 -4
- package/dist/stores/file/permission-rule-store.js +1 -0
- package/dist/stores/file/strategy-store.d.ts +3 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +87 -3
- package/dist/tools/fs/bash-readonly-classifier.js +106 -4
- package/dist/tools/fs/fs-bash.js +9 -5
- package/dist/tools/fs/fs-shared.d.ts +52 -1
- package/dist/tools/fs/fs-shared.js +14 -0
- package/dist/tools/fs/fs-write.d.ts +5 -5
- package/dist/tools/fs/fs-write.js +71 -14
- package/dist/tools/fs/index.d.ts +6 -1
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/web.js +2 -1
- package/package.json +5 -1
- package/test/export-surface.snapshot.json +155 -23
- package/dist/core/file-snapshot-store.d.ts +0 -165
- package/dist/core/file-snapshot-store.js +0 -259
- package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +0 -13
- package/dist/core/store-contracts/file-snapshot-store-contract.js +0 -134
- package/dist/stores/file/file-snapshot-store.d.ts +0 -58
- package/dist/stores/file/file-snapshot-store.js +0 -353
|
@@ -152,16 +152,11 @@ export declare class Runner {
|
|
|
152
152
|
readonly sessions: SessionStore;
|
|
153
153
|
/** Per-sessionId serialization so two tasks never mutate one session concurrently. */
|
|
154
154
|
private sessionLocks;
|
|
155
|
-
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
|
|
160
|
-
* user later SHRINKS re-probes instead of being locked out until restart.
|
|
161
|
-
* RB-430-b: `skipAnnounced` makes the SKIP itself observable — one announcement per cooldown window,
|
|
162
|
-
* so an operator can tell "this turn produced no snapshot" from "snapshots are working", without the
|
|
163
|
-
* per-turn log spam the latch exists to prevent. Cleared with the entry when the window elapses. */
|
|
164
|
-
private snapshotTooLargeRoots;
|
|
155
|
+
/** #483 rung-1 — per-session read-file-state seats (CC parity: readFileState is session-scoped;
|
|
156
|
+
* sema's map stays per-task, so the session scope lives here). A bounded CACHE, never an
|
|
157
|
+
* authority: a durable resume's checkpoint seed wins on its leg, and a miss (cross-process,
|
|
158
|
+
* evicted) falls back to the transcript-replay rung. See {@link SessionReadFileStates}. */
|
|
159
|
+
private readonly sessionReadStates;
|
|
165
160
|
/** task notifications born BETWEEN turns (run torn down / harness already idle), parked
|
|
166
161
|
* per session and drained into the session's next run at its first boundary. Runner-lived (outlives any
|
|
167
162
|
* single run, like the registry handles that produce into it); bounded + drop-disclosing, see
|
|
@@ -274,13 +269,6 @@ export declare class Runner {
|
|
|
274
269
|
* never rejects. Also fills the budget-excluded `stats.suggestions` line.
|
|
275
270
|
*/
|
|
276
271
|
private suggestNextPrompts;
|
|
277
|
-
/**
|
|
278
|
-
* design/101 §E19 — capture a working-tree snapshot of the just-finished turn, keyed by the leaf
|
|
279
|
-
* `SessionTreeEntry.id`. Best-effort + never throws (errors route to `onError(phase:"rewind")`). Runs against
|
|
280
|
-
* ANY ExecutionEnv (local or remote) when a `fileSnapshotStore` is wired (GATE-SPLIT) — the
|
|
281
|
-
* snapshot is portable (env FileSystem ops). A deployment wanting VM-snapshot instead wires a VM-backed store.
|
|
282
|
-
*/
|
|
283
|
-
private snapshotTurn;
|
|
284
272
|
/** Run a task to completion and return a machine-readable result. */
|
|
285
273
|
runTask(spec: TaskSpec, internals?: RunInternals): Promise<TaskResult>;
|
|
286
274
|
/**
|
|
@@ -429,9 +417,10 @@ export declare class Runner {
|
|
|
429
417
|
* reaper is the backstop for an env orphaned by a rare pre-teardown throw (same posture as `mcp`).
|
|
430
418
|
*
|
|
431
419
|
* Runs from the run-loop tail AFTER every env-reader (finish()'s compaction `attachWorkingFiles` +
|
|
432
|
-
*
|
|
433
|
-
* fix for a teardown-order defect: the old in-`finish` destroy ran BEFORE
|
|
434
|
-
* remote/worktree lane
|
|
420
|
+
* the file-history boundary settle), so the working tree is still readable when they run. This ORDER is
|
|
421
|
+
* the fix for a teardown-order defect: the old in-`finish` destroy ran BEFORE the capture seat, so a
|
|
422
|
+
* factory-minted remote/worktree lane captured an already-destroyed env (plain host/static lanes were
|
|
423
|
+
* false-green).
|
|
435
424
|
*
|
|
436
425
|
* design/49 v1.5 / design/76 §2.5: a SUSPENDED (`suspendRef.token`) or `needs_review` (`reviewRef.token`) pause
|
|
437
426
|
* `suspendVM`-paused the env and persisted its `workspaceHandle` into the checkpoint — destroying it would
|
|
@@ -34,9 +34,10 @@ import { toImageContent } from "./image.js";
|
|
|
34
34
|
import { OUTPUT_TOOL_NAME, SKILLS_LISTING_PROBE_HEADER, resolveOutputRetries } from "./synthetic-tools.js";
|
|
35
35
|
import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
|
|
36
36
|
import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
37
|
-
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, advanceCadenceClock, agentListingDeltaHeader, attachmentEnvelopeTags, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpDroppedTools, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, selectMcpDroppedBatch, renderSkillsListingDelta, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
38
|
-
import { buildWorkingFileAttachments, centerAdoptionOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
37
|
+
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, TOTAL_TOKENS_REMINDER_DEFAULT_MODE, AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, advanceCadenceClock, agentListingDeltaHeader, attachmentEnvelopeTags, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpDroppedTools, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, selectMcpDroppedBatch, renderSkillsListingDelta, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
38
|
+
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
39
39
|
import { effectiveDelegationFacts, gatedCallIdOf, placementValueOrAbsent, prepareTask, resolveCheckpointStore } from "./prepare-task.js";
|
|
40
|
+
import { SessionReadFileStates } from "./prepare-hands-readface.js";
|
|
40
41
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
41
42
|
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
|
|
42
43
|
import { hasVerifiableStructureSignal } from "./grounding-signal.js";
|
|
@@ -48,7 +49,7 @@ import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntr
|
|
|
48
49
|
import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
|
|
49
50
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
50
51
|
import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
|
|
51
|
-
import { checkToolPolicyProjection, constraintChainDigest,
|
|
52
|
+
import { checkToolPolicyProjection, constraintChainDigest, constraintChainEntryOfSuppliedLayer, isApprovalSettledBy, refuseOutOfContractDecision, screenApproverAttribution, toolPolicyNameSets } from "../tool-policy.js";
|
|
52
53
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
53
54
|
import { discloseDroppedPending, isDelegatedAgentTerminal, isSystemInjectionPriority, isTerminalTaskNotification, PendingSessionNotifications, renderTaskNotificationXml, SYSTEM_INJECTION_PRIORITIES, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
54
55
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
@@ -57,7 +58,7 @@ import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-
|
|
|
57
58
|
import { structuredFrom, toolOutputFrom } from "./tool-output-projection.js";
|
|
58
59
|
function createRunState() {
|
|
59
60
|
return {
|
|
60
|
-
telemetry: { cacheFamily: "input-excludes-cached", pricing: { inputPer1M: 0, outputPer1M: 0 }, pricingConfigured: true, unpricedSpend: false, tracer: undefined, taskId: "", taskStart: 0, taskStartMonotonic: 0, cacheBreakReported: false },
|
|
61
|
+
telemetry: { cacheFamily: "input-excludes-cached", pricing: { inputPer1M: 0, outputPer1M: 0 }, pricingConfigured: true, unpricedSpend: false, tracer: undefined, taskId: "", runId: "", taskStart: 0, taskStartMonotonic: 0, cacheBreakReported: false },
|
|
61
62
|
degrade: { degradeToModel: undefined, degraded: undefined, outputErrorStreak: 0, outputInvalid: false, recordDegraded: () => { } },
|
|
62
63
|
limits: { turnsExceeded: false, budgetHit: undefined, budgetAxis: undefined, platformTerminal: undefined, outputRetryCap: 0, effectiveMaxTurns: undefined },
|
|
63
64
|
budget: { remainingMicroUsd: undefined, maxCostMicroUsd: undefined, remainingTokens: undefined, maxTokensWindow: undefined, overBudget: () => undefined, streamCancel: false, callOutputChars: 0, lastStreamBudgetCheck: 0, projectedOverBudget: () => undefined },
|
|
@@ -124,8 +125,6 @@ const SUGGESTIONS_DEFAULT_COUNT = 3;
|
|
|
124
125
|
const SUGGESTIONS_MAX_COUNT = 8;
|
|
125
126
|
const SUGGESTIONS_TIMEOUT_MS = 30_000;
|
|
126
127
|
const SUGGESTIONS_TRANSCRIPT_MESSAGES = 12;
|
|
127
|
-
const SNAPSHOT_TIMEOUT_MS = 30_000;
|
|
128
|
-
const SNAPSHOT_TOO_LARGE_TTL_MS = 30 * 60_000;
|
|
129
128
|
const LIMIT_APPROACH_DEFAULT_THRESHOLDS = [0.8, 0.95];
|
|
130
129
|
const DEFAULT_PRECALL_OUTPUT_TOKENS = 4096;
|
|
131
130
|
function limitApproachFrames(index, axis, percent, mark) {
|
|
@@ -607,7 +606,13 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
607
606
|
const mcpRemovals = mcpInstructionsOn && mcpDelta.pendingRemovals.length > 0 ? [...mcpDelta.pendingRemovals] : undefined;
|
|
608
607
|
const mcpDropped = mcpInstructionsOn && prepared.mcp.droppedTools.length > 0 ? selectMcpDroppedBatch(prepared.mcp.droppedTools) : undefined;
|
|
609
608
|
const budgetUsdOn = rs.attach.attachmentsCfg?.budgetUsd === true && rs.budget.maxCostMicroUsd !== undefined && rs.turn.lastTurnHadToolCalls;
|
|
610
|
-
const
|
|
609
|
+
const totalTokensDeclared = rs.attach.attachmentsCfg?.totalTokensReminder;
|
|
610
|
+
const totalTokensMode = rs.attach.attachmentsCfg?.totalTokensReminderMode ?? TOTAL_TOKENS_REMINDER_DEFAULT_MODE;
|
|
611
|
+
const totalTokensArmedByDefault = totalTokensDeclared === undefined &&
|
|
612
|
+
eventDefaultOn("total_tokens_reminder") &&
|
|
613
|
+
totalTokensMode !== "fixed" &&
|
|
614
|
+
totalTokensMode !== "infinite";
|
|
615
|
+
const totalTokensOn = (totalTokensDeclared === true || totalTokensArmedByDefault) && rs.turn.lastTurnHadToolCalls;
|
|
611
616
|
const toolSearchReminderOn = rs.attach.attachmentsCfg?.toolSearchReminder === true && prepared.deferredToolNames !== undefined;
|
|
612
617
|
const undiscoveredTools = toolSearchReminderOn
|
|
613
618
|
? [...prepared.deferredToolNames].filter((n) => !prepared.activeTools.has(n)).sort()
|
|
@@ -897,6 +902,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
897
902
|
overheadTokens: prepared.promptOverheadTokens,
|
|
898
903
|
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
899
904
|
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
905
|
+
...contextInstructionFilesOption(prepared),
|
|
900
906
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
901
907
|
...runnerHooks.seamCCompactionOptions(prepared),
|
|
902
908
|
...gitRestateOption(prepared),
|
|
@@ -1165,6 +1171,8 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1165
1171
|
if (event.entryId !== undefined && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")) {
|
|
1166
1172
|
emitCommitted(event.entryId, m.role, m.role === "toolResult" ? m.toolCallId : undefined);
|
|
1167
1173
|
}
|
|
1174
|
+
if (event.entryId !== undefined && m.role === "user")
|
|
1175
|
+
prepared.fileHistoryBoundary?.begin(event.entryId);
|
|
1168
1176
|
if (event.entryId !== undefined && m.role === "user" && prepared.gitStatusRef.pendingReceipt !== undefined) {
|
|
1169
1177
|
const pr = prepared.gitStatusRef.pendingReceipt;
|
|
1170
1178
|
const c = m.content;
|
|
@@ -1453,7 +1461,7 @@ export class Runner {
|
|
|
1453
1461
|
deps;
|
|
1454
1462
|
sessions;
|
|
1455
1463
|
sessionLocks = new Map();
|
|
1456
|
-
|
|
1464
|
+
sessionReadStates = new SessionReadFileStates();
|
|
1457
1465
|
pendingSessionNotifications = new PendingSessionNotifications();
|
|
1458
1466
|
parentConstraintRegistry = new Map();
|
|
1459
1467
|
suspendedEnvReaps = new Map();
|
|
@@ -1605,7 +1613,7 @@ export class Runner {
|
|
|
1605
1613
|
deliverEngineNotice(this.deps.onNotice, {
|
|
1606
1614
|
code: "config.models_swapped",
|
|
1607
1615
|
message: `model catalog swapped: ${Object.keys(next.models).length} model(s), ${tiers ? Object.keys(tiers).length : 0} tier binding(s); in-flight tasks finish on their resolved models, new tasks resolve against the new catalog`,
|
|
1608
|
-
detail: { models: Object.keys(next.models).length, tiers: tiers ? Object.keys(tiers).length : 0 },
|
|
1616
|
+
detail: { models: Object.keys(next.models).length, tiers: tiers ? Object.keys(tiers).length : 0, occurrenceId: uuidv7() },
|
|
1609
1617
|
});
|
|
1610
1618
|
if (movedEntries.length > 0) {
|
|
1611
1619
|
const RENDER_CAP = 8;
|
|
@@ -1616,7 +1624,7 @@ export class Runner {
|
|
|
1616
1624
|
rendered.map((e) => `"${e.modelId}" ${e.from || "(config root)"} → ${e.to || "(config root)"}`).join("; ") +
|
|
1617
1625
|
(movedEntries.length > rendered.length ? `; +${movedEntries.length - rendered.length} more` : "") +
|
|
1618
1626
|
" — if the provider changed (not just its domain), update the credential reference in the same step",
|
|
1619
|
-
detail: { entries: rendered, total: movedEntries.length },
|
|
1627
|
+
detail: { entries: rendered, total: movedEntries.length, occurrenceId: uuidv7() },
|
|
1620
1628
|
});
|
|
1621
1629
|
}
|
|
1622
1630
|
}
|
|
@@ -1629,6 +1637,7 @@ export class Runner {
|
|
|
1629
1637
|
"(Note the parameter order: runTaskStream(spec, resume?, internals?) — internals is the THIRD parameter.)");
|
|
1630
1638
|
}
|
|
1631
1639
|
const entryActor = spec.actor === undefined ? undefined : snapshotActorAssertion(spec.actor);
|
|
1640
|
+
const entryTracer = { tracer: spec.tracer ?? this.deps.tracer };
|
|
1632
1641
|
const queue = new PushQueue();
|
|
1633
1642
|
const detachHub = new ToolDetachHub();
|
|
1634
1643
|
let resultValue;
|
|
@@ -1650,6 +1659,7 @@ export class Runner {
|
|
|
1650
1659
|
let steerChain = Promise.resolve();
|
|
1651
1660
|
const acceptedSteerInputs = new Map();
|
|
1652
1661
|
const notifyRef = {};
|
|
1662
|
+
const captureOptOutRef = {};
|
|
1653
1663
|
const manualCompactRef = { requested: false, waiters: [] };
|
|
1654
1664
|
const drainManualCompactWaiters = (outcome) => {
|
|
1655
1665
|
manualCompactRef.requested = false;
|
|
@@ -1688,7 +1698,7 @@ export class Runner {
|
|
|
1688
1698
|
}, (s) => {
|
|
1689
1699
|
reapHandle = s;
|
|
1690
1700
|
this.suspendedEnvReaps.set(s.token, s);
|
|
1691
|
-
}, manualCompactRef, taskIdRef, resume, { ...(internals ?? {}), detachHub }, notifyRef, entryActor);
|
|
1701
|
+
}, manualCompactRef, taskIdRef, resume, { ...(internals ?? {}), detachHub }, notifyRef, captureOptOutRef, entryActor, entryTracer);
|
|
1692
1702
|
}
|
|
1693
1703
|
finally {
|
|
1694
1704
|
releaseLock?.();
|
|
@@ -1759,6 +1769,7 @@ export class Runner {
|
|
|
1759
1769
|
resultValue = {
|
|
1760
1770
|
taskId: taskIdRef.current ?? "unknown",
|
|
1761
1771
|
sessionId: taskIdRef.sessionId ?? "unknown",
|
|
1772
|
+
...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}),
|
|
1762
1773
|
status: "failed",
|
|
1763
1774
|
result: "",
|
|
1764
1775
|
errorMessage: err instanceof Error ? err.message : String(err),
|
|
@@ -1774,10 +1785,11 @@ export class Runner {
|
|
|
1774
1785
|
})(),
|
|
1775
1786
|
stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costMicroUsd: 0 },
|
|
1776
1787
|
};
|
|
1777
|
-
emitTrace(
|
|
1788
|
+
emitTrace(entryTracer.tracer, () => ({
|
|
1778
1789
|
kind: "task.end",
|
|
1779
1790
|
version: 1,
|
|
1780
1791
|
taskId: taskIdRef.current ?? "unknown",
|
|
1792
|
+
...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}),
|
|
1781
1793
|
status: "failed",
|
|
1782
1794
|
errorCode: code,
|
|
1783
1795
|
turns: 0,
|
|
@@ -1985,6 +1997,7 @@ export class Runner {
|
|
|
1985
1997
|
detail: {
|
|
1986
1998
|
inputId: effectiveInputId,
|
|
1987
1999
|
sessionId: h.sessionId,
|
|
2000
|
+
runId: h.runId,
|
|
1988
2001
|
...(actor?.id !== undefined ? { actorId: actor.id } : {}),
|
|
1989
2002
|
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
1990
2003
|
},
|
|
@@ -2065,6 +2078,26 @@ export class Runner {
|
|
|
2065
2078
|
}
|
|
2066
2079
|
notifyRef.inject(payload, priority !== undefined ? { priority } : undefined);
|
|
2067
2080
|
},
|
|
2081
|
+
optOutMemoryCapture: async (options) => {
|
|
2082
|
+
const reasonIn = options?.reason;
|
|
2083
|
+
if (reasonIn !== undefined && typeof reasonIn !== "string") {
|
|
2084
|
+
const e = new Error(`cannot opt out of memory capture: options.reason must be a string when present`);
|
|
2085
|
+
e.code = "steering.invalid_content";
|
|
2086
|
+
throw e;
|
|
2087
|
+
}
|
|
2088
|
+
const reason = reasonIn;
|
|
2089
|
+
if (resultValue)
|
|
2090
|
+
throw steeringError("the task has already finished");
|
|
2091
|
+
const h = handle ?? (await orTimeout(ready));
|
|
2092
|
+
if (!h)
|
|
2093
|
+
throw steeringError("the task is not running");
|
|
2094
|
+
if (captureOptOutRef.flip === undefined) {
|
|
2095
|
+
const e = new Error(`cannot opt out of memory capture: this run mounted no memory session (no memory spec / no backend / prepare degraded memory-less) — there is nothing to opt out of`);
|
|
2096
|
+
e.code = "memory.capture_optout_unavailable";
|
|
2097
|
+
throw e;
|
|
2098
|
+
}
|
|
2099
|
+
return captureOptOutRef.flip(reason);
|
|
2100
|
+
},
|
|
2068
2101
|
compact: async (opts) => {
|
|
2069
2102
|
if (resultValue)
|
|
2070
2103
|
throw steeringError("the task has already finished");
|
|
@@ -2118,6 +2151,60 @@ export class Runner {
|
|
|
2118
2151
|
h.abortController.abort();
|
|
2119
2152
|
void h.harness.abort();
|
|
2120
2153
|
},
|
|
2154
|
+
halt: async () => {
|
|
2155
|
+
const haltRefused = (msg) => {
|
|
2156
|
+
const e = new Error(`cannot halt: ${msg}`);
|
|
2157
|
+
e.code = "steering.not_running";
|
|
2158
|
+
return e;
|
|
2159
|
+
};
|
|
2160
|
+
if (resultValue)
|
|
2161
|
+
throw haltRefused("the task has already finished");
|
|
2162
|
+
const h = handle ?? (await orTimeout(ready));
|
|
2163
|
+
if (!h)
|
|
2164
|
+
throw haltRefused("the task is not running");
|
|
2165
|
+
const apply = () => {
|
|
2166
|
+
const abortOwnedBeforeHalt = h.abortController.signal.aborted;
|
|
2167
|
+
const receipt = h.harness.halt();
|
|
2168
|
+
if (receipt.accepted && !abortOwnedBeforeHalt)
|
|
2169
|
+
h.loop.userHalted = true;
|
|
2170
|
+
if (receipt.turnCut) {
|
|
2171
|
+
deliverEngineNotice(this.deps.onNotice, {
|
|
2172
|
+
code: "task.turn_interrupted",
|
|
2173
|
+
message: "a bare user halt cut the running turn: in-flight work was cut at a manufactured boundary " +
|
|
2174
|
+
"(finished tool calls keep their real results; never-started ones settle as interrupted) and " +
|
|
2175
|
+
"the run is collecting to a clean, resumable stop — no further model turn will start.",
|
|
2176
|
+
detail: {
|
|
2177
|
+
cause: "user_halt",
|
|
2178
|
+
sessionId: h.sessionId,
|
|
2179
|
+
runId: h.runId,
|
|
2180
|
+
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
2181
|
+
},
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
return { turnCut: receipt.turnCut };
|
|
2185
|
+
};
|
|
2186
|
+
try {
|
|
2187
|
+
if (resultValue !== undefined || h.loop.ended)
|
|
2188
|
+
throw haltRefused("the task is no longer running");
|
|
2189
|
+
return apply();
|
|
2190
|
+
}
|
|
2191
|
+
catch (e) {
|
|
2192
|
+
if (!(e instanceof Error && e.code === "invalid_state"))
|
|
2193
|
+
throw e;
|
|
2194
|
+
}
|
|
2195
|
+
const birthDeadline = Date.now() + READY_TIMEOUT_MS;
|
|
2196
|
+
while (resultValue === undefined && !h.loop.ended && Date.now() < birthDeadline) {
|
|
2197
|
+
try {
|
|
2198
|
+
return apply();
|
|
2199
|
+
}
|
|
2200
|
+
catch (e2) {
|
|
2201
|
+
if (!(e2 instanceof Error && e2.code === "invalid_state"))
|
|
2202
|
+
throw e2;
|
|
2203
|
+
}
|
|
2204
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
2205
|
+
}
|
|
2206
|
+
throw haltRefused("the task is no longer running");
|
|
2207
|
+
},
|
|
2121
2208
|
destroy: () => (destroyOnce ??= (async () => {
|
|
2122
2209
|
const h = handle ?? (await orTimeout(ready));
|
|
2123
2210
|
if (h) {
|
|
@@ -2129,7 +2216,7 @@ export class Runner {
|
|
|
2129
2216
|
})()),
|
|
2130
2217
|
};
|
|
2131
2218
|
}
|
|
2132
|
-
async runLocked(spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, entryActor) {
|
|
2219
|
+
async runLocked(spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, entryActor, entryTracer) {
|
|
2133
2220
|
const prepareResume = resume
|
|
2134
2221
|
? {
|
|
2135
2222
|
leafId: resume.cp.leafId,
|
|
@@ -2233,6 +2320,7 @@ export class Runner {
|
|
|
2233
2320
|
peerSelfRef,
|
|
2234
2321
|
peerInboundChainRef,
|
|
2235
2322
|
detachHub,
|
|
2323
|
+
sessionReadStates: this.sessionReadStates,
|
|
2236
2324
|
onTaskNotification: (notification, opts) => {
|
|
2237
2325
|
try {
|
|
2238
2326
|
upstreamTaskNotification?.(notification, opts);
|
|
@@ -2241,7 +2329,7 @@ export class Runner {
|
|
|
2241
2329
|
}
|
|
2242
2330
|
injectTaskNotification(notification, opts);
|
|
2243
2331
|
},
|
|
2244
|
-
}, this);
|
|
2332
|
+
}, this, taskIdRef);
|
|
2245
2333
|
notificationHarness = prepared.harness;
|
|
2246
2334
|
notificationSessionId = prepared.sessionId;
|
|
2247
2335
|
if (taskIdRef)
|
|
@@ -2281,7 +2369,7 @@ export class Runner {
|
|
|
2281
2369
|
undrainedUserAtEnd = counts;
|
|
2282
2370
|
return;
|
|
2283
2371
|
}
|
|
2284
|
-
for (const notice of undrainedUserInputNotices(counts, spec.taskId, prepared.sessionId)) {
|
|
2372
|
+
for (const notice of undrainedUserInputNotices(counts, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
|
|
2285
2373
|
deliverEngineNotice(this.deps.onNotice, notice);
|
|
2286
2374
|
}
|
|
2287
2375
|
};
|
|
@@ -2292,6 +2380,9 @@ export class Runner {
|
|
|
2292
2380
|
};
|
|
2293
2381
|
if (notifyRef)
|
|
2294
2382
|
notifyRef.inject = injectTaskNotification;
|
|
2383
|
+
if (captureOptOutRef && prepared.memoryEngineSession?.captureOptOut !== undefined) {
|
|
2384
|
+
captureOptOutRef.flip = prepared.memoryEngineSession.captureOptOut.flip;
|
|
2385
|
+
}
|
|
2295
2386
|
try {
|
|
2296
2387
|
internals?.onNotifyInjectorReady?.(injectTaskNotification);
|
|
2297
2388
|
}
|
|
@@ -2310,8 +2401,8 @@ export class Runner {
|
|
|
2310
2401
|
}
|
|
2311
2402
|
}
|
|
2312
2403
|
}
|
|
2313
|
-
const loopLatch = { ended: false, userInterrupted: false };
|
|
2314
|
-
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity });
|
|
2404
|
+
const loopLatch = { ended: false, userInterrupted: false, userHalted: false };
|
|
2405
|
+
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, runId: prepared.runId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity });
|
|
2315
2406
|
const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
|
|
2316
2407
|
prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
|
|
2317
2408
|
if (resume &&
|
|
@@ -2352,8 +2443,9 @@ export class Runner {
|
|
|
2352
2443
|
rs.telemetry.cacheBreakReported = false;
|
|
2353
2444
|
rs.limits.outputRetryCap = resolveOutputRetries(spec.outputRetries);
|
|
2354
2445
|
rs.limits.effectiveMaxTurns = resolveMaxTurns(spec.limits);
|
|
2355
|
-
rs.telemetry.tracer = spec.tracer ?? this.deps.tracer;
|
|
2446
|
+
rs.telemetry.tracer = entryTracer !== undefined ? entryTracer.tracer : (spec.tracer ?? this.deps.tracer);
|
|
2356
2447
|
rs.telemetry.taskId = runSourceTaskId;
|
|
2448
|
+
rs.telemetry.runId = prepared.runId;
|
|
2357
2449
|
if (taskIdRef) {
|
|
2358
2450
|
taskIdRef.current = rs.telemetry.taskId;
|
|
2359
2451
|
taskIdRef.sessionId = prepared.sessionId;
|
|
@@ -2368,7 +2460,7 @@ export class Runner {
|
|
|
2368
2460
|
}
|
|
2369
2461
|
};
|
|
2370
2462
|
try {
|
|
2371
|
-
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId)).catch(discloseNoteTaskRunFailure);
|
|
2463
|
+
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId, rs.telemetry.runId)).catch(discloseNoteTaskRunFailure);
|
|
2372
2464
|
}
|
|
2373
2465
|
catch (err) {
|
|
2374
2466
|
discloseNoteTaskRunFailure(err);
|
|
@@ -2654,7 +2746,7 @@ export class Runner {
|
|
|
2654
2746
|
const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(name);
|
|
2655
2747
|
const toolStartAt = new Map();
|
|
2656
2748
|
const startedToolCallIds = new Set();
|
|
2657
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "task.start", version: 1, taskId: rs.telemetry.taskId, model: prepared.model.id, engineVersion: engineVersion(), ts: rs.telemetry.taskStart }));
|
|
2749
|
+
emitTrace(rs.telemetry.tracer, () => ({ kind: "task.start", version: 1, taskId: rs.telemetry.taskId, runId: rs.telemetry.runId, model: prepared.model.id, engineVersion: engineVersion(), ts: rs.telemetry.taskStart }));
|
|
2658
2750
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
2659
2751
|
kind: "prompt.assembled",
|
|
2660
2752
|
version: 1,
|
|
@@ -2746,7 +2838,9 @@ export class Runner {
|
|
|
2746
2838
|
}
|
|
2747
2839
|
}
|
|
2748
2840
|
};
|
|
2841
|
+
const ownCommittedTailRef = { current: undefined };
|
|
2749
2842
|
const emitCommitted = (entryId, role, toolCallId) => {
|
|
2843
|
+
ownCommittedTailRef.current = entryId;
|
|
2750
2844
|
if (role === "toolResult" && toolCallId !== undefined)
|
|
2751
2845
|
startedToolCallIds.delete(toolCallId);
|
|
2752
2846
|
queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
|
|
@@ -2762,6 +2856,7 @@ export class Runner {
|
|
|
2762
2856
|
...(s.detail !== undefined ? { detail: s.detail } : {}),
|
|
2763
2857
|
...(s.retryInSec !== undefined ? { retryInSec: s.retryInSec } : {}),
|
|
2764
2858
|
...(s.retryInMs !== undefined ? { retryInMs: s.retryInMs } : {}),
|
|
2859
|
+
...(s.retryAtMs !== undefined ? { retryAtMs: s.retryAtMs } : {}),
|
|
2765
2860
|
...(s.attempt !== undefined ? { attempt: s.attempt } : {}),
|
|
2766
2861
|
...(s.maxRetries !== undefined ? { maxRetries: s.maxRetries } : {}),
|
|
2767
2862
|
...(s.errClass !== undefined ? { errClass: s.errClass } : {}),
|
|
@@ -3079,6 +3174,7 @@ export class Runner {
|
|
|
3079
3174
|
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
3080
3175
|
onInputTruncated: emitInputTruncated(rs.telemetry.tracer, rs.telemetry.taskId),
|
|
3081
3176
|
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
3177
|
+
...contextInstructionFilesOption(prepared),
|
|
3082
3178
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
3083
3179
|
...this.seamCCompactionOptions(prepared),
|
|
3084
3180
|
...gitRestateOption(prepared),
|
|
@@ -3479,6 +3575,7 @@ export class Runner {
|
|
|
3479
3575
|
detail: {
|
|
3480
3576
|
...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
|
|
3481
3577
|
sessionId: prepared.sessionId,
|
|
3578
|
+
runId: prepared.runId,
|
|
3482
3579
|
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
3483
3580
|
},
|
|
3484
3581
|
});
|
|
@@ -3859,7 +3956,7 @@ export class Runner {
|
|
|
3859
3956
|
steer: Math.max(0, undrainedUserAtEnd.steer - migratedParked.steer),
|
|
3860
3957
|
followUp: Math.max(0, undrainedUserAtEnd.followUp - migratedParked.followUp),
|
|
3861
3958
|
};
|
|
3862
|
-
for (const notice of undrainedUserInputNotices(remaining, spec.taskId, prepared.sessionId)) {
|
|
3959
|
+
for (const notice of undrainedUserInputNotices(remaining, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
|
|
3863
3960
|
deliverEngineNotice(this.deps.onNotice, notice);
|
|
3864
3961
|
}
|
|
3865
3962
|
}
|
|
@@ -3867,6 +3964,7 @@ export class Runner {
|
|
|
3867
3964
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
3868
3965
|
}
|
|
3869
3966
|
const comp = await this.finish(spec, prepared, {
|
|
3967
|
+
telemetry: { tracer: rs.telemetry.tracer, taskId: rs.telemetry.taskId },
|
|
3870
3968
|
skipCompaction: durablyPaused || compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES,
|
|
3871
3969
|
minTokens: rs.counters.compactionFloor,
|
|
3872
3970
|
brain: compactionBrain,
|
|
@@ -3951,10 +4049,12 @@ export class Runner {
|
|
|
3951
4049
|
}
|
|
3952
4050
|
const result = assembleResult(spec, prepared.sessionId, final, stats, {
|
|
3953
4051
|
threw,
|
|
4052
|
+
runId: prepared.runId,
|
|
3954
4053
|
model: prepared.model.id,
|
|
3955
4054
|
unpricedSpend: rs.telemetry.unpricedSpend,
|
|
3956
4055
|
rewindNotes: prepared.rewindNotes,
|
|
3957
4056
|
haltedOnUserRejection: prepared.batchHaltRef.current !== undefined,
|
|
4057
|
+
userHalted: loopLatch.userHalted,
|
|
3958
4058
|
strandedHumanAnswers,
|
|
3959
4059
|
remoteEnvFailures: prepared.remoteEnvFailures,
|
|
3960
4060
|
effectiveReadFace: prepared.effectiveReadFace,
|
|
@@ -4008,6 +4108,7 @@ export class Runner {
|
|
|
4008
4108
|
r.errorMessage = `${r.errorMessage ?? "the resume leg did not complete"} — the approved action never started, and reopening the checkpoint FAILED: its state is unprovable from here. Re-read it before deciding; do NOT issue a fresh approval on the assumption the old one is dead`;
|
|
4009
4109
|
}
|
|
4010
4110
|
}
|
|
4111
|
+
prepared.sealReadStateSeat?.(ownCommittedTailRef.current);
|
|
4011
4112
|
{
|
|
4012
4113
|
const wss = prepared.workspaceStateSettle;
|
|
4013
4114
|
if (wss !== undefined && result.status !== "suspended" && result.status !== "needs_review") {
|
|
@@ -4096,6 +4197,7 @@ export class Runner {
|
|
|
4096
4197
|
kind: "task.end",
|
|
4097
4198
|
version: 1,
|
|
4098
4199
|
taskId: rs.telemetry.taskId,
|
|
4200
|
+
runId: rs.telemetry.runId,
|
|
4099
4201
|
status: result.status,
|
|
4100
4202
|
errorCode: result.errorCode,
|
|
4101
4203
|
turns: stats.turns,
|
|
@@ -4139,9 +4241,7 @@ export class Runner {
|
|
|
4139
4241
|
manualCompactRef.emitMooted = undefined;
|
|
4140
4242
|
queue.push({ type: "done", result });
|
|
4141
4243
|
queue.close();
|
|
4142
|
-
|
|
4143
|
-
await this.snapshotTurn(prepared);
|
|
4144
|
-
}
|
|
4244
|
+
await prepared.fileHistoryBoundary?.settle();
|
|
4145
4245
|
if (spec.suggestNextPrompts && result.status === "completed") {
|
|
4146
4246
|
onSuggestions(this.suggestNextPrompts(spec, prepared, result));
|
|
4147
4247
|
}
|
|
@@ -4220,7 +4320,7 @@ export class Runner {
|
|
|
4220
4320
|
if (!sameRouteIdentity(model, prepared.model)) {
|
|
4221
4321
|
const verdict = await adjudicateDerivedRoute({ brain: this.deps.brain, model, getApiKeyAndHeaders: spec.getApiKeyAndHeaders });
|
|
4222
4322
|
if (verdict !== undefined && !verdict.ok) {
|
|
4223
|
-
deliverEngineNotice(this.deps.onNotice, fallbackToPrimaryNotice({ seat: "prompt-suggestions", from: model.id, to: prepared.model.id, verdict, sessionId: prepared.sessionId }));
|
|
4323
|
+
deliverEngineNotice(this.deps.onNotice, fallbackToPrimaryNotice({ seat: "prompt-suggestions", from: model.id, to: prepared.model.id, verdict, sessionId: prepared.sessionId, runId: prepared.runId }));
|
|
4224
4324
|
model = prepared.model;
|
|
4225
4325
|
thinking = prepared.thinking;
|
|
4226
4326
|
}
|
|
@@ -4253,71 +4353,6 @@ export class Runner {
|
|
|
4253
4353
|
clearTimeout(timer);
|
|
4254
4354
|
}
|
|
4255
4355
|
}
|
|
4256
|
-
async snapshotTurn(prepared) {
|
|
4257
|
-
const store = this.deps.fileSnapshotStore;
|
|
4258
|
-
const env = prepared.ownedEnv ?? this.deps.executionEnv;
|
|
4259
|
-
if (!store || !env)
|
|
4260
|
-
return;
|
|
4261
|
-
try {
|
|
4262
|
-
const leafId = await prepared.session.getLeafId();
|
|
4263
|
-
if (!leafId)
|
|
4264
|
-
return;
|
|
4265
|
-
const root = prepared.taskRootPath;
|
|
4266
|
-
const refusal = this.snapshotTooLargeRoots.get(root);
|
|
4267
|
-
if (refusal !== undefined) {
|
|
4268
|
-
if (Date.now() - refusal.refusedAt < SNAPSHOT_TOO_LARGE_TTL_MS) {
|
|
4269
|
-
if (!refusal.skipAnnounced) {
|
|
4270
|
-
refusal.skipAnnounced = true;
|
|
4271
|
-
try {
|
|
4272
|
-
this.deps.onError?.(new Error(`rewind-files snapshot skipped for this working-tree root: a too_large refusal is still inside its ${SNAPSHOT_TOO_LARGE_TTL_MS / 60_000}-minute cooldown (re-probed at ${new Date(refusal.refusedAt + SNAPSHOT_TOO_LARGE_TTL_MS).toISOString()}) — turns completed during the cooldown capture no snapshot and cannot be rewound to. Announced once per cooldown window.`), { phase: "rewind", sessionId: prepared.sessionId, classification: "too_large" });
|
|
4273
|
-
}
|
|
4274
|
-
catch {
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
return;
|
|
4278
|
-
}
|
|
4279
|
-
this.snapshotTooLargeRoots.delete(root);
|
|
4280
|
-
}
|
|
4281
|
-
const ac = new AbortController();
|
|
4282
|
-
let timer;
|
|
4283
|
-
const timeout = new Promise((resolve) => {
|
|
4284
|
-
timer = setTimeout(() => {
|
|
4285
|
-
ac.abort();
|
|
4286
|
-
resolve({ ok: false, error: { code: "aborted", message: "snapshot timed out" } });
|
|
4287
|
-
}, SNAPSHOT_TIMEOUT_MS);
|
|
4288
|
-
});
|
|
4289
|
-
const work = store.snapshot(prepared.sessionId, leafId, env, root, ac.signal);
|
|
4290
|
-
work.catch(() => { });
|
|
4291
|
-
let r;
|
|
4292
|
-
try {
|
|
4293
|
-
r = await Promise.race([work, timeout]);
|
|
4294
|
-
}
|
|
4295
|
-
finally {
|
|
4296
|
-
clearTimeout(timer);
|
|
4297
|
-
}
|
|
4298
|
-
if (!r.ok) {
|
|
4299
|
-
const tooLarge = r.error.code === "too_large";
|
|
4300
|
-
const refusedAt = Date.now();
|
|
4301
|
-
if (tooLarge && !this.snapshotTooLargeRoots.has(root))
|
|
4302
|
-
this.snapshotTooLargeRoots.set(root, { refusedAt, skipAnnounced: false });
|
|
4303
|
-
try {
|
|
4304
|
-
this.deps.onError?.(new Error(`rewind-files snapshot failed (${r.error.code}): ${r.error.message}` +
|
|
4305
|
-
(tooLarge
|
|
4306
|
-
? ` — skipping further snapshots for this root (this process) during a ${SNAPSHOT_TOO_LARGE_TTL_MS / 60_000}-minute cooldown, until ${new Date(refusedAt + SNAPSHOT_TOO_LARGE_TTL_MS).toISOString()}, when the tree is re-probed automatically. Raise the store's snapshotBounds to accept a tree this size; shrinking the tree also re-enables rewind, but only at that re-probe — not immediately`
|
|
4307
|
-
: "")), { phase: "rewind", sessionId: prepared.sessionId, ...(tooLarge ? { classification: "too_large" } : {}) });
|
|
4308
|
-
}
|
|
4309
|
-
catch {
|
|
4310
|
-
}
|
|
4311
|
-
}
|
|
4312
|
-
}
|
|
4313
|
-
catch (err) {
|
|
4314
|
-
try {
|
|
4315
|
-
this.deps.onError?.(err, { phase: "rewind", sessionId: prepared.sessionId });
|
|
4316
|
-
}
|
|
4317
|
-
catch {
|
|
4318
|
-
}
|
|
4319
|
-
}
|
|
4320
|
-
}
|
|
4321
4356
|
async runTask(spec, internals) {
|
|
4322
4357
|
const stream = this.runTaskStream(spec, undefined, internals);
|
|
4323
4358
|
for await (const _ of stream) {
|
|
@@ -4687,12 +4722,13 @@ export class Runner {
|
|
|
4687
4722
|
"(corrupt / inconsistently-minted row) — the frozen deny data the resume would execute is not the data the " +
|
|
4688
4723
|
"digest authenticates; rejected pre-CAS (the checkpoint stays pending)");
|
|
4689
4724
|
}
|
|
4690
|
-
const suppliedDigest = constraintChainDigest((internals?.inheritedGate?.parentConstraints ?? []).map((pc) =>
|
|
4725
|
+
const suppliedDigest = constraintChainDigest((internals?.inheritedGate?.parentConstraints ?? []).map((pc, i) => constraintChainEntryOfSuppliedLayer(pc, persistedChain[i])));
|
|
4691
4726
|
if (suppliedDigest !== expectedDigest) {
|
|
4692
4727
|
throw new CheckpointError("resume.parent_constraint_mismatch", "the re-supplied parent-constraint chain's projection content does not match what the checkpoint recorded " +
|
|
4693
4728
|
`(recorded ${expectedDigest}, re-supplied ${suppliedDigest}) — a same-shape chain with different frozen ` +
|
|
4694
4729
|
"deny data would run the resumed leg under a different ancestor gate than it suspended with; " +
|
|
4695
|
-
"rejected pre-CAS (the checkpoint stays pending) — re-resume with the original chain's policies"
|
|
4730
|
+
"rejected pre-CAS (the checkpoint stays pending) — re-resume with the original chain's policies " +
|
|
4731
|
+
"(including, when the row recorded one, the ancestor's auto-mode arming recipe verbatim)");
|
|
4696
4732
|
}
|
|
4697
4733
|
}
|
|
4698
4734
|
}
|
|
@@ -5370,9 +5406,10 @@ export class Runner {
|
|
|
5370
5406
|
minTokens: opts?.minTokens,
|
|
5371
5407
|
overheadTokens: prepared.promptOverheadTokens,
|
|
5372
5408
|
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
5373
|
-
onInputTruncated: emitInputTruncated(spec.tracer ?? this.deps.tracer, spec.taskId ?? prepared.sessionId),
|
|
5409
|
+
onInputTruncated: emitInputTruncated(opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId),
|
|
5374
5410
|
...(opts?.windowSafety ?? {}),
|
|
5375
5411
|
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
5412
|
+
...contextInstructionFilesOption(prepared),
|
|
5376
5413
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
5377
5414
|
...this.seamCCompactionOptions(prepared),
|
|
5378
5415
|
...gitRestateOption(prepared),
|
|
@@ -5383,17 +5420,17 @@ export class Runner {
|
|
|
5383
5420
|
});
|
|
5384
5421
|
this.recordCompactionReuse(prepared, finishComp);
|
|
5385
5422
|
if (finishComp.unevaluableWindow) {
|
|
5386
|
-
discloseUnevaluableWindow(finishComp.unevaluableWindow, prepared, spec.tracer ?? this.deps.tracer, spec.taskId ?? prepared.sessionId, this.deps.onError);
|
|
5423
|
+
discloseUnevaluableWindow(finishComp.unevaluableWindow, prepared, opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId, this.deps.onError);
|
|
5387
5424
|
}
|
|
5388
5425
|
comp = finishComp;
|
|
5389
5426
|
}
|
|
5390
5427
|
catch (err) {
|
|
5391
5428
|
const failMsg = String(err instanceof Error ? err.message : err);
|
|
5392
5429
|
const failReason = failMsg.length > 512 ? `${failMsg.slice(0, 512)}…` : failMsg;
|
|
5393
|
-
emitTrace(spec.tracer ?? this.deps.tracer, () => ({
|
|
5430
|
+
emitTrace(opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, () => ({
|
|
5394
5431
|
kind: "compaction.failed",
|
|
5395
5432
|
version: 1,
|
|
5396
|
-
taskId: spec.taskId ?? prepared.sessionId,
|
|
5433
|
+
taskId: opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId,
|
|
5397
5434
|
trigger: "auto",
|
|
5398
5435
|
reason: failReason,
|
|
5399
5436
|
ts: Date.now(),
|
|
@@ -18,11 +18,17 @@
|
|
|
18
18
|
* a `git checkout`, a bash command — anything this transcript does not record) therefore fails
|
|
19
19
|
* the staleness comparison and the edit is refused, exactly as it is inside a single task. A
|
|
20
20
|
* reconstruction that re-read the file to build its hash would instead wave the dirty edit
|
|
21
|
-
* through; that is the one thing this module must never do.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* through; that is the one thing this module must never do. (#483 D1, ruled 2026-08-28: this is
|
|
22
|
+
* deliberately STRICTER than CC's own rehydrate, whose Edit arm re-reads the disk and would
|
|
23
|
+
* admit a between-turns external change — same gate satisfaction, no staleness give-up.)
|
|
24
|
+
* 2. **Only records whose full post-condition is PROVABLE qualify.** A whole-file `Read` and a
|
|
25
|
+
* `Write` carry the text verbatim. An `Edit` card (#483 rung-2) carries a diff BASE — the
|
|
26
|
+
* pre-edit whole text plus the resolved mutations — so its post-condition is RECOMPUTED through
|
|
27
|
+
* the same single-source application step the tool itself used (`applyRecordedEdit`); a card
|
|
28
|
+
* that cannot recompute (malformed, offloaded, or its oldString absent from its own base) falls
|
|
29
|
+
* back to RETRACTING the file's record, never to a guess. A partial read or a notebook
|
|
30
|
+
* projection proves nothing and is simply not reported: the gate then behaves exactly as before
|
|
31
|
+
* (the model re-reads), which is the pre-existing cost, not a new failure.
|
|
26
32
|
*/
|
|
27
33
|
import type { AgentMessage } from "../../internal/harness.js";
|
|
28
34
|
/** The whole text of one file at the moment the transcript recorded it. */
|
|
@@ -39,11 +45,13 @@ export interface TranscriptFileRecord {
|
|
|
39
45
|
*
|
|
40
46
|
* Records are read only from SUCCESSFUL results (an `isError` result changed nothing and proves
|
|
41
47
|
* nothing). A `Write` proves the post-condition it just persisted; a whole-file `Read` proves what it
|
|
42
|
-
* just served. An `Edit`
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
48
|
+
* just served. An `Edit` card (#483 rung-2) proves the post-condition RECOMPUTED from its recorded
|
|
49
|
+
* diff base + mutations — or, when it cannot prove (malformed / offloaded / inconsistent), retracts
|
|
50
|
+
* whatever an earlier record said (leaving a stale entry behind would blame the user for the agent's
|
|
51
|
+
* own edit, "modified since read", the next turn). A `NotebookEdit` always retracts (deliberate
|
|
52
|
+
* residual — see {@link RETRACTING_RESULTS}). Anything else (partial reads, dedup stubs, notebook
|
|
53
|
+
* projections, image/document reads, foreign tools) is inert: it neither proves nor retracts,
|
|
54
|
+
* because reading a file does not change it.
|
|
47
55
|
*
|
|
48
56
|
* The parameter is the session's own message lane (`AgentMessage`, custom lanes included) rather than
|
|
49
57
|
* the narrowed model-facing union: this reads the branch as the session stores it, and every lane that
|