@sema-agent/core 7.21.1 → 7.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -1
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +2 -1
- package/dist/core/ask-origin.d.ts +1 -0
- package/dist/core/ask-origin.js +3 -2
- package/dist/core/auto-mode-rebuild.js +6 -2
- package/dist/core/auto-mode.d.ts +40 -18
- package/dist/core/auto-mode.js +63 -45
- package/dist/core/checkpoint-store.d.ts +8 -8
- package/dist/core/engine-notice.d.ts +49 -1
- package/dist/core/engine-notice.js +14 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/mcp.d.ts +65 -0
- package/dist/core/mcp.js +190 -41
- package/dist/core/memory-engine/consolidation.d.ts +5 -0
- package/dist/core/memory-engine/consolidation.js +7 -3
- package/dist/core/memory-engine/engine.d.ts +8 -0
- package/dist/core/memory-engine/engine.js +25 -19
- package/dist/core/memory-engine/layout.d.ts +47 -9
- package/dist/core/memory-engine/layout.js +157 -57
- package/dist/core/memory-engine/session-incarnation.d.ts +27 -0
- package/dist/core/memory-engine/session-incarnation.js +63 -0
- package/dist/core/parent-spec-seat.d.ts +15 -3
- package/dist/core/parent-spec-seat.js +5 -0
- package/dist/core/permission-rule-model.d.ts +1 -2
- package/dist/core/retention.d.ts +8 -1
- package/dist/core/retention.js +2 -0
- package/dist/core/runner/classifier-ladder.js +1 -0
- package/dist/core/runner/contracts.d.ts +11 -0
- package/dist/core/runner/mcp-redial.d.ts +109 -0
- package/dist/core/runner/mcp-redial.js +60 -0
- package/dist/core/runner/memory-consolidation-driver.d.ts +2 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +9 -12
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -0
- package/dist/core/runner/prepare-hands-readface.js +13 -3
- package/dist/core/runner/prepare-memory.js +3 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +6 -1
- package/dist/core/runner/prepare-protocol-tools.js +20 -38
- package/dist/core/runner/prepare-task.js +3 -3
- package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -0
- package/dist/core/runner/prepare-wiring-manifest.js +2 -1
- package/dist/core/runner/run-identity-wiring.d.ts +3 -1
- package/dist/core/runner/run-identity-wiring.js +4 -1
- package/dist/core/runner/run-settle-and-teardown.d.ts +3 -1
- package/dist/core/runner/run-settle-and-teardown.js +6 -2
- package/dist/core/runner/run-terminal-adoption.d.ts +4 -1
- package/dist/core/runner/run-terminal-adoption.js +2 -6
- package/dist/core/runner/runtask.js +8 -5
- package/dist/core/runner/stream-lifecycle-verbs.d.ts +5 -1
- package/dist/core/runner/stream-lifecycle-verbs.js +23 -2
- package/dist/core/runner-deps.d.ts +16 -7
- package/dist/core/skill-listing-budget.d.ts +5 -4
- package/dist/core/task-result.d.ts +3 -2
- package/dist/core/task-spec.d.ts +2 -1
- package/dist/core/task-stream.d.ts +26 -0
- package/dist/core/tool-spec.d.ts +5 -1
- package/dist/core/trace.d.ts +5 -0
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/wiring-manifest.d.ts +23 -0
- package/dist/core/wiring-manifest.js +3 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +2 -2
- package/dist/tools/fs/bash-program-position.d.ts +26 -5
- package/dist/tools/fs/bash-program-position.js +63 -16
- package/dist/tools/fs/bash-readonly-classifier.d.ts +27 -17
- package/dist/tools/fs/bash-readonly-classifier.js +134 -55
- package/dist/tools/fs/fs-bash.js +43 -18
- package/dist/tools/fs/index.d.ts +4 -2
- package/dist/tools/fs/read-deny.d.ts +25 -11
- package/dist/tools/fs/read-deny.js +5 -1
- package/dist/tools/fs/safety.d.ts +1 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -1
- package/wire-changes/core@7.22.0.json +637 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The LIVE MCP roster lane: applying one server's intake result to a RUNNING roster, and the
|
|
3
|
+
* host-driven re-dial that is the second caller of it.
|
|
4
|
+
*
|
|
5
|
+
* Two faces change a task's MCP roster after prepare has finished — the model's `RefreshMcpTools`
|
|
6
|
+
* tool and the run handle's `reconnectMcpServer` verb — and what they must do to the roster is the
|
|
7
|
+
* same list in the same order: drop the deployment-excluded names, re-fold the safety axes (with the
|
|
8
|
+
* non-monotone restore arm on refusal), replace the server's whole name-prefix domain at its own
|
|
9
|
+
* anchor, rebuild the harness toolset, and re-mint the roster delta. That list lived inside the
|
|
10
|
+
* refresh tool's execute closure, where the second face could only have copied it; a copy would have
|
|
11
|
+
* been a second answer to "what does a mid-task roster change consist of".
|
|
12
|
+
*
|
|
13
|
+
* It is machinery, not a phase: the protocol-tools phase reaches DOWN for it to build the refresh
|
|
14
|
+
* tool, and the run's verb wiring reaches down for the same functions.
|
|
15
|
+
*/
|
|
16
|
+
import type { AgentTool } from "../../internal/harness.js";
|
|
17
|
+
import type { McpDroppedTool, McpReconnectResult, MaterializedMcp, McpToolAxis } from "../mcp.js";
|
|
18
|
+
import type { RunnerDeps, ToolEffect } from "../types.js";
|
|
19
|
+
import type { IrreversibilityTier, ToolFaceSnapshot } from "./contracts.js";
|
|
20
|
+
import type { RosterBuilder, ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
21
|
+
/** One server's assertion about its own prefix domain — the shape both faces reduce to. `tools` is
|
|
22
|
+
* the roster the domain must END UP with (empty withdraws it); `added`/`removed` are the reporting
|
|
23
|
+
* half and never drive the swap. */
|
|
24
|
+
export interface McpRosterDelta {
|
|
25
|
+
server: string;
|
|
26
|
+
prefix: string;
|
|
27
|
+
tools: AgentTool[];
|
|
28
|
+
axes: McpToolAxis[];
|
|
29
|
+
dropped: McpDroppedTool[];
|
|
30
|
+
}
|
|
31
|
+
/** The seats a roster application writes through. Every one of them is borrowed from the phase that
|
|
32
|
+
* minted it — this module owns nothing. */
|
|
33
|
+
export interface McpRosterApplyInput {
|
|
34
|
+
/** borrowed-readonly — the frozen tool face; `exclude` keeps a deployment-excluded tool from ever
|
|
35
|
+
* being re-mounted by a face the model or a host can trigger. */
|
|
36
|
+
toolFaceSnapshot: Pick<ToolFaceSnapshot, "exclude">;
|
|
37
|
+
/** borrowed-readonly — the per-server face overlay (the phase's `withFaces`). */
|
|
38
|
+
withFaces: (tools: AgentTool[], server: string) => AgentTool[];
|
|
39
|
+
/** borrowed-readonly — the large-result wrapper every protocol tool is mounted through. */
|
|
40
|
+
remoteToolOffload: (tool: AgentTool) => AgentTool;
|
|
41
|
+
/** borrowed-readonly — the phase's axis fold (throws on a config contradiction). */
|
|
42
|
+
foldProtocolAxes: (axes: McpToolAxis[], label: string) => void;
|
|
43
|
+
/** borrowed-mutable — the two NON-MONOTONE tables the fold writes and this applier snapshots. */
|
|
44
|
+
toolEffects: Map<string, ToolEffect>;
|
|
45
|
+
axisExplicitNegatives: Map<string, {
|
|
46
|
+
irreversible?: false;
|
|
47
|
+
egress?: false;
|
|
48
|
+
}>;
|
|
49
|
+
/** borrowed-mutable — the run's shared roster. */
|
|
50
|
+
roster: RosterBuilder;
|
|
51
|
+
}
|
|
52
|
+
/** What one application did, for the caller's receipt. `applied:false` means the axis fold refused
|
|
53
|
+
* and the domain was left exactly as it was (the tables restored) — no roster write happened. */
|
|
54
|
+
export type McpRosterApplyOutcome = {
|
|
55
|
+
applied: true;
|
|
56
|
+
pushed: number;
|
|
57
|
+
excluded: string[];
|
|
58
|
+
} | {
|
|
59
|
+
applied: false;
|
|
60
|
+
foldError: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Apply one server's delta to the live roster. Behavior-identical to the body this was extracted
|
|
64
|
+
* from; the comments are the extracted reasoning, not new claims.
|
|
65
|
+
*/
|
|
66
|
+
export declare function applyMcpRosterDelta(input: McpRosterApplyInput, delta: McpRosterDelta): McpRosterApplyOutcome;
|
|
67
|
+
/** The seats the post-application commit writes through. */
|
|
68
|
+
export interface McpRosterCommitInput {
|
|
69
|
+
rebuildHarnessToolsRef: {
|
|
70
|
+
current?: () => Promise<void>;
|
|
71
|
+
};
|
|
72
|
+
rosterSeat: ToolRosterDeltaSeat;
|
|
73
|
+
toolCallGateArmedRef: {
|
|
74
|
+
armed: boolean;
|
|
75
|
+
};
|
|
76
|
+
irreversibleTools: ReadonlySet<string>;
|
|
77
|
+
egressTools: ReadonlySet<string>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Publish whatever the applications above changed: rebuild the harness toolset (the content-origin
|
|
81
|
+
* re-wrap runs inside it), then re-mint the roster delta so the stream's rows describe the LIVE
|
|
82
|
+
* objects. Batched deliberately — a refresh spanning several servers rebuilds once.
|
|
83
|
+
*
|
|
84
|
+
* Returns the gate-honesty line when one is due. The built-in integrity policies are composed
|
|
85
|
+
* UNCONDITIONALLY, so `armed` is always true in this engine and the line never fires today; the arm
|
|
86
|
+
* stays as a zero-cost guard for a refactor that would make the composition conditional.
|
|
87
|
+
*/
|
|
88
|
+
export declare function commitMcpRosterChanges(input: McpRosterCommitInput): Promise<string | undefined>;
|
|
89
|
+
/** The re-dial's own seats: the handle it drives, the two operator sinks, and the run identity the
|
|
90
|
+
* disclosure names. */
|
|
91
|
+
export interface McpRedialInput extends McpRosterApplyInput, McpRosterCommitInput {
|
|
92
|
+
mcp: MaterializedMcp;
|
|
93
|
+
deps: Pick<RunnerDeps, "onError" | "onNotice">;
|
|
94
|
+
sessionId: string;
|
|
95
|
+
runId: string;
|
|
96
|
+
irreversibilityTier: IrreversibilityTier;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The run handle's `reconnectMcpServer` verb, phase-side: re-dial one server, put whatever it
|
|
100
|
+
* asserted about its prefix domain onto the live roster, and disclose.
|
|
101
|
+
*
|
|
102
|
+
* The roster is written IFF the result carries `tools` — the one rule this lane reads. That covers
|
|
103
|
+
* both directions with no second condition: a successful re-dial asserts its fresh roster, a failed
|
|
104
|
+
* DIAL asserts the empty one (the connection it tore down is gone, so leaving its names mounted would
|
|
105
|
+
* leave the model holding tools that can only fail), and an arm that never touched the connection —
|
|
106
|
+
* unknown name, revoked server, re-dial already in flight, disposed materialization — asserts nothing
|
|
107
|
+
* and the mounted roster is left alone.
|
|
108
|
+
*/
|
|
109
|
+
export declare function redialMcpServer(input: McpRedialInput, server: string): Promise<McpReconnectResult>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { deliverEngineNotice } from "../types.js";
|
|
2
|
+
export function applyMcpRosterDelta(input, delta) {
|
|
3
|
+
const { toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes, toolEffects, axisExplicitNegatives, roster } = input;
|
|
4
|
+
const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
|
|
5
|
+
const pushable = delta.tools.filter((t) => !excludedSet.has(t.name));
|
|
6
|
+
const excluded = delta.tools.filter((t) => excludedSet.has(t.name)).map((t) => t.name);
|
|
7
|
+
const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(delta.prefix)));
|
|
8
|
+
const restoreDomain = (m, snap) => {
|
|
9
|
+
for (const name of [...m.keys()])
|
|
10
|
+
if (name.startsWith(delta.prefix))
|
|
11
|
+
m.delete(name);
|
|
12
|
+
for (const [name, v] of snap)
|
|
13
|
+
m.set(name, v);
|
|
14
|
+
};
|
|
15
|
+
const priorDomainEffects = domainSnapshot(toolEffects);
|
|
16
|
+
const priorDomainNegatives = domainSnapshot(axisExplicitNegatives);
|
|
17
|
+
for (const name of priorDomainEffects.keys())
|
|
18
|
+
toolEffects.delete(name);
|
|
19
|
+
for (const name of priorDomainNegatives.keys())
|
|
20
|
+
axisExplicitNegatives.delete(name);
|
|
21
|
+
try {
|
|
22
|
+
foldProtocolAxes(delta.axes.filter((a) => !excludedSet.has(a.name)), "MCP");
|
|
23
|
+
}
|
|
24
|
+
catch (foldErr) {
|
|
25
|
+
restoreDomain(toolEffects, priorDomainEffects);
|
|
26
|
+
restoreDomain(axisExplicitNegatives, priorDomainNegatives);
|
|
27
|
+
return { applied: false, foldError: foldErr instanceof Error ? foldErr.message : String(foldErr) };
|
|
28
|
+
}
|
|
29
|
+
roster.replaceDomain(delta.prefix, withFaces(pushable, delta.server).map((t) => remoteToolOffload(t)), { source: "mcp", mountedBy: "optional", origin: { peer: delta.server } });
|
|
30
|
+
return { applied: true, pushed: pushable.length, excluded };
|
|
31
|
+
}
|
|
32
|
+
export async function commitMcpRosterChanges(input) {
|
|
33
|
+
await input.rebuildHarnessToolsRef.current?.();
|
|
34
|
+
input.rosterSeat.republish();
|
|
35
|
+
if (!input.toolCallGateArmedRef.armed && (input.irreversibleTools.size > 0 || input.egressTools.size > 0)) {
|
|
36
|
+
return "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.";
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
export async function redialMcpServer(input, server) {
|
|
41
|
+
const result = await input.mcp.reconnect(server);
|
|
42
|
+
if (result.tools !== undefined) {
|
|
43
|
+
const outcome = applyMcpRosterDelta(input, { server: result.server, prefix: result.prefix, tools: result.tools, axes: result.axes ?? [], dropped: result.dropped ?? [] });
|
|
44
|
+
if (!outcome.applied)
|
|
45
|
+
input.deps.onError?.(Object.assign(new Error(`MCP re-dial of "${server}" connected but its safety-axis fold refused, so its tools were not mounted: ${outcome.foldError}`), { code: "mcp.server_unavailable" }), { phase: "mcp", sessionId: input.sessionId });
|
|
46
|
+
const ungated = await commitMcpRosterChanges(input);
|
|
47
|
+
if (ungated !== undefined)
|
|
48
|
+
input.deps.onError?.(Object.assign(new Error(ungated), { code: "mcp.server_unavailable" }), { phase: "mcp", sessionId: input.sessionId });
|
|
49
|
+
}
|
|
50
|
+
for (const w of result.warnings ?? [])
|
|
51
|
+
input.deps.onError?.(Object.assign(new Error(w), { code: "mcp.server_unavailable" }), { phase: "mcp", sessionId: input.sessionId });
|
|
52
|
+
deliverEngineNotice(input.deps.onNotice, {
|
|
53
|
+
code: "mcp.server_redialed",
|
|
54
|
+
message: result.outcome === "reconnected"
|
|
55
|
+
? `MCP server "${server}" was re-dialed at the host's request: ${result.toolCount} tool${result.toolCount === 1 ? "" : "s"} mounted (${result.added.length} added, ${result.removed.length} removed)`
|
|
56
|
+
: `MCP server "${server}" was NOT re-dialed (${result.outcome})${result.reason !== undefined ? `: ${result.reason}` : ""}`,
|
|
57
|
+
detail: { server, outcome: result.outcome, added: result.added.length, removed: result.removed.length, ...(result.reason !== undefined ? { reason: result.reason } : {}), sessionId: input.sessionId, runId: input.runId },
|
|
58
|
+
});
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
@@ -94,6 +94,8 @@ export declare const AUTO_RUN_ON_RECOMMENDATION_DEFAULT = true;
|
|
|
94
94
|
* switch wired AND a model seat resolvable) — the reference CLI's "auto-dream: off while
|
|
95
95
|
* auto-memory is off": a default-on world must not arm a deployment that wired no consolidation;
|
|
96
96
|
* - any other value ⇒ off (never arm on garbage; the door refuses it at prepare anyway).
|
|
97
|
+
*
|
|
98
|
+
* @contract memory.auto_consolidation.arming_has_one_resolver — whether a deployment's automatic consolidation is armed is answered here and nowhere else, a host that merely DISPLAYS the arming included: an ABSENT seat is not "off" (it reads the named default, and only where both seats are wired), so a reader branching on `RunnerDeps.autoRunOnRecommendation` alone is wrong in BOTH directions — it calls a default-armed deployment unarmed and a deployment with no consolidation armed. A deployment fact, never a per-session one; exported from the package root for that reason.
|
|
97
99
|
*/
|
|
98
100
|
export declare function autoRunOnRecommendationEffective(deps: MemoryConsolidationDriverDeps & {
|
|
99
101
|
autoRunOnRecommendation?: unknown;
|
|
@@ -7,7 +7,7 @@ import { isSelfOrchestrationActive } from "../../orchestration/workflow-script-r
|
|
|
7
7
|
import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
8
8
|
import { autoModeArmingRecipeOf } from "../auto-mode-arming.js";
|
|
9
9
|
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
|
|
10
|
-
import { autoModeClassifierFallbackOf,
|
|
10
|
+
import { autoModeClassifierFallbackOf, createAutoModeDecider, createAutoModeDenialTracker } from "../auto-mode.js";
|
|
11
11
|
import { checkpointStoreOfSeat } from "../checkpoint-seat.js";
|
|
12
12
|
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, resolveComplianceDenies } from "../compliance.js";
|
|
13
13
|
import { createPresentPlanTool, createEnterPlanModeTool } from "../present-plan-tool.js";
|
|
@@ -207,27 +207,24 @@ export async function prepareCapsAndWorkflow(input) {
|
|
|
207
207
|
const classifierSystemPrompt = buildAutoModePrompt(classifierLaneRule ? { ...am, crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : am);
|
|
208
208
|
const classifierRuntime = brainToRuntime(deps.brain);
|
|
209
209
|
const classifierRungs = classifierLadderRungs(am.classifierFallback, classifierModel, model);
|
|
210
|
-
const roundPrompt = new WeakMap();
|
|
211
210
|
autoModeDenialTracking = createAutoModeDenialTracker(am.denialLimit);
|
|
212
211
|
autoModeDecider = createAutoModeDecider({
|
|
213
212
|
...(am.timeoutMs !== undefined ? { timeoutMs: am.timeoutMs } : {}),
|
|
214
213
|
candidates: classifierRungs.map((m) => m.id),
|
|
215
214
|
onClassified: classifierRoundObserver({ tracer: deps.tracer, onNotice: deps.onNotice, taskId: hostTaskId, seatModelId: classifierModel.id, ...(sessionId !== undefined ? { sessionId } : {}), ...(runId !== undefined ? { runId } : {}) }),
|
|
216
|
-
|
|
215
|
+
assemble: async (input) => {
|
|
216
|
+
const ctx = await session.buildContext();
|
|
217
|
+
const known = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m) && (m.role === "user" || m.role === "assistant" || m.role === "toolResult"));
|
|
218
|
+
return renderAutoModeWindow(known, am.window) + renderAutoModeAction(input);
|
|
219
|
+
},
|
|
220
|
+
classify: async (_input, signal, rung, userPrompt) => {
|
|
217
221
|
const rungModel = rung !== undefined ? classifierRungs[rung.index] : undefined;
|
|
218
222
|
if (rung === undefined || rungModel === undefined) {
|
|
219
223
|
throw new Error(`the auto-mode classifier was asked at ladder position ${JSON.stringify(rung?.index)}, which this arming did not build`);
|
|
220
224
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
build = (async () => {
|
|
224
|
-
const ctx = await session.buildContext();
|
|
225
|
-
const known = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m) && (m.role === "user" || m.role === "assistant" || m.role === "toolResult"));
|
|
226
|
-
return renderAutoModeWindow(known, am.window) + renderAutoModeAction(input);
|
|
227
|
-
})();
|
|
228
|
-
roundPrompt.set(input, build);
|
|
225
|
+
if (userPrompt === undefined) {
|
|
226
|
+
throw new Error("the auto-mode classifier was asked with no assembled question, which this arming builds once per classification");
|
|
229
227
|
}
|
|
230
|
-
const userPrompt = await build.catch((e) => { throw autoModeRoundNotAsked(e); });
|
|
231
228
|
const classifierAuth = await spec.getApiKeyAndHeaders?.(rungModel);
|
|
232
229
|
const request = async (seat, suffix) => {
|
|
233
230
|
const response = await classifierRuntime.completeSimple(rungModel, { systemPrompt: classifierSystemPrompt, messages: [{ role: "user", content: userPrompt + suffix, timestamp: Date.now() }] }, {
|
|
@@ -284,6 +284,9 @@ export interface PrepareHandsReadFaceResult {
|
|
|
284
284
|
pattern: string;
|
|
285
285
|
caseSensitive: boolean;
|
|
286
286
|
}>;
|
|
287
|
+
/** owned — the built-in deny TIERS this leg MATERIALLY activated, off the same snapshot the compile
|
|
288
|
+
* judged with; empty ⇒ none. Relayed to the wiring manifest, never re-derived from `deps` there. */
|
|
289
|
+
readDenyBuiltinTiersActive: readonly string[];
|
|
287
290
|
/** write-back (D-3 hold) — the hands-LESS legs' resolved face (the read posture is a
|
|
288
291
|
* property of the RUN, not the mount). Driver assigns onto its outer `let`. */
|
|
289
292
|
handsLessResolvedFace: ContainmentFace | undefined;
|
|
@@ -3,7 +3,7 @@ import { StubExecutionEnv } from "../stub-env.js";
|
|
|
3
3
|
import { hasBackgroundShell } from "../background-shell.js";
|
|
4
4
|
import { boundaryIsTheSandbox } from "../remote-env.js";
|
|
5
5
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
6
|
-
import { bashReadBoundaryProbe, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf,
|
|
6
|
+
import { bashReadBoundaryProbe, bashReversibilityProbe, activeReadDenyBuiltinTiers, compileReadDeny, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
|
|
7
7
|
import { resolveKey, sha256 } from "../../tools/fs/safety.js";
|
|
8
8
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
9
9
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
@@ -98,6 +98,7 @@ export function resolveHandsLessReadFace(input) {
|
|
|
98
98
|
let handsLessResolvedFace;
|
|
99
99
|
let handsLessResolvedWriteFace;
|
|
100
100
|
let readDenyAdditionsNormalized = [];
|
|
101
|
+
let readDenyBuiltinTiersActive = [];
|
|
101
102
|
{
|
|
102
103
|
const liveFace = resolveContainmentFace({
|
|
103
104
|
kind: "read",
|
|
@@ -120,7 +121,7 @@ export function resolveHandsLessReadFace(input) {
|
|
|
120
121
|
const seedWrite = resume?.seed.writeFace;
|
|
121
122
|
handsLessResolvedWriteFace = resume !== undefined && (seedWrite === undefined || seedWrite.face === "roots") ? "roots" : liveWriteFace;
|
|
122
123
|
const denyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seed?.denyEntries ?? [])];
|
|
123
|
-
|
|
124
|
+
readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers({
|
|
124
125
|
...(deps.readDenyBuiltinTiers !== undefined ? { tiers: deps.readDenyBuiltinTiers } : {}),
|
|
125
126
|
...(deps.readDenyBuiltinExclude !== undefined ? { exclude: deps.readDenyBuiltinExclude } : {}),
|
|
126
127
|
});
|
|
@@ -139,6 +140,7 @@ export function resolveHandsLessReadFace(input) {
|
|
|
139
140
|
resolvedReadFace: undefined,
|
|
140
141
|
resolvedWriteFace: undefined,
|
|
141
142
|
readDenyAdditionsNormalized,
|
|
143
|
+
readDenyBuiltinTiersActive,
|
|
142
144
|
handsLessResolvedFace,
|
|
143
145
|
handsLessResolvedWriteFace,
|
|
144
146
|
additionalRootsCanonical: [],
|
|
@@ -168,6 +170,7 @@ export async function prepareHandsMount(input) {
|
|
|
168
170
|
let resolvedReadFace;
|
|
169
171
|
let resolvedWriteFace;
|
|
170
172
|
let readDenyAdditionsNormalized = [];
|
|
173
|
+
let readDenyBuiltinTiersActive = [];
|
|
171
174
|
let handsLessResolvedFace;
|
|
172
175
|
const handsLessResolvedWriteFace = undefined;
|
|
173
176
|
const shellGatedTools = new Set();
|
|
@@ -245,6 +248,7 @@ export async function prepareHandsMount(input) {
|
|
|
245
248
|
...(deps.readDenyBuiltinExclude !== undefined ? { exclude: [...deps.readDenyBuiltinExclude] } : {}),
|
|
246
249
|
};
|
|
247
250
|
readDenyMatcher = compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg);
|
|
251
|
+
readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers(readDenyBuiltinCfg);
|
|
248
252
|
readDenyAdditionsNormalized = compileReadDeny(readDenyAdditions, "readDenyPatterns", { tiers: [] }).entries;
|
|
249
253
|
let liveReadFace = resolveContainmentFace({
|
|
250
254
|
kind: "read",
|
|
@@ -323,11 +327,16 @@ export async function prepareHandsMount(input) {
|
|
|
323
327
|
sessionSeats.reanchor(sessionId, readFileState, ownCommittedTailEntryId);
|
|
324
328
|
}
|
|
325
329
|
: undefined;
|
|
330
|
+
const compiledDeny = readDenyMatcher;
|
|
331
|
+
const denySeatJudge = compiledDeny !== undefined && compiledDeny.entries.length > 0
|
|
332
|
+
? (p) => compiledDeny.matchPath(p)?.pattern ?? null
|
|
333
|
+
: undefined;
|
|
326
334
|
const shellReadBoundary = () => ({
|
|
327
335
|
roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
328
336
|
...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
329
337
|
...(executionEnv.homeDir !== undefined ? { homeDir: executionEnv.homeDir } : {}),
|
|
330
|
-
denyMatch:
|
|
338
|
+
...(denySeatJudge !== undefined ? { denyMatch: denySeatJudge } : {}),
|
|
339
|
+
denyEntries: (compiledDeny?.entries ?? []).map((e) => e.pattern),
|
|
331
340
|
...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
|
|
332
341
|
});
|
|
333
342
|
const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
|
|
@@ -421,6 +430,7 @@ export async function prepareHandsMount(input) {
|
|
|
421
430
|
resolvedWriteFace,
|
|
422
431
|
handsLessResolvedWriteFace,
|
|
423
432
|
readDenyAdditionsNormalized,
|
|
433
|
+
readDenyBuiltinTiersActive,
|
|
424
434
|
handsLessResolvedFace,
|
|
425
435
|
additionalRootsCanonical,
|
|
426
436
|
additionalReadRootsCanonical,
|
|
@@ -171,7 +171,7 @@ export async function prepareMemory(input) {
|
|
|
171
171
|
const choosePersonalBackend = () => typeof pinned === "string" && pinned ? new FileMemoryEngineBackend(personalMemoryDir, { controlDir: personalControlDir }) : backend;
|
|
172
172
|
const createPersonalEngine = (personalBackend) => {
|
|
173
173
|
return {
|
|
174
|
-
engine: new MemoryEngine({ backend: personalBackend, memoryDir: personalMemoryDir, controlDir: personalControlDir, onIncident: onEngineIncident, provenance: input.memoryProvenance, ...captureStoreOpt(personalBackend, personalControlDir), ...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}) }),
|
|
174
|
+
engine: new MemoryEngine({ backend: personalBackend, memoryDir: personalMemoryDir, controlDir: personalControlDir, onIncident: onEngineIncident, provenance: input.memoryProvenance, ...captureStoreOpt(personalBackend, personalControlDir), ...(deps.memorySessionIncarnation !== undefined ? { sessionIncarnation: deps.memorySessionIncarnation } : {}), ...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}) }),
|
|
175
175
|
backend: personalBackend,
|
|
176
176
|
};
|
|
177
177
|
};
|
|
@@ -359,7 +359,7 @@ export async function prepareMemory(input) {
|
|
|
359
359
|
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
|
|
360
360
|
onIncident: onEngineIncident,
|
|
361
361
|
provenance: input.memoryProvenance,
|
|
362
|
-
...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)),
|
|
362
|
+
...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)), ...(deps.memorySessionIncarnation !== undefined ? { sessionIncarnation: deps.memorySessionIncarnation } : {}),
|
|
363
363
|
...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}),
|
|
364
364
|
});
|
|
365
365
|
const personal = createPersonalEngine(personalBackendChosen);
|
|
@@ -439,7 +439,7 @@ export async function prepareMemory(input) {
|
|
|
439
439
|
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
|
|
440
440
|
onIncident: onEngineIncident,
|
|
441
441
|
provenance: input.memoryProvenance,
|
|
442
|
-
...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)),
|
|
442
|
+
...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)), ...(deps.memorySessionIncarnation !== undefined ? { sessionIncarnation: deps.memorySessionIncarnation } : {}),
|
|
443
443
|
...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}),
|
|
444
444
|
});
|
|
445
445
|
await armCaptureOptOut(engine);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool } from "../../internal/harness.js";
|
|
2
|
-
import { type MaterializedMcp, type McpDroppedTool } from "../mcp.js";
|
|
2
|
+
import { type MaterializedMcp, type McpDroppedTool, type McpReconnectResult } from "../mcp.js";
|
|
3
3
|
import { type MaterializedA2a } from "../a2a.js";
|
|
4
4
|
import type { LockedPreflight } from "../locked-config.js";
|
|
5
5
|
import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
|
|
@@ -90,6 +90,11 @@ export interface PrepareProtocolToolsResult {
|
|
|
90
90
|
toolCallGateArmedRef: {
|
|
91
91
|
armed: boolean;
|
|
92
92
|
};
|
|
93
|
+
/** owned — the run handle's `reconnectMcpServer` implementation, present ONLY when this leg declared
|
|
94
|
+
* MCP servers (a leg with none has nothing to re-dial, and the stream verb refuses typed rather
|
|
95
|
+
* than answering about a materialization that was never made). The driver projects it onto
|
|
96
|
+
* `Prepared.mcpRedial`, from which the run's wiring binds the verb's seat. */
|
|
97
|
+
mcpRedial?: (server: string) => Promise<McpReconnectResult>;
|
|
93
98
|
}
|
|
94
99
|
/** The M5 phase body — prepareTask's protocol-tools stretch, verbatim (see the module header). */
|
|
95
100
|
export declare function prepareProtocolTools(input: PrepareProtocolToolsInput): Promise<PrepareProtocolToolsResult>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { materializeMcpTools } from "../mcp.js";
|
|
3
|
+
import { applyMcpRosterDelta, commitMcpRosterChanges, redialMcpServer } from "./mcp-redial.js";
|
|
3
4
|
import { materializeA2aTools } from "../a2a.js";
|
|
4
5
|
import { deliverEngineNotice } from "../types.js";
|
|
5
6
|
import { RosterBuilder } from "../tool-roster.js";
|
|
@@ -16,7 +17,8 @@ export async function prepareProtocolTools(input) {
|
|
|
16
17
|
}
|
|
17
18
|
const mcp = lockedPreflight.mcp?.length
|
|
18
19
|
? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId))
|
|
19
|
-
:
|
|
20
|
+
:
|
|
21
|
+
{ tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], reconnect: async (server) => ({ server, prefix: mintNamespacePrefix(MCP_NAMESPACE, server), outcome: "not_declared", reason: "this task declared no MCP servers", toolCount: 0, added: [], removed: [] }), dispose: async () => { } };
|
|
20
22
|
if (lockedPreflight.mcp?.length)
|
|
21
23
|
rollback.push(function mcpDispose() { return mcp.dispose(); });
|
|
22
24
|
for (const w of mcp.warnings)
|
|
@@ -58,6 +60,10 @@ export async function prepareProtocolTools(input) {
|
|
|
58
60
|
const rebuildHarnessToolsRef = {};
|
|
59
61
|
const contentOriginWrapRef = {};
|
|
60
62
|
const toolCallGateArmedRef = { armed: false };
|
|
63
|
+
const rosterApply = { toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes: (axes, label) => foldProtocolAxes(axes, label), toolEffects, axisExplicitNegatives, roster };
|
|
64
|
+
const rosterCommit = { rebuildHarnessToolsRef, rosterSeat, toolCallGateArmedRef, irreversibleTools, egressTools };
|
|
65
|
+
const redialInput = { ...rosterApply, ...rosterCommit, mcp, deps, sessionId, runId, irreversibilityTier };
|
|
66
|
+
const mcpRedial = lockedPreflight.mcp?.length ? (server) => redialMcpServer(redialInput, server) : undefined;
|
|
61
67
|
if (lockedPreflight.mcp?.length) {
|
|
62
68
|
roster.mount({
|
|
63
69
|
...toolFace("refresh-mcp-tools"),
|
|
@@ -73,42 +79,20 @@ export async function prepareProtocolTools(input) {
|
|
|
73
79
|
const lines = [];
|
|
74
80
|
let changed = false;
|
|
75
81
|
let anyActiveFailure = false;
|
|
82
|
+
const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
|
|
76
83
|
for (const r of results) {
|
|
77
|
-
if (r.
|
|
84
|
+
if (r.tools === undefined) {
|
|
78
85
|
lines.push(`${r.server}: ${r.status}${r.error !== undefined ? ` (${r.error})` : ""}`);
|
|
79
86
|
if (r.status === "failed")
|
|
80
87
|
anyActiveFailure = true;
|
|
81
88
|
continue;
|
|
82
89
|
}
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(r.prefix)));
|
|
87
|
-
const restoreDomain = (m, snap) => {
|
|
88
|
-
for (const name of [...m.keys()])
|
|
89
|
-
if (name.startsWith(r.prefix))
|
|
90
|
-
m.delete(name);
|
|
91
|
-
for (const [name, v] of snap)
|
|
92
|
-
m.set(name, v);
|
|
93
|
-
};
|
|
94
|
-
const priorDomainEffects = domainSnapshot(toolEffects);
|
|
95
|
-
const priorDomainNegatives = domainSnapshot(axisExplicitNegatives);
|
|
96
|
-
for (const name of priorDomainEffects.keys())
|
|
97
|
-
toolEffects.delete(name);
|
|
98
|
-
for (const name of priorDomainNegatives.keys())
|
|
99
|
-
axisExplicitNegatives.delete(name);
|
|
100
|
-
try {
|
|
101
|
-
foldProtocolAxes((r.axes ?? []).filter((a) => !excludedSet.has(a.name)), "MCP");
|
|
102
|
-
}
|
|
103
|
-
catch (foldErr) {
|
|
104
|
-
restoreDomain(toolEffects, priorDomainEffects);
|
|
105
|
-
restoreDomain(axisExplicitNegatives, priorDomainNegatives);
|
|
106
|
-
lines.push(`${r.server}: failed (${foldErr instanceof Error ? foldErr.message : String(foldErr)})`);
|
|
90
|
+
const applied = applyMcpRosterDelta(rosterApply, { server: r.server, prefix: r.prefix, tools: r.tools, axes: r.axes ?? [], dropped: r.dropped ?? [] });
|
|
91
|
+
if (!applied.applied) {
|
|
92
|
+
lines.push(`${r.server}: failed (${applied.foldError})`);
|
|
107
93
|
anyActiveFailure = true;
|
|
108
94
|
continue;
|
|
109
95
|
}
|
|
110
|
-
const refreshedMounts = withFaces(pushable, r.server).map((t) => remoteToolOffload(t));
|
|
111
|
-
roster.replaceDomain(r.prefix, refreshedMounts, { source: "mcp", mountedBy: "optional", origin: { peer: r.server } });
|
|
112
96
|
changed = true;
|
|
113
97
|
const detail = [];
|
|
114
98
|
const shownAdded = r.added.filter((n) => !excludedSet.has(n));
|
|
@@ -116,18 +100,16 @@ export async function prepareProtocolTools(input) {
|
|
|
116
100
|
detail.push(`added: ${shownAdded.join(", ")}`);
|
|
117
101
|
if (r.removed.length > 0)
|
|
118
102
|
detail.push(`removed: ${r.removed.join(", ")}`);
|
|
119
|
-
if (
|
|
120
|
-
detail.push(`excluded by deployment config (not mounted): ${
|
|
103
|
+
if (applied.excluded.length > 0)
|
|
104
|
+
detail.push(`excluded by deployment config (not mounted): ${applied.excluded.join(", ")}`);
|
|
121
105
|
if ((r.dropped?.length ?? 0) > 0)
|
|
122
106
|
detail.push(`dropped: ${r.dropped.map((d) => `${d.tool} (${d.reason.length > 90 ? `${d.reason.slice(0, 90)}…` : d.reason})`).join("; ")}`);
|
|
123
|
-
lines.push(`${r.server}: refreshed — ${
|
|
107
|
+
lines.push(`${r.server}: refreshed — ${applied.pushed} tool${applied.pushed === 1 ? "" : "s"}${detail.length > 0 ? ` (${detail.join("; ")})` : ""}`);
|
|
124
108
|
}
|
|
125
|
-
if (changed)
|
|
126
|
-
await
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (changed && !toolCallGateArmedRef.armed && (irreversibleTools.size > 0 || egressTools.size > 0)) {
|
|
130
|
-
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
|
+
if (changed) {
|
|
110
|
+
const ungated = await commitMcpRosterChanges(rosterCommit);
|
|
111
|
+
if (ungated !== undefined)
|
|
112
|
+
lines.push(ungated);
|
|
131
113
|
}
|
|
132
114
|
const text = lines.length === 0 ? "No connected MCP servers to refresh." : lines.join("\n");
|
|
133
115
|
return {
|
|
@@ -184,7 +166,7 @@ export async function prepareProtocolTools(input) {
|
|
|
184
166
|
for (const t of a2a.tools)
|
|
185
167
|
roster.mount(remoteToolOffload(t), { source: "a2a", mountedBy: "optional", origin: { peer: A2A_NAMESPACE.parse(t.name)?.peer ?? t.name } });
|
|
186
168
|
foldProtocolAxes(a2a.toolAxes, "A2A");
|
|
187
|
-
return { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef };
|
|
169
|
+
return { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef, ...(mcpRedial !== undefined ? { mcpRedial } : {}) };
|
|
188
170
|
}
|
|
189
171
|
function mcpRevocationWiring(deps, runId) {
|
|
190
172
|
if (deps.mcpRevocations === undefined)
|
|
@@ -436,7 +436,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
436
436
|
roster.mount(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined), { source: "synthetic", mountedBy: "optional" });
|
|
437
437
|
}
|
|
438
438
|
const rosterSeat = new ToolRosterDeltaSeat();
|
|
439
|
-
const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
|
|
439
|
+
const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef, mcpRedial } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
|
|
440
440
|
const callIssuedAtRef = {};
|
|
441
441
|
const workCutRef = { continuation: false, abortedBeforeTurnEnd: false };
|
|
442
442
|
const advertisedWritableDirs = handsEnabled
|
|
@@ -690,7 +690,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
690
690
|
}
|
|
691
691
|
}
|
|
692
692
|
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
693
|
-
const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined });
|
|
693
|
+
const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined });
|
|
694
694
|
if (fileHistoryBoundary !== undefined) {
|
|
695
695
|
harness.on("tool_call", async () => {
|
|
696
696
|
await fileHistoryBoundary.settle();
|
|
@@ -706,7 +706,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
706
706
|
const preparedHolder = {};
|
|
707
707
|
const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
|
|
708
708
|
await onceLedger.settle(session, announcedListingsRef);
|
|
709
|
-
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
|
|
709
|
+
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
|
|
710
710
|
rollback.commit();
|
|
711
711
|
const prepared = buildPrepared();
|
|
712
712
|
preparedHolder.current = prepared;
|
|
@@ -108,6 +108,10 @@ export interface PrepareWiringManifestInput {
|
|
|
108
108
|
/** borrowed-readonly — the leg's minted tool roster: rides the manifest's `tools` section and is the
|
|
109
109
|
* source of the hand band's write-capable names for the ungated-write warning (what actually MOUNTED). */
|
|
110
110
|
toolRoster: ToolRoster;
|
|
111
|
+
/** borrowed-readonly — from the read-face phase: the built-in read-deny TIERS that leg's own
|
|
112
|
+
* resolution materially activated; relayed onto the manifest (present iff non-empty), never
|
|
113
|
+
* re-derived from `deps` here (which by this phase sits past that phase's await). */
|
|
114
|
+
readDenyBuiltinTiersActive: readonly string[];
|
|
111
115
|
/** borrowed-readonly — from the LSP phase's manager fold: gates the manifest's LSP section (absent ⇒ byte-identical manifest). */ lspManagerWired: boolean;
|
|
112
116
|
/** borrowed-readonly — from the LSP phase: is the diagnostics lane live? The `lane` word's only input. */ lspDiagnosticsLive: boolean;
|
|
113
117
|
/** borrowed-readonly — the arming outcome the caps phase minted (the manifest's auto-mode face). */
|
|
@@ -113,7 +113,7 @@ function announceDurableGateUnavailable(args) {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
export function prepareWiringManifest(input) {
|
|
116
|
-
const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, lspManagerWired, lspDiagnosticsLive } = input;
|
|
116
|
+
const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive } = input;
|
|
117
117
|
const onAsk = spec.onAsk ?? deps.onAsk;
|
|
118
118
|
const handWriteTools = toolRoster.entries.filter((e) => e.mountedBy === "hands" && (e.effect === "write" || e.effect === "idempotent")).map((e) => e.name);
|
|
119
119
|
const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
|
|
@@ -208,6 +208,7 @@ export function prepareWiringManifest(input) {
|
|
|
208
208
|
autoMode: { ...autoModeArmReason },
|
|
209
209
|
mcp: mcpManifestEntries(lockedPreflight.mcp, mcp.statuses),
|
|
210
210
|
...(writeProtection !== undefined ? { writeProtectionTargetView: writeProtection.targetView } : {}),
|
|
211
|
+
readDenyBuiltinTiersActive,
|
|
211
212
|
autoConsolidationOnRecommendation: autoRunOnRecommendationEffective(deps),
|
|
212
213
|
tools: toolRoster,
|
|
213
214
|
hooks: wiredHookEntries(hooks, spec.hooks !== undefined ? "spec" : "deps", preToolUseObservational),
|
|
@@ -3,7 +3,7 @@ import { type PendingSessionNotifications, type SystemInjectionPriority, type Ta
|
|
|
3
3
|
import type { PushQueue } from "../push-queue.js";
|
|
4
4
|
import type { TaskEvent, TaskSpec } from "../types.js";
|
|
5
5
|
import type { Stats } from "./assemble-result.js";
|
|
6
|
-
import type { CaptureOptOutRef, LiveHandle, ManualCompactRef, NotificationLaneBindings, NotifyRef, Prepared, RunInternals, RunnerDepsSeat, TaskIdRef } from "./contracts.js";
|
|
6
|
+
import type { CaptureOptOutRef, LiveHandle, ManualCompactRef, McpRedialRef, NotificationLaneBindings, NotifyRef, Prepared, RunInternals, RunnerDepsSeat, TaskIdRef } from "./contracts.js";
|
|
7
7
|
export interface RunIdentityWiringInput {
|
|
8
8
|
/** borrowed-readonly — the task spec: the task id and the memory / notification seats it names. */
|
|
9
9
|
spec: TaskSpec;
|
|
@@ -27,6 +27,8 @@ export interface RunIdentityWiringInput {
|
|
|
27
27
|
notifyRef: NotifyRef | undefined;
|
|
28
28
|
/** borrowed-mutable — the capture opt-out bridge: `flip` is bound here when a memory session mounted. */
|
|
29
29
|
captureOptOutRef: CaptureOptOutRef | undefined;
|
|
30
|
+
/** borrowed-mutable — the MCP re-dial bridge: `redial` is bound here when the leg declared MCP servers. */
|
|
31
|
+
mcpRedialRef: McpRedialRef | undefined;
|
|
30
32
|
/** borrowed-readonly — the notification lane's ONE injection entry (the R0 lane's). */
|
|
31
33
|
injectTaskNotification: (notification: TaskNotificationPayload, opts?: {
|
|
32
34
|
priority?: SystemInjectionPriority;
|
|
@@ -3,7 +3,7 @@ import { createSafeNotifier } from "../safe-notify.js";
|
|
|
3
3
|
import { discloseDroppedPending, renderTaskNotificationXml, taskNotificationDedupKey } from "../task-notification.js";
|
|
4
4
|
import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
|
|
5
5
|
export function runIdentityWiring(input) {
|
|
6
|
-
const { spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, notificationLane, pendingSessionNotifications, runner } = input;
|
|
6
|
+
const { spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, mcpRedialRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, notificationLane, pendingSessionNotifications, runner } = input;
|
|
7
7
|
notificationLane.harness = prepared.harness;
|
|
8
8
|
notificationLane.sessionId = prepared.sessionId;
|
|
9
9
|
if (taskIdRef)
|
|
@@ -66,6 +66,9 @@ export function runIdentityWiring(input) {
|
|
|
66
66
|
if (captureOptOutRef && prepared.memoryEngineSession?.captureOptOut !== undefined) {
|
|
67
67
|
captureOptOutRef.flip = prepared.memoryEngineSession.captureOptOut.flip;
|
|
68
68
|
}
|
|
69
|
+
if (mcpRedialRef && prepared.mcpRedial !== undefined) {
|
|
70
|
+
mcpRedialRef.redial = prepared.mcpRedial;
|
|
71
|
+
}
|
|
69
72
|
try {
|
|
70
73
|
internals?.onNotifyInjectorReady?.(injectTaskNotification);
|
|
71
74
|
}
|
|
@@ -23,7 +23,7 @@ import type { AssistantMessage } from "../../internal/llm.js";
|
|
|
23
23
|
import type { ResolvedReasoning } from "../../brain/reasoning.js";
|
|
24
24
|
import type { PushQueue } from "../push-queue.js";
|
|
25
25
|
import type { SessionStore } from "../session.js";
|
|
26
|
-
import { type CompactOutcome, type DelegationLifecycleEvent, type TaskEvent, type TaskResult, type TaskSpec } from "../types.js";
|
|
26
|
+
import { type CompactOutcome, type DelegationLifecycleEvent, type OwedUserInputAccount, type TaskEvent, type TaskResult, type TaskSpec } from "../types.js";
|
|
27
27
|
import { type Stats } from "./assemble-result.js";
|
|
28
28
|
import type { EndOfTaskCompaction, InstallingPhaseResult, ManualCompactRef, Prepared, ResumeRun, RunInternals, RunnerDepsSeat, RunState, SuspendReap, TaskIdRef } from "./contracts.js";
|
|
29
29
|
export interface RunSettleAndTeardownInput {
|
|
@@ -94,6 +94,8 @@ export interface RunSettleAndTeardownInput {
|
|
|
94
94
|
};
|
|
95
95
|
/** borrowed-readonly — the end-of-task compaction's outcome (the terminal-adoption lane's `finish` call). */
|
|
96
96
|
comp: EndOfTaskCompaction;
|
|
97
|
+
/** borrowed-readonly — the adoption lane's unsettled undrained-input account, closed below (after the reopen). */
|
|
98
|
+
owedUserInput: OwedUserInputAccount;
|
|
97
99
|
/** borrowed-readonly — the stream's result setter: the assembled TaskResult is published through it exactly once. */
|
|
98
100
|
setResult: (r: TaskResult) => void;
|
|
99
101
|
/** borrowed-readonly — the stream's suggestions seat: the fire-and-forget suggestion pass's promise is handed to it. */
|