@sema-agent/core
Version:
Stateless, task-oriented AI agent core
100 lines (99 loc) • 7.79 kB
TypeScript
/**
* PrepareTask's file-history phase, verbatim from the driver: the DV-14 first-touch
* policy door, the `RunnerDeps.fileHistoryStore` / `fileHistoryCapture` and `TaskSpec.fileHistory` bad-value doors, the retired `rewindFiles` two-sense
* split, the restoreFiles / resumeAt / rewindFilesTo combination gates, the run's history coordinates
* (canonical root, recording scope, filesystem identity), the fork carry, the child-scope refusal, the
* rewind RESTORE itself (anchor translation in "before" mode, the per-file ledger, partial-restore
* policy), the `trackFileEdit` hook the hands band calls before every mutation lane, and the turn-start
* boundary seat runtask drives.
*
* Runs INSIDE the driver's prepare try-block, after the workspace root is FINAL (`taskRootFinal`) and the
* env is resolved: every refusal here throws through to the driver's prepare-throw catch (a minted
* ownedEnv is destroyed there, a resumeAt branch written back, the session view forgotten). The phase acquires
* no resource of its own — the two seats it returns are closures over the store the deployment owns.
*
* The request door (the bad-value doors and every refusal the request decides alone) lives in
* `./file-history-request.ts` and runs BEFORE the session is acquired; this phase receives its resolution.
* The module-level helpers below the phase (`restoredAbsPathsOf`,
* the refusal's epoch-tail wording helper, `adoptForkedFileHistory` with its per-store carry memo,
* `createFileHistoryBoundarySeat`, `resolveFileHistoryCoordinates` / `resolveFileHistoryScope` /
* `fileHistoryFilesystemIdentity`, `childScopeRewindRefusal`) moved with it unchanged; the two the test
* suite reaches through the driver are re-exported from there.
*/
import type { ExecutionEnv } from "../../internal/harness.js";
import type { StoredSession } from "../../engine/session/session.js";
import type { RunnerDeps, TaskResult, TaskSpec, TrackFileEditHook } from "../types.js";
import type { FileHistoryBoundarySeat, RunInternals } from "./contracts.js";
import type { FileHistoryRequest } from "./file-history-request.js";
export interface PrepareFileHistoryInput {
/** borrowed-readonly — the deployment seats this phase reads: the first-touch failure policy, the history
* store and its capture switch (all validated HERE, once), the incident sink, and the retired-epoch diagnostic probe. */
deps: Pick<RunnerDeps, "onError">;
/** borrowed-readonly — the REBOUND spec's rewind request fields and off-switch word (the retired `rewindFiles`
* spelling is read off the same object as an unknown key) and the caller's cancel signal for the restore. */
spec: Pick<TaskSpec, "resumeAt" | "restoreFiles" | "acceptPartialRestore" | "signal">;
/** borrowed-readonly — the request door's resolution ({@link resolveFileHistoryRequest}), judged before the session was
* acquired: every refusal the request and the deployment's seats decide on their own has already thrown. */
request: FileHistoryRequest;
/** borrowed-readonly — the trusted lineage a delegating parent handed this run (absent for a root run
* and for a host's own fork), read for the recording-scope decision only. */
internals: Pick<RunInternals, "fileHistoryLineage"> | undefined;
/** borrowed-readonly — the run's resolved env (owned or static or the stub); every history verb runs
* against ITS filesystem ops. Never rebound or destroyed here. */
executionEnv: ExecutionEnv;
/** borrowed-readonly — the FINAL task root (the workspace-restore phase's output binding). */
taskRootFinal: string;
/** borrowed-readonly — the "real fs env" fact (`ownedEnv !== undefined || deps.executionEnv !== undefined`). */
handsEnabled: boolean;
/** borrowed-readonly — the acquired session id: the run's OWN history scope and the restore's session key. */
sessionId: string;
/** borrowed-readonly — the acquired session, read for the fork lineage (`getMetadata`); never written here. */
session: Pick<StoredSession, "getMetadata">;
/** borrowed-readonly — the run's abort controller (driver-minted); only its `signal` is read, composed
* with `spec.signal` for the restore. */
abortController: AbortController;
}
export interface PrepareFileHistoryResult {
/** borrowed-readonly — the hook the hands band calls before every mutation lane; `undefined` when the
* run does not record (no store, capture turned off by any layer, or no real fs env). */
trackFileEdit: TrackFileEditHook | undefined;
/** borrowed-readonly — the turn-start boundary seat runtask drives; `undefined` when not live or when
* this run records into its ROOT session's scope (a child mints no boundaries). */
fileHistoryBoundary: FileHistoryBoundarySeat | undefined;
/** borrowed-readonly — the absolute paths the restore MUTATED (empty when no restore ran), for the
* read-face phase's read-state invalidation. */
restoredFilePaths: string[];
/** borrowed-mutable — the non-fatal rewind disclosures (`TaskResult.rewindNotes`). Mutation owners after
* this call: none — every push happens inside this phase; the driver only reads it into `Prepared`. */
rewindNotes: NonNullable<TaskResult["rewindNotes"]>;
/** borrowed-readonly — whether this run RECORDS file history (store wired, no layer turned capture off, AND hands); restore reads the store seat. */
fileHistoryEnabled: boolean;
/** borrowed-readonly — the manifest's word: `"on"` iff live, else `"disabled"` (whichever layer or missing half). */
fileHistoryFact: "on" | "disabled";
/** borrowed-readonly — the scope this run's first-touch records land in (own session, or the root
* session's for a same-tree delegated child). */
historyScope: string;
/** borrowed-readonly — the canonical history root every history verb keys against. */
historyRoot: string;
/** borrowed-readonly — the filesystem identity of the env (the lineage's second tree coordinate). */
historyFs: string;
}
export declare function prepareFileHistory(input: PrepareFileHistoryInput): Promise<PrepareFileHistoryResult>;
/** The ONE reading of {@link RunInternals.fileHistoryLineage} (see {@link resolveFileHistoryCoordinates}):
* the lineage's scope iff BOTH tree coordinates match; otherwise the run's own session. */
export declare function resolveFileHistoryScope(lineage: RunInternals["fileHistoryLineage"], historyRoot: string, historyFs: string, sessionId: string): string;
/**
* WHICH filesystem an env is a view of — the second coordinate of tree identity for the file-history
* lineage, read from what the env's MINTER states rather than inferred from a path:
* · a remote workspace names itself through its {@link WorkspaceHandle} (provider + sandbox, and the
* device lane's id when stamped) — two runs on one sandbox share a tree, two sandboxes never do;
* · an env that DECLARES its paths host-local (`hostLocalPaths: true`), or is the host adapter itself
* (a {@link NodeExecutionEnv}, which the seam names as the host-local default), is the
* control-plane host's filesystem — every such env is one tree;
* · anything else — an env declaring `hostLocalPaths: false`, or an undeclared custom adapter — is
* attested to nothing, so it is its OWN tree: a per-instance token, which still matches when the
* child literally holds the parent's env object and never matches a fresh per-task mint.
* A remote handle that cannot be read (an env not yet connected) falls to the per-instance arm for
* the same reason: unattested is not shared.
*/
export declare function fileHistoryFilesystemIdentity(env: ExecutionEnv): string;