@sema-agent/core 7.5.0 → 7.5.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/core/auto-mode.d.ts +9 -0
  3. package/dist/core/auto-mode.js +11 -0
  4. package/dist/core/checkpoint-store.js +5 -1
  5. package/dist/core/protocol-table.d.ts +5 -0
  6. package/dist/core/protocol-table.js +1 -0
  7. package/dist/core/runner/abort-race.d.ts +41 -0
  8. package/dist/core/runner/abort-race.js +38 -0
  9. package/dist/core/runner/checkpoint-scope.d.ts +44 -0
  10. package/dist/core/runner/checkpoint-scope.js +7 -0
  11. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  12. package/dist/core/runner/content-ask-bindings.d.ts +27 -0
  13. package/dist/core/runner/content-ask-bindings.js +1 -0
  14. package/dist/core/runner/contracts.d.ts +1990 -0
  15. package/dist/core/runner/contracts.js +1 -0
  16. package/dist/core/runner/denial-limit-arms.d.ts +24 -1
  17. package/dist/core/runner/denial-limit-arms.js +21 -0
  18. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  19. package/dist/core/runner/derived-route-fallback.js +16 -0
  20. package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
  21. package/dist/core/runner/inherited-ask-grants.js +29 -0
  22. package/dist/core/runner/park-commit.d.ts +108 -0
  23. package/dist/core/runner/park-commit.js +32 -0
  24. package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
  25. package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
  26. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  27. package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
  28. package/dist/core/runner/prepare-ask-lane.js +133 -0
  29. package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
  30. package/dist/core/runner/prepare-boundary-parks.js +169 -0
  31. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  32. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  33. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  34. package/dist/core/runner/prepare-context-lane.d.ts +119 -0
  35. package/dist/core/runner/prepare-context-lane.js +230 -0
  36. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  37. package/dist/core/runner/prepare-defer-classify.js +107 -0
  38. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  39. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  40. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  41. package/dist/core/runner/prepare-execution-env.js +86 -0
  42. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  43. package/dist/core/runner/prepare-file-history.js +383 -0
  44. package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
  45. package/dist/core/runner/prepare-gate-stations.js +290 -0
  46. package/dist/core/runner/prepare-hands-readface.d.ts +9 -11
  47. package/dist/core/runner/prepare-hands-readface.js +1 -1
  48. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  49. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  50. package/dist/core/runner/prepare-listings.d.ts +77 -0
  51. package/dist/core/runner/prepare-listings.js +76 -0
  52. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  53. package/dist/core/runner/prepare-lsp.js +27 -0
  54. package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
  55. package/dist/core/runner/prepare-memory-engine-session.js +233 -0
  56. package/dist/core/runner/prepare-memory.d.ts +1 -1
  57. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  58. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  59. package/dist/core/runner/prepare-park-ask.d.ts +164 -0
  60. package/dist/core/runner/prepare-park-ask.js +377 -0
  61. package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
  62. package/dist/core/runner/prepare-policy-chain.js +584 -0
  63. package/dist/core/runner/prepare-project-context.d.ts +119 -0
  64. package/dist/core/runner/prepare-project-context.js +148 -0
  65. package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
  66. package/dist/core/runner/prepare-prompt-assembly.js +162 -0
  67. package/dist/core/runner/prepare-prompt-inputs.d.ts +119 -0
  68. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  69. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  70. package/dist/core/runner/prepare-protocol-tools.js +179 -0
  71. package/dist/core/runner/prepare-question-face.d.ts +101 -0
  72. package/dist/core/runner/prepare-question-face.js +84 -0
  73. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  74. package/dist/core/runner/prepare-run-refs.js +39 -0
  75. package/dist/core/runner/prepare-safety-scan.d.ts +3 -7
  76. package/dist/core/runner/prepare-safety-scan.js +1 -2
  77. package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
  78. package/dist/core/runner/prepare-suspend-saga.js +308 -0
  79. package/dist/core/runner/prepare-task.d.ts +14 -1976
  80. package/dist/core/runner/prepare-task.js +182 -5162
  81. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  82. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  83. package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
  84. package/dist/core/runner/prepare-turn-wiring.js +201 -0
  85. package/dist/core/runner/prepare-wiring-manifest.d.ts +192 -0
  86. package/dist/core/runner/prepare-wiring-manifest.js +247 -0
  87. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -56
  88. package/dist/core/runner/prepare-workspace-restore.js +4 -38
  89. package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
  90. package/dist/core/runner/prompt-hash-salt.js +2 -0
  91. package/dist/core/runner/remote-env-retry.d.ts +29 -0
  92. package/dist/core/runner/remote-env-retry.js +16 -0
  93. package/dist/core/runner/rollback-stack.d.ts +32 -0
  94. package/dist/core/runner/rollback-stack.js +30 -0
  95. package/dist/core/runner/runtask.js +1 -1
  96. package/dist/core/runner/workspace-path.d.ts +33 -0
  97. package/dist/core/runner/workspace-path.js +22 -0
  98. package/dist/core/session.d.ts +12 -0
  99. package/dist/core/session.js +3 -0
  100. package/dist/core/tool-policy.d.ts +16 -0
  101. package/dist/core/tool-policy.js +3 -0
  102. package/dist/core/types.d.ts +2 -2
  103. package/dist/core/write-protect.js +3 -2
  104. package/package.json +6 -2
  105. /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
  106. /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
