UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

200 lines (199 loc) • 12.3 kB
import type { AgentMessage, ExecutionEnv, GitAnnouncementKind, SessionTreeEntry } from "../../internal/harness-types.js"; /** Bound into the hash domain: bump when the frame's wording/structure changes so the upgraded * renderer re-announces on its first leg instead of being suppressed by a pre-upgrade hash. */ export declare const GIT_STATUS_FRAME_FORMAT_VERSION = 1; /** * The frame's own first paragraph (replaces the CC "will not update during the conversation" head, * which would be FALSE under the frame protocol). Wording obligations: self-declares the update * semantics (latest frame supersedes earlier ones), promises re-send on VISIBLE-view change only * (truncation-bound honesty), and "observed while preparing" does not claim an atomic instant (the * probe is two shell round-trips and a write can land between them). */ export declare const GIT_STATUS_FRAME_PREAMBLE = "This is the git status observed while preparing this request. When the visible snapshot changes it will be re-sent in a later message \u2014 the most recent git status frame supersedes earlier ones."; /** Tombstone body — the probes could not reach git at all while earlier frames announced a * positive view (sent once, on the availability flip edge only). */ export declare const GIT_STATUS_UNAVAILABLE_BODY = "Git status is currently unavailable; the most recent git status frame above may be stale."; /** Tombstone body — the directory stopped being a git repository while earlier frames announced a * positive view (sent once, on the flip edge; the env block's static is-repo line flips the same leg). */ export declare const GIT_STATUS_NON_REPO_BODY = "The working directory is no longer a git repository; earlier git status frames no longer apply."; /** The `steering_injected` echo previews for the frame — CONSTANT wordings on purpose: branch and * status text are repo-controlled and must not enter the event telemetry plane through the echo. */ export declare const GIT_STATUS_ECHO_PREVIEW: Record<GitAnnouncementKind, string>; /** The raw material of one probe cycle, as prepare-task resolved it (§4.3 unified ladder): * - `full` — §E14 and the H4 snapshot both succeeded: `snapshot` = the pre-rendered * {@link import("../../prompts/default.js").buildGitSnapshot} block (CC template, * sanitized + bounded by its renderer); * - `degraded` — §E14 yielded git facts but the H4 snapshot failed (exit 41/42, timeout, sentinel * mis-split): branch + dirtiness are all that is honestly known; * - `unavailable` — the §E14 probe itself failed (git facts unknowable this leg); * - `non-repo` — the probe ran and the cwd is not a git repository. */ export type GitStatusProbeOutcome = { kind: "full"; snapshot: string; } | { kind: "degraded"; branch?: string; dirty?: boolean; } | { kind: "unavailable"; } | { kind: "non-repo"; }; /** Render the frame BODY (the text inside the `<system-reminder>` shell) for one probe outcome. */ export declare function renderGitStatusFrameBody(outcome: GitStatusProbeOutcome): string; /** * Content hash of a rendered frame body — the hash half of the `(kind, hash)` comparison tuple. * Digest domain = format version + canonical repo root + kind + body, NUL-separated (none of the * inputs may contain NUL: the body is sanitized text, the root a canonical path). `canonicalRoot` * is the ExecutionEnv-canonicalized worktree root (or cwd outside a repo) so identical bytes from * two different checkouts never share an announcement. */ export declare function hashGitStatusFrame(kind: GitAnnouncementKind, body: string, canonicalRoot: string): string; /** One resolved frame — outcome rendered and hashed, ready for the (kind, hash) compare. */ export interface ResolvedGitStatusFrame { kind: GitAnnouncementKind; body: string; hash: string; /** Pre-rendered DEGRADED body + hash for the same probe cycle (present only when kind is * `full`): the one-shot deterministic shrink target of the irreducible-core over-budget arc — * computed at prepare time because the request-build path must not re-run probes. */ shrunk?: { body: string; hash: string; }; } /** Render + hash one probe outcome (and its deterministic degraded shrink target when full). */ export declare function resolveGitStatusFrame(outcome: GitStatusProbeOutcome, canonicalRoot: string, degradedMaterial?: { branch?: string; dirty?: boolean; }): ResolvedGitStatusFrame; /** * The run-local git-status lane state (one per run, on `Prepared`) — the coordination surface * between prepare (probe resolution), the run loop (delivery + receipt + compaction re-assertion) * and the request-build context handler (trim protection). Deliberately a plain mutable ref, the * `announcedListingsRef` discipline. */ export interface GitStatusLaneRef { /** This leg's resolved frame (undefined ⇒ hands-less leg: the lane is out of scope, announced * state untouched — the env block equally carried no git facts for such a leg). */ frame?: ResolvedGitStatusFrame; /** Canonical repo root the frame was hashed against. */ canonicalRoot?: string; /** The announced state as THIS run knows it (seeded from the read ladder at leg start, advanced * at each receipt) — mirrored onto the suspend checkpoint. `pending: true` ⇒ a re-announcement * is owed (frame append failed / compaction restated pending); the next boundary retries. */ announced?: { kind: GitAnnouncementKind; hash: string; entryId?: string; pending?: true; }; /** Exact WRAPPED text of the newest announced frame — the frame SEGMENT, on both carry forms * (this moved off "the whole first-message text"; the contract here had * kept the pre-r1 words). The context guard finds the carrier by CONTAINING this engine-held * string (engine-region gated), protects that carrier as a replace-by-key slot — only the newest * carrier is protected, older frames trim like ordinary history — and charges the irreducible * core by THIS segment, never by the whole carrier (the objective riding the same message keeps * its own overflow exit). */ protectedText?: string; /** One-shot latch of the irreducible-core arc's DISCLOSURE: the request view substituted the * degraded rendering at least once (the substitution itself is per-request deterministic; the * disclosure + pending re-announcement fire only on the first). */ overBudgetShrunk?: boolean; /** The exact WRAPPED frame segment of the protected carrier and its degraded substitution target * (both `<system-reminder>`-shelled, exactly as delivered) — the irreducible-core shrink is a * literal string replace of `find` with `replace` inside the carrier's text. Set at delivery * alongside {@link GitStatusLaneRef.protectedText}; present only for a full-kind frame. */ wrappedShrink?: { find: string; replace: string; }; /** Wired by the run loop once its queue exists: re-assert the current frame (compaction landing * + boundary retry both call this). */ reassert?: () => Promise<void>; /** F5 — the typed terminal's surface bridge: the loop converts a * context-hook throw into a failure MESSAGE (text only), so the thrown `code` never reaches * result assembly on its own. Set alongside the throw; the run loop lifts it into * `TaskResult.errorCode`. */ terminalCode?: "irreducible_core_over_budget"; /** F4 — the receipt's mirror write, PARKED instead of fired: a * fire-and-forget CAS append from inside the message_end walk can race the loop's own next * transcript append and fail the CRITICAL write with a conflict. The run loop flushes this at * serialization points only (turn boundary, prompt settle); an unflushed slot at suspend is * covered by the checkpoint rung (the frame IS on the branch — visibility check passes). */ mirrorOwed?: { kind: GitAnnouncementKind; hash: string; entryId: string; }; } /** * prepare-side probe half of the lane (H4, extracted from prepareTask): run the CC-shape snapshot * round-trip through the SAME ExecutionEnv seam when the * §E14 probe confirmed a repo, resolve the outcome through the §4.3 unified kind ladder, and hash * the rendered frame with the canonical repo root bound in. `envFacts` carries the §E14 results * (read-only here). Every degrade names its reason through `onDegrade` — including the sentinel * MIS-SPLIT arm (parts !== 4), which previously dropped the snapshot with ZERO telemetry (the * loud-bad-value fix). Returns the run's lane ref (empty on a hands-less leg). * * H4 anchor notes carried from the prepareTask body: four sections split by a sentinel line — * main-branch inference (CC qP symref → [inferred, main, master] each show-ref → renderer falls * back "main"), `git config user.name`, `git --no-optional-locks status --short`, and * `git --no-optional-locks log --oneline -n 5` (CC-exact commands). The two * REQUIRED sections fail the WHOLE script with distinct exit codes (41 = status, 42 = log) so a * failing status never half-renders as "(clean)"; both now DEGRADE the frame to the branch+dirty * residual instead of silently skipping. The main-branch/user sections stay best-effort. */ export declare function probeGitStatusLane(args: { executionEnv: Pick<ExecutionEnv, "exec" | "canonicalPath">; envFacts: { isGitRepo?: boolean; gitBranch?: string; gitDirty?: boolean; gitWorktreeRoot?: string; cwd?: string; }; handsEnabled: boolean; taskRoot: string; onDegrade: (reason: string) => void; }): Promise<GitStatusLaneRef>; /** * Request-build half of the trim protection (extracted from prepareTask's context handler under the * same D-7 ratchet): wraps {@link protectGitFrame} with the lane ref's state — the substitution * pair, the one-shot disclosure latch and the pending re-announcement on the first shrink — and * converts the over-budget verdict into the LOUD typed terminal (`irreducible_core_over_budget`), * never a pretended trim success. Returns the (possibly re-inserted / shrunk) request view. */ export declare function applyGitFrameGuard(args: { before: AgentMessage[]; trimmed: AgentMessage[]; budgetTokens: number; ref: GitStatusLaneRef; charsPerToken?: number; onDegrade: (message: string) => void; }): AgentMessage[]; /** * The transcript-side classifier of engine * git frames. The mirror plane is a CACHE of "which frame is newest"; the TRANSCRIPT is the truth, * and the two diverge exactly when a mirror write was lost. This scan finds the NEWEST engine git * frame on the branch, with TOP-LEVEL WRAPPED-UNIT matching: * - positive frame: the wrapped unit's head (open tag + newline + the frame preamble); * - tombstone: the exact wrapped unit (open tag + newline + tombstone body + newline + close tag); * - the occurrence must sit inside the message's ENGINE region (engineMinted / enginePrefixChars * / engineSegments — metadata gates, never user-text shape-guessing), AND at TOP LEVEL (r4-2: * start-of-text or right after a close tag — a hook relay that QUOTES the exact frame unit * nests it inside its own shell with prose before the open tag, and is rejected); * - within one message the LAST top-level unit wins (textual order = issue order). */ export declare function newestEngineGitFrame(branch: SessionTreeEntry[], mark?: string): { entryId: string; positive: boolean; } | undefined; export declare function stripGitStatusUnits(text: string, mark?: string): string; /** * The readable-absence tombstone question (R2-1), answered by the classifier above: does the * branch carry a still-context-visible POSITIVE engine frame as its newest git frame? A newest * TOMBSTONE means the disowning already happened (no re-spam); no frame at all means a genuinely * fresh lane (no tombstone out of nowhere). */ export declare function branchCarriesVisiblePositiveGitFrame(branch: SessionTreeEntry[], mark?: string): boolean;