UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

100 lines (99 loc) • 7.79 kB
/** * 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;