@sema-agent/core
Version:
Stateless, task-oriented AI agent core
54 lines (53 loc) • 4.17 kB
TypeScript
import { traceHookCrash } from "./gate-crash-trace.js";
import type { WriteProtectionVerdict } from "./write-protect.js";
/**
* design/77 §4: resolve `p`, but REJECT (so the caller catches and fails closed) if it hasn't settled within
* `ms` OR the `signal` aborts first (the task timed out / was cancelled — its real deadline). A rejection of
* `p` propagates with its true cause. Both `ms` and `signal` undefined ⇒ no deadline (await `p`) — the
* helper stays generic; every gate call site supplies a finite `ms` ({@link createGateDeadline}), so the
* no-deadline arm is unreachable from the gate. Mirrors `tool-policy.ts withTimeout`, but REJECTS on
* timeout (no bounded await here has a safe fallback VALUE — the gate's only safe action on an unsettled
* one is its own fail-closed arm). `label` names the wait in the rejection, so the arm that catches it can
* say which one elapsed.
*/
/** WHY a bounded wait ended with no answer, stated by the WAIT rather than read off an error's text:
* `timeout` — the bound elapsed; `aborted` — the task ended. Neither mark ⇒ the awaited work's own. */
export type GateWaitFailure = "timeout" | "aborted";
/** The mark above, read back off a rejection. `undefined` ⇒ the awaited work's own rejection. */
export declare function gateWaitFailureOf(err: unknown): GateWaitFailure | undefined;
export declare function withGateTimeout<T>(p: Promise<T>, ms: number | undefined, signal?: AbortSignal, label?: string): Promise<T>;
/**
* The deadline used when the caller supplies no `approvalTimeoutMs`. The wait must be bounded EVERYWHERE:
* the engine's own assembly never passes `approvalTimeoutMs`, and its only other bound is the task abort
* signal, which a task with no walltime limit and no external cancel never fires — so a host driving the
* public `runToolGate` with neither knob had NO bound at all and one hung probe froze the call forever. A
* probe is an args inspection and a judgment is a path resolution, not an approval wait: 30s is generous
* for any legitimate one, and an elapse simply takes the caller's own fail-closed arm.
*/
export declare const DEFAULT_GATE_AWAIT_TIMEOUT_MS = 30000;
/**
* The gate's ONE deadline seat, resolved LAZILY and at most once per gate pass: a pass that makes no
* bounded await must not sound about a seat it never read. A garbage value (NaN/Infinity/negative, or
* above setTimeout's 2^31-1 ceiling, which it would silently clamp to ~1ms — an instant fail-closed the
* deployment never asked for) is refused LOUDLY to the default rather than silently reinterpreted
* (bad-value loudness discipline; the same ceiling the approve-wait seat refuses).
*/
export declare function createGateDeadline(input: {
approvalTimeoutMs?: number;
}, notifier: Parameters<typeof traceHookCrash>[2]): () => number;
/**
* The write-protection judge's wait, at both of its call sites — and the ONE minter of the `unresolvable`
* verdict. An elapsed or aborted judgment is not "a target the environment could not resolve" (the judge
* answers that one itself, by dropping the view that could not contribute and letting the lexical half
* stand); it is NO judgment at all — not even the lexical half's answer came back — and there is nothing
* for the fold or the approved-edit arm to act on but their own fail-closed arm. A wait that produced
* nothing knows nothing about what it was waiting for, which is why `unresolvedSymlink` is `false` here
* and not a guess.
*
* KNOWN, AND NARROWER THAN IT LOOKS: the lexical half is computed synchronously at the top of `judge`, so
* a spelling HIT is already on a settled promise before this wait begins and cannot elapse; what this arm
* refuses is a spelling MISS whose environment half never came back. Closing even that would mean handing
* the deadline INTO the judge so the missing half could be dropped there too — a seat change on a public
* type, deliberately not taken here.
*/
export declare function awaitWriteProtectionJudgment(judged: Promise<WriteProtectionVerdict>, ms: number, signal?: AbortSignal, onFailure?: (err: unknown) => void): Promise<WriteProtectionVerdict>;