@sema-agent/core 7.11.0 → 7.11.2
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 +39 -1
- package/dist/core/auto-mode-defaults.d.ts +13 -9
- package/dist/core/auto-mode-defaults.js +1 -1
- package/dist/core/governance-codes.d.ts +1 -0
- package/dist/core/governance-codes.js +1 -0
- package/dist/core/runner/compaction-knobs.d.ts +45 -0
- package/dist/core/runner/compaction-knobs.js +3 -0
- package/dist/core/runner/contracts.d.ts +78 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +23 -8
- package/dist/core/runner/run-attachment-seats.d.ts +20 -0
- package/dist/core/runner/run-attachment-seats.js +187 -0
- package/dist/core/runner/run-brain-sinks.d.ts +29 -0
- package/dist/core/runner/run-brain-sinks.js +61 -0
- package/dist/core/runner/run-clock-and-content.d.ts +52 -0
- package/dist/core/runner/run-clock-and-content.js +26 -0
- package/dist/core/runner/run-compaction-machinery.d.ts +35 -0
- package/dist/core/runner/run-compaction-machinery.js +98 -0
- package/dist/core/runner/run-git-lane.d.ts +64 -0
- package/dist/core/runner/run-git-lane.js +102 -0
- package/dist/core/runner/run-identity-wiring.d.ts +96 -0
- package/dist/core/runner/run-identity-wiring.js +84 -0
- package/dist/core/runner/run-reasoning-seat.d.ts +27 -0
- package/dist/core/runner/run-reasoning-seat.js +48 -0
- package/dist/core/runner/run-recovery-lanes.d.ts +54 -0
- package/dist/core/runner/run-recovery-lanes.js +180 -0
- package/dist/core/runner/run-stop-and-final-verify.d.ts +32 -0
- package/dist/core/runner/run-stop-and-final-verify.js +159 -0
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +38 -0
- package/dist/core/runner/run-telemetry-and-budget-seats.js +159 -0
- package/dist/core/runner/run-tool-mount-facts.d.ts +26 -0
- package/dist/core/runner/run-tool-mount-facts.js +58 -0
- package/dist/core/runner/run-turn-boundary.d.ts +0 -35
- package/dist/core/runner/run-turn-boundary.js +1 -3
- package/dist/core/runner/runtask.js +126 -1624
- package/dist/core/runner/stream-halt-verbs.d.ts +38 -0
- package/dist/core/runner/stream-halt-verbs.js +82 -0
- package/dist/core/runner/stream-lifecycle-verbs.d.ts +34 -0
- package/dist/core/runner/stream-lifecycle-verbs.js +126 -0
- package/dist/core/runner/stream-reap.d.ts +30 -0
- package/dist/core/runner/stream-reap.js +40 -0
- package/dist/core/runner/stream-settle-backstop.d.ts +38 -0
- package/dist/core/runner/stream-settle-backstop.js +113 -0
- package/dist/core/runner/stream-steer-verb.d.ts +30 -0
- package/dist/core/runner/stream-steer-verb.js +185 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +5 -0
- package/dist/tools/fs/bash-readonly-classifier.js +1 -0
- package/dist/tools/fs/fs-bash.js +6 -4
- package/package.json +1 -1
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
|
|
4
|
-
import { planRejectionClears, resolveTriggerWindow } from "../context-edit.js";
|
|
1
|
+
import { mintSystemReminder } from "../reminder-mint.js";
|
|
2
|
+
import { deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
|
|
5
3
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
|
|
6
4
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
7
|
-
import { CheckpointError, remainingBudgetMicroUsd, readPendingSteerQueue,
|
|
8
|
-
import { GIT_STATUS_ECHO_PREVIEW
|
|
5
|
+
import { CheckpointError, remainingBudgetMicroUsd, readPendingSteerQueue, resolveCheckpointStore, realApprovalOrgFact } from "../checkpoint-store.js";
|
|
6
|
+
import { GIT_STATUS_ECHO_PREVIEW } from "./git-status-frame.js";
|
|
9
7
|
import { settleExecutionRecord } from "./execution-record.js";
|
|
10
|
-
import { engineVersion } from "../version.js";
|
|
11
|
-
import { CONFIG_CATALOG_VERSION, declarationReasons, resolveEffectiveConfig } from "../../config/catalog.js";
|
|
12
8
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
13
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact
|
|
9
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact } from "../auto-compaction.js";
|
|
14
10
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
|
|
15
11
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
16
|
-
import { computeCostMicroUsd,
|
|
12
|
+
import { computeCostMicroUsd, modelCostToPricing } from "../pricing.js";
|
|
17
13
|
import { emitTrace } from "../trace.js";
|
|
18
14
|
import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
19
15
|
import { emitTaskOutcome } from "../task-outcome.js";
|
|
20
16
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
21
17
|
import { redactThenCut } from "../../agents/subagent-steps.js";
|
|
22
|
-
import { resolveReasoning } from "../../brain/reasoning.js";
|
|
23
18
|
import { adjudicateDerivedRoute, authCarrierFingerprint, fallbackToPrimaryNotice, normalizeBaseUrl, sameRouteIdentity } from "../../brain/route-adjudicator.js";
|
|
24
|
-
import {
|
|
25
|
-
import { expandTiers,
|
|
19
|
+
import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
|
|
20
|
+
import { expandTiers, resolveTaskModel } from "../roles.js";
|
|
26
21
|
import { screenSwappableDeps } from "../swappable-deps.js";
|
|
27
22
|
import { AutoModeBreakerLedger } from "../auto-mode.js";
|
|
28
23
|
import { runSideQuery } from "../side-query.js";
|
|
@@ -30,11 +25,9 @@ import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
|
30
25
|
import { PushQueue } from "../push-queue.js";
|
|
31
26
|
import { TtlSessionStore } from "../session-store.js";
|
|
32
27
|
import { toImageContent } from "./image.js";
|
|
33
|
-
import { SKILLS_LISTING_PROBE_HEADER, resolveOutputRetries } from "./synthetic-tools.js";
|
|
34
|
-
import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
|
|
35
28
|
import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
36
29
|
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
37
|
-
import {
|
|
30
|
+
import { attachmentEnvelopeTags, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingDelta, renderOrphanedBackgroundTasks, renderSkillsListingDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
38
31
|
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
39
32
|
import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
|
|
40
33
|
import { drainForwardedFramesBeforeDone, forwardsSubagentEvents } from "./prepare-run-refs.js";
|
|
@@ -46,7 +39,7 @@ import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
|
46
39
|
import { hasDestroy, isIsolated } from "../remote-env.js";
|
|
47
40
|
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
48
41
|
import { formatHookFeedback, hookSeatExpiredError, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
|
|
49
|
-
import { buildHumanInputEvent,
|
|
42
|
+
import { buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
|
|
50
43
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
51
44
|
import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
|
|
52
45
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
@@ -54,16 +47,17 @@ import { PAUSE_REGISTRY } from "../pause-registry.js";
|
|
|
54
47
|
import { refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.js";
|
|
55
48
|
import { mintGateOutcome } from "./gate-exit.js";
|
|
56
49
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
57
|
-
import {
|
|
50
|
+
import { isDelegatedAgentTerminal, isTerminalTaskNotification, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
58
51
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
59
52
|
import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
|
|
60
53
|
import { createRunState } from "./initial-run-state.js";
|
|
61
|
-
import { nextHumanInputSeq
|
|
62
|
-
import { reconciledToolEndBody, toolEndBodyFrom, toolResultMsg
|
|
54
|
+
import { nextHumanInputSeq } from "./steer-admission.js";
|
|
55
|
+
import { reconciledToolEndBody, toolEndBodyFrom, toolResultMsg } from "./tool-end-body.js";
|
|
63
56
|
import { answerFaceForRedeemedCall, deepJsonEqual, DEFERRED_REISSUE, pendingContentAskCallId, resumeContinuation, resumeDecisionWasNegative } from "./decide-continuation.js";
|
|
64
|
-
import { awaitChargeWithSlowDisclosure, DEFAULT_PRECALL_OUTPUT_TOKENS, discloseUnevaluableWindow, platformLimitTerminal,
|
|
57
|
+
import { awaitChargeWithSlowDisclosure, DEFAULT_PRECALL_OUTPUT_TOKENS, discloseUnevaluableWindow, platformLimitTerminal, TIMER_LATENESS_REPORT_MS } from "./clock-and-limits.js";
|
|
65
58
|
import { gitRestateOption, resolveGitLegDelivery, wrapGitFrame } from "./git-leg-delivery.js";
|
|
66
|
-
import {
|
|
59
|
+
import { createTurnBoundary } from "./run-turn-boundary.js";
|
|
60
|
+
import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
67
61
|
import { createHarnessHandlers } from "./run-harness-handlers.js";
|
|
68
62
|
import { resumeAdmission } from "./resume-admission.js";
|
|
69
63
|
import { resumeReviewOutcome } from "./resume-review-outcome.js";
|
|
@@ -73,7 +67,22 @@ import { resumeInternalsAndConfig } from "./resume-internals-and-config.js";
|
|
|
73
67
|
import { resumeApply } from "./resume-apply.js";
|
|
74
68
|
import { resumePreflight } from "./resume-preflight.js";
|
|
75
69
|
import { resumeClaim } from "./resume-claim.js";
|
|
76
|
-
|
|
70
|
+
import { streamSettleBackstop } from "./stream-settle-backstop.js";
|
|
71
|
+
import { streamReap } from "./stream-reap.js";
|
|
72
|
+
import { streamSteerVerb } from "./stream-steer-verb.js";
|
|
73
|
+
import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
|
|
74
|
+
import { streamHaltVerbs } from "./stream-halt-verbs.js";
|
|
75
|
+
import { runIdentityWiring } from "./run-identity-wiring.js";
|
|
76
|
+
import { runTelemetryAndBudgetSeats } from "./run-telemetry-and-budget-seats.js";
|
|
77
|
+
import { runAttachmentSeats } from "./run-attachment-seats.js";
|
|
78
|
+
import { runToolMountFacts } from "./run-tool-mount-facts.js";
|
|
79
|
+
import { runReasoningSeat } from "./run-reasoning-seat.js";
|
|
80
|
+
import { runClockAndContent } from "./run-clock-and-content.js";
|
|
81
|
+
import { runBrainSinks } from "./run-brain-sinks.js";
|
|
82
|
+
import { runCompactionMachinery } from "./run-compaction-machinery.js";
|
|
83
|
+
import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
|
|
84
|
+
import { runRecoveryLanes } from "./run-recovery-lanes.js";
|
|
85
|
+
import { runGitLane } from "./run-git-lane.js";
|
|
77
86
|
const ORG_DISCLOSURE_MAX_CHARS = 600;
|
|
78
87
|
const SUGGESTIONS_DEFAULT_COUNT = 3;
|
|
79
88
|
const SUGGESTIONS_MAX_COUNT = 8;
|
|
@@ -308,7 +317,6 @@ export class Runner {
|
|
|
308
317
|
return Promise.race([p, timeout]).finally(() => clearTimeout(t));
|
|
309
318
|
};
|
|
310
319
|
let reapHandle;
|
|
311
|
-
let steerChain = Promise.resolve();
|
|
312
320
|
const acceptedSteerInputs = new Map();
|
|
313
321
|
const notifyRef = {};
|
|
314
322
|
const captureOptOutRef = {};
|
|
@@ -325,33 +333,48 @@ export class Runner {
|
|
|
325
333
|
? await this.acquireSessionLock(spec.sessionId)
|
|
326
334
|
: undefined;
|
|
327
335
|
try {
|
|
328
|
-
await this.runLocked(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const oldest = this.parentConstraintRegistry.keys().next().value;
|
|
341
|
-
if (oldest !== undefined)
|
|
342
|
-
this.parentConstraintRegistry.delete(oldest);
|
|
336
|
+
await this.runLocked({
|
|
337
|
+
spec,
|
|
338
|
+
queue,
|
|
339
|
+
setResult: (r) => {
|
|
340
|
+
resultValue = r;
|
|
341
|
+
const pcs = internals?.inheritedGate?.parentConstraints;
|
|
342
|
+
if (resume !== undefined)
|
|
343
|
+
this.locallyClaimedTokens.delete(resume.cp.token);
|
|
344
|
+
const rToken = r.terminal.kind === "paused" ? r.terminal.token : undefined;
|
|
345
|
+
if (resume !== undefined && rToken !== resume.cp.token) {
|
|
346
|
+
this.parentConstraintRegistry.delete(resume.cp.token);
|
|
347
|
+
this.suspendedEnvReaps.delete(resume.cp.token);
|
|
343
348
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
349
|
+
if (r.terminal.kind === "paused" && PAUSE_REGISTRY[r.terminal.gate.kind].taskStatus === "suspended" && pcs !== undefined && pcs.length > 0) {
|
|
350
|
+
if (this.parentConstraintRegistry.size >= Runner.PARENT_CONSTRAINT_REGISTRY_CAP) {
|
|
351
|
+
const oldest = this.parentConstraintRegistry.keys().next().value;
|
|
352
|
+
if (oldest !== undefined)
|
|
353
|
+
this.parentConstraintRegistry.delete(oldest);
|
|
354
|
+
}
|
|
355
|
+
this.parentConstraintRegistry.set(r.terminal.token, pcs);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
onSuggestions: (p) => {
|
|
359
|
+
suggestionsDone = p;
|
|
360
|
+
},
|
|
361
|
+
onReady: (h) => {
|
|
362
|
+
handle = h;
|
|
363
|
+
publishReady(h);
|
|
364
|
+
},
|
|
365
|
+
onSuspend: (s) => {
|
|
366
|
+
reapHandle = s;
|
|
367
|
+
this.suspendedEnvReaps.set(s.token, s);
|
|
368
|
+
},
|
|
369
|
+
manualCompactRef,
|
|
370
|
+
taskIdRef,
|
|
371
|
+
resume,
|
|
372
|
+
internals: { ...(internals ?? {}), detachHub },
|
|
373
|
+
notifyRef,
|
|
374
|
+
captureOptOutRef,
|
|
375
|
+
entryActor,
|
|
376
|
+
entryTracer,
|
|
377
|
+
});
|
|
355
378
|
}
|
|
356
379
|
finally {
|
|
357
380
|
releaseLock?.();
|
|
@@ -363,149 +386,29 @@ export class Runner {
|
|
|
363
386
|
drainManualCompactWaiters("mooted");
|
|
364
387
|
}
|
|
365
388
|
})();
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}
|
|
377
|
-
catch (reopenErr) {
|
|
378
|
-
this.parentConstraintRegistry.delete(resume.cp.token);
|
|
379
|
-
const original = err instanceof Error ? err : new Error(String(err));
|
|
380
|
-
const reopenMsg = reopenErr instanceof Error ? reopenErr.message : String(reopenErr);
|
|
381
|
-
const definitive = errorCodeOf(reopenErr) === "checkpoint.reopen_failed";
|
|
382
|
-
reopenStateUnknown = !definitive;
|
|
383
|
-
err = Object.assign(new Error(definitive
|
|
384
|
-
? `${reopenMsg} (original resume failure: ${original.message})`
|
|
385
|
-
: `checkpoint reopen threw mid-flight — state UNKNOWN, confirm via the checkpoint store before retrying: ${reopenMsg} (original resume failure: ${original.message})`, { cause: original }), { code: "checkpoint.reopen_failed" });
|
|
386
|
-
code = "checkpoint.reopen_failed";
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (resume)
|
|
390
|
-
this.locallyClaimedTokens.delete(resume.cp.token);
|
|
391
|
-
if (resume && !reopenCommitted) {
|
|
392
|
-
this.parentConstraintRegistry.delete(resume.cp.token);
|
|
393
|
-
await settleTeardownLeg(() => this.sessions.unpin?.(resume.cp.sessionId), "sessions.unpin (resume-failure leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: resume.cp.sessionId }));
|
|
394
|
-
if (!reopenStateUnknown) {
|
|
395
|
-
const failedReap = this.suspendedEnvReaps.get(resume.cp.token);
|
|
396
|
-
this.suspendedEnvReaps.delete(resume.cp.token);
|
|
397
|
-
if (failedReap?.env !== undefined && hasDestroy(failedReap.env)) {
|
|
398
|
-
const failedEnv = failedReap.env;
|
|
399
|
-
await settleTeardownLeg(() => failedEnv.destroy(), "terminal resume-failure env destroy", (e) => this.deps.onError?.(e, { phase: "config", sessionId: resume.cp.sessionId }));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (resultValue !== undefined) {
|
|
404
|
-
try {
|
|
405
|
-
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId: resultValue.sessionId });
|
|
406
|
-
}
|
|
407
|
-
catch {
|
|
408
|
-
}
|
|
409
|
-
queue.close();
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
const remoteEnvFailure = (() => {
|
|
413
|
-
let cur = err;
|
|
414
|
-
for (let depth = 0; cur && typeof cur === "object" && depth < 8; depth++) {
|
|
415
|
-
const note = cur.remoteEnvFailure;
|
|
416
|
-
if (note !== undefined)
|
|
417
|
-
return [note];
|
|
418
|
-
cur = cur.cause;
|
|
419
|
-
}
|
|
420
|
-
return undefined;
|
|
421
|
-
})();
|
|
422
|
-
resultValue = {
|
|
423
|
-
taskId: taskIdRef.current ?? "unknown",
|
|
424
|
-
sessionId: taskIdRef.sessionId ?? "unknown",
|
|
425
|
-
...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}),
|
|
426
|
-
terminal: { kind: "failed", ...(code !== undefined ? { code } : {}), message: err instanceof Error ? err.message : String(err) },
|
|
427
|
-
result: "",
|
|
428
|
-
...(remoteEnvFailure !== undefined ? { remoteEnvFailures: remoteEnvFailure } : {}),
|
|
429
|
-
...(taskIdRef.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: taskIdRef.effectiveMemoryScopes } : {}),
|
|
430
|
-
...(taskIdRef.effectiveReasoning !== undefined ? { effectiveReasoning: taskIdRef.effectiveReasoning } : {}),
|
|
431
|
-
...(() => {
|
|
432
|
-
const observed = taskIdRef.editedFiles?.();
|
|
433
|
-
return observed !== undefined && observed.length > 0 ? { editedFiles: observed } : {};
|
|
434
|
-
})(),
|
|
435
|
-
...(() => {
|
|
436
|
-
const hinted = err.retryAfterMs;
|
|
437
|
-
return code === "memory.admission_required" && typeof hinted === "number" && Number.isFinite(hinted) && hinted > 0
|
|
438
|
-
? { retryAfterMs: hinted }
|
|
439
|
-
: {};
|
|
440
|
-
})(),
|
|
441
|
-
stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costMicroUsd: 0 },
|
|
442
|
-
};
|
|
443
|
-
emitTrace(entryTracer.tracer, () => ({
|
|
444
|
-
kind: "task.end",
|
|
445
|
-
version: 1,
|
|
446
|
-
taskId: taskIdRef.current ?? "unknown",
|
|
447
|
-
...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}),
|
|
448
|
-
status: "failed",
|
|
449
|
-
errorCode: code,
|
|
450
|
-
turns: 0,
|
|
451
|
-
tokens: 0,
|
|
452
|
-
durationMs: Date.now() - runStartedAt,
|
|
453
|
-
ts: Date.now(),
|
|
454
|
-
}));
|
|
455
|
-
const owedTerminal = taskIdRef.delegationTerminalOwed;
|
|
456
|
-
if (owedTerminal !== undefined) {
|
|
457
|
-
taskIdRef.delegationTerminalOwed = undefined;
|
|
458
|
-
deliverDelegationLifecycle(this.deps.onDelegationLifecycle, { phase: "terminal", identity: owedTerminal, status: "failed", turns: 0, ...(code !== undefined ? { errorCode: code } : {}) }, createSafeNotifier({
|
|
459
|
-
onError: (f) => console.warn(`[sema-core] ${f.site}: delegation-lifecycle observer threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
460
|
-
}), "runtask.onDelegationLifecycle");
|
|
461
|
-
}
|
|
462
|
-
if (forwardsSubagentEvents(spec))
|
|
463
|
-
await drainForwardedFramesBeforeDone();
|
|
464
|
-
queue.push({ type: "done", result: resultValue });
|
|
465
|
-
queue.close();
|
|
466
|
-
});
|
|
389
|
+
const live = {
|
|
390
|
+
get resultValue() { return resultValue; },
|
|
391
|
+
set resultValue(v) { resultValue = v; },
|
|
392
|
+
get handle() { return handle; },
|
|
393
|
+
get reapHandle() { return reapHandle; },
|
|
394
|
+
};
|
|
395
|
+
const settled = run.catch(streamSettleBackstop({
|
|
396
|
+
spec, resume, queue, taskIdRef, entryTracer, runStartedAt, live, runner: this.depsSeat, sessions: this.sessions,
|
|
397
|
+
locallyClaimedTokens: this.locallyClaimedTokens, parentConstraintRegistry: this.parentConstraintRegistry, suspendedEnvReaps: this.suspendedEnvReaps,
|
|
398
|
+
}).onRunRejected);
|
|
467
399
|
void settled.then(() => acceptedSteerInputs.clear(), () => acceptedSteerInputs.clear());
|
|
468
400
|
const steeringError = (msg, code = "steering.not_running") => {
|
|
469
401
|
const e = new Error(`cannot steer: ${msg}`);
|
|
470
402
|
e.code = code;
|
|
471
403
|
return e;
|
|
472
404
|
};
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
this.deps.onError?.(err, { phase: "config", sessionId: rh.sessionId });
|
|
481
|
-
}
|
|
482
|
-
catch {
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
let won;
|
|
486
|
-
try {
|
|
487
|
-
won = await rh.store.expire(rh.token, rh.scope);
|
|
488
|
-
}
|
|
489
|
-
catch (err) {
|
|
490
|
-
reportErr(err);
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
if (!won) {
|
|
494
|
-
if (!this.locallyClaimedTokens.has(rh.token))
|
|
495
|
-
this.suspendedEnvReaps.delete(rh.token);
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
this.parentConstraintRegistry.delete(rh.token);
|
|
499
|
-
this.suspendedEnvReaps.delete(rh.token);
|
|
500
|
-
if (rh.env && hasDestroy(rh.env)) {
|
|
501
|
-
try {
|
|
502
|
-
await rh.env.destroy();
|
|
503
|
-
}
|
|
504
|
-
catch (err) {
|
|
505
|
-
reportErr(err);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
};
|
|
405
|
+
const { reapSuspended } = streamReap({
|
|
406
|
+
live, runner: this.depsSeat,
|
|
407
|
+
locallyClaimedTokens: this.locallyClaimedTokens, parentConstraintRegistry: this.parentConstraintRegistry, suspendedEnvReaps: this.suspendedEnvReaps,
|
|
408
|
+
});
|
|
409
|
+
const { steer } = streamSteerVerb({ spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner: this.depsSeat });
|
|
410
|
+
const { notify, optOutMemoryCapture, compact, detach, interrupt } = streamLifecycleVerbs({ live, ready, orTimeout, steeringError, notifyRef, captureOptOutRef, manualCompactRef, detachHub });
|
|
411
|
+
const { halt, destroy } = streamHaltVerbs({ spec, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, run, reapSuspended, runner: this.depsSeat });
|
|
509
412
|
return {
|
|
510
413
|
[Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
|
|
511
414
|
result: async () => {
|
|
@@ -516,377 +419,18 @@ export class Runner {
|
|
|
516
419
|
await run.catch(() => { });
|
|
517
420
|
return suggestionsDone.catch(() => []);
|
|
518
421
|
},
|
|
519
|
-
steer
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
throw steeringError("inputId must be a string when supplied", "steering.invalid_content");
|
|
528
|
-
}
|
|
529
|
-
if (inputId === "" || inputId.length > MAX_STEER_INPUT_ID_CHARS) {
|
|
530
|
-
throw steeringError(`inputId must be a non-empty string of at most ${MAX_STEER_INPUT_ID_CHARS} characters`, "steering.invalid_content");
|
|
531
|
-
}
|
|
532
|
-
if (inputId === LEGACY_PENDING_STEER_INPUT_ID) {
|
|
533
|
-
throw steeringError(`inputId "${LEGACY_PENDING_STEER_INPUT_ID}" is reserved for a pre-queue parked steer and cannot be supplied by a caller`, "steering.invalid_content");
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
const priorityIn = options?.priority;
|
|
537
|
-
if (priorityIn !== undefined && !isSystemInjectionPriority(priorityIn)) {
|
|
538
|
-
throw steeringError(`priority must be one of ${SYSTEM_INJECTION_PRIORITIES.join("/")} when supplied`, "steering.invalid_content");
|
|
539
|
-
}
|
|
540
|
-
const priority = priorityIn ?? "next";
|
|
541
|
-
const actorIn = options?.actor;
|
|
542
|
-
const actor = actorIn === undefined ? undefined : snapshotActorAssertion(actorIn);
|
|
543
|
-
const projected = projectHumanInput({ text, actor, source: "steer" });
|
|
544
|
-
const effectiveInputId = typeof inputId === "string" ? inputId : uuidv7();
|
|
545
|
-
const parkRecord = {
|
|
546
|
-
text,
|
|
547
|
-
trusted,
|
|
548
|
-
inputId: effectiveInputId,
|
|
549
|
-
...(priorityIn !== undefined ? { priority } : {}),
|
|
550
|
-
...(actor !== undefined ? { actor } : {}),
|
|
551
|
-
};
|
|
552
|
-
let payload;
|
|
553
|
-
let mintsAFrame;
|
|
554
|
-
let replay;
|
|
555
|
-
const noteAccepted = (h) => {
|
|
556
|
-
if (!mintsAFrame)
|
|
557
|
-
return;
|
|
558
|
-
if (typeof inputId === "string")
|
|
559
|
-
acceptedSteerInputs.set(inputId, replay);
|
|
560
|
-
queue.push({
|
|
561
|
-
...buildHumanInputEvent({
|
|
562
|
-
carrier: "steer",
|
|
563
|
-
source: "steer",
|
|
564
|
-
delivery: "queued",
|
|
565
|
-
sessionSeq: nextHumanInputSeq(h.harness),
|
|
566
|
-
inputId: effectiveInputId,
|
|
567
|
-
...(actor !== undefined ? { actor } : {}),
|
|
568
|
-
...(actor?.issuer !== undefined ? { issuer: actor.issuer } : {}),
|
|
569
|
-
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
570
|
-
}),
|
|
571
|
-
eventId: uuidv7(),
|
|
572
|
-
...(internals?.parentToolCallId !== undefined
|
|
573
|
-
? { parentToolCallId: internals.parentToolCallId, ...(spec.taskId !== undefined ? { sourceTaskId: spec.taskId } : {}) }
|
|
574
|
-
: {}),
|
|
575
|
-
});
|
|
576
|
-
};
|
|
577
|
-
const deliver = async () => {
|
|
578
|
-
if (resultValue)
|
|
579
|
-
throw steeringError("the task has already finished");
|
|
580
|
-
const h = handle ?? (await orTimeout(ready));
|
|
581
|
-
if (!h)
|
|
582
|
-
throw steeringError("the task is not running");
|
|
583
|
-
payload = trusted ? formatHookFeedback(projected, h.reminderMark) : frameMidTurnUserInput(projected);
|
|
584
|
-
mintsAFrame = payload.trim().length !== 0;
|
|
585
|
-
replay = { payload, trusted, priority, ...(actor !== undefined ? { actor } : {}) };
|
|
586
|
-
if (typeof inputId === "string") {
|
|
587
|
-
const prior = acceptedSteerInputs.get(inputId);
|
|
588
|
-
if (prior !== undefined) {
|
|
589
|
-
if (resultValue !== undefined || h.loop.ended)
|
|
590
|
-
throw steeringError("the task is no longer running");
|
|
591
|
-
if (!sameAcceptedSteerInput(prior, replay)) {
|
|
592
|
-
throw steeringError("a different steering instruction was already accepted under this inputId — re-issue this one with a fresh inputId " +
|
|
593
|
-
"(an identical payload would have been an idempotent retry)", "steering.duplicate_input_id");
|
|
594
|
-
}
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
if (resultValue !== undefined || h.loop.ended)
|
|
599
|
-
throw steeringError("the task is no longer running");
|
|
600
|
-
if (mintsAFrame) {
|
|
601
|
-
const screen = (spec.hooks ?? this.deps.hooks)?.userPromptSubmit;
|
|
602
|
-
if (screen !== undefined) {
|
|
603
|
-
let decision;
|
|
604
|
-
try {
|
|
605
|
-
const seat = await runHookSeat("userPromptSubmit", { timeoutMs: h.hookTimeoutMs, signal: h.abortController.signal, abortEnds: true }, (sig) => screen(text, { identity: h.hookIdentity, signal: sig, source: "steer", inputId: effectiveInputId, ...(actor !== undefined ? { actor: snapshotActorAssertion(actor) } : {}) }));
|
|
606
|
-
if (seat.expired) {
|
|
607
|
-
if (seat.cause === "timeout") {
|
|
608
|
-
try {
|
|
609
|
-
this.deps.onError?.(hookSeatExpiredError("userPromptSubmit", h.hookTimeoutMs, seat.cause, "the steering input was NOT accepted (fail-closed) and the caller was refused typed"), { phase: "hook", sessionId: h.sessionId });
|
|
610
|
-
}
|
|
611
|
-
catch {
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
throw steeringError(seat.cause === "timeout"
|
|
615
|
-
? `the deployment's userPromptSubmit hook did not answer within its ${h.hookTimeoutMs}ms bound while screening this steering input; the input was NOT accepted (fail-closed)`
|
|
616
|
-
: `the task was cancelled while the deployment's userPromptSubmit hook was still screening this steering input; the input was NOT accepted (fail-closed)`, "steering.blocked_by_hook");
|
|
617
|
-
}
|
|
618
|
-
decision = seat.value;
|
|
619
|
-
}
|
|
620
|
-
catch (hookErr) {
|
|
621
|
-
if (hookErr instanceof Error && hookErr.code === "steering.blocked_by_hook")
|
|
622
|
-
throw hookErr;
|
|
623
|
-
const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
|
|
624
|
-
try {
|
|
625
|
-
this.deps.onError?.(err, { phase: "hook", sessionId: h.sessionId });
|
|
626
|
-
}
|
|
627
|
-
catch {
|
|
628
|
-
}
|
|
629
|
-
throw steeringError(`the deployment's userPromptSubmit hook crashed while screening this steering input (${inlineUntrusted(err.message)}); the input was NOT accepted (fail-closed)`, "steering.blocked_by_hook");
|
|
630
|
-
}
|
|
631
|
-
if (decision?.block !== undefined && decision.block !== "") {
|
|
632
|
-
throw steeringError(`the deployment's userPromptSubmit hook blocked this steering input: ${inlineUntrusted(decision.block)}`, "steering.blocked_by_hook");
|
|
633
|
-
}
|
|
634
|
-
if (decision?.additionalContext !== undefined && decision.additionalContext !== "") {
|
|
635
|
-
payload = `${formatHookFeedback(decision.additionalContext, h.reminderMark)}\n\n${payload}`;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
const injectFramed = async () => {
|
|
640
|
-
const noteOptions = { provenance: "engine-note", callerAuthored: true, parkRecord, ...(actor !== undefined ? { actor } : {}) };
|
|
641
|
-
if (priority === "later") {
|
|
642
|
-
await h.harness.followUp(payload, noteOptions);
|
|
643
|
-
noteAccepted(h);
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
const frame = await h.harness.steer(payload, { ...noteOptions, ...(priority === "now" ? { immediate: true } : {}) });
|
|
647
|
-
noteAccepted(h);
|
|
648
|
-
if (priority === "now" && frame !== undefined && h.harness.interruptTurn(frame)) {
|
|
649
|
-
deliverEngineNotice(this.deps.onNotice, {
|
|
650
|
-
code: "task.turn_interrupted",
|
|
651
|
-
message: "a caller-provenance steer with priority \"now\" interrupted the running turn: in-flight work was cut at " +
|
|
652
|
-
"a manufactured boundary (finished tool calls keep their real results; never-started ones settle as " +
|
|
653
|
-
"interrupted) and the run continues with the steer at the queue head.",
|
|
654
|
-
detail: {
|
|
655
|
-
inputId: effectiveInputId,
|
|
656
|
-
sessionId: h.sessionId,
|
|
657
|
-
runId: h.runId,
|
|
658
|
-
...(actor?.id !== undefined ? { actorId: actor.id } : {}),
|
|
659
|
-
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
660
|
-
},
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
try {
|
|
665
|
-
await injectFramed();
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
catch (e) {
|
|
669
|
-
if (!(e instanceof Error && e.code === "invalid_state"))
|
|
670
|
-
throw e;
|
|
671
|
-
}
|
|
672
|
-
const birthDeadline = Date.now() + READY_TIMEOUT_MS;
|
|
673
|
-
while (resultValue === undefined && !h.loop.ended && Date.now() < birthDeadline) {
|
|
674
|
-
try {
|
|
675
|
-
await injectFramed();
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
catch (e2) {
|
|
679
|
-
if (!(e2 instanceof Error && e2.code === "invalid_state"))
|
|
680
|
-
throw e2;
|
|
681
|
-
}
|
|
682
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
683
|
-
}
|
|
684
|
-
throw steeringError("the task is no longer running");
|
|
685
|
-
};
|
|
686
|
-
const p = steerChain.then(deliver);
|
|
687
|
-
steerChain = p.then(() => undefined, () => undefined);
|
|
688
|
-
return p;
|
|
689
|
-
},
|
|
690
|
-
notify: async (input, opts) => {
|
|
691
|
-
const notifyError = (msg, code) => {
|
|
692
|
-
const e = new Error(`cannot notify: ${msg}`);
|
|
693
|
-
e.code = code;
|
|
694
|
-
return e;
|
|
695
|
-
};
|
|
696
|
-
if (typeof input?.task_id !== "string" || input.task_id.trim() === "") {
|
|
697
|
-
throw notifyError("input.task_id must be a non-empty string", "notify.invalid_payload");
|
|
698
|
-
}
|
|
699
|
-
const VALID_STATUSES = new Set(["completed", "failed", "killed", "cancelled", "event"]);
|
|
700
|
-
if (!VALID_STATUSES.has(input.status)) {
|
|
701
|
-
throw notifyError(`input.status must be one of ${[...VALID_STATUSES].join("/")}`, "notify.invalid_payload");
|
|
702
|
-
}
|
|
703
|
-
if (typeof input.summary !== "string" || input.summary.trim() === "") {
|
|
704
|
-
throw notifyError("input.summary must be a non-empty string", "notify.invalid_payload");
|
|
705
|
-
}
|
|
706
|
-
if (input.result !== undefined && typeof input.result !== "string") {
|
|
707
|
-
throw notifyError("input.result must be a string when present", "notify.invalid_payload");
|
|
708
|
-
}
|
|
709
|
-
if (input.seq !== undefined && (typeof input.seq !== "number" || !Number.isFinite(input.seq))) {
|
|
710
|
-
throw notifyError("input.seq must be a finite number when present", "notify.invalid_payload");
|
|
711
|
-
}
|
|
712
|
-
if (input.source !== undefined && typeof input.source !== "string") {
|
|
713
|
-
throw notifyError("input.source must be a string when present", "notify.invalid_payload");
|
|
714
|
-
}
|
|
715
|
-
const priorityIn = opts?.priority;
|
|
716
|
-
if (priorityIn !== undefined && !isSystemInjectionPriority(priorityIn)) {
|
|
717
|
-
throw notifyError(`opts.priority must be one of ${SYSTEM_INJECTION_PRIORITIES.join("/")} when present`, "notify.invalid_payload");
|
|
718
|
-
}
|
|
719
|
-
const priority = priorityIn;
|
|
720
|
-
if (priority === "now") {
|
|
721
|
-
throw notifyError('priority "now" (turn interrupt) is not a notification-lane power — it belongs to the steer face; use "next" for earliest-boundary delivery', "notify.invalid_priority");
|
|
722
|
-
}
|
|
723
|
-
const payload = {
|
|
724
|
-
task_id: input.task_id,
|
|
725
|
-
task_type: "external",
|
|
726
|
-
status: input.status,
|
|
727
|
-
summary: input.summary,
|
|
728
|
-
...(input.result !== undefined ? { result: input.result } : {}),
|
|
729
|
-
...(input.seq !== undefined ? { seq: input.seq } : {}),
|
|
730
|
-
...(input.source !== undefined ? { source: input.source } : {}),
|
|
731
|
-
};
|
|
732
|
-
const h = handle ?? (await orTimeout(ready));
|
|
733
|
-
if (!h || notifyRef.inject === undefined) {
|
|
734
|
-
throw notifyError("the task is not running", "notify.not_running");
|
|
735
|
-
}
|
|
736
|
-
notifyRef.inject(payload, priority !== undefined ? { priority } : undefined);
|
|
737
|
-
},
|
|
738
|
-
optOutMemoryCapture: async (options) => {
|
|
739
|
-
const reasonIn = options?.reason;
|
|
740
|
-
if (reasonIn !== undefined && typeof reasonIn !== "string") {
|
|
741
|
-
const e = new Error(`cannot opt out of memory capture: options.reason must be a string when present`);
|
|
742
|
-
e.code = "steering.invalid_content";
|
|
743
|
-
throw e;
|
|
744
|
-
}
|
|
745
|
-
const reason = reasonIn;
|
|
746
|
-
if (resultValue)
|
|
747
|
-
throw steeringError("the task has already finished");
|
|
748
|
-
const h = handle ?? (await orTimeout(ready));
|
|
749
|
-
if (!h)
|
|
750
|
-
throw steeringError("the task is not running");
|
|
751
|
-
if (captureOptOutRef.flip === undefined) {
|
|
752
|
-
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`);
|
|
753
|
-
e.code = "memory.capture_optout_unavailable";
|
|
754
|
-
throw e;
|
|
755
|
-
}
|
|
756
|
-
return captureOptOutRef.flip(reason);
|
|
757
|
-
},
|
|
758
|
-
compact: async (opts) => {
|
|
759
|
-
if (resultValue)
|
|
760
|
-
throw steeringError("the task has already finished");
|
|
761
|
-
if (opts?.signal?.aborted)
|
|
762
|
-
return "mooted";
|
|
763
|
-
const h = handle ?? (await orTimeout(ready));
|
|
764
|
-
if (!h)
|
|
765
|
-
throw steeringError("the task is not running");
|
|
766
|
-
if (opts?.signal?.aborted)
|
|
767
|
-
return "mooted";
|
|
768
|
-
if (manualCompactRef.closed)
|
|
769
|
-
return "mooted";
|
|
770
|
-
return new Promise((resolve) => {
|
|
771
|
-
const signal = opts?.signal;
|
|
772
|
-
const entry = {
|
|
773
|
-
resolve,
|
|
774
|
-
...(signal !== undefined ? { signal } : {}),
|
|
775
|
-
...(opts?.instructions !== undefined ? { instructions: opts.instructions } : {}),
|
|
776
|
-
};
|
|
777
|
-
if (signal !== undefined) {
|
|
778
|
-
const onAbort = () => {
|
|
779
|
-
const i = manualCompactRef.waiters.indexOf(entry);
|
|
780
|
-
if (i >= 0) {
|
|
781
|
-
manualCompactRef.waiters.splice(i, 1);
|
|
782
|
-
if (manualCompactRef.waiters.length === 0) {
|
|
783
|
-
manualCompactRef.requested = false;
|
|
784
|
-
}
|
|
785
|
-
manualCompactRef.emitMooted?.("cancelled");
|
|
786
|
-
entry.resolve("mooted");
|
|
787
|
-
}
|
|
788
|
-
};
|
|
789
|
-
entry.resolve = (outcome) => {
|
|
790
|
-
signal.removeEventListener("abort", onAbort);
|
|
791
|
-
resolve(outcome);
|
|
792
|
-
};
|
|
793
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
794
|
-
}
|
|
795
|
-
manualCompactRef.requested = true;
|
|
796
|
-
manualCompactRef.waiters.push(entry);
|
|
797
|
-
});
|
|
798
|
-
},
|
|
799
|
-
detach: (toolCallId) => {
|
|
800
|
-
detachHub.request(toolCallId);
|
|
801
|
-
},
|
|
802
|
-
interrupt: async () => {
|
|
803
|
-
const h = handle ?? (await orTimeout(ready));
|
|
804
|
-
if (!h)
|
|
805
|
-
return;
|
|
806
|
-
if (!h.abortController.signal.aborted)
|
|
807
|
-
h.loop.userInterrupted = true;
|
|
808
|
-
h.abortController.abort();
|
|
809
|
-
void h.harness.abort();
|
|
810
|
-
},
|
|
811
|
-
halt: async () => {
|
|
812
|
-
const haltRefused = (msg) => {
|
|
813
|
-
const e = new Error(`cannot halt: ${msg}`);
|
|
814
|
-
e.code = "steering.not_running";
|
|
815
|
-
return e;
|
|
816
|
-
};
|
|
817
|
-
if (resultValue)
|
|
818
|
-
throw haltRefused("the task has already finished");
|
|
819
|
-
const h = handle ?? (await orTimeout(ready));
|
|
820
|
-
if (!h)
|
|
821
|
-
throw haltRefused("the task is not running");
|
|
822
|
-
const apply = () => {
|
|
823
|
-
const abortOwnedBeforeHalt = h.abortController.signal.aborted;
|
|
824
|
-
const receipt = h.harness.halt();
|
|
825
|
-
if (receipt.accepted && !abortOwnedBeforeHalt) {
|
|
826
|
-
h.loop.userHalted = true;
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
deliverEngineNotice(this.deps.onNotice, {
|
|
830
|
-
code: "task.halt_unconsumed",
|
|
831
|
-
message: "a user halt arrived while the run was already ending for its own reason: nothing was cut or stopped " +
|
|
832
|
-
"by it — the run's own ending stands, and the result will not carry haltedByUser for this halt.",
|
|
833
|
-
detail: {
|
|
834
|
-
sessionId: h.sessionId,
|
|
835
|
-
runId: h.runId,
|
|
836
|
-
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
837
|
-
},
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
if (receipt.turnCut) {
|
|
841
|
-
deliverEngineNotice(this.deps.onNotice, {
|
|
842
|
-
code: "task.turn_interrupted",
|
|
843
|
-
message: "a bare user halt cut the running turn: in-flight work was cut at a manufactured boundary " +
|
|
844
|
-
"(finished tool calls keep their real results; never-started ones settle as interrupted) and " +
|
|
845
|
-
"the run is collecting to a clean, resumable stop — no further model turn will start.",
|
|
846
|
-
detail: {
|
|
847
|
-
cause: "user_halt",
|
|
848
|
-
sessionId: h.sessionId,
|
|
849
|
-
runId: h.runId,
|
|
850
|
-
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
851
|
-
},
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
return { turnCut: receipt.turnCut };
|
|
855
|
-
};
|
|
856
|
-
try {
|
|
857
|
-
if (resultValue !== undefined || h.loop.ended)
|
|
858
|
-
throw haltRefused("the task is no longer running");
|
|
859
|
-
return apply();
|
|
860
|
-
}
|
|
861
|
-
catch (e) {
|
|
862
|
-
if (!(e instanceof Error && e.code === "invalid_state"))
|
|
863
|
-
throw e;
|
|
864
|
-
}
|
|
865
|
-
const birthDeadline = Date.now() + READY_TIMEOUT_MS;
|
|
866
|
-
while (resultValue === undefined && !h.loop.ended && Date.now() < birthDeadline) {
|
|
867
|
-
try {
|
|
868
|
-
return apply();
|
|
869
|
-
}
|
|
870
|
-
catch (e2) {
|
|
871
|
-
if (!(e2 instanceof Error && e2.code === "invalid_state"))
|
|
872
|
-
throw e2;
|
|
873
|
-
}
|
|
874
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
875
|
-
}
|
|
876
|
-
throw haltRefused("the task is no longer running");
|
|
877
|
-
},
|
|
878
|
-
destroy: () => (destroyOnce ??= (async () => {
|
|
879
|
-
const h = handle ?? (await orTimeout(ready));
|
|
880
|
-
if (h) {
|
|
881
|
-
h.abortController.abort();
|
|
882
|
-
void h.harness.abort();
|
|
883
|
-
}
|
|
884
|
-
await orTimeout(run.catch(() => { }));
|
|
885
|
-
await reapSuspended();
|
|
886
|
-
})()),
|
|
422
|
+
steer,
|
|
423
|
+
notify,
|
|
424
|
+
optOutMemoryCapture,
|
|
425
|
+
compact,
|
|
426
|
+
detach,
|
|
427
|
+
interrupt,
|
|
428
|
+
halt,
|
|
429
|
+
destroy,
|
|
887
430
|
};
|
|
888
431
|
}
|
|
889
|
-
async runLocked(
|
|
432
|
+
async runLocked(input) {
|
|
433
|
+
const { spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, entryActor, entryTracer } = input;
|
|
890
434
|
const prepareResume = resume
|
|
891
435
|
? {
|
|
892
436
|
leafId: resume.cp.leafId,
|
|
@@ -1001,577 +545,26 @@ export class Runner {
|
|
|
1001
545
|
injectTaskNotification(notification, opts);
|
|
1002
546
|
},
|
|
1003
547
|
}, this, taskIdRef);
|
|
1004
|
-
notificationHarness = prepared.harness;
|
|
1005
|
-
notificationSessionId = prepared.sessionId;
|
|
1006
|
-
if (taskIdRef)
|
|
1007
|
-
taskIdRef.effectiveMemoryScopes = prepared.effectiveMemoryScopes;
|
|
1008
|
-
if (taskIdRef)
|
|
1009
|
-
taskIdRef.editedFiles = prepared.editedFilesSnapshot;
|
|
1010
|
-
const runSourceTaskId = spec.taskId ?? prepared.sessionId;
|
|
1011
|
-
const parentToolCallId = internals?.parentToolCallId;
|
|
1012
|
-
const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: runSourceTaskId } : { eventId: uuidv7() };
|
|
1013
|
-
notificationIdent = ident;
|
|
1014
|
-
queue.push({ type: "wiring_manifest", manifest: prepared.wiringManifest, ...ident() });
|
|
1015
|
-
prepared.toolRosterDeltas.subscribe((delta) => queue.push({ type: "tool_roster_delta", delta, ...ident() }));
|
|
1016
|
-
const delegationLifecycleNotifier = createSafeNotifier({
|
|
1017
|
-
onError: (f) => console.warn(`[sema-core] ${f.site}: delegation-lifecycle observer threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
1018
|
-
});
|
|
1019
|
-
const emitDelegationLifecycle = (event) => {
|
|
1020
|
-
if (!prepared.hookIdentity.isDelegatedChild)
|
|
1021
|
-
return;
|
|
1022
|
-
if (this.deps.onDelegationLifecycle === undefined)
|
|
1023
|
-
return;
|
|
1024
|
-
deliverDelegationLifecycle(this.deps.onDelegationLifecycle, event, delegationLifecycleNotifier, "runtask.onDelegationLifecycle");
|
|
1025
|
-
};
|
|
1026
|
-
emitDelegationLifecycle({ phase: "spawn", identity: prepared.hookIdentity });
|
|
1027
|
-
if (taskIdRef !== undefined && prepared.hookIdentity.isDelegatedChild && this.deps.onDelegationLifecycle !== undefined) {
|
|
1028
|
-
taskIdRef.delegationTerminalOwed = prepared.hookIdentity;
|
|
1029
|
-
}
|
|
1030
|
-
manualCompactRef.emitMooted = (reason) => {
|
|
1031
|
-
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "manual", reason, ...ident() });
|
|
1032
|
-
};
|
|
1033
|
-
prepared.harness.onUndrainedEngineNotes = (payloads) => {
|
|
1034
|
-
if (notificationSessionId === undefined)
|
|
1035
|
-
return;
|
|
1036
|
-
for (const p of payloads)
|
|
1037
|
-
this.pendingSessionNotifications.pend(notificationSessionId, p);
|
|
1038
|
-
};
|
|
1039
|
-
prepared.harness.engineInjectionsHeld = () => prepared.batchHaltRef.current !== undefined;
|
|
1040
548
|
let undrainedUserAtEnd;
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1046
|
-
for (const notice of undrainedUserInputNotices(counts, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
|
|
1047
|
-
deliverEngineNotice(this.deps.onNotice, notice);
|
|
1048
|
-
}
|
|
1049
|
-
};
|
|
1050
|
-
prepared.harness.onEngineNoteConsumed = (p) => {
|
|
1051
|
-
const peer = p?.peer;
|
|
1052
|
-
if (peer !== undefined && Array.isArray(peer.hopChain))
|
|
1053
|
-
peerInboundChainRef.current = [...peer.hopChain];
|
|
1054
|
-
};
|
|
1055
|
-
if (notifyRef)
|
|
1056
|
-
notifyRef.inject = injectTaskNotification;
|
|
1057
|
-
if (captureOptOutRef && prepared.memoryEngineSession?.captureOptOut !== undefined) {
|
|
1058
|
-
captureOptOutRef.flip = prepared.memoryEngineSession.captureOptOut.flip;
|
|
1059
|
-
}
|
|
1060
|
-
try {
|
|
1061
|
-
internals?.onNotifyInjectorReady?.(injectTaskNotification);
|
|
1062
|
-
}
|
|
1063
|
-
catch {
|
|
1064
|
-
}
|
|
1065
|
-
{
|
|
1066
|
-
const pendingIdle = this.pendingSessionNotifications.drain(prepared.sessionId);
|
|
1067
|
-
if (pendingIdle !== undefined) {
|
|
1068
|
-
for (const payload of discloseDroppedPending(pendingIdle)) {
|
|
1069
|
-
const parkedPriority = pendingIdle.priorities?.get(payload);
|
|
1070
|
-
deliveredAtTurnOpen.add(taskNotificationDedupKey(payload));
|
|
1071
|
-
queue.push({ type: "task_notification", notification: payload, ...(parkedPriority !== undefined ? { priority: parkedPriority } : {}), ...ident() });
|
|
1072
|
-
void prepared.harness.nextTurn(renderTaskNotificationXml(payload), { provenance: "engine-note", enginePayload: payload }).catch(() => {
|
|
1073
|
-
this.pendingSessionNotifications.pend(prepared.sessionId, payload, parkedPriority);
|
|
1074
|
-
});
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
const loopLatch = { ended: false, userInterrupted: false, userHalted: false };
|
|
1079
|
-
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, runId: prepared.runId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity });
|
|
1080
|
-
const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
|
|
1081
|
-
prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
|
|
1082
|
-
if (resume &&
|
|
1083
|
-
resume.cp.suspendedAt !== undefined &&
|
|
1084
|
-
(resume.cp.gate.kind === "human" ||
|
|
1085
|
-
resume.cp.gate.kind === "irreversible_ask" ||
|
|
1086
|
-
resume.cp.gate.kind === "needs_review" ||
|
|
1087
|
-
resume.cp.gate.kind === "plan_review")) {
|
|
1088
|
-
const waitMs = Math.max(0, prepared.now() - resume.cp.suspendedAt);
|
|
1089
|
-
const decision = resume.outcome.gate === "wake" ? undefined : resume.outcome.decision;
|
|
1090
|
-
prepared.humanReviewRef.count += 1;
|
|
1091
|
-
prepared.humanReviewRef.totalWaitMs += waitMs;
|
|
1092
|
-
const gateToolName = "toolName" in resume.cp.gate ? resume.cp.gate.toolName : undefined;
|
|
1093
|
-
prepared.humanReviewRef.gates.push({ kind: resume.cp.gate.kind, waitMs, ...(decision !== undefined ? { decision } : {}), ...(gateToolName !== undefined ? { toolName: gateToolName } : {}) });
|
|
1094
|
-
}
|
|
1095
|
-
if (resume !== undefined && resume.outcome.gate === "plan_review" && resume.outcome.decision === "reject") {
|
|
1096
|
-
prepared.planModeRef.active = true;
|
|
1097
|
-
}
|
|
1098
|
-
const rs = createRunState();
|
|
1099
|
-
rs.telemetry.cacheFamily = cacheFamilyOf(prepared.model);
|
|
1100
|
-
rs.telemetry.pricing = this.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
|
|
1101
|
-
rs.telemetry.pricingConfigured = isModelPriced(prepared.model, this.deps.pricing);
|
|
1102
|
-
if (spec.limits?.degrade) {
|
|
1103
|
-
try {
|
|
1104
|
-
rs.degrade.degradeToModel = resolveModel(spec.limits.degrade.to, modelCatalog);
|
|
1105
|
-
}
|
|
1106
|
-
catch (e) {
|
|
1107
|
-
this.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId });
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
const famOverride = prepared.model.params?.promptCacheFamily;
|
|
1111
|
-
if (famOverride && !["input-includes-cached", "input-excludes-cached", "openai", "anthropic"].includes(famOverride)) {
|
|
1112
|
-
this.deps.onError?.(new Error(`prompt-cache: unrecognized model.params.promptCacheFamily "${famOverride}" — ignored (fell back to model.api inference). Use "input-includes-cached" | "input-excludes-cached" (aliases "openai" | "anthropic").`), { phase: "prompt-cache", sessionId: prepared.sessionId });
|
|
1113
|
-
}
|
|
1114
|
-
rs.limits.turnsExceeded = false;
|
|
1115
|
-
rs.degrade.outputErrorStreak = 0;
|
|
1116
|
-
rs.degrade.outputInvalid = false;
|
|
1117
|
-
rs.telemetry.cacheBreakReported = false;
|
|
1118
|
-
rs.limits.outputRetryCap = resolveOutputRetries(spec.outputRetries);
|
|
1119
|
-
rs.limits.effectiveMaxTurns = resolveMaxTurns(spec.limits);
|
|
1120
|
-
rs.telemetry.tracer = entryTracer !== undefined ? entryTracer.tracer : (spec.tracer ?? this.deps.tracer);
|
|
1121
|
-
rs.telemetry.taskId = runSourceTaskId;
|
|
1122
|
-
rs.telemetry.runId = prepared.runId;
|
|
1123
|
-
if (taskIdRef) {
|
|
1124
|
-
taskIdRef.current = rs.telemetry.taskId;
|
|
1125
|
-
taskIdRef.sessionId = prepared.sessionId;
|
|
1126
|
-
}
|
|
1127
|
-
rs.telemetry.taskStart = Date.now();
|
|
1128
|
-
rs.telemetry.taskStartMonotonic = performance.now();
|
|
1129
|
-
const discloseNoteTaskRunFailure = (err) => {
|
|
1130
|
-
try {
|
|
1131
|
-
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId: prepared.sessionId });
|
|
1132
|
-
}
|
|
1133
|
-
catch {
|
|
1134
|
-
}
|
|
1135
|
-
};
|
|
1136
|
-
try {
|
|
1137
|
-
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId, rs.telemetry.runId)).catch(discloseNoteTaskRunFailure);
|
|
1138
|
-
}
|
|
1139
|
-
catch (err) {
|
|
1140
|
-
discloseNoteTaskRunFailure(err);
|
|
1141
|
-
}
|
|
1142
|
-
const noteUnevaluablePriceTable = (p) => {
|
|
1143
|
-
if (prepared.usageGovernance?.governsCost !== true)
|
|
1144
|
-
return;
|
|
1145
|
-
if (malformedPricingField(p) !== undefined)
|
|
1146
|
-
rs.telemetry.unpricedSpend = true;
|
|
549
|
+
const notificationLane = {
|
|
550
|
+
get harness() { return notificationHarness; }, set harness(h) { notificationHarness = h; },
|
|
551
|
+
get sessionId() { return notificationSessionId; }, set sessionId(s) { notificationSessionId = s; },
|
|
552
|
+
get ident() { return notificationIdent; }, set ident(f) { notificationIdent = f; },
|
|
1147
553
|
};
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
if (!m) {
|
|
1153
|
-
try {
|
|
1154
|
-
m = resolveModel(info.to, modelCatalog);
|
|
1155
|
-
}
|
|
1156
|
-
catch {
|
|
1157
|
-
m = undefined;
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
if (m) {
|
|
1161
|
-
rs.telemetry.pricing = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
|
|
1162
|
-
rs.telemetry.pricingConfigured = isModelPriced(m, this.deps.pricing);
|
|
1163
|
-
noteUnevaluablePriceTable(rs.telemetry.pricing);
|
|
1164
|
-
rs.telemetry.cacheFamily = cacheFamilyOf(m);
|
|
1165
|
-
}
|
|
1166
|
-
else {
|
|
1167
|
-
if (this.deps.pricing?.[info.to]) {
|
|
1168
|
-
rs.telemetry.pricing = this.deps.pricing[info.to];
|
|
1169
|
-
rs.telemetry.pricingConfigured = true;
|
|
1170
|
-
noteUnevaluablePriceTable(rs.telemetry.pricing);
|
|
1171
|
-
}
|
|
1172
|
-
else {
|
|
1173
|
-
rs.telemetry.pricingConfigured = false;
|
|
1174
|
-
}
|
|
1175
|
-
rs.telemetry.cacheFamily = "input-excludes-cached";
|
|
1176
|
-
}
|
|
1177
|
-
rs.degrade.degraded = info;
|
|
1178
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "task.degraded", version: 1, taskId: rs.telemetry.taskId, from: info.from, to: info.to, reason: info.reason, atTurn: info.atTurn, ts: Date.now() }));
|
|
1179
|
-
try {
|
|
1180
|
-
this.deps.onError?.(new Error(`degraded to a cheaper model: ${info.from} → ${info.to} (${info.reason}) at turn ${info.atTurn}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
1181
|
-
}
|
|
1182
|
-
catch {
|
|
1183
|
-
}
|
|
1184
|
-
};
|
|
1185
|
-
const sliceWindowMicroUsd = spec.limits?.maxCostUsd !== undefined ? Math.round(spec.limits.maxCostUsd * 1e6) : undefined;
|
|
1186
|
-
rs.budget.remainingMicroUsd = prepared.resourceLedger
|
|
1187
|
-
? remainingBudgetMicroUsd(prepared.resourceLedger)
|
|
1188
|
-
: spec.resourceSuspend?.totalBudgetUsd !== undefined
|
|
1189
|
-
? Math.round(spec.resourceSuspend.totalBudgetUsd * 1e6)
|
|
1190
|
-
: undefined;
|
|
1191
|
-
rs.budget.maxCostMicroUsd =
|
|
1192
|
-
sliceWindowMicroUsd !== undefined && rs.budget.remainingMicroUsd !== undefined
|
|
1193
|
-
? Math.min(sliceWindowMicroUsd, rs.budget.remainingMicroUsd)
|
|
1194
|
-
: (sliceWindowMicroUsd ?? rs.budget.remainingMicroUsd);
|
|
1195
|
-
rs.budget.remainingTokens = prepared.resourceLedger
|
|
1196
|
-
? remainingTokens(prepared.resourceLedger)
|
|
1197
|
-
: spec.resourceSuspend?.totalTokens;
|
|
1198
|
-
rs.budget.maxTokensWindow =
|
|
1199
|
-
spec.limits?.maxTokens !== undefined && rs.budget.remainingTokens !== undefined
|
|
1200
|
-
? Math.min(spec.limits.maxTokens, rs.budget.remainingTokens)
|
|
1201
|
-
: (spec.limits?.maxTokens ?? rs.budget.remainingTokens);
|
|
1202
|
-
rs.budget.overBudget = () => {
|
|
1203
|
-
if (rs.budget.maxTokensWindow !== undefined && stats.tokens > rs.budget.maxTokensWindow)
|
|
1204
|
-
return "tokens";
|
|
1205
|
-
if (rs.budget.maxCostMicroUsd !== undefined && stats.costMicroUsd > rs.budget.maxCostMicroUsd)
|
|
1206
|
-
return "cost";
|
|
1207
|
-
return undefined;
|
|
1208
|
-
};
|
|
1209
|
-
rs.budget.streamCancel =
|
|
1210
|
-
(spec.limits?.budgetStreamCancel ?? rs.budget.maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
|
|
1211
|
-
rs.budget.callOutputChars = 0;
|
|
1212
|
-
rs.budget.lastStreamBudgetCheck = 0;
|
|
1213
|
-
rs.budget.projectedOverBudget = () => {
|
|
1214
|
-
const estOut = Math.ceil(rs.budget.callOutputChars / 4);
|
|
1215
|
-
if (rs.budget.maxTokensWindow !== undefined && stats.tokens + estOut > rs.budget.maxTokensWindow)
|
|
1216
|
-
return "tokens";
|
|
1217
|
-
if (rs.budget.maxCostMicroUsd !== undefined) {
|
|
1218
|
-
const estOutMicro = computeCostMicroUsd({ totalInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: estOut }, rs.telemetry.pricing);
|
|
1219
|
-
if (stats.costMicroUsd + estOutMicro > rs.budget.maxCostMicroUsd)
|
|
1220
|
-
return "cost";
|
|
1221
|
-
}
|
|
1222
|
-
return undefined;
|
|
1223
|
-
};
|
|
1224
|
-
rs.turn.turnUsageMissing = false;
|
|
1225
|
-
rs.counters.repetitionCuts = 0;
|
|
1226
|
-
rs.counters.repetitionSpared = 0;
|
|
1227
|
-
rs.counters.repetitionEvents = [];
|
|
1228
|
-
rs.counters.REPETITION_EVENTS_CAP = 20;
|
|
1229
|
-
rs.counters.preemptIgnoredReported = false;
|
|
1230
|
-
rs.counters.wroteThisRun = false;
|
|
1231
|
-
rs.counters.finalVerifyInjections = 0;
|
|
1232
|
-
rs.counters.groundingSignalPreR9 = false;
|
|
1233
|
-
rs.counters.groundingSignalPostR9 = false;
|
|
1234
|
-
rs.attach.attachmentsCfg = spec.attachments;
|
|
1235
|
-
rs.attach.agentListingOn = rs.attach.attachmentsCfg?.agentListing !== false && eventDefaultOn("agent_listing");
|
|
1236
|
-
rs.attach.skillsListingOn = rs.attach.attachmentsCfg?.skillsListing !== false && eventDefaultOn("skills_listing");
|
|
1237
|
-
const listingsLive = (rs.attach.agentListingOn && prepared.agentListing !== undefined) || (rs.attach.skillsListingOn && prepared.skillsListing !== undefined);
|
|
1238
|
-
const backgroundTasksLive = (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true;
|
|
1239
|
-
rs.attach.attachState = rs.attach.attachmentsCfg !== undefined || listingsLive || backgroundTasksLive ? createAttachmentState() : undefined;
|
|
1240
|
-
rs.attach.dateState = prepared.dateChange !== undefined ? { announcedDate: prepared.dateChange.legDate } : undefined;
|
|
1241
|
-
rs.attach.instrProbe = this.deps.probeInstructionSources;
|
|
1242
|
-
rs.attach.instrState =
|
|
1243
|
-
rs.attach.instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
|
|
1244
|
-
? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
|
|
1245
|
-
: undefined;
|
|
1246
|
-
rs.attach.sizeGuidelineState =
|
|
1247
|
-
prepared.workflowSizeGuideline !== undefined
|
|
1248
|
-
? { announcedGuideline: prepared.workflowSizeGuideline.legGuideline, current: prepared.workflowSizeGuideline.current }
|
|
1249
|
-
: undefined;
|
|
1250
|
-
rs.counters.cadenceTurns = 0;
|
|
1251
|
-
rs.turn.lastTurnHadToolCalls = false;
|
|
1252
|
-
if (rs.attach.attachState !== undefined && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
|
|
1253
|
-
try {
|
|
1254
|
-
const branch = await prepared.session.getBranch();
|
|
1255
|
-
for (let i = branch.length - 1; i >= 0; i--) {
|
|
1256
|
-
const e = branch[i];
|
|
1257
|
-
if (e.type === "compaction") {
|
|
1258
|
-
rs.attach.attachState.postCompactPending = true;
|
|
1259
|
-
break;
|
|
1260
|
-
}
|
|
1261
|
-
if (e.type === "message" && e.message.role === "assistant")
|
|
1262
|
-
break;
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
catch {
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
if (rs.attach.attachState !== undefined &&
|
|
1269
|
-
((rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) ||
|
|
1270
|
-
(rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true))) {
|
|
1271
|
-
const cpListings = resume?.cp.state.announcedListings;
|
|
1272
|
-
let entryListings;
|
|
1273
|
-
try {
|
|
1274
|
-
entryListings = await prepared.session.getAnnouncedListing();
|
|
1275
|
-
}
|
|
1276
|
-
catch {
|
|
1277
|
-
entryListings = undefined;
|
|
1278
|
-
}
|
|
1279
|
-
const textOf = (m) => {
|
|
1280
|
-
const c = m.content;
|
|
1281
|
-
if (typeof c === "string")
|
|
1282
|
-
return c;
|
|
1283
|
-
if (Array.isArray(c)) {
|
|
1284
|
-
return c
|
|
1285
|
-
.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : ""))
|
|
1286
|
-
.join("\n");
|
|
1287
|
-
}
|
|
1288
|
-
return "";
|
|
1289
|
-
};
|
|
1290
|
-
let branchTexts = null;
|
|
1291
|
-
const branchLoad = async () => {
|
|
1292
|
-
if (branchTexts === null) {
|
|
1293
|
-
const branch = await prepared.session.getBranch();
|
|
1294
|
-
branchTexts = branch.flatMap((e) => {
|
|
1295
|
-
if (e.type !== "message")
|
|
1296
|
-
return [];
|
|
1297
|
-
const m = e.message;
|
|
1298
|
-
if (m.role !== "user")
|
|
1299
|
-
return [];
|
|
1300
|
-
const full = textOf(e.message);
|
|
1301
|
-
if (m.engineMinted === true)
|
|
1302
|
-
return [stripGitStatusUnits(full, prepared.reminderMark)];
|
|
1303
|
-
if (Array.isArray(m.engineSegments) && m.engineSegments.length > 0) {
|
|
1304
|
-
return m.engineSegments.map((s) => stripGitStatusUnits(full.slice(Math.max(0, s.start), Math.max(0, s.end)), prepared.reminderMark));
|
|
1305
|
-
}
|
|
1306
|
-
if (typeof m.enginePrefixChars === "number" && m.enginePrefixChars > 0)
|
|
1307
|
-
return [stripGitStatusUnits(full.slice(0, m.enginePrefixChars), prepared.reminderMark)];
|
|
1308
|
-
return [];
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
return branchTexts;
|
|
1312
|
-
};
|
|
1313
|
-
const branchReplay = async (headers) => {
|
|
1314
|
-
try {
|
|
1315
|
-
return replayAnnouncedListing(await branchLoad(), headers);
|
|
1316
|
-
}
|
|
1317
|
-
catch {
|
|
1318
|
-
return undefined;
|
|
1319
|
-
}
|
|
1320
|
-
};
|
|
1321
|
-
if (rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) {
|
|
1322
|
-
const listing = prepared.agentListing;
|
|
1323
|
-
if (cpListings?.agents === undefined && entryListings?.agents !== undefined) {
|
|
1324
|
-
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
1325
|
-
rs.attach.attachState.announcedAgentTypes = new Map(entryListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
|
|
1326
|
-
prepared.announcedListingsRef.agents = [...entryListings.agents];
|
|
1327
|
-
if (entryListings.models !== undefined) {
|
|
1328
|
-
rs.attach.attachState.announcedModels = [...entryListings.models];
|
|
1329
|
-
prepared.announcedListingsRef.models = [...entryListings.models];
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
else if (cpListings?.agents !== undefined) {
|
|
1333
|
-
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
1334
|
-
rs.attach.attachState.announcedAgentTypes = new Map(cpListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
|
|
1335
|
-
prepared.announcedListingsRef.agents = [...cpListings.agents];
|
|
1336
|
-
if (cpListings.models !== undefined) {
|
|
1337
|
-
rs.attach.attachState.announcedModels = [...cpListings.models];
|
|
1338
|
-
prepared.announcedListingsRef.models = [...cpListings.models];
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
else {
|
|
1342
|
-
const replayed = await branchReplay({
|
|
1343
|
-
initial: agentListingInitialHeader(listing.toolName),
|
|
1344
|
-
delta: agentListingDeltaHeader(listing.toolName),
|
|
1345
|
-
removed: AGENT_LISTING_REMOVED_HEADER,
|
|
1346
|
-
});
|
|
1347
|
-
if (replayed !== undefined) {
|
|
1348
|
-
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
1349
|
-
rs.attach.attachState.announcedAgentTypes = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
|
|
1350
|
-
prepared.announcedListingsRef.agents = [...replayed];
|
|
1351
|
-
const replayedModels = replayAnnouncedModels(await branchLoad());
|
|
1352
|
-
if (replayedModels !== undefined) {
|
|
1353
|
-
rs.attach.attachState.announcedModels = [...replayedModels];
|
|
1354
|
-
prepared.announcedListingsRef.models = [...replayedModels];
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
if (rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true) {
|
|
1360
|
-
const listing = prepared.skillsListing;
|
|
1361
|
-
if (cpListings?.skills === undefined && entryListings?.skills !== undefined) {
|
|
1362
|
-
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
1363
|
-
rs.attach.attachState.announcedSkills = new Map(entryListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
|
|
1364
|
-
prepared.announcedListingsRef.skills = [...entryListings.skills];
|
|
1365
|
-
}
|
|
1366
|
-
else if (cpListings?.skills !== undefined) {
|
|
1367
|
-
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
1368
|
-
rs.attach.attachState.announcedSkills = new Map(cpListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
|
|
1369
|
-
prepared.announcedListingsRef.skills = [...cpListings.skills];
|
|
1370
|
-
}
|
|
1371
|
-
else {
|
|
1372
|
-
const replayed = await branchReplay({
|
|
1373
|
-
initial: SKILLS_LISTING_PROBE_HEADER,
|
|
1374
|
-
delta: SKILLS_LISTING_DELTA_HEADER,
|
|
1375
|
-
removed: SKILLS_LISTING_REMOVED_HEADER,
|
|
1376
|
-
});
|
|
1377
|
-
if (replayed !== undefined) {
|
|
1378
|
-
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
1379
|
-
rs.attach.attachState.announcedSkills = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
|
|
1380
|
-
prepared.announcedListingsRef.skills = [...replayed];
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
if (spec.finalVerification === true && resume !== undefined) {
|
|
1386
|
-
try {
|
|
1387
|
-
const branch = await prepared.session.getBranch();
|
|
1388
|
-
for (const e of branch) {
|
|
1389
|
-
if (e.type !== "message")
|
|
1390
|
-
continue;
|
|
1391
|
-
const m = e.message;
|
|
1392
|
-
if (m.role === "user") {
|
|
1393
|
-
const text = typeof m.content === "string"
|
|
1394
|
-
? m.content
|
|
1395
|
-
: m.content.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : "")).join("\n");
|
|
1396
|
-
const fvMarked = m.engineMinted === true && text.includes(`<system-reminder mark="${prepared.reminderMark}">[final verification]`);
|
|
1397
|
-
if ((fvMarked || text.includes("<system-reminder>[final verification]")) && rs.counters.finalVerifyInjections < 2)
|
|
1398
|
-
rs.counters.finalVerifyInjections += 1;
|
|
1399
|
-
}
|
|
1400
|
-
else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
|
|
1401
|
-
rs.counters.wroteThisRun = true;
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
catch {
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
rs.attach.attachmentsInjected = 0;
|
|
1409
|
-
const todoToolMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => t.name === "TodoWrite");
|
|
1410
|
-
const taskToolsMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => t.name === "TaskCreate");
|
|
1411
|
-
const writeFamilyByName = new Map();
|
|
1412
|
-
if (rs.attach.attachState !== undefined) {
|
|
1413
|
-
for (const t of prepared.tools) {
|
|
1414
|
-
const family = writeFamilyOfCanonical(t.name);
|
|
1415
|
-
if (family !== undefined)
|
|
1416
|
-
for (const n of [t.name, ...(t.aliases ?? [])])
|
|
1417
|
-
writeFamilyByName.set(n, family);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(name);
|
|
1421
|
-
const toolStartAt = new Map();
|
|
1422
|
-
const startedToolCallIds = new Set();
|
|
1423
|
-
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 }));
|
|
1424
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1425
|
-
kind: "prompt.assembled",
|
|
1426
|
-
version: 1,
|
|
1427
|
-
taskId: rs.telemetry.taskId,
|
|
1428
|
-
constitution: prepared.promptManifest.constitution,
|
|
1429
|
-
blocks: prepared.promptManifest.blocks,
|
|
1430
|
-
...(prepared.promptManifest.sections ? { sections: prepared.promptManifest.sections } : {}),
|
|
1431
|
-
...(prepared.promptManifest.tools ? { tools: prepared.promptManifest.tools } : {}),
|
|
1432
|
-
...(prepared.promptManifest.snapshot ? { snapshot: prepared.promptManifest.snapshot } : {}),
|
|
1433
|
-
...(prepared.promptManifest.lowering ? { lowering: prepared.promptManifest.lowering } : {}),
|
|
1434
|
-
...(prepared.promptManifest.toolDisclosure ? { toolDisclosure: prepared.promptManifest.toolDisclosure } : {}),
|
|
1435
|
-
totalChars: prepared.promptManifest.blocks.reduce((n, b) => n + b.chars, 0),
|
|
1436
|
-
ts: rs.telemetry.taskStart,
|
|
1437
|
-
}));
|
|
1438
|
-
emitTrace(rs.telemetry.tracer, () => {
|
|
1439
|
-
const reasons = declarationReasons(spec.configOverrides);
|
|
1440
|
-
return {
|
|
1441
|
-
kind: "config.assembled",
|
|
1442
|
-
version: 1,
|
|
1443
|
-
taskId: rs.telemetry.taskId,
|
|
1444
|
-
catalogVersion: CONFIG_CATALOG_VERSION,
|
|
1445
|
-
fields: resolveEffectiveConfig(spec, { modelMaxTokens: prepared.model.maxTokens }),
|
|
1446
|
-
...(Object.keys(reasons).length > 0 ? { overrideReasons: reasons } : {}),
|
|
1447
|
-
ts: rs.telemetry.taskStart,
|
|
1448
|
-
};
|
|
1449
|
-
});
|
|
1450
|
-
if (prepared.turnSnapshot !== undefined) {
|
|
1451
|
-
prepared.turnSnapshot.onChange = (snap) => emitTrace(rs.telemetry.tracer, () => ({
|
|
1452
|
-
kind: "prompt.snapshot_changed",
|
|
1453
|
-
version: 1,
|
|
1454
|
-
taskId: rs.telemetry.taskId,
|
|
1455
|
-
snapshot: { cacheIdentity: snap.cacheIdentity, elements: { ...snap.elements } },
|
|
1456
|
-
ts: Date.now(),
|
|
1457
|
-
}));
|
|
1458
|
-
}
|
|
1459
|
-
let reasoningResolution = prepared.thinking && prepared.thinking !== "off" ? resolveReasoning(prepared.thinking, prepared.model) : undefined;
|
|
1460
|
-
const publishReasoningResolution = (r) => {
|
|
1461
|
-
reasoningResolution = r;
|
|
1462
|
-
if (taskIdRef)
|
|
1463
|
-
taskIdRef.effectiveReasoning = r;
|
|
1464
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1465
|
-
kind: "reasoning.resolved",
|
|
1466
|
-
version: 1,
|
|
1467
|
-
taskId: rs.telemetry.taskId,
|
|
1468
|
-
model: prepared.model.id,
|
|
1469
|
-
requested: r.requested,
|
|
1470
|
-
effective: r.effective,
|
|
1471
|
-
graded: r.graded,
|
|
1472
|
-
clamped: r.clamped,
|
|
1473
|
-
format: r.format,
|
|
1474
|
-
endpoint: r.endpoint,
|
|
1475
|
-
...(r.dropped === true ? { dropped: true } : {}),
|
|
1476
|
-
ts: Date.now(),
|
|
1477
|
-
}));
|
|
1478
|
-
};
|
|
1479
|
-
if (reasoningResolution !== undefined)
|
|
1480
|
-
publishReasoningResolution(reasoningResolution);
|
|
1481
|
-
let reasoningFactsConsumed = false;
|
|
1482
|
-
const observeReasoningWireFacts = (facts) => {
|
|
1483
|
-
if (reasoningFactsConsumed)
|
|
1484
|
-
return;
|
|
1485
|
-
reasoningFactsConsumed = true;
|
|
1486
|
-
if (prepared.thinking === undefined || prepared.thinking === "off")
|
|
1487
|
-
return;
|
|
1488
|
-
const next = resolveReasoning(prepared.thinking, prepared.model, facts);
|
|
1489
|
-
const current = reasoningResolution;
|
|
1490
|
-
if (current !== undefined &&
|
|
1491
|
-
current.effective === next.effective &&
|
|
1492
|
-
current.graded === next.graded &&
|
|
1493
|
-
current.clamped === next.clamped &&
|
|
1494
|
-
current.format === next.format &&
|
|
1495
|
-
current.endpoint === next.endpoint &&
|
|
1496
|
-
current.dropped === next.dropped) {
|
|
1497
|
-
return;
|
|
1498
|
-
}
|
|
1499
|
-
publishReasoningResolution(next);
|
|
1500
|
-
};
|
|
1501
|
-
const effectiveTimeoutMs = spec.limits?.maxWalltimeMs;
|
|
1502
|
-
const walltimeMonotonicDeadline = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStartMonotonic + effectiveTimeoutMs : undefined;
|
|
1503
|
-
rs.counters.walltimeSyncBackstopFired = false;
|
|
1504
|
-
const timeout = startTimeout(prepared.harness, prepared.abortController, walltimeMonotonicDeadline !== undefined ? walltimeMonotonicDeadline - performance.now() : undefined, prepared.suspendForResource !== undefined);
|
|
1505
|
-
const pushContent = (e) => {
|
|
1506
|
-
queue.push(e);
|
|
1507
|
-
if (parentToolCallId !== undefined && internals?.onForwardEvent) {
|
|
1508
|
-
try {
|
|
1509
|
-
internals.onForwardEvent(e);
|
|
1510
|
-
}
|
|
1511
|
-
catch {
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
};
|
|
1515
|
-
const ownCommittedTailRef = { current: undefined };
|
|
1516
|
-
const emitCommitted = (entryId, role, toolCallId) => {
|
|
1517
|
-
ownCommittedTailRef.current = entryId;
|
|
1518
|
-
if (role === "toolResult" && toolCallId !== undefined)
|
|
1519
|
-
startedToolCallIds.delete(toolCallId);
|
|
1520
|
-
queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
|
|
1521
|
-
};
|
|
1522
|
-
const subagentName = parentToolCallId !== undefined && internals?.agentName !== undefined ? inlineUntrusted(internals.agentName.slice(0, 320), 80) : undefined;
|
|
1523
|
-
const statusSinkNotifier = createSafeNotifier({
|
|
1524
|
-
onError: (f) => console.warn(`[sema-core] ${f.site}: run-internals status sink threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
554
|
+
const undrainedUserAtEndCell = { get current() { return undrainedUserAtEnd; }, set current(v) { undrainedUserAtEnd = v; } };
|
|
555
|
+
const { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats } = runIdentityWiring({
|
|
556
|
+
spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef,
|
|
557
|
+
notificationLane, undrainedUserAtEnd: undrainedUserAtEndCell, pendingSessionNotifications: this.pendingSessionNotifications, runner: this.depsSeat,
|
|
1525
558
|
});
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
...(s.timeoutMs !== undefined ? { timeoutMs: s.timeoutMs } : {}),
|
|
1536
|
-
...(s.attempt !== undefined ? { attempt: s.attempt } : {}),
|
|
1537
|
-
...(s.maxRetries !== undefined ? { maxRetries: s.maxRetries } : {}),
|
|
1538
|
-
...(s.errClass !== undefined ? { errClass: s.errClass } : {}),
|
|
1539
|
-
...(s.errorStatus !== undefined ? { errorStatus: s.errorStatus } : {}),
|
|
1540
|
-
...ident(),
|
|
1541
|
-
};
|
|
1542
|
-
Object.freeze(frame);
|
|
1543
|
-
const accepted = queue.push(frame);
|
|
1544
|
-
if (accepted && internals?.onStatusEvent !== undefined) {
|
|
1545
|
-
statusSinkNotifier.notify(() => observeThenableRejection(internals.onStatusEvent?.(frame), statusSinkNotifier, "runtask.onStatusEvent"), "runtask.onStatusEvent");
|
|
1546
|
-
}
|
|
1547
|
-
};
|
|
1548
|
-
const telemetryEmit = (t) => {
|
|
1549
|
-
emitTrace(rs.telemetry.tracer, () => t.kind === "failover"
|
|
1550
|
-
? {
|
|
1551
|
-
kind: "brain.failover",
|
|
1552
|
-
version: 1,
|
|
1553
|
-
taskId: rs.telemetry.taskId,
|
|
1554
|
-
servedIndex: t.servedIndex,
|
|
1555
|
-
total: t.total,
|
|
1556
|
-
...(t.errorCode !== undefined ? { errorCode: t.errorCode } : {}),
|
|
1557
|
-
ts: Date.now(),
|
|
1558
|
-
}
|
|
1559
|
-
: t.kind === "breaker"
|
|
1560
|
-
? { kind: "breaker.transition", version: 1, taskId: rs.telemetry.taskId, key: t.key, phase: t.phase, failures: t.failures, ts: Date.now() }
|
|
1561
|
-
: t.kind === "retry"
|
|
1562
|
-
? {
|
|
1563
|
-
kind: "brain.retry",
|
|
1564
|
-
version: 1,
|
|
1565
|
-
taskId: rs.telemetry.taskId,
|
|
1566
|
-
attempt: t.attempt,
|
|
1567
|
-
phase: t.phase,
|
|
1568
|
-
...(t.errClass !== undefined ? { errClass: t.errClass } : {}),
|
|
1569
|
-
...(t.nextDelayMs !== undefined ? { nextDelayMs: t.nextDelayMs } : {}),
|
|
1570
|
-
ts: Date.now(),
|
|
1571
|
-
}
|
|
1572
|
-
: { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
|
|
1573
|
-
};
|
|
1574
|
-
const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, () => runWithReasoningWireFacts(observeReasoningWireFacts, fn)));
|
|
559
|
+
const rs = createRunState();
|
|
560
|
+
const { noteUnevaluablePriceTable } = runTelemetryAndBudgetSeats({ spec, prepared, resume, stats, taskIdRef, entryTracer, modelCatalog, runSourceTaskId, rs, runner: this.depsSeat, sessions: this.sessions });
|
|
561
|
+
await runAttachmentSeats({ spec, prepared, resume, rs, runner: this.depsSeat });
|
|
562
|
+
const { todoToolMounted, taskToolsMounted, writeFamilyOf, toolStartAt, startedToolCallIds } = runToolMountFacts({ spec, prepared, rs });
|
|
563
|
+
let reasoningResolution;
|
|
564
|
+
const reasoningResolutionCell = { get current() { return reasoningResolution; }, set current(v) { reasoningResolution = v; } };
|
|
565
|
+
const { observeReasoningWireFacts } = runReasoningSeat({ prepared, rs, taskIdRef, reasoningResolution: reasoningResolutionCell });
|
|
566
|
+
const { effectiveTimeoutMs, walltimeMonotonicDeadline, timeout, pushContent, ownCommittedTailRef, emitCommitted } = runClockAndContent({ spec, prepared, queue, internals, rs, ident, parentToolCallId, startedToolCallIds });
|
|
567
|
+
const { subagentName, withBrainSinks } = runBrainSinks({ queue, internals, rs, ident, parentToolCallId, observeReasoningWireFacts });
|
|
1575
568
|
const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
|
|
1576
569
|
for (const orphan of prepared.wakeRecovered) {
|
|
1577
570
|
queue.push({ type: "tool_end", toolCallId: orphan.toolCallId, toolName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), isError: true, ...reconciledToolEndBody(orphan, prepared.structuredProjector), ...ident() });
|
|
@@ -1589,412 +582,16 @@ export class Runner {
|
|
|
1589
582
|
runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
|
|
1590
583
|
},
|
|
1591
584
|
});
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
rs.telemetry.unpricedSpend = true;
|
|
1601
|
-
noteUnevaluablePriceTable(price);
|
|
1602
|
-
const { totalInputTokens, uncachedInputTokens, costMicroUsd } = usageCostMicroUsd(fam, u, price);
|
|
1603
|
-
stats.tokens += u.totalTokens || 0;
|
|
1604
|
-
stats.promptTokens += uncachedInputTokens;
|
|
1605
|
-
stats.totalInputTokens += totalInputTokens;
|
|
1606
|
-
stats.cachedTokens += u.cacheRead || 0;
|
|
1607
|
-
stats.cacheWriteTokens += u.cacheWrite || 0;
|
|
1608
|
-
stats.outputTokens += u.output || 0;
|
|
1609
|
-
stats.costMicroUsd += costMicroUsd;
|
|
1610
|
-
stats.compactionMicroUsd = (stats.compactionMicroUsd ?? 0) + costMicroUsd;
|
|
1611
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1612
|
-
kind: "brain.call", version: 1, taskId: rs.telemetry.taskId, model: m.id, provider: m.provider,
|
|
1613
|
-
promptTokens: uncachedInputTokens, totalInputTokens, completionTokens: u.output || 0,
|
|
1614
|
-
cacheRead: u.cacheRead || 0, cacheWrite: u.cacheWrite || 0,
|
|
1615
|
-
latencyMs: 0, ...(priced ? { costMicroUsd } : {}), ...(typeof msg?.stopReason === "string" ? { stopReason: msg.stopReason } : {}), ts: Date.now(),
|
|
1616
|
-
}));
|
|
1617
|
-
};
|
|
1618
|
-
const compactionBrain = {
|
|
1619
|
-
stream: this.deps.brain.stream,
|
|
1620
|
-
complete: async (m, c, o) => {
|
|
1621
|
-
const msg = await runWithStatusSink(() => { }, async () => await runWithReasoningWireFacts(() => { }, async () => this.deps.brain.complete
|
|
1622
|
-
? await this.deps.brain.complete(m, c, o)
|
|
1623
|
-
: await (await Promise.resolve(this.deps.brain.stream(m, c, o))).result()));
|
|
1624
|
-
recordCompactionUsage(m, msg);
|
|
1625
|
-
return msg;
|
|
1626
|
-
},
|
|
1627
|
-
};
|
|
1628
|
-
const withinTaskCompaction = (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true);
|
|
1629
|
-
const compactionBreaker = { failures: 0 };
|
|
1630
|
-
if (spec.compaction?.enabled ?? true) {
|
|
1631
|
-
const prefixWindow = resolveTriggerWindow(prepared.model).window;
|
|
1632
|
-
if (Number.isFinite(prefixWindow) && prefixWindow > 0) {
|
|
1633
|
-
const prefixSettings = sanitizeCompactionSettings({ ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction }, prefixWindow);
|
|
1634
|
-
const prefixCompactAt = prefixWindow - prefixSettings.reserveTokens;
|
|
1635
|
-
if (prepared.promptOverheadTokens >= prefixCompactAt) {
|
|
1636
|
-
this.deps.onError?.(new Error(`compaction cannot help: the fixed request prefix (system prompt + tool schemas, ≈${prepared.promptOverheadTokens} tokens) ` +
|
|
1637
|
-
`already meets or exceeds the compaction threshold (${prefixCompactAt} of a ${prefixWindow}-token window). ` +
|
|
1638
|
-
`Compaction only shrinks conversation history, so this run will re-trigger or overflow regardless — ` +
|
|
1639
|
-
`shrink the system prompt/tool surface or use a larger-window model.`), { phase: "config", sessionId: prepared.sessionId });
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
const windowSafetyOptions = (mainModel) => ({
|
|
1644
|
-
...(rs.budget.maxCostMicroUsd !== undefined
|
|
1645
|
-
? {
|
|
1646
|
-
fallbackBudget: {
|
|
1647
|
-
spentMicroUsd: () => stats.costMicroUsd,
|
|
1648
|
-
capMicroUsd: rs.budget.maxCostMicroUsd,
|
|
1649
|
-
mainInputPer1M: (this.deps.pricing?.[mainModel.id] ?? modelCostToPricing(mainModel.cost)).inputPer1M,
|
|
1650
|
-
},
|
|
1651
|
-
}
|
|
1652
|
-
: {}),
|
|
1653
|
-
onWindowSafety: (info) => {
|
|
1654
|
-
if (info.kind === "fallback") {
|
|
1655
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1656
|
-
kind: "compaction.model_fallback",
|
|
1657
|
-
version: 1,
|
|
1658
|
-
taskId: rs.telemetry.taskId,
|
|
1659
|
-
contentTokens: info.contentTokens,
|
|
1660
|
-
headroomTokens: info.headroomTokens ?? 0,
|
|
1661
|
-
truncationRatio: info.truncationRatio,
|
|
1662
|
-
...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
|
|
1663
|
-
ts: Date.now(),
|
|
1664
|
-
}));
|
|
1665
|
-
}
|
|
1666
|
-
else {
|
|
1667
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1668
|
-
kind: "compaction.clamp_disclosure",
|
|
1669
|
-
version: 1,
|
|
1670
|
-
taskId: rs.telemetry.taskId,
|
|
1671
|
-
truncationRatio: info.truncationRatio,
|
|
1672
|
-
reason: info.reason ?? "tolerance",
|
|
1673
|
-
...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
|
|
1674
|
-
ts: Date.now(),
|
|
1675
|
-
}));
|
|
1676
|
-
}
|
|
585
|
+
const { compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions } = runCompactionMachinery({ spec, prepared, rs, stats, noteUnevaluablePriceTable, runner: this.depsSeat });
|
|
586
|
+
runStopAndFinalVerify({ spec, prepared, queue, rs, stats, ident, walltimeMonotonicDeadline, runner: this.depsSeat });
|
|
587
|
+
runRecoveryLanes({
|
|
588
|
+
spec, prepared, queue, rs, ident, compactionBrain, compactionBreaker, windowSafetyOptions, runner: this.depsSeat,
|
|
589
|
+
compactionSeats: {
|
|
590
|
+
seamCCompactionOptions: (p) => this.seamCCompactionOptions(p),
|
|
591
|
+
compactionHookOptions: (s, sid, trig, identity, seatBound) => this.compactionHookOptions(s, sid, trig, identity, seatBound),
|
|
592
|
+
recordCompactionReuse: (p, c) => this.recordCompactionReuse(p, c),
|
|
1677
593
|
},
|
|
1678
594
|
});
|
|
1679
|
-
const stopHook = (spec.hooks ?? this.deps.hooks)?.stop;
|
|
1680
|
-
const finalVerificationOn = spec.finalVerification === true;
|
|
1681
|
-
const finalVerifyBudgetFill = () => {
|
|
1682
|
-
let worst = 0;
|
|
1683
|
-
if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow > 0)
|
|
1684
|
-
worst = Math.max(worst, stats.tokens / rs.budget.maxTokensWindow);
|
|
1685
|
-
if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd > 0)
|
|
1686
|
-
worst = Math.max(worst, stats.costMicroUsd / rs.budget.maxCostMicroUsd);
|
|
1687
|
-
if (walltimeMonotonicDeadline !== undefined && prepared.suspendForResource === undefined) {
|
|
1688
|
-
const windowMs = walltimeMonotonicDeadline - rs.telemetry.taskStartMonotonic;
|
|
1689
|
-
if (windowMs > 0)
|
|
1690
|
-
worst = Math.max(worst, (performance.now() - rs.telemetry.taskStartMonotonic) / windowMs);
|
|
1691
|
-
}
|
|
1692
|
-
return worst;
|
|
1693
|
-
};
|
|
1694
|
-
const emitFinalVerifyEcho = (body) => {
|
|
1695
|
-
queue.push({ type: "steering_injected", source: "final_verification", preview: body.slice(0, 220), ...ident() });
|
|
1696
|
-
};
|
|
1697
|
-
if (stopHook || finalVerificationOn) {
|
|
1698
|
-
let consecutiveBlocks = 0;
|
|
1699
|
-
prepared.harness.setStopGate(async () => {
|
|
1700
|
-
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined || prepared.batchHaltRef.current !== undefined)
|
|
1701
|
-
return [];
|
|
1702
|
-
if (finalVerificationOn &&
|
|
1703
|
-
(rs.counters.finalVerifyInjections === 0 || (rs.counters.finalVerifyInjections === 1 && rs.counters.groundingSignalPreR9 && !rs.counters.groundingSignalPostR9)) &&
|
|
1704
|
-
rs.counters.wroteThisRun &&
|
|
1705
|
-
prepared.outputRef.set !== true &&
|
|
1706
|
-
!(rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns - 1) &&
|
|
1707
|
-
finalVerifyBudgetFill() < 0.9) {
|
|
1708
|
-
rs.counters.finalVerifyInjections += 1;
|
|
1709
|
-
if (rs.counters.finalVerifyInjections === 2) {
|
|
1710
|
-
const reentryBody = openSystemReminder(prepared.reminderMark) +
|
|
1711
|
-
"[final verification] Your tool calls in this run worked with raw bytes, structural parsing, " +
|
|
1712
|
-
"or checksum/digest computation — the deliverable very likely embeds verifiable structure (structural fields, an " +
|
|
1713
|
-
"embedded checksum-family value, reference data it must match, or a replayable deterministic path). You MUST " +
|
|
1714
|
-
"execute the grounding check that structure supports — recompute the embedded value and compare it against the " +
|
|
1715
|
-
"declared one, re-parse the structure from the raw bytes and reconcile it with your output, compare against the " +
|
|
1716
|
-
"reference data, or replay the deterministic path — and REPORT the check's concrete result before finishing. " +
|
|
1717
|
-
"A closing statement without a reported check result is not verification. If you already ran such a check, state " +
|
|
1718
|
-
"its concrete result now; if the check mismatches, fix the deliverable first. This is the final reminder from " +
|
|
1719
|
-
"this verification gate — it will not intervene again.</system-reminder>";
|
|
1720
|
-
emitFinalVerifyEcho(reentryBody);
|
|
1721
|
-
return [
|
|
1722
|
-
{
|
|
1723
|
-
role: "user",
|
|
1724
|
-
engineMinted: true,
|
|
1725
|
-
content: reentryBody,
|
|
1726
|
-
timestamp: Date.now(),
|
|
1727
|
-
},
|
|
1728
|
-
];
|
|
1729
|
-
}
|
|
1730
|
-
const nudgeBody = openSystemReminder(prepared.reminderMark) +
|
|
1731
|
-
"[final verification] Before finishing: re-verify the FINAL deliverable through its REAL entry point, " +
|
|
1732
|
-
"exactly as the acceptance criteria would exercise it — execute the binary/function/endpoint directly and read the ACTUAL " +
|
|
1733
|
-
"output and exit code. Do NOT rely on earlier self-tests, shell redirections, or assumptions (a program that prints to " +
|
|
1734
|
-
"stdout is not a program that writes the required file). If anything mismatches the task's requirements, fix it before " +
|
|
1735
|
-
"finishing. " +
|
|
1736
|
-
"Treat verification writes as state-harmless: when the deliverable itself is a persisted final " +
|
|
1737
|
-
"state (for example a committed or pushed file, a deployed artifact, or a required output file), " +
|
|
1738
|
-
"do not change that state merely to test it. This constrains HOW you verify — it is never a " +
|
|
1739
|
-
"license to skip the real acceptance path or to check a substitute of your own making: expected " +
|
|
1740
|
-
"values must come from the task's requirements, never from content you generated. If the real " +
|
|
1741
|
-
"acceptance path requires a write, use disposable inputs or an isolated target, end in the exact " +
|
|
1742
|
-
"required final state, and verify that final state before finishing. " +
|
|
1743
|
-
"If the work relied on a third-party API, library, or model, check the usage contract the object itself declares " +
|
|
1744
|
-
"(docstrings, metadata, configuration — e.g. prompt conventions shipped with a model) and confirm your calls follow " +
|
|
1745
|
-
"it rather than a default symmetric usage. Verify not only that the deliverable EXISTS but that the METHOD that " +
|
|
1746
|
-
"produced it matches the task's requirements. " +
|
|
1747
|
-
"Choose the verification SURFACE deliberately: check against the reference data, oracle, or evaluation tooling the " +
|
|
1748
|
-
"task itself provides — re-running your own implementation and getting the same answer is self-consistency, not " +
|
|
1749
|
-
"correctness — and cross-check through an independent second path where the task or environment offers one " +
|
|
1750
|
-
"(checksums, runtime artifacts). Verify the PERSISTED artifact — re-read what is actually on disk or committed, " +
|
|
1751
|
-
"not in-memory state — against every hard constraint from the original task text (numeric bounds, allowed-value " +
|
|
1752
|
-
"lists, naming semantics, required files), reconciling whole-set completeness: nothing missing, nothing duplicated. " +
|
|
1753
|
-
"If the deliverable embeds verifiable structure — structural fields, an embedded checksum-family value, " +
|
|
1754
|
-
"reference data it must match, or a replayable deterministic path — you MUST execute the grounding check " +
|
|
1755
|
-
"that structure supports and REPORT its concrete result in your closing summary: for such a deliverable, " +
|
|
1756
|
-
"no reported check result means the work is not finished. " +
|
|
1757
|
-
"If the task produced neither an executable deliverable nor any verifiable structure or acceptance " +
|
|
1758
|
-
"oracle to check against, briefly confirm completion and stop. " +
|
|
1759
|
-
"Residue YOUR OWN testing created (scratch files, running processes, generated outputs the task does not ask for) " +
|
|
1760
|
-
"is not protected state — if the task's required final state is a clean target, removing your own residue is part " +
|
|
1761
|
-
"of delivering it. " +
|
|
1762
|
-
"Verification must never LAUNDER uncertainty: if part of your conclusion was uncertain before this check, keep " +
|
|
1763
|
-
"reporting it as uncertain unless the check you actually ran resolved it — a re-stated conclusion is not new " +
|
|
1764
|
-
"evidence.</system-reminder>";
|
|
1765
|
-
emitFinalVerifyEcho(nudgeBody);
|
|
1766
|
-
return [
|
|
1767
|
-
{
|
|
1768
|
-
role: "user",
|
|
1769
|
-
engineMinted: true,
|
|
1770
|
-
content: nudgeBody,
|
|
1771
|
-
timestamp: Date.now(),
|
|
1772
|
-
},
|
|
1773
|
-
];
|
|
1774
|
-
}
|
|
1775
|
-
if (!stopHook)
|
|
1776
|
-
return [];
|
|
1777
|
-
let result;
|
|
1778
|
-
try {
|
|
1779
|
-
const stopSeat = await runHookSeat("stop", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => stopHook({
|
|
1780
|
-
stopHookActive: consecutiveBlocks > 0,
|
|
1781
|
-
consecutiveBlocks,
|
|
1782
|
-
getBranch: () => prepared.session.getBranch(),
|
|
1783
|
-
identity: prepared.hookIdentity,
|
|
1784
|
-
signal: sig,
|
|
1785
|
-
}));
|
|
1786
|
-
if (stopSeat.expired) {
|
|
1787
|
-
if (stopSeat.cause === "timeout") {
|
|
1788
|
-
this.deps.onError?.(hookSeatExpiredError("stop", prepared.hookTimeoutMs, stopSeat.cause, "the run was allowed to END (the seat's own no-opinion answer); no pushback and no additional context were injected"), { phase: "hook", sessionId: prepared.sessionId });
|
|
1789
|
-
}
|
|
1790
|
-
consecutiveBlocks = 0;
|
|
1791
|
-
return [];
|
|
1792
|
-
}
|
|
1793
|
-
result = stopSeat.value;
|
|
1794
|
-
}
|
|
1795
|
-
catch (err) {
|
|
1796
|
-
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
|
|
1797
|
-
return [];
|
|
1798
|
-
}
|
|
1799
|
-
const messages = [];
|
|
1800
|
-
if (result?.additionalContext) {
|
|
1801
|
-
messages.push({
|
|
1802
|
-
role: "user",
|
|
1803
|
-
engineMinted: true,
|
|
1804
|
-
content: formatHookFeedback(`Stop hook additional context: ${result.additionalContext}`, prepared.reminderMark),
|
|
1805
|
-
timestamp: Date.now(),
|
|
1806
|
-
});
|
|
1807
|
-
}
|
|
1808
|
-
if (messages.length === 0 && !result?.block) {
|
|
1809
|
-
consecutiveBlocks = 0;
|
|
1810
|
-
return messages;
|
|
1811
|
-
}
|
|
1812
|
-
consecutiveBlocks++;
|
|
1813
|
-
const cap = STOP_HOOK_BLOCK_CAP;
|
|
1814
|
-
if (consecutiveBlocks > cap) {
|
|
1815
|
-
this.deps.onError?.(new Error(`a Stop hook kept the turn from ending ${consecutiveBlocks} consecutive times — overriding and ending the run. ` +
|
|
1816
|
-
`Both a block and an additionalContext-only push-back count (CC 2.1.220). ` +
|
|
1817
|
-
`Check ctx.stopHookActive in the hook and return success while it's true.`), { phase: "hook", sessionId: prepared.sessionId });
|
|
1818
|
-
return [];
|
|
1819
|
-
}
|
|
1820
|
-
if (result?.block) {
|
|
1821
|
-
messages.push({
|
|
1822
|
-
role: "user",
|
|
1823
|
-
engineMinted: true,
|
|
1824
|
-
content: formatHookFeedback(`Stop hook stopped continuation: ${result.block}`, prepared.reminderMark),
|
|
1825
|
-
timestamp: Date.now(),
|
|
1826
|
-
});
|
|
1827
|
-
}
|
|
1828
|
-
return messages;
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
const runForcedCompactionPass = async (lane, turnSignal) => {
|
|
1832
|
-
if (!(spec.compaction?.enabled ?? true))
|
|
1833
|
-
return false;
|
|
1834
|
-
if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES)
|
|
1835
|
-
return false;
|
|
1836
|
-
const passSignal = turnSignal !== undefined ? AbortSignal.any([prepared.abortController.signal, turnSignal]) : prepared.abortController.signal;
|
|
1837
|
-
try {
|
|
1838
|
-
const comp = await maybeCompact({
|
|
1839
|
-
session: prepared.session,
|
|
1840
|
-
epochDeclaredSections: prepared.epochDeclaredSections,
|
|
1841
|
-
...centerAdoptionOption(prepared),
|
|
1842
|
-
model: prepared.harness.getModel(),
|
|
1843
|
-
compactionModel: prepared.compModel,
|
|
1844
|
-
...forkContextOption(prepared, true),
|
|
1845
|
-
brain: compactionBrain,
|
|
1846
|
-
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1847
|
-
thinking: prepared.thinking,
|
|
1848
|
-
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
1849
|
-
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
1850
|
-
signal: passSignal,
|
|
1851
|
-
minTokens: 0,
|
|
1852
|
-
force: true,
|
|
1853
|
-
overheadTokens: prepared.promptOverheadTokens,
|
|
1854
|
-
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
1855
|
-
onInputTruncated: emitInputTruncated(rs.telemetry.tracer, rs.telemetry.taskId),
|
|
1856
|
-
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
1857
|
-
...contextInstructionFilesOption(prepared),
|
|
1858
|
-
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
1859
|
-
...this.seamCCompactionOptions(prepared),
|
|
1860
|
-
...gitRestateOption(prepared),
|
|
1861
|
-
...windowSafetyOptions(prepared.harness.getModel()),
|
|
1862
|
-
...this.compactionHookOptions(spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: passSignal }),
|
|
1863
|
-
});
|
|
1864
|
-
if (comp.compacted) {
|
|
1865
|
-
compactionBreaker.failures = 0;
|
|
1866
|
-
this.recordCompactionReuse(prepared, comp);
|
|
1867
|
-
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
1868
|
-
const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
|
|
1869
|
-
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
1870
|
-
}
|
|
1871
|
-
queue.push({
|
|
1872
|
-
type: "compacted",
|
|
1873
|
-
trigger: "forced",
|
|
1874
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
1875
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
1876
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
1877
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
1878
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
1879
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
1880
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
1881
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
1882
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
1883
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
1884
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
1885
|
-
...ident(),
|
|
1886
|
-
});
|
|
1887
|
-
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
1888
|
-
const pd = comp.phaseDurations;
|
|
1889
|
-
const pdDur = comp.durationMs;
|
|
1890
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
|
|
1891
|
-
}
|
|
1892
|
-
prepared.cacheBreakDetector?.notifyCompaction();
|
|
1893
|
-
if (rs.attach.attachState !== undefined) {
|
|
1894
|
-
rs.attach.attachState.postCompactPending = true;
|
|
1895
|
-
rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
else {
|
|
1899
|
-
if (comp.noop) {
|
|
1900
|
-
compactionBreaker.failures = 0;
|
|
1901
|
-
}
|
|
1902
|
-
else {
|
|
1903
|
-
compactionBreaker.failures += 1;
|
|
1904
|
-
}
|
|
1905
|
-
const declineReason = lane === "rejection" ? "prompt-too-long recovery pass did not land" : "guard-chain forced compaction pass did not land";
|
|
1906
|
-
if (!comp.noop) {
|
|
1907
|
-
this.deps.onError?.(new Error(declineReason), { phase: "compaction", sessionId: prepared.sessionId });
|
|
1908
|
-
}
|
|
1909
|
-
queue.push({
|
|
1910
|
-
type: "compaction_outcome",
|
|
1911
|
-
outcome: comp.noop ? "noop" : "failed",
|
|
1912
|
-
trigger: "forced",
|
|
1913
|
-
reason: declineReason,
|
|
1914
|
-
...ident(),
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
|
-
return comp.compacted === true;
|
|
1918
|
-
}
|
|
1919
|
-
catch (err) {
|
|
1920
|
-
if (turnSignal?.aborted === true && !prepared.abortController.signal.aborted) {
|
|
1921
|
-
const interruptReason = lane === "rejection"
|
|
1922
|
-
? "prompt-too-long recovery pass cut short by a turn interrupt"
|
|
1923
|
-
: "guard-chain forced compaction cut short by a turn interrupt";
|
|
1924
|
-
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: interruptReason, ...ident() });
|
|
1925
|
-
return false;
|
|
1926
|
-
}
|
|
1927
|
-
compactionBreaker.failures += 1;
|
|
1928
|
-
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "compaction", sessionId: prepared.sessionId });
|
|
1929
|
-
const msg = String(err instanceof Error ? err.message : err);
|
|
1930
|
-
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
|
|
1931
|
-
return false;
|
|
1932
|
-
}
|
|
1933
|
-
};
|
|
1934
|
-
prepared.harness.setLoopRecovery({
|
|
1935
|
-
truncatedOutput: {},
|
|
1936
|
-
malformedToolUse: {},
|
|
1937
|
-
thinkingOnly: {},
|
|
1938
|
-
degenerateOutput: { detect: (m) => isDegenerateCutMessage(m) },
|
|
1939
|
-
promptTooLong: {
|
|
1940
|
-
recover: async (attempt, turnSignal) => {
|
|
1941
|
-
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
|
|
1942
|
-
return false;
|
|
1943
|
-
if (turnSignal?.aborted === true)
|
|
1944
|
-
return false;
|
|
1945
|
-
if (prepared.microCompact.clearOnRejection && attempt === 1) {
|
|
1946
|
-
const proj = prepared.microCompact.projectionRef.current;
|
|
1947
|
-
const compactionAvailable = (spec.compaction?.enabled ?? true) && compactionBreaker.failures < MAX_CONSECUTIVE_COMPACTION_FAILURES;
|
|
1948
|
-
const declineArm = compactionAvailable ? "forced_compaction" : "none";
|
|
1949
|
-
const plan = proj === undefined
|
|
1950
|
-
? { declined: "no_candidates" }
|
|
1951
|
-
: planRejectionClears(proj.messages, {
|
|
1952
|
-
keyOf: proj.keyOf,
|
|
1953
|
-
...(prepared.microCompact.offloadPersist ? { offload: { persist: prepared.microCompact.offloadPersist } } : {}),
|
|
1954
|
-
});
|
|
1955
|
-
if ("declined" in plan) {
|
|
1956
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1957
|
-
kind: "context.mc_null",
|
|
1958
|
-
version: 1,
|
|
1959
|
-
taskId: rs.telemetry.taskId,
|
|
1960
|
-
reason: plan.declined,
|
|
1961
|
-
nextArm: declineArm,
|
|
1962
|
-
ts: Date.now(),
|
|
1963
|
-
}));
|
|
1964
|
-
}
|
|
1965
|
-
else {
|
|
1966
|
-
for (const e of plan.cleared) {
|
|
1967
|
-
prepared.microCompact.ledger.entries.set(e.key, { marker: e.marker, groupCount: e.groupCount, fp: e.fp });
|
|
1968
|
-
}
|
|
1969
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1970
|
-
kind: "context.mc_clear",
|
|
1971
|
-
version: 1,
|
|
1972
|
-
taskId: rs.telemetry.taskId,
|
|
1973
|
-
clearedCount: plan.cleared.length,
|
|
1974
|
-
tokensSavedEstimate: plan.tokensSavedEstimate,
|
|
1975
|
-
trigger: "refusal",
|
|
1976
|
-
ts: Date.now(),
|
|
1977
|
-
}));
|
|
1978
|
-
return true;
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
return runForcedCompactionPass("rejection", turnSignal);
|
|
1982
|
-
},
|
|
1983
|
-
},
|
|
1984
|
-
});
|
|
1985
|
-
prepared.microCompact.inTurnCompactionRef.current = async (turnSignal, anchoredEstimate) => {
|
|
1986
|
-
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
|
|
1987
|
-
return false;
|
|
1988
|
-
if (turnSignal?.aborted === true)
|
|
1989
|
-
return false;
|
|
1990
|
-
if (anchoredEstimate !== undefined && rs.counters.compactionFloor > 0 && anchoredEstimate < rs.counters.compactionFloor) {
|
|
1991
|
-
const floor = rs.counters.compactionFloor;
|
|
1992
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.suppressed", version: 1, taskId: rs.telemetry.taskId, estTokens: anchoredEstimate, floor, ts: Date.now() }));
|
|
1993
|
-
queue.push({ type: "compaction_outcome", outcome: "suppressed", trigger: "forced", ...ident() });
|
|
1994
|
-
return false;
|
|
1995
|
-
}
|
|
1996
|
-
return runForcedCompactionPass("guard", turnSignal);
|
|
1997
|
-
};
|
|
1998
595
|
const rapidRefill = createRapidRefillState();
|
|
1999
596
|
const drainManualCompact = (outcome) => {
|
|
2000
597
|
manualCompactRef.requested = false;
|
|
@@ -2016,102 +613,7 @@ export class Runner {
|
|
|
2016
613
|
},
|
|
2017
614
|
},
|
|
2018
615
|
});
|
|
2019
|
-
|
|
2020
|
-
const ref = prepared.gitStatusRef;
|
|
2021
|
-
const frame = ref.frame;
|
|
2022
|
-
if (frame === undefined)
|
|
2023
|
-
return;
|
|
2024
|
-
delete ref.mirrorOwed;
|
|
2025
|
-
const use = ref.overBudgetShrunk && frame.shrunk !== undefined
|
|
2026
|
-
? { kind: "degraded", body: frame.shrunk.body, hash: frame.shrunk.hash }
|
|
2027
|
-
: { kind: frame.kind, body: frame.body, hash: frame.hash };
|
|
2028
|
-
const wrapped = wrapGitFrame(use.body, prepared.reminderMark);
|
|
2029
|
-
try {
|
|
2030
|
-
const entryId = await prepared.session.appendMessage({
|
|
2031
|
-
role: "user",
|
|
2032
|
-
content: [{ type: "text", text: wrapped }],
|
|
2033
|
-
timestamp: Date.now(),
|
|
2034
|
-
engineMinted: true,
|
|
2035
|
-
});
|
|
2036
|
-
ref.announced = { kind: use.kind, hash: use.hash, entryId };
|
|
2037
|
-
ref.protectedText = wrapped;
|
|
2038
|
-
if (use.kind === "full" && frame.shrunk !== undefined) {
|
|
2039
|
-
ref.wrappedShrink = { find: wrapped, replace: wrapGitFrame(frame.shrunk.body, prepared.reminderMark) };
|
|
2040
|
-
}
|
|
2041
|
-
else {
|
|
2042
|
-
delete ref.wrappedShrink;
|
|
2043
|
-
}
|
|
2044
|
-
queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[use.kind], ...ident() });
|
|
2045
|
-
rs.attach.attachmentsInjected += 1;
|
|
2046
|
-
try {
|
|
2047
|
-
await prepared.session.appendGitAnnouncement?.({ kind: use.kind, hash: use.hash, entryId });
|
|
2048
|
-
}
|
|
2049
|
-
catch (mirrorErr) {
|
|
2050
|
-
try {
|
|
2051
|
-
this.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; next leg re-announces): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
2052
|
-
}
|
|
2053
|
-
catch {
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
catch (err) {
|
|
2058
|
-
ref.announced = { kind: use.kind, hash: use.hash, pending: true };
|
|
2059
|
-
try {
|
|
2060
|
-
this.deps.onError?.(new Error(`git status frame re-assert append failed (pending; retried at the next boundary): ${err instanceof Error ? err.message : String(err)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
2061
|
-
}
|
|
2062
|
-
catch {
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
};
|
|
2066
|
-
const flushGitMirror = async () => {
|
|
2067
|
-
const owed = prepared.gitStatusRef.mirrorOwed;
|
|
2068
|
-
if (owed === undefined)
|
|
2069
|
-
return;
|
|
2070
|
-
if (prepared.gitStatusRef.overBudgetShrunk && owed.kind === "full") {
|
|
2071
|
-
delete prepared.gitStatusRef.mirrorOwed;
|
|
2072
|
-
return;
|
|
2073
|
-
}
|
|
2074
|
-
delete prepared.gitStatusRef.mirrorOwed;
|
|
2075
|
-
try {
|
|
2076
|
-
await prepared.session.appendGitAnnouncement?.(owed);
|
|
2077
|
-
}
|
|
2078
|
-
catch (mirrorErr) {
|
|
2079
|
-
prepared.gitStatusRef.mirrorOwed = owed;
|
|
2080
|
-
try {
|
|
2081
|
-
this.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; retried at the next serialization point): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
2082
|
-
}
|
|
2083
|
-
catch {
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
};
|
|
2087
|
-
const unsubGitRetry = prepared.harness.on("turn_boundary", async () => {
|
|
2088
|
-
await flushGitMirror();
|
|
2089
|
-
if (prepared.gitStatusRef.announced?.pending === true)
|
|
2090
|
-
await prepared.gitStatusRef.reassert?.();
|
|
2091
|
-
return undefined;
|
|
2092
|
-
});
|
|
2093
|
-
const unsubBoundary = prepared.harness.on("turn_boundary", onTurnBoundary);
|
|
2094
|
-
const unsub = prepared.harness.subscribe((event) => {
|
|
2095
|
-
switch (event.type) {
|
|
2096
|
-
case "message_update":
|
|
2097
|
-
onMessageUpdate(event);
|
|
2098
|
-
break;
|
|
2099
|
-
case "message_end":
|
|
2100
|
-
onMessageEnd(event);
|
|
2101
|
-
break;
|
|
2102
|
-
case "tool_execution_start":
|
|
2103
|
-
onToolStart(event);
|
|
2104
|
-
break;
|
|
2105
|
-
case "tool_execution_end":
|
|
2106
|
-
onToolEnd(event);
|
|
2107
|
-
break;
|
|
2108
|
-
case "turn_end":
|
|
2109
|
-
onTurnEnd();
|
|
2110
|
-
break;
|
|
2111
|
-
default:
|
|
2112
|
-
break;
|
|
2113
|
-
}
|
|
2114
|
-
});
|
|
616
|
+
const { flushGitMirror, unsubGitRetry, unsubBoundary, unsub } = runGitLane({ prepared, queue, rs, ident, onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, onTurnBoundary, runner: this.depsSeat });
|
|
2115
617
|
let final;
|
|
2116
618
|
let threw;
|
|
2117
619
|
let abortedLive = false;
|