@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.
- package/CHANGELOG.md +44 -0
- package/dist/core/auto-mode.d.ts +9 -0
- package/dist/core/auto-mode.js +11 -0
- package/dist/core/checkpoint-store.js +5 -1
- package/dist/core/protocol-table.d.ts +5 -0
- package/dist/core/protocol-table.js +1 -0
- package/dist/core/runner/abort-race.d.ts +41 -0
- package/dist/core/runner/abort-race.js +38 -0
- package/dist/core/runner/checkpoint-scope.d.ts +44 -0
- package/dist/core/runner/checkpoint-scope.js +7 -0
- package/dist/core/runner/compaction-call-options.d.ts +1 -1
- package/dist/core/runner/content-ask-bindings.d.ts +27 -0
- package/dist/core/runner/content-ask-bindings.js +1 -0
- package/dist/core/runner/contracts.d.ts +1990 -0
- package/dist/core/runner/contracts.js +1 -0
- package/dist/core/runner/denial-limit-arms.d.ts +24 -1
- package/dist/core/runner/denial-limit-arms.js +21 -0
- package/dist/core/runner/derived-route-fallback.d.ts +34 -0
- package/dist/core/runner/derived-route-fallback.js +16 -0
- package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
- package/dist/core/runner/inherited-ask-grants.js +29 -0
- package/dist/core/runner/park-commit.d.ts +108 -0
- package/dist/core/runner/park-commit.js +32 -0
- package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
- package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
- package/dist/core/runner/prepare-ask-lane.js +133 -0
- package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
- package/dist/core/runner/prepare-boundary-parks.js +169 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
- package/dist/core/runner/prepare-config-doors.d.ts +2 -10
- package/dist/core/runner/prepare-context-lane.d.ts +119 -0
- package/dist/core/runner/prepare-context-lane.js +230 -0
- package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
- package/dist/core/runner/prepare-defer-classify.js +107 -0
- package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
- package/dist/core/runner/prepare-delegation-surface.js +144 -0
- package/dist/core/runner/prepare-execution-env.d.ts +54 -0
- package/dist/core/runner/prepare-execution-env.js +86 -0
- package/dist/core/runner/prepare-file-history.d.ts +95 -0
- package/dist/core/runner/prepare-file-history.js +383 -0
- package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
- package/dist/core/runner/prepare-gate-stations.js +290 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +9 -11
- package/dist/core/runner/prepare-hands-readface.js +1 -1
- package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
- package/dist/core/runner/prepare-inherited-gate.js +266 -0
- package/dist/core/runner/prepare-listings.d.ts +77 -0
- package/dist/core/runner/prepare-listings.js +76 -0
- package/dist/core/runner/prepare-lsp.d.ts +55 -0
- package/dist/core/runner/prepare-lsp.js +27 -0
- package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
- package/dist/core/runner/prepare-memory-engine-session.js +233 -0
- package/dist/core/runner/prepare-memory.d.ts +1 -1
- package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
- package/dist/core/runner/prepare-offload-wrappers.js +45 -0
- package/dist/core/runner/prepare-park-ask.d.ts +164 -0
- package/dist/core/runner/prepare-park-ask.js +377 -0
- package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
- package/dist/core/runner/prepare-policy-chain.js +584 -0
- package/dist/core/runner/prepare-project-context.d.ts +119 -0
- package/dist/core/runner/prepare-project-context.js +148 -0
- package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
- package/dist/core/runner/prepare-prompt-assembly.js +162 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +119 -0
- package/dist/core/runner/prepare-prompt-inputs.js +141 -0
- package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
- package/dist/core/runner/prepare-protocol-tools.js +179 -0
- package/dist/core/runner/prepare-question-face.d.ts +101 -0
- package/dist/core/runner/prepare-question-face.js +84 -0
- package/dist/core/runner/prepare-run-refs.d.ts +89 -0
- package/dist/core/runner/prepare-run-refs.js +39 -0
- package/dist/core/runner/prepare-safety-scan.d.ts +3 -7
- package/dist/core/runner/prepare-safety-scan.js +1 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
- package/dist/core/runner/prepare-suspend-saga.js +308 -0
- package/dist/core/runner/prepare-task.d.ts +14 -1976
- package/dist/core/runner/prepare-task.js +182 -5162
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
- package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
- package/dist/core/runner/prepare-turn-wiring.js +201 -0
- package/dist/core/runner/prepare-wiring-manifest.d.ts +192 -0
- package/dist/core/runner/prepare-wiring-manifest.js +247 -0
- package/dist/core/runner/prepare-workspace-restore.d.ts +3 -56
- package/dist/core/runner/prepare-workspace-restore.js +4 -38
- package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
- package/dist/core/runner/prompt-hash-salt.js +2 -0
- package/dist/core/runner/remote-env-retry.d.ts +29 -0
- package/dist/core/runner/remote-env-retry.js +16 -0
- package/dist/core/runner/rollback-stack.d.ts +32 -0
- package/dist/core/runner/rollback-stack.js +30 -0
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/workspace-path.d.ts +33 -0
- package/dist/core/runner/workspace-path.js +22 -0
- package/dist/core/session.d.ts +12 -0
- package/dist/core/session.js +3 -0
- package/dist/core/tool-policy.d.ts +16 -0
- package/dist/core/tool-policy.js +3 -0
- package/dist/core/types.d.ts +2 -2
- package/dist/core/write-protect.js +3 -2
- package/package.json +6 -2
- /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
- /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
3
|
+
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
4
|
+
import { listAgentsMountable, mountListAgents, peerLaneSendMessageSeats } from "../../agents/peer-session-drain.js";
|
|
5
|
+
import { createMonitorTool } from "../../tools/monitor.js";
|
|
6
|
+
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
7
|
+
import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
|
|
8
|
+
import { createReadToolResultTool } from "../tool-result-store.js";
|
|
9
|
+
import { emitTrace } from "../trace.js";
|
|
10
|
+
import { deliverEngineNotice } from "../types.js";
|
|
11
|
+
function buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) {
|
|
12
|
+
if (delegationForRevive === undefined)
|
|
13
|
+
return undefined;
|
|
14
|
+
return async (req) => {
|
|
15
|
+
const out = await delegationForRevive.execute({
|
|
16
|
+
description: req.row.description ?? "revived teammate",
|
|
17
|
+
prompt: req.prompt,
|
|
18
|
+
run_in_background: true,
|
|
19
|
+
...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
|
|
20
|
+
...(req.row.name !== undefined ? { name: req.row.name } : {}),
|
|
21
|
+
...(req.row.model !== undefined ? { model: req.row.model } : {}),
|
|
22
|
+
}, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
|
|
23
|
+
const o = (typeof out === "string" ? { content: out } : out);
|
|
24
|
+
return {
|
|
25
|
+
content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
|
|
26
|
+
...(o.isError === true ? { isError: true } : {}),
|
|
27
|
+
...(o.details !== undefined ? { details: o.details } : {}),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function prepareDelegationSurface(input) {
|
|
32
|
+
const { spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, tools, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle } = input;
|
|
33
|
+
let worktreeSessionRef;
|
|
34
|
+
const delegationSurfaceActive = backgroundTaskToolsActive || workflowToolsActive;
|
|
35
|
+
if (delegationSurfaceActive || internals?.parentNotify !== undefined || peerLaneActive) {
|
|
36
|
+
if (delegationSurfaceActive) {
|
|
37
|
+
toolEffects.set("TaskOutput", "read");
|
|
38
|
+
toolEffects.set("TaskStop", "write");
|
|
39
|
+
tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, oneShot: spec.oneShot, toolResultStore: offloadStore })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
|
|
40
|
+
}
|
|
41
|
+
if (runnerSelf && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME)) {
|
|
42
|
+
toolEffects.set(SEND_MESSAGE_TOOL_NAME, "write");
|
|
43
|
+
axisExplicitNegatives.set(SEND_MESSAGE_TOOL_NAME, { ...axisExplicitNegatives.get(SEND_MESSAGE_TOOL_NAME), egress: false });
|
|
44
|
+
const delegationForRevive = (spec.tools ?? []).find((t) => t.agentListing !== undefined);
|
|
45
|
+
const reviveSpawn = deps.backgroundAgentStore !== undefined && deps.mailboxStore !== undefined ? buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) : undefined;
|
|
46
|
+
tools.push(firstPartyOffload(createSendMessageTool({
|
|
47
|
+
runner: runnerSelf,
|
|
48
|
+
registry: defaultTaskRegistry,
|
|
49
|
+
...(subagentRetain ? { retain: subagentRetain } : {}),
|
|
50
|
+
owner: hostTaskId,
|
|
51
|
+
scope: taskScope,
|
|
52
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
53
|
+
...(internals?.onTaskNotification !== undefined ? { notify: internals.onTaskNotification } : {}), ...(spec.oneShot !== undefined ? { oneShot: spec.oneShot } : {}), retrievalToolMounted: delegationSurfaceActive,
|
|
54
|
+
...(internals?.onSubagentSpawn !== undefined ? { sink: internals.onSubagentSpawn } : {}),
|
|
55
|
+
...(internals?.parentNotify !== undefined
|
|
56
|
+
? { uplink: internals.parentNotify, ...(internals.parentPeerRef !== undefined ? { uplinkRecipient: internals.parentPeerRef } : {}) }
|
|
57
|
+
: {}),
|
|
58
|
+
...(internals?.explicitAgentName !== undefined ? { senderName: internals.explicitAgentName } : {}),
|
|
59
|
+
...(internals?.parentRetainLedger !== undefined ? { siblingRetain: internals.parentRetainLedger } : {}),
|
|
60
|
+
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
61
|
+
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
62
|
+
enrichCtx: enrichSpecToolCtx,
|
|
63
|
+
...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}),
|
|
64
|
+
...(deps.peerAdmission !== undefined ? { admission: deps.peerAdmission } : {}),
|
|
65
|
+
...(deps.onNotice !== undefined ? { onNotice: deps.onNotice } : {}),
|
|
66
|
+
...(internals?.peerSelfRef !== undefined ? { peerSelf: internals.peerSelfRef } : {}),
|
|
67
|
+
...(internals?.peerInboundChainRef !== undefined ? { peerInbound: internals.peerInboundChainRef } : {}),
|
|
68
|
+
...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
|
|
69
|
+
...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
|
|
70
|
+
...(deps.mailboxStore !== undefined ? { mailbox: deps.mailboxStore } : {}),
|
|
71
|
+
...(reviveSpawn !== undefined ? { reviveSpawn } : {}),
|
|
72
|
+
...(peerLaneActive ? peerLaneSendMessageSeats({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, ...(internals?.explicitAgentName !== undefined ? { name: internals.explicitAgentName } : {}), refs: peerLaneRefs, listingMounted: listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) }) : {}),
|
|
73
|
+
onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
|
|
74
|
+
onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
|
|
75
|
+
code: "delegation.transcript_integrity",
|
|
76
|
+
message: `delegation transcript integrity: agent ${handle}'s durable row binds a transcript session the session store attests is gone — the declared transcript durability is being contradicted (check the session store wiring/retention)`,
|
|
77
|
+
detail: { handle, ...(scope !== undefined ? { scope } : {}) },
|
|
78
|
+
}),
|
|
79
|
+
})));
|
|
80
|
+
if (delegationSurfaceActive && !(spec.tools ?? []).some((t) => t.name === AGENT_TRANSCRIPT_TOOL_NAME)) {
|
|
81
|
+
tools.push(firstPartyOffload(createAgentTranscriptTool({
|
|
82
|
+
runner: runnerSelf,
|
|
83
|
+
registry: defaultTaskRegistry,
|
|
84
|
+
agentStore: deps.backgroundAgentStore,
|
|
85
|
+
owner: hostTaskId,
|
|
86
|
+
scope: taskScope,
|
|
87
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
88
|
+
enrichCtx: enrichSpecToolCtx,
|
|
89
|
+
onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
|
|
90
|
+
code: "delegation.transcript_integrity",
|
|
91
|
+
message: `delegation transcript integrity: agent ${handle}'s durable row binds a transcript session the session store attests is gone — the declared transcript durability is being contradicted (check the session store wiring/retention)`,
|
|
92
|
+
detail: { handle, ...(scope !== undefined ? { scope } : {}) },
|
|
93
|
+
}),
|
|
94
|
+
})));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const listAgents = peerLaneActive && peerSendMessageBuiltIn && listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) ? mountListAgents({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, hostTaskId, ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}), ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}), registry: defaultTaskRegistry, ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}), specTools: spec.tools ?? [], toolEffects }) : undefined;
|
|
99
|
+
if (listAgents !== undefined)
|
|
100
|
+
tools.push(firstPartyOffload(listAgents));
|
|
101
|
+
if (backgroundTaskToolsActive) {
|
|
102
|
+
toolEffects.set("Monitor", "write");
|
|
103
|
+
envHandToolNames.add("Monitor");
|
|
104
|
+
tools.push(firstPartyOffload(createMonitorTool(executionEnv, {
|
|
105
|
+
registry: defaultTaskRegistry,
|
|
106
|
+
owner: hostTaskId,
|
|
107
|
+
scope: taskScope,
|
|
108
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
109
|
+
...(internals?.onTaskNotification !== undefined ? { onTaskNotification: internals.onTaskNotification } : {}),
|
|
110
|
+
...(handsCwdRef !== undefined ? { cwdRef: handsCwdRef } : {}),
|
|
111
|
+
...(offloadStore !== undefined ? { toolResultStore: offloadStore } : {}),
|
|
112
|
+
...(spec.retainBackgroundProcesses === true ? { retainBackgroundProcesses: true } : {}),
|
|
113
|
+
})));
|
|
114
|
+
}
|
|
115
|
+
if (handsCwdRef !== undefined) {
|
|
116
|
+
toolEffects.set("EnterWorktree", "write");
|
|
117
|
+
toolEffects.set("ExitWorktree", "write");
|
|
118
|
+
worktreeSessionRef = {
|
|
119
|
+
...(resume?.seed.activeWorktree
|
|
120
|
+
? {
|
|
121
|
+
current: {
|
|
122
|
+
...resume.seed.activeWorktree,
|
|
123
|
+
worktreeDir: rebaseRestoredPath(resume.seed.activeWorktree.worktreeDir),
|
|
124
|
+
originalCwd: rebaseRestoredPath(resume.seed.activeWorktree.originalCwd),
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
: wsSnapshot?.activeWorktree !== undefined
|
|
128
|
+
? {
|
|
129
|
+
current: {
|
|
130
|
+
...wsSnapshot.activeWorktree,
|
|
131
|
+
worktreeDir: rebaseWsPath(wsSnapshot.activeWorktree.worktreeDir),
|
|
132
|
+
originalCwd: rebaseWsPath(wsSnapshot.activeWorktree.originalCwd),
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
: {}),
|
|
136
|
+
};
|
|
137
|
+
if (workspaceStateSettle !== undefined)
|
|
138
|
+
workspaceStateSettle.restoredWorktreeDir = worktreeSessionRef.current?.worktreeDir;
|
|
139
|
+
tools.push(...createWorktreeTools(executionEnv, { repoRoot: taskRootFinal, cwdRef: handsCwdRef, session: worktreeSessionRef }).map((t) => (envHandToolNames.add(t.name), firstPartyOffload(t))));
|
|
140
|
+
}
|
|
141
|
+
if (offloadStore)
|
|
142
|
+
tools.push(createReadToolResultTool(offloadStore));
|
|
143
|
+
return { delegationSurfaceActive, worktreeSessionRef };
|
|
144
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
2
|
+
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
3
|
+
import type { PrepareResume, RollbackStack, RunInternals } from "./contracts.js";
|
|
4
|
+
/**
|
|
5
|
+
* The ONE reference to a factory-minted env, shared by the driver's detach guard and the rollback entry
|
|
6
|
+
* this phase registers. `current` is the minted env, or `undefined` when the run has none / after the
|
|
7
|
+
* driver detached an aliasing one. Written by: this phase (once, at the mint) and the driver's worktree
|
|
8
|
+
* guard (the detach). Read by: the rollback entry (at unwind), and the driver, which settles it into a
|
|
9
|
+
* plain `ownedEnv` binding once the guard has passed.
|
|
10
|
+
*/
|
|
11
|
+
export interface OwnedEnvSeat {
|
|
12
|
+
current: ExecutionEnv | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface PrepareExecutionEnvInput {
|
|
15
|
+
/** borrowed-readonly — the deployment's env seats: the per-task factory (wins), the static env, the
|
|
16
|
+
* declared root fence, and the incident sink for the bounded teardown legs. */
|
|
17
|
+
deps: Pick<RunnerDeps, "executionEnvFactory" | "executionEnv" | "rootPath" | "onError">;
|
|
18
|
+
/** borrowed-readonly — the REBOUND spec; only the caller's task id rides into the factory ctx. */
|
|
19
|
+
spec: Pick<TaskSpec, "taskId">;
|
|
20
|
+
/** borrowed-readonly — the trusted spawn-side requests: the explicit cwd (a contract), the isolation
|
|
21
|
+
* request and the parent's cwd (both threaded to the factory ctx). */
|
|
22
|
+
internals: Pick<RunInternals, "requestedCwd" | "isolation" | "parentCwd"> | undefined;
|
|
23
|
+
/** borrowed-readonly — the resume leg, read only to refuse an explicit cwd that a checkpoint workspace
|
|
24
|
+
* restore would overrule. */
|
|
25
|
+
resume: Pick<PrepareResume, "workspaceHandle"> | undefined;
|
|
26
|
+
/** borrowed-readonly — the run tree's placement fixed point, resolved ONCE by the driver; the factory
|
|
27
|
+
* door refuses an empty value loudly before the factory is asked. */
|
|
28
|
+
placementRootResolved: string;
|
|
29
|
+
/** borrowed-readonly — the acquired session id (the factory ctx's identity, and the incident phase tag). */
|
|
30
|
+
sessionId: string;
|
|
31
|
+
/** borrowed-readonly — the driver's session-view forget leg; every refusal here runs it, bounded, before
|
|
32
|
+
* throwing (the acquired view must not outlive a prepare the caller never gets a handle to). */
|
|
33
|
+
forgetOnThrow: () => Promise<void>;
|
|
34
|
+
/** borrowed-mutable — the driver-owned prepare-failure stack; this phase pushes ONE entry (the minted
|
|
35
|
+
* env's destroy, read through the seat) and nothing else touches it here. */
|
|
36
|
+
rollback: RollbackStack;
|
|
37
|
+
}
|
|
38
|
+
export interface PrepareExecutionEnvResult {
|
|
39
|
+
/** borrowed-readonly — the run's resolved env: the minted one, else the static one, else the no-I/O stub. */
|
|
40
|
+
executionEnv: ExecutionEnv;
|
|
41
|
+
/** ownership-ref — the seat holding the factory-minted env (`current` undefined when none was minted).
|
|
42
|
+
* The exception-path protocol stays with the driver (D-8 案①): its worktree guard may detach through
|
|
43
|
+
* this seat before throwing; the rollback entry registered here reads it at unwind. */
|
|
44
|
+
ownedEnvSeat: OwnedEnvSeat;
|
|
45
|
+
/** borrowed-readonly — epoch ms at which the factory-minted env was obtained (the lifetime anchor of
|
|
46
|
+
* last resort); `undefined` for a static env or no env. */
|
|
47
|
+
ownedEnvObservedAt: number | undefined;
|
|
48
|
+
/** borrowed-readonly — whether a real fs env is present (a minted env, or a static one). */
|
|
49
|
+
handsEnabled: boolean;
|
|
50
|
+
/** borrowed-readonly — the INITIAL task root (the workspace-restore phase may re-root it and returns the
|
|
51
|
+
* settled root under the NEW name `taskRootFinal`). */
|
|
52
|
+
taskRootInitial: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function prepareExecutionEnv(input: PrepareExecutionEnvInput): Promise<PrepareExecutionEnvResult>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { StubExecutionEnv } from "../stub-env.js";
|
|
2
|
+
import { hasDestroy } from "../remote-env.js";
|
|
3
|
+
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
4
|
+
export async function prepareExecutionEnv(input) {
|
|
5
|
+
const { deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback } = input;
|
|
6
|
+
let ownedEnv;
|
|
7
|
+
let ownedEnvObservedAt;
|
|
8
|
+
if (internals?.requestedCwd !== undefined && deps.executionEnvFactory === undefined) {
|
|
9
|
+
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-unsupported leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
10
|
+
const e = new Error(`Agent cwd "${internals.requestedCwd}" cannot take effect: this deployment has no executionEnvFactory (a static execution environment cannot be re-rooted per agent). Drop the cwd parameter or deploy a factory.`);
|
|
11
|
+
e.code = "config.cwd_unsupported";
|
|
12
|
+
throw e;
|
|
13
|
+
}
|
|
14
|
+
if (internals?.requestedCwd !== undefined && resume?.workspaceHandle !== undefined) {
|
|
15
|
+
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-conflicts-restore leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
16
|
+
throw cwdConflictsRestoreError(internals.requestedCwd);
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
ownedEnv = deps.executionEnvFactory
|
|
20
|
+
? await deps.executionEnvFactory({
|
|
21
|
+
sessionId, placementRootSessionId: mintPlacementRootSessionId(placementRootResolved),
|
|
22
|
+
taskId: spec.taskId,
|
|
23
|
+
...(internals?.isolation ? { isolation: internals.isolation } : {}),
|
|
24
|
+
...(internals?.parentCwd ? { parentCwd: internals.parentCwd } : {}),
|
|
25
|
+
})
|
|
26
|
+
: undefined;
|
|
27
|
+
if (ownedEnv !== undefined)
|
|
28
|
+
ownedEnvObservedAt = Date.now();
|
|
29
|
+
}
|
|
30
|
+
catch (factoryErr) {
|
|
31
|
+
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (env-factory-throw leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
32
|
+
throw factoryErr;
|
|
33
|
+
}
|
|
34
|
+
const ownedEnvSeat = { current: ownedEnv };
|
|
35
|
+
if (ownedEnv !== undefined) {
|
|
36
|
+
rollback.push(function ownedEnvDestroy() {
|
|
37
|
+
const env = ownedEnvSeat.current;
|
|
38
|
+
return env !== undefined && hasDestroy(env) ? env.destroy() : undefined;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const executionEnv = ownedEnv ?? deps.executionEnv ?? new StubExecutionEnv();
|
|
42
|
+
if (internals?.requestedCwd !== undefined) {
|
|
43
|
+
const canon = async (p) => {
|
|
44
|
+
const r = await executionEnv.canonicalPath(p);
|
|
45
|
+
return r.ok ? r.value : p;
|
|
46
|
+
};
|
|
47
|
+
const want = await canon(internals.requestedCwd);
|
|
48
|
+
const rejectCwd = async (message, code) => {
|
|
49
|
+
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-reject leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
50
|
+
await settleTeardownLeg(() => (ownedEnv && hasDestroy(ownedEnv) ? ownedEnv.destroy() : undefined), "ownedEnv.destroy (cwd-reject leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
51
|
+
const e = new Error(message);
|
|
52
|
+
e.code = code;
|
|
53
|
+
throw e;
|
|
54
|
+
};
|
|
55
|
+
if (deps.rootPath !== undefined) {
|
|
56
|
+
const root = await canon(deps.rootPath);
|
|
57
|
+
const sep = root.includes("\\") && !root.includes("/") ? "\\" : "/";
|
|
58
|
+
const rootPrefix = root.endsWith(sep) ? root : root + sep;
|
|
59
|
+
if (want !== root && !want.startsWith(rootPrefix)) {
|
|
60
|
+
await rejectCwd(`Agent cwd "${internals.requestedCwd}" is outside this deployment's declared root "${deps.rootPath}" — the cwd must be located within the deployment root.`, "config.cwd_outside_root");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const got = await canon(executionEnv.cwd);
|
|
64
|
+
if (want !== got) {
|
|
65
|
+
await rejectCwd(`Agent cwd "${internals.requestedCwd}" was not honored by the execution environment (it rooted at "${executionEnv.cwd}") — failing loud instead of running the agent in the wrong tree.`, "config.cwd_not_honored");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const handsEnabled = ownedEnv !== undefined || deps.executionEnv !== undefined;
|
|
69
|
+
const taskRootInitial = internals?.isolation === "worktree" || internals?.requestedCwd !== undefined || !deps.rootPath ? executionEnv.cwd : deps.rootPath;
|
|
70
|
+
return { executionEnv, ownedEnvSeat, ownedEnvObservedAt, handsEnabled, taskRootInitial };
|
|
71
|
+
}
|
|
72
|
+
function cwdConflictsRestoreError(requestedCwd) {
|
|
73
|
+
const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
|
|
74
|
+
`the restored workspace's own mount path is authoritative for the task root, so a requested cwd on this leg ` +
|
|
75
|
+
`would be ignored (or worse, probed against an unrestored environment). Drop requestedCwd on resume legs.`);
|
|
76
|
+
e.code = "config.cwd_conflicts_restore";
|
|
77
|
+
return e;
|
|
78
|
+
}
|
|
79
|
+
function mintPlacementRootSessionId(placementRootResolved) {
|
|
80
|
+
if (placementRootResolved === "") {
|
|
81
|
+
const e = new Error("placement root resolved EMPTY (internals.placementRoot / internals.rootSessionId carries an empty string) — an empty fixed point cannot key a placement lookup; fix the spawning lane instead of defaulting around it.");
|
|
82
|
+
e.code = "config.placement_root_invalid";
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
85
|
+
return placementRootResolved;
|
|
86
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/390 §1.2 M2 — prepareTask's file-history phase, verbatim from the driver: the DV-14 first-touch
|
|
3
|
+
* policy door, the `RunnerDeps.fileHistoryStore` bad-value door, the retired `rewindFiles` two-sense
|
|
4
|
+
* split, the restoreFiles / resumeAt / rewindFilesTo combination gates, the run's history coordinates
|
|
5
|
+
* (canonical root, recording scope, filesystem identity), the fork carry, the child-scope refusal, the
|
|
6
|
+
* rewind RESTORE itself (anchor translation in "before" mode, the per-file ledger, partial-restore
|
|
7
|
+
* policy), the `trackFileEdit` hook the hands band calls before every mutation lane, and the turn-start
|
|
8
|
+
* boundary seat runtask drives.
|
|
9
|
+
*
|
|
10
|
+
* Runs INSIDE the driver's prepare try-block, after the workspace root is FINAL (`taskRootFinal`) and the
|
|
11
|
+
* env is resolved: every refusal here throws through to the driver's prepare-throw catch exactly as
|
|
12
|
+
* before the move (a minted ownedEnv is destroyed there, the session view forgotten). The phase acquires
|
|
13
|
+
* no resource of its own — the two seats it returns are closures over the store the deployment owns.
|
|
14
|
+
*
|
|
15
|
+
* The module-level helpers below the phase (`restoredAbsPathsOf`, `resolveWiredFileHistoryStore`,
|
|
16
|
+
* the refusal's epoch-tail wording helper, `adoptForkedFileHistory` with its per-store carry memo,
|
|
17
|
+
* `createFileHistoryBoundarySeat`, `resolveFileHistoryCoordinates` / `resolveFileHistoryScope` /
|
|
18
|
+
* `fileHistoryFilesystemIdentity`, `childScopeRewindRefusal`) moved with it unchanged; the two the test
|
|
19
|
+
* suite reaches through the driver are re-exported from there.
|
|
20
|
+
*/
|
|
21
|
+
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
22
|
+
import type { StoredSession } from "../../engine/session/session.js";
|
|
23
|
+
import type { RunnerDeps, TaskResult, TaskSpec, TrackFileEditHook } from "../types.js";
|
|
24
|
+
import type { FileHistoryBoundarySeat, RunInternals } from "./contracts.js";
|
|
25
|
+
export interface PrepareFileHistoryInput {
|
|
26
|
+
/** borrowed-readonly — the deployment seats this phase reads: the first-touch failure policy and the
|
|
27
|
+
* history store (both validated HERE, once), the incident sink, and the retired-epoch diagnostic probe. */
|
|
28
|
+
deps: Pick<RunnerDeps, "onTrackFailure" | "fileHistoryStore" | "onError" | "legacyRewindBoundaryProbe">;
|
|
29
|
+
/** borrowed-readonly — the REBOUND spec's rewind request fields (the retired `rewindFiles` spelling is
|
|
30
|
+
* read off the same object as an unknown key) and the caller's cancel signal for the restore. */
|
|
31
|
+
spec: Pick<TaskSpec, "resumeAt" | "resumeAtMode" | "restoreFiles" | "rewindFilesTo" | "acceptPartialRestore" | "signal">;
|
|
32
|
+
/** borrowed-readonly — the trusted lineage a delegating parent handed this run (absent for a root run
|
|
33
|
+
* and for a host's own fork), read for the recording-scope decision only. */
|
|
34
|
+
internals: Pick<RunInternals, "fileHistoryLineage"> | undefined;
|
|
35
|
+
/** borrowed-readonly — the run's resolved env (owned or static or the stub); every history verb runs
|
|
36
|
+
* against ITS filesystem ops. Never rebound or destroyed here. */
|
|
37
|
+
executionEnv: ExecutionEnv;
|
|
38
|
+
/** borrowed-readonly — the FINAL task root (the workspace-restore phase's output binding). */
|
|
39
|
+
taskRootFinal: string;
|
|
40
|
+
/** borrowed-readonly — the "real fs env" fact (`ownedEnv !== undefined || deps.executionEnv !== undefined`). */
|
|
41
|
+
handsEnabled: boolean;
|
|
42
|
+
/** borrowed-readonly — the acquired session id: the run's OWN history scope and the restore's session key. */
|
|
43
|
+
sessionId: string;
|
|
44
|
+
/** borrowed-readonly — the acquired session, read for the fork lineage (`getMetadata`) and the "before"
|
|
45
|
+
* anchor walk (`getEntry`); never written here. */
|
|
46
|
+
session: Pick<StoredSession, "getEntry" | "getMetadata">;
|
|
47
|
+
/** borrowed-readonly — §E18 "before" mode: the validated target's parentId (the acquire phase's output);
|
|
48
|
+
* `null` outside "before" mode. */
|
|
49
|
+
resumeAtBeforeParentId: string | null;
|
|
50
|
+
/** borrowed-readonly — the run's abort controller (driver-minted); only its `signal` is read, composed
|
|
51
|
+
* with `spec.signal` for the restore. */
|
|
52
|
+
abortController: AbortController;
|
|
53
|
+
}
|
|
54
|
+
export interface PrepareFileHistoryResult {
|
|
55
|
+
/** borrowed-readonly — the hook the hands band calls before every mutation lane; `undefined` when the
|
|
56
|
+
* machinery is not live (no store, or no real fs env). */
|
|
57
|
+
trackFileEdit: TrackFileEditHook | undefined;
|
|
58
|
+
/** borrowed-readonly — the turn-start boundary seat runtask drives; `undefined` when not live or when
|
|
59
|
+
* this run records into its ROOT session's scope (a child mints no boundaries). */
|
|
60
|
+
fileHistoryBoundary: FileHistoryBoundarySeat | undefined;
|
|
61
|
+
/** borrowed-readonly — the absolute paths the restore MUTATED (empty when no restore ran), for the
|
|
62
|
+
* read-face phase's read-state invalidation. */
|
|
63
|
+
restoredFilePaths: string[];
|
|
64
|
+
/** borrowed-mutable — the non-fatal rewind disclosures (`TaskResult.rewindNotes`). Mutation owners after
|
|
65
|
+
* this call: none — every push happens inside this phase; the driver only reads it into `Prepared`. */
|
|
66
|
+
rewindNotes: NonNullable<TaskResult["rewindNotes"]>;
|
|
67
|
+
/** borrowed-readonly — whether the file-history machinery is LIVE for this run (store wired AND hands). */
|
|
68
|
+
fileHistoryEnabled: boolean;
|
|
69
|
+
/** borrowed-readonly — the scope this run's first-touch records land in (own session, or the root
|
|
70
|
+
* session's for a same-tree delegated child). */
|
|
71
|
+
historyScope: string;
|
|
72
|
+
/** borrowed-readonly — the canonical history root every history verb keys against. */
|
|
73
|
+
historyRoot: string;
|
|
74
|
+
/** borrowed-readonly — the filesystem identity of the env (the lineage's second tree coordinate). */
|
|
75
|
+
historyFs: string;
|
|
76
|
+
}
|
|
77
|
+
export declare function prepareFileHistory(input: PrepareFileHistoryInput): Promise<PrepareFileHistoryResult>;
|
|
78
|
+
/** The ONE reading of {@link RunInternals.fileHistoryLineage} (see {@link resolveFileHistoryCoordinates}):
|
|
79
|
+
* the lineage's scope iff BOTH tree coordinates match; otherwise the run's own session. */
|
|
80
|
+
export declare function resolveFileHistoryScope(lineage: RunInternals["fileHistoryLineage"], historyRoot: string, historyFs: string, sessionId: string): string;
|
|
81
|
+
/**
|
|
82
|
+
* WHICH filesystem an env is a view of — the second coordinate of tree identity for the file-history
|
|
83
|
+
* lineage, read from what the env's MINTER states rather than inferred from a path:
|
|
84
|
+
* · a remote workspace names itself through its {@link WorkspaceHandle} (provider + sandbox, and the
|
|
85
|
+
* device lane's id when stamped) — two runs on one sandbox share a tree, two sandboxes never do;
|
|
86
|
+
* · an env that DECLARES its paths host-local (`hostLocalPaths: true`), or is the host adapter itself
|
|
87
|
+
* (a {@link NodeExecutionEnv}, which the #211 seam names as the host-local default), is the
|
|
88
|
+
* control-plane host's filesystem — every such env is one tree;
|
|
89
|
+
* · anything else — an env declaring `hostLocalPaths: false`, or an undeclared custom adapter — is
|
|
90
|
+
* attested to nothing, so it is its OWN tree: a per-instance token, which still matches when the
|
|
91
|
+
* child literally holds the parent's env object and never matches a fresh per-task mint.
|
|
92
|
+
* A remote handle that cannot be read (an env not yet connected) falls to the per-instance arm for
|
|
93
|
+
* the same reason: unattested is not shared.
|
|
94
|
+
*/
|
|
95
|
+
export declare function fileHistoryFilesystemIdentity(env: ExecutionEnv): string;
|