@sema-agent/core
Version:
Stateless, task-oriented AI agent core
111 lines (110 loc) • 7.76 kB
TypeScript
import type { RunnerDeps } from "../types.js";
import type { ExecutionEnv } from "../../internal/harness.js";
import type { PathRuleBases } from "../permission-rule-model.js";
import type { ChildPlacement, InheritedGate, RunInternals } from "./contracts.js";
import type { ToolPolicy } from "../tool-policy.js";
import { CheckpointError } from "../checkpoint-store.js";
/**
* Whether this deployment can place a child agent in a tree OTHER than the caller's — the one fact behind
* every request of that shape: an explicit `cwd`, a workflow agent's `isolation: "worktree"`, and the
* delegation tool's per-call or per-definition `isolation: "worktree"`.
*
* Only an `executionEnvFactory` can: it builds one execution env per task, so a child can be rooted
* wherever the request says. A static `executionEnv` is ONE shared env with ONE cwd — every child that
* runs on it runs in the caller's root, whatever tree it was asked to work in. A request the deployment
* cannot honor is refused before the child starts (and, for a worktree, before one is minted); each site
* keeps its own code and sentence for the refusal, but none decides the condition itself.
*/
export declare function deploymentCanRerootChild(deps: Pick<RunnerDeps, "executionEnvFactory">): boolean;
/**
* Where a child the caller asked to place at `asked` actually landed (`childRoot`, the root its environment gave it) — the
* ONE judgment every reader of that fact takes
* (the delegation tool's worktree request, and the anchors of the rules the child inherits):
* · `honored` — the environment's root is where the child was asked to be (canonical on both sides);
* · `caller_tree` — it is not, and it lies inside the caller's own tree (`callerRoot`): the same filesystem, the wrong
* directory — a worktree request then fails (the child would write the caller's checkout);
* · `elsewhere` — neither: another filesystem namespace (a sandbox the factory minted). The child runs there, it is not
* in the worktree it was offered, and it takes the caller's path rules again at its own anchors.
* A path the environment cannot canonicalize is compared as spelled. Residual: two namespaces that hold the same path
* string, with a factory claiming to honor it, read `honored`.
*/
export declare function childPlacementOf(env: Pick<ExecutionEnv, "canonicalPath">, childRoot: string, asked: string | undefined, callerRoot: string): Promise<ChildPlacement>;
/** A delegated leg's placement: the place asked for is the hint (a minted worktree, an explicit cwd, the caller's own cwd)
* — or, for a worktree the factory mints itself (`isolation`), that worktree on the caller's host (a remote environment's
* is another namespace). A leg no delegation caller placed (no `callerRoot`) has no placement and reads nothing. */
export declare function childPlacementOnLeg(env: ExecutionEnv, internals: Pick<RunInternals, "isolation" | "parentCwd"> & {
readonly callerRoot: string;
}, root: string): Promise<ChildPlacement>;
/** The refusal of a worktree the caller minted for a child its environment placed in the caller's own tree. */
export declare function worktreeNotHonored(cwd: string): Error & {
code: "isolation.unavailable";
};
/**
* The chain's top leg's anchors as THIS leg reads them (the person's global / session path rows and every `~/` row anchor
* there): the chain's own (`live` as recorded, else the checkpoint's `seed` rebased — {@link recordedAnchors}), unless this
* leg is a child placed `elsewhere` or no chain names one — then this leg is the top. A recorded root that is not an
* absolute path anchors nothing: the rows it would anchor read `unreadable`.
*/
export declare function chainTopAnchors(cfg: {
leg: PathRuleBases;
live: InheritedGate["pathRuleAnchors"];
seed: InheritedGate["pathRuleAnchors"];
placement: ChildPlacement | undefined;
rebase: (p: string) => string;
}): PathRuleBases;
/** The installation records a leg reads: the live chain's (as recorded), else the checkpoint's (through the workspace rebase). */
export interface ChainInstallations {
readonly live: InheritedGate["pathRuleInstallations"];
readonly seed: InheritedGate["pathRuleInstallations"];
}
/** Why no usable record says where a chain entry was installed: no record (a chain written before the record existed), a
* record that does not cover the chain position for position, or a member that names no absolute root. */
export type ChainInstallationGap = "absent" | "misaligned" | "invalid";
/**
* The chain entries a leg could not install where they were installed: each UNINSTALLED entry (a host re-supplying a
* chain) whose path rules depend on the installing leg, with no usable record of that leg. Such a chain is refused —
* installing those entries anywhere else (the chain's top leg, this leg) would move what they protect. Entries whose rules
* do not depend on it, and installed entries, never need a record.
*/
export declare function chainInstallationGaps(chain: readonly ToolPolicy[], installations: ChainInstallations, rebase: (p: string) => string): Array<{
readonly position: number;
readonly gap: ChainInstallationGap;
}>;
/** The refusal of a chain with {@link chainInstallationGaps} (`resume.parent_constraint_mismatch`, `detail.reason` naming
* the first gap's form); `pending` says the checkpoint is still pending (the refusal came before the claim). */
export declare function chainInstallationRefusal(gaps: ReadonlyArray<{
readonly position: number;
readonly gap: ChainInstallationGap;
}>, pending: boolean): CheckpointError;
/**
* How a leg holds a caller policy — its own (`TaskSpec.toolPolicy` / `RunnerDeps.toolPolicy`) or one its delegation chain
* hands it: a policy is installed where it ENTERS the chain, and a child holds its ancestor's installation (one object,
* the same anchors) — its own caller slot included when that slot names the policy an ancestor installed. Installed again:
* on a child placed `elsewhere` (another filesystem namespace — the ancestor's anchors name another namespace's files), at
* this leg's anchors; a chain entry handed uninstalled (a host re-supplying a chain), where its installing leg recorded it
* (`InheritedGate.pathRuleInstallations`, live or from the checkpoint). An uninstalled entry whose rules depend on that
* leg and whose record is not usable refuses the leg ({@link chainInstallationRefusal}); one whose rules do not, reads
* the same wherever it is installed (the chain's top leg).
*/
export declare function chainPolicyOnLeg(cfg: {
leg: PathRuleBases;
chainTop: PathRuleBases;
placement: ChildPlacement | undefined;
chain: readonly ToolPolicy[];
installations: ChainInstallations;
rebase: (p: string) => string;
}): {
own: (policy: ToolPolicy | undefined) => ToolPolicy | undefined;
chained: (policy: ToolPolicy, position: number) => ToolPolicy;
};
/** The installation record of a chain entry (its root and home), `null` for an entry with no path rules installed. */
export declare function installationRecordOf(policy: ToolPolicy): {
readonly root: string;
readonly home?: string;
} | null;
/** What a leg hands its children about anchors (data half): the chain's top leg's anchors — the person's global / session
* rows and every `~/` row anchor there — and where each entry handed down was installed, position for position (a chain
* re-supplied uninstalled is installed there again; absent when no entry is an installation). */
export declare function chainAnchorRecords(chainTop: PathRuleBases, chain: readonly {
readonly policy: ToolPolicy;
}[]): Pick<InheritedGate, "pathRuleAnchors" | "pathRuleInstallations">;