UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

145 lines (144 loc) • 9.49 kB
/** The model-face envelope tag (single source; a shell renders/collapses by this name). */ export declare const CROSS_SESSION_MESSAGE_TAG = "cross-session-message"; /** * The ONE model-face neutralization every PEER-AUTHORED body receives before a framing carrier wraps * it — the same-process `<agent-message>` uplink, the `<cross-session-message>` envelope and the * `<teammate-message>` frame all call exactly this, then their own {@link escapeEnvelopeTag}. * * WHAT it defuses: the whole harness AUTHORITY family ({@link ENGINE_AUTHORITY_ENVELOPE_TAGS} — * `system-reminder`, `task-notification`, `user_memory`, `skills`, …), not the reminder tag alone. A * peer's text is another model's output; a copy of any engine envelope inside it must arrive as * data, never as a frame the recipient's model reads as engine speech. The primitive underneath * ({@link sanitizeUntrustedText}) deliberately defuses only the reminder tag by default, because it * also runs over engine-assembled bodies that legitimately ARE envelopes; a peer body never is, so * the family-wide set is correct here and belongs to this seat — one helper, so the three carriers * cannot drift apart on which tags they contain. * * Idempotent (a defused tag no longer matches), which is what keeps the model-face parser's * rebuild-and-compare a fixed point on an already-canonical body. */ export declare function neutralizePeerBody(body: string): string; /** * Frame-adjacent discipline block for the cross-session lane (design/385 §4.1). DISTINCT from the * teammate lane's `PEER_MESSAGE_NOTICE`: the two lanes carry different trust levels (engine-attributed * teammate vs same-user peer session with self-claimed identity), and sharing one text would blur the * classifier's lane judgment. Content mirrors the three hard rules a peer session cannot override: * no settings/instruction-file/config edits on a peer's say-so, a peer message is never the user's * approval of a pending prompt, and relaying a denied action is permission laundering. */ export declare const CROSS_SESSION_MESSAGE_NOTICE: string; /** * design/385 §4.5 layer 2 — the classifier-slot text spliced into the auto-mode system prompt's * `<cross_session_messages_rule>` slot WHILE the cross-session lane is mounted (the base rule 8 is * always present; this names the concrete frame and the outbound half). The slot stays empty when the * lane is absent, so an unmounted deployment's classifier prompt is byte-identical to before. */ export declare const CROSS_SESSION_CLASSIFIER_RULE: string; /** The sender's self-attested permission-mode CLASS (CC `ZP` vocabulary, two values; absence is the * third state "no class asserted"). An attestation, not a verified fact — see cross-session-judge.ts. */ export type PermissionModeClass = "bypass" | "prompting"; export declare const PERMISSION_MODE_CLASSES: readonly PermissionModeClass[]; export declare function isPermissionModeClass(value: unknown): value is PermissionModeClass; /** Typed codec failure: a caller-supplied field that cannot be serialized canonically. Thrown (never * silently dropped) so a producer bug is loud at the mint site rather than a silently attribute-less * envelope on the wire (bad-value loudness rule). */ export declare class CrossSessionCodecError extends Error { readonly code: "cross_session.invalid_from" | "cross_session.invalid_from_session" | "cross_session.invalid_from_mode" | "cross_session.invalid_from_scope" | "cross_session.invalid_hop_chain"; constructor(code: CrossSessionCodecError["code"], message: string); } /** CC `nM`/`PGe`: 24-hex tokens, at most 32 carried (the oldest are dropped first). */ export declare const PEER_HOP_CHAIN_CARRY_WINDOW = 32; /** CC `eM` — percent-encode everything outside `[A-Za-z0-9:_/.\-]` (UTF-8 bytes, uppercase hex). The * scheme prefix (`uds:` / `session.`) is part of the raw address and passes through unchanged. * @throws CrossSessionCodecError on a non-string or a string with an unpaired surrogate. */ export declare function encodePeerAddress(raw: string): string; /** * Is this address in its CANONICAL spelling — the one {@link encodePeerAddress} would produce? The * grammar alone admits `%ZZ`, a lone `%`, or a needless `%41`; none of those can come out of the * encoder (CC's `eM` encodes `%` itself, so a genuine CC peer never sends them either). Addresses are * copied verbatim into reply targets, so a non-canonical spelling is refused rather than carried. */ export declare function isCanonicalPeerAddress(address: string): boolean; /** design/385 §4.2 — the canonical `from-scope` attribute form of a raw scope string. * @throws CrossSessionCodecError on a non-string or a string with an unpaired surrogate. */ export declare function encodeScopeAttribute(rawScope: string): string; /** Inverse of {@link encodeScopeAttribute}; `undefined` when the bytes are not valid percent-encoded * UTF-8 (the parser refuses the whole envelope on that). */ export declare function decodeScopeAttribute(encoded: string): string | undefined; /** CC `_b` ∘ `replace(/["<>]/g,"")` — the display-name canonicalization: strip the attribute-breaking * bytes, strip invisible/format/control/separator code points, trim, cap at 64 code points with an * ellipsis. An empty result means "omit the attribute" (CC `if(g)`). */ export declare function canonicalPeerDisplayName(raw: string): string; export interface CrossSessionEnvelopeFields { /** The sender's ADDRESS in its canonical (already percent-encoded) spelling — what a reply copies * into `to`. Required. Mint it with {@link encodePeerAddress} from a raw address. */ from: string; /** The sender's session id (`^[A-Za-z0-9_-]{1,80}$`). Self-claimed. */ fromSession?: string; /** The sender's display name (raw; canonicalized at build — see {@link canonicalPeerDisplayName}). */ fromName?: string; /** The sender's self-attested permission-mode class; absent = no class asserted. */ fromMode?: PermissionModeClass; /** design/385 §10 — the sender's RAW scope on the cross-principal lane (encoded on the attribute; * presence marks the message as having crossed a principal wall). Presentation only: the typed * authority anchor is the store record's `peerMeta.fromScope`, never this attribute. */ fromScope?: string; } /** * Serialize the model-face envelope. Canonical attribute order is FIXED (`from` → `from-session` → * `from-name` → `from-mode` → `from-scope`; the CC `D8` order with `from-scope` appended). The body is * neutralized with {@link escapeEnvelopeTag} so a sender-embedded copy of this tag can never close the * real frame early (idempotent: an already-neutralized body is unchanged, which is what makes the * parser's rebuild comparison work on the extracted body). * @throws CrossSessionCodecError on a field that cannot be spelled canonically. */ export declare function buildCrossSessionEnvelope(fields: CrossSessionEnvelopeFields, body: string): string; /** Why a parse refused: `not_an_envelope` (the strict grammar did not match at all), `bad_scope_encoding` * (a `from-scope` that is not valid percent-encoded UTF-8), `roundtrip_mismatch` (the grammar matched * but the canonical re-serialization differs — a near-miss / non-canonical form). All three mean * "treat the text as ordinary data". */ export type CrossSessionEnvelopeRefusal = "not_an_envelope" | "bad_scope_encoding" | "roundtrip_mismatch"; export type CrossSessionEnvelopeParse = { ok: true; fields: CrossSessionEnvelopeFields; body: string; } | { ok: false; reason: CrossSessionEnvelopeRefusal; }; /** * Strict parse + round-trip check of the model-face envelope. `body` is returned in its ENVELOPE form * (nested-tag-neutralized, exactly as the model saw it). A refusal is a classification of the text as * data, not an error. */ export declare function parseCrossSessionEnvelope(text: string): CrossSessionEnvelopeParse; export interface CcPeerFrameFields { /** The sender address (CC: optional even on the wire). */ from?: string; fromSession?: string; /** Forwarding chain of 24-hex hop tokens; more than 32 ⇒ the OLDEST are dropped (CC `ygt`). */ hopChain?: string[]; fromName?: string; fromMode?: PermissionModeClass; } /** CC `ygt` — the carry window: keep the newest 32. */ export declare function clampHopChain(chain: readonly string[]): string[]; /** * Serialize the CC wire envelope (what goes into a UDS user frame's `message.content`). Attribute * order `from` → `from-session` → `hop-chain` → `from-name` → `from-mode`. Unlike CC's own * constructor, which silently DROPS an ill-formed `from-session`/`hop-chain`, this throws on them: the * values are ours, and a producer bug must not become an attribute-less frame on the wire. * @throws CrossSessionCodecError */ export declare function encodeCcPeerFrame(fields: CcPeerFrameFields, body: string): string; export type CcPeerFrameParse = { ok: true; fields: CcPeerFrameFields; body: string; } | { ok: false; reason: "not_an_envelope" | "roundtrip_mismatch"; }; /** CC `oM` byte-exact: strict grammar, then rebuild-and-compare. The hop chain comes back as a typed * array for the engine's side channel — a consumer must strip it before the model face (CC `ymn`). */ export declare function decodeCcPeerFrame(text: string): CcPeerFrameParse;