@sema-agent/core
Version:
Stateless, task-oriented AI agent core
76 lines (75 loc) • 6.21 kB
TypeScript
import { type RedactionReport } from "./redaction-passes.js";
export type { RedactionFinding, RedactionReport, RedactionConfidence, RedactionSource, SummarizableFinding, } from "./redaction-passes.js";
export { summarizeRedactions } from "./redaction-passes.js";
/**
* Every exit that redacts — into a model's context, a host's event or error lane, a log line, a person's screen — does it
* with ONE pass, its tier's, and nothing else redacts: a text through {@link egressText}, a thrown value's text through
* {@link egressThrownText}, a value with members (a thrown error, a marks bag) through {@link egressValue}, which runs the
* same pass over every string member. What comes out is an {@link Egress}: a string the type checker knows came out of
* that tier. What a tool RETURNS is its content, read by the model that asked for it, and is not redacted (CC redacts
* nothing there either); what is redacted is what the engine itself words or carries: errors, summaries, run rows.
*
* Two tiers, because the faces have two audiences (CC 2.1.212 alignment):
* - **`same-principal`** — the model, the script and the host that launched the work (tool results, workflow agent
* outputs / results / errors, run rows behind the scope-gated observe API, host error lanes): the credential
* vocabulary only. Paths and URLs in a worker's answer are the caller's OWN machine's paths — redacting them killed the
* cross-agent assertion pattern ("return the file you wrote" → `[redacted-path]`) while protecting no one;
* cross-tenant isolation on these faces is ACCESS CONTROL (scope gates), not string surgery. CC redacts nothing here;
* sema keeps the credential pass as a declared superset.
* - **`human`** — text shown to a person or exported beyond the launching principal: the credential vocabulary plus
* every URL and absolute path — host internals, and exfil-lure links in untrusted text.
* {@link untrustedEgressForHuman} additionally FENCES its text (design/97 CORE-2), so untrusted content cannot pose as
* instructions to its reader.
*/
export type EgressTier = "same-principal" | "human";
declare const EGRESS_TIER: unique symbol;
/** A string that came out of tier `T`'s pass. Only this module mints one. */
export type Egress<T extends EgressTier> = string & {
readonly [EGRESS_TIER]: T;
};
/** Tier `tier`'s one pass over a text. */
export declare function egressText<T extends EgressTier>(tier: T, text: string, report?: RedactionReport): Egress<T>;
/** The text a thrown value reads as (`thrownText`), through tier `tier`'s pass: the one form a thrown value's words take
* on their way out of the engine. */
export declare function egressThrownText<T extends EgressTier>(tier: T, err: unknown): Egress<T>;
/** The `same-principal` tier's pass, under the name hosts call it by (the engine calls {@link egressText}). */
export declare function redactSecrets(s: string, report?: RedactionReport): Egress<"same-principal">;
/**
* An extension's own words — its registration name, a bus reason, a key it wrote — made fit for an operator line: the
* `same-principal` egress pass first, then one line and bounded. The pass runs before the cut, so a cut never leaves part
* of a credential the pass would have recognised whole. Every station sentence that carries such words reads them here.
* It lives beside the pass, not in the stations module: a value import of the stations graph from a station's consumer
* closed a load-order cycle through the fs tools' barrel (an export the barrel copied before it existed).
*/
export declare function extensionWords(s: string, max: number): string;
/** The `human` tier's pass, under the name hosts call it by (the engine calls {@link egressText}). */
export declare function redactHostLeaks(s: string, report?: RedactionReport): Egress<"human">;
/**
* Tier `tier`'s pass over every string member of `value`, returned as a DATA SNAPSHOT — never the value itself, so nothing
* that leaves can run the value's code again (a getter, a serializer, a proxy trap): a host's serializer, a log's inspector
* and the loop's own readers (`thrownText`, `thrownMarksOf`) reach what was read, once, and passed. The rules, one each:
* - a string goes through {@link egressText}, and so does every member NAME; a function is code, not data, and is not
* carried at any depth (the value handed in included: it comes back `undefined`); any other primitive is itself;
* - an error (any realm, any class) is re-minted as a plain `Error` — never on the thrown value's prototype, whose
* serializers and accessors would run again — carrying EVERY own member (`message`, `stack`, `cause`, `errors`,
* `details`, `code` …) and its `name`, `message`, `code` and `stack` read once through its chain, all as own data;
* - any other object is what JSON reads: its `toJSON(name)` when it has one (a `URL` is its `href`, a `Date` its ISO
* text; one that answers itself is read for its members), otherwise its own enumerable members;
* - a member whose name is a credential name (the vocabulary's record-key view: `password`, `Authorization`,
* `x-api-key`) and whose reading is a string is the marker whole;
* - cycles and shared members stay shared on the snapshot.
*/
export declare function egressValue<T extends EgressTier, V>(tier: T, value: V): V;
/** The `same-principal` tier's pass + size-bound (NO injection fence): workflow outputs/results/errors riding back to
* the launching model/script, run rows behind scope-gated observe. Paths/URLs survive (the assertion pattern depends on
* them); secrets never do. */
export declare function boundedRedactedSummary(value: unknown, max: number): Egress<"same-principal">;
/**
* The FULL untrusted-egress transform (design/97 CORE-2): the `human` tier's pass + size-bound + FENCE. Use
* for any worker prompt/output (#5 transcript) or steer content (#6) shown to a HUMAN or exported beyond the
* launching principal, so untrusted text can neither leak host internals nor pose as instructions.
*/
export declare function untrustedEgressForHuman(value: unknown, opts: {
label: string;
max: number;
}): Egress<"human">;