@sema-agent/core
Version:
Stateless, task-oriented AI agent core
70 lines (69 loc) • 4.47 kB
JavaScript
import { readMailboxPeerMeta } from "../core/mailbox-store.js";
import { buildCrossSessionEnvelope, encodePeerAddress } from "./cross-session-envelope.js";
import { PRINCIPAL_INBOX_HANDLE } from "./cross-session-ref.js";
import { describeRejectedValue } from "../core/tool-search-setting.js";
export const PRINCIPAL_INBOX_CONSUMER_INVALID_CODE = "config.principal_inbox_consumer_invalid";
export function principalInboxConsumerOf(deps) {
const raw = deps.principalInboxConsumer;
if (raw === undefined)
return { kind: "none" };
if (raw === "terminals") {
throw consumerRefusal(`RunnerDeps.principalInboxConsumer "terminals" is not available yet: the shared held decision ledger is not yet available, so a letter a person refused in one terminal could be delivered by another. Declare { assistant: "<agent handle>" } or leave the seat absent.`, "terminals_unsupported");
}
if (raw !== null && typeof raw === "object" && !Array.isArray(raw) && Object.getPrototypeOf(raw) === Object.prototype) {
const keys = Reflect.ownKeys(raw);
const handle = raw.assistant;
if (keys.length === 1 && keys[0] === "assistant" && typeof handle === "string" && handle.trim() !== "")
return { kind: "assistant", handle };
}
throw consumerRefusal(`RunnerDeps.principalInboxConsumer must be absent (no principal-inbox consumer), "terminals" (the scope's terminal sessions drain it beside their own box) or { assistant: "<agent handle>" } ` +
`(got ${describeRejectedValue(raw)}) — a malformed value is refused rather than read as "terminals", which would let terminal sessions take cross-principal letters past the assistant`, "malformed");
}
function consumerRefusal(message, reason) {
const e = new Error(message);
e.code = PRINCIPAL_INBOX_CONSUMER_INVALID_CODE;
e.detail = { reason };
return e;
}
export const ORG_DELIVERY_REFUSAL_CODES = Object.freeze(["org_delivery.not_member", "org_delivery.rate_limited", "org_delivery.inbox_full", "org_delivery.recipient_tombstoned"]);
export function isOrgDeliveryRefusalCode(value) {
return typeof value === "string" && ORG_DELIVERY_REFUSAL_CODES.includes(value);
}
export function mintCrossPrincipalRecord(input) {
const { from, body, receiptId, sentAt, consumer } = input;
if (consumer?.kind !== "terminals" && consumer?.kind !== "assistant") {
throw consumerRefusal(`an organization delivery gate is minting a principal-inbox letter, but the deployment declares no principal-inbox consumer (RunnerDeps.principalInboxConsumer is absent): nobody would ever read it. Declare { assistant: "<agent handle>" }.`, "no_consumer");
}
if (typeof body.content !== "string")
throw new TypeError("mintCrossPrincipalRecord: body.content must be a string");
if (!Number.isFinite(sentAt))
throw new TypeError("mintCrossPrincipalRecord: sentAt must be a finite epoch-ms number");
const peerMeta = readMailboxPeerMeta({ kind: "peer_message", fromScope: from.scope, gateReceiptId: receiptId, ...(body.urgent === true ? { urgent: true } : {}) });
const name = typeof from.name === "string" && from.name !== "" ? from.name : undefined;
buildCrossSessionEnvelope({ from: encodePeerAddress(name ?? "unknown"), ...(name !== undefined ? { fromName: name } : {}), fromScope: from.scope }, body.content);
return { ...(name !== undefined ? { from: name } : {}), content: body.content, sentAt, peerMeta };
}
export function isGateMintedRecord(meta) {
return meta !== undefined && meta.kind === "peer_message" && meta.fromScope !== undefined && meta.gateReceiptId !== undefined;
}
export const PRINCIPAL_INBOX_TRIAGE_POLICIES = Object.freeze(["triage_readonly", "draft_for_approval", "decline"]);
export function principalInboxTriagePolicy(verdict) {
switch (verdict.verdict) {
case "deliver":
return "triage_readonly";
case "hold":
return "draft_for_approval";
case "refuse":
return "decline";
default: {
const _exhaustive = verdict;
void _exhaustive;
throw new Error(`unreachable verdict ${JSON.stringify(verdict)}`);
}
}
}
export function principalInboxSideEffectKey(seq) {
if (!Number.isInteger(seq) || seq < 1)
throw new RangeError(`principalInboxSideEffectKey: seq must be a positive integer (got ${String(seq)})`);
return `${PRINCIPAL_INBOX_HANDLE}#${seq}`;
}