import { components } from '@ai-matrx/api/types'; import { MatrxTransport } from '@ai-matrx/api'; /** * `@ai-matrx/agents/intelligence` — the IntelligencePort (CONTRACTS §3). * * One framework-free port a client uses to describe, run, stream, rejoin, * read the typed result of, list, answer and softly create ANY mandate — * agent-held or workflow-held — by KEY ALONE. It EXTENDS the existing run * client (`startMandateRun`, `rejoinRuntimeOperation`, `followWorkflowRunEvents`, * `normalizeMatrxError`); there is no second client here. * * Wire: aidream's mandate doors D1-D5 (ap1 wire spec) — * D1 `GET /mandates/{key}/describe` * D2 `POST /ai/mandates/{key}` (NDJSON; both Holder types) * D3 `GET /mandates/runs/{request_id}/result` * D4 `GET /mandates/{key}/runs` * D5 `POST /mandates/runs/{request_id}/answer` (NDJSON) * `POST /mandates/soft` * * Keys are DATA: only the key FORMAT (`.`, both non-empty) is * checked locally; a key absent from the compiled key set is never rejected — * the server decides. The in-process stream handle never crosses the seam: * only `RunRef` does, and events ride `port.events.on("run:", …)`. * Every call returns a `Result` and never throws; an error `message` is the * server's own sentence. */ /** A UUID string. */ type Uuid = string; type Result = { ok: true; data: T; } | { ok: false; error: AppletError; }; /** * The package-local structural shape of the applet error. The owning package * (`@ai-matrx/applets`) aliases its own `AppletError` to this one. */ interface AppletError { code: string; message: string; retryable: boolean; diagnostic?: string; } /** The only handle that crosses the seam — never the in-process stream. */ type RunRef = { requestId: string; conversationId: string; mandateKey: string; /** * The organization the run started in (`null` = the guest lane), when this ref was minted by * `run`/`continue` in this process. Every follow-up and read rides it, so a page with no * organization selected still reads and continues its own run; absent (a ref rebuilt from a * listing) = read from the run's durable result. */ organizationId?: string | null; }; /** A mandate input the server asks for — the generated `MandateServedInput` wire type. */ type ServedInput = components["schemas"]["MandateServedInput"]; /** * A `__kind`-marked content envelope. Structural on purpose: * `@ai-matrx/content-ir` (a dependency of this package) does not export a * `KindEnvelope` type today; swap this alias when it does. */ type KindEnvelope = { __kind: string; [k: string]: unknown; }; type RunEvent = { type: "text"; delta: string; } | { type: "status"; status: string; } | { type: "needs_input"; inputs: ServedInput[]; } | { type: "result"; envelope: KindEnvelope | null; } | { type: "error"; error: AppletError; }; interface MandateDescription { key: string; label: string; goal: string; outputKind: string | null; inputs: ServedInput[]; holder: { type: "agent" | "workflow"; } | null; } interface RunRequest { key: string; variables?: Record; context?: Record; /** What the human typed — never machine content. */ userInput?: string; /** `null` = the guest lane: the field is omitted and the server decides. */ organizationId: Uuid | null; surfaceName?: string; appletId?: string; } interface RunResult { envelope: KindEnvelope | null; text: string; status: string; /** * Why `envelope` is null when the mandate declares an output kind (the * server's own sentence), or the server answered an object with no `__kind`. * A superset of the contract's shape: nothing is dropped silently. */ note?: string; } interface RunListing { ref: RunRef; startedAt: string; status: string; } interface SoftMandateSpec { key: string; label: string; goal: string; outputKind?: string; draftInputs: unknown[]; level: "user" | "organization"; organizationId: Uuid; } type RunEventListener = (event: RunEvent) => void; interface IntelligencePort { describe(key: string): Promise>; run(req: RunRequest): Promise>; /** Event stream `run:` carries `RunEvent`s; returns unsubscribe. */ readonly events: { on(stream: `run:${string}` | string, listener: RunEventListener): () => void; }; result(ref: RunRef): Promise>; answer(ref: RunRef, values: Record): Promise>; rejoin(ref: RunRef): Promise>; listRuns(key: string, appletId?: string): Promise>; createSoft(spec: SoftMandateSpec): Promise>; /** * The next turn of a run's conversation (`POST /ai/conversations/{id}`), in the run's own organization. * `ref` is the conversation's FIRST run (its result names the organization); the answer is the new turn's * ref, whose events stream on `run:` like any run. */ continue(ref: RunRef, req: ContinueRequest): Promise>; /** Stop a turn at its next boundary (`POST /ai/cancel/{requestId}`); what streamed persists. */ stop(ref: RunRef): Promise>; } interface ContinueRequest { /** What the human typed — never machine content. */ userInput: string; context?: Record; } interface CreateIntelligencePortOptions { transport: MatrxTransport; } /** Any thrown value → `AppletError` carrying the server's own sentence. */ declare function toAppletError(err: unknown): AppletError; /** Format-only key check: `.`, both non-empty (never a key-set lookup). */ declare function isMandateKeyFormat(key: unknown): key is string; /** * A stateful mapper for ONE stream: normalized `{event, data}` envelopes in, * `RunEvent`s out. Statefulness is what makes the terminal `completion` * unambiguous (no second error after an `error` event, no `result: null` when * a typed result or an input request already arrived). */ declare function createRunEventMapper(): (envelope: { event: string; data: unknown; }) => RunEvent[]; declare function createIntelligencePort(options: CreateIntelligencePortOptions): IntelligencePort; export { createIntelligencePort, createRunEventMapper, isMandateKeyFormat, toAppletError }; export type { AppletError, ContinueRequest, CreateIntelligencePortOptions, IntelligencePort, KindEnvelope, MandateDescription, Result, RunEvent, RunEventListener, RunListing, RunRef, RunRequest, RunResult, ServedInput, SoftMandateSpec, Uuid };