UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

107 lines (106 loc) • 7.27 kB
/** * The per-call hook stations a tool call passes OUTSIDE the tool gate. ONE rule for every executed call, live or * approved on a parked checkpoint: before it runs, the PreToolUse screening seat; after it runs, the `tool_result` * station (the PostToolUse / PostToolUseFailure seats and the PreToolUse context drain). * · a LIVE call meets the screening inside the gate and the station through the loop's finalize (the gate-stations * phase registers the station on the harness); * · an APPROVED call meets {@link createApprovedCallScreen} — the SAME seat (`createPreToolUseScreening`), folded under * the decide that approved it — and the station through `AgentHarness.finalizeToolCall`, the loop's own finalize. */ import type { DocumentContent, ImageContent, TextContent } from "../../internal/llm.js"; import { type HookEnvCapabilities, type HookInvocationIdentity, type Hooks } from "../hooks.js"; import type { PermissionModeController } from "../mode-policy.js"; import type { TaskSpec } from "../types.js"; import type { CwdRef } from "../../tools/fs/fs-shared.js"; import { type ToolRosterDeltaSeat } from "../tool-roster.js"; import { type FrameSettlement } from "../frame-settlement.js"; import type { Prepared } from "./contracts.js"; import type { Checkpoint } from "../checkpoint-store.js"; /** One executed call's result as the station reads it (the harness `tool_result` event's members). */ export interface ToolResultSeatEvent { toolCallId: string; toolName: string; input: Record<string, unknown>; content: Array<TextContent | ImageContent | DocumentContent>; details: unknown; isError: boolean; } /** The station: the patch the loop applies to the result (`undefined` = unchanged). */ export type ToolResultStation = (e: ToolResultSeatEvent) => Promise<{ content: Array<TextContent | ImageContent | DocumentContent>; } | undefined>; export interface ToolResultStationInput { /** borrowed-readonly — the resolved hook record: the two post seats and the failure seat, called through the record. */ hooks: Hooks | undefined; /** borrowed-mutable — the blocked/suspended call ids. Writer here: the one-shot `delete`. */ blockedToolCalls: Set<string>; /** borrowed-readonly — the leg's delta seat: the post seats receive the call's wire name off its current roster. */ toolRosterDeltas: ToolRosterDeltaSeat; /** borrowed-mutable — PreToolUse `additionalContext` per call id. Writer here: the one-shot drain (`delete`). */ preToolContexts: Map<string, string[]>; /** borrowed-readonly — the validated per-prepare hook seat bound. */ hookTimeoutMs: number; /** borrowed-readonly — prepare's abort seat; `.signal` bounds the post seats and is the failure seat's interrupt fact. */ abortController: { readonly signal: AbortSignal; }; /** borrowed-readonly — the REBOUND spec's `signal` (the failure seat's interrupt fact). */ spec: Pick<TaskSpec, "signal">; /** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read per post seat. */ handsCwdRef: CwdRef | undefined; /** borrowed-readonly — the env face the post seats' contexts carry, or undefined. */ hookEnvFace: HookEnvCapabilities | undefined; /** borrowed-readonly — the leg's identity envelope every HookToolContext carries. */ hookIdentity: HookInvocationIdentity; /** borrowed-readonly — the leg's live permission-mode seat (each context reads `observed`). */ permissionMode: PermissionModeController; /** borrowed-readonly — where an expired post seat is reported (the leg's own hook-crash lane). */ notifyOwnHookCrash: (err: unknown) => void; /** borrowed-readonly — the leg's reminder provenance mark (every hook context line carries it). */ reminderMark: string; /** borrowed-mutable — the leg's settlement seam. Writer here: a carrier whose output a host hook rewrote (`noteHostRewrite`). */ frameSettlement: FrameSettlement; } /** The `tool_result` station, or undefined when no post / screening hook is wired (nothing to register). */ export declare function createToolResultStation(input: ToolResultStationInput): ToolResultStation | undefined; /** What the resumed leg does with the approved call: refuse it with a sentence (it is NOT executed), or run these bytes. * `admit` stashes the screen's context for the result's drain — called only once the call is past every belt and about * to run (a refused call stashes nothing, the live gate's rule). */ export type ApprovedCallScreen = NonNullable<Prepared["approvedCallScreen"]>; export type ApprovedCallVerdict = Awaited<ReturnType<ApprovedCallScreen>>; export interface ApprovedCallScreenInput { /** borrowed-readonly — the face the gate screens with (the observational wrapper when declared; the leg's mode on every context). */ preToolUse: Hooks["preToolUse"] | undefined; /** borrowed-readonly — the validated per-prepare hook seat bound. */ hookTimeoutMs: number; /** borrowed-readonly — prepare's abort seat (the screening seat is abort-ended on it). */ abortController: { readonly signal: AbortSignal; }; /** borrowed-readonly — the hand's live tracked cwd, or undefined. */ handsCwdRef: CwdRef | undefined; /** borrowed-readonly — the read-only env capability face, or undefined. */ hookEnvFace: HookEnvCapabilities | undefined; /** borrowed-readonly — the leg's identity envelope. */ hookIdentity: HookInvocationIdentity; /** borrowed-readonly — the own hook-crash reporter (a crashing or timed-out screening face is reported through it). */ notifyOwnHookCrash: (err: unknown) => void; /** borrowed-mutable — PreToolUse `additionalContext` per call id. Writer here: `admit`'s stash (the station drains it). */ preToolContexts: Map<string, string[]>; } /** * The approved call's PreToolUse screening — the gate's own seat, its answer FOLDED UNDER THE DECIDE that approved the * call (the decide is the upper bound; the screen can only narrow): a deny, a crash, a silence or a refused answer * refuses the call in the live gate's own sentences; an ask is the question the decide already answered — unless it * demands a person and the decide was the deployment's policy (the one predicate every resume station holds a decide * against); a rewrite is what executes when the decide carried none of its own (the person's bytes are the upper bound); * context rides the result. Undefined when no face is wired. */ export declare function createApprovedCallScreen(input: ApprovedCallScreenInput): ApprovedCallScreen | undefined; /** * Why the frozen inherited chain cannot judge bytes no gate judged (an approver's edit, the re-screen's rewrite): the * row predates the chain, a layer is opaque, or a layer declares a live remainder — undefined when the frozen * projections can judge them, or when no parent constraint binds. ONE rule for the pre-claim edit rung (which refuses * the edit, the row pending) and the resumed leg's belt (which refuses a rewrite, the approval spent). */ export declare function chainCannotJudge(ig: Checkpoint["state"]["inheritedGate"]): string | undefined;