@@ -0,0 +1,84 @@
1
+ import { createAskUserQuestionTool, isLiveQuestionFace } from "../ask-question.js";
2
+ import { boundInputHashOf } from "../canonical-json.js";
3
+ import { resolveCheckpointStore } from "../checkpoint-store.js";
4
+ import { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
5
+ export { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
6
+ export function prepareQuestionFace(input) {
7
+ const { frozenOnQuestion, spec, deps, runtimeCaps, resolvedInteractionPosture, resume, sessionId, tools, inheritedUnavailableAsks } = input;
8
+ const onQuestion = frozenOnQuestion;
9
+ const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
10
+ const contentAskBindings = new Map();
11
+ const checkpointStore = resolveCheckpointStore(spec, deps);
12
+ const questionParkStoreWired = checkpointStore !== undefined;
13
+ const questionDurableMandateBinding = runtimeCaps?.forceDurableGate === true && questionParkStoreWired;
14
+ const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
15
+ mountedQuestionTool !== undefined &&
16
+ tools.includes(mountedQuestionTool) &&
17
+ !questionDurableMandateBinding &&
18
+ !inheritedUnavailableAsks().has(toolCallId);
19
+ const lateStrandedAnswers = [];
20
+ const discloseStrandedAnswers = (records, why) => {
21
+ if (records.length === 0)
22
+ return;
23
+ try {
24
+ deps.onError?.(new Error(`AskUserQuestion: ${records.length} question(s) were answered by a person but the call never ` +
25
+ `executed to collect the answer (${records.map((r) => `${r.toolCallId} [delivery ${r.deliveryId}]`).join(", ")}) — ${why}. The answer(s) were NOT ` +
26
+ `delivered to the model and are gone with this leg; re-ask if the decision is still needed.`), { phase: "degraded", sessionId, classification: "unconsumed-human-answer" });
27
+ }
28
+ catch {
29
+ }
30
+ };
31
+ const settleContentAskBindings = () => {
32
+ const stranded = [];
33
+ for (const [callId, bound] of contentAskBindings) {
34
+ if (bound.kind === "answered")
35
+ stranded.push({ deliveryId: bound.deliveryId, toolCallId: callId });
36
+ }
37
+ contentAskBindings.clear();
38
+ discloseStrandedAnswers(stranded, "the leg ended first (abort, batch teardown, or a loop failure)");
39
+ const byDelivery = new Map();
40
+ for (const r of [...stranded, ...lateStrandedAnswers])
41
+ byDelivery.set(r.deliveryId, r);
42
+ return [...byDelivery.values()];
43
+ };
44
+ const durableQuestionFace = questionParkStoreWired && (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
45
+ const mountedQuestionFace = liveQuestionFace !== undefined
46
+ ? async (req, signal) => {
47
+ const bound = contentAskBindings.get(req.toolCallId);
48
+ if (bound !== undefined) {
49
+ if (bound.questionsHash === boundInputHashOf(req.questions)) {
50
+ contentAskBindings.delete(req.toolCallId);
51
+ if (bound.kind === "answered")
52
+ return bound.answer;
53
+ throw bound.error;
54
+ }
55
+ contentAskBindings.delete(req.toolCallId);
56
+ }
57
+ return liveQuestionFace(req, signal);
58
+ }
59
+ : onQuestion;
60
+ const questionToolMounted = spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace));
61
+ let mountedQuestionTool;
62
+ if (questionToolMounted)
63
+ tools.push((mountedQuestionTool =
64
+ createAskUserQuestionTool(mountedQuestionFace, { principal: spec.principal, sourceTaskId: sessionId }, {
65
+ ...(resume?.redeemedContentAskCallId !== undefined
66
+ ? {
67
+ redeemedApprovalCallId: resume.redeemedContentAskCallId,
68
+ ...(resume.redeemedContentAskQuestionsHash !== undefined ? { redeemedApprovalQuestionsHash: resume.redeemedContentAskQuestionsHash } : {}),
69
+ }
70
+ : {}),
71
+ ...(resolvedInteractionPosture !== undefined ? { posture: resolvedInteractionPosture } : {}),
72
+ ...(spec.interactiveQuestionFallback === true ? { interactiveFallback: true } : {}),
73
+ onSyntheticContinuation: ({ questionId, reason }) => {
74
+ deps.onError?.(new Error(`AskUserQuestion ${questionId}: no human answer was obtainable (` +
75
+ (reason === "seam_absent"
76
+ ? "no onQuestion seam is wired"
77
+ : reason === "declined_unavailable"
78
+ ? "the wired question channel reported nobody was reachable"
79
+ : "the wired question channel failed") +
80
+ `) — the model was instructed to self-answer and the run CONTINUES (warning, not a failure).`), { phase: "degraded", sessionId, classification: "no-human-autoanswered" });
81
+ },
82
+ })));
83
+ return { checkpointStore, liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, mountedQuestionFace, questionToolMounted, mountedQuestionTool };
84
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * design/390 §1.2 M3a — prepareTask's RUN-REFS phase, verbatim from the driver: the run's own edited-files
3
+ * ledger, the harness ref, the per-task skill scope, the display-forwarding wrapper, the three run-local refs
4
+ * (review request / stop-after-turn / plan mode) with the ctx primitives that write them, the subagent-retain
5
+ * ledger and the worktree-isolation helper. Nothing here has behavior of its own: the phase MINTS SEATS —
6
+ * mutable cells and the closures bound to them — that later phases, the tool ctx and the run loop read and
7
+ * write. The interface is the dependency list the segment had implicitly (design/238 R-1).
8
+ *
9
+ * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the
10
+ * file-history phase before it and the inherited-gate fold after it (design/390 §1.5 S1: a stretch with no
11
+ * await is extracted as a sync function — an `async` wrapper would open a microtask window).
12
+ *
13
+ * Every cell that leaves here as `borrowed-mutable` carries its writer table on the Result member (design/238
14
+ * R-3): the closures minted here, the driver's harness build, the project-context phase's inherited-skill
15
+ * seed, the skill tool, and the run loop's two writes — nothing else writes them. Read-stability of the host
16
+ * handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
17
+ */
18
+ import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
19
+ import type { SubagentWorktreeIsolation } from "../../agents/subagent.js";
20
+ import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
21
+ import type { TaskEvent, TaskSpec } from "../types.js";
22
+ import { ActiveSkillScope } from "./active-skill-scope.js";
23
+ import type { Prepared, RunInternals } from "./contracts.js";
24
+ import { type EditedFilesLedger } from "./edited-files-ledger.js";
25
+ export interface PrepareRunRefsInput {
26
+ /** borrowed-readonly — the REBOUND spec. Read: `forwardSubagentEvents` (widens the forwarding filter),
27
+ * `retainSubagentSessions` (the ledger opt-in and its config), `handsReadOnly` (a read-only run mints no
28
+ * worktree helper). Never mutated. */
29
+ spec: Pick<TaskSpec, "forwardSubagentEvents" | "retainSubagentSessions" | "handsReadOnly">;
30
+ /** borrowed-readonly — the trusted spawn-side channel; only `onForwardEvent` (the deployment's display
31
+ * sink) is read, once, to decide whether a forwarding wrapper exists at all. */
32
+ internals: Pick<RunInternals, "onForwardEvent"> | undefined;
33
+ /** borrowed-readonly — the run's resolved env; the worktree helper closes over it (lazy: no I/O until a
34
+ * delegation actually mints a worktree). */
35
+ executionEnv: ExecutionEnv;
36
+ /** borrowed-readonly — the SETTLED task root (`taskRootFinal`): the worktree helper's base repo root. */
37
+ taskRootFinal: string;
38
+ }
39
+ export interface PrepareRunRefsResult {
40
+ /** owned — the ledger's writer (`FileEditedHook`): handed to the hands/read-face phase as its post-write
41
+ * seat; nothing else calls it. */
42
+ noteFileEdited: EditedFilesLedger["note"];
43
+ /** owned — the ledger's LIVE read face (`Prepared.editedFilesSnapshot`, read at result assembly). */
44
+ editedFilesSnapshot: EditedFilesLedger["snapshot"];
45
+ /** borrowed-mutable — the harness seat: holds the harness once built, so a tool's execute can read the model
46
+ * the parent is CURRENTLY on (design/38 1B: the `ToolExecuteContext.model` snapshot a delegation tool uses
47
+ * for `inherit`), and breaks the harness↔tool_search build cycle (design/36 rematerialize). Writers (R-3):
48
+ * the driver, ONCE, when the harness is built (`harnessRef.current = harness`) — before any run. Readers:
49
+ * `requestStopAfterTurn` here (mirrors the stop onto the harness's one-way flag), the tool ctx's model
50
+ * snapshot, the rebuild seam and the delegation mounts. Identity is the contract: every closure minted
51
+ * before the harness exists reads this exact cell. */
52
+ harnessRef: {
53
+ current?: AgentHarness;
54
+ };
55
+ /** borrowed-mutable — the per-task LIFO skill scope. Writers: the project-context phase (the inherited-
56
+ * from-parent seed frame, `push`) and the `skill` tool minted over it (`push` on a manifest load; a frame
57
+ * closes by `pop`). Readers: the tool ctx's `activeSkillScope()` snapshot and the skill-scope policy layer. */
58
+ skillScope: ActiveSkillScope;
59
+ /** owned — the FILTERED display-forwarding wrapper, or undefined when the deployment wired no sink (the
60
+ * tool ctx then carries no `forwardEvent` key). */
61
+ forwardEvent: ((e: TaskEvent) => void) | undefined;
62
+ /** borrowed-mutable — the review-request cell (`Prepared.reviewRequestRef`). Writers: `requestReview` here
63
+ * (the first request wins) and the run loop's turn-boundary consumer, which clears `pending` once the
64
+ * `plan_review` checkpoint is minted. */
65
+ reviewRequestRef: Prepared["reviewRequestRef"];
66
+ /** owned — the ctx primitive that arms `reviewRequestRef` (design/80 D-B). */
67
+ requestReview: (opts?: {
68
+ reason?: string;
69
+ }) => void;
70
+ /** borrowed-mutable — the stop-after-turn latch (`Prepared.stopRequestedRef`). Writers: `requestStopAfterTurn`
71
+ * here, and nothing else; the run loop reads it at a turn boundary. */
72
+ stopRequestedRef: Prepared["stopRequestedRef"];
73
+ /** owned — the ctx primitive: flips the latch and mirrors it onto the harness's one-way flag. */
74
+ requestStopAfterTurn: () => void;
75
+ /** borrowed-mutable — the plan-mode flag (`Prepared.planModeRef`). Writers: `enterPlanMode` here, and the run
76
+ * loop's resume ladder, which re-arms it when a REJECTED `plan_review` resumes into a re-plan. Read by the
77
+ * tool-call gate (the write-effect refusal). */
78
+ planModeRef: Prepared["planModeRef"];
79
+ /** owned — the ctx primitive (mounted on the ctx only under `enablePlanMode`). */
80
+ enterPlanMode: () => void;
81
+ /** ownership-ref — the parent-run subagent-retain ledger, or undefined when the spec did not opt in. Its
82
+ * lifecycle protocol belongs to the Runner (disposed in the task's terminal finally, design/122 D4); a
83
+ * prepare that throws has nothing to release here — the ledger is empty until a delegation retains. */
84
+ subagentRetain: SubagentRetainLedger | undefined;
85
+ /** owned — the Agent tool's worktree-isolation helper (lazy; write-capable runs only), or undefined. */
86
+ worktreeIsolation: SubagentWorktreeIsolation | undefined;
87
+ }
88
+ /** The M3a phase body — prepareTask's run-refs stretch, verbatim (see the module header). */
89
+ export declare function prepareRunRefs(input: PrepareRunRefsInput): PrepareRunRefsResult;
@@ -0,0 +1,39 @@
1
+ import { createSubagentWorktreeHelper } from "../../agents/subagent.js";
2
+ import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
3
+ import { ActiveSkillScope } from "./active-skill-scope.js";
4
+ import { createEditedFilesLedger } from "./edited-files-ledger.js";
5
+ export function prepareRunRefs(input) {
6
+ const { spec, internals, executionEnv, taskRootFinal } = input;
7
+ const { note: noteFileEdited, snapshot: editedFilesSnapshot } = createEditedFilesLedger();
8
+ const harnessRef = {};
9
+ const skillScope = new ActiveSkillScope();
10
+ const forwardSink = internals?.onForwardEvent;
11
+ const forwardEvent = forwardSink
12
+ ? (e) => {
13
+ if (e.type === "task_progress")
14
+ forwardSink(e);
15
+ else if (spec.forwardSubagentEvents === true &&
16
+ (e.type === "text_delta" || e.type === "text_end" || e.type === "reasoning_delta" || e.type === "tool_start" || e.type === "tool_end")) {
17
+ forwardSink(e);
18
+ }
19
+ }
20
+ : undefined;
21
+ const reviewRequestRef = {};
22
+ const requestReview = (opts) => {
23
+ if (reviewRequestRef.pending === undefined) {
24
+ reviewRequestRef.pending = opts?.reason !== undefined ? { reason: opts.reason } : {};
25
+ }
26
+ };
27
+ const stopRequestedRef = { current: false };
28
+ const requestStopAfterTurn = () => {
29
+ stopRequestedRef.current = true;
30
+ harnessRef.current?.requestStopAfterTurn();
31
+ };
32
+ const planModeRef = { active: false };
33
+ const enterPlanMode = () => {
34
+ planModeRef.active = true;
35
+ };
36
+ const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
37
+ const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
38
+ return { noteFileEdited, editedFilesSnapshot, harnessRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, planModeRef, enterPlanMode, subagentRetain, worktreeIsolation };
39
+ }
@@ -1,9 +1,5 @@
1
1
  import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
2
- /** The namespaced-name shapes the protocol table currently owns, rendered for the two messages that have
3
- * to name them (the caller-name reservation and the policy audit's unprefixed-name arm). Read from the
4
- * table rather than written out: a message that hard-codes ONE protocol becomes wrong — while staying
5
- * green — the moment a second one is appended, and both messages tell a deployment what to write. */
6
- export declare const NAMESPACED_NAME_SHAPES: string;
2
+ import type { IrreversibilityTier, ReversibilityProbes } from "./contracts.js";
7
3
  export interface PrepareSafetyScanInput {
8
4
  /** borrowed-readonly — the REBOUND spec (`spec′`, the config-doors result). The scan reads
9
5
  * `tools`, `handsReadOnly` and `enablePlanMode`; it never mutates. */
@@ -38,14 +34,14 @@ export interface PrepareSafetyScanResult {
38
34
  /** borrowed-mutable — tools whose effect is irreversible (`always`, or `maybe` incl. by probe). */
39
35
  irreversibleTools: Set<string>;
40
36
  /** borrowed-mutable — static per-tool irreversibility tier (prepare-time resolution). */
41
- irreversibilityTier: Map<string, "never" | "maybe" | "always">;
37
+ irreversibilityTier: IrreversibilityTier;
42
38
  /** borrowed-mutable — explicit NEGATIVE axis judgments (report-face only; `riskAxesOf` reads). */
43
39
  axisExplicitNegatives: Map<string, {
44
40
  irreversible?: false;
45
41
  egress?: false;
46
42
  }>;
47
43
  /** borrowed-mutable — declared reversibility probes by tool name. */
48
- reversibilityProbes: Map<string, NonNullable<TaskSpec["tools"]>[number]["reversibilityProbe"]>;
44
+ reversibilityProbes: ReversibilityProbes;
49
45
  /** borrowed-readonly — the caller roster's own names, snapshotted before the first await. */
50
46
  ownToolNames: ReadonlySet<string>;
51
47
  }
@@ -1,8 +1,7 @@
1
- import { PROTOCOL_TABLE } from "../protocol-table.js";
1
+ import { NAMESPACED_NAME_SHAPES } from "../protocol-table.js";
2
2
  import { HAND_TOOL_EFFECTS } from "../../tools/fs/index.js";
3
3
  import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
4
4
  import { PRESENT_PLAN_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME } from "../present-plan-tool.js";
5
- export const NAMESPACED_NAME_SHAPES = PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>__<tool>`).join(", ");
6
5
  export function prepareSafetyScan(input) {
7
6
  const { spec, deps } = input;
8
7
  const toolEffects = new Map();
@@ -0,0 +1,170 @@
1
+ /**
2
+ * design/390 §1.2 M19 (suspend saga) — prepareTask's DURABLE SETTLEMENT machinery, verbatim from the driver's
3
+ * gate-machinery block: the in-flight spend and repair-bundle folds, the §4.bis correctness-state serializer, the shared
4
+ * paused-VM compensation bound to this task's failure ledger, the put-commit + put-failure saga (the pre-put cut
5
+ * checkpoint, the screening-park disclosure, the rejection read-back with its three answers), the suspend-loop cap, the
6
+ * env capability split (suspendable / park-only / neither) and the park-only handle. The module-scope helpers only this
7
+ * segment called moved with it (the compensation's bound and body, the auto-mode latch reading, the placement stamp).
8
+ * The interface is the dependency list the segment had implicitly (design/238 R-1) — every seat below used to be
9
+ * captured off the driver's scope.
10
+ *
11
+ * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await of its own (every await lives inside the closures it
12
+ * builds, which run at park time), so the phase adds no yield between the ask lane before it and the boundary parks
13
+ * after it (design/390 §1.5). Read-stability of the host handles for the call: {@link RunInternals}
14
+ * (`@contract prepare.deps-read-stable`).
15
+ *
16
+ * The saga exists exactly when the wiring-manifest phase's `gateMachineryActive` holds (the ask lane's presence law) —
17
+ * the boundary parks and the park closure key off `saga`'s presence, so the orchestrator grows no branch.
18
+ */
19
+ import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
20
+ import type { AutoModeDecider } from "../auto-mode.js";
21
+ import type { OwnOrgAdmissionVerdict } from "../memory-admission.js";
22
+ import type { RemoteExecutionEnv, SnapshotId } from "../remote-env.js";
23
+ import type { CheckpointStore, SerializedCheckpointState } from "../checkpoint-store.js";
24
+ import type { RemoteEnvFailureNote, RunnerDeps } from "../types.js";
25
+ import type { CwdRef } from "../../tools/fs/fs-shared.js";
26
+ import type { ReadFileState } from "../../tools/fs/safety.js";
27
+ import type { OutputRef } from "./synthetic-tools.js";
28
+ import type { HookInvocationIdentity } from "../hooks.js";
29
+ import type { InheritedGate, Prepared, PrepareResume, RunInternals, SuspendSaga } from "./contracts.js";
30
+ /**
31
+ * design/384 slice 2 — the ONE compensation for a paused-but-unparked VM, shared by the fence's
32
+ * post-pause checkpoints (③ in the park closure, ④ in the saga) and the saga's put-failure absent
33
+ * arm (previously inline there: same two hops, one implementation now, so the three sites cannot
34
+ * drift). Restores the workspace (bounded transient retry) then re-establishes consistency
35
+ * (`postResumeInit`), both hops under one INDEPENDENT `AbortSignal.timeout(io.boundMs)` —
36
+ * deliberately NOT the run/cut signal: the remote contract answers an already-aborted signal
37
+ * `{ok:false,"aborted"}`, so gating the restore on the very signal whose firing caused the
38
+ * compensation made it die instantly and strand the paused VM on exactly the run-abort arm that
39
+ * needs it most. The adapter signal is best-effort (a deaf adapter ignores it), so the caller's
40
+ * await is ALSO raced against the same bound — bounded decision, three outcomes:
41
+ * · settled ok ⇒ the VM is running again ({ok:true});
42
+ * · settled not-ok / threw ⇒ recorded + disclosed, {ok:false} — the caller takes its fatal
43
+ * fail-closed arm (abort the run; never continue on a paused VM);
44
+ * · bound fires with the adapter still in flight ⇒ {ok:false} NOW, and the in-flight call
45
+ * continues DETACHED + swallow-guarded with the late-settlement split:
46
+ * – late SUCCESS: a running VM now exists with no run and no committed row to own it — the
47
+ * detached continuation compensates the compensation with `destroy()` (the adapter's own
48
+ * best-effort contract, swallow-guarded, disclosed);
49
+ * – late FAILURE: disclosed; the VM is most likely still paused — provider-side TTL/GC
50
+ * territory, and the disclosure is the end of this engine's obligation (a deaf adapter's
51
+ * stranded VM is that adapter's own defect surface).
52
+ * Decide-then-disclose throughout: `io.disclose`/`io.noteFailure` must be swallow-guarded by the
53
+ * caller's binding, and nothing they do can change the returned verdict. Never throws. Exported for
54
+ * direct unit pinning (the bounded/deaf/late arms need a small bound; production binds the
55
+ * `PARK_COMPENSATION_TIMEOUT_MS` constant at the one call-site closure).
56
+ */
57
+ export declare function compensateUnparkedPause(remoteEnv: RemoteExecutionEnv, snapshotId: SnapshotId, io: {
58
+ boundMs: number;
59
+ noteFailure: (note: RemoteEnvFailureNote) => void;
60
+ disclose: (err: unknown) => void;
61
+ }): Promise<{
62
+ ok: true;
63
+ } | {
64
+ ok: false;
65
+ reason: string;
66
+ }>;
67
+ export interface PrepareSuspendSagaInput {
68
+ /** borrowed-readonly — the wiring-manifest phase's ONE activation predicate; false ⇒ this phase builds nothing and
69
+ * returns an absent saga. */
70
+ gateMachineryActive: boolean;
71
+ /** borrowed-mutable — prepare's abort seat. Writer here: the fail-closed arms (`abort()` when a paused VM cannot be
72
+ * restored, when the put outcome is unknowable, when the loop cap trips); `.signal` is read by the mint sites. */
73
+ abortController: {
74
+ readonly signal: AbortSignal;
75
+ abort(): void;
76
+ };
77
+ /** borrowed-mutable — the built harness. Writer here: `abort()` on the same fail-closed arms (release the gate,
78
+ * then stop the loop — the two-step every stop in this file takes). */
79
+ harness: Pick<AgentHarness, "abort">;
80
+ /** borrowed-readonly — the leg's checkpoint store, or undefined (then the saga answers `absent` and no park commits). */
81
+ checkpointStore: CheckpointStore | undefined;
82
+ /** borrowed-readonly — the deployment's error face, as a Pick over the SAME `deps` object (receiver preserved for
83
+ * `deps.onError?.()`): the put-failure disclosures, the compensation's disclose binding, the cap line. */
84
+ deps: Pick<RunnerDeps, "onError">;
85
+ /** borrowed-readonly — the acquired session id (every disclosure's tag, the running-tasks owner triple, the row's
86
+ * session). */
87
+ sessionId: string;
88
+ /** borrowed-readonly — the per-task identity (the running-tasks owner triple). */
89
+ hostTaskId: string;
90
+ /** borrowed-readonly — the registry scope (the running-tasks owner triple). */
91
+ taskScope: string;
92
+ /** borrowed-readonly — the loop's live spend seat (`Prepared.liveSpendRef`); `.get` is read at mint time. Not written
93
+ * here (runtask sets `.get`). */
94
+ liveSpendRef: Prepared["liveSpendRef"];
95
+ /** borrowed-readonly — the resume leg, or undefined: `seed.nestedStats` (the spend delta's base), `seed.repairBundle`
96
+ * (the pass-through bundle). */
97
+ resume: Pick<PrepareResume, "seed"> | undefined;
98
+ /** borrowed-readonly — the cumulative nested-usage accumulator (`Prepared.nestedStats`); read at mint time, copied
99
+ * onto the row. */
100
+ nestedStats: Prepared["nestedStats"];
101
+ /** borrowed-readonly — the trusted spawn-side channel: `repairBundle` (the live bundle) and `delegationProvenance`
102
+ * (the aggregate copied onto the row). */
103
+ internals: Pick<RunInternals, "repairBundle" | "delegationProvenance"> | undefined;
104
+ /** borrowed-readonly — the ToolSearch activation set (`Prepared.activeTools`); copied onto the row at mint time. */
105
+ activeTools: Prepared["activeTools"];
106
+ /** borrowed-readonly — the structured-output seat; `value`/`set` are copied onto the row at mint time. */
107
+ outputRef: OutputRef;
108
+ /** borrowed-readonly — the hand's read-file state for the checkpoint, or undefined (hands-less); its entries are
109
+ * copied onto the row at mint time. */
110
+ readFileStateForCheckpoint: ReadFileState | undefined;
111
+ /** borrowed-readonly — the inherited-gate phase's read-face section builder (the row's `readFace`). */
112
+ faceCheckpointSection: () => SerializedCheckpointState["readFace"];
113
+ /** borrowed-readonly — the leg's reminder provenance mark (travels with the row). */
114
+ reminderMark: string;
115
+ /** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read at mint time. Not written here. */
116
+ handsCwdRef: CwdRef | undefined;
117
+ /** borrowed-readonly — the active EnterWorktree session seat, or undefined; `current` is read at mint time. */
118
+ worktreeSessionRef: Prepared["worktreeSessionRef"];
119
+ /** borrowed-readonly — the LIVE inherited parent constraints, or undefined (the row's `requiresParentConstraint`,
120
+ * count, frozen chain and the screening-park disclosure's predicate). */
121
+ inheritedParentConstraints: NonNullable<InheritedGate["parentConstraints"]>[number][] | undefined;
122
+ /** borrowed-readonly — the resume seed's inherited gate, or undefined (the carried-forward half of the same fields). */
123
+ seedInheritedGate: PrepareResume["seed"]["inheritedGate"];
124
+ /** borrowed-readonly — the inherited ancestor rules (data half), cloned onto the row. */
125
+ inheritedAncestorRules: InheritedGate["ancestorRules"];
126
+ /** borrowed-readonly — the inherited shell-gate doctrine, persisted verbatim. */
127
+ inheritedShellGate: InheritedGate["shellGate"];
128
+ /** borrowed-readonly — the auto-mode intent (seat ∨ live ∨ seed), carried while the latch is healthy. */
129
+ autoModeIntent: boolean;
130
+ /** borrowed-readonly — the auto-mode decider, or undefined (the latch-health reading only). */
131
+ autoModeDecider: AutoModeDecider | undefined;
132
+ /** borrowed-readonly — the inherited admitted org scopes, or undefined (copied onto the row). */
133
+ inheritedAdmittedOrgScopes: readonly string[] | undefined;
134
+ /** borrowed-readonly — this leg's OWN org verdict seat; `current` is read at mint time. Not written here. */
135
+ ownOrgVerdictRef: {
136
+ current: OwnOrgAdmissionVerdict | undefined;
137
+ };
138
+ /** borrowed-readonly — the monotonic org-governed provenance bit. */
139
+ orgGovernedProvenance: boolean;
140
+ /** borrowed-readonly — the listing frames' announced name-sets mirror (`Prepared.announcedListingsRef`); serialized
141
+ * onto the row at mint time. */
142
+ announcedListingsRef: Prepared["announcedListingsRef"];
143
+ /** borrowed-readonly — the git frame's lane ref (`Prepared.gitStatusRef`); `announced` is copied at mint time. */
144
+ gitStatusRef: Prepared["gitStatusRef"];
145
+ /** borrowed-readonly — the leg's identity envelope; only `isDelegatedChild` is read (the row's presence-coded bit). */
146
+ hookIdentity: Pick<HookInvocationIdentity, "isDelegatedChild">;
147
+ /** borrowed-readonly — the frozen prepare-time placement resolution (the row's placement stamp). */
148
+ placementRootResolved: string;
149
+ /** borrowed-readonly — the external-content-target fold (the row's monotonic bit). */
150
+ externalContentTargetActive: boolean;
151
+ /** borrowed-mutable — the run-scoped remote-lifecycle failure log (`Prepared.remoteEnvFailures`). Writer here: the
152
+ * compensation's note binding (`push`). */
153
+ remoteEnvFailures: Prepared["remoteEnvFailures"];
154
+ /** borrowed-readonly — the memory-engine session, or undefined; `harvest("checkpoint")` is called at the commit door. */
155
+ memoryEngineSession: Prepared["memoryEngineSession"];
156
+ /** borrowed-mutable — the loop-cap latch (`Prepared.suspendLoopRef`). Writer here: `hit = true` when the cap trips. */
157
+ suspendLoopRef: Prepared["suspendLoopRef"];
158
+ /** borrowed-readonly — the per-task owned env when a factory minted one, or undefined (the capability split). Never
159
+ * destroyed here. */
160
+ ownedEnv: ExecutionEnv | undefined;
161
+ /** borrowed-readonly — the restore-incomplete adapter's missing members, or undefined (excluded from both targets). */
162
+ incompleteSuspendAdapter: readonly ("resumeVM" | "postResumeInit")[] | undefined;
163
+ }
164
+ export interface PrepareSuspendSagaResult {
165
+ /** owned — the saga, or undefined exactly when `gateMachineryActive` is false (the boundary parks and the park closure
166
+ * key off this seat). */
167
+ saga: SuspendSaga | undefined;
168
+ }
169
+ /** The M19 suspend-saga phase body — prepareTask's durable settlement stretch, verbatim (see the module header). */
170
+ export declare function prepareSuspendSaga(input: PrepareSuspendSagaInput): PrepareSuspendSagaResult;