cocottetech/@platform/codebase/@packages/surface-adapter-contracts/src/context.ts

86 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* AdapterContext — the per-invocation execution environment handed to every
* adapter action's `precheck` / `execute` / `rollback`.
*
* Deliberately framework- and Playwright-free. The Layer-6 design doc listed
* `browserContext: BrowserContext`, `torCircuit`, and `captchaSolver` directly on
* the context; those are intentionally collapsed here into a single opaque
* {@link SurfaceSession} handle so that this shared contract — imported by every
* specialist and every action — never drags `playwright` into its dependency
* graph. The container/Tor/captcha machinery lives behind the session handle,
* owned by the adapter runtime, not exposed in the type.
*/
import type { AgentActionsClient } from './audit.js';
import type { BlocklistAccessor } from './blocklist.js';
import type { AdapterLogger, PlatformApiClient } from './platform-api.js';
import type { SurfaceKind } from './surface-kind.js';
/**
* Opaque handle to the live, authenticated surface session (the
* container-managed browser context + Tor circuit + captcha solver from
* `_engineering-surface-adapter-container.md` Layers 15). The contract treats it
* as a black box; the surface-specific adapter runtime supplies and interprets
* it. Each concrete specialist augments this via declaration merging or casts to
* its own session type — the base shape carries only what every action can rely
* on without importing Playwright.
*/
export interface SurfaceSession {
/** Which surface this session is authenticated against. */
readonly surface: SurfaceKind;
/** Whether the session currently holds live credentials (cookies still valid). */
readonly authenticated: boolean;
}
/**
* Session-free slice of the per-invocation context, handed to an action's
* `precheck`. Precheck runs the deterministic K-gates, which only read
* `blocklist` / `platformApi` — they NEVER need the live {@link SurfaceSession}.
* Splitting the context this way lets the dispatcher run precheck (and decline a
* K-gate failure) WITHOUT acquiring a container-managed session: a blocked or
* approval-required attempt costs no session spin-up.
*
* Carries `agentActions` so the dispatcher (the one place that owns the audit
* write on a decline) can record the declined row from this context; an action's
* own `precheck` never writes audit rows.
*/
export interface PrecheckContext {
/** Person tenant (`agent_actions.user_id`). Always present (person-first). */
readonly userId: string;
/** Optional Org overlay (`agent_actions.org_id`). Absent = Person-owned. */
readonly orgId?: string;
/** Client for reads/writes against platform.api on black. */
readonly platformApi: PlatformApiClient;
/** Accessor for the tenant's safety blocklist (K-gate data source). */
readonly blocklist: BlocklistAccessor;
/** Writer for the `agent_actions` audit spine. */
readonly agentActions: AgentActionsClient;
/** Structured logger (never logs credentials or raw page content). */
readonly logger: AdapterLogger;
}
/**
* Per-invocation context handed to an action's `execute` / `rollback`. The
* session-free {@link PrecheckContext} plus the two things only available once the
* dispatcher has decided to run the action: the live surface session and the
* dispatcher's `autoExecuted` decision.
*
* One {@link AdapterContext} is constructed per executed action and is scoped to a
* single (userId, [orgId], surface session).
*
* `autoExecuted` is the dispatcher's single source of truth for the audit flag:
* `autoExecutable && !approved`. An action that writes an `agent_actions` row in
* `execute` MUST set `auto_executed` from this field (never a literal) so the
* persisted value always equals the dispatcher's decision — even for an
* autoExecutable action that was ALSO explicitly approved (then `autoExecuted` is
* `false`, recording the run as operator-driven).
*/
export interface AdapterContext extends PrecheckContext {
/** Live authenticated surface session handle (container-managed). */
readonly session: SurfaceSession;
/**
* The dispatcher's decision for this run: `true` iff the action ran without a
* per-call approval (`autoExecutable && !approved`). Actions writing an audit
* row in `execute` set `auto_executed` from this — never from a hardcoded value.
*/
readonly autoExecuted: boolean;
}