@sema-agent/core 7.6.0 → 7.6.1
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 +26 -0
- package/dist/agents/agent-transcript-tool.d.ts +2 -2
- package/dist/agents/cascade.d.ts +2 -3
- package/dist/agents/repair-loop.d.ts +2 -2
- package/dist/agents/retain-ledger.d.ts +2 -3
- package/dist/agents/send-message-tool.d.ts +2 -2
- package/dist/agents/session-util.d.ts +2 -2
- package/dist/agents/subagent.d.ts +3 -4
- package/dist/agents/teacher.d.ts +2 -2
- package/dist/agents/team.d.ts +2 -2
- package/dist/agents/verify.d.ts +5 -6
- package/dist/core/agent-definition.d.ts +172 -0
- package/dist/core/agent-definition.js +1 -0
- package/dist/core/delegation-frames.d.ts +298 -0
- package/dist/core/delegation-frames.js +21 -0
- package/dist/core/engine-notice.d.ts +555 -0
- package/dist/core/engine-notice.js +55 -0
- package/dist/core/gate-fold.d.ts +12 -0
- package/dist/core/gate-fold.js +158 -0
- package/dist/core/gate-lanes.d.ts +93 -0
- package/dist/core/gate-lanes.js +626 -0
- package/dist/core/hands-band.d.ts +134 -0
- package/dist/core/hands-band.js +1 -0
- package/dist/core/hooks.d.ts +20 -101
- package/dist/core/hooks.js +53 -854
- package/dist/core/mcp-failure.d.ts +43 -5
- package/dist/core/mcp-failure.js +31 -14
- package/dist/core/mcp-server-spec.d.ts +217 -0
- package/dist/core/mcp-server-spec.js +1 -0
- package/dist/core/model-seat.d.ts +99 -0
- package/dist/core/model-seat.js +1 -0
- package/dist/core/reminder-mint.d.ts +10 -0
- package/dist/core/reminder-mint.js +3 -0
- package/dist/core/runner/contracts.d.ts +382 -6
- package/dist/core/runner/gate-exit.d.ts +177 -9
- package/dist/core/runner/gate-exit.js +70 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
- package/dist/core/runner/prepare-task.d.ts +2 -2
- package/dist/core/runner/runtask.d.ts +4 -71
- package/dist/core/runner/runtask.js +14 -5
- package/dist/core/runner-deps.d.ts +1416 -0
- package/dist/core/runner-deps.js +1 -0
- package/dist/core/runtime-caps.d.ts +164 -0
- package/dist/core/runtime-caps.js +1 -0
- package/dist/core/task-event.d.ts +910 -0
- package/dist/core/task-event.js +1 -0
- package/dist/core/task-limits.d.ts +110 -0
- package/dist/core/task-limits.js +1 -0
- package/dist/core/task-result.d.ts +809 -0
- package/dist/core/task-result.js +1 -0
- package/dist/core/task-spec.d.ts +1370 -0
- package/dist/core/task-spec.js +1 -0
- package/dist/core/task-stream.d.ts +382 -0
- package/dist/core/task-stream.js +1 -0
- package/dist/core/tool-spec.d.ts +1174 -0
- package/dist/core/tool-spec.js +1 -0
- package/dist/core/types.d.ts +26 -7691
- package/dist/core/types.js +2 -76
- package/dist/core/warm-resume.d.ts +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/orchestration/goal.d.ts +2 -2
- package/dist/orchestration/run-spec.d.ts +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +3 -3
- package/dist/orchestration/workflow.d.ts +4 -4
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/scenarios/teacher-quickstart.d.ts +2 -2
- package/dist/server/http.d.ts +2 -2
- package/dist/stores/file/fs-atomic.d.ts +88 -12
- package/dist/stores/file/fs-atomic.js +184 -55
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +1 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { screenGateOutcome, SETTLEMENT_IS_REFUSAL } from "../gate-outcome.js";
|
|
2
|
-
import { engineSettlementOf } from "../tool-policy.js";
|
|
2
|
+
import { decisionText, engineSettlementOf } from "../tool-policy.js";
|
|
3
|
+
import { inlineUntrusted } from "../untrusted-text.js";
|
|
4
|
+
import { formatHookFeedback } from "../reminder-mint.js";
|
|
3
5
|
export function mintGateOutcome(facts) {
|
|
4
6
|
const settlement = facts.settled !== undefined ? Object.freeze({ ...facts.settled.settlement, who: Object.freeze({ ...facts.settled.settlement.who }) }) : undefined;
|
|
5
7
|
const outcome = Object.freeze({
|
|
@@ -53,3 +55,70 @@ export function createSettlementLedger(call) {
|
|
|
53
55
|
},
|
|
54
56
|
};
|
|
55
57
|
}
|
|
58
|
+
export function cloneObserverInput(input) {
|
|
59
|
+
try {
|
|
60
|
+
return structuredClone(input);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
if (Array.isArray(input))
|
|
64
|
+
return [...input];
|
|
65
|
+
if (input !== null && typeof input === "object")
|
|
66
|
+
return { ...input };
|
|
67
|
+
return input;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function traceHookCrash(input, err, notifier) {
|
|
71
|
+
notifier.notify(() => input.onHookError?.(err), "toolGate.onHookError");
|
|
72
|
+
}
|
|
73
|
+
const PARK_FAILURE_CAUSE_MAX = 600;
|
|
74
|
+
function withParkFailureCause(reason, parkFailed) {
|
|
75
|
+
if (parkFailed === undefined)
|
|
76
|
+
return reason;
|
|
77
|
+
return (`${reason} — note: a durable approval park was attempted for this call FIRST and could not be minted ` +
|
|
78
|
+
`(${inlineUntrusted(parkFailed, PARK_FAILURE_CAUSE_MAX)}), so the refusal above is what the fallback had ` +
|
|
79
|
+
`left to say, not the reason the call stopped.`);
|
|
80
|
+
}
|
|
81
|
+
export async function engineFailClosedExit(pass, reason) {
|
|
82
|
+
const { input, toolName, toolCallId, notifyPermissionDenied } = pass;
|
|
83
|
+
const gate = directDeny("hook");
|
|
84
|
+
await notifyPermissionDenied({ toolName, input: cloneObserverInput(pass.currentInput), toolCallId, reason, gate, ...(input.identity !== undefined ? { identity: input.identity } : {}) });
|
|
85
|
+
return { block: true, reason: formatHookFeedback(reason, input.reminderMark), gate, preToolContext: pass.preToolContext };
|
|
86
|
+
}
|
|
87
|
+
export function hookDenyExit(pass, r) {
|
|
88
|
+
const { input, toolName } = pass;
|
|
89
|
+
return {
|
|
90
|
+
block: true,
|
|
91
|
+
reason: formatHookFeedback(decisionText(r) ?? `tool "${toolName}" blocked by a PreToolUse hook`, input.reminderMark),
|
|
92
|
+
gate: directDeny("hook"),
|
|
93
|
+
preToolContext: pass.preToolContext,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export async function exitGate(pass) {
|
|
97
|
+
const { input, event, toolName, toolCallId, ledger, notifyPermissionDenied } = pass;
|
|
98
|
+
if (pass.decision.action === "deny") {
|
|
99
|
+
const denyReason = withParkFailureCause(decisionText(pass.decision) ?? `tool "${toolName}" denied by policy`, pass.parkFailed);
|
|
100
|
+
if (pass.decision.updatedInput !== undefined) {
|
|
101
|
+
pass.currentInput = pass.decision.updatedInput;
|
|
102
|
+
}
|
|
103
|
+
if (pass.deniedBy === undefined)
|
|
104
|
+
throw new Error(`the tool gate refused "${toolName}" without a refusing layer — every deny site attributes itself`);
|
|
105
|
+
const gate = mintGateOutcome({ deniedBy: pass.deniedBy, ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}) });
|
|
106
|
+
await notifyPermissionDenied({ toolName, input: cloneObserverInput(pass.currentInput), toolCallId, reason: denyReason, gate, ...(input.identity !== undefined ? { identity: input.identity } : {}) });
|
|
107
|
+
return {
|
|
108
|
+
block: true,
|
|
109
|
+
reason: formatHookFeedback(denyReason, input.reminderMark),
|
|
110
|
+
gate,
|
|
111
|
+
preToolContext: pass.preToolContext,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (pass.decision.action === "allow") {
|
|
115
|
+
const rw = pass.decision.updatedInput !== undefined ? pass.decision.updatedInput : pass.policyRewrite;
|
|
116
|
+
if (rw !== undefined)
|
|
117
|
+
pass.currentInput = rw;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
updatedInput: pass.currentInput === event.input ? undefined : pass.currentInput,
|
|
121
|
+
gate: mintGateOutcome(ledger.settled !== undefined ? { settled: ledger.settled } : {}),
|
|
122
|
+
preToolContext: pass.preToolContext,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -22,7 +22,6 @@ import type { AgentHarness, AgentTool, ThinkingLevel } from "../../internal/harn
|
|
|
22
22
|
import type { Model } from "../../internal/llm.js";
|
|
23
23
|
import { forkGovernanceDenial } from "../../agents/subagent.js";
|
|
24
24
|
import type { PromptEpochArtifact } from "../../prompt-assembly/artifact.js";
|
|
25
|
-
import { createRunWorkflowTool } from "../../orchestration/run-workflow-tool.js";
|
|
26
25
|
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
27
26
|
import { type AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
28
27
|
import { type AutoModeDecider, type AutoModeDenialTracker } from "../auto-mode.js";
|
|
@@ -33,11 +32,8 @@ import type { OnAsk } from "../tool-policy.js";
|
|
|
33
32
|
import type { RunnerDeps, RuntimeCaps, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
34
33
|
import type { AutoModeArmReason } from "../wiring-manifest.js";
|
|
35
34
|
import type { ReadFace } from "../../tools/fs/index.js";
|
|
36
|
-
import type { InheritedGate, Prepared, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
35
|
+
import type { InheritedGate, Prepared, RunInternals, RunnerSelfSeat, ToolFaceSnapshot } from "./contracts.js";
|
|
37
36
|
import { type BlockedRef } from "./synthetic-tools.js";
|
|
38
|
-
/** The runner the Workflow mount accepts — spelled as the factory's own option type so this phase names no runner
|
|
39
|
-
* module of its own (the narrow `RunnerSelfSeat` contract is a design decision still open; see the layering registry). */
|
|
40
|
-
type WorkflowMountRunner = Parameters<typeof createRunWorkflowTool>[0]["runner"];
|
|
41
37
|
export interface PrepareCapsAndWorkflowInput {
|
|
42
38
|
/** borrowed-readonly — the REBOUND spec, whole: the roster map reads `tools`; the mounts read `enableBlockedReport`,
|
|
43
39
|
* `enablePlanMode`, `interactiveTools`, `checkpointStore`; the caps/compliance stations read `principal`, `selfOrchestration`,
|
|
@@ -113,7 +109,7 @@ export interface PrepareCapsAndWorkflowInput {
|
|
|
113
109
|
peerSendMessageBuiltIn: boolean;
|
|
114
110
|
/** borrowed-readonly — the trusted Runner self-reference the Workflow mount executes children through; undefined when
|
|
115
111
|
* prepareTask runs standalone (then Workflow is simply not mounted). */
|
|
116
|
-
runnerSelf:
|
|
112
|
+
runnerSelf: RunnerSelfSeat | undefined;
|
|
117
113
|
/** borrowed-readonly — LATE-BOUND: the memory-engine session; the Workflow mount's capture-floor getter reads it at ITS call time. */
|
|
118
114
|
memoryEngineSession: () => Prepared["memoryEngineSession"];
|
|
119
115
|
/** borrowed-readonly — LATE-BOUND: the adopted center artifact; the Workflow mount's two center getters read it at THEIR call time. */
|
|
@@ -167,4 +163,3 @@ export interface PrepareCapsAndWorkflowResult {
|
|
|
167
163
|
}
|
|
168
164
|
/** The M4 phase body — prepareTask's caps-and-workflow stretch, verbatim (see the module header). */
|
|
169
165
|
export declare function prepareCapsAndWorkflow(input: PrepareCapsAndWorkflowInput): Promise<PrepareCapsAndWorkflowResult>;
|
|
170
|
-
export {};
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import type { AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
|
|
2
|
-
import { createSendMessageTool } from "../../agents/send-message-tool.js";
|
|
3
2
|
import { type PeerLaneRefs } from "../../agents/peer-session-drain.js";
|
|
4
3
|
import type { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
5
4
|
import { type WorktreeSessionRef } from "../../tools/worktree.js";
|
|
6
5
|
import type { CwdRef } from "../../tools/fs/index.js";
|
|
7
6
|
import { type ToolResultStore } from "../tool-result-store.js";
|
|
8
7
|
import type { RunnerDeps, TaskSpec, ToolEffect, ToolExecuteContext } from "../types.js";
|
|
9
|
-
import type { PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
10
|
-
/** The runner the SendMessage / AgentTranscript mounts accept — spelled as the factory's own option type so this phase
|
|
11
|
-
* names no runner module of its own (the narrow `RunnerSelfSeat` contract is a design decision still open). */
|
|
12
|
-
type DelegationMountRunner = Parameters<typeof createSendMessageTool>[0]["runner"];
|
|
8
|
+
import type { PrepareResume, RunInternals, RunnerSelfSeat, ToolFaceSnapshot } from "./contracts.js";
|
|
13
9
|
export interface PrepareDelegationSurfaceInput {
|
|
14
10
|
/** borrowed-readonly — the REBOUND spec. Read: `tools` (the shadow checks and the revival spawner's delegation-tool
|
|
15
11
|
* lookup), `oneShot` (the read/kill faces' completion promises), `retainBackgroundProcesses` (the Monitor receipt),
|
|
@@ -36,7 +32,7 @@ export interface PrepareDelegationSurfaceInput {
|
|
|
36
32
|
executionEnv: ExecutionEnv;
|
|
37
33
|
/** borrowed-readonly — the trusted Runner self-reference the SendMessage / AgentTranscript mounts execute through; undefined
|
|
38
34
|
* when prepareTask runs standalone (then neither mounts). */
|
|
39
|
-
runnerSelf:
|
|
35
|
+
runnerSelf: RunnerSelfSeat | undefined;
|
|
40
36
|
/** borrowed-readonly — the background/workflow door's first half: the hands mount decided a real shell with write hands. */
|
|
41
37
|
backgroundTaskToolsActive: boolean;
|
|
42
38
|
/** borrowed-readonly — the door's second half: the Workflow tool is on the roster. */
|
|
@@ -101,4 +97,3 @@ export interface PrepareDelegationSurfaceResult {
|
|
|
101
97
|
}
|
|
102
98
|
/** The M6 phase body — prepareTask's delegation-surface stretch, verbatim (see the module header). */
|
|
103
99
|
export declare function prepareDelegationSurface(input: PrepareDelegationSurfaceInput): PrepareDelegationSurfaceResult;
|
|
104
|
-
export {};
|
|
@@ -7,7 +7,7 @@ export { DEFAULT_IRREVERSIBLE_SCOPE, checkpointScopeOf, placementValueOrAbsent }
|
|
|
7
7
|
export { mcpManifestEntries } from "./prepare-wiring-manifest.js";
|
|
8
8
|
export { __resetMaterializeEnvAnnouncements } from "./prepare-tool-disclosure-mount.js";
|
|
9
9
|
export { fileHistoryFilesystemIdentity, resolveFileHistoryScope } from "./prepare-file-history.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { RunnerSelfSeat } from "./contracts.js";
|
|
11
11
|
import type { Prepared, PrepareResume, RunInternals } from "./contracts.js";
|
|
12
12
|
export type { FileHistoryBoundarySeat, InheritedGate, Prepared, PreparedMicroCompact, PrepareResume, ResolvedWorkspace, RunInternals, UsageGovernance } from "./contracts.js";
|
|
13
13
|
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
@@ -107,7 +107,7 @@ export declare function prepareTask(spec: TaskSpec, deps: RunnerDeps, sessions:
|
|
|
107
107
|
/** design/98 §3.1 (S8c): a TRUSTED self-reference to the Runner, passed by the Runner itself (never a
|
|
108
108
|
* TaskSpec field) so the `run_workflow` tool can execute child tasks via `runner.runTask`. Undefined when
|
|
109
109
|
* prepareTask is exercised standalone (then run_workflow is simply not mounted). */
|
|
110
|
-
runnerSelf?:
|
|
110
|
+
runnerSelf?: RunnerSelfSeat,
|
|
111
111
|
/** #499 — carrier for the id minted below, so a prepare that THROWS still lets its caller name the
|
|
112
112
|
* run (prepare emits run-scoped notices of its own; each needs a terminal that names it). Same seat
|
|
113
113
|
* shape the Runner uses for the generated taskId/sessionId. */
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type Model } from "../../internal/llm.js";
|
|
2
|
-
import { type
|
|
3
|
-
import type { RunInternals } from "./prepare-task.js";
|
|
2
|
+
import { type CheckpointToken, type ResumeOutcome } from "../checkpoint-store.js";
|
|
4
3
|
import { type TaskOutcome } from "../task-outcome.js";
|
|
5
4
|
import { type SideQuerySpec, type SideQueryResult } from "../side-query.js";
|
|
6
5
|
import type { SessionStore } from "../session.js";
|
|
@@ -8,74 +7,8 @@ import { type RecoveredOrphan } from "../session-reconcile.js";
|
|
|
8
7
|
import type { GateOutcome } from "../gate-outcome.js";
|
|
9
8
|
import { type McpDelivered } from "../mcp-failure.js";
|
|
10
9
|
import type { AgentDefinition, ModelRef, RunnerDeps, TaskEvent, TaskResult, TaskSpec, TaskStream } from "../types.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* hooks cannot be reconstructed from a checkpoint token (the session stores neither tool implementations
|
|
14
|
-
* nor the hand band), so the caller's trusted control plane re-supplies the same {@link TaskSpec} it ran
|
|
15
|
-
* with — minus the conversation bits: `sessionId` comes from the checkpoint and `objective` is replaced by
|
|
16
|
-
* an internally-generated continuation, so both are omitted.
|
|
17
|
-
*/
|
|
18
|
-
export type ResumeTaskConfig = Omit<TaskSpec, "objective" | "sessionId">;
|
|
19
|
-
/** design/45 resume plan threaded from {@link Runner.resume} into the shared run loop. */
|
|
20
|
-
interface ResumeRun {
|
|
21
|
-
cp: Checkpoint;
|
|
22
|
-
/**
|
|
23
|
-
* RB-152 (2026-07-25, 按面收口): did the APPROVED ACTION actually begin executing?
|
|
24
|
-
*
|
|
25
|
-
* The reopen compensation used to key on an error-code whitelist (`resume.env_failed` /
|
|
26
|
-
* `resume.session_not_found` / `resume.tool_unavailable`). That is a proxy for the thing that actually
|
|
27
|
-
* matters, and every time a NEW way to exit without running the action appeared, the whitelist did not
|
|
28
|
-
* know about it: a caller's abort landing anywhere between the CAS and `tool.execute` — across
|
|
29
|
-
* `SessionStore.acquire` (pluggable, cross-network on a durable backend), the MCP reconnect, the env
|
|
30
|
-
* rebuild, `resumeVM`, tool materialization — produced a `failed` result with NO errorCode, no reopen,
|
|
31
|
-
* and a human approval consumed for work that never happened. Two rounds of fixes (RB-77, RB-109) each
|
|
32
|
-
* moved that window rather than closing it, because both extended the whitelist.
|
|
33
|
-
*
|
|
34
|
-
* This binds the compensation to the FACT instead: set the instant before `tool.execute` for the gated
|
|
35
|
-
* call (the same `onExecuteStart` signal the orphan-reconcile split already trusts). Every terminal —
|
|
36
|
-
* throw OR returned result — asks one question: was the checkpoint consumed while this stayed false?
|
|
37
|
-
*/
|
|
38
|
-
pendingActionStarted?: boolean;
|
|
39
|
-
/** Validated against `cp.gate.kind` at the resume entry: human/irreversible_ask→`policy_ask`,
|
|
40
|
-
* resource_limit→`resource_limit` (design/74), needs_review→`dry_run_review` (design/76 §2.5),
|
|
41
|
-
* plan_review→`plan_review` (design/80 D-B). The gate-match guard in `resumeStream` enforces the
|
|
42
|
-
* correlation. design/144 §3: `wake` is the NON-GATE arm — only a checkpoint awaiting NO gate
|
|
43
|
-
* decision passes the resume entry with it (gate purity, `wake.gate_pending`); the run loop skips
|
|
44
|
-
* `applyResumeDecision` for it (no pending action to resolve) and re-enters via the continuation +
|
|
45
|
-
* pendingSteer tail alone. */
|
|
46
|
-
outcome: Extract<ResumeOutcome, {
|
|
47
|
-
gate: "policy_ask" | "resource_limit" | "dry_run_review" | "plan_review" | "wake";
|
|
48
|
-
}>;
|
|
49
|
-
/** design/144 §3 (X5) — the wake's own operator message, validated (`validatePendingSteer`) at the
|
|
50
|
-
* resume entry and carried SEPARATELY from the checkpoint's parked `pendingSteer`: a message-bearing
|
|
51
|
-
* wake of a checkpoint that ALSO holds a parked steer must deliver BOTH (park order: parked first,
|
|
52
|
-
* wake message second), each under its own trusted framing — the old merge-into-the-slot shape
|
|
53
|
-
* silently DISPLACED the parked (undelivered) supervisor steer. Wake outcomes only. */
|
|
54
|
-
wakeMessage?: Omit<PendingSteerEntry, "seq">;
|
|
55
|
-
/** design/373 §4.3 (D2) — the userPromptSubmit screen's `additionalContext` for {@link wakeMessage},
|
|
56
|
-
* captured at the resume ENTRY (the message is screened once, pre-CAS, on the resuming process's
|
|
57
|
-
* hook) and delivered by the drain as the engine's own reminder AHEAD of the wake frame — carrying
|
|
58
|
-
* it forward is what keeps the hook single-run (re-screening at the drain would be the double-run
|
|
59
|
-
* §4.3-3 reserves for the cross-process parked leg). Present only when a wake message passed a
|
|
60
|
-
* screen that supplied context. */
|
|
61
|
-
wakeMessageHookContext?: string;
|
|
62
|
-
/** Compensation hook (design/45/49): called iff the resumed run fails with `resume.env_failed` (post-CAS
|
|
63
|
-
* workspace `resumeVM` failed) OR `resume.tool_unavailable` (P-7: the approved tool vanished) — in both
|
|
64
|
-
* the CAS already consumed the checkpoint but the pending action never ran. `resumeStream` supplies a
|
|
65
|
-
* closure that reopens the checkpoint (`resolved → pending`) so a retry re-resumes the SAME suspended work
|
|
66
|
-
* instead of losing it to a forced "re-initiate". design/80 D-1 (reopen-by-reason): the `reason` is
|
|
67
|
-
* recorded on the reopened row so the next re-resume validates per reason — an `env_failed` reopen must
|
|
68
|
-
* replay the persisted winner (a system retry of the approved action), while a `tool_unavailable` reopen
|
|
69
|
-
* lets a human re-decide with the tool present (a fresh decision is allowed — preserves P-7). */
|
|
70
|
-
onEnvRestoreFailed?: (reason: ReopenReason) => Promise<void>;
|
|
71
|
-
/** RB-471/FR-C1 — set in the run body right after `applyResumeDecision` completes: the negative-
|
|
72
|
-
* decision twin of `pendingActionStarted`. A reject/deny consumes its gate BY BEING DELIVERED, and
|
|
73
|
-
* this bit is the delivery fact — every throw-arm cause (prepare failure, `session_not_found`, a
|
|
74
|
-
* pre-delivery abort) and the walltime-exhausted settle fire BEFORE it is set, so an undelivered
|
|
75
|
-
* negative decision still reopens as `env_failed` (the retry replays the persisted decision; it
|
|
76
|
-
* never re-asks — design/80 D-1), closing the RB-152/RB-70 loss class the first RB-471 cut reopened. */
|
|
77
|
-
decisionDelivered?: boolean;
|
|
78
|
-
}
|
|
10
|
+
import type { ResumeRun, ResumeTaskConfig, RunInternals, RunnerSelfSeat } from "./contracts.js";
|
|
11
|
+
export type { ResumeTaskConfig } from "./contracts.js";
|
|
79
12
|
/** The `tool_end` body fields projected from a harness tool result — output/truncated/totalChars via
|
|
80
13
|
* {@link toolOutputFrom} and the CC card via {@link structuredFrom}. Single construction point for BOTH
|
|
81
14
|
* the live loop's frames and the resumed batch's frames (`resolvePendingCall` + the deferred-sibling
|
|
@@ -145,7 +78,7 @@ export declare function raceUntilDeadline<T>(p: Promise<T>, deadline: number): P
|
|
|
145
78
|
* A stateless task runner. Holds shared deps (the external brain, model catalog) and an
|
|
146
79
|
* in-memory session store so that passing a `sessionId` continues a prior conversation.
|
|
147
80
|
*/
|
|
148
|
-
export declare class Runner {
|
|
81
|
+
export declare class Runner implements RunnerSelfSeat {
|
|
149
82
|
private deps;
|
|
150
83
|
readonly sessions: SessionStore;
|
|
151
84
|
/** Per-sessionId serialization so two tasks never mutate one session concurrently. */
|
|
@@ -6,7 +6,7 @@ import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNoti
|
|
|
6
6
|
import { planRejectionClears, resolveTriggerWindow } from "../context-edit.js";
|
|
7
7
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
|
|
8
8
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
9
|
-
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, LEGACY_PENDING_STEER_INPUT_ID, MAX_STEER_INPUT_ID_CHARS, validatePendingSteer, winnerFromOutcome, realApprovalOrgFact } from "../checkpoint-store.js";
|
|
9
|
+
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, resolveCheckpointStore, LEGACY_PENDING_STEER_INPUT_ID, MAX_STEER_INPUT_ID_CHARS, validatePendingSteer, winnerFromOutcome, realApprovalOrgFact } from "../checkpoint-store.js";
|
|
10
10
|
import { GIT_STATUS_ECHO_PREVIEW, branchCarriesVisiblePositiveGitFrame, newestEngineGitFrame, stripGitStatusUnits } from "./git-status-frame.js";
|
|
11
11
|
import { gitFrameContextVisible, normalizeGitAnnouncement } from "../../internal/harness.js";
|
|
12
12
|
import { engineVersion } from "../version.js";
|
|
@@ -38,7 +38,9 @@ import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
|
38
38
|
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
39
39
|
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, TOTAL_TOKENS_REMINDER_DEFAULT_MODE, AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, advanceCadenceClock, agentListingDeltaHeader, attachmentEnvelopeTags, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpDroppedTools, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, selectMcpDroppedBatch, renderSkillsListingDelta, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
40
40
|
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
41
|
-
import { effectiveDelegationFacts,
|
|
41
|
+
import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
|
|
42
|
+
import { gatedCallIdOf } from "./park-commit.js";
|
|
43
|
+
import { placementValueOrAbsent } from "./checkpoint-scope.js";
|
|
42
44
|
import { SessionReadFileStates } from "./prepare-hands-readface.js";
|
|
43
45
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
44
46
|
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
|
|
@@ -1480,6 +1482,11 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1480
1482
|
};
|
|
1481
1483
|
return { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove };
|
|
1482
1484
|
}
|
|
1485
|
+
const REFUSED_DECIDE_KEYS = [
|
|
1486
|
+
["gateOutcome", "the settlement record is minted by core from the host's decision facts and the row's origin, never supplied"],
|
|
1487
|
+
["settledBy", 'a retired key — the host supplies decision FACTS as hostDecision{decidedBy: "person" | "sla_timeout", approver?} and core mints the settlement word from them'],
|
|
1488
|
+
["approver", "a retired key — the attribution rides the facts it belongs to, as hostDecision.approver"],
|
|
1489
|
+
];
|
|
1483
1490
|
export class Runner {
|
|
1484
1491
|
deps;
|
|
1485
1492
|
sessions;
|
|
@@ -4599,6 +4606,11 @@ export class Runner {
|
|
|
4599
4606
|
if (decision !== "allow" && decision !== "deny") {
|
|
4600
4607
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume decision "${describeSuppliedValue(decision)}" is outside the policy_ask domain — a decide is exactly "allow" or "deny"; refusing pre-CAS, the checkpoint stays pending`);
|
|
4601
4608
|
}
|
|
4609
|
+
for (const [key, why] of REFUSED_DECIDE_KEYS) {
|
|
4610
|
+
if (Reflect.get(decide, key) !== undefined) {
|
|
4611
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries \`${key}\` — ${why}; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4602
4614
|
const hostDecisionRaw = decide.hostDecision;
|
|
4603
4615
|
if (typeof hostDecisionRaw !== "object" || hostDecisionRaw === null) {
|
|
4604
4616
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries no hostDecision — a policy_ask decide names who decided it ({ decidedBy: "person" | "sla_timeout", approver? }); refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
@@ -4610,9 +4622,6 @@ export class Runner {
|
|
|
4610
4622
|
if (decidedBy === "sla_timeout" && decision === "allow") {
|
|
4611
4623
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries decision "allow" decided by "sla_timeout" — an elapsed SLA window cannot be what approved an action that EXECUTES; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
4612
4624
|
}
|
|
4613
|
-
if (decide.gateOutcome !== undefined) {
|
|
4614
|
-
throw new CheckpointError("checkpoint.invalid_outcome", "resume carries a gateOutcome — the settlement record is minted by core from the host's decision facts and the row's origin, never supplied; refusing pre-CAS, the checkpoint stays pending", { field: "hostDecision" });
|
|
4615
|
-
}
|
|
4616
4625
|
const attribution = screenApproverAttribution(hostDecisionRaw.approver);
|
|
4617
4626
|
if (attribution.defect !== undefined) {
|
|
4618
4627
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries an approver attribution this engine refuses: ${attribution.defect}; refusing pre-CAS, the checkpoint stays pending`, { field: "approver" });
|