/** * Effective capabilities — trust-class capabilities COMPOSED with runtime context. * * `resolveCapabilities` (`capabilities.ts`) answers "what may this trust class * do" from the actor's class alone, and stays deliberately pure (no context * dependencies) so it remains the single fail-closed trust boundary. Some real * decisions additionally depend on runtime context — the channel a request * arrived on, surface actions, task authorization. Those compositions belong * here, in named/testable helpers, rather than re-derived inline at each call * site (which scatters a single policy across the codebase). * * Scope: this module composes capabilities with *actor/request* context. It does * not read global config itself — callers resolve those inputs and pass the * result in, keeping this layer focused on the capability composition. * `resolveRoutingState` in `trust-context-resolver.ts` is the same shape * (capability + guardian-route context → `promptWaitingAllowed`) and predates * this module; it stays where it is. */ import type { TrustClass } from "./actor-trust-resolver.js"; import { resolveCapabilities } from "./capabilities.js"; type RawTrustClass = TrustClass | (string & {}) | undefined; /** * Channels that are themselves privileged document surfaces. Actors on these get * privileged document access regardless of trust class — the `vellum` first-party * console is the operator's own surface, not an external contact channel. */ const PRIVILEGED_DOCUMENT_CHANNELS = new Set(["vellum"]); /** * Whether an actor may perform privileged (non-conversation-scoped) document * operations. True when the trust class grants it OR the request arrived on a * privileged channel. */ export function canActOnPrivilegedDocuments(actor: { trustClass: RawTrustClass; executionChannel?: string; }): boolean { return ( resolveCapabilities(actor.trustClass).canAccessPrivilegedDocuments || (actor.executionChannel != null && PRIVILEGED_DOCUMENT_CHANNELS.has(actor.executionChannel)) ); } /** * Whether personal-memory content (memory pages, PKB, matched v3 card * sections, the v2 static block) may be surfaced to an actor. * * The trust class decides, and nothing else. Personal memory is elevated * access, and every other consumer of "no actor was bound" already fails * closed for it: `FALLBACK_TURN_TRUST` is documented as low-trust and "never * guardian", and the workflow resume path deliberately declines to fall back * to the guardian context so a resumed run cannot gain trust it did not start * with. A gate that granted memory for the same condition would contradict * them. * * Notably this takes no channel. A surface is not an actor: arriving on the * first-party console says nothing about who is asking, and an actor whose * class could not be resolved fails closed to `unknown` upstream. Answering * "who is this" is `resolveTrustClass`'s job -- including the local/native * turns that resolve to guardian under a disabled-auth posture -- and this * function only answers what that actor may see. */ export function canSeePersonalMemory(actor: { trustClass: RawTrustClass; }): boolean { return resolveCapabilities(actor.trustClass).canAccessMemory; } /** * Whether an archive-by-sender invocation is authorized. Any one of a surface * action, a task-batch authorization, or an explicit prompt approval suffices. * Absent those, the actor's own `user_approved` flag only counts when its trust * class may self-authorize archive-by-sender. */ export function isArchiveBySenderAuthorized(args: { trustClass: RawTrustClass; triggeredBySurfaceAction?: boolean; batchAuthorizedByTask?: boolean; approvedViaPrompt?: boolean; userApproved?: boolean; }): boolean { const selfAuthorized = args.userApproved === true && resolveCapabilities(args.trustClass).canSelfAuthorizeArchiveBySender; return ( args.triggeredBySurfaceAction === true || args.batchAuthorizedByTask === true || args.approvedViaPrompt === true || selfAuthorized ); }