@sema-agent/core
Version:
Stateless, task-oriented AI agent core
71 lines (70 loc) • 5.35 kB
TypeScript
/**
* PrepareTask's Artifact phase: the `RunnerDeps.artifactHost` seat is read once; an absent seat mounts
* nothing and announces nothing; a seat whose declaration fails an invariant mounts nothing and announces
* `config.artifact_host_invalid` (operator audience) so a bad wiring can never read as "no hosting"; a
* valid seat mounts ONE `Artifact` tool assembled from the declaration, on the same optional band the LSP
* seat uses. The tool's local-file read goes through the run's fs fence (root containment, read-deny, read
* face) exactly as `Read` does, then reads the bytes once from the execution env. A local stub's store
* (`capabilities.storageDir`) must lie OUTSIDE every root the run may write or read — a ledger the model can
* edit or read through the fs tools is not a trust boundary — judged by {@link rootAdmittingArtifactStore} at
* every point a root is admitted: here over the prepare-time roots (a store that overlaps one refuses the mount,
* naming both), and at an in-flight read-root grant over the granted root (the grant is refused). The phase
* publishes the declared store into the run's {@link ArtifactStoreRef} for that second point.
* Synchronous: the stretch has no await (the file read is a closure the tool calls later).
*/
import type { PermissionModeController } from "../mode-policy.js";
import type { ExecutionEnv } from "../../internal/harness.js";
import type { OnAsk } from "../tool-policy.js";
import { type ArtifactPlane } from "../artifact-host.js";
import { RosterBuilder } from "../tool-roster.js";
import type { AgentTool, RunnerDeps } from "../types.js";
import type { ContainmentFace } from "../../tools/fs/index.js";
import type { PlacedReadDenyMatcher } from "../../tools/fs/read-deny-home.js";
import { type ArtifactStoreRef } from "./read-root-grant.js";
import type { Prepared } from "./contracts.js";
export interface PrepareArtifactInput {
/** borrowed-readonly — the deployment's hosting seat and the notice sink the refusal arm announces on. */
deps: Pick<RunnerDeps, "artifactHost" | "onNotice" | "tracer">;
/** borrowed-readonly — the engine-minted run id: the host's `onMount` key and every call context's `runId`. */
runId: string;
/** borrowed-readonly — the host task id, the call context's audit attribution. */
taskId: string;
/** borrowed-readonly — the acquired session id (notice attribution and the call context's `sessionId`). */
sessionId: string;
/** borrowed-readonly — the spawned name this run carries, when any (call-context attribution only). */
agentName: string | undefined;
/** borrowed-readonly — the run's effective approver (`spec.onAsk ?? deps.onAsk`, the seat the tool gate resolves at):
* the tool's shared-read consent gate rides it as a mount dep (a core mount's execute ctx is minimal). */
onAsk: OnAsk | undefined;
/** borrowed-readonly — the leg's live permission-mode seat; the tool's shared-read consent gate arbitrates at it before
* the approver is consulted (a released read is delivered, undisclosed to nobody: the gate records it). */
permissionMode: PermissionModeController;
/** borrowed-readonly — the run's resolved env: the byte source of a published local file. */
executionEnv: ExecutionEnv;
/** borrowed-readonly — the canonical task root the fs fence contains reads to; undefined ⇒ no local-file publish. */
rootCanonical: string | undefined;
/** borrowed-readonly — the extra containment roots (write and read-only) the fence admits, read per call (the run's
* live set: an in-flight read-root grant widens it). */
additionalRootsCanonical: () => readonly string[];
/** borrowed-readonly — the read-deny matcher in force for the run, when any. */
readDenyMatcher: PlacedReadDenyMatcher | undefined;
/** borrowed-readonly — the resolved read face (`"open"` lifts root containment for reads). */
resolvedReadFace: ContainmentFace | undefined;
/** borrowed-readonly — the tracked-cwd cell; `current` is read per call as the relative-path base. */
handsCwdRef: Prepared["cwdRef"];
/** borrowed-mutable — the run's roster builder; writer here: ONE mount when the seat is valid. */
roster: RosterBuilder;
/** borrowed-readonly — the first-party result-offload wrapper every core mount passes through (a large read or a conflict snapshot is offloaded, not inlined). */
firstPartyOffload: (tool: AgentTool) => AgentTool;
/** borrowed-mutable — the env-hand membership set the memory content-origin face reads (a tool that reads the execution env's files is a hand); writer here: `add("Artifact")` on the mount. */
envHandToolNames: Set<string>;
/** borrowed-mutable — the run's store cell the in-flight read-root grant reads (minted by the hands phase; absent on
* a hands-less leg, which has no grant); writer here: the declared `storageDir`'s physical path once the
* declaration validates. */
artifactStoreRef?: ArtifactStoreRef;
}
export interface PrepareArtifactResult {
/** owned — the mounted face's plane, or undefined when nothing mounted (absent seat, or an invalid one). */
artifactPlane: ArtifactPlane | undefined;
}
export declare function prepareArtifact(input: PrepareArtifactInput): PrepareArtifactResult;