@sema-agent/core
Version:
Stateless, task-oriented AI agent core
163 lines (162 loc) • 11.5 kB
TypeScript
import type { AskEffective } from "../core/wiring-manifest.js";
import { type PermissionModeClass } from "./cross-session-envelope.js";
/** The permission-mode words the fold reads (the engine's closed set, as the wire spells it). */
import type { PermissionMode } from "../core/ask-origin.js";
/**
* The explicit values of `crossSessionInbound`, loosest first: `wake` (deliver, and the HOST may start
* a turn on this session when its box grows), `next-turn` (deliver at the session's next turn
* boundary), `hold` (park for a human), `refuse`. The UI's spelling `"default"` means UNSET (mode
* parity) and is folded to `undefined` by the resolver — it is not a distinct policy.
*
* @contract cross_session.wake_is_the_hosts_leg — `wake` is judged exactly as `next-turn` at the drain
* (deliver); what it adds is the HOST's leg: a host that observes a `peer_message` or `engine_frame`
* record appended to a session whose setting resolves to `wake` may start ONE turn on that session,
* bounded by `maxWakesPerHour`, and the engine promises that the turn's first drain pass ATTEMPTS the
* box (a claim that loses, a setting a host cannot read, an admission stop or the pass budget can
* still leave the record for a later boundary — every such outcome is observable, none is silent).
* With the budget spent, or on a host that starts no turns, `wake` degrades to `next-turn`: the record
* stays in the box and the next user turn reads it. The engine never starts a turn itself.
*/
export type CrossSessionInboundSetting = "wake" | "next-turn" | "hold" | "refuse";
export declare const CROSS_SESSION_INBOUND_SETTINGS: readonly CrossSessionInboundSetting[];
export declare function isCrossSessionInboundSetting(value: unknown): value is CrossSessionInboundSetting;
/** Which layer decided the effective value. `"invalid"` = an unrecognized value was found in some
* layer and the fail-closed rule forced `hold` (the loud arm). */
export type CrossSessionSettingSource = "managed" | "user" | "repo" | "invalid";
export interface CrossSessionInboundSettingLayers {
/** Organization-managed layer (CC `policySettings`): beats the user layer when set. It is NOT
* immune to repository TIGHTENING (CC form: the repo loop runs over whatever the first two layers
* resolved to) — a repo may make a managed `next-turn` stricter, never looser. */
managed?: unknown;
/** The user's own layer (CC `userSettings`/`flagSettings`): consulted after managed. */
user?: unknown;
/** Repository layers (CC `localSettings`/`projectSettings`), in precedence order. A repo layer can
* only TIGHTEN the standing value (wake < next-turn < hold < refuse), whichever layer set it; it can
* never loosen it — so `wake` written in a repo layer never takes effect (the unset baseline is
* `next-turn`, and a repo layer cannot loosen past it). */
repo?: readonly unknown[];
}
export interface ResolvedCrossSessionInboundSetting {
/** The effective explicit value; `undefined` = unset ⇒ mode parity. */
value?: CrossSessionInboundSetting;
decidedBy?: CrossSessionSettingSource;
/** Layers whose value was neither an explicit setting nor the `"default"` spelling. Non-empty
* ⇒ the resolver applied the fail-closed rule (see `decidedBy: "invalid"`) unless the effective
* value was already at least as tight as `hold`. Reported so a host can warn — never swallowed. */
invalidLayers: Array<"managed" | "user" | "repo">;
}
/**
* CC `I` transcribed: managed beats user; repo layers may only tighten the standing value — including a
* managed one (tightening is the safe direction; the loosening direction is what policy forbids) — and
* take attribution on a tie unless managed decided; an unrecognized value anywhere forces `hold` when
* the standing value is looser than `hold` (bad-value loudness: a garbage setting never silently reads
* as `next-turn`).
*/
export declare function resolveCrossSessionInboundSetting(layers: CrossSessionInboundSettingLayers): ResolvedCrossSessionInboundSetting;
/**
* The layers a CROSS-PRINCIPAL record is judged on: the principal's own
* (`managed`, `user`). The repository layers speak for the workspace one terminal runs in; a record in
* the principal inbox is addressed to the principal, and any of its terminals may win the box's lease —
* reading the winner's repository would let the lease race decide the verdict. One projection, applied by
* every drain lane before {@link resolveCrossSessionInboundSetting}.
*/
export declare function principalInboundLayers(layers: CrossSessionInboundSettingLayers): CrossSessionInboundSettingLayers;
/**
* Why a message was held. The first eight are CC 2.1.250's closed set (`sne`), reachable exactly
* where CC reaches them. `invalid-mode-attestation` is a sema-only arm: CC's envelope parser drops a
* non-vocabulary `from-mode` before any judgment (the state is unreachable there); sema receives the
* attestation as a TYPED store/wire field, where a garbage value is a producer bug that must hold
* loudly rather than fold into "no class asserted" (which would DELIVER to a prompting recipient).
* `cross-principal-default` is the other sema-only arm (CC has no cross-principal lane): a record that
* carries the delivery gate's trust anchor (`fromScope`) and meets no explicit setting is held — the
* principal arm has no mode parity to fall back on.
*/
export type CrossSessionHoldCause = "mode-mismatch" | "no-mode-asserted" | "explicit-setting" | "managed-setting" | "repo-setting" | "invalid-setting" | "bypass-default" | "mode-unknown" | "invalid-mode-attestation" | "cross-principal-default";
export declare const CROSS_SESSION_HOLD_CAUSES: readonly CrossSessionHoldCause[];
/** CC `sne` verbatim for the eight CC causes; the sema arm has its own sentence. */
export declare function describeCrossSessionHoldCause(cause: CrossSessionHoldCause): string;
export type CrossSessionInboundVerdict = {
verdict: "deliver";
} | {
verdict: "hold";
cause: CrossSessionHoldCause;
message: string;
warning?: string;
} | {
verdict: "refuse";
cause: "opt-out";
};
export interface CrossSessionInboundInput {
/** The recipient's resolved setting ({@link resolveCrossSessionInboundSetting}). */
setting: ResolvedCrossSessionInboundSetting;
/** The RECIPIENT's own permission-mode class right now ({@link foldPermissionModeClass});
* `"unknown"` fails closed (`mode-unknown`). */
selfModeClass: PermissionModeClass | "unknown";
/** The message under judgment. `undefined` = no message: the message-less POSTURE probe (CC `H`,
* what a session advertises as `cross_session_inbound: available|unavailable`) — the arm that
* reaches `bypass-default`. */
sender?: {
/** The sender's attestation as received (typed field; validated here, never assumed). */
fromMode?: unknown;
/** A session's message to itself never needs parity. */
selfSent?: boolean;
/** The organization delivery gate's typed trust anchor (`MailboxPeerMeta.fromScope`). Present ⇒
* the PRINCIPAL arm: the words come from another principal, so neither `fromMode` nor `selfSent` nor
* this session's own class is read, and an unset setting holds (`cross-principal-default`). The
* caller passes the principal's layers ({@link principalInboundLayers}) as `setting` for it. */
fromScope?: string;
};
}
/**
* The four-state judgment at the RECIPIENT's drain point (judgment authority is the recipient's — its
* settings, its current mode, judged when the message is consumed, not when it was sent).
*
* Explicit value always wins. Unset ⇒ mode parity: self-sent delivers; an undeterminable own mode
* holds (`mode-unknown`); a message whose sender attested a class delivers on match and holds on
* mismatch; a sender asserting no class is held only while this session bypasses prompts; the
* message-less probe holds a bypassing session (`bypass-default`) and clears a prompting one.
*
* A record carrying the delivery gate's anchor (`sender.fromScope`) is judged on the PRINCIPAL arm: the
* explicit value still wins exactly as above, and unset holds (`cross-principal-default`) — no parity, no
* self-sent, no read of this session's class, so every terminal of the principal reaches the same verdict.
*/
export declare function judgeCrossSessionInbound(input: CrossSessionInboundInput): CrossSessionInboundVerdict;
/**
* design/385 §4.4 — sema's two-class fold of its own permission posture, from the session's EFFECTIVE
* permission mode (the engine's own resolution — never the requested one, never re-derived from spec shape)
* and the wiring manifest's ask derivation ({@link AskEffective}):
* · `bypass` — the effective mode is `bypassPermissions`, OR no human decides an ask (`auto_allow`, a
* blanket answering seat — the adapter's own reading of "nobody is watching");
* · `"unknown"` — otherwise, when the ask derivation is `unresolved` (the judge fails closed on it);
* · `prompting` — otherwise: an ask reaches a human or parks for one (`human_reachable`, `park_only`),
* and — CC's literal rule for a never-prompting-never-bypassing mode — `auto_deny`.
* `effective` absent (a leg with no mode fact) reads the ask derivation alone.
*
* WHAT THE CLASS IS: `fromMode` is a presence LABEL — whether a person is watching this session's approvals —
* attested to a peer so a bypass session does not park work where nobody watches. It is not a permission
* authority and authorizes no execution; the permission mode itself is read only from `effective`.
*/
export declare function foldPermissionModeClass(effective: PermissionMode | undefined, askEffective: AskEffective): PermissionModeClass | "unknown";
/** The synchronous send outcomes a sender can observe at the send point (CC's ten classes on sema's
* codes; `sender_paced` is expressed by the admission gate's `rate_limited` and is deliberately not a
* second code). `parked_offline` is a DISPOSITION (durable append succeeded, recipient offline) and
* not an error; `send_uncertain` exists only on the CC-adapter arm (a store append returns a seq or
* throws — there is no "written but unconfirmed" state to name). */
export type PeerSendVerdictCode = "peer_send.stale_socket" | "peer_send.socket_busy" | "peer_send.too_large" | "peer_send.invalid_target" | "peer_send.timeout" | "peer_send.recipient_refuses" | "peer_send.not_reachable" | "peer_send.subscription_cap" | "peer_send.send_uncertain" | "peer_send.other";
export declare const PEER_SEND_VERDICT_CODES: readonly PeerSendVerdictCode[];
/** CC `Be`: three classes are DEGRADED (the send may still have effect / the refusal is best-effort),
* the rest are errors. */
export declare function peerSendVerdictSeverity(code: PeerSendVerdictCode): "error" | "degraded";
/** How a successful send was carried and where it stands. `via` discloses the lane (store = durable
* mailbox, uds = the CC-dialect socket adapter); `parked_offline` promises crash-durability under the
* deployment's mailbox retirement policy, not unbounded delivery. */
export type PeerSendVerdict = {
ok: true;
disposition: "delivered" | "queued" | "parked_offline";
via: "store" | "uds";
} | {
ok: false;
code: PeerSendVerdictCode;
severity: "error" | "degraded";
message: string;
};