@sema-agent/core
Version:
Stateless, task-oriented AI agent core
127 lines (126 loc) • 9.64 kB
TypeScript
import type { SessionStore } from "../session.js";
export { runGuardChain, type GuardChainArgs, type GuardChainOutcome } from "./prepare-context-lane.js";
export { gatedCallIdOf, USAGE_WINDOW_REAP_MARGIN_MS } from "./park-commit.js";
export { batchContextAt } from "./prepare-park-ask.js";
export { compensateUnparkedPause } from "./prepare-suspend-saga.js";
export { DEFAULT_IRREVERSIBLE_SCOPE, checkpointScopeOf, placementValueOrAbsent } from "./checkpoint-scope.js";
export { mcpManifestEntries } from "./prepare-wiring-manifest.js";
export { __resetMaterializeEnvAnnouncements } from "./prepare-tool-disclosure-mount.js";
export { fileHistoryFilesystemIdentity, resolveFileHistoryScope } from "./prepare-file-history.js";
import type { RunnerSelfSeat } from "./contracts.js";
import type { Prepared, PrepareResume, PrepareTaskFn, RunInternals } from "./contracts.js";
export type { FileHistoryBoundarySeat, InheritedGate, Prepared, PreparedMicroCompact, PrepareResume, ResolvedWorkspace, RunInternals, UsageGovernance } from "./contracts.js";
import type { ExecutionEnv } from "../../internal/harness.js";
import type { RunnerDeps, TaskSpec } from "../types.js";
/** Test seam (mirrors `__resetMaterializeEnvAnnouncements`): never called by production code.
* Re-arms BOTH arms (a WeakMap has no clear — it is re-minted). Deliberately UNLIKE the read-face
* seam below (console latch only): tests here legitimately reuse ONE sink across prepares to pin
* the per-sink dedup itself, so the seam must be able to re-arm a still-referenced sink. */
export declare function __resetToolModelGateAnnouncements(): void;
/**
* The model gate's per-session READ face ({@link WiringManifest.modelGate}) — projected from the SAME
* gate decision {@link announceToolModelGate} reads (one source; the notice is the operator-sink
* dialect, this the manifest dialect). `undefined` when no default-mounted tool was trimmed on this
* run. The restore sentence is the notice's, verbatim.
*/
export declare const TOOL_MODEL_GATE_RESTORE_SENTENCE = "restore via TaskSpec.restoreGatedTools, SEMA_TOOL_MODEL_GATE=off, or RunnerDeps.toolModelGate: false";
export { __resetReadFaceClampAnnouncement } from "./prepare-hands-readface.js";
/**
* How long before an execution environment's declared `lifetimeMs` expires the engine
* stops the run and checkpoints it. The margin has to cover ONE suspend: pausing/snapshotting the
* workspace, minting the checkpoint, committing it durably, and letting the loop stop cleanly — with
* room for a slow durable store. 60s is generous for that work and small next to any real sandbox
* lifetime (the shortest tier observed in the platform survey is minutes), so it neither truncates the
* task nor risks losing the race with the platform's reclaim.
*
* It is an ENGINE constant, not a knob: an adapter that wants a wider safety band declares a shorter
* `lifetimeMs` (it owns the platform fact), and one number that every deployment shares keeps the
* checkpoint-cost calibration in one place.
*/
export declare const ENV_LIFETIME_SUSPEND_MARGIN_MS = 60000;
/**
* Resolve the moment an env's lifetime EXPIRES (epoch ms), or `undefined` when the env
* declares none / cannot be aged. Split out as a pure function so the anchor rules are testable and
* stated once.
*
* Honesty of the anchor, in order of preference:
* 1. `env.lifetimeStartedAt` — the platform's own answer. The only anchor that survives an env being
* older than this run (a shared static env, or a sandbox restored from a snapshot).
* 2. `observedAt` — when this run's `executionEnvFactory` MINTED the env. Exact only for that case,
* which is why the caller passes it only then.
* 3. Nothing — an env that declares a lifetime, self-reports no start, and was not minted here. The
* lifetime is NOT enforced and the caller reports it, because the only remaining guess (task start)
* OVERSTATES the remaining lifetime, i.e. errs toward the mid-turn kill this mechanism exists to
* prevent. A silent under-estimate would be the wrong direction to fail in.
*
* An unevaluable `lifetimeMs` is refused (`config.env_lifetime_invalid`) rather than dropped — the
* `resolveTaskLimits` posture: an adapter that fumbled the units must not silently get no protection.
*/
export declare function resolveEnvLifetimeExpiry(env: ExecutionEnv, observedAt: number | undefined): {
expiresAt: number;
} | {
unanchored: true;
} | undefined;
export { resolveCheckpointStore } from "../checkpoint-store.js";
export { isFableFamilyModelId, resolveAttachmentsConfig, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./workspace-path.js";
/** Everything the run loop needs, built once by {@link prepareTask} (task setup, isolated from the loop). */
/**
* The SINGLE effective-delegation derivation for a leg, minted once per prepare
* and read by EVERY consumer face; two facets, one source:
*
* - `isDelegatedChild` — the raw delegation fact, forks included. Trusted internals first; on a
* resume leg where the caller supplied NO delegation fact, the checkpoint's persisted axis
* ({@link import("../checkpoint-store.js").CheckpointState.isDelegatedChild}) stands in — else a
* deps-only `resume(token, outcome, config)` of a parked delegation read `false` at every station:
* persona flip (subagent consent/notes pack dropped), ask refusal texts on the parent-thread arm
* ("wait for the user" in a transcript no user turn ever lands in), and a lifecycle-observer gate
* that never opened. Consumed by `askSourceIdentity` (a fork's refusal posture is the child's) and
* the `hookIdentity` mint.
* - `isNonForkChild` — the fact minus forks, for the authority/context faces: the `loadProjectMemory`
* ctx flag ({@link RunnerDeps.loadProjectMemory} `isSubagent`) and the prompt runtime fact
* `isSubagent` (the `SUBAGENT_CONSENT_NOTICE` gate). A FORK is excluded on both, deliberately:
* a fork IS the parent continuing, so it inherits the parent's authority — and, on the
* memory face, the parent's own project context; trimming a fork's CLAUDE.md would hand the
* continuation LESS context than the run it continues. (The memory face must not use
* `parentTaskId !== undefined` alone, which {@link RunInternals.isDelegatedChild}'s own docstring
* forbids — a directly-started workflow's children have no nameable parent.)
*
* `insideFork` is NOT persisted (the checkpoint's documented honest absence), so on a deps-only resume
* a FORK child reads as a plain delegated child on the non-fork facet — the honest residue is written
* on the checkpoint field's own list, not papered over with a new persistence axis.
*/
export declare function effectiveDelegationFacts(internals: Pick<RunInternals, "isDelegatedChild" | "insideFork"> | undefined, seedIsDelegatedChild: boolean | undefined): {
isDelegatedChild: boolean;
isNonForkChild: boolean;
};
/**
* Build everything a task run needs (a free function with EXPLICIT deps, not a
* Runner method — testable and decoupled). Resolves the model/role/thinking, acquires + reconciles the
* session (with the conflict-detecting Proxy + bounded re-wake on a lost CAS), assembles the tool set
* (caller tools + offload wrap + report_blocked + submit_output + MCP + memory + read_tool_result),
* builds the system prompt, constructs the harness, wires the tool-policy gate + context-edit/guard
* hooks + external-signal cancellation, and sets up the cache-break detector. **No run-loop control
* flow** lives here — that stays in the Runner loop.
*/
export declare function prepareTask(spec: TaskSpec, deps: RunnerDeps, sessions: SessionStore, resume?: PrepareResume, internals?: RunInternals,
/** A TRUSTED self-reference to the Runner, passed by the Runner itself (never a
* TaskSpec field) so the `run_workflow` tool can execute child tasks via `runner.runTask`. Undefined when
* prepareTask is exercised standalone (then run_workflow is simply not mounted). */
runnerSelf?: RunnerSelfSeat,
/** Carrier for the id minted below, so a prepare that THROWS still lets its caller name the
* run (prepare emits run-scoped notices of its own; each needs a terminal that names it). Same seat
* shape the Runner uses for the generated taskId/sessionId. */
runIdSink?: {
runId?: string;
},
/** The Runner's lock on this leg's session, handed only when the spec named none: taken the moment the store mints the id. */
holdSessionLock?: (sessionId: string) => Promise<void>): Promise<Prepared>;
/** The entry IS the contract the notification lane's seat names ({@link PrepareTaskFn}, contracts.ts):
* a signature change reds HERE, at the declaration, as `false` failing the `true` bound. IDENTITY, not assignability:
* the parameter tuple (arity, optionality, each type) and the return type must be the SAME type in both directions —
* one-way `extends` let a dropped trailing parameter, an added optional one or a narrowed return slip through. Type-level only —
* nothing is emitted. */
type Holds<T extends true> = T;
type Same<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
export type PrepareTaskIsTheContract = Holds<Same<Parameters<typeof prepareTask>, Parameters<PrepareTaskFn>>> & Holds<Same<ReturnType<typeof prepareTask>, ReturnType<PrepareTaskFn>>> & Holds<Same<ThisParameterType<typeof prepareTask>, ThisParameterType<PrepareTaskFn>>> & Holds<Same<typeof prepareTask, PrepareTaskFn>>;