@sema-agent/core
Version:
Stateless, task-oriented AI agent core
86 lines (85 loc) • 5.82 kB
TypeScript
import type { PromptPack } from "./types.js";
/** the provider-declared sections of THIS run's resolved prompt (the `origin:"provider"`
* subset of the compiled IR), the declaration axis of the artifact identity.
*
* (S1 round 2): declaration TEXT deliberately does NOT enter this shape — the epoch
* digest is a deterministic, unsalted value persisted in exportable session entries, and session
* readers do not see system prompts; hashing private text into it would be a dictionary-preimage
* surface (the exact class the salted prompt-manifest hashes exist to prevent). Identity instead
* rides `(id, slot, chars)` plus the declarer's OPT-IN `contentHash` (the digest-tiering value,
* already public by declaration): with contentHash — byte-precise re-pin detection (the center
* publish path always mints it); without — length-level detection only, documented degradation
* for private/forwarded text. */
export type EpochDeclaredSections = ReadonlyArray<{
id: string;
slot: string;
chars: number;
contentHash?: string;
}>;
/** How an epoch became active (protocol §9.2 descriptor field; `clear` arrives with a future
* first-class context_reset — v1 clear = new session = `session_start`). */
export type PromptEpochActivation = "session_start" | "compaction" | "legacy_migration";
/** The durable pin, persisted as a first-class session entry (root) and restated in every
* compaction entry's `details.promptEpoch` (same-CAS atomicity; bounded-tail recoverable). */
export interface PromptEpochDescriptor {
/** Monotonic per-session epoch counter (session_start = 1). */
epoch: number;
artifactDigest: string;
packId: string;
assemblyApi: number;
activatedBy: PromptEpochActivation;
/** design/148 S1 (additive provenance, present only on center-adopted pins): the VERIFIED center
* artifact this session composes from. `artifactDigest` above stays the computed
* bundled+declarations identity (the existing re-pin machinery); this field is what the resume
* path resolves through the artifact store — a miss is fail-loud (`prompt.snapshot_unavailable`),
* never a silent hybrid recompose (protocol §9.2). */
centerArtifactDigest?: string;
/** Opaque publish provenance of the adopted envelope (audit only — never enters any digest). */
sourceRevision?: string;
}
/** Deterministic canonical JSON (sorted keys) — shared identity encoding for the bundled-pack
* digest AND the center artifact digests (design/148 S1: one canonicalization, never two). */
export declare function canonical(node: unknown): string;
/**
* Bundled-pack artifact digest: canonical hash over the pack's structural declaration PLUS each
* section's probe-rendered content under the probe-fact vectors — all-off, all-on, and one
* single-axis-flipped vector per axis (content- and admission-binding, incl. single-gate mixed
* states). TOTALITY over the axes the pack forks on is the load-bearing property (see
* PROBE_FACTS_OFF/PROBE_FACTS_ON); it is pinned behaviourally by
* test/hardening-prompt-assembly.test.ts. Deterministic across processes and object insertion order;
* changes to any section's prose, admission behavior, or structure change the digest. Build version
* deliberately EXCLUDED (F3): a release that doesn't touch the pack keeps the same identity.
*/
export declare function computeBundledArtifactDigest(pack: PromptPack, declared?: EpochDeclaredSections): string;
/** Mint the descriptor for a NEW session on the current bundled artifact.
* Subagent/fork note (RB-16 ruling, 2026-07-17): children mint their OWN session_start pin rather
* than inheriting the parent's descriptor — under v1 bundled-only resolution both produce the
* identical pin (same process, same pack, same digest), so the protocol's "explicit inheritance
* via RunInternals" is deferred to the multi-artifact era, where a parent pinned to a non-latest
* artifact genuinely diverges from a fresh mint (lands with the center candidate machinery). */
export declare function mintSessionStartEpoch(pack: PromptPack, declared?: EpochDeclaredSections): PromptEpochDescriptor;
/** Epoch selection at a compaction boundary (protocol §9.3 order steps 1-4, v1 bundled form):
* the "latest validated candidate" IS the current bundled artifact. Same digest ⇒ restate the
* current epoch (no advance); different digest (engine upgraded mid-session) ⇒ advance. */
export declare function selectCompactionEpoch(current: PromptEpochDescriptor | undefined, pack: PromptPack, declared?: EpochDeclaredSections,
/** RB-31 (design/148 S2) — a compaction-boundary CENTER ADOPTION: the new candidate's provenance
* rides the advanced descriptor (the caller pre-assembled with the candidate's declarations, so
* `declared` already reflects the new closure — the digest moves, the epoch advances). */
centerAdoption?: {
centerArtifactDigest: string;
sourceRevision?: string;
} | {
clear: true;
}): PromptEpochDescriptor;
/**
* Resolve the session's pinned epoch against the CURRENT bundled artifact (v1 resolution).
* Returns the effective descriptor plus whether a legacy migration happened (pin from another
* build, or a pre-epoch session with no pin at all) — the caller records the migration entry.
*/
export declare function resolveEpochAgainstBundled(pinned: PromptEpochDescriptor | undefined, pack: PromptPack, declared?: EpochDeclaredSections): {
descriptor: PromptEpochDescriptor;
migrated: boolean;
};
/** Shape-normalize an untrusted candidate (session import face — RB-7: import-validate is a
* consumer). Returns undefined for anything that isn't a plausible descriptor. */
export declare function normalizePromptEpoch(raw: unknown): PromptEpochDescriptor | undefined;