/** * Guuey Worker Protocol v1 — the frozen, versioned types the Router and a Worker * exchange. Types ARE the protocol's source of truth (per-language SDKs derive * from these). See the north-star design §1. * * Router → Worker (fd 0 / stdin): ControlMessage (invoke | shutdown) * Worker → Router (fd 3): WorkerEvent (text | done | error) * stdout (1) + stderr (2): the builder's own logs — not the protocol. */ /** v1 ≡ the worker-facing half of Guuey Router v1 (pinned via `runtime.router`). */ export declare const PROTOCOL_VERSION: "v1"; export type ProtocolVersion = typeof PROTOCOL_VERSION; /** A JSON value — the precise parse target for NDJSON control/event lines * (NOT `unknown`: a parsed line has no static shape yet, but it IS JSON). */ export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue; }; export type AuthMode = "anonymous" | "authenticated"; /** Router-vouched end-user identity (the Worker writes zero auth code). */ export interface Identity { userId: string; authMode: AuthMode; } /** The three GuueyFS layer mounts the Worker reads/writes. */ export interface Fs { app: string; home: string; session: string; } export interface HistoryMessage { role: "user" | "agent"; text: string; } /** * One prior-memory record pushed by value on the invoke (§1.4). A minimal, * dependency-free projection of the Router's `AgMemoryRecord` — the worker reads * only `key`/`value` for its `` preamble. `value` is required * (the fold seeds it); `key` is optional (unkeyed records exist). */ export interface PriorMemoryRecord { key?: string; value: JsonValue; } /** * One app's section of the user's cross-app profile (cross-app-profile T7). The * Router reads every section it may recall (`readProfileSections`), resolves the * writing app's display `name` for provenance, and pushes them by value so the * worker renders them into the `` RECALL block. `app` is the * source app's display name (or a synthetic `""` for the truncation marker line, * whose `content` is the `[…omitted…]` note); `content` is the section body. * Minimal + dependency-free, mirroring {@link PriorMemoryRecord}. */ /** See {@link Invoke.firstImpression}. `contract` is protocol's `DataContract` as ggui returned it — opaque here, forwarded verbatim. */ export interface FirstImpressionPush { chipKey: string; intent: string; contract: JsonValue; blueprintId?: string; /** * protocol `BlueprintVariance` (guuey#1256) — present only for a * variance-named binding. ggui's exact-key match keys on `(contract, * variance)`, so the host sends it in `blueprintDraft.variance` to reach * the variance-named blueprint; absent = the default variant. */ variance?: JsonValue; } export interface ProfileSection { app: string; content: string; } export type StopReason = "end_turn" | "max_turns" | "error"; /** Start a turn. Carries the pushed context (identity, fs, a recent history * window — the full transcript is at `/session/.guuey/history.jsonl`). * * `priorMemory`/`priorState` are the §1.4 push-by-value context the worker * renders into its system-prompt preamble (thread memory + working state). * Both optional: an early-thread invoke carries neither. */ /** * guuey#901 — a `credential: 'oauth'` server's availability THIS turn, as the * pod's pre-turn preflight resolved it. Pushed by value like the rest of the * invoke context so the model's picture of what it can reach is the pod's, * not an inference from its own earlier answers (the failure this fixes: a * turn-1 "not connected" line in the history preamble anchored turn 2 into * saying it again with the tools in hand). */ export type McpAvailabilityState = "connected" | "needs_authorization" | "denied" | "unavailable"; export interface McpAvailability { /** The declared `mcpServers` key. */ server: string; state: McpAvailabilityState; } export interface Invoke { type: "invoke"; input: string; identity: Identity; /** * The three layer mounts. NON-NULL on every turn (§1.4): real GuueyFS layers * when the pod has durable storage armed, the spec-default `/app`/`/home`/ * `/session` mounts otherwise — so presence says nothing about whether the * layers are real. See {@link fsBound}. */ fs: Fs; /** * Whether {@link fs} names REAL GuueyFS layers this turn (guuey#234): the * pod has `GUUEY_FS_BASE` armed by the operator and the Router resolved * per-session layer dirs under it — NOT the spec-default mounts, NOT the * federation-only ephemeral base the broker uses for credential files. THE * gate for a host's built-in file tools + shell (`@guuey/host`'s Claude * adapter: `tools: [Read, Write, Edit, Glob, Grep, Bash]` iff true). Absent/ * false → the host is purely MCP-driven. Mirrors {@link memoryAttached}: a * pod-side signal threaded per invoke, never re-derived from `fs` presence * (the pre-#234 proxy that put Bash in EVERY no-code agent's catalog). */ fsBound?: boolean; history: HistoryMessage[]; /** Thread-scoped memory folded from prior turns (the `` preamble). */ priorMemory?: PriorMemoryRecord[]; /** Prior working-state blob carried from the previous turn (the `` preamble). */ priorState?: JsonValue; /** * guuey#901 — per `credential: 'oauth'` server, whether its tools are * available this turn. Absent when the app declares no such server (the * common case — nothing is rendered); present with one entry per declared * OAuth server otherwise, `connected` ones included. */ mcpAvailability?: McpAvailability[]; /** * Content of the authenticated caller's persistent `MEMORY.md` file — * prompted memory's RECALL half (memory-mcp spec §4), read Router-side * BEFORE this invoke and pushed by value so recall never depends on the * model choosing to read a file. DISTINCT from `priorMemory`: that is * thread-scoped conversation memory folded from AgJSON (the persistence- * fold's `` push); this is the user's own cross-session, * cross-thread memory file at `$GUUEY_HOME_DIR/memories/MEMORY.md`. * FRAMEWORK-BLIND as of memory-mcp T5: present for an authenticated caller * when the memory MCP child is attached this pod (the Router's * `recallUserMemory` gate — `authenticated && memoryAttached`, the single * producer and canonical statement of this invariant), for ANY framework * (all three renderers consume it as the RECALL block). Absent for an * anonymous caller (never read), a memory-off / pre-rollout pod, or — the * bootstrap case — an authenticated caller on an attached pod with no memory * file YET (turn one): {@link memoryAttached} is still true, so the SAVE * instruction still renders (save-only) even though this is absent. */ userMemory?: string; /** * Whether the auto-injected memory MCP child booted with a ready port this * pod (memory-mcp T5) — the pod-boot constant the Router threads from * `SseServerOptions.memoryAttached`. When true, an AUTHENTICATED invoke has * the `save_memory` tool spliced (T4), so all three framework renderers emit * the SAVE instruction — independent of whether {@link userMemory} exists * (fixing the bootstrap gap: turn one, no file yet, the model must still be * told the tool exists). Absent/false → no memory tool → no SAVE instruction. * DISTINCT from {@link userMemory}, which gates only the RECALL block: the * SAVE gate is attachment, the RECALL gate is file presence. */ memoryAttached?: boolean; /** * The resolved cross-app profile access for THIS invoke (cross-app-profile * T6/T7) — the Router's fail-closed grant check (`resolveProfileForInvoke`) * clamps the honored grant to the app's declared posture and threads the * result here. Present ONLY for an authenticated caller with a live grant; * absent → no profile surface this turn. Gates BOTH halves of the profile * system-prompt section across all three frameworks: the `save_profile` SAVE * instruction renders ONLY when this is `"read-write"` (a `"read"` grant has * no write tool to name), and the RECALL block renders whenever * {@link profileSections} is present. Kept as an inline literal (NOT imported * from `@guuey/config`) to keep this package dependency-free. */ profileAccess?: "read" | "read-write"; /** * The user's cross-app profile sections for the RECALL push * (cross-app-profile T7). Each entry is one writing-app's section (plus, when * older sections were dropped to fit the 64 KiB recall budget, a leading * marker entry with `app: ""`). Pushed by value like {@link priorMemory}; the * worker renders them into the `` block. Absent → nothing to * recall. Gated by {@link profileAccess} (never rendered for a guest / an * ungranted app). */ profileSections?: ProfileSection[]; /** * The first-impression push (guuey#1183): a pre-minted ggui blueprint is * BOUND for exactly this turn — the greeting on a thread's first turn, or a * suggestion chip whose text the visitor just sent. The Router selects it * from the app's binding (`GuueyApp.gguiBootstrap`, written by the platform's * bootstrap door) and pushes it by value; the host renders ONE system-prompt * section instructing the model to open the turn with `ggui_handshake` * carrying `intent` + `blueprintDraft.contract` (and `variance` when the * binding is variance-named — guuey#1256), so ggui's exact-key match serves * the bound row. Absent → nothing to render. * Gated by {@link gguiAttached} in every renderer (no rail, no instruction). */ firstImpression?: FirstImpressionPush; /** * How many builder-provided reference files sit in the app layer's * `resources/` directory (`/resources`) this turn (guuey#456 B4). * The Router counts the directory at invoke-assembly time and writes the * field ONLY when the layers are REAL ({@link fsBound}) AND the count is * positive — an absent or empty directory (the normal state until the * builder uploads resources) rides NOTHING, mirroring {@link fsBound}'s * only-when-true pattern. Gates the host's framework-blind app-resources * system-prompt section (`@guuey/host`'s `renderResourcesSection`) on * `fsBound && resourceCount > 0` — the memory-mcp T5 lesson restated for * files: `fsBound` is the REAL "file tools are armed" signal, so the hint * can never name files the model has no tools to read. NOT auth-gated: * the app layer is shared, public-by-definition content. */ resourceCount?: number; /** * Whether this turn's ggui generative-UI rail is actually ARMED (guuey#630) * — the Router writes it only when the ggui server's credential was WRITTEN * for this invoke (`run-seam`'s guuey#407 `gguiCredentialWritten`), not when * the snapshot merely declares one. Gates the host's framework-blind * generative-UI system-prompt section (`@guuey/host`'s * `renderGenerativeUiSection`) — the memory-mcp T5 lesson restated for * renders: gate on the REAL "the tool is dialable" signal, so the section * can never tell the model to draw a card on a turn with no rail to draw it * on. Absent for a `ggui: false` opt-out, a swapped-in non-ggui server, a * broker failure, or a no-layers turn. NOT auth-gated: rendering is not a * privileged surface — guests get cards too. Mirrors {@link memoryAttached}'s * only-when-true shape, so "no rail" is always the ABSENT field on the wire. */ gguiAttached?: boolean; } /** Graceful termination (also signalled by stdin EOF). */ export interface Shutdown { type: "shutdown"; } export type ControlMessage = Invoke | Shutdown; /** Stream a chunk of assistant output. */ export interface TextEvent { type: "text"; text: string; } /** Terminal success. `result` is the turn's final text. */ export interface DoneEvent { type: "done"; stopReason: StopReason; result: string; } /** Terminal failure. */ export interface ErrorEvent { type: "error"; message: string; } /** * Pass-through carrier for framework-native SDK events (fd-3). * The Worker emits these opaquely; the Router dispatches to the matching * `@silverprotocol/` normalizer. * * `framework` is typed as `string` rather than the `AgentFramework` enum from * `@guuey/config` to keep this package dependency-free. The Router validates * `framework` against the real enum before dispatching. */ export interface NativeEvent { readonly type: "native"; /** The framework whose native event this is — Router picks the normalizer. */ readonly framework: string; /** One native SDK event, opaque JSON; only the Router's normalizer reads it. */ readonly event: JsonValue; } /** * Additive-optional in protocol v1 (the SDK-version handshake, model-release * playbook §8 item B). A worker MAY emit this once, before any native/turn * event, to report its own SDK provenance. The Router treats it as Router-plane * ONLY: never forwarded to the SSE client, never fed to a `@silverprotocol/*` * normalizer (it is not an AgJSON-eligible event) — just logged + carried into * the invoke's completion telemetry. Absence is fully tolerated (older workers * that predate this event, or a builder's own `serve()`-based worker that never * emits it): the Router simply has no SDK provenance to log for that invoke. */ export interface WorkerHelloEvent { readonly type: "hello"; /** The framework this worker runs — matches `NativeEvent.framework`. */ readonly framework: string; /** The SDK package name, e.g. `"@anthropic-ai/claude-agent-sdk"`; `null` when unknown/inapplicable. */ readonly sdkName: string | null; /** The SDK's installed version, resolved at RUNTIME (never a hardcoded literal); `null` when unresolvable. */ readonly sdkVersion: string | null; } export type WorkerEvent = TextEvent | DoneEvent | ErrorEvent | NativeEvent | WorkerHelloEvent; //# sourceMappingURL=protocol.d.ts.map