@sema-agent/core 7.5.0 → 7.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -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/runner/checkpoint-scope.d.ts +32 -0
- package/dist/core/runner/checkpoint-scope.js +4 -0
- package/dist/core/runner/contracts.d.ts +1878 -0
- package/dist/core/runner/contracts.js +1 -0
- package/dist/core/runner/denial-limit-arms.d.ts +1 -1
- 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/prepare-acquire-reconcile.d.ts +1 -1
- 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-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-hands-readface.d.ts +5 -7
- 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.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-project-context.d.ts +131 -0
- package/dist/core/runner/prepare-project-context.js +150 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +138 -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 +182 -0
- package/dist/core/runner/prepare-question-face.d.ts +119 -0
- package/dist/core/runner/prepare-question-face.js +83 -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 -2
- package/dist/core/runner/prepare-task.d.ts +11 -1846
- package/dist/core/runner/prepare-task.js +83 -2366
- 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-wiring-manifest.d.ts +184 -0
- package/dist/core/runner/prepare-wiring-manifest.js +240 -0
- package/dist/core/runner/prepare-workspace-restore.d.ts +1 -27
- package/dist/core/runner/prepare-workspace-restore.js +1 -22
- package/dist/core/runner/rollback-stack.d.ts +32 -0
- package/dist/core/runner/rollback-stack.js +30 -0
- package/dist/core/runner/workspace-path.d.ts +33 -0
- package/dist/core/runner/workspace-path.js +22 -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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/390 §1.2 M7 — prepareTask's QUESTION-FACE phase, verbatim from the driver: the leg's ONE checkpoint-store
|
|
3
|
+
* read, the durable mandate as it binds at the question seat, the content-ask binding table (with the leak
|
|
4
|
+
* bound the routing lane checks against — the constant below), the stranded-answer disclosure and the leg-end
|
|
5
|
+
* settle, the three-way mount decision (live face / durable face / override), and the AskUserQuestion mount
|
|
6
|
+
* with its redeemed-approval, posture, fallback and synthetic-continuation arms. The interface is the
|
|
7
|
+
* dependency list the segment had implicitly (design/238 R-1).
|
|
8
|
+
*
|
|
9
|
+
* SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the
|
|
10
|
+
* offload-reader mount before it and the scheduler mount after it (design/390 §1.5 S1: a stretch with no await
|
|
11
|
+
* is extracted as a sync function — an `async` wrapper would open a microtask window). Read-stability of the
|
|
12
|
+
* host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
|
|
13
|
+
*
|
|
14
|
+
* One seat is a LIVE GETTER (design/238 D-3): the inherited-unavailable marker set is declared by the driver's
|
|
15
|
+
* gate-fold section AFTER this phase and read per call at RUN time, so it enters as `() => ReadonlySet` — a
|
|
16
|
+
* read before the driver declares it throws (temporal dead zone), which is the fail-loud an uninitialized
|
|
17
|
+
* live read owes.
|
|
18
|
+
*/
|
|
19
|
+
import type { AgentTool } from "../../internal/harness.js";
|
|
20
|
+
import { type OnQuestion, type QuestionAnswer } from "../ask-question.js";
|
|
21
|
+
import { type CheckpointStore } from "../checkpoint-store.js";
|
|
22
|
+
import type { RunnerDeps, RuntimeCaps, TaskSpec } from "../types.js";
|
|
23
|
+
import type { PrepareResume } from "./contracts.js";
|
|
24
|
+
export declare const CONTENT_ASK_BINDING_CAP = 32;
|
|
25
|
+
/** One gate-routed content-ask outcome, keyed by the call id it was produced for (see the table's own note). */
|
|
26
|
+
export type ContentAskBinding = ({
|
|
27
|
+
kind: "answered";
|
|
28
|
+
answer: QuestionAnswer;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "failed";
|
|
31
|
+
error: unknown;
|
|
32
|
+
}) & {
|
|
33
|
+
/** Digest of the exact question batch this outcome belongs to. The call id alone is not enough
|
|
34
|
+
* to redeem a binding: it comes from the model/provider and can repeat, so an entry that
|
|
35
|
+
* outlived its call (its call was allowed but never executed) could otherwise be handed to a
|
|
36
|
+
* LATER call that happens to reuse the id. Matching on the batch as well means an outcome can
|
|
37
|
+
* only ever be delivered for the question it was produced for. */
|
|
38
|
+
questionsHash: string;
|
|
39
|
+
/** The engine-minted per-delivery identity (see AskQuestionRequest.deliveryId) — carried so a
|
|
40
|
+
* stranded-answer disclosure names DELIVERIES, not call ids: call ids can repeat, and a
|
|
41
|
+
* disclosure keyed on them would collapse two lost answers into one. */
|
|
42
|
+
deliveryId: string;
|
|
43
|
+
};
|
|
44
|
+
export interface PrepareQuestionFaceInput {
|
|
45
|
+
/** borrowed-readonly — the run's frozen question seat (spec > deps), resolved once at the top of prepare;
|
|
46
|
+
* the mount predicate and the live-face derivation both read this one value. */
|
|
47
|
+
frozenOnQuestion: OnQuestion | undefined;
|
|
48
|
+
/** borrowed-readonly — the REBOUND spec. Read: `checkpointStore` (the per-run off switch), `durableApproval`
|
|
49
|
+
* (the durable face's opt-in), `interactiveTools` (the override), `principal` (the tool's source identity),
|
|
50
|
+
* `interactiveQuestionFallback` (restores the continuation under a resolved posture). Never mutated. */
|
|
51
|
+
spec: Pick<TaskSpec, "checkpointStore" | "durableApproval" | "interactiveTools" | "principal" | "interactiveQuestionFallback">;
|
|
52
|
+
/** borrowed-readonly — the deployment seats: `checkpointStore` (the store the leg snapshot resolves against)
|
|
53
|
+
* and `onError` (the stranded-answer and synthetic-continuation operator lines; swallow-guarded). */
|
|
54
|
+
deps: Pick<RunnerDeps, "checkpointStore" | "onError">;
|
|
55
|
+
/** borrowed-readonly — the principal's resolved runtime caps as of this point (`forceDurableGate` is the
|
|
56
|
+
* mandate that outranks a live face only where a store exists to park the question). */
|
|
57
|
+
runtimeCaps: Pick<RuntimeCaps, "forceDurableGate"> | undefined;
|
|
58
|
+
/** borrowed-readonly — the resolved interaction posture (the door's value), threaded to the tool so both
|
|
59
|
+
* no-answer arms harden under it. */
|
|
60
|
+
resolvedInteractionPosture: "interactive" | "headless" | undefined;
|
|
61
|
+
/** borrowed-readonly — the durable-resume carrier; only the redeemed content-ask call id and its questions
|
|
62
|
+
* hash are read (the one call an operator's approval was spent on). */
|
|
63
|
+
resume: Pick<PrepareResume, "redeemedContentAskCallId" | "redeemedContentAskQuestionsHash"> | undefined;
|
|
64
|
+
/** borrowed-readonly — the acquired session id (the tool's source task id; operator-line context). */
|
|
65
|
+
sessionId: string;
|
|
66
|
+
/** borrowed-mutable — the run's shared roster (identity is the contract). Writer here: ONE push (the question
|
|
67
|
+
* tool) when the mount predicate holds; `contentAskRoutable` reads `includes` per call against the LIVE
|
|
68
|
+
* array (the roster is edited in place after the mount). */
|
|
69
|
+
tools: AgentTool[];
|
|
70
|
+
/** borrowed-readonly — LIVE GETTER over the inherited-unavailable marker set (declared by the driver's
|
|
71
|
+
* gate-fold section after this phase; `has` only, per call at run time). See the module header. */
|
|
72
|
+
inheritedUnavailableAsks: () => ReadonlySet<string>;
|
|
73
|
+
}
|
|
74
|
+
export interface PrepareQuestionFaceResult {
|
|
75
|
+
/** borrowed-readonly — THE leg's store snapshot (`resolveCheckpointStore(spec, deps)`): the mount face, the
|
|
76
|
+
* routability predicate, the manifest and suspendAsk all read this one value. */
|
|
77
|
+
checkpointStore: CheckpointStore | undefined;
|
|
78
|
+
/** borrowed-readonly — the run's live question face through the shared predicate (the reserved resume
|
|
79
|
+
* placeholder is a function and is NOT a live face), or undefined. */
|
|
80
|
+
liveQuestionFace: OnQuestion | undefined;
|
|
81
|
+
/** borrowed-mutable — the one-shot answer binding table for questions the GATE routed (scope = this leg).
|
|
82
|
+
* Writers (R-3): the routing lane (`set`, cap-checked against {@link CONTENT_ASK_BINDING_CAP} before every
|
|
83
|
+
* insertion), the mounted face here (`delete`, one-shot redemption or a mismatched-batch drop), the settle
|
|
84
|
+
* here (`clear`). Identity is the contract (the routing lane and the face share this exact map). */
|
|
85
|
+
contentAskBindings: Map<string, ContentAskBinding>;
|
|
86
|
+
/** owned — THE routability predicate for a content ask (per call, against the live roster and the marker set). */
|
|
87
|
+
contentAskRoutable: (toolCallId: string) => boolean;
|
|
88
|
+
/** borrowed-mutable — late answers noted before the leg settles. Writer: the routing lane's delivery-race
|
|
89
|
+
* watcher (`push`); reader: the settle here (merged into the result disclosure by delivery id). */
|
|
90
|
+
lateStrandedAnswers: Array<{
|
|
91
|
+
deliveryId: string;
|
|
92
|
+
toolCallId: string;
|
|
93
|
+
}>;
|
|
94
|
+
/** owned — THE disclosure lane for an answer a person gave that no call could collect (swallow-guarded). */
|
|
95
|
+
discloseStrandedAnswers: (records: ReadonlyArray<{
|
|
96
|
+
deliveryId: string;
|
|
97
|
+
toolCallId: string;
|
|
98
|
+
}>, why: string) => void;
|
|
99
|
+
/** owned — the leg-end settle (`Prepared.settleContentAskBindings`): discloses surviving answers, clears the
|
|
100
|
+
* table, returns the records the result carries. */
|
|
101
|
+
settleContentAskBindings: () => ReadonlyArray<{
|
|
102
|
+
deliveryId: string;
|
|
103
|
+
toolCallId: string;
|
|
104
|
+
}>;
|
|
105
|
+
/** owned — whether a durable delivery face exists (store wired ∧ (durableApproval ∨ forceDurableGate));
|
|
106
|
+
* the manifest's composition-lie detector reuses this exact value. */
|
|
107
|
+
durableQuestionFace: boolean;
|
|
108
|
+
/** owned — the face the tool was mounted with: the gate-bound answer for THIS call id if one exists (one-shot),
|
|
109
|
+
* else the live face itself; the raw frozen seat when no live face exists. */
|
|
110
|
+
mountedQuestionFace: OnQuestion | undefined;
|
|
111
|
+
/** owned — whether THIS prepare mounted the first-party question tool (the routing lane keys on it, not on
|
|
112
|
+
* the reserved name). */
|
|
113
|
+
questionToolMounted: boolean;
|
|
114
|
+
/** borrowed-readonly — the mounted tool object itself (the exact roster member), so the routing lane can
|
|
115
|
+
* validate a batch against the schema the executing call is re-checked against; undefined when not mounted. */
|
|
116
|
+
mountedQuestionTool: AgentTool | undefined;
|
|
117
|
+
}
|
|
118
|
+
/** The M7 phase body — prepareTask's question-face stretch, verbatim (see the module header). */
|
|
119
|
+
export declare function prepareQuestionFace(input: PrepareQuestionFaceInput): PrepareQuestionFaceResult;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createAskUserQuestionTool, isLiveQuestionFace } from "../ask-question.js";
|
|
2
|
+
import { boundInputHashOf } from "../canonical-json.js";
|
|
3
|
+
import { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
4
|
+
export const CONTENT_ASK_BINDING_CAP = 32;
|
|
5
|
+
export function prepareQuestionFace(input) {
|
|
6
|
+
const { frozenOnQuestion, spec, deps, runtimeCaps, resolvedInteractionPosture, resume, sessionId, tools, inheritedUnavailableAsks } = input;
|
|
7
|
+
const onQuestion = frozenOnQuestion;
|
|
8
|
+
const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
|
|
9
|
+
const contentAskBindings = new Map();
|
|
10
|
+
const checkpointStore = resolveCheckpointStore(spec, deps);
|
|
11
|
+
const questionParkStoreWired = checkpointStore !== undefined;
|
|
12
|
+
const questionDurableMandateBinding = runtimeCaps?.forceDurableGate === true && questionParkStoreWired;
|
|
13
|
+
const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
|
|
14
|
+
mountedQuestionTool !== undefined &&
|
|
15
|
+
tools.includes(mountedQuestionTool) &&
|
|
16
|
+
!questionDurableMandateBinding &&
|
|
17
|
+
!inheritedUnavailableAsks().has(toolCallId);
|
|
18
|
+
const lateStrandedAnswers = [];
|
|
19
|
+
const discloseStrandedAnswers = (records, why) => {
|
|
20
|
+
if (records.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
deps.onError?.(new Error(`AskUserQuestion: ${records.length} question(s) were answered by a person but the call never ` +
|
|
24
|
+
`executed to collect the answer (${records.map((r) => `${r.toolCallId} [delivery ${r.deliveryId}]`).join(", ")}) — ${why}. The answer(s) were NOT ` +
|
|
25
|
+
`delivered to the model and are gone with this leg; re-ask if the decision is still needed.`), { phase: "degraded", sessionId, classification: "unconsumed-human-answer" });
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const settleContentAskBindings = () => {
|
|
31
|
+
const stranded = [];
|
|
32
|
+
for (const [callId, bound] of contentAskBindings) {
|
|
33
|
+
if (bound.kind === "answered")
|
|
34
|
+
stranded.push({ deliveryId: bound.deliveryId, toolCallId: callId });
|
|
35
|
+
}
|
|
36
|
+
contentAskBindings.clear();
|
|
37
|
+
discloseStrandedAnswers(stranded, "the leg ended first (abort, batch teardown, or a loop failure)");
|
|
38
|
+
const byDelivery = new Map();
|
|
39
|
+
for (const r of [...stranded, ...lateStrandedAnswers])
|
|
40
|
+
byDelivery.set(r.deliveryId, r);
|
|
41
|
+
return [...byDelivery.values()];
|
|
42
|
+
};
|
|
43
|
+
const durableQuestionFace = questionParkStoreWired && (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
|
|
44
|
+
const mountedQuestionFace = liveQuestionFace !== undefined
|
|
45
|
+
? async (req, signal) => {
|
|
46
|
+
const bound = contentAskBindings.get(req.toolCallId);
|
|
47
|
+
if (bound !== undefined) {
|
|
48
|
+
if (bound.questionsHash === boundInputHashOf(req.questions)) {
|
|
49
|
+
contentAskBindings.delete(req.toolCallId);
|
|
50
|
+
if (bound.kind === "answered")
|
|
51
|
+
return bound.answer;
|
|
52
|
+
throw bound.error;
|
|
53
|
+
}
|
|
54
|
+
contentAskBindings.delete(req.toolCallId);
|
|
55
|
+
}
|
|
56
|
+
return liveQuestionFace(req, signal);
|
|
57
|
+
}
|
|
58
|
+
: onQuestion;
|
|
59
|
+
const questionToolMounted = spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace));
|
|
60
|
+
let mountedQuestionTool;
|
|
61
|
+
if (questionToolMounted)
|
|
62
|
+
tools.push((mountedQuestionTool =
|
|
63
|
+
createAskUserQuestionTool(mountedQuestionFace, { principal: spec.principal, sourceTaskId: sessionId }, {
|
|
64
|
+
...(resume?.redeemedContentAskCallId !== undefined
|
|
65
|
+
? {
|
|
66
|
+
redeemedApprovalCallId: resume.redeemedContentAskCallId,
|
|
67
|
+
...(resume.redeemedContentAskQuestionsHash !== undefined ? { redeemedApprovalQuestionsHash: resume.redeemedContentAskQuestionsHash } : {}),
|
|
68
|
+
}
|
|
69
|
+
: {}),
|
|
70
|
+
...(resolvedInteractionPosture !== undefined ? { posture: resolvedInteractionPosture } : {}),
|
|
71
|
+
...(spec.interactiveQuestionFallback === true ? { interactiveFallback: true } : {}),
|
|
72
|
+
onSyntheticContinuation: ({ questionId, reason }) => {
|
|
73
|
+
deps.onError?.(new Error(`AskUserQuestion ${questionId}: no human answer was obtainable (` +
|
|
74
|
+
(reason === "seam_absent"
|
|
75
|
+
? "no onQuestion seam is wired"
|
|
76
|
+
: reason === "declined_unavailable"
|
|
77
|
+
? "the wired question channel reported nobody was reachable"
|
|
78
|
+
: "the wired question channel failed") +
|
|
79
|
+
`) — the model was instructed to self-answer and the run CONTINUES (warning, not a failure).`), { phase: "degraded", sessionId, classification: "no-human-autoanswered" });
|
|
80
|
+
},
|
|
81
|
+
})));
|
|
82
|
+
return { checkpointStore, liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, mountedQuestionFace, questionToolMounted, mountedQuestionTool };
|
|
83
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/390 §1.2 M3a — prepareTask's RUN-REFS phase, verbatim from the driver: the run's own edited-files
|
|
3
|
+
* ledger, the harness ref, the per-task skill scope, the display-forwarding wrapper, the three run-local refs
|
|
4
|
+
* (review request / stop-after-turn / plan mode) with the ctx primitives that write them, the subagent-retain
|
|
5
|
+
* ledger and the worktree-isolation helper. Nothing here has behavior of its own: the phase MINTS SEATS —
|
|
6
|
+
* mutable cells and the closures bound to them — that later phases, the tool ctx and the run loop read and
|
|
7
|
+
* write. The interface is the dependency list the segment had implicitly (design/238 R-1).
|
|
8
|
+
*
|
|
9
|
+
* SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the
|
|
10
|
+
* file-history phase before it and the inherited-gate fold after it (design/390 §1.5 S1: a stretch with no
|
|
11
|
+
* await is extracted as a sync function — an `async` wrapper would open a microtask window).
|
|
12
|
+
*
|
|
13
|
+
* Every cell that leaves here as `borrowed-mutable` carries its writer table on the Result member (design/238
|
|
14
|
+
* R-3): the closures minted here, the driver's harness build, the project-context phase's inherited-skill
|
|
15
|
+
* seed, the skill tool, and the run loop's two writes — nothing else writes them. Read-stability of the host
|
|
16
|
+
* handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
|
|
17
|
+
*/
|
|
18
|
+
import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
|
|
19
|
+
import type { SubagentWorktreeIsolation } from "../../agents/subagent.js";
|
|
20
|
+
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
21
|
+
import type { TaskEvent, TaskSpec } from "../types.js";
|
|
22
|
+
import { ActiveSkillScope } from "./active-skill-scope.js";
|
|
23
|
+
import type { Prepared, RunInternals } from "./contracts.js";
|
|
24
|
+
import { type EditedFilesLedger } from "./edited-files-ledger.js";
|
|
25
|
+
export interface PrepareRunRefsInput {
|
|
26
|
+
/** borrowed-readonly — the REBOUND spec. Read: `forwardSubagentEvents` (widens the forwarding filter),
|
|
27
|
+
* `retainSubagentSessions` (the ledger opt-in and its config), `handsReadOnly` (a read-only run mints no
|
|
28
|
+
* worktree helper). Never mutated. */
|
|
29
|
+
spec: Pick<TaskSpec, "forwardSubagentEvents" | "retainSubagentSessions" | "handsReadOnly">;
|
|
30
|
+
/** borrowed-readonly — the trusted spawn-side channel; only `onForwardEvent` (the deployment's display
|
|
31
|
+
* sink) is read, once, to decide whether a forwarding wrapper exists at all. */
|
|
32
|
+
internals: Pick<RunInternals, "onForwardEvent"> | undefined;
|
|
33
|
+
/** borrowed-readonly — the run's resolved env; the worktree helper closes over it (lazy: no I/O until a
|
|
34
|
+
* delegation actually mints a worktree). */
|
|
35
|
+
executionEnv: ExecutionEnv;
|
|
36
|
+
/** borrowed-readonly — the SETTLED task root (`taskRootFinal`): the worktree helper's base repo root. */
|
|
37
|
+
taskRootFinal: string;
|
|
38
|
+
}
|
|
39
|
+
export interface PrepareRunRefsResult {
|
|
40
|
+
/** owned — the ledger's writer (`FileEditedHook`): handed to the hands/read-face phase as its post-write
|
|
41
|
+
* seat; nothing else calls it. */
|
|
42
|
+
noteFileEdited: EditedFilesLedger["note"];
|
|
43
|
+
/** owned — the ledger's LIVE read face (`Prepared.editedFilesSnapshot`, read at result assembly). */
|
|
44
|
+
editedFilesSnapshot: EditedFilesLedger["snapshot"];
|
|
45
|
+
/** borrowed-mutable — the harness seat: holds the harness once built, so a tool's execute can read the model
|
|
46
|
+
* the parent is CURRENTLY on (design/38 1B: the `ToolExecuteContext.model` snapshot a delegation tool uses
|
|
47
|
+
* for `inherit`), and breaks the harness↔tool_search build cycle (design/36 rematerialize). Writers (R-3):
|
|
48
|
+
* the driver, ONCE, when the harness is built (`harnessRef.current = harness`) — before any run. Readers:
|
|
49
|
+
* `requestStopAfterTurn` here (mirrors the stop onto the harness's one-way flag), the tool ctx's model
|
|
50
|
+
* snapshot, the rebuild seam and the delegation mounts. Identity is the contract: every closure minted
|
|
51
|
+
* before the harness exists reads this exact cell. */
|
|
52
|
+
harnessRef: {
|
|
53
|
+
current?: AgentHarness;
|
|
54
|
+
};
|
|
55
|
+
/** borrowed-mutable — the per-task LIFO skill scope. Writers: the project-context phase (the inherited-
|
|
56
|
+
* from-parent seed frame, `push`) and the `skill` tool minted over it (`push` on a manifest load; a frame
|
|
57
|
+
* closes by `pop`). Readers: the tool ctx's `activeSkillScope()` snapshot and the skill-scope policy layer. */
|
|
58
|
+
skillScope: ActiveSkillScope;
|
|
59
|
+
/** owned — the FILTERED display-forwarding wrapper, or undefined when the deployment wired no sink (the
|
|
60
|
+
* tool ctx then carries no `forwardEvent` key). */
|
|
61
|
+
forwardEvent: ((e: TaskEvent) => void) | undefined;
|
|
62
|
+
/** borrowed-mutable — the review-request cell (`Prepared.reviewRequestRef`). Writers: `requestReview` here
|
|
63
|
+
* (the first request wins) and the run loop's turn-boundary consumer, which clears `pending` once the
|
|
64
|
+
* `plan_review` checkpoint is minted. */
|
|
65
|
+
reviewRequestRef: Prepared["reviewRequestRef"];
|
|
66
|
+
/** owned — the ctx primitive that arms `reviewRequestRef` (design/80 D-B). */
|
|
67
|
+
requestReview: (opts?: {
|
|
68
|
+
reason?: string;
|
|
69
|
+
}) => void;
|
|
70
|
+
/** borrowed-mutable — the stop-after-turn latch (`Prepared.stopRequestedRef`). Writers: `requestStopAfterTurn`
|
|
71
|
+
* here, and nothing else; the run loop reads it at a turn boundary. */
|
|
72
|
+
stopRequestedRef: Prepared["stopRequestedRef"];
|
|
73
|
+
/** owned — the ctx primitive: flips the latch and mirrors it onto the harness's one-way flag. */
|
|
74
|
+
requestStopAfterTurn: () => void;
|
|
75
|
+
/** borrowed-mutable — the plan-mode flag (`Prepared.planModeRef`). Writers: `enterPlanMode` here, and the run
|
|
76
|
+
* loop's resume ladder, which re-arms it when a REJECTED `plan_review` resumes into a re-plan. Read by the
|
|
77
|
+
* tool-call gate (the write-effect refusal). */
|
|
78
|
+
planModeRef: Prepared["planModeRef"];
|
|
79
|
+
/** owned — the ctx primitive (mounted on the ctx only under `enablePlanMode`). */
|
|
80
|
+
enterPlanMode: () => void;
|
|
81
|
+
/** ownership-ref — the parent-run subagent-retain ledger, or undefined when the spec did not opt in. Its
|
|
82
|
+
* lifecycle protocol belongs to the Runner (disposed in the task's terminal finally, design/122 D4); a
|
|
83
|
+
* prepare that throws has nothing to release here — the ledger is empty until a delegation retains. */
|
|
84
|
+
subagentRetain: SubagentRetainLedger | undefined;
|
|
85
|
+
/** owned — the Agent tool's worktree-isolation helper (lazy; write-capable runs only), or undefined. */
|
|
86
|
+
worktreeIsolation: SubagentWorktreeIsolation | undefined;
|
|
87
|
+
}
|
|
88
|
+
/** The M3a phase body — prepareTask's run-refs stretch, verbatim (see the module header). */
|
|
89
|
+
export declare function prepareRunRefs(input: PrepareRunRefsInput): PrepareRunRefsResult;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createSubagentWorktreeHelper } from "../../agents/subagent.js";
|
|
2
|
+
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
3
|
+
import { ActiveSkillScope } from "./active-skill-scope.js";
|
|
4
|
+
import { createEditedFilesLedger } from "./edited-files-ledger.js";
|
|
5
|
+
export function prepareRunRefs(input) {
|
|
6
|
+
const { spec, internals, executionEnv, taskRootFinal } = input;
|
|
7
|
+
const { note: noteFileEdited, snapshot: editedFilesSnapshot } = createEditedFilesLedger();
|
|
8
|
+
const harnessRef = {};
|
|
9
|
+
const skillScope = new ActiveSkillScope();
|
|
10
|
+
const forwardSink = internals?.onForwardEvent;
|
|
11
|
+
const forwardEvent = forwardSink
|
|
12
|
+
? (e) => {
|
|
13
|
+
if (e.type === "task_progress")
|
|
14
|
+
forwardSink(e);
|
|
15
|
+
else if (spec.forwardSubagentEvents === true &&
|
|
16
|
+
(e.type === "text_delta" || e.type === "text_end" || e.type === "reasoning_delta" || e.type === "tool_start" || e.type === "tool_end")) {
|
|
17
|
+
forwardSink(e);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
: undefined;
|
|
21
|
+
const reviewRequestRef = {};
|
|
22
|
+
const requestReview = (opts) => {
|
|
23
|
+
if (reviewRequestRef.pending === undefined) {
|
|
24
|
+
reviewRequestRef.pending = opts?.reason !== undefined ? { reason: opts.reason } : {};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const stopRequestedRef = { current: false };
|
|
28
|
+
const requestStopAfterTurn = () => {
|
|
29
|
+
stopRequestedRef.current = true;
|
|
30
|
+
harnessRef.current?.requestStopAfterTurn();
|
|
31
|
+
};
|
|
32
|
+
const planModeRef = { active: false };
|
|
33
|
+
const enterPlanMode = () => {
|
|
34
|
+
planModeRef.active = true;
|
|
35
|
+
};
|
|
36
|
+
const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
|
|
37
|
+
const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
|
|
38
|
+
return { noteFileEdited, editedFilesSnapshot, harnessRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, planModeRef, enterPlanMode, subagentRetain, worktreeIsolation };
|
|
39
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
|
|
2
|
+
import type { IrreversibilityTier, ReversibilityProbes } from "./contracts.js";
|
|
2
3
|
/** The namespaced-name shapes the protocol table currently owns, rendered for the two messages that have
|
|
3
4
|
* to name them (the caller-name reservation and the policy audit's unprefixed-name arm). Read from the
|
|
4
5
|
* table rather than written out: a message that hard-codes ONE protocol becomes wrong — while staying
|
|
@@ -38,14 +39,14 @@ export interface PrepareSafetyScanResult {
|
|
|
38
39
|
/** borrowed-mutable — tools whose effect is irreversible (`always`, or `maybe` incl. by probe). */
|
|
39
40
|
irreversibleTools: Set<string>;
|
|
40
41
|
/** borrowed-mutable — static per-tool irreversibility tier (prepare-time resolution). */
|
|
41
|
-
irreversibilityTier:
|
|
42
|
+
irreversibilityTier: IrreversibilityTier;
|
|
42
43
|
/** borrowed-mutable — explicit NEGATIVE axis judgments (report-face only; `riskAxesOf` reads). */
|
|
43
44
|
axisExplicitNegatives: Map<string, {
|
|
44
45
|
irreversible?: false;
|
|
45
46
|
egress?: false;
|
|
46
47
|
}>;
|
|
47
48
|
/** borrowed-mutable — declared reversibility probes by tool name. */
|
|
48
|
-
reversibilityProbes:
|
|
49
|
+
reversibilityProbes: ReversibilityProbes;
|
|
49
50
|
/** borrowed-readonly — the caller roster's own names, snapshotted before the first await. */
|
|
50
51
|
ownToolNames: ReadonlySet<string>;
|
|
51
52
|
}
|