@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,119 @@
1
+ /**
2
+ * design/390 M10 — prepareTask's PROMPT-INPUTS phase: the prompt provider selection, the runtime
3
+ * feature flags the harness-context block gates on (so it never claims a mechanism the task lacks),
4
+ * the three-source user system prompt + the caller's append block, the fenced MCP server-instructions
5
+ * block, the environment facts (client zone + date basis, knowledge cutoff, the deployment's sandbox
6
+ * axes, resume-continuity facts on a durable-resume leg only, the hands facts + the one batched env
7
+ * probe with its cwd/pwd alignment guard) and the git-status lane probe. The body is the driver's
8
+ * stretch moved verbatim; the interface is the dependency list it had implicitly (design/238 R-1).
9
+ *
10
+ * ASYNC BY CONTENT: the stretch awaits the env probe (hands) and the git-status lane probe (always).
11
+ * It sits past prepareTask's first await, so the driver's `await` adds no window a caller could reach.
12
+ *
13
+ * The one input computed by the DRIVER rather than read here: `failClosedReason` — the
14
+ * self-orchestration fail-closed advisory's text. Its helper takes the whole `TaskSpec`/`RunnerDeps`
15
+ * (it audits several deployment seats), which this phase's narrow `spec`/`deps` seats cannot satisfy
16
+ * (design/238 R-4); the driver evaluates the helper in the Input literal and this phase only announces
17
+ * the result on the operator lane, exactly where the stretch always did.
18
+ *
19
+ * Nothing here throws on its own: the env probe and the alignment guard are best-effort (a stub/odd env
20
+ * just omits the shell-derived facts), and the git lane degrades through `onDegrade` → `deps.onError`.
21
+ */
22
+ import type { ExecutionEnv, ThinkingLevel } from "../../internal/harness.js";
23
+ import type { Model } from "../../internal/llm.js";
24
+ import { type EnvironmentFacts, type PromptProvider } from "../../prompts/default.js";
25
+ import type { ReadFace } from "../../tools/fs/index.js";
26
+ import type { LockedPreflight } from "../locked-config.js";
27
+ import type { MaterializedMcp } from "../mcp.js";
28
+ import type { ResolvedRole } from "../roles.js";
29
+ import type { RunnerDeps, TaskSpec } from "../types.js";
30
+ import type { PrepareResume, PromptFeatureFlags, RunInternals, ToolFaceSnapshot } from "./contracts.js";
31
+ import { type GitStatusLaneRef } from "./git-status-frame.js";
32
+ export interface PreparePromptInputsInput {
33
+ /** borrowed-readonly — the REBOUND spec (`spec′`). Read: `promptProvider`, `hooks` (flag), `systemPrompt`
34
+ * / `appendSystemPrompt` (the user prompt sources), `clientContext` (zone, email), `envFacts` (the
35
+ * deployment's sandbox + resume facts), `compaction` (the within-task flag). Never mutated. */
36
+ spec: Pick<TaskSpec, "promptProvider" | "hooks" | "systemPrompt" | "appendSystemPrompt" | "clientContext" | "envFacts" | "compaction">;
37
+ /** borrowed-readonly — deployment deps: `promptProvider` (fallback), `hooks` (flag), `onError` (the
38
+ * operator lane: fail-closed advisory, cwd misalignment, git-lane degrade), `modelKnowledgeCutoffs`.
39
+ * Receiver preserved (`deps.onError?.(…)`). */
40
+ deps: Pick<RunnerDeps, "promptProvider" | "hooks" | "onError" | "modelKnowledgeCutoffs">;
41
+ /** borrowed-readonly — the self-orchestration fail-closed advisory (null = nothing to announce),
42
+ * evaluated by the DRIVER against the whole spec/deps (see the module header). */
43
+ failClosedReason: string | null;
44
+ /** borrowed-readonly — the locked preflight; only `toolPolicy` presence is read (the policy flag). */
45
+ lockedPreflight: Pick<LockedPreflight, "toolPolicy">;
46
+ /** borrowed-readonly — whether self-orchestration is ACTIVE (the same gate that mounted the
47
+ * workflow tool): drives the orchestration prompt flags. */
48
+ selfOrchestrationActive: boolean;
49
+ /** borrowed-readonly — the task's execution env: `isIsolated` for the isolation flag, `exec` for the
50
+ * one batched probe, `canonicalPath` for the alignment guard. Never re-bound, never destroyed here. */
51
+ executionEnv: ExecutionEnv;
52
+ /** borrowed-readonly — the session's reminder mark (rides the prompt facts for the declaration). */
53
+ reminderMark: string;
54
+ /** borrowed-readonly — the resolved read face (the open-reads flag + the structured env fact). */
55
+ resolvedReadFace: ReadFace | undefined;
56
+ /** borrowed-readonly — the frozen tool face; only `defer` is read (the Workflow short-pointer flag). */
57
+ toolFaceSnapshot: Pick<ToolFaceSnapshot, "defer">;
58
+ /** borrowed-readonly — the resolved prompt profile (rides the flags to the legacy ctx). */
59
+ promptProfile: "simple" | "classic";
60
+ /** borrowed-readonly — the fable-mitigations profile axis (rides the flags with the profile). */
61
+ fableMitigations: boolean;
62
+ /** borrowed-readonly — trusted internals: `goalMode` (goal flag), `isolation` (worktree flag),
63
+ * `defaultSystemPrompt` (the third prompt source). `undefined` on a standalone prepare. */
64
+ internals: Pick<RunInternals, "goalMode" | "isolation" | "defaultSystemPrompt"> | undefined;
65
+ /** borrowed-readonly — the resolved, role-folded reasoning tier (the awareness flag's input). */
66
+ thinking: ThinkingLevel | undefined;
67
+ /** borrowed-readonly — the factory-minted isolated env, read for PRESENCE only (the worktree flag's
68
+ * belt over the fail-closed isolation guard). */
69
+ ownedEnv: ExecutionEnv | undefined;
70
+ /** borrowed-readonly — the leg's effective-delegation facts; only `isNonForkChild` is read. */
71
+ delegation: {
72
+ readonly isNonForkChild: boolean;
73
+ };
74
+ /** borrowed-readonly — the resolved role; only its `systemPrompt` preset is read (second source). */
75
+ resolvedRole: Pick<ResolvedRole, "systemPrompt">;
76
+ /** borrowed-readonly — the materialized MCP roster; only `serverInstructions` is read (fenced). */
77
+ mcp: Pick<MaterializedMcp, "serverInstructions">;
78
+ /** borrowed-readonly — the resolved model (id for the env line + the cutoff lookup). */
79
+ model: Model;
80
+ /** borrowed-readonly — the run's external-content-target fold (the single-source disclosure fact). */
81
+ externalContentTargetActive: boolean;
82
+ /** borrowed-readonly — the durable-resume carrier, read for PRESENCE only (resume facts copy on a
83
+ * resume leg and never on a fresh one). */
84
+ resume: PrepareResume | undefined;
85
+ /** borrowed-readonly — whether a real execution env is mounted (the hands facts + both probes). */
86
+ handsEnabled: boolean;
87
+ /** borrowed-readonly — the FINAL task root: the env `cwd` fact and the git lane's root. */
88
+ taskRootFinal: string;
89
+ /** borrowed-readonly — the canonical extra working roots (copied into the env facts). */
90
+ additionalRootsCanonical: readonly string[];
91
+ /** borrowed-readonly — the canonical extra read-only roots (copied into the env facts). */
92
+ additionalReadRootsCanonical: readonly string[];
93
+ /** borrowed-readonly — the acquired session's id (operator-line context). */
94
+ sessionId: string;
95
+ }
96
+ export interface PreparePromptInputsResult {
97
+ /** owned — the prompt provider in force (`spec` ?? `deps` ?? the default). */
98
+ provider: PromptProvider;
99
+ /** owned — the runtime feature flags (see {@link PromptFeatureFlags}). */
100
+ featureFlags: PromptFeatureFlags;
101
+ /** owned — the user system prompt: `spec.systemPrompt ?? resolvedRole.systemPrompt ?? internals.defaultSystemPrompt`. */
102
+ userSystemPrompt: string | undefined;
103
+ /** owned — the caller's append block (`spec.appendSystemPrompt`). */
104
+ userAppendSystemPrompt: string | undefined;
105
+ /** owned — the fenced MCP server-instructions block, or undefined when no server declared any. */
106
+ mcpInstructionsBlock: string | undefined;
107
+ /** owned — the environment facts the env tail renders from. Filled here; no later phase writes it
108
+ * (the run loop re-renders only the date through `renderWithDate`). */
109
+ envFacts: EnvironmentFacts;
110
+ /** owned — whether the client zone validated (the date-change seam re-renders in the same zone). */
111
+ tzValid: boolean;
112
+ /** owned — the client's declared zone as given (paired with `tzValid`; unvalidated on its own). */
113
+ userTz: string | undefined;
114
+ /** owned — the git-status lane's run-local state (empty on a hands-less leg); the run loop and the
115
+ * checkpoint serializer mutate its fields from here on (`announced`, `pendingReceipt`, …). */
116
+ gitStatusRef: GitStatusLaneRef;
117
+ }
118
+ /** The M10 phase body — prepareTask's prompt-inputs stretch, verbatim (see the module header). */
119
+ export declare function preparePromptInputs(input: PreparePromptInputsInput): Promise<PreparePromptInputsResult>;
@@ -0,0 +1,141 @@
1
+ import { defaultPromptProvider, formatLocalDate, isValidTimeZone } from "../../prompts/default.js";
2
+ import { isIsolated } from "../remote-env.js";
3
+ import { probeGitStatusLane } from "./git-status-frame.js";
4
+ import { fenceMcpServerInstructions } from "./turn-attachments.js";
5
+ const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
6
+ export async function preparePromptInputs(input) {
7
+ const { spec, deps, failClosedReason, lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId } = input;
8
+ const provider = spec.promptProvider ?? deps.promptProvider ?? defaultPromptProvider;
9
+ const promptPolicyEnabled = Boolean(lockedPreflight.toolPolicy);
10
+ const promptHooks = spec.hooks ?? deps.hooks;
11
+ const promptHooksEnabled = Boolean(promptHooks?.preToolUse || promptHooks?.postToolUse);
12
+ if (failClosedReason) {
13
+ deps.onError?.(new Error(failClosedReason), { phase: "config", sessionId });
14
+ }
15
+ const featureFlags = {
16
+ policyEnabled: promptPolicyEnabled,
17
+ hooksEnabled: promptHooksEnabled,
18
+ isolationEnabled: isIsolated(executionEnv),
19
+ reminderMark,
20
+ readFaceOpen: resolvedReadFace === "open",
21
+ orchestrationEnabled: selfOrchestrationActive,
22
+ orchestrationDeferred: selfOrchestrationActive && (toolFaceSnapshot.defer?.includes("Workflow") ?? false),
23
+ promptProfile,
24
+ fableMitigations,
25
+ goalEnabled: internals?.goalMode === true,
26
+ awarenessEnabled: thinking !== undefined && ULTRA_REASONING_TIERS.has(thinking),
27
+ worktreeIsolated: internals?.isolation === "worktree" && ownedEnv !== undefined,
28
+ withinTaskCompactionEnabled: (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true),
29
+ isSubagent: delegation.isNonForkChild,
30
+ };
31
+ const userSystemPrompt = spec.systemPrompt ?? resolvedRole.systemPrompt ?? internals?.defaultSystemPrompt;
32
+ const userAppendSystemPrompt = spec.appendSystemPrompt;
33
+ const mcpInstructionsBlock = mcp.serverInstructions.length
34
+ ? `# MCP Server Instructions\n${mcp.serverInstructions.map((s) => fenceMcpServerInstructions(s.server, s.text)).join("\n\n")}`
35
+ : undefined;
36
+ const userTz = spec.clientContext?.timeZone;
37
+ const tzValid = userTz !== undefined && isValidTimeZone(userTz);
38
+ const envFacts = { date: formatLocalDate(new Date(), tzValid ? userTz : undefined), modelId: model.id };
39
+ if (tzValid)
40
+ envFacts.timeZone = userTz;
41
+ if (spec.clientContext?.userEmail)
42
+ envFacts.userEmail = spec.clientContext.userEmail;
43
+ const declaredCutoff = deps.modelKnowledgeCutoffs?.[model.id];
44
+ if (declaredCutoff)
45
+ envFacts.knowledgeCutoff = declaredCutoff;
46
+ if (spec.envFacts?.profile)
47
+ envFacts.sandboxProfile = spec.envFacts.profile;
48
+ if (spec.envFacts?.capabilities && spec.envFacts.capabilities.length > 0)
49
+ envFacts.sandboxCapabilities = [...spec.envFacts.capabilities];
50
+ if (spec.envFacts?.pkgSource)
51
+ envFacts.sandboxPkgSource = spec.envFacts.pkgSource;
52
+ if (spec.envFacts?.egress === "none" || spec.envFacts?.egress === "allowlist" || spec.envFacts?.egress === "full")
53
+ envFacts.sandboxEgress = spec.envFacts.egress;
54
+ if (spec.envFacts?.scratchpadDir)
55
+ envFacts.scratchpadDir = spec.envFacts.scratchpadDir;
56
+ if (externalContentTargetActive)
57
+ envFacts.externalContentTarget = true;
58
+ if (resume !== undefined && spec.envFacts?.resumeFacts) {
59
+ const rf = spec.envFacts.resumeFacts;
60
+ const copy = {};
61
+ if (rf.processes === "preserved" || rf.processes === "lost")
62
+ copy.processes = rf.processes;
63
+ if (rf.scratch === "preserved" || rf.scratch === "lost")
64
+ copy.scratch = rf.scratch;
65
+ if (typeof rf.note === "string" && rf.note.length > 0)
66
+ copy.note = rf.note;
67
+ if (Object.keys(copy).length > 0)
68
+ envFacts.resumeFacts = copy;
69
+ }
70
+ if (handsEnabled) {
71
+ envFacts.cwd = taskRootFinal;
72
+ if (additionalRootsCanonical.length > 0) {
73
+ envFacts.additionalDirectories = [...additionalRootsCanonical];
74
+ }
75
+ if (resolvedReadFace !== undefined)
76
+ envFacts.readFace = resolvedReadFace;
77
+ if (additionalReadRootsCanonical.length > 0) {
78
+ envFacts.additionalReadDirectories = [...additionalReadRootsCanonical];
79
+ }
80
+ try {
81
+ const probe = await executionEnv.exec('uname -s; uname -r; (git rev-parse --is-inside-work-tree 2>/dev/null || echo false); (git symbolic-ref --short -q HEAD 2>/dev/null || echo "HEAD (detached)"); (git rev-parse --show-toplevel 2>/dev/null || echo); (test -n "$(git status --porcelain 2>/dev/null | head -1)" && echo dirty || echo clean); (s=$(ps -p $$ -o comm= 2>/dev/null); s=${s##*/}; echo "${s#-}"); (test "$(git rev-parse --git-dir 2>/dev/null)" != "$(git rev-parse --git-common-dir 2>/dev/null)" && echo linked || echo main); (pwd -P 2>/dev/null || pwd)', { cwd: envFacts.cwd, timeout: 10 });
82
+ if (probe.ok && probe.value.exitCode === 0) {
83
+ const lines = probe.value.stdout.split("\n");
84
+ if (lines.length >= 6) {
85
+ const [osName, osVer, git, branch, worktreeRoot, dirty] = lines;
86
+ if (osName?.trim())
87
+ envFacts.platform = osName.trim();
88
+ if (osVer?.trim())
89
+ envFacts.osVersion = osVer.trim();
90
+ envFacts.isGitRepo = (git ?? "").trim() === "true";
91
+ if (envFacts.isGitRepo) {
92
+ const b = (branch ?? "").trim();
93
+ if (b)
94
+ envFacts.gitBranch = b;
95
+ const root = (worktreeRoot ?? "").trim();
96
+ if (root)
97
+ envFacts.gitWorktreeRoot = root;
98
+ const d = (dirty ?? "").trim();
99
+ if (d === "dirty" || d === "clean")
100
+ envFacts.gitDirty = d === "dirty";
101
+ }
102
+ const sh = (lines[6] ?? "").trim();
103
+ if (sh)
104
+ envFacts.shell = sh;
105
+ if (envFacts.isGitRepo && (lines[7] ?? "").trim() === "linked")
106
+ envFacts.isLinkedWorktree = true;
107
+ const probedPwd = (lines[8] ?? "").trim();
108
+ if (probedPwd && envFacts.cwd && probedPwd !== envFacts.cwd) {
109
+ const canonOf = async (p) => {
110
+ try {
111
+ const c = await executionEnv.canonicalPath(p);
112
+ return c.ok ? c.value : p;
113
+ }
114
+ catch {
115
+ return p;
116
+ }
117
+ };
118
+ const canonicalProbed = await canonOf(probedPwd);
119
+ if (canonicalProbed !== (await canonOf(envFacts.cwd))) {
120
+ try {
121
+ deps.onError?.(new Error(`execution-env cwd misalignment: task root is "${envFacts.cwd}" but the env's shell reports pwd "${canonicalProbed}" — per-exec cwd injection may be ignored on this lane (execs/artifacts can land outside the workspace root)`), { phase: "config", sessionId });
122
+ }
123
+ catch {
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ catch {
131
+ }
132
+ }
133
+ const gitStatusRef = await probeGitStatusLane({
134
+ executionEnv,
135
+ envFacts,
136
+ handsEnabled,
137
+ taskRoot: taskRootFinal,
138
+ onDegrade: (reason) => deps.onError?.(new Error(`env git snapshot degraded — ${reason}`), { phase: "degraded", sessionId, classification: "env-git-snapshot" }),
139
+ });
140
+ return { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, gitStatusRef };
141
+ }
@@ -0,0 +1,91 @@
1
+ import type { AgentTool } from "../../internal/harness.js";
2
+ import { type MaterializedMcp, type McpDroppedTool } from "../mcp.js";
3
+ import { type MaterializedA2a } from "../a2a.js";
4
+ import type { LockedPreflight } from "../locked-config.js";
5
+ import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
6
+ import type { AnnounceOnceSink, IrreversibilityTier, Prepared, RollbackStack, ToolFaceSnapshot } from "./contracts.js";
7
+ export interface PrepareProtocolToolsInput {
8
+ /** borrowed-readonly — the frozen preflight; only `mcp` (the declared server entries) is read: the
9
+ * materialize input, the RefreshMcpTools mount predicate, and whether a real client was acquired. */
10
+ lockedPreflight: Pick<LockedPreflight, "mcp">;
11
+ /** borrowed-readonly — the REBOUND spec. Read: `principal` (the MCP header / the A2A identity), `tools`
12
+ * (the caller names both reserved-name guards check against), `a2a` (the peer entries). Never mutated. */
13
+ spec: Pick<TaskSpec, "principal" | "tools" | "a2a">;
14
+ /** borrowed-readonly — the deployment seats the materializers and their operator faces read: `onElicit`
15
+ * and `mcpImageResizer` ride into the MCP intake; `onError` takes the A2A warnings (no ledger — a peer is
16
+ * re-read every prepare); `mcpRevocations` + `onNotice` make the revocation wiring. */
17
+ deps: Pick<RunnerDeps, "onElicit" | "mcpImageResizer" | "onError" | "mcpRevocations" | "onNotice">;
18
+ /** borrowed-readonly — the leg's reminder mark (the MCP intake's disclosure bounding reads it). */
19
+ reminderMark: string;
20
+ /** borrowed-mutable — the reminder-disclosure counters: the MCP intake's bounding writes them here, and
21
+ * the run loop's attachment lanes keep writing the same object (`Prepared.reminderDisclosureCounts`). */
22
+ reminderDisclosureCounts: Prepared["reminderDisclosureCounts"];
23
+ /** borrowed-readonly — the run's invocation id (the revocation probe-failure notice names it). */
24
+ runId: string;
25
+ /** borrowed-readonly — the acquired session id (operator-line context). */
26
+ sessionId: string;
27
+ /** borrowed-mutable — the once-per-session ledger: its deduplicated operator sink (`onError`, one line per
28
+ * (code, message) per session) and the announce-lane prune of tools already delivered on this branch.
29
+ * The ledger module owns the record; this phase writes through those two methods and nothing else. */
30
+ onceLedger: AnnounceOnceSink & {
31
+ pruneDroppedTools(dropped: McpDroppedTool[]): void;
32
+ };
33
+ /** borrowed-mutable — the run's shared roster (identity is the contract: `Prepared.tools`, the wire order).
34
+ * Writers here: three pushes (the MCP tools through the wrapper, RefreshMcpTools, the A2A tools through the
35
+ * wrapper) and, at RUN time, the refresh closure's splice within a server's name-prefix domain. Later
36
+ * phases push their own mounts; the roster's full writer table is the delegation-surface phase's. */
37
+ tools: AgentTool[];
38
+ /** borrowed-readonly — the large-result wrapper for protocol tools (per-tool `_meta` bar, 50K default). */
39
+ remoteToolOffload: (tool: AgentTool) => AgentTool;
40
+ /** borrowed-readonly — the frozen tool face; `exclude` is read at REFRESH time so a deployment-excluded
41
+ * tool is never re-mounted by a model-triggered refresh. */
42
+ toolFaceSnapshot: Pick<ToolFaceSnapshot, "exclude">;
43
+ /** borrowed-mutable — the effect table the fold writes under a has-guard (`?? "write"` default) and the
44
+ * refresh closure resets per prefix domain before re-folding; RefreshMcpTools registers itself `read`. */
45
+ toolEffects: Map<string, ToolEffect>;
46
+ /** borrowed-mutable — the explicit-negative axes (a caller's vouched `never` / `egress:false`); written by
47
+ * the fold and reset per prefix domain by the refresh closure. */
48
+ axisExplicitNegatives: Map<string, {
49
+ irreversible?: false;
50
+ egress?: false;
51
+ }>;
52
+ /** borrowed-mutable — the irreversibility tier, tightened to `always` by the fold (monotone). */
53
+ irreversibilityTier: IrreversibilityTier;
54
+ /** borrowed-mutable — the irreversible set, added to by the fold (tighten-only). */
55
+ irreversibleTools: Set<string>;
56
+ /** borrowed-mutable — the egress set, added to by the fold (tighten-only; the effect:"write" invariant). */
57
+ egressTools: Set<string>;
58
+ /** borrowed-readonly — the run's abort controller; its `signal` rides into the A2A materialization. */
59
+ abortController: Pick<AbortController, "signal">;
60
+ /** borrowed-mutable — the driver-owned prepare-failure stack; this phase pushes one entry per REAL handle
61
+ * acquired (the MCP clients, then the A2A peers) and nothing else touches it here. */
62
+ rollback: RollbackStack;
63
+ }
64
+ export interface PrepareProtocolToolsResult {
65
+ /** owned — the materialized MCP handle (`Prepared.mcp`): clients, axes, server instructions, statuses, the
66
+ * refresh and the `dispose` the Runner's finish calls. Its prepare-failure release is the rollback entry
67
+ * this phase pushed (only when a real client was acquired — the inert stub owns nothing). */
68
+ mcp: MaterializedMcp;
69
+ /** owned — the materialized A2A handle: the driver projects it onto `Prepared.a2a` only when it carries
70
+ * tools. Same rollback posture as `mcp`, pushed after it (disposed before it on unwind). */
71
+ a2a: MaterializedA2a;
72
+ /** borrowed-mutable — the RefreshMcpTools rebuild seam (`current` undefined until installed). Writers: the
73
+ * tool-disclosure mount, by exactly one of its two mutually exclusive arms (the deferred arm's
74
+ * rematerialize; the else-arm's bare setTools + fingerprint sync). Read by the refresh closure at run time. */
75
+ rebuildHarnessToolsRef: {
76
+ current?: () => Promise<void>;
77
+ };
78
+ /** borrowed-mutable — the content-origin re-classification seat. Writer: the memory-engine-session phase
79
+ * (installs the re-runnable pass ONCE when it arms — a mounted engine-memory session or an upstream recorder
80
+ * channel; undefined otherwise). Read by the rebuild closures before a swapped-in tool becomes callable. */
81
+ contentOriginWrapRef: {
82
+ current?: () => void;
83
+ };
84
+ /** borrowed-mutable — whether the tool_call gate handler was REGISTERED at prepare time. Writer: the driver,
85
+ * once, at gate registration. Read by the refresh closure's honesty warning. */
86
+ toolCallGateArmedRef: {
87
+ armed: boolean;
88
+ };
89
+ }
90
+ /** The M5 phase body — prepareTask's protocol-tools stretch, verbatim (see the module header). */
91
+ export declare function prepareProtocolTools(input: PrepareProtocolToolsInput): Promise<PrepareProtocolToolsResult>;
@@ -0,0 +1,179 @@
1
+ import { Type } from "typebox";
2
+ import { materializeMcpTools } from "../mcp.js";
3
+ import { materializeA2aTools } from "../a2a.js";
4
+ import { deliverEngineNotice } from "../types.js";
5
+ export async function prepareProtocolTools(input) {
6
+ const { lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, tools, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback } = input;
7
+ const mcp = lockedPreflight.mcp?.length
8
+ ? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId))
9
+ : { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], dispose: async () => { } };
10
+ if (lockedPreflight.mcp?.length)
11
+ rollback.push(function mcpDispose() { return mcp.dispose(); });
12
+ for (const w of mcp.warnings)
13
+ onceLedger.onError(w, { phase: "mcp", sessionId });
14
+ for (const d of mcp.droppedTools) {
15
+ onceLedger.onError(new Error(`MCP tool dropped at intake — ${d.tool} (server "${d.server}"): ${d.reason}`), { phase: "mcp", sessionId });
16
+ }
17
+ onceLedger.pruneDroppedTools(mcp.droppedTools);
18
+ {
19
+ const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
20
+ const clash = mcp.tools.find((t) => [t.name, ...(t.aliases ?? [])].some((name) => callerNames.has(name)));
21
+ if (clash) {
22
+ const e = new Error(`Tool name "${clash.name}" is reserved by an injected MCP tool — a caller tool of the same name would silently shadow it.`);
23
+ e.code = "config.reserved_tool_name";
24
+ throw e;
25
+ }
26
+ }
27
+ tools.push(...mcp.tools.map((t) => remoteToolOffload(t)));
28
+ const rebuildHarnessToolsRef = {};
29
+ const contentOriginWrapRef = {};
30
+ const toolCallGateArmedRef = { armed: false };
31
+ if (lockedPreflight.mcp?.length) {
32
+ tools.push({
33
+ name: "RefreshMcpTools",
34
+ label: "RefreshMcpTools",
35
+ description: "Refresh the tool list of connected MCP servers. Never dials or re-dials connections — it only re-reads the tool list over the existing connection. The refreshed tools are available immediately; you can call them on your next step. Use when a server's expected tool is missing, or the tool list looks stale after a connection recovered. Omit `server` to refresh every connected server.",
36
+ parameters: Type.Object({
37
+ server: Type.Optional(Type.String({ description: "Name of a single MCP server to refresh; omit to refresh all connected servers." })),
38
+ }),
39
+ execute: async (_toolCallId, params) => {
40
+ const serverArg = params?.server;
41
+ const results = await mcp.refresh(typeof serverArg === "string" && serverArg.length > 0 ? serverArg : undefined);
42
+ const lines = [];
43
+ let changed = false;
44
+ let anyActiveFailure = false;
45
+ for (const r of results) {
46
+ if (r.status !== "refreshed" || r.tools === undefined) {
47
+ lines.push(`${r.server}: ${r.status}${r.error !== undefined ? ` (${r.error})` : ""}`);
48
+ if (r.status === "failed")
49
+ anyActiveFailure = true;
50
+ continue;
51
+ }
52
+ const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
53
+ const pushable = r.tools.filter((t) => !excludedSet.has(t.name));
54
+ const excludedNow = r.tools.filter((t) => excludedSet.has(t.name)).map((t) => t.name);
55
+ const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(r.prefix)));
56
+ const restoreDomain = (m, snap) => {
57
+ for (const name of [...m.keys()])
58
+ if (name.startsWith(r.prefix))
59
+ m.delete(name);
60
+ for (const [name, v] of snap)
61
+ m.set(name, v);
62
+ };
63
+ const priorDomainEffects = domainSnapshot(toolEffects);
64
+ const priorDomainNegatives = domainSnapshot(axisExplicitNegatives);
65
+ for (const name of priorDomainEffects.keys())
66
+ toolEffects.delete(name);
67
+ for (const name of priorDomainNegatives.keys())
68
+ axisExplicitNegatives.delete(name);
69
+ try {
70
+ foldProtocolAxes((r.axes ?? []).filter((a) => !excludedSet.has(a.name)), "MCP");
71
+ }
72
+ catch (foldErr) {
73
+ restoreDomain(toolEffects, priorDomainEffects);
74
+ restoreDomain(axisExplicitNegatives, priorDomainNegatives);
75
+ lines.push(`${r.server}: failed (${foldErr instanceof Error ? foldErr.message : String(foldErr)})`);
76
+ anyActiveFailure = true;
77
+ continue;
78
+ }
79
+ let domainAnchor = -1;
80
+ for (let i = tools.length - 1; i >= 0; i--) {
81
+ const t = tools[i];
82
+ if (t.name.startsWith(r.prefix)) {
83
+ domainAnchor = i;
84
+ tools.splice(i, 1);
85
+ }
86
+ }
87
+ const refreshedMounts = pushable.map((t) => remoteToolOffload(t));
88
+ if (domainAnchor >= 0)
89
+ tools.splice(domainAnchor, 0, ...refreshedMounts);
90
+ else
91
+ tools.push(...refreshedMounts);
92
+ changed = true;
93
+ const detail = [];
94
+ const shownAdded = r.added.filter((n) => !excludedSet.has(n));
95
+ if (shownAdded.length > 0)
96
+ detail.push(`added: ${shownAdded.join(", ")}`);
97
+ if (r.removed.length > 0)
98
+ detail.push(`removed: ${r.removed.join(", ")}`);
99
+ if (excludedNow.length > 0)
100
+ detail.push(`excluded by deployment config (not mounted): ${excludedNow.join(", ")}`);
101
+ if ((r.dropped?.length ?? 0) > 0)
102
+ detail.push(`dropped: ${r.dropped.map((d) => `${d.tool} (${d.reason.length > 90 ? `${d.reason.slice(0, 90)}…` : d.reason})`).join("; ")}`);
103
+ lines.push(`${r.server}: refreshed — ${pushable.length} tool${pushable.length === 1 ? "" : "s"}${detail.length > 0 ? ` (${detail.join("; ")})` : ""}`);
104
+ }
105
+ if (changed)
106
+ await rebuildHarnessToolsRef.current?.();
107
+ if (changed && !toolCallGateArmedRef.armed && (irreversibleTools.size > 0 || egressTools.size > 0)) {
108
+ lines.push("WARNING: refreshed tools declare irreversible/egress safety hints, but this task started with no approval gate registered (no policy/hooks and no gated tools at start) — these hints cannot arm a gate mid-task (RB-46); the refreshed tools run ungated on this deployment shape.");
109
+ }
110
+ const text = lines.length === 0 ? "No connected MCP servers to refresh." : lines.join("\n");
111
+ return {
112
+ content: [{ type: "text", text }],
113
+ details: { results },
114
+ terminate: false,
115
+ ...(anyActiveFailure && !changed ? { isError: true } : {}),
116
+ };
117
+ },
118
+ });
119
+ toolEffects.set("RefreshMcpTools", "read");
120
+ }
121
+ const foldProtocolAxes = (axes, protocolLabel) => {
122
+ for (const axis of axes) {
123
+ if (axis.irreversibility === "always") {
124
+ irreversibilityTier.set(axis.name, "always");
125
+ irreversibleTools.add(axis.name);
126
+ }
127
+ if (axis.irreversibility === "never") {
128
+ axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), irreversible: false });
129
+ }
130
+ if (axis.egress === false) {
131
+ axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), egress: false });
132
+ }
133
+ if (axis.egress) {
134
+ if (axis.effect !== undefined && axis.effect !== "write") {
135
+ const e = new Error(`${protocolLabel} tool "${axis.name}" resolves to egress:true with effect:"${axis.effect}" — an egress tool (external write) must have effect:"write". Clear egress (toolAxes egress:false) if it is a pure read, or set effect:"write".`);
136
+ e.code = "config.egress_requires_write_effect";
137
+ throw e;
138
+ }
139
+ egressTools.add(axis.name);
140
+ }
141
+ if (!toolEffects.has(axis.name))
142
+ toolEffects.set(axis.name, axis.effect ?? "write");
143
+ }
144
+ };
145
+ foldProtocolAxes(mcp.toolAxes, "MCP");
146
+ const a2aDeclared = spec.a2a?.length ? spec.a2a : undefined;
147
+ const a2a = a2aDeclared
148
+ ? await materializeA2aTools(a2aDeclared, spec.principal, abortController.signal)
149
+ : { tools: [], toolAxes: [], warnings: [], statuses: [], refresh: async () => [], dispose: async () => { } };
150
+ if (a2aDeclared)
151
+ rollback.push(function a2aDispose() { return a2a.dispose(); });
152
+ for (const w of a2a.warnings)
153
+ deps.onError?.(w, { phase: "a2a", sessionId });
154
+ {
155
+ const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
156
+ const clash = a2a.tools.find((t) => callerNames.has(t.name));
157
+ if (clash) {
158
+ const e = new Error(`Tool name "${clash.name}" is reserved by an injected A2A tool — a caller tool of the same name would silently shadow it.`);
159
+ e.code = "config.reserved_tool_name";
160
+ throw e;
161
+ }
162
+ }
163
+ tools.push(...a2a.tools.map((t) => remoteToolOffload(t)));
164
+ foldProtocolAxes(a2a.toolAxes, "A2A");
165
+ return { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef };
166
+ }
167
+ function mcpRevocationWiring(deps, runId) {
168
+ if (deps.mcpRevocations === undefined)
169
+ return undefined;
170
+ const ledger = deps.mcpRevocations;
171
+ return {
172
+ isRevoked: (name) => ledger.isRevoked(name),
173
+ onProbeFailure: (e) => deliverEngineNotice(deps.onNotice, {
174
+ code: "mcp.revocation_probe_failed",
175
+ message: `the mcpRevocations.isRevoked probe threw — MCP dispatch fails OPEN (no server treated as revoked) until the probe recovers: ${e instanceof Error ? e.message : String(e)}`,
176
+ detail: { message: e instanceof Error ? e.message : String(e), runId },
177
+ }),
178
+ };
179
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * design/390 §1.2 M7 — prepareTask's QUESTION-FACE phase, verbatim from the driver: the leg's ONE checkpoint-store
3
+ * read, the durable mandate as it binds at the question seat, the content-ask binding table (with the leak
4
+ * bound the routing lane checks against — the constant below), the stranded-answer disclosure and the leg-end
5
+ * settle, the three-way mount decision (live face / durable face / override), and the AskUserQuestion mount
6
+ * with its redeemed-approval, posture, fallback and synthetic-continuation arms. The interface is the
7
+ * 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
+ * offload-reader mount before it and the scheduler mount after it (design/390 §1.5 S1: a stretch with no await
11
+ * is extracted as a sync function — an `async` wrapper would open a microtask window). Read-stability of the
12
+ * host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
13
+ *
14
+ * One seat is a LIVE GETTER (design/238 D-3): the inherited-unavailable marker set is declared by the driver's
15
+ * gate-fold section AFTER this phase and read per call at RUN time, so it enters as `() => ReadonlySet` — a
16
+ * read before the driver declares it throws (temporal dead zone), which is the fail-loud an uninitialized
17
+ * live read owes.
18
+ */
19
+ import type { AgentTool } from "../../internal/harness.js";
20
+ import { type OnQuestion } from "../ask-question.js";
21
+ import { type CheckpointStore } from "../checkpoint-store.js";
22
+ import type { RunnerDeps, RuntimeCaps, TaskSpec } from "../types.js";
23
+ import type { PrepareResume } from "./contracts.js";
24
+ import { type ContentAskBinding } from "./content-ask-bindings.js";
25
+ export { CONTENT_ASK_BINDING_CAP, type ContentAskBinding } from "./content-ask-bindings.js";
26
+ export interface PrepareQuestionFaceInput {
27
+ /** borrowed-readonly — the run's frozen question seat (spec > deps), resolved once at the top of prepare;
28
+ * the mount predicate and the live-face derivation both read this one value. */
29
+ frozenOnQuestion: OnQuestion | undefined;
30
+ /** borrowed-readonly — the REBOUND spec. Read: `checkpointStore` (the per-run off switch), `durableApproval`
31
+ * (the durable face's opt-in), `interactiveTools` (the override), `principal` (the tool's source identity),
32
+ * `interactiveQuestionFallback` (restores the continuation under a resolved posture). Never mutated. */
33
+ spec: Pick<TaskSpec, "checkpointStore" | "durableApproval" | "interactiveTools" | "principal" | "interactiveQuestionFallback">;
34
+ /** borrowed-readonly — the deployment seats: `checkpointStore` (the store the leg snapshot resolves against)
35
+ * and `onError` (the stranded-answer and synthetic-continuation operator lines; swallow-guarded). */
36
+ deps: Pick<RunnerDeps, "checkpointStore" | "onError">;
37
+ /** borrowed-readonly — the principal's resolved runtime caps as of this point (`forceDurableGate` is the
38
+ * mandate that outranks a live face only where a store exists to park the question). */
39
+ runtimeCaps: Pick<RuntimeCaps, "forceDurableGate"> | undefined;
40
+ /** borrowed-readonly — the resolved interaction posture (the door's value), threaded to the tool so both
41
+ * no-answer arms harden under it. */
42
+ resolvedInteractionPosture: "interactive" | "headless" | undefined;
43
+ /** borrowed-readonly — the durable-resume carrier; only the redeemed content-ask call id and its questions
44
+ * hash are read (the one call an operator's approval was spent on). */
45
+ resume: Pick<PrepareResume, "redeemedContentAskCallId" | "redeemedContentAskQuestionsHash"> | undefined;
46
+ /** borrowed-readonly — the acquired session id (the tool's source task id; operator-line context). */
47
+ sessionId: string;
48
+ /** borrowed-mutable — the run's shared roster (identity is the contract). Writer here: ONE push (the question
49
+ * tool) when the mount predicate holds; `contentAskRoutable` reads `includes` per call against the LIVE
50
+ * array (the roster is edited in place after the mount). */
51
+ tools: AgentTool[];
52
+ /** borrowed-readonly — LIVE GETTER over the inherited-unavailable marker set (declared by the driver's
53
+ * gate-fold section after this phase; `has` only, per call at run time). See the module header. */
54
+ inheritedUnavailableAsks: () => ReadonlySet<string>;
55
+ }
56
+ export interface PrepareQuestionFaceResult {
57
+ /** borrowed-readonly — THE leg's store snapshot (`resolveCheckpointStore(spec, deps)`): the mount face, the
58
+ * routability predicate, the manifest and suspendAsk all read this one value. */
59
+ checkpointStore: CheckpointStore | undefined;
60
+ /** borrowed-readonly — the run's live question face through the shared predicate (the reserved resume
61
+ * placeholder is a function and is NOT a live face), or undefined. */
62
+ liveQuestionFace: OnQuestion | undefined;
63
+ /** borrowed-mutable — the one-shot answer binding table for questions the GATE routed (scope = this leg).
64
+ * Writers (R-3): the routing lane (`set`, cap-checked against {@link CONTENT_ASK_BINDING_CAP} before every
65
+ * insertion), the mounted face here (`delete`, one-shot redemption or a mismatched-batch drop), the settle
66
+ * here (`clear`). Identity is the contract (the routing lane and the face share this exact map). */
67
+ contentAskBindings: Map<string, ContentAskBinding>;
68
+ /** owned — THE routability predicate for a content ask (per call, against the live roster and the marker set). */
69
+ contentAskRoutable: (toolCallId: string) => boolean;
70
+ /** borrowed-mutable — late answers noted before the leg settles. Writer: the routing lane's delivery-race
71
+ * watcher (`push`); reader: the settle here (merged into the result disclosure by delivery id). */
72
+ lateStrandedAnswers: Array<{
73
+ deliveryId: string;
74
+ toolCallId: string;
75
+ }>;
76
+ /** owned — THE disclosure lane for an answer a person gave that no call could collect (swallow-guarded). */
77
+ discloseStrandedAnswers: (records: ReadonlyArray<{
78
+ deliveryId: string;
79
+ toolCallId: string;
80
+ }>, why: string) => void;
81
+ /** owned — the leg-end settle (`Prepared.settleContentAskBindings`): discloses surviving answers, clears the
82
+ * table, returns the records the result carries. */
83
+ settleContentAskBindings: () => ReadonlyArray<{
84
+ deliveryId: string;
85
+ toolCallId: string;
86
+ }>;
87
+ /** owned — whether a durable delivery face exists (store wired ∧ (durableApproval ∨ forceDurableGate));
88
+ * the manifest's composition-lie detector reuses this exact value. */
89
+ durableQuestionFace: boolean;
90
+ /** owned — the face the tool was mounted with: the gate-bound answer for THIS call id if one exists (one-shot),
91
+ * else the live face itself; the raw frozen seat when no live face exists. */
92
+ mountedQuestionFace: OnQuestion | undefined;
93
+ /** owned — whether THIS prepare mounted the first-party question tool (the routing lane keys on it, not on
94
+ * the reserved name). */
95
+ questionToolMounted: boolean;
96
+ /** borrowed-readonly — the mounted tool object itself (the exact roster member), so the routing lane can
97
+ * validate a batch against the schema the executing call is re-checked against; undefined when not mounted. */
98
+ mountedQuestionTool: AgentTool | undefined;
99
+ }
100
+ /** The M7 phase body — prepareTask's question-face stretch, verbatim (see the module header). */
101
+ export declare function prepareQuestionFace(input: PrepareQuestionFaceInput): PrepareQuestionFaceResult;