UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

163 lines (162 loc) • 11.5 kB
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; };