@sema-agent/core
Version:
Stateless, task-oriented AI agent core
226 lines (225 loc) • 16.6 kB
TypeScript
/**
* PrepareTask's CAPS-AND-WORKFLOW phase, verbatim from the driver: the caller-tool roster
* map (the defineTool-product branch and the rich-ctx wrap through the large-result wrapper), the ReportBlocked /
* ReportFindings / plan-pair mounts, the per-principal runtime caps resolve with its fail-closed degrade and value
* screen, the compliance posture veto with its two refusal codes, the fork verdict and the observers grant, the
* auto-mode arming (classifier model, lane rule, decider + denial tracker + persisted recipe) with its manifest
* reason, the self-orchestration gate with its governance note, and the Workflow mount with its size-guideline
* seat. The three arming helpers and the parent-capture-state join moved with the segment (only this phase calls
* them). The interface is the dependency list the segment had implicitly.
*
* ASYNC PHASE: the segment awaits the two resolvers, the classifier route pre-flight and the Workflow factory,
* exactly where the driver awaited them; the roster map and the synthetic mounts before the first of those awaits
* run synchronously as they did. Read-stability of the host handles for the call: {@link RunInternals}
* (`@contract prepare.deps-read-stable`).
*
* THE ROSTER IS MINTED HERE (`tools`): every later phase that mounts pushes into this same array — the writer table
* lives on {@link Prepared.tools}. LATE-BOUND SEATS: the Workflow mount's call-time getters read
* three facts later stations resolve (the memory session, the center adoption, the hands mount's deny additions);
* each enters as a getter on the Input and is read exactly where the driver's getter read the variable.
*/
import type { AgentHarness, AgentTool, ThinkingLevel } from "../../internal/harness.js";
import type { Model } from "../../internal/llm.js";
import { forkGovernanceDenial } from "../../agents/subagent.js";
import type { PromptArtifact } from "../../prompt-assembly/artifact.js";
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
import { type AutoModeArmingRecipe } from "../auto-mode-arming.js";
import { type PermissionModeController } from "../mode-policy.js";
import { type AutoModeDecider, type AutoModeDenialTracker } from "../auto-mode.js";
import { type CheckpointSeat } from "../checkpoint-seat.js";
import { type ComplianceCapability } from "../compliance.js";
import type { LockedPreflight } from "../locked-config.js";
import type { StoredSession } from "../session.js";
import type { RunnerDeps, RuntimeCaps, TaskEvent, ToolExecuteContext } from "../types.js";
import type { AutoModeArmFact } from "../wiring-manifest.js";
import type { AssertAllKeysHandled } from "../ask-origin.js";
import { type ChildClampSource } from "../child-clamp-seats.js";
import type { InheritedGate, Prepared, RunInternals, RunnerSelfSeat, ToolFaceSnapshot } from "./contracts.js";
import { type BlockedRef } from "./synthetic-tools.js";
import { RosterBuilder } from "../tool-roster.js";
import type { ParentSpecSeat } from "../parent-spec-seat.js";
import { type RosterSpec } from "./caller-roster.js";
export interface PrepareCapsAndWorkflowInput {
/** borrowed-readonly — the REBOUND spec, whole: the roster map reads `tools`; the mounts read `enableBlockedReport`,
* `enablePlanMode`, `interactiveTools`; the caps/compliance stations read `principal`, `selfOrchestration`,
* `enableFork`, `getApiKeyAndHeaders`; the Workflow mount forwards `handsReadOnly`, `interactiveTools`. The
* self-orchestration predicate takes the whole spec. Never mutated. Typed as a spec whose roster is snapshot faces. */
spec: RosterSpec;
/** borrowed-readonly — the deployment seats, whole: `runtimeCapsResolver`, `compliancePostureResolver`, `onError`, `onNotice`,
* `autoMode`, `brain`, `models`, and the Workflow mount's dozen deps (`workflowScriptRunner`,
* `workflowGovernanceBaseline`, `workflowRunStore`, `workflowJournalStore`, `workflowScriptStore`,
* `onWorkflowAgentSpawn`, `workflowCompletionNotifier`, `workflowLimits`, `agents`, `builtinAgents`, `onBackgroundChildEvent`).
* The self-orchestration predicate takes the whole record. */
deps: RunnerDeps;
/** borrowed-readonly — the seat this run hands its children (`checkpointSeatOf`, minted once by the driver right after the
* config doors): the plan-pair face reads the store it names, the Workflow mount hands the seat itself to its children. */
checkpointSeat: CheckpointSeat | undefined;
/** borrowed-readonly — the trusted spawn-side channel; the Workflow mount forwards `rootSessionId`, `placementRoot`,
* `onTaskNotification`, `workflowDepth`, the capture-floor getter reads `memoryCaptureAncestors`, and the arming reads
* the chain's auto-mode face (`inheritedGate.autoModeFace`). */
internals: Pick<RunInternals, "rootSessionId" | "placementRoot" | "onTaskNotification" | "workflowDepth" | "memoryCaptureAncestors" | "workflowParkedResume" | "questionFaceStripped" | "inheritedGate"> | undefined;
/** borrowed-readonly — the acquired session; the classifier leg rebuilds its transcript window from `buildContext`. */
session: Pick<StoredSession, "buildContext">;
/** borrowed-readonly — the acquired session id (operator-line context, the refusal codes' phase record). */
sessionId: string;
/** borrowed-readonly — the run's invocation id (the classifier route pre-flight's notice names it). */
runId: string;
/** borrowed-readonly — the per-task identity the Workflow mount registers its children under. */
hostTaskId: string;
/** borrowed-readonly — the registry scope the Workflow mount registers in. */
taskScope: string;
/** borrowed-readonly — the SETTLED task root the Workflow mount hands its children as `parentCwd`. */
taskRootFinal: string;
/** borrowed-readonly — the run's resolved model: the classifier leg's fallback and the route pre-flight's primary. */
model: Model;
/** borrowed-readonly — the run's resolved thinking level, when one is (the Workflow mount's parentThinking fallback). */
thinking: ThinkingLevel | undefined;
/** borrowed-readonly — the frozen preflight; only `mcp` (the compliance veto's mcp_servers conflict check). */
lockedPreflight: Pick<LockedPreflight, "mcp">;
/** borrowed-mutable — the effect collection (`Prepared.toolEffects`). Writer here: ONE set, `Workflow → write`, at its mount.
* Every other writer is the safety scan (the seed) and the later mounts (see the roster table on `Prepared.tools`). */
toolEffects: Prepared["toolEffects"];
/** borrowed-readonly — the frozen tool-face snapshot the Workflow mount forwards (exclude / defer / alwaysLoad / restoreGated). */
toolFaceSnapshot: ToolFaceSnapshot;
/** borrowed-readonly — the resolved prompt profile (the Workflow mount's parentPromptProfile). */
promptProfile: ToolExecuteContext["promptProfile"];
/** borrowed-readonly — the resolved interaction posture (the Workflow mount's parentInteractionPosture; absent ⇒ no key). */
resolvedInteractionPosture: ToolExecuteContext["interactionPosture"];
/** borrowed-readonly — the harness seat; the Workflow mount's parentModel / parentThinking getters read it call-time. */
harnessRef: {
current?: AgentHarness;
};
/** borrowed-readonly — the run's approval seat as the config doors froze it (the Workflow mount's parentOnAsk). */
askSeat: import("../wiring-manifest.js").AskSeat;
/** borrowed-readonly — the parent SPEC seat the children inherit (minted once by the inherited-gate phase; the Workflow mount's dep, the same value the delegation ctx carries). */ parentSpecSeat: ParentSpecSeat;
/** borrowed-readonly — the run's clamp source (minted once by the inherited-gate phase; the Workflow mount's `parentClamps`, the same source the delegation ctx carries). */
runClampSource: () => ChildClampSource;
/** borrowed-readonly — the filtered display-forwarding wrapper (the Workflow mount's forwardEvent; absent ⇒ no key). */
forwardEvent: ((e: TaskEvent) => void) | undefined;
/** borrowed-readonly — the child-chain assembly (the Workflow mount's inheritedGateForChildren). */
inheritedGateForChildren: () => InheritedGate;
/** borrowed-readonly — the caller-tool ctx enrichment the roster map wraps every raw ToolSpec with. */
enrichSpecToolCtx: (ctx: ToolExecuteContext) => ToolExecuteContext;
/** borrowed-readonly — the large-result wrapper every roster entry passes through (per-tool policy honored inside). */
maybeOffload: (tool: AgentTool, perTool?: {
offload?: boolean;
offloadThresholdChars?: number;
}) => AgentTool;
/** borrowed-readonly — the plan-review primitive the ExitPlanMode tool calls. */
requestReview: (opts?: {
reason?: string;
}) => void;
/** borrowed-readonly — the plan-mode primitive the EnterPlanMode tool calls: a steer of the leg's mode seat into `plan`. */
enterPlanMode: () => Promise<void>;
/** borrowed-readonly — the leg's live permission-mode seat: the arming's first arm is `declared === "auto"`, and the
* arming facts (the deny bits, the face, the re-entrant arm) are bound onto it here. */
permissionMode: PermissionModeController;
/** borrowed-readonly — LATE-BOUND: the effective read-deny table the hands phase compiles (the rows the classifier's deny
* paragraph renders); read at ARMING, which the driver runs after that phase — never at this phase's own call — once the
* run's newest tables are compiled (an arming is no call). */
readDenyEntries: () => Promise<{
readonly entries: ReadonlyArray<{
readonly pattern: string;
readonly caseSensitive?: boolean;
}>;
readonly ruleRows: ReadonlyArray<{
readonly tool: string;
readonly pattern: string;
}>;
}>;
/** borrowed-readonly — the peer lane's mount verdict (the classifier's cross-session rule fills only while mounted). */
peerLaneActive: boolean;
/** borrowed-readonly — the leg's effective delegation fact (`effectiveDelegationFacts`): the classifier window shows its user lane as the launching agent's. */
delegated: boolean;
/** borrowed-mutable — `Prepared.sideCallBookRef`: written by the run body (never here); the classifier books each of its replies through it. */
sideCallBookRef: Prepared["sideCallBookRef"];
/** borrowed-readonly — whether the BUILT-IN SendMessage is on this face (the lane rule's second term). */
peerSendMessageBuiltIn: boolean;
/** borrowed-readonly — the trusted Runner self-reference the Workflow mount executes children through; undefined when
* prepareTask runs standalone (then Workflow is simply not mounted). */
runnerSelf: RunnerSelfSeat | undefined;
/** borrowed-readonly — LATE-BOUND: the memory-engine session; the Workflow mount's capture-floor getter reads it at ITS call time. */
memoryEngineSession: () => Prepared["memoryEngineSession"];
/** borrowed-readonly — LATE-BOUND: the adopted center artifact; the Workflow mount's two center getters read it at THEIR call time. */
centerAdoption: () => {
artifact: PromptArtifact;
sourceRevision?: string;
} | undefined;
}
export interface PrepareCapsAndWorkflowResult {
/** owned — THE run's roster BUILDER (the mount array is private to it; `Prepared.tools` is its read face,
* whose JSDoc carries the writer table). Minted here from the caller's tools; every later mount goes through its verbs.
* Writers here: the caller mounts, then the ReportBlocked, ReportFindings, ExitPlanMode, EnterPlanMode and Workflow mounts. */
roster: RosterBuilder;
/** borrowed-mutable — the ReportBlocked tool's write seat (`Prepared.blockedRef`). Writer: the tool at run time; read at
* result assembly. */
blockedRef: BlockedRef;
/** owned — the per-principal entitlements as resolved (or the fail-closed degrade), undefined = no restriction. */
runtimeCaps: RuntimeCaps | undefined;
/** owned — whether the caps resolve FAULTED (throw or value screen) — never read off a coined explicit value. */
runtimeCapsFaulted: boolean;
/** owned — the compliance deny set (every capability under a resolver fault). */
complianceDenies: ReadonlySet<ComplianceCapability>;
/** owned — whether the compliance resolve faulted (the refusal code distinguishes it). */
complianceDegraded: boolean;
/** owned — the fork-governance verdict (present only when denied; the ctx reads it call-time). */
agentForkDenial: ReturnType<typeof forkGovernanceDenial>;
/** owned — the observers grant (`allowObservers === true`, explicit opt-in). */
observersActive: boolean;
/** owned — the arming outcome as a closed reason (the manifest's auto-mode face), read LIVE: a leg steered into `auto`
* after prepare arms then, and the fact follows. */
autoModeArmReason: () => AutoModeArmFact;
/** owned — the armed classifier's live seat (`current` undefined until an arming succeeds — at prepare for an `auto`
* leg, or at a steer into `auto`); every consumer reads `current` at its own call. */
autoModeDeciderRef: {
readonly current: AutoModeDecider | undefined;
};
/** owned — the denial tracker built beside the decider (same face, same arming), same live seat. */
autoModeDenialTrackingRef: {
readonly current: AutoModeDenialTracker | undefined;
};
/** owned — the serializable arming recipe (persistArming opt-in only), same live seat. */
autoModeArmingRef: {
readonly current: AutoModeArmingRecipe | undefined;
};
/** owned — arm the classifier for THIS leg (idempotent; a no-op unless the effective mode is `auto` and the face and the
* deny bit admit it). The driver calls it once after the hands phase (the deny table must exist first); the mode seat
* calls it again on a steer into `auto`. */
armAutoMode: () => Promise<void>;
/** owned — self-orchestration is ACTIVE for this leg (deployment ready ∧ not entitlement-denied); drives the prompt injection. */
selfOrchestrationActive: boolean;
/** owned — the Workflow tool is on the roster (the delegation surface's second door). */
workflowToolsActive: boolean;
/** owned — the mid-session size-guideline seat, armed with the Workflow mount (`Prepared.workflowSizeGuideline`). */
workflowSizeGuideline: {
legGuideline: WorkflowSizeGuideline;
current: () => WorkflowSizeGuideline;
} | undefined;
}
/**
* The resolved-caps value screen (the caps-side twin of `refuseInvalidAutoModeSeat`): the arming arm reads
* `autoMode !== false`, so a resolver that returns a MARSHALLED value — a non-boolean member (`"false"`,
* `0`, `null`) or a non-record container (`false`, `"false"`, `[]`; the resolver is an HTTP-backed seam the
* type system does not validate at runtime) — would pass a value meant as a denial straight into arming.
* Either shape is treated as a disclosed resolver fault: announced through onError (phase config), and
* coined fail-closed — a bad MEMBER to `autoMode:false` (the rest of the record kept), a bad CONTAINER to
* the same degrade object the resolver-throw arm uses (every `!== false`-gated cap denied), with
* `faulted` raised so the posture-keyed consumers read it as an outage, not as a verdict. `undefined`, or
* a record whose `autoMode` is `undefined`/`true`/`false`, passes untouched (byte-identical).
*/
/** The supported capability keys, fenced total over {@link RuntimeCaps}: a key added to the type without a row here
* is a red build, so the snapshot below can never silently drop a new restriction. */
declare const RUNTIME_CAP_KEYS: {
readonly allowWorkflows: true;
readonly allowFork: true;
readonly allowObservers: true;
readonly forceDurableGate: true;
readonly autoMode: true;
readonly autoModeDenySource: true;
readonly bypassPermissions: true;
readonly allowMemoryOptOut: true;
};
export type RuntimeCapKeysCoverEveryCap = AssertAllKeysHandled<Exclude<keyof RuntimeCaps, keyof typeof RUNTIME_CAP_KEYS>>;
/** The M4 phase body — prepareTask's caps-and-workflow stretch, verbatim (see the module header). */
export declare function prepareCapsAndWorkflow(input: PrepareCapsAndWorkflowInput): Promise<PrepareCapsAndWorkflowResult>;
export {};