@sema-agent/core 7.30.1 → 7.31.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 +284 -0
- package/dist/agents/cross-session-judge.d.ts +4 -12
- package/dist/agents/cross-session-judge.js +4 -11
- package/dist/agents/peer-session-drain.js +0 -5
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +1 -0
- package/dist/core/ask-origin.d.ts +6 -0
- package/dist/core/ask-origin.js +10 -0
- package/dist/core/checkpoint-store.d.ts +14 -0
- package/dist/core/checkpoint-store.js +1 -0
- package/dist/core/engine-notice.d.ts +4 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/memory-engine/data-plane.d.ts +3 -1
- package/dist/core/memory-engine/data-plane.js +1 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +44 -3
- package/dist/core/memory-engine/delegation-settlement.js +38 -3
- package/dist/core/memory-engine/engine.d.ts +133 -27
- package/dist/core/memory-engine/engine.js +452 -151
- package/dist/core/memory-engine/entry-scan.d.ts +23 -0
- package/dist/core/memory-engine/entry-scan.js +55 -0
- package/dist/core/memory-engine/file-backend.d.ts +31 -23
- package/dist/core/memory-engine/file-backend.js +174 -100
- package/dist/core/memory-engine/layer-observations.d.ts +109 -0
- package/dist/core/memory-engine/layer-observations.js +148 -0
- package/dist/core/memory-engine/layout.d.ts +40 -4
- package/dist/core/memory-engine/layout.js +43 -7
- package/dist/core/memory-engine/types.d.ts +26 -2
- package/dist/core/parent-spec-seat.d.ts +4 -1
- package/dist/core/parent-spec-seat.js +2 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/runner/announce-once-ledger.d.ts +26 -0
- package/dist/core/runner/announce-once-ledger.js +37 -0
- package/dist/core/runner/contracts.d.ts +22 -5
- package/dist/core/runner/denial-limit-arms.d.ts +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +0 -4
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -3
- package/dist/core/runner/prepare-file-history.d.ts +13 -15
- package/dist/core/runner/prepare-file-history.js +57 -43
- package/dist/core/runner/prepare-hands-readface.d.ts +2 -2
- package/dist/core/runner/prepare-listings.d.ts +3 -1
- package/dist/core/runner/prepare-listings.js +2 -2
- package/dist/core/runner/prepare-memory.js +16 -5
- package/dist/core/runner/prepare-park-ask.js +27 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +8 -1
- package/dist/core/runner/prepare-prompt-inputs.js +5 -3
- package/dist/core/runner/prepare-task.js +7 -5
- package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -0
- package/dist/core/runner/prepare-wiring-manifest.js +2 -1
- package/dist/core/runner/run-attachment-seats.js +1 -1
- package/dist/core/runner/run-leg.js +61 -52
- package/dist/core/runner/run-settle-and-teardown.js +4 -0
- package/dist/core/runner/run-turn-boundary.js +6 -6
- package/dist/core/runner/turn-attachments.d.ts +6 -6
- package/dist/core/runner-deps.d.ts +3 -1
- package/dist/core/secret-env.d.ts +12 -1
- package/dist/core/secret-env.js +27 -2
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +22 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +17 -8
- package/dist/core/tool-spec.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +10 -0
- package/dist/core/wiring-manifest.js +7 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -0
- package/dist/engine/execution-env/node-execution-env.js +15 -17
- package/dist/engine/harness/agent-harness.js +7 -5
- package/dist/engine/lsp/node-lsp-manager.d.ts +4 -0
- package/dist/engine/lsp/node-lsp-manager.js +10 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/fs/shell-read-plan.d.ts +2 -2
- package/dist/tools/fs/shell-read-plan.js +15 -10
- package/dist/tools/fs/shell-read-word.d.ts +5 -4
- package/dist/tools/fs/shell-read-word.js +3 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +7 -1
- package/wire-changes/core@7.31.0.json +705 -0
- package/wire-changes/core@7.31.1.json +189 -0
|
@@ -176,11 +176,22 @@ export interface CommittedPause {
|
|
|
176
176
|
scope: string;
|
|
177
177
|
gatedCallId?: string;
|
|
178
178
|
}
|
|
179
|
+
/** The session's date anchor as prepare read it off the branch. The system-prefix date is frozen per
|
|
180
|
+
* SEGMENT — from session start, or from the latest landed compaction, to the next one. */
|
|
181
|
+
export interface DateAnchorRead {
|
|
182
|
+
/** The anchor written for the current segment; undefined ⇒ the leg renders today and anchors it. */
|
|
183
|
+
current: {
|
|
184
|
+
prefixDate: string;
|
|
185
|
+
announcedDate: string;
|
|
186
|
+
} | undefined;
|
|
187
|
+
/** The current segment: the nearest compaction entry's id, null before the first compaction. */
|
|
188
|
+
segment: string | null;
|
|
189
|
+
}
|
|
179
190
|
/**
|
|
180
191
|
* One engine frame whose commit waits for the receipt of the message carrying it.
|
|
181
192
|
*
|
|
182
193
|
* @contract frames.commit_on_receipt — an engine-minted frame that rides a user message (the git-status
|
|
183
|
-
* frame, an `agent_listing` or `
|
|
194
|
+
* frame, an `agent_listing`, `skills_listing` or `date_change` frame) is recorded as announced ONLY when the run loop's
|
|
184
195
|
* `message_end` walk sees a persisted user entry whose text equals the carrying text it was registered
|
|
185
196
|
* with; every receipt with that text settles on that one entry. Until then nothing is recorded — a
|
|
186
197
|
* message that never reaches the transcript (a pre-append failure, a suppressed or undrained steer, a
|
|
@@ -193,7 +204,7 @@ export interface FrameReceipt {
|
|
|
193
204
|
text: string;
|
|
194
205
|
/** Which frame this receipt settles. A boundary collector must not render a listing whose receipt is
|
|
195
206
|
* still pending (the frame is queued, not yet in the transcript). */
|
|
196
|
-
source: "git_status" | "agent_listing" | "skills_listing";
|
|
207
|
+
source: "git_status" | "agent_listing" | "skills_listing" | "date_change";
|
|
197
208
|
/** Runs inside the `message_end` walk (synchronous, never awaited): advances the run-local record
|
|
198
209
|
* and parks any session write for the next serialization point. */
|
|
199
210
|
commit: (entryId: string) => void;
|
|
@@ -344,7 +355,7 @@ export interface Prepared {
|
|
|
344
355
|
* than a snapshot, so the throw-path backstop terminal reports the same ledger the ordinary
|
|
345
356
|
* assembly would. Always present on Prepared; independent of whether a fileHistoryStore is wired. */
|
|
346
357
|
editedFilesSnapshot: () => TaskResult["editedFiles"];
|
|
347
|
-
/** The run's turn-start boundary seat (present iff
|
|
358
|
+
/** The run's turn-start boundary seat (present iff the run records file history into a wired store and
|
|
348
359
|
* the run mounts a real fs env). runtask calls begin() at the first committed user entry and
|
|
349
360
|
* awaits settle() at the lease close + the finish tail. */
|
|
350
361
|
fileHistoryBoundary?: FileHistoryBoundarySeat;
|
|
@@ -623,6 +634,9 @@ export interface Prepared {
|
|
|
623
634
|
/** Merged injection across all planes (single-root sessions: identical to engine.inject(handle)). */
|
|
624
635
|
inject: () => import("../memory-engine/engine.js").MemoryInjection;
|
|
625
636
|
harvest: (phase?: "checkpoint" | "terminal") => Promise<HarvestReport | undefined>;
|
|
637
|
+
/** End this mount's session leg WITHOUT a harvest: the run that mounted it will never reach its
|
|
638
|
+
* terminal harvest (a prepare that failed after the mount). Never throws. */
|
|
639
|
+
release: () => void;
|
|
626
640
|
/**
|
|
627
641
|
* The session's capture opt-out face (the `TaskStream.optOutMemoryCapture`
|
|
628
642
|
* verb's target; host-API only — no tool face reads it, §2.6). `flip` re-adjudicates the
|
|
@@ -1027,10 +1041,13 @@ export interface Prepared {
|
|
|
1027
1041
|
/** The date-flip detector seam: `legDate` = the date frozen
|
|
1028
1042
|
* into this leg's system prefix; `today()` = the boundary-time LOCAL date (user-zone-bound closure).
|
|
1029
1043
|
* The run loop feeds both to the `date_change` attachment producer; the prefix itself never
|
|
1030
|
-
* re-renders mid-leg (cache preservation, CC parity). Present only when the prompt carries a date.
|
|
1044
|
+
* re-renders mid-leg (cache preservation, CC parity). Present only when the prompt carries a date.
|
|
1045
|
+
* `anchor` = the session's date anchor as prepare read it (undefined ⇒ the branch was unreadable
|
|
1046
|
+
* and the leg writes none); the settle lane persists it when it is new or the announced date moved. */
|
|
1031
1047
|
dateChange?: {
|
|
1032
1048
|
legDate: string;
|
|
1033
1049
|
today: () => string;
|
|
1050
|
+
anchor: DateAnchorRead | undefined;
|
|
1034
1051
|
};
|
|
1035
1052
|
/** [A2] C-4 (design-A §4) — the loadProjectMemory snapshot's declared instruction sources
|
|
1036
1053
|
* (`ProjectMemoryLoad.instructionSources`): the probe input AND the lane's per-path baseline
|
|
@@ -2066,7 +2083,7 @@ export interface RunInternals {
|
|
|
2066
2083
|
* re-exposes the SAME triple on its own ctx, so every same-tree descendant of a root session — at
|
|
2067
2084
|
* any depth — lands in the root session's scope (the fixed point), while a descendant on another
|
|
2068
2085
|
* tree (worktree isolation, explicit `cwd`, a fresh per-task sandbox) becomes the root of its own
|
|
2069
|
-
* subtree's history. Absent on a top-level run, on a run
|
|
2086
|
+
* subtree's history. Absent on a top-level run, on a run that records no file history, and on a
|
|
2070
2087
|
* tier-3 revival (the reviver's lineage says nothing about the revived row's tree).
|
|
2071
2088
|
*/
|
|
2072
2089
|
fileHistoryLineage?: {
|
|
@@ -60,8 +60,8 @@ export declare function attachRebuiltDenialTrackers<T extends {
|
|
|
60
60
|
* in headless mode"). Once per run leg (the first such fallback owns the terminal); the deny that
|
|
61
61
|
* triggered it stands as that call's result. Minted once so every mint site — the main gate's seat, the
|
|
62
62
|
* inherited-lane arms, and the durable resume station — stops the SAME leg through the SAME seat.
|
|
63
|
-
* `abort` is the run's own abort (controller + harness
|
|
64
|
-
* the seat
|
|
63
|
+
* `abort` is the run's own abort (controller + harness — the one a cancel fires; handed in because the harness
|
|
64
|
+
* is built after the seat): the durable stop fires it before the resume leg's prompt, whose door reads it.
|
|
65
65
|
*
|
|
66
66
|
* TWO LANES, ONE SENTENCE. The synchronous lane reached no approver at all; the durable lane parked the
|
|
67
67
|
* fallback, and the decide that came back named a settlement word the ONE table
|
|
@@ -75,10 +75,6 @@ export interface PrepareAcquireReconcileResult {
|
|
|
75
75
|
* SUCCEEDS (a conflict-losing attempt re-wakes and re-derives the whole set from the fresh
|
|
76
76
|
* branch), and stays empty for a fresh session / a clean one with nothing to reconcile. */
|
|
77
77
|
wakeRecovered: RecoveredOrphan[];
|
|
78
|
-
/** owned — §E18 `"before"` mode: the validated target's parentId — the new conversation leaf, and
|
|
79
|
-
* the E19 file-anchor walk's starting point (consumed by the rewind-restore block after env
|
|
80
|
-
* setup). `null` outside `"before"` mode. */
|
|
81
|
-
resumeAtBeforeParentId: string | null;
|
|
82
78
|
}
|
|
83
79
|
/** The B-3 phase body — the acquire+reconcile slice, verbatim (see the module header for the contract). */
|
|
84
80
|
export declare function prepareAcquireReconcile(input: PrepareAcquireReconcileInput): Promise<PrepareAcquireReconcileResult>;
|
|
@@ -32,7 +32,6 @@ export async function prepareAcquireReconcile(input) {
|
|
|
32
32
|
let session;
|
|
33
33
|
let conflictRef;
|
|
34
34
|
let wakeRecovered = [];
|
|
35
|
-
let resumeAtBeforeParentId = null;
|
|
36
35
|
for (let attempt = 0;; attempt++) {
|
|
37
36
|
try {
|
|
38
37
|
acquired = await sessions.acquire(spec.sessionId, spec.requireExistingSession ? { requireExisting: true } : input.placement !== undefined ? { placement: input.placement } : undefined);
|
|
@@ -105,7 +104,6 @@ export async function prepareAcquireReconcile(input) {
|
|
|
105
104
|
e.code = "resume_at.before_root_unsupported";
|
|
106
105
|
throw e;
|
|
107
106
|
}
|
|
108
|
-
resumeAtBeforeParentId = entry.parentId;
|
|
109
107
|
}
|
|
110
108
|
await session.getStorage().setLeafId(spec.resumeAtMode === "before" ? entry.parentId : spec.resumeAt);
|
|
111
109
|
}
|
|
@@ -123,5 +121,5 @@ export async function prepareAcquireReconcile(input) {
|
|
|
123
121
|
throw err;
|
|
124
122
|
}
|
|
125
123
|
}
|
|
126
|
-
return { acquired, session, conflictRef, wakeRecovered
|
|
124
|
+
return { acquired, session, conflictRef, wakeRecovered };
|
|
127
125
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PrepareTask's file-history phase, verbatim from the driver: the DV-14 first-touch
|
|
3
|
-
* policy door, the `RunnerDeps.fileHistoryStore` bad-value
|
|
3
|
+
* policy door, the `RunnerDeps.fileHistoryStore` / `fileHistoryCapture` and `TaskSpec.fileHistory` bad-value doors, the retired `rewindFiles` two-sense
|
|
4
4
|
* split, the restoreFiles / resumeAt / rewindFilesTo combination gates, the run's history coordinates
|
|
5
5
|
* (canonical root, recording scope, filesystem identity), the fork carry, the child-scope refusal, the
|
|
6
6
|
* rewind RESTORE itself (anchor translation in "before" mode, the per-file ledger, partial-restore
|
|
@@ -23,12 +23,12 @@ import type { StoredSession } from "../../engine/session/session.js";
|
|
|
23
23
|
import type { RunnerDeps, TaskResult, TaskSpec, TrackFileEditHook } from "../types.js";
|
|
24
24
|
import type { FileHistoryBoundarySeat, RunInternals } from "./contracts.js";
|
|
25
25
|
export interface PrepareFileHistoryInput {
|
|
26
|
-
/** borrowed-readonly — the deployment seats this phase reads: the first-touch failure policy
|
|
27
|
-
*
|
|
28
|
-
deps: Pick<RunnerDeps, "onTrackFailure" | "fileHistoryStore" | "onError">;
|
|
29
|
-
/** borrowed-readonly — the REBOUND spec's rewind request fields (the retired `rewindFiles`
|
|
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">;
|
|
26
|
+
/** borrowed-readonly — the deployment seats this phase reads: the first-touch failure policy, the history
|
|
27
|
+
* store and its capture switch (all validated HERE, once), the incident sink, and the retired-epoch diagnostic probe. */
|
|
28
|
+
deps: Pick<RunnerDeps, "onTrackFailure" | "fileHistoryStore" | "fileHistoryCapture" | "onError">;
|
|
29
|
+
/** borrowed-readonly — the REBOUND spec's rewind request fields and off-switch word (the retired `rewindFiles`
|
|
30
|
+
* spelling is 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" | "fileHistory">;
|
|
32
32
|
/** borrowed-readonly — the trusted lineage a delegating parent handed this run (absent for a root run
|
|
33
33
|
* and for a host's own fork), read for the recording-scope decision only. */
|
|
34
34
|
internals: Pick<RunInternals, "fileHistoryLineage"> | undefined;
|
|
@@ -41,19 +41,15 @@ export interface PrepareFileHistoryInput {
|
|
|
41
41
|
handsEnabled: boolean;
|
|
42
42
|
/** borrowed-readonly — the acquired session id: the run's OWN history scope and the restore's session key. */
|
|
43
43
|
sessionId: string;
|
|
44
|
-
/** borrowed-readonly — the acquired session, read for the fork lineage (`getMetadata`)
|
|
45
|
-
|
|
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;
|
|
44
|
+
/** borrowed-readonly — the acquired session, read for the fork lineage (`getMetadata`); never written here. */
|
|
45
|
+
session: Pick<StoredSession, "getMetadata">;
|
|
50
46
|
/** borrowed-readonly — the run's abort controller (driver-minted); only its `signal` is read, composed
|
|
51
47
|
* with `spec.signal` for the restore. */
|
|
52
48
|
abortController: AbortController;
|
|
53
49
|
}
|
|
54
50
|
export interface PrepareFileHistoryResult {
|
|
55
51
|
/** borrowed-readonly — the hook the hands band calls before every mutation lane; `undefined` when the
|
|
56
|
-
*
|
|
52
|
+
* run does not record (no store, capture turned off by any layer, or no real fs env). */
|
|
57
53
|
trackFileEdit: TrackFileEditHook | undefined;
|
|
58
54
|
/** borrowed-readonly — the turn-start boundary seat runtask drives; `undefined` when not live or when
|
|
59
55
|
* this run records into its ROOT session's scope (a child mints no boundaries). */
|
|
@@ -64,8 +60,10 @@ export interface PrepareFileHistoryResult {
|
|
|
64
60
|
/** borrowed-mutable — the non-fatal rewind disclosures (`TaskResult.rewindNotes`). Mutation owners after
|
|
65
61
|
* this call: none — every push happens inside this phase; the driver only reads it into `Prepared`. */
|
|
66
62
|
rewindNotes: NonNullable<TaskResult["rewindNotes"]>;
|
|
67
|
-
/** borrowed-readonly — whether
|
|
63
|
+
/** borrowed-readonly — whether this run RECORDS file history (store wired, no layer turned capture off, AND hands); restore reads the store seat. */
|
|
68
64
|
fileHistoryEnabled: boolean;
|
|
65
|
+
/** borrowed-readonly — the manifest's word: `"on"` iff live, else `"disabled"` (whichever layer or missing half). */
|
|
66
|
+
fileHistoryFact: "on" | "disabled";
|
|
69
67
|
/** borrowed-readonly — the scope this run's first-touch records land in (own session, or the root
|
|
70
68
|
* session's for a same-tree delegated child). */
|
|
71
69
|
historyScope: string;
|
|
@@ -2,7 +2,7 @@ import { NodeExecutionEnv } from "../../engine/execution-env/node-execution-env.
|
|
|
2
2
|
import { isRemoteExecutionEnv } from "../remote-env.js";
|
|
3
3
|
import { resolveTrackKey } from "../file-history-store.js";
|
|
4
4
|
export async function prepareFileHistory(input) {
|
|
5
|
-
const { deps, spec, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session,
|
|
5
|
+
const { deps, spec, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController } = input;
|
|
6
6
|
const rewindNotes = [];
|
|
7
7
|
const onTrackFailure = deps.onTrackFailure === undefined ? "refuse" : deps.onTrackFailure;
|
|
8
8
|
if (onTrackFailure !== "refuse" && onTrackFailure !== "proceed-unprotected") {
|
|
@@ -11,16 +11,20 @@ export async function prepareFileHistory(input) {
|
|
|
11
11
|
throw e;
|
|
12
12
|
}
|
|
13
13
|
const wiredSeat = resolveWiredFileHistoryStore(deps.fileHistoryStore);
|
|
14
|
-
const
|
|
14
|
+
const runDeclaredOff = resolveRunFileHistoryWord(spec.fileHistory);
|
|
15
|
+
const captureOff = resolveFileHistoryCaptureWord(deps.fileHistoryCapture, wiredSeat);
|
|
16
|
+
const declaredOff = wiredSeat === "disabled" || runDeclaredOff;
|
|
17
|
+
const fileHistoryStore = declaredOff ? undefined : wiredSeat;
|
|
18
|
+
const captureStore = captureOff ? undefined : fileHistoryStore;
|
|
15
19
|
const legacyRewindFiles = spec.rewindFiles;
|
|
16
20
|
if (legacyRewindFiles === true) {
|
|
17
21
|
if (spec.resumeAt !== undefined) {
|
|
18
|
-
const e = new Error(`rewind-files: TaskSpec.rewindFiles is retired (design/381 — rewind now converges the per-edited-file history, not a whole-tree snapshot). This request pairs it with resumeAt, i.e. the RESTORE sense: set restoreFiles: true instead. (The capture sense needs no request anymore — first-touch tracking is
|
|
22
|
+
const e = new Error(`rewind-files: TaskSpec.rewindFiles is retired (design/381 — rewind now converges the per-edited-file history, not a whole-tree snapshot). This request pairs it with resumeAt, i.e. the RESTORE sense: set restoreFiles: true instead. (The capture sense needs no request anymore — first-touch tracking is on whenever a RunnerDeps.fileHistoryStore is wired and neither RunnerDeps.fileHistoryCapture nor TaskSpec.fileHistory turns it off.)`);
|
|
19
23
|
e.code = "rewind.rewind_files_retired";
|
|
20
24
|
throw e;
|
|
21
25
|
}
|
|
22
26
|
try {
|
|
23
|
-
deps.onError?.(new Error(`TaskSpec.rewindFiles (capture sense) is retired and was ignored: per-edited-file first-touch tracking is
|
|
27
|
+
deps.onError?.(new Error(`TaskSpec.rewindFiles (capture sense) is retired and was ignored: per-edited-file first-touch tracking is on whenever RunnerDeps.fileHistoryStore is wired and neither RunnerDeps.fileHistoryCapture nor TaskSpec.fileHistory turns it off, so a per-turn capture request has nothing left to ask for. Drop the field; the restore sense moved to TaskSpec.restoreFiles.`), { phase: "config", sessionId });
|
|
24
28
|
}
|
|
25
29
|
catch {
|
|
26
30
|
}
|
|
@@ -35,7 +39,7 @@ export async function prepareFileHistory(input) {
|
|
|
35
39
|
e.code = "rewind.conflicting_targets";
|
|
36
40
|
throw e;
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
const rewindTarget = spec.resumeAt !== undefined ? (spec.restoreFiles === true ? spec.resumeAt : undefined) : spec.rewindFilesTo;
|
|
39
43
|
const rewindBefore = rewindTarget !== undefined && spec.resumeAt !== undefined && spec.resumeAtMode === "before";
|
|
40
44
|
if (spec.resumeAt !== undefined && spec.restoreFiles !== true) {
|
|
41
45
|
rewindNotes.push({
|
|
@@ -43,9 +47,9 @@ export async function prepareFileHistory(input) {
|
|
|
43
47
|
message: `the conversation was branched at entry "${spec.resumeAt}" but the working tree was NOT rewound — this task set resumeAt without restoreFiles, so files remain at their current state`,
|
|
44
48
|
});
|
|
45
49
|
}
|
|
46
|
-
const fileHistoryEnabled =
|
|
50
|
+
const fileHistoryEnabled = captureStore !== undefined && handsEnabled;
|
|
47
51
|
const livenessArm = fileHistoryStore === undefined ? "file-history-no-store" : "file-history-no-env";
|
|
48
|
-
if (!fileHistoryEnabled &&
|
|
52
|
+
if (!fileHistoryEnabled && !declaredOff && !captureOff && (handsEnabled || fileHistoryStore !== undefined) && deps.onError !== undefined && !toldAlready(deps.onError, livenessArm)) {
|
|
49
53
|
const reason = livenessArm === "file-history-no-store"
|
|
50
54
|
? `this deployment wired no RunnerDeps.fileHistoryStore, so NOTHING is recorded: no first-touch snapshot before an edit, no turn boundary, and a later restoreFiles / rewindFilesTo has nothing to restore from and will refuse (rewind.store_unconfigured). Supply a store, or accept an unrecoverable working tree knowingly (fileHistoryStore: "disabled" says so and silences this line)`
|
|
51
55
|
: `RunnerDeps.fileHistoryStore is wired but this run mounts no filesystem-capable ExecutionEnv, so the store is INERT: there is no working tree to snapshot and nothing will ever be written to it. Wire RunnerDeps.executionEnv, or drop the store from this deployment shape`;
|
|
@@ -55,14 +59,18 @@ export async function prepareFileHistory(input) {
|
|
|
55
59
|
catch {
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
|
-
const { historyRoot, historyScope, historyFs } = await resolveFileHistoryCoordinates(
|
|
59
|
-
if (
|
|
60
|
-
await adoptForkedFileHistory(
|
|
62
|
+
const { historyRoot, historyScope, historyFs } = await resolveFileHistoryCoordinates(fileHistoryStore !== undefined && handsEnabled, executionEnv, taskRootFinal, internals?.fileHistoryLineage, sessionId);
|
|
63
|
+
if (captureStore !== undefined && internals?.fileHistoryLineage === undefined)
|
|
64
|
+
await adoptForkedFileHistory(captureStore, session, sessionId, deps.onError);
|
|
61
65
|
if (rewindTarget !== undefined && historyScope !== sessionId)
|
|
62
66
|
throw childScopeRewindRefusal(historyScope, rewindTarget);
|
|
63
67
|
if (fileHistoryStore === undefined) {
|
|
68
|
+
if (rewindTarget !== undefined && runDeclaredOff)
|
|
69
|
+
throw captureDisabledRefusal(rewindTarget);
|
|
64
70
|
if (rewindTarget !== undefined) {
|
|
65
|
-
const e = new Error(
|
|
71
|
+
const e = new Error(wiredSeat === "disabled"
|
|
72
|
+
? `rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment declared file history disabled (RunnerDeps.fileHistoryStore: "disabled"), so there is no history to restore — the files were NOT rewound`
|
|
73
|
+
: `rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment wired no RunnerDeps.fileHistoryStore — no file history was ever recorded, so the files were NOT rewound`);
|
|
66
74
|
e.code = "rewind.store_unconfigured";
|
|
67
75
|
throw e;
|
|
68
76
|
}
|
|
@@ -78,39 +86,17 @@ export async function prepareFileHistory(input) {
|
|
|
78
86
|
let restoredFilePaths = [];
|
|
79
87
|
if (rewindTarget !== undefined && fileHistoryStore !== undefined && handsEnabled) {
|
|
80
88
|
const historyStore = fileHistoryStore;
|
|
81
|
-
if (rewindBefore)
|
|
82
|
-
|
|
83
|
-
if (await historyStore.canRestore(sessionId, rewindTarget)) {
|
|
84
|
-
anchor = rewindTarget;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
const walked = new Set();
|
|
88
|
-
let cursor = resumeAtBeforeParentId;
|
|
89
|
-
while (cursor !== null && !walked.has(cursor)) {
|
|
90
|
-
walked.add(cursor);
|
|
91
|
-
if (await historyStore.canRestore(sessionId, cursor)) {
|
|
92
|
-
anchor = cursor;
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
cursor = (await session.getEntry(cursor))?.parentId ?? null;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (anchor === undefined) {
|
|
99
|
-
const e = new Error(`rewind-files: no file-history boundary exists at or above the "before" branch point on session "${sessionId}" — cannot restore files consistent with the rewound context. ${rewindRefusalTail("at-or-above")}`);
|
|
100
|
-
e.code = "rewind_snapshot.unresolvable";
|
|
101
|
-
throw e;
|
|
102
|
-
}
|
|
103
|
-
rewindTarget = anchor;
|
|
104
|
-
}
|
|
89
|
+
if (rewindBefore && !(await historyStore.canRestore(sessionId, rewindTarget)))
|
|
90
|
+
throw beforeWithoutBoundaryRefusal(rewindTarget, sessionId);
|
|
105
91
|
const restoreRoot = historyRoot;
|
|
106
92
|
const restoreSignal = spec.signal ? AbortSignal.any([abortController.signal, spec.signal]) : abortController.signal;
|
|
107
93
|
const restored = await historyStore.restore(sessionId, rewindTarget, executionEnv, restoreRoot, restoreSignal);
|
|
108
94
|
const ledgerLine = (r) => `applied ${r.filesChanged.length}, identical ${r.identicalSkipped.length}, refused ${r.refused.length}${r.refused.length > 0 ? ` (${r.refused.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}, failed ${r.failed.length}${r.failed.length > 0 ? ` (${r.failed.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}`;
|
|
109
95
|
if (restored.outcome === "failed") {
|
|
110
96
|
if (restored.error?.code === "not_found") {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
97
|
+
if (rewindBefore)
|
|
98
|
+
throw beforeWithoutBoundaryRefusal(rewindTarget, sessionId);
|
|
99
|
+
const e = new Error(`rewind-files: no file-history boundary exists for entry "${rewindTarget}" on session "${sessionId}" — the working tree was NOT rewound. ${rewindRefusalTail()}`);
|
|
114
100
|
e.code = "rewind_snapshot.unresolvable";
|
|
115
101
|
throw e;
|
|
116
102
|
}
|
|
@@ -170,7 +156,8 @@ export async function prepareFileHistory(input) {
|
|
|
170
156
|
const fileHistoryBoundary = fileHistoryEnabled && historyScope === sessionId
|
|
171
157
|
? createFileHistoryBoundarySeat({ store: fileHistoryStore, sessionId, env: executionEnv, root: historyRoot, onError: deps.onError })
|
|
172
158
|
: undefined;
|
|
173
|
-
|
|
159
|
+
const fileHistoryFact = fileHistoryEnabled ? "on" : "disabled";
|
|
160
|
+
return { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs };
|
|
174
161
|
}
|
|
175
162
|
function restoredAbsPathsOf(restored, root) {
|
|
176
163
|
const out = [];
|
|
@@ -203,10 +190,37 @@ function resolveWiredFileHistoryStore(candidate) {
|
|
|
203
190
|
}
|
|
204
191
|
return candidate;
|
|
205
192
|
}
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
193
|
+
function resolveRunFileHistoryWord(word) {
|
|
194
|
+
if (word === undefined || word === "disabled")
|
|
195
|
+
return word === "disabled";
|
|
196
|
+
const e = new Error(`rewind-files: TaskSpec.fileHistory holds ${shownWord(word)} — the only value is "disabled"; omit the key to leave the deployment's setting in force`);
|
|
197
|
+
e.code = "rewind.invalid_spec";
|
|
198
|
+
throw e;
|
|
199
|
+
}
|
|
200
|
+
function resolveFileHistoryCaptureWord(word, wiredSeat) {
|
|
201
|
+
if (word === undefined || (word === "off" && wiredSeat !== undefined && wiredSeat !== "disabled"))
|
|
202
|
+
return word === "off";
|
|
203
|
+
const e = new Error(word === "off"
|
|
204
|
+
? `RunnerDeps.fileHistoryCapture: "off" requires a wired RunnerDeps.fileHistoryStore — nothing to switch off; to drop history entirely write fileHistoryStore: "disabled"`
|
|
205
|
+
: `RunnerDeps.fileHistoryCapture holds ${shownWord(word)} — the only value is "off"; omit the key to record whenever a fileHistoryStore is wired`);
|
|
206
|
+
e.code = "config.invalid_file_history_capture";
|
|
207
|
+
throw e;
|
|
208
|
+
}
|
|
209
|
+
function shownWord(word) {
|
|
210
|
+
return typeof word === "string" ? JSON.stringify(word.slice(0, 40)) : word === null || typeof word === "boolean" || typeof word === "number" ? String(word) : `a ${typeof word}`;
|
|
211
|
+
}
|
|
212
|
+
function captureDisabledRefusal(target) {
|
|
213
|
+
const e = new Error(`rewind-files: this run set TaskSpec.fileHistory: "disabled", so it neither records nor restores file history — restoring files to entry "${target}" was refused and the files were NOT rewound`);
|
|
214
|
+
e.code = "rewind.capture_disabled";
|
|
215
|
+
return e;
|
|
216
|
+
}
|
|
217
|
+
function rewindRefusalTail() {
|
|
218
|
+
return `Boundaries are keyed by a turn's INITIAL entry and exist only for turns that recorded history (a RunnerDeps.fileHistoryStore wired, capture on, and the run's file history not disabled); a boundary from the retired whole-tree snapshot era is not restorable by this seat, and reaped boundaries have none.`;
|
|
219
|
+
}
|
|
220
|
+
function beforeWithoutBoundaryRefusal(target, sessionId) {
|
|
221
|
+
const e = new Error(`rewind-files: entry "${target}" has no file-history boundary on session "${sessionId}" — a before-restore needs the target's own boundary and never crosses into an earlier turn, so the working tree was NOT rewound. ${rewindRefusalTail()}`);
|
|
222
|
+
e.code = "rewind_snapshot.unresolvable";
|
|
223
|
+
return e;
|
|
210
224
|
}
|
|
211
225
|
const FILE_HISTORY_BOUNDARY_TIMEOUT_MS = 30_000;
|
|
212
226
|
const FILE_HISTORY_LIVENESS_MEMO = new WeakMap();
|
|
@@ -218,8 +218,8 @@ export interface PrepareHandsReadFaceInput {
|
|
|
218
218
|
* band's Read/notebook/PDF trailer outlets bump keys on it at tool-execute time (the driver owns
|
|
219
219
|
* the object and folds it into result stats). */
|
|
220
220
|
reminderDisclosureCounts: import("../reminder-disclosure.js").ReminderDisclosureCounts;
|
|
221
|
-
/** borrowed-readonly — the first-touch history hook prepare built (present iff
|
|
222
|
-
* wired and
|
|
221
|
+
/** borrowed-readonly — the first-touch history hook prepare built (present iff the run records file history into a
|
|
222
|
+
* wired store and mounts a real fs env); threaded to the write band's trackFileEdit seat. */
|
|
223
223
|
trackFileEdit?: import("../types.js").TrackFileEditHook;
|
|
224
224
|
/** borrowed-readonly — the mutation lane's LANDED observation seat (the accumulator behind
|
|
225
225
|
* `TaskResult.editedFiles`); threaded to the write band's onFileEdited seat. Unlike
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentTool } from "../../internal/harness.js";
|
|
17
17
|
import type { TaskSpec } from "../types.js";
|
|
18
|
-
import type { AnnounceOnceSink, Prepared, PrepareResume, ToolFaceSnapshot } from "./contracts.js";
|
|
18
|
+
import type { AnnounceOnceSink, DateAnchorRead, Prepared, PrepareResume, ToolFaceSnapshot } from "./contracts.js";
|
|
19
19
|
import type { AnnouncedListingSnapshot } from "../../internal/harness.js";
|
|
20
20
|
import type { ResolvedSkillListingBudget } from "../skill-listing-budget.js";
|
|
21
21
|
export interface PrepareListingsInput {
|
|
@@ -65,6 +65,8 @@ export interface PrepareListingsInput {
|
|
|
65
65
|
tzValid: boolean;
|
|
66
66
|
/** borrowed-readonly — the client's declared zone (used only when `tzValid`). */
|
|
67
67
|
userTz: string | undefined;
|
|
68
|
+
/** borrowed-readonly — the session's date anchor as the prompt-inputs phase read it (rides the seam). */
|
|
69
|
+
dateAnchor: DateAnchorRead | undefined;
|
|
68
70
|
}
|
|
69
71
|
export interface PrepareListingsResult {
|
|
70
72
|
/** owned — the agent-listing announce projection (`Prepared.agentListing`): the roster off the
|
|
@@ -2,7 +2,7 @@ import { formatLocalDate } from "../../prompts/default.js";
|
|
|
2
2
|
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
3
3
|
import { renderAgentListingDelta } from "./turn-attachments.js";
|
|
4
4
|
export function prepareListings(input) {
|
|
5
|
-
const { spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingBudget, listingRideRef, renderWithDate, envFacts, tzValid, userTz } = input;
|
|
5
|
+
const { spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingBudget, listingRideRef, renderWithDate, envFacts, tzValid, userTz, dateAnchor } = input;
|
|
6
6
|
if (spec.agents !== undefined && spec.agents.length > 0) {
|
|
7
7
|
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
8
8
|
for (const def of spec.agents) {
|
|
@@ -72,7 +72,7 @@ export function prepareListings(input) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
const dateChange = renderWithDate
|
|
75
|
-
? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined) }
|
|
75
|
+
? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined), anchor: dateAnchor }
|
|
76
76
|
: undefined;
|
|
77
77
|
return { agentListing, announcedListingsRef, dateChange };
|
|
78
78
|
}
|
|
@@ -81,6 +81,7 @@ export async function prepareMemory(input) {
|
|
|
81
81
|
let captureIndeterminate = false;
|
|
82
82
|
let captureLineageIndeterminate = false;
|
|
83
83
|
let memoryEngineSession;
|
|
84
|
+
const openedLegs = [];
|
|
84
85
|
let memoryTools;
|
|
85
86
|
let writeChannelOpen = false;
|
|
86
87
|
let memoryMarkedEntriesServed = false;
|
|
@@ -385,6 +386,7 @@ export async function prepareMemory(input) {
|
|
|
385
386
|
sessionId,
|
|
386
387
|
...(m.write === null && shadowNames !== undefined && shadowNames.size > 0 ? { shadowNames } : {}),
|
|
387
388
|
});
|
|
389
|
+
openedLegs.push(() => void m.engine.endSessionLeg(handle, sessionId));
|
|
388
390
|
mounted.push(handle);
|
|
389
391
|
materializedResidue.push(...planeScopes(m.scopes, m.write));
|
|
390
392
|
if (m.write !== null) {
|
|
@@ -425,10 +427,10 @@ export async function prepareMemory(input) {
|
|
|
425
427
|
committedHeaders: () => personalEngine.listCommittedHeaders(planeScopes(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null)),
|
|
426
428
|
},
|
|
427
429
|
];
|
|
428
|
-
harvestBoth = async () => {
|
|
430
|
+
harvestBoth = async (phase) => {
|
|
429
431
|
const writeFirst = writeIsPersonal ? [personalEngine, personalHandle] : [projectEngine, projectHandle];
|
|
430
432
|
const readOther = writeIsPersonal ? [projectEngine, projectHandle] : [personalEngine, personalHandle];
|
|
431
|
-
const writeReport = await writeFirst[0].harvest(writeFirst[1], { ...pollutedOpts(writeFirst[0]), sessionId, ...(await admitNothingOptsNow()) });
|
|
433
|
+
const writeReport = await writeFirst[0].harvest(writeFirst[1], { ...pollutedOpts(writeFirst[0]), sessionId, ...(await admitNothingOptsNow()), phase });
|
|
432
434
|
let readReport;
|
|
433
435
|
let readFailure;
|
|
434
436
|
try {
|
|
@@ -448,12 +450,13 @@ export async function prepareMemory(input) {
|
|
|
448
450
|
const personalEngine = personal.engine;
|
|
449
451
|
await armCaptureOptOut(personalEngine);
|
|
450
452
|
const handle = await personalEngine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
453
|
+
openedLegs.push(() => void personalEngine.endSessionLeg(handle, sessionId));
|
|
451
454
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
452
455
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
453
456
|
writeEngine = personalEngine;
|
|
454
457
|
writeHandle = handle;
|
|
455
458
|
injectFn = () => personalEngine.inject(handle, { writeToolMounted: writeChannelOpen, reminderMark: input.reminderMark });
|
|
456
|
-
harvestBoth = async () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...(await admitNothingOptsNow()) });
|
|
459
|
+
harvestBoth = async (phase) => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...(await admitNothingOptsNow()), phase });
|
|
457
460
|
toolPlanes = [
|
|
458
461
|
{
|
|
459
462
|
backend: retrievalBackend(personal.backend, adoptionRestricted || captureOptedOut || captureIndeterminate || memorySpec.writeScope === null),
|
|
@@ -477,12 +480,13 @@ export async function prepareMemory(input) {
|
|
|
477
480
|
});
|
|
478
481
|
await armCaptureOptOut(engine);
|
|
479
482
|
const handle = await engine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
483
|
+
openedLegs.push(() => void engine.endSessionLeg(handle, sessionId));
|
|
480
484
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
481
485
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
482
486
|
writeEngine = engine;
|
|
483
487
|
writeHandle = handle;
|
|
484
488
|
injectFn = () => engine.inject(handle, { writeToolMounted: writeChannelOpen, reminderMark: input.reminderMark });
|
|
485
|
-
harvestBoth = async () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...(await admitNothingOptsNow()) });
|
|
489
|
+
harvestBoth = async (phase) => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...(await admitNothingOptsNow()), phase });
|
|
486
490
|
toolPlanes = [
|
|
487
491
|
{
|
|
488
492
|
backend: retrievalBackend(backend, adoptionRestricted || captureOptedOut || captureIndeterminate || memorySpec.writeScope === null),
|
|
@@ -574,7 +578,7 @@ export async function prepareMemory(input) {
|
|
|
574
578
|
};
|
|
575
579
|
const harvestSafe = async (phase = "terminal") => {
|
|
576
580
|
try {
|
|
577
|
-
const report = await harvestBoth();
|
|
581
|
+
const report = await harvestBoth(phase);
|
|
578
582
|
if (!report.ok && report.incident) {
|
|
579
583
|
const named = report.reportId !== undefined ? `${report.incident.kind}, report ${report.reportId}` : report.incident.kind;
|
|
580
584
|
deps.onError?.(new Error(`memory harvest refused (${named}): ${report.incident.detail}`), { phase: "memory", sessionId });
|
|
@@ -615,6 +619,10 @@ export async function prepareMemory(input) {
|
|
|
615
619
|
handle: writeHandle,
|
|
616
620
|
inject: injectFn,
|
|
617
621
|
harvest: harvestSafe,
|
|
622
|
+
release: () => {
|
|
623
|
+
for (const end of openedLegs)
|
|
624
|
+
end();
|
|
625
|
+
},
|
|
618
626
|
captureOptOut: {
|
|
619
627
|
optedOut: () => {
|
|
620
628
|
if (captureOptedOut)
|
|
@@ -733,6 +741,9 @@ export async function prepareMemory(input) {
|
|
|
733
741
|
memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId), provenance: input.memoryProvenance, writeScope: memorySpec.writeScope });
|
|
734
742
|
}
|
|
735
743
|
catch (err) {
|
|
744
|
+
if (memoryEngineSession === undefined)
|
|
745
|
+
for (const end of openedLegs)
|
|
746
|
+
end();
|
|
736
747
|
if (typeof err.code === "string" &&
|
|
737
748
|
(err.code.startsWith("config.memory_scope") ||
|
|
738
749
|
err.code.startsWith("config.memory_project") ||
|
|
@@ -6,6 +6,7 @@ import { boundInputHashOf } from "../canonical-json.js";
|
|
|
6
6
|
import { buildRiskDescriptor, checkpointVersionFor, debitLedger, encodeAtFidelity, mintCheckpointId, mintCheckpointToken, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
7
7
|
import { pauseOf } from "../pause-registry.js";
|
|
8
8
|
import { askCarryRowMembers } from "../hooks.js";
|
|
9
|
+
import { askMandateMember, ownedClearanceFacts } from "../ask-origin.js";
|
|
9
10
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
10
11
|
import { advertisedWritableRootsSeat, isLiveApproverSeat, carriesBidiControls, describeThrown, refuseOutOfContractDecision, tryCloneArgs } from "../tool-policy.js";
|
|
11
12
|
import { InMemoryToolResultStore, ScopedToolResultStore, isVolatileOffloadStore } from "../tool-result-store.js";
|
|
@@ -21,6 +22,9 @@ class ParkRefusal extends Error {
|
|
|
21
22
|
this.name = "ParkRefusal";
|
|
22
23
|
}
|
|
23
24
|
}
|
|
25
|
+
const wordlessDemand = (toolName) => {
|
|
26
|
+
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
|
|
27
|
+
};
|
|
24
28
|
export function prepareParkAsk(input) {
|
|
25
29
|
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
26
30
|
if (askLane === undefined || saga === undefined)
|
|
@@ -148,6 +152,7 @@ export function prepareParkAsk(input) {
|
|
|
148
152
|
throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and no policy is wired to re-adjudicate the stored shape ` +
|
|
149
153
|
`(no basePolicyForResumeEdit override and no caller toolPolicy) — refusing to file a row whose executable form nothing adjudicated`);
|
|
150
154
|
}
|
|
155
|
+
let storedShapeFacts;
|
|
151
156
|
if (projectionMovedArgs && basePolicyForResumeEdit !== undefined) {
|
|
152
157
|
const presented = encodeAtFidelity(backendFidelity, parkedArgs);
|
|
153
158
|
if (!presented.ok) {
|
|
@@ -177,7 +182,10 @@ export function prepareParkAsk(input) {
|
|
|
177
182
|
`${reprojected.action === "deny" && reprojected.message ? `: ${reprojected.message}` : ""} — ` +
|
|
178
183
|
`refusing to file a row whose executable form nothing adjudicated`);
|
|
179
184
|
}
|
|
185
|
+
if (reprojected.action === "ask")
|
|
186
|
+
storedShapeFacts = ownedClearanceFacts(reprojected);
|
|
180
187
|
}
|
|
188
|
+
const rowRealApproval = realApproval ?? (storedShapeFacts?.requiresRealApproval === true ? { origin: carry?.origin ?? wordlessDemand(req.toolName) } : undefined);
|
|
181
189
|
if (offloadStore && isVolatileOffloadStore(offloadStore) && !offloadStore.isEmpty()) {
|
|
182
190
|
throw new ParkRefusal("durable suspend requires a durable toolResultStore (the default InMemoryToolResultStore " +
|
|
183
191
|
"holds offloaded results that are lost across a cross-replica resume); inject " +
|
|
@@ -237,15 +245,15 @@ export function prepareParkAsk(input) {
|
|
|
237
245
|
shellGateDoctrine: effectiveShellGate,
|
|
238
246
|
});
|
|
239
247
|
gate =
|
|
240
|
-
safety !== undefined ||
|
|
248
|
+
safety !== undefined || rowRealApproval !== undefined
|
|
241
249
|
? {
|
|
242
250
|
kind: "irreversible_ask",
|
|
243
251
|
reason: safety !== undefined
|
|
244
252
|
? `human approval required before safety-tightened tool "${req.toolName}"`
|
|
245
|
-
: `real human approval required for tool "${req.toolName}" (non-budgetable: ${
|
|
253
|
+
: `real human approval required for tool "${req.toolName}" (non-budgetable: ${rowRealApproval.origin})`,
|
|
246
254
|
toolName: req.toolName,
|
|
247
255
|
...(safety !== undefined ? { safetyAxis: safety } : {}),
|
|
248
|
-
...(
|
|
256
|
+
...(rowRealApproval !== undefined ? { realApproval: rowRealApproval } : {}),
|
|
249
257
|
riskDescriptor,
|
|
250
258
|
}
|
|
251
259
|
: durableApproval
|
|
@@ -264,15 +272,26 @@ export function prepareParkAsk(input) {
|
|
|
264
272
|
const scope = durableApproval?.scope || checkpointScopeOf({ principal: spec.principal });
|
|
265
273
|
const ttlMs = sanitizedTtlMs(durableApproval?.ttlMs);
|
|
266
274
|
const checkpointState = serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd());
|
|
267
|
-
if (
|
|
275
|
+
if (rowRealApproval !== undefined && checkpointState.readFace !== undefined) {
|
|
268
276
|
checkpointState.readFace = { ...checkpointState.readFace, realApproval: true };
|
|
269
277
|
}
|
|
270
278
|
const approvalLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
|
|
279
|
+
const rowMatchedAskRule = matchedAskRule ?? storedShapeFacts?.matchedAskRule;
|
|
280
|
+
const clearanceFacts = {
|
|
281
|
+
...(carry?.mandated === true || askMandateMember(storedShapeFacts).mandated === true ? { mandated: true } : {}),
|
|
282
|
+
...(rowRealApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
283
|
+
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
284
|
+
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
|
|
285
|
+
...(rowMatchedAskRule !== undefined ? { matchedAskRule: rowMatchedAskRule } : {}),
|
|
286
|
+
...(probeMandated === true ? { probeMandated: true } : {}),
|
|
287
|
+
...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
|
|
288
|
+
...(segmentCoverage !== undefined ? { segmentCoverage } : {}),
|
|
289
|
+
};
|
|
271
290
|
const mintedAt = Date.now();
|
|
272
291
|
cp = {
|
|
273
292
|
token,
|
|
274
293
|
checkpointId: mintCheckpointId(),
|
|
275
|
-
version: checkpointVersionFor({ resource: false, binding: true, token: approvalLedger.totalTokens !== undefined, orgAdmission: orgAdmissionCheckpointState(), f012: f012CheckpointState(), realApproval:
|
|
294
|
+
version: checkpointVersionFor({ resource: false, binding: true, token: approvalLedger.totalTokens !== undefined, orgAdmission: orgAdmissionCheckpointState(), f012: f012CheckpointState(), realApproval: rowRealApproval !== undefined, face: faceCheckpointState() }),
|
|
276
295
|
scope,
|
|
277
296
|
sessionId,
|
|
278
297
|
leafId,
|
|
@@ -291,18 +310,10 @@ export function prepareParkAsk(input) {
|
|
|
291
310
|
...(bidi ? { hasBidiControls: true } : {}),
|
|
292
311
|
};
|
|
293
312
|
})(),
|
|
294
|
-
...(
|
|
313
|
+
...(rowRealApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
314
|
+
...askMandateMember(clearanceFacts),
|
|
295
315
|
...askCarryRowMembers(carry),
|
|
296
|
-
...ruleOffersOf(req.toolName, parkedArgs,
|
|
297
|
-
...(carry?.mandated === true ? { mandated: true } : {}),
|
|
298
|
-
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
299
|
-
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
300
|
-
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
|
|
301
|
-
...(matchedAskRule !== undefined ? { matchedAskRule } : {}),
|
|
302
|
-
...(probeMandated === true ? { probeMandated: true } : {}),
|
|
303
|
-
...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
|
|
304
|
-
...(segmentCoverage !== undefined ? { segmentCoverage } : {}),
|
|
305
|
-
}),
|
|
316
|
+
...ruleOffersOf(req.toolName, parkedArgs, clearanceFacts),
|
|
306
317
|
boundInputHash: boundInputHashOf(parkedArgs),
|
|
307
318
|
batchToolCallIds,
|
|
308
319
|
completedCallIds,
|