UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

127 lines (126 loc) • 9.64 kB
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>>;