/** * Protocol-level types for TUI chrome steps and interactive selectors. * * Agent-AGNOSTIC: the *shapes* below are part of the chunk-protocol contract * between the CLI/IDE clients and the mobile app. The *parsers* that recognize * chrome lines and selectors live next to each agent's runtime strategy * (e.g. apps/cli/src/agents/claude/parsing.ts) because the glyphs and * conventions vary per agent. */ type ChromeToolType = 'read' | 'edit' | 'bash' | 'search' | 'thinking' | 'other'; interface ChromeStep { tool: ChromeToolType; label: string; detail?: string; status: 'running' | 'done'; } interface SelectPrompt { question: string; options: string[]; optionDescriptions: string[]; /** 0-based index of the highlighted item (always 0 for numbered selectors). */ currentIndex: number; } /** * Shared wire / lifecycle constants. The values here are bundled * into the CLI + VS Code extension at build time via tsup / esbuild * and mirrored in `apps/jetbrains-plugin/.../protocol/Constants.kt` * since Kotlin can't import an npm package. * * If you change one of these values, also update the Kotlin mirror. */ /** * Discriminated chunk-protocol version sent as the * `X-Codeam-Protocol-Version` header on every authed request. The * backend uses this to opt into legacy translations or to reject * with 426 when the client is too far behind. Bumped in lockstep * with chunk-shape changes (e.g. when the `chrome_steps` chunk * type is added). */ declare const PROTOCOL_VERSION: "2.0.0"; /** * The VS Code AgentOutputMonitor's loopback HTTP server bound to * 127.0.0.1 on this port — the observer JS in the IDE renderer * uses it to round-trip captured chat content back into the * extension host. The port is intentionally fixed (rather than * `listen(0)`) so the observer script can be a static constant * rather than dynamically rewriting itself per session. * * Multi-window collision is solved by listen(0) per-window in the * monitor (see #103); this default is still the documented * starting port for tooling that needs to probe whether a CodeAgent * Mobile session is active locally. */ declare const OBSERVER_BRIDGE_PORT = 47832; /** * Default plugin → backend heartbeat interval. User-configurable * via `codeagent-mobile.heartbeatIntervalMs` on VS Code and * `heartbeatIntervalMs` in SettingsService.kt's @State on JetBrains. * Mirrors the value the apps/api side uses to flip the paired * session to offline. */ declare const HEARTBEAT_INTERVAL_MS_DEFAULT = 30000; /** * SSE + polling reconnect cap. Vercel's serverless functions close * SSE connections after ~25 s by default; the client uses 35 s as * its overall socket timeout to leave a beat for graceful close. */ declare const SSE_SOCKET_TIMEOUT_MS = 35000; /** * Render raw PTY bytes into an array of screen lines using a simplified * virtual terminal. Handles cursor movements (A/B/C/D/G/H), erase (J/K), * alternate-screen (?1049h), carriage return, and LF. * * This is the authoritative implementation used by both codeam-cli (PTY * output) and the VS Code extension (shell-integration output) so that * the mobile/web client sees identical chunks regardless of surface. */ declare function renderToLines(raw: string): string[]; /** * The command envelope clients receive from the backend relay — both from * the `commands` SSE frames on `/api/commands/pending/stream` and from the * `GET /api/commands/pending` polling fallback. One schema, shared, so the * VS Code extension (and eventually the CLI) stop blind-casting * `Record` into this shape. */ interface RemoteCommand { id: string; sessionId: string; pluginId: string; type: string; payload: Record; status: string; createdAt: number; } /** * Validate a raw (already JSON-parsed) value into a `RemoteCommand`. * Returns `null` — never throws — on a malformed envelope so callers can * log-and-skip the single bad command without dropping the whole batch. */ declare function toRemoteCommand(raw: unknown): RemoteCommand | null; interface ModelPricing { input: number; output: number; cacheRead: number; cacheWrite: number; } declare const MODEL_PRICING: Record; declare const MODEL_CONTEXT_WINDOW: Record; /** True when the model id resolves to a real MODEL_PRICING row (i.e. getPricing * will NOT be guessing via the unknown-model fallback). */ declare function isKnownModel(model: string): boolean; /** * Flagged default for an unpriced model id. All-zero so an unknown model is * VISIBLY unpriced ($0) rather than silently MISPRICED at some other family's * rates (the old sonnet-4 fallback billed unknown ids — including a haiku id * that matched no row — at sonnet rates). `getPricing` returns this object for * unknown ids so callers that do unconditional arithmetic still work; callers * that must distinguish real pricing from the default check `isKnownModel`. */ declare const UNKNOWN_MODEL_PRICING: ModelPricing; /** * Resolve pricing by longest matching prefix. Unknown models resolve to the * flagged {@link UNKNOWN_MODEL_PRICING} default (all-zero, i.e. visibly * unpriced) instead of guessing at another model's rates. Callers that need to * distinguish real pricing from the default must check `isKnownModel(model)`. */ declare function getPricing(model: string): ModelPricing; declare function getContextWindow(model: string | null): number; /** * Context window ONLY when it's a confident match — `undefined` otherwise (no * default). Use where a wrong value is worse than none: the runtime model * selector maps native ACP model ids, many of which are opaque aliases * ("default", "opus") or proxied ids (a MiniMax-backed house agent) that aren't * in the catalog. Falling back to 200K for those printed a fake "200K context" * on every row; returning undefined lets the UI omit the sub-label instead. */ declare function tryGetContextWindow(model: string | null): number | undefined; type AgentId = 'claude' | 'codex' | 'copilot' | 'coderabbit' | 'cursor' | 'aider' | 'gemini' | 'kimi' | 'opencode'; type AgentAuthKind = 'oauth_token' | 'api_key' | 'setup_token'; /** * The agent kinds Headroom (the token-compression proxy) can actually * wrap/route — the exact subcommands `headroom init --global ` * accepts. NOT an alias of {@link AgentId}: cursor / gemini / aider run * native (Headroom disabled) because `headroom init` has no recipe that * routes them. */ type HeadroomKind = 'claude' | 'codex' | 'copilot'; interface AgentAuth { kind: AgentAuthKind; /** API key plain, or JSON serialized for oauth_token. Interpretation depends on the agent. */ value: string; } interface AgentModel { id: string; label: string; /** Optional — omitted when the model id isn't in the context-window catalog * (opaque native aliases / proxied ids). The UI hides the sub-label rather * than printing a misleading default. */ contextWindow?: number; pricing?: { inputPerM: number; outputPerM: number; cacheReadPerM?: number; cacheCreationPerM?: number; }; } /** * An agent operating/permission MODE — a DIFFERENT ACP axis than {@link AgentModel}. * Sourced from the native ACP `SessionModeState` on `newSession`/`loadSession` * (the standard `session/set_mode` write targets it), e.g. Claude's * default/plan/acceptEdits/bypass. Modes have neither pricing nor a context * window — just an id, a human label, and an optional description. */ interface AgentMode { id: string; label: string; description?: string; } interface NormalizedMessage { id: string; role: 'user' | 'agent' | 'system'; text: string; timestamp: string; modelId?: string; usage?: { input: number; output: number; cacheRead?: number; cacheCreation?: number; }; } interface AgentMetadata { id: AgentId; displayName: string; binaryName: string; enabled: boolean; supportedAuthKinds: AgentAuthKind[]; preferredAuthKind: AgentAuthKind; /** * Whether Headroom can wrap/route this agent (claude / codex / copilot * only). Canonical truth previously scattered across two prefix-matching * predicates: `isHeadroomSupportedAgent` (CLI `host-agent.ts`) and * `isHeadroomWrappableAgent` (api-v2 `codespaces/headroom.ts`). When * false the agent MUST run native — wrapping an unsupported agent * mislaunches it as Claude (the 2026-06 Cursor incident). */ headroomWrappable: boolean; /** * The `headroom init --global ` subcommand for this agent. * Present iff {@link headroomWrappable} is true. */ headroomKind?: HeadroomKind; /** * Whether the agent runs over ACP (Agent Client Protocol) in the CLI — * mirrors which agents have an entry in the CLI's ACP adapter registry * (`apps/cli/src/agents/acp/adapters.ts`). `false` ⇒ legacy PTY runtime. */ acp: boolean; /** * `true` for agents that authorize via the OAuth DEVICE-code flow * (Codex, Cursor) rather than the redirect/paste flow (Claude, Gemini). * Mirrors the mobile agent catalog (`apps/mobile/src/lib/agentCatalog.ts`). */ deviceFlow?: boolean; /** * Whether the device-code flow surfaces `userCode` to the user as an * "enter this code" string. `true` for Codex (a real human-typed * user_code shown on the OpenAI page). `false` for Cursor — there * `userCode` is the secret PKCE verifier used only for the poll * echo-back; rendering it would leak the secret. Only meaningful when * {@link deviceFlow} is `true`. */ showsUserCode?: boolean; } declare const AGENT_REGISTRY: Record; declare function getEnabledAgents(): AgentMetadata[]; declare function getAgent(id: AgentId): AgentMetadata; declare function isKnownAgentId(id: string): id is AgentId; /** * Agent identity — the ONE place the public (`LinkedAgentId`) and internal * (`AgentId`) id spaces are declared and bridged, plus the ONE alias * normalizer every surface funnels through. * * Canonical values consolidated from (Phase 2, PR-1): * - backend `apps/api-v2/src/linked-agents/agent-map.ts` * (`PUBLIC_TO_INTERNAL` / `INTERNAL_TO_PUBLIC` / `LinkedAgentId`), * - CLI `apps/cli/src/commands/host/agent-provisioning.ts` * (`PUBLIC_TO_INTERNAL_AGENT`), * - VS Code plugin `apps/vsc-plugin/src/utils/cli-agent-id.ts` * (marketplace aliases + `__terminal__:` strip), * - CLI `apps/cli/src/commands/start/handlers.ts` * (the `claude_code` → `claude` normalization), * - mobile `apps/mobile/src/lib/agent-id-map.ts`. */ /** Sentinel id for the synthetic "CodeAgent Cloud (incluido)" house agent. */ declare const HOUSE_AGENT_ID = "house-codeagent-cloud"; /** Internal provider discriminator for the house agent. */ declare const HOUSE_AGENT_PROVIDER = "codeagent_cloud"; /** White-label display strings — never mention the backend model. */ declare const HOUSE_AGENT_NAME = "CodeAgent Cloud"; declare const HOUSE_AGENT_VENDOR = "CodeAgent"; declare const HOUSE_AGENT_SUBTITLE = "Included \u2014 no setup"; /** * Public-facing linked-agent ids — the id space the `/api/agents/...` * endpoints and the mobile/web surfaces speak. The internal `AgentId` * (`'claude' | 'codex' | …`) is what the runtimes / provisioning key on. */ type LinkedAgentId = 'claude_code' | 'codex' | 'cursor' | 'aider' | 'coderabbit' | 'gemini' | 'kimi' | 'openrouter' | 'opencode' | typeof HOUSE_AGENT_ID; declare const LINKED_AGENT_IDS: readonly LinkedAgentId[]; declare function isLinkedAgentId(value: string): value is LinkedAgentId; /** * Every public id → internal `AgentId`. * * ⚠️ RECONCILED ASYMMETRY — this map is the UNION of what the two sides * historically accepted: * - The backend's `agent-map.ts` accepts only the `LinkedAgentId` union * (incl. the house agent, whose runtime is Claude Code) — no bare * `claude`, no `copilot` (there is no public copilot LinkedAgentId). * - The CLI's self-hosted `agent-provisioning.ts` additionally accepts * bare `'claude'` and `'copilot'` (deploy payloads have carried * already-internal ids), but not the house agent. * Consumers that must REJECT ids outside their own historical set keep * their own guard on top (e.g. `isLinkedAgentId`). */ declare const PUBLIC_TO_INTERNAL: Readonly>; /** * Internal → public. Partial: `copilot` has no public LinkedAgentId, and * `claude` maps back to `claude_code` (never the house agent — that * direction is intentionally lossy). */ declare const INTERNAL_TO_PUBLIC: Readonly>>; /** Resolve a public/linked id to the internal `AgentId`, or null. */ declare function publicToInternal(publicId: string): AgentId | null; /** Resolve an internal `AgentId` to its public `LinkedAgentId`, or null. */ declare function internalToPublic(internal: AgentId): LinkedAgentId | null; /** Prefix IDE plugins use for terminal-hosted agent ids. */ declare const TERMINAL_AGENT_PREFIX = "__terminal__:"; /** * THE agent-id normalizer. Collapses every known spelling of an agent id * (registry id, public `claude_code` form, marketplace extension id, * `__terminal__:`-prefixed plugin id — case/whitespace tolerant) onto the * internal `AgentId`, or `null` when unknown. * * Deliberately does NOT: * - gate on `enabled` (callers that need availability check the * registry — see the VS Code wrapper `normalizeCliAgentId`); * - map the house agent (that's a runtime substitution, not an alias — * use {@link publicToInternal}); * - fall back to anything. Unknown in → `null` out. */ declare function normalizeAgentId(raw: string): AgentId | null; /** * The `headroom init --global ` subcommand for an agent id, derived * from the registry's `headroomKind` flags — or `null` for unknown or * non-wrappable agents (cursor / gemini / aider / anything else). * * ⚠️ NEVER falls back to `'claude'`. The historical CLI fallback is how * the 2026-06 Cursor incident happened: an unsupported agent slipped * through, defaulted to `claude`, and `headroom wrap claude` launched * Claude Code instead of the user's agent. Callers that genuinely need a * default (e.g. picking an init subcommand AFTER the wrappable gate has * already passed) apply it themselves — see the CLI's * `agentIdToHeadroomKind` wrapper. * * Matching mirrors the historical predicates on BOTH sides (CLI * `isHeadroomSupportedAgent`, api-v2 `isHeadroomWrappableAgent`): * case-insensitive, `_`/`-` tolerant, prefix match — so `claude_code`, * `Claude-Code`, `codex_cli`, `copilot-cli` all resolve. */ declare function headroomKindFor(agentId: string): HeadroomKind | null; /** * Registry-derived replacement for the two scattered predicates * (`isHeadroomSupportedAgent` in the CLI, `isHeadroomWrappableAgent` in * api-v2). Accepts both id spaces (`claude_code` and `claude`). */ declare function isHeadroomWrappable(agentId: string): boolean; /** * Canonical per-agent CLI **install** snippets. * * ─── Why this file exists ──────────────────────────────────────────────────── * Until now the only copy of these shell recipes lived inside the backend's * codespace provisioning strategies — one `getInstallSnippet()` per * `…ProvisioningStrategy` in * `codeagent-mobile/apps/api-v2/src/codespaces/agent.ts`. That made them * unreachable from the clients repo, so the CLI could never be tested against * the REAL command a box actually runs: the `switch_agent` install path only * ever saw a snippet handed to it over the wire at runtime. * * That gap is exactly how the **stale-PATH / half-finished-install** failure * class (fleet-1, 2026-08-14) reached production: `npm install -g @openai/codex` * succeeded, but the long-running CLI daemon's PATH predated the npm * global-prefix bin dir, so the post-install probe reported "installed but its * binary never appeared on PATH" — forever. No CI test could have caught it, * because no CI test could run the real install. * * These strings are now **canonical HERE**. `apps/cli/__tests__/integration/ * agent-install.int.test.ts` runs each one for real, inside a container with a * deliberately minimal (systemd-like) PATH, and then drives the REAL adapter * probe from a single long-running node process. * * ─── api-v2 migration (deliberate follow-up, NOT done yet) ─────────────────── * The backend still owns its own copies. At the next `@codeam/shared` pin bump * in api-v2, change exactly ONE file — * * codeagent-mobile/apps/api-v2/src/codespaces/agent.ts * * — so every `…ProvisioningStrategy.getInstallSnippet()` returns * `INSTALL_SNIPPETS[]` imported from `@codeam/shared` instead of an * inline template literal. The `getAuthSnippet()` half stays in api-v2: it is * credential-bearing and must never be mirrored into a package the clients * bundle. * * ─── Invariants ────────────────────────────────────────────────────────────── * • **Install ONLY.** Every credential/auth line from the backend strategies is * deliberately excluded — no token, key, or secret substitution variable may * ever appear here (asserted by `__tests__/agents-install-snippets.test.ts`). * • **Idempotent.** Each snippet is `command -v`-guarded, so re-running it is * always safe — that is what makes the `switch-agent.ts` retry-once recovery * for the half-finished-bin-link class legitimate. * • **Verbatim.** These are byte-for-byte ports of the api-v2 snippets. Do not * "clean them up" independently — a divergence means a codespace and a * self-hosted switch install different things. * • `claude` is absent ON PURPOSE: its binary ships as an optional platform * dependency of `@anthropic-ai/claude-agent-sdk` (installed with the CLI * itself), so there is no separate install recipe to run. */ /** * The canonical install recipe per agent. `Partial` because not every agent has * one: `claude` ships its binary with the SDK, and `copilot` has no * provisioning strategy yet. */ declare const INSTALL_SNIPPETS: Readonly>>; /** * Agents whose canonical snippet actually installs something. Excludes * `coderabbit` (credential-only no-op — see {@link INSTALL_SNIPPETS}). * * This is the list the real-install integration gate iterates. */ declare function installableAgentIds(): AgentId[]; /** * True when a snippet performs no installation at all (a bare marker `echo`). * Callers that need a real binary must fall back to the agent's own CLI-side * installer instead of running the snippet. */ declare function isNoopInstallSnippet(snippet: string | undefined): boolean; /** * Agent Toolkits — integration wire types. * Spec: docs/superpowers/specs/2026-07-10-agent-toolkits-integrations-design.md * * Decision rule for the delivery rails: if the agent already masters a * ubiquitous CLI for the tool → `cliEnv`; otherwise → `mcp`. A tool may * declare both. */ type IntegrationId = 'jira' | 'sentry' | 'linear' | 'slack' | 'notion' | 'azure_devops' | 'figma' | 'microsoft_teams' | 'google_chat' | 'discord' | 'resend' | 'posthog' | 'datadog' | 'stitch' | 'github_issues' | 'github' | 'gitlab' | 'vercel' | 'trello' | 'clickup' | 'n8n' | 'postman' | 'mixpanel' | 'supabase' | 'convex' | 'confluence' | 'cloudflare'; /** * `derived` = no link flow of its own: the credential is BORROWED live from a * connection the user already made elsewhere (see `derivedFrom`). The backend * resolves it per call instead of vaulting a copy, so it can never go stale and * there is exactly one source of truth. Such an integration is "linked" iff its * source connection exists, and has no Connect/Disconnect action of its own. */ /** * `connection` = the integration IS a standing connection whose credential lives * in a `ProviderToken` row rather than the integrations vault, and whose * connect/disconnect flow is owned by ANOTHER module (GitHub's OAuth belongs to * the codespaces rail). It renders and acts like any other connector — the only * difference is which code performs the link. * * Contrast `derived`, which BORROWS such a connection and owns no actions at * all. Both resolve their credential from the same place; only `derived` has no * link flow of its own. */ type IntegrationAuthKind = 'oauth_redirect' | 'oauth_device' | 'api_key' | 'derived' | 'connection'; /** * A standing connection whose credential lives in a `ProviderToken` row — either * because the integration IS that connection (`kind: 'connection'`) or because * it borrows it (`kind: 'derived'`). Its own id doubles as the source name. */ type DerivedCredentialSource = 'github'; /** Grouping used by category-driven surfaces (Start-from-Work-Item picker, * catalog sections). A future tracker integration joins those features with * ZERO feature code — they resolve sources from the registry by category. */ type IntegrationCategory = 'version_control' | 'tracker' | 'design' | 'comms' | 'docs' | 'observability' | 'infrastructure' | 'automation' | 'analytics' | 'api_tools' | 'database'; /** * One user-entered field for an `api_key` integration (no browser OAuth — the * user pastes credentials directly, e.g. a PAT + an org URL). The mobile form * is driven entirely by this list, and `key` maps to a `BrokeredIntegrationToken` * field the delivery `envMapping` references. */ interface IntegrationApiKeyField { /** Maps to a BrokeredIntegrationToken field (`accessToken`, `orgUrl`, * `appKey`, `host`, …). */ key: 'accessToken' | 'orgUrl' | 'appKey' | 'host' | 'apiKey' | 'instanceUrl' | 'secretKey' | 'projectId'; /** Form label. */ label: string; /** Example / placeholder. */ placeholder?: string; /** Masked (secret) input — true for the token, false for a plain URL. */ secret?: boolean; /** Short help under the field (e.g. how to create the PAT). */ help?: string; } type IntegrationHealth = 'ok' | 'expired' | 'revoked'; /** stdio MCP server spec, executed as DATA by the CLI shim (`codeam mcp-run `). * * Two transports: * - **stdio (default):** the shim spawns `command args` (with `envMapping` * credentials in the env) and byte-pipes it to the agent. * - **HTTP (`httpUrl` set):** the shim connects to a REMOTE MCP over * Streamable HTTP and relays it to the agent's stdio — for vendors that only * ship a hosted MCP (e.g. PostHog `mcp.posthog.com`). `command`/`args` are * unused (empty); credentials go in `httpHeaders` (never `envMapping`). */ interface IntegrationMcpDelivery { command: string; args: string[]; /** env var name → credential field (`accessToken` | `cloudId` | …). Env only, never argv. STDIO transport. */ envMapping: Record; /** Static, non-credential env the server needs to boot (e.g. mode flags). * Merged into the child env BENEATH the credential envMapping. Never secrets. STDIO transport. */ staticEnv?: Record; /** HTTP transport — a REMOTE MCP URL (Streamable HTTP). When set, the shim * relays to it instead of spawning `command`. May carry `{field}` placeholders * filled from the BrokeredIntegrationToken (e.g. a per-user regional host: * `https://mcp.{host}/…`). */ httpUrl?: string; /** HTTP transport — header name → value TEMPLATE with `{field}` placeholders * filled from the BrokeredIntegrationToken at spawn (e.g. * `{ Authorization: 'Bearer {accessToken}' }` or Datadog's * `{ 'DD-API-KEY': '{accessToken}', 'DD-APPLICATION-KEY': '{appKey}' }`). The * token stays server-side of the shim — never on argv. */ httpHeaders?: Record; /** BUILT-IN transport — a codeam-authored MCP server (not a spawned `command`, * not a remote `httpUrl` relay). The shim dispatches to the named internal * server, which fulfils the tools itself with the brokered credential. * `command`/`args`/`envMapping` are empty. Used by Convex (`'convex-admin'`), * whose own MCP rejects every headless credential — we serve its tools * against the deployment admin REST API instead. */ builtin?: 'convex-admin'; } interface IntegrationDelivery { mcp?: IntegrationMcpDelivery; /** env var name → credential field, merged into agent child spawns. No MVP consumer. */ cliEnv?: Record; } interface IntegrationDefinition { id: IntegrationId; name: string; icon: string; category: IntegrationCategory; enabled: boolean; /** A `comms` integration that only SENDS (e.g. Resend email) — it has no * readable threads, so it is EXCLUDED from From-Conversation's source list * (which needs `listRecentThreads`/`getThread`/`postReply`) while still being * a linkable comms tool the agent uses via its MCP delivery. Absent/false for * conversational comms (Slack, Discord). */ sendOnly?: boolean; auth: { kind: IntegrationAuthKind; scopes?: string[]; /** For `kind: 'api_key'` — the credential fields the user pastes (no OAuth). */ fields?: IntegrationApiKeyField[]; /** For `kind: 'derived'` — the connection whose credential this borrows. * Drives both the backend's credential resolution and the client's * "no connect action, managed by " rendering. */ derivedFrom?: DerivedCredentialSource; /** For `kind: 'connection'` — the connection this integration IS. Same * credential lookup as `derivedFrom`, but this one owns its link flow * (performed by the module that owns the connection, not by the generic * integrations OAuth path). */ connection?: DerivedCredentialSource; /** For `kind: 'derived'` — the OTHER integration whose vaulted credential * this one reuses (Confluence → `jira`, since one Atlassian OAuth + the one * `mcp-atlassian` server serves both). Like `derivedFrom` but the source is * a vault-backed integration, not a `ProviderToken` connection: the backend * resolves the credential by delegating to the source integration, and the * client renders it as "part of " with no Connect action of its own. */ aliasOf?: IntegrationId; /** SECONDARY api-key / PAT path for a PRIMARY OAuth integration. OAuth * (`kind`) stays the primary/preferred flow; this lets the user paste a * personal access token instead — a fallback while the vendor's OAuth app * is in review, or a permanent alternative (like GitHub OAuth + PAT). * ⚠️ The PAT often needs a DIFFERENT delivery env var than the OAuth token * (e.g. Figma: OAuth → `FIGMA_OAUTH_TOKEN`/Bearer, PAT → * `FIGMA_API_KEY`/`X-Figma-Token`), so it carries its OWN `envMapping` used * INSTEAD of `delivery.mcp.envMapping` when the credential was linked via PAT. */ apiKeyFallback?: { fields: IntegrationApiKeyField[]; envMapping: Record; }; }; delivery: IntegrationDelivery; } /** What a deploy writes to `~/.codeam/integrations.json` — manifests, never secrets. */ interface IntegrationsManifestEntry { id: IntegrationId; delivery: IntegrationDelivery; } interface IntegrationsManifest { integrations: IntegrationsManifestEntry[]; } /** `GET /api/integrations` row: registry definition merged with the user's link state. */ interface IntegrationStatus { id: IntegrationId; linked: boolean; health?: IntegrationHealth; siteUrl?: string; accountEmail?: string; linkedAt?: string; } /** `POST /api/plugin/integrations/:id/token` response — ~1 h access token, never a refresh token. */ interface BrokeredIntegrationToken { accessToken: string; expiresAt: string; /** Atlassian cloud id — the site the Jira MCP server targets. */ cloudId?: string; /** Sentry host (e.g. `sentry.io`, or a self-hosted domain) — the API base * the Sentry MCP server targets. Absent for integrations that don't need * a host discriminator. */ host?: string; /** Slack workspace/team id (`T…`) — the Slack MCP server needs it alongside * the bot token (`SLACK_TEAM_ID`). Absent for non-Slack integrations. */ teamId?: string; /** Azure DevOps organization URL (`https://dev.azure.com/`) — the ADO * MCP server needs it alongside the PAT (`AZURE_DEVOPS_ORG_URL`). Absent for * non-ADO integrations. */ orgUrl?: string; /** Discord guild (server) id the app's bot was invited into for this user — * the `mcp-discord` server scopes to it (`DISCORD_GUILD_ID`). ⚠️ Discord OAuth * gives NO per-install token (unlike Slack): `accessToken` here is the app's * single BOT token, injected by the broker from config — the per-user * credential IS the guildId. Absent for non-Discord integrations. */ guildId?: string; /** Datadog Application key (user-scoped) — sent alongside the API key * (`accessToken`) as the `DD-APPLICATION-KEY` header; `host` carries the * regional site. Absent for non-Datadog integrations. */ appKey?: string; /** Trello developer API key — the app-level key the Trello MCP server needs * alongside the user token (`TRELLO_API_KEY` + `TRELLO_TOKEN`=`accessToken`). * Absent for non-Trello integrations. */ apiKey?: string; /** n8n instance base URL (self-hosted or n8n.cloud) — the MCP server needs it * alongside the API key (N8N_API_URL + N8N_API_KEY). Absent for non-n8n. */ instanceUrl?: string; /** Secret half of a 2-part api_key credential (Amplitude secret, Mixpanel * service-account password, etc.). Absent for single-key integrations. */ secretKey?: string; /** Project/workspace id some analytics APIs require (Mixpanel project id). */ projectId?: string; /** Cloudflare account id — the Cloudflare MCP needs it alongside the OAuth * access token (`CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN`) for * account-level operations. Captured at link time from `GET /accounts`. * Absent for non-Cloudflare integrations. */ accountId?: string; } /** * The single source of truth for supported integrations. Adding one = * 1 entry here + 1 backend OAuth provider + icon. The `delivery` spec is * resolved into deploy manifests and executed as data by the CLI, so a new * MCP integration with no special logic needs no CLI release. */ declare const INTEGRATION_REGISTRY: Record; declare function getEnabledIntegrations(): IntegrationDefinition[]; declare function getIntegration(id: IntegrationId): IntegrationDefinition; declare function isKnownIntegrationId(id: string): id is IntegrationId; declare function getIntegrationsByCategory(category: IntegrationCategory): IntegrationDefinition[]; /** * Agent Toolkits — centralized integration branding catalog. * Spec: docs/superpowers/specs/2026-07-10-agent-toolkits-integrations-design.md * * Shared is pure TS (no React, no platform imports), so this catalog is DATA: * raw SVG markup strings + display metadata. Renderers stay per-app (RN * `SvgXml` on mobile, inline/`` on web) — this module never renders * anything itself. * * `logoSvg` values are the OFFICIAL brand marks. jira/slack are the * multicolor originals (from the vendor). Every other entry (the 6 live * integrations' single-path marks + the whole COMING SOON set) is a * simple-icons single-path mark that ships with a black fill by default — * that fill has been rewritten here to #FFFFFF so the mark reads on the * dark surfaces this catalog targets; consumers may re-tint via * `brandColor` (e.g. an SVG ``/currentColor wrapper) if a different * treatment is needed. `pendo` + `amplitude` are NOT in simple-icons * (brand-guideline restrictions) so they carry faithful hand-authored * monochrome marks in the same 24×24 single-path shape. */ interface IntegrationBranding { /** Stable id — registry ids ('jira') plus upcoming ones not yet in IntegrationId. */ id: string; name: string; vendor: string; /** One-line value prop shown under the name. */ tagline: string; /** Brand accent for tinted containers/pills on dark surfaces. */ brandColor: string; /** Official logo as raw SVG markup (renderers: SvgXml on RN, inline/img on web). */ logoSvg: string; } declare const INTEGRATION_BRANDING: Record; declare const UPCOMING_INTEGRATION_IDS: readonly ["gmail", "asana", "stripe", "pendo", "pagerduty", "amplitude"]; declare function getIntegrationBranding(id: string): IntegrationBranding | null; /** * PR / MR Command Center — pull-request wire types (canonical home). * * Spec: docs/superpowers/specs/2026-07-18-pr-mr-command-center-design.md (§5, §6, §9, §10). * * These are the frozen shapes the backend `vcs` engine, the mobile PR panel, * and the CLI agent-review handler all agree on byte-for-byte. Pure data — no * platform imports. v1 is GitHub-only, but every shape is vendor-neutral so a * future GitLab/Bitbucket provider reuses them with ZERO panel/type change. */ /** A stable, cross-vendor reference to one pull/merge request. */ interface PrRef { /** Repo owner / org (GitHub `owner`). */ owner: string; /** Repo name (GitHub `repo`). */ repo: string; /** PR number within the repo. */ number: number; /** Canonical web URL, when known (e.g. `https://github.com/o/r/pull/12`). */ url?: string; } /** The three review actions a reviewer (human or agent) can submit. */ type PrReviewVerdict = 'approve' | 'request_changes' | 'comment'; /** One CI check / status on a PR head commit (GitHub Checks + commit statuses). */ interface PrCheck { name: string; /** GitHub check-run status. */ status: 'queued' | 'in_progress' | 'completed'; /** Conclusion once `status === 'completed'`; null/absent while running. */ conclusion?: 'success' | 'failure' | 'neutral' | 'cancelled' | 'skipped' | 'timed_out' | 'action_required' | null; /** Deep link to the check's details page. */ detailsUrl?: string; } /** One existing review already on the PR (shown in the detail screen). */ interface PrReviewEntry { /** Reviewer login. */ author: string; state: 'approved' | 'changes_requested' | 'commented' | 'dismissed' | 'pending'; submittedAt?: string; body?: string; } /** * A row in the PR Command Center panel (Review Requested / Your Open PRs). * Pulled on-open via the GitHub Search API — never polled. */ interface PullRequestSummary { ref: PrRef; title: string; number: number; /** Author login. */ author: string; state: 'open' | 'closed' | 'merged'; isDraft?: boolean; /** Head → base branch names. */ headBranch: string; baseBranch: string; updatedAt: string; /** `owner/repo`. */ repoFullName: string; /** Aggregate review decision (GitHub `reviewDecision`). */ reviewDecision?: 'approved' | 'changes_requested' | 'review_required' | null; additions?: number; deletions?: number; changedFiles?: number; } /** The full PR detail screen payload — summary + body + checks + reviews. */ interface PullRequestDetail extends PullRequestSummary { body?: string; checks?: PrCheck[]; reviews?: PrReviewEntry[]; /** GitHub mergeability; null while GitHub is still computing it. */ mergeable?: boolean | null; /** Role flags the mobile UI uses to pick the action set (author vs reviewer). */ viewerIsAuthor?: boolean; viewerIsRequestedReviewer?: boolean; } /** One finding an agent surfaced during a review (mirrors a CodeRabbit hunk). */ interface AgentReviewFinding { /** Repo-relative file path. */ path: string; /** 1-based line the finding anchors to, when locatable. */ line?: number; severity?: 'info' | 'warn' | 'error'; message: string; } /** * Phase-2 agent-review LAUNCH plan — what the backend composes when the user * taps "Ask an agent to review PR #X". Delivered to the review session so the * agent (ACP via initial prompt, or CodeRabbit via the CLI handler) knows what * to review and which toolkits it has. */ interface AgentReviewPlan { prRef: PrRef; /** Which linked agent performs the review (`coderabbit`, `claude`, …). */ agentId: string; /** The composed "review PR #X" initial prompt (ACP agents consume this). */ prompt: string; /** `owner/repo` (or a provider-specific project identifier). */ repoIdentifier: string; /** The PR head branch the deploy checks out and the review runs against. */ branch: string; /** Toolkit integrations the review box is provisioned with (includes `github`). */ integrationIds: string[]; /** Curated skill id(s) attached to this session's purpose (e.g. `code-review`). * Claude gets them as `~/.codeam/skills.json`; other agents get the instruction * preamble prepended to `prompt` server-side. The client forwards these into the * deploy request the same way as `integrationIds`. */ skillIds: string[]; } /** * Phase-2 agent-review RESULT — POSTed by the review session to * `POST /api/vcs/agent-review/report` once the agent finishes posting to * GitHub. Drives the completion push + Completion Result card. */ interface AgentReviewReport { prRef: PrRef; agentId: string; /** The verdict the agent submitted to GitHub. */ verdict: PrReviewVerdict; /** Number of inline comments the agent posted to GitHub. */ commentCount: number; /** The findings behind the verdict (rendered in the Completion Result card). */ findings?: AgentReviewFinding[]; } type RepoStack = 'frontend' | 'backend' | 'fullstack' | 'mobile' | 'unknown'; /** The Session Tools stack-detection result (wire type, carried by the * SESSION_STACK_DETECTED event). `source` distinguishes the deterministic * dependency scan from the agent one-shot fallback. */ interface RepoStackDetection { stack: RepoStack; /** Integrations the repo's dependencies directly evidence (high confidence). */ detected: IntegrationId[]; /** Integrations commonly paired with the classified stack, minus `detected`. */ recommended: IntegrationId[]; source: 'scan' | 'agent'; } /** Exact dependency name → integration (across ecosystems: npm, pip, go, gem…). */ declare const DEP_TO_INTEGRATION: Record; /** Classify the repo stack from its dependency NAMES. */ declare function classifyStack(depNames: string[]): RepoStack; /** Integrations directly evidenced by the repo's dependencies (deduped, stable order). */ declare function detectedIntegrationsFromDeps(depNames: string[]): IntegrationId[]; /** Commonly-paired integrations per classified stack (the curated inference layer). */ declare const STACK_TO_RECOMMENDED: Record; /** * The deterministic core of Session Tools "Recommended": classify the stack and * combine direct-evidence detections with stack inference. `recommended` * excludes anything already `detected`. Returns `source:'scan'`. When both * `detected` and `recommended` are empty (unrecognized stack), the CLI falls * back to the agent one-shot (B). */ declare function recommendForDeps(depNames: string[]): RepoStackDetection; /** Curated skills shipped with the client. Grows over time. */ type SkillId = 'code-review' | 'resolve-conflicts' | 'spec-driven-development' | 'code-naming'; /** Delivery rails, twin of IntegrationDelivery's mcp/cliEnv. */ type SkillRail = 'skillFile' | 'instruction'; /** `skillFile` payload — a Claude-Code SKILL.md bundle (body + optional files). */ interface SkillFileDelivery { /** The SKILL.md markdown body (WITHOUT frontmatter — frontmatter is generated * at materialize time with `name` set to the namespaced skill id `codeam-` and * `description` from the skill definition). */ body: string; /** Extra files written alongside SKILL.md: relative path → file contents. */ files?: Record; } interface SkillDelivery { skillFile?: SkillFileDelivery; /** Agent-agnostic instruction body, prepended to the composed prompt by the * backend for agents that do not get the skillFile rail. */ instruction?: { body: string; }; } interface SkillDefinition { id: SkillId; name: string; /** One-line; becomes the SKILL.md frontmatter `description` (progressive * disclosure — this is what sits in the model's context until invoked). */ description: string; /** MVP: all curated. 'user' is added with the library fast-follow. */ source: 'curated'; delivery: SkillDelivery; } /** What a deploy writes to `~/.codeam/skills.json` — ids only, never content. */ interface SkillsManifestEntry { id: SkillId; } interface SkillsManifest { skills: SkillsManifestEntry[]; } declare const SKILL_REGISTRY: Record; declare function isSkillId(id: string): id is SkillId; declare function getSkillDefinition(id: string): SkillDefinition | null; declare function skillHasRail(id: SkillId, rail: SkillRail): boolean; /** * The always-on **Agent Standard** — baseline working + safety guidance injected * into EVERY managed deployed session, for ALL agents. This is NOT a curated * skill: it is deliberately absent from `SKILL_REGISTRY` and the skills picker, * so it is product-level baseline behavior the user can't accidentally turn off * (curated skills, by contrast, are opt-in). Single source of the text — the CLI * delivers it two ways, split on the Claude rail: Claude gets a marker-guarded * append to `~/.claude/CLAUDE.md` at spawn (always in context); every other ACP * agent gets a one-time preface on the first turn of a new conversation. * * Repo-agnostic on purpose: it governs how the agent works on the USER's own * project, so it must never mention CodeAgent-internal workflow (issue tracker, * our branch/deploy rules, our infrastructure). */ /** Idempotency marker wrapping the block appended to an agent's instruction file. */ declare const AGENT_STANDARD_MARKER = ""; /** The standard, clean markdown (no markers) — used verbatim as a prompt preface. */ declare const AGENT_STANDARD_TEXT = "# Working standard\n\nYou are an AI coding agent working on the user's project through CodeAgent Mobile. Follow this standard on every task.\n\n## How to work\n- **Understand before acting.** Restate the goal, read the relevant code, and be clear on what \"done\" looks like before changing anything.\n- **Plan first for anything non-trivial** (3+ steps or a design decision): outline the approach and the files you'll touch, and share it before implementing. Skip the ceremony for small, obvious fixes.\n- **Ground every claim in reality** \u2014 the actual code, tests, or output, never guesswork. If you are unsure, say so and verify.\n- **Ask when intent is genuinely ambiguous** \u2014 one sharp question. At a real fork, give a recommendation, not a survey of every option.\n- **Stay in scope.** Solve what was asked; no \"while I'm here\" refactors or speculative abstractions. Note unrelated issues instead of acting on them.\n- **Favor the simplest solution that fully solves the problem.** Fix root causes, not symptoms \u2014 no temporary patches and no defensive code for cases that can't happen.\n- **Verify your work and show the evidence** \u2014 run the project's tests, linters, and build, and read the output. \"It runs\" is not \"it's done.\"\n- **Match the project's existing style, structure, and conventions.** Comment only the non-obvious WHY, briefly \u2014 don't narrate the code.\n- **Stop when stuck.** If the same fix fails twice, step back and reconsider the approach rather than repeating variations.\n\n## Safety\n- **Never expose or exfiltrate** secrets, credentials, tokens, or customer data, and never print a credential's value.\n- **Treat destructive or irreversible actions as needing explicit confirmation** \u2014 force-push, history rewrite, bulk deletes, hard resets, dropping data. Don't run them unprompted.\n- **Don't push to a shared/default branch or make outward-facing changes** unless the user asked for it.\n- **Report honestly when you finish**: what you changed, what you verified, and anything you could not."; /** Marker-wrapped block for an idempotent append to an agent's instruction file. */ declare const AGENT_STANDARD_BLOCK = "\n# Working standard\n\nYou are an AI coding agent working on the user's project through CodeAgent Mobile. Follow this standard on every task.\n\n## How to work\n- **Understand before acting.** Restate the goal, read the relevant code, and be clear on what \"done\" looks like before changing anything.\n- **Plan first for anything non-trivial** (3+ steps or a design decision): outline the approach and the files you'll touch, and share it before implementing. Skip the ceremony for small, obvious fixes.\n- **Ground every claim in reality** \u2014 the actual code, tests, or output, never guesswork. If you are unsure, say so and verify.\n- **Ask when intent is genuinely ambiguous** \u2014 one sharp question. At a real fork, give a recommendation, not a survey of every option.\n- **Stay in scope.** Solve what was asked; no \"while I'm here\" refactors or speculative abstractions. Note unrelated issues instead of acting on them.\n- **Favor the simplest solution that fully solves the problem.** Fix root causes, not symptoms \u2014 no temporary patches and no defensive code for cases that can't happen.\n- **Verify your work and show the evidence** \u2014 run the project's tests, linters, and build, and read the output. \"It runs\" is not \"it's done.\"\n- **Match the project's existing style, structure, and conventions.** Comment only the non-obvious WHY, briefly \u2014 don't narrate the code.\n- **Stop when stuck.** If the same fix fails twice, step back and reconsider the approach rather than repeating variations.\n\n## Safety\n- **Never expose or exfiltrate** secrets, credentials, tokens, or customer data, and never print a credential's value.\n- **Treat destructive or irreversible actions as needing explicit confirmation** \u2014 force-push, history rewrite, bulk deletes, hard resets, dropping data. Don't run them unprompted.\n- **Don't push to a shared/default branch or make outward-facing changes** unless the user asked for it.\n- **Report honestly when you finish**: what you changed, what you verified, and anything you could not.\n"; /** * Native ACP guardrails — the shared policy model. * * A guardrail is a per-category disposition applied in the ACP client to a * deployed agent's tool calls: `deny` (block), `confirm` (surface a tappable * approve/deny on mobile), or `off`. Default-on, configurable per session. * * ⚠️ SOFT guardrail, NOT a security boundary — it only sees tool calls the * agent routes through the ACP client (permission requests + delegated * fs read/write). An in-process tool call, a `bypassPermissions` agent (no * permission requests), or a PTY agent (aider, bypasses ACP) slips it. Real * containment is server-side (scoped, revocable tokens; per-user containers). * The UI must present it as a safety net, never as a hard boundary. * * Spec: docs/superpowers/specs/2026-08-08-native-acp-guardrails-design.md. */ type GuardrailDisposition = 'deny' | 'confirm' | 'off'; type GuardrailCategory = 'secretRead' | 'destructiveShell' | 'protectedBranch' | 'outwardIrreversible'; type GuardrailPolicy = Record; /** Stable order for UI rows + iteration. */ declare const GUARDRAIL_CATEGORIES: readonly GuardrailCategory[]; declare const GUARDRAIL_DISPOSITIONS: readonly GuardrailDisposition[]; /** Default-on: safe by default (everything asks) but nothing hard-blocked, so a * legitimate action is one tap away rather than a wall. */ declare const DEFAULT_GUARDRAIL_POLICY: GuardrailPolicy; interface GuardrailCategoryMeta { id: GuardrailCategory; /** Short label for a settings row. */ label: string; /** One line describing what it catches — user-facing. */ description: string; } /** Single source for the mobile settings copy + the backend/agent block reason. */ declare const GUARDRAIL_CATEGORY_META: Record; declare function isGuardrailDisposition(x: unknown): x is GuardrailDisposition; /** * Coerce an untrusted value (a `~/.codeam/guardrails.json` blob, a wire payload, * a partial policy) into a complete policy, falling back to the default per * category. Absent/garbage → the full default (default-on). */ declare function normalizeGuardrailPolicy(raw: unknown): GuardrailPolicy; /** The wire command that pushes a live policy update to a running session. */ declare const GUARDRAIL_CONFIGURE_COMMAND = "guardrail_configure"; /** Curated packs shipped with the client. Grows over time. */ type PackId = 'quick-pack' | 'full-pack'; /** A role inside a pack — one pipeline stage. */ interface PackStageDef { /** Stable role key (also the commit byline: `By .`). */ role: string; /** Display name for the pipeline UI. */ name: string; /** One line: what this specialist does — shown on the pack card. */ description: string; /** Curated skills mounted for this stage (skillFile rail, best-effort). */ skillIds: string[]; /** The full role prompt sent (with the pack workflow article + task + * previous handoff) as the stage's opening prompt. Read-only in the app. */ prompt: string; /** * Whether this stage MUST end in a new commit to hand off. Defaults to `true` * (undefined = true) — a Specifier/Coder/QA produces an artifact (spec, code, * report) and a no-commit stage is a stall. Set `false` for review-style * stages that legitimately approve clean with NO change: a Reviewer that finds * nothing to fix should hand off "reviewed, no changes needed" against the * commit it reviewed, not stall the pipeline. (Agent Packs live-run finding, * 2026-08-08 — a clean Reviewer was mis-treated as "stage produced no commit".) */ requiresCommit?: boolean; } interface PackDefinition { id: PackId; name: string; /** One line for the pack card. */ tagline: string; /** Plan gate — 'free' or 'pro' (enforced backend-side on pack_start). */ gate: 'free' | 'pro'; stages: PackStageDef[]; } type PackRunStatus = 'running' | 'paused' | 'stalled' | 'completed' | 'aborted' | 'failed'; type PackStageStatus = 'pending' | 'active' | 'done' | 'failed' | 'skipped'; /** Mechanically captured proof of what a stage delivered. */ interface PackHandoffRecord { /** Canonical 10-hex commit abbreviation (git-validated, never model-claimed). */ commit: string; /** Short summary of the stage's reply (first lines, capped). */ summary: string; /** `git diff --stat` summary line between the stage's start and end commits. */ diffStat: string; /** Project checks captured at the stage boundary, when a command was available. */ checks?: { command: string; passed: boolean; tail: string; }; durationMs: number; } interface PackStageState { role: string; name: string; status: PackStageStatus; /** ACP conversation id for this stage — mobile deep-links the stage chat. */ conversationId?: string; handoff?: PackHandoffRecord; /** Populated when status === 'failed' (or the run stalled on this stage). */ error?: string; } interface PackRunState { runId: string; packId: PackId; /** The user's task, verbatim. */ task: string; status: PackRunStatus; /** Index into `stages` of the stage currently active/next. */ currentStage: number; stages: PackStageState[]; /** Set when status is 'stalled' | 'failed' — the honest reason. */ stalledReason?: string; startedAt: string; updatedAt: string; } /** Relay command: start a pack run on the session. */ interface PackStartPayload { packId: PackId; task: string; } type PackActionKind = 'pause' | 'resume' | 'retry_stage' | 'skip_stage' | 'abort'; /** Relay command: mutate the active run. */ interface PackActionPayload { action: PackActionKind; } declare const PACK_START_COMMAND = "pack_start"; declare const PACK_ACTION_COMMAND = "pack_action"; declare const PACK_STATUS_COMMAND = "pack_status"; /** * The curated pack registry — same model as SKILL_REGISTRY: bundled content * selected by id. Adding a pack = a stages list here + widening `PackId`. * Role prompts are shared building blocks (roles.ts); a pack is a pipeline * of roles. v1 ships two presets; the custom builder is a fast-follow. */ declare const PACK_REGISTRY: Record; declare function isPackId(id: string): id is PackId; declare function getPackDefinition(id: string): PackDefinition | null; /** * Curated role prompts — shared across packs. Each is the specialist's full * working brief: mission, method, and the handoff bar it must clear. Kept * role-scoped and repo-agnostic; the pipeline rules ride separately * (PACK_WORKFLOW_ARTICLE) and the task + previous handoff are appended by the * runner at stage start. */ declare const SPECIFIER_PROMPT = "# Role: Specifier\n\nYou turn the user's task into a precise, testable specification the rest of the pipeline implements against. You do NOT write implementation code.\n\nMethod:\n1. Read the task and explore the relevant parts of the codebase until you understand the real problem, the desired outcome, and the constraints the code imposes.\n2. Write the specification to `SPEC.pack.md` at the repo root:\n - **Problem** \u2014 what is wrong or missing, and for whom.\n - **Outcome** \u2014 what must be true when this is done.\n - **Acceptance criteria** \u2014 a numbered checklist of observable, testable conditions. Each criterion must be verifiable by a test or a concrete manual check. They must fully cover the outcome.\n - **Out of scope** \u2014 what this task deliberately does not touch.\n - **Verification plan** \u2014 for each criterion, the level that proves it (unit / integration / manual) and why.\n3. Right-size: if the task is clearly too large for one pipeline run, narrow the criteria to a coherent first slice and record the rest under \"Out of scope / next\".\n\nHandoff bar: the spec file is committed; every acceptance criterion is testable as written; a competent implementer could start without asking you anything."; declare const CODER_PROMPT = "# Role: Coder\n\nYou implement the task with test-driven discipline. You are the only stage that adds behavior.\n\nMethod:\n1. Read the task \u2014 and `SPEC.pack.md` if a Specifier stage produced one; its acceptance criteria are your contract. Without a spec, derive the minimal criteria from the task itself before coding.\n2. Test-first where it fits: write the test that proves a criterion, watch it fail, implement until it passes. Where strict test-first doesn't fit, still land tests alongside the change.\n3. Match the project's existing style, structure, and conventions. Simplest design that fully solves the problem \u2014 no speculative abstractions, no \"while I'm here\" changes.\n4. Run the project's tests / linters / build and make them pass.\n\nHandoff bar: every acceptance criterion is implemented and covered by a test; the project's checks pass; the work is committed in focused commits."; declare const REVIEWER_PROMPT = "# Role: Reviewer\n\nYou are a skeptical senior reviewer with fresh eyes \u2014 you did NOT write this code, and your job is to find what's wrong, not to approve it. You also own architectural cleanliness for this change.\n\nMethod:\n1. Read the task, `SPEC.pack.md` (when present), and the diff of the pipeline's commits (`git log` + `git diff` against the state before the pipeline's first commit). Read enough surrounding code to judge in context.\n2. Audit, in priority order:\n - **Correctness** \u2014 logic, edge cases, error paths. For each acceptance criterion: point to the test that proves it, and check the test would FAIL if the behavior broke.\n - **Scope** \u2014 anything beyond the task is flagged and reverted unless it is load-bearing.\n - **Design** \u2014 duplication, dead code, needless complexity, dependency direction, encapsulation. Verify every API/library call actually exists in the project's dependencies.\n - **Conventions & naming** \u2014 matches the surrounding code; names say what things are.\n - **Safety** \u2014 no secrets, credentials, or debugging remnants in code, tests, or fixtures.\n3. Fix what is justified \u2014 smallest change that resolves the finding, keeping behavior. Re-run the checks after material fixes.\n4. Record your findings honestly in your closing summary: what you found, what you fixed, what you deliberately left, and what you could not verify.\n\nHandoff bar: checks pass on YOUR final commit; every fix is committed; your summary lists findings \u2192 resolutions (an empty findings list must say what you checked)."; declare const QA_PROMPT = "# Role: QA\n\nYou are the final gate. You verify the delivered work against the acceptance criteria as a whole \u2014 end to end, the way a demanding user would \u2014 and produce the run's closing report.\n\nMethod:\n1. Read the task and `SPEC.pack.md` (when present). Your contract is the acceptance criteria; without a spec, derive them from the task.\n2. For EACH criterion, verify it against the real project: run the relevant tests, execute the code paths where feasible, inspect actual behavior/output. Do not take earlier stages' word for anything.\n3. Run the project's full checks (tests, lint, types, build) one final time.\n4. Write `QA-REPORT.pack.md` at the repo root: per-criterion verdict (\u2705 verified / \u26A0\uFE0F partially / \u274C failed \u2014 with evidence for each), the checks' results, anything not verifiable in this environment (stated plainly), and a short \"ready to ship?\" conclusion.\n5. If a criterion FAILS: fix it only when the fix is small and unambiguous; otherwise mark it failed with exact evidence \u2014 the user decides. Never paper over a failure.\n\nHandoff bar: the report is committed; every verdict carries evidence; the conclusion is honest about anything unverified."; /** * The pack **workflow article** — the shared constitution layer every stage * prompt carries (on top of the always-on Agent Standard the session already * has). It encodes the handoff discipline that makes the pipeline auditable: * commit per stage with the role byline, stay in stage scope, never touch the * run ledger. Layered-constitution model adapted from swarm-forge. */ declare const PACK_WORKFLOW_ARTICLE = "## Pipeline rules (you are one stage of an assembly line)\n\nYou are ONE specialist role in a multi-role pipeline running on this repository. Other specialist roles ran before you and/or run after you, each in a separate conversation. Follow these rules exactly:\n\n- **Do only your role's job.** The next stage exists for a reason \u2014 don't do its work, and don't redo a previous stage's work unless your role explicitly calls for correcting it.\n- **Work from the handoff.** The previous stage's handoff (commit + summary) is your input. Start by reading the current state of the working tree \u2014 it already contains all prior stages' work.\n- **Commit your work when your stage is complete.** One or more focused commits; the final state of the tree IS your handoff to the next stage. End every commit message with your role byline on its own line: `By .`\n- **Never leave the tree broken.** Run the project's checks before finishing when the project has them; your stage ends with a working tree the next role can build on.\n- **Do not push, force-push, or touch remotes** \u2014 the pipeline works locally; publishing is the user's call at the end.\n- **Never read, edit, or commit anything under `.codeam/`** \u2014 that is the pipeline's own ledger, not project code.\n- **Finish decisively.** When your stage's job is done and committed, say so in 2-4 lines (what you did, what you verified, anything the next stage should know) and stop. Don't ask \"should I continue?\" \u2014 the pipeline advances automatically.\n- **If you are genuinely blocked** (contradictory requirements, missing access), say exactly what is blocking you and stop \u2014 the user is supervising and will decide."; /** * Wire-shape types for the CLI / IDE-plugin → backend producer endpoints * that feed the mobile Files screen and the Pending Review Queue: * * - `POST /api/files/changed` — register a file change (upsert keyed by * `sessionId + filePath` server-side, so re-emitting on every save is * safe). * - `POST /api/review/hunks` — register an individual hunk for the * Pending Review Queue. The "Aggressive" policy this codebase ships * with sends one of these per hunk in the diff so the mobile user * approves/rejects each one independently. * * These mirror the backend NestJS DTOs at: * apps/api-v2/src/files/dto/report-file-changed.dto.ts * apps/api-v2/src/review/dto/create-hunk.dto.ts * * They are wire-only — no class-validator decorators, no runtime * coercion. The producer constructs them in TypeScript and serialises * directly to JSON. The backend re-validates on its side. */ type FileChangeStatus = 'modified' | 'added' | 'deleted' | 'renamed'; type FileReviewStatus = 'modified' | 'awaiting_review' | 'approved' | 'rejected' | 'reviewed'; /** * Body for `POST /api/files/changed`. The producer emits one of these * per modified file per session-tick (debounced). The server upserts * on `(sessionId, filePath)` so re-emitting on every save is safe. * * `pluginId` is required by the backend's `PluginAuthGuard` — it's * read off the body so the guard can derive the expected HMAC of the * `X-Plugin-Auth-Token` header against this exact `(session, plugin)` * pair before the controller runs. */ interface FileChangedEvent { sessionId: string; pluginId: string; filePath: string; fileStatus: FileChangeStatus; linesAdded: number; linesRemoved: number; hunkCount: number; /** * Optional. When the producer also emits hunks to `/api/review/hunks` * for this file, set this to `'awaiting_review'` so the Files screen * renders the pending-review badge. Defaults to `'modified'` * server-side when omitted. */ reviewStatus?: FileReviewStatus; /** * Optional path of the enclosing git repo, relative to the * producer's workingDir / workspace folder. Empty string when the * producer was launched from inside the repo (single-repo * workspace). Lets the UI attribute each row to its sub-repo when * the user paired from a multi-repo parent directory (e.g. * `~/Documents/codeagent/` containing several sibling repos). * Optional for back-compat with older producers — backend * defaults to null. */ repoPath?: string; /** * Optional basename of the enclosing git repo. Provides a short * label the UI can render in a chip without parsing `repoPath`. * Optional for back-compat. */ repoName?: string; } type HunkLineType = 'add' | 'remove' | 'context'; /** * One line of a unified diff hunk. `lineNumber` carries the * post-change ('+'-side) line numbers from `git diff` so the mobile * UI can render the gutter without re-deriving them. */ interface PendingReviewHunkLine { type: HunkLineType; lineNumber: number; text: string; } /** * Body for `POST /api/review/hunks`. One per hunk in the diff. * * `reasoning` and `sessionLogPreview` are nullable — the v1 chokidar * producer doesn't have either (it can't tell agent edits from human * edits, and it doesn't read the agent's rationale), so it skips * these fields entirely. A future PTY-output-parsing producer can * populate them. */ interface PendingReviewHunkEvent { sessionId: string; pluginId: string; filePath: string; fileStatus: FileChangeStatus; hunkHeader: string; lines: PendingReviewHunkLine[]; linesAdded: number; linesRemoved: number; reasoning?: string; sessionLogPreview?: string[]; } /** * One commit in the file's git log (newest first). `sha` is the full * 40-char hash; consumers truncate for display. `committedAt` is ISO * 8601 in UTC. Mirrors `apps/api-v2/src/review/dto/create-history.dto.ts` * `CommitEntryDto`. */ interface CommitEntryWire { sha: string; authorName: string; authorEmail: string; committedAt: string; subject: string; } /** * Body for `POST /api/review/history`. The producer captures `git log * --max-count=N -- ` for each touched file at the same point it * pushes hunks, then upserts on `(sessionId, repoPath, filePath)` * server-side. Re-emitting per save is safe. */ interface FileHistoryEvent { sessionId: string; pluginId: string; filePath: string; repoPath?: string; repoName?: string; commits: CommitEntryWire[]; } /** * One line of `git blame`. `lineNumber` is 1-based and matches the * post-image (current) file gutter. */ interface BlameLineWire { lineNumber: number; sha: string; authorName: string; committedAt: string; text: string; } /** * Body for `POST /api/review/blame`. Capped server-side by what the * producer chose to emit — large files get truncated by the CLI so a * single payload stays under the JSON size limit. */ interface FileBlameEvent { sessionId: string; pluginId: string; filePath: string; repoPath?: string; repoName?: string; lines: BlameLineWire[]; } /** * Wire-shape types for the CLI / IDE-plugin → backend Epic C streaming * endpoints. The CLI parses Claude's (or Codex's) PTY output into a * stream of discriminated chunks and pushes each one to the backend, so * the mobile client can render an in-progress agent turn token-by-token * instead of waiting for the entire turn to finalise. * * - `POST /api/sessions/:id/streaming-chunk` — body is * {@link StreamingChunkEvent}. Fires an SSE delta downstream. * - `POST /api/sessions/:id/awaiting-answer` — body is * {@link AwaitingAnswerEvent}. Pauses the turn on the mobile side * and prompts the user for a reply. Stored in Redis with a 5 min TTL. * - Answer channel: backend publishes user replies on the Redis * channel `session:${sessionId}:answers` with the payload shape * {@link AnswerResolvedEvent}. The CLI polls * `GET /api/sessions/:id/pending-answer` to drain it (the polling * interval is 1.5 s — short enough to feel instant, long enough to * stay well under any sane rate limit). * * These mirror the backend NestJS DTOs at: * apps/api-v2/src/sessions/dto/streaming-chunk.dto.ts * apps/api-v2/src/sessions/dto/awaiting-answer.dto.ts * apps/api-v2/src/sessions/dto/answer-resolved.dto.ts * * They are wire-only — no class-validator decorators, no runtime * coercion. The producer constructs them in TypeScript and serialises * directly to JSON. The backend re-validates on its side. */ /** * Logical kind of an Epic C streaming chunk. * * - `text` — agent prose (the conversational reply the user sees). * - `thinking` — Claude's "(thinking)" / "+ Puttering…" frame between * the prompt and the answer. * - `tool_use` — a tool call (Read / Edit / Bash / Search / …) the * agent invoked. * - `tool_result` — the result body of the prior tool call (typically * the `└ …` continuation line in Claude's TUI). */ type StreamingChunkKind = 'text' | 'thinking' | 'tool_use' | 'tool_result'; /** * Body for `POST /api/sessions/:id/streaming-chunk`. * * `chunkId` is stable across continuation pushes for the same logical * chunk (so the backend can splice deltas), and changes when the * producer flips `kind` or finalises the chunk. `isFinal: true` marks * the last push for this `chunkId`; the next emission opens a fresh * chunkId. */ interface StreamingChunkEvent { chunkId: string; kind: StreamingChunkKind; content: string; isFinal: boolean; } /** * Body for `POST /api/sessions/:id/awaiting-answer`. * * `prompt` is the question text the agent rendered (free-form). When * the agent presented a multiple-choice selector, `options` is the * ordered list of choices the user can pick. `questionId` is the * producer-generated UUID the backend echoes back through the answer * channel so the CLI can correlate the user's reply with the prompt. */ interface AwaitingAnswerEvent { questionId: string; prompt: string; options?: string[]; } /** * Wire shape for the `input_suggestion` output chunk emitted by CLI agents * after a turn ends. * * PTY agents (claude, codex, copilot, aider) emit `content: string` — * the detected ghost-text from the agent's input area (backward-compat, * one chip on mobile). * * ACP agents emit `content: string[]` — a static set of chip labels such * as `['Continue', 'Yes, go ahead', 'Explain']` (multiple chips on mobile). * * Consumers MUST normalise: * `Array.isArray(content) ? content : [content]` */ interface InputSuggestionChunk { type: 'input_suggestion'; /** Single string (PTY, backward-compat) or array of chip labels (ACP). */ content: string | string[]; done: true; } /** * Payload published on the Redis `session:${sessionId}:answers` * channel — and also the shape returned by the polling fallback * `GET /api/sessions/:id/pending-answer` (wrapped in `{ data: … }` by * the backend's standard envelope). * * For free-form prompts `answer` is the user's typed text. For a * selector prompt, the backend forwards the chosen option label as * `answer` and additionally sets `optionIndex` (0-based) so the * producer can drive arrow-key navigation in a React Ink selector * without re-resolving the label. */ interface AnswerResolvedEvent { questionId: string; answer: string; optionIndex?: number; } /** * Production API base URL for all CodeAgent Mobile clients. * * History note: prod migrated from Vercel (`https://api.codeagent-mobile.com`) * to Cloud Run / api-v2 (`https://api.codeagent-mobile.com`) in 2026-05. The * Vercel deployment is now gated by Vercel deployment protection and returns * 403 for unauthed traffic — DO NOT fall back to it. * * Override at runtime with `CODEAM_API_URL` (full URL override) OR set * `CODEAM_TEST_MODE=1` to point every client request at the dev * preview without having to know its host. */ declare const DEFAULT_API_BASE_URL: "https://api.codeagent-mobile.com"; /** * Dev-preview API base URL. Same Cloud Run service as prod but routed * to the `dev` revision (auto-deploys from the `dev` branch in the * backend repo). Manual smoke tests + load runs land here. */ declare const DEV_API_BASE_URL: "https://dev-api.codeagent-mobile.com"; /** * Resolve the active API base URL, honoring in priority order: * * 1. Explicit `CODEAM_API_URL` env var — full URL, takes precedence. * 2. `CODEAM_TEST_MODE=1` shortcut — flips to [DEV_API_BASE_URL] * without the user having to know the dev host. * 3. The `DEFAULT_API_BASE_URL` constant (prod). * * Used by every CLI service that talks to the backend so one env var * flips heartbeats, command relay, chunk uploads, and the pairing * flow in lockstep — eliminates the cross-environment misroute where * pairing succeeds in dev (shared Redis) but the CLI keeps * heartbeating to prod. */ declare function resolveApiBaseUrl(): string; /** * Preview wire types (PreviewDetection / PreviewStatus / EnvVar). * * CANONICAL WIRE OWNER: this file (`@codeam/shared`) owns the wire * protocol, per the cross-repo rule. The backend repo keeps hand-synced * MIRRORS (`codeagent-mobile/packages/shared/src/types/preview.ts` for * mobile/landing, `codeagent-mobile/apps/api-v2/src/common/types/preview.ts` * for the backend); a drift-check script at * `codeagent-mobile/scripts/check-shared-drift` compares them. */ interface PreviewDetection { framework: string; command: string; args: string[]; port: number; ready_pattern: string; env?: Record; setup_commands?: Array<{ cmd: string; args: string[]; }>; notes?: string; } type PreviewState = 'idle' | 'detection_pending' | 'detection_ready' | 'starting' | 'running' | 'error'; type PreviewErrorStage = 'detection' | 'spawn' | 'tunnel' | 'ready_timeout' | 'unsupported'; interface PreviewStatus { state: PreviewState; url?: string; framework?: string; detection?: PreviewDetection; error?: { stage: PreviewErrorStage; message: string; }; } /** * One environment variable as edited from the app and written to the * project `.env`. The wire shape for `env_read` (returns EnvVar[]) and * `env_write` (accepts EnvVar[]). */ interface EnvVar { key: string; value: string; } /** * Beads wire protocol — the bytes the codeam-cli pushes to the backend's * `POST /api/beads/ingest` and that the backend mirrors + fans out over the * per-user SSE bus. * * CANONICAL WIRE OWNER: this file (`@codeam/shared`) owns the wire * protocol, per the cross-repo rule. The backend repo keeps hand-synced * MIRRORS of these shapes (`codeagent-mobile/packages/shared/src/types/beads.ts` * for mobile/landing, `codeagent-mobile/apps/api-v2/src/beads/beads.types.ts` * for the backend service); a drift-check script at * `codeagent-mobile/scripts/check-shared-drift` compares them. Both the CLI * (tsup) and the VS Code extension (esbuild) inline this file at build time. * * Shape rationale: `BeadsIssueDto` mirrors `bd ready --json` / `bd list --json` * output (verified against `@beads/bd@1.0.5`) plus the backend-required * `projectKey` scoping field (design decision D7). We do NOT reshape bd's * field names — the mirror stores them as-is so a bd schema bump is a * one-file change here, not a sprawling rename across the codebase. */ /** bd lifecycle status. bd emits these literals in `--json`. */ type BeadsIssueStatus = 'open' | 'in_progress' | 'blocked' | 'closed'; /** * Single issue as emitted by `bd ready --json` / `bd list --json`, plus the * backend scoping field. The counts are REQUIRED on the wire — the backend's * ingest DTO validates them as required ints, so the producer (`bd-adapter`'s * `parseIssues`) defaults any count bd omits to 0 rather than dropping the * field. */ interface BeadsIssueDto { id: string; title: string; status: BeadsIssueStatus; /** 0 = P0 (highest). bd emits an integer; null when unset. */ priority: number | null; /** bug | task | feature | message | … (free-form in bd). */ issue_type: string; /** agent / session id that claimed the issue, when claimed. */ owner: string | null; created_at: string; updated_at: string; dependency_count: number; dependent_count: number; comment_count: number; /** D7 scoping — normalized git origin (or path-hash fallback). */ projectKey: string; } /** bd dependency kind. */ type BeadsDependencyKind = 'blocks' | 'related' | 'parent-child' | 'discovered-from'; /** * One dependency edge. Rows carry NO per-row `projectKey` — the ingest * payload is per-project, so edges are scoped by the payload-level * `projectKey` on the backend. */ interface BeadsDependencyDto { /** stable id — `${fromId}:${kind}:${toId}` when bd doesn't supply one. */ id: string; fromId: string; toId: string; kind: BeadsDependencyKind; } interface BeadsMemoryDto { id: string; body: string; createdAt: string; /** null = cross-cutting / personal (not scoped to one project). */ projectKey: string | null; } /** `bd status --json` → `summary` block. */ interface BeadsStatusSummary { open_issues: number; ready_issues: number; blocked_issues: number; in_progress_issues: number; closed_issues: number; total_issues: number; } /** * The delta (or full snapshot) the CLI POSTs to `/api/beads/ingest` whenever * `.beads/issues.jsonl` changes. `fullSnapshot: true` tells the backend to * prune issues absent from `issues` (station-wins reconciliation). */ interface BeadsIngestPayload { sessionId: string; pluginId: string; /** D7 project key the issues/memories belong to. */ projectKey: string; /** Human-readable label (repo name) for the UI. */ projectLabel: string; /** When true, the backend prunes mirror rows not present in `issues`. */ fullSnapshot?: boolean; issues: BeadsIssueDto[]; /** Dependency edges between issues. The current watcher ALWAYS sends this * (an empty array today — edges aren't computed in the P0 snapshot); the * backend DTO nevertheless marks it optional to tolerate older producers. * Field name matches the backend (`dependencies`, not `deps`). */ dependencies: BeadsDependencyDto[]; memories: BeadsMemoryDto[]; summary?: BeadsStatusSummary; } /** * A mobile-originated action relayed to the CLI as a pending command * (`type: 'beads_action'`). The CLI replays it as a native `bd` command * (Task 9) then pushes the resulting state back through ingest. * * NOTE — this is the backend→CLI COMMAND hop, NOT the mobile→backend * request hop. The mobile client POSTs a `BeadsActionRequest` * (discriminator `action`, create-title in `title`) to * `POST /api/beads/actions`; the backend translates it in * `codeagent-mobile/apps/api-v2/src/beads/beads.controller.ts` (+ * `bd-action.util.ts`) and pushes a `beads_action` command whose payload * the CLI decodes in `apps/cli/src/beads/wiring.ts` * (`beadsActionFromPayload`) into THIS shape (discriminator `kind`, * title/body in `text`, plus `owner`). */ type BeadsActionKind = 'claim' | 'close' | 'create' | 'remember'; interface BeadsActionCommand { kind: BeadsActionKind; /** Target issue id — required for `claim` / `close`. */ issueId?: string; /** Free text — `create` title or `remember` body. */ text?: string; /** `close` reason. */ reason?: string; /** Owner to claim as — defaults to the session/agent id when omitted. */ owner?: string; /** Project the action targets (so the right `bd` working context applies). */ projectKey?: string; } /** @deprecated Renamed to `BeadsActionCommand` — the old name collided with * the backend repo's mobile→backend request type (now `BeadsActionRequest`). */ type BeadsActionPayload = BeadsActionCommand; /** Action verb for `configureBeads` (enable / disable / status). */ type BeadsConfigureAction = 'enable' | 'disable' | 'status'; /** Lifecycle state emitted by `configureBeads` on the per-session SSE bus. */ type BeadsStatusState = 'enabled' | 'disabled' | 'error' | 'provisioning'; /** * Lifecycle of the CLI's Beads provisioning step (spec D10/D13). * * The CLI's composition-root provisions Beads (`bd init` → start the * shared dolt server → enable auto-export → start the watcher) as a * parallel, non-fatal concern, decoupled from the agent run. It signals * each phase to the backend, which fans it out as a `beads_provisioning` * UserEvent so the read-only mobile/web surface can show a lightweight * status line ("Provisioning Beads…", "Beads ready", "Beads * provisioning failed") — distinct from the data feed (`beads_state_changed`). * * - `provisioning` — bootstrap started / in flight. * - `ready` — Beads is up; the mirror will begin receiving deltas. * - `failed` — bootstrap aborted (e.g. bd install failed); the * surface explains the agent is running without Beads. */ type BeadsProvisioningStatus = 'provisioning' | 'ready' | 'failed'; /** * Body of `POST /api/beads/provisioning` — the CLI is the producer. * * `sessionId` + `pluginId` carry the plugin-auth envelope (the CLI has * NO user JWT, same as `/ingest`). The backend resolves the userId from * the session and publishes `{ type: 'beads_provisioning', status, * projectKey }` on the per-user SSE bus. * * `projectKey` is optional: the home-level `bd init` runs before any * repo is resolved, so the first `provisioning` frame may not carry one * yet. `detail` is an optional human-readable note (e.g. the failure * reason) the surface can render verbatim. */ interface BeadsProvisioningPayload { /** Paired session the CLI is pushing from (plugin-auth). */ sessionId: string; /** Plugin id the auth token was minted for (plugin-auth). */ pluginId: string; status: BeadsProvisioningStatus; /** Affected project, when the bootstrap has resolved a repo. */ projectKey?: string; /** Optional human-readable note (e.g. failure reason). */ detail?: string; } /** Snapshot of the station's Beads add-on state (mirrored to mobile). */ interface BeadsStatus { state: BeadsStatusState; running?: boolean; bdAvailable?: boolean; doltAvailable?: boolean; serverUp?: boolean; prefix?: string | null; error?: string; } /** One project the mirror has seen — drives the project-list view. */ interface BeadsProjectDto { projectKey: string; /** Human repo name for the UI. */ label: string; /** ISO8601 — last time the CLI pushed state for this project. */ lastSyncedAt: string; } /** * One-shot snapshot returned by `GET /api/beads/me` and embedded in the * per-user SSE `snapshot` so reconnects rehydrate. `issuesByProject` is * keyed by `projectKey`. */ interface BeadsSnapshotDto { projects: BeadsProjectDto[]; issuesByProject: Record; memories: BeadsMemoryDto[]; /** Aggregate summary across all the user's projects. */ summary: BeadsStatusSummary; } /** * The user-initiated Beads actions a mobile/web client can request. * * - `claim` — take ownership of a ready issue (`bd update --status in_progress`). * - `close` — close an issue (`bd close`). * - `create` — create a new issue (`bd create`). * - `remember` — record a persistent memory note (`bd remember`). * * The backend does NOT mutate the mirror directly — it relays the * action as a `bd` command to the user's active paired session, where * the CLI runs it natively against the station's `bd` graph. The * resulting state change flows back via `POST /api/beads/ingest` and * fans out over the per-user SSE bus. The mirror is read-optimised; * the station stays the source of truth (station-wins). */ type BeadsActionType = 'claim' | 'close' | 'create' | 'remember'; /** * Body of `POST /api/beads/actions` (JWT-authed mobile/web user). * * NOTE — this is the mobile→backend REQUEST hop, NOT the backend→CLI * command hop (`BeadsActionCommand` above). * * Field relevance by action: * - `claim` — `issueId` required. * - `close` — `issueId` required; `reason` optional. * - `create` — `title` required; `projectKey` optional (targets a project). * - `remember` — `text` required; `projectKey` optional (scopes the memory). */ interface BeadsActionRequest { action: BeadsActionType; /** Target issue for `claim` / `close`. */ issueId?: string; /** Title for a `create`d issue. */ title?: string; /** Optional human-readable reason for `close`. */ reason?: string; /** Memory body for `remember`. */ text?: string; /** Project scope for `create` / `remember`; defaults to the station's current repo. */ projectKey?: string; } /** * Headroom budget configuration and command types. * Used by the CLI to enable/disable cost-saving Headroom token compression * and track spending against configured budgets. * * CANONICAL WIRE OWNER: this file (`@codeam/shared`) owns the wire * protocol, per the cross-repo rule. The backend repo keeps hand-synced * MIRRORS (`codeagent-mobile/packages/shared/src/types/headroom.ts` for * mobile/landing, `codeagent-mobile/apps/api-v2/src/common/types/headroom.ts` * for the backend); a drift-check script at * `codeagent-mobile/scripts/check-shared-drift` compares them. */ type HeadroomBudgetPeriod = 'hourly' | 'daily' | 'monthly'; /** * Install-progress milestone emitted on the `headroom_progress` SSE event while * a session is provisioning Headroom on-demand. Must stay byte-for-byte aligned * with the backend's `HEADROOM_STEPS` validator * (`apps/api-v2/src/headroom/headroom.controller.ts`) and the mobile store — * the backend 400s (`INVALID_STEP`) on any value outside this set. Note * `'provisioning'` is a `HeadroomStatus['state']`, NOT a step. */ type HeadroomStep = 'pip' | 'model' | 'init' | 'proxy' | 'ready'; /** * Command sent via relay to enable/disable/configure Headroom budget settings. * The `agentId` field is included because PairedSession has no agentId server-side, * so the relay command carries it for the CLI handler to guard on. */ interface HeadroomBudgetCommand { budgetEnabled: boolean; budgetUsd?: number; budgetPeriod?: HeadroomBudgetPeriod; agentId?: string; } /** * Budget usage fields appended to the savings payload that the Headroom reporter * sends to the backend. Tracks spending in the current budget period. */ interface HeadroomBudgetUsage { periodSpendUsd?: number; budgetUsd?: number; budgetPeriod?: HeadroomBudgetPeriod; /** True iff this turn pushed periodSpendUsd to or past budgetUsd. */ budgetReached?: boolean; } /** * Headroom cost-saving state for a session — carried on the `headroom_status` * SSE event and snapshotted by the backend into Redis `headroom:`. * Mirrored byte-for-byte in `apps/api-v2/src/common/types/headroom.ts`. */ interface HeadroomStatus { state: 'enabled' | 'disabled' | 'error' | 'provisioning'; running?: boolean; agent?: string; savings?: number; error?: string; } /** One provider/model slice inside a rollup bucket. */ interface HeadroomUsageSlice { /** Tokens Headroom removed in this bucket (a DELTA, not cumulative). */ tokens_saved: number; compression_savings_usd_delta: number; total_input_tokens_delta: number; total_input_cost_usd_delta: number; } /** * One rollup bucket. ⚠️ The `*_delta` fields (and `tokens_saved`) are * PER-BUCKET; the bare `total_*` fields are the CUMULATIVE value at the * bucket's end. Chart the deltas, show the totals as headline figures. */ interface HeadroomUsageBucket { /** Bucket start, UTC ISO-8601. */ timestamp: string; tokens_saved: number; compression_savings_usd_delta: number; total_tokens_saved: number; compression_savings_usd: number; total_input_tokens_delta: number; total_input_tokens: number; total_input_cost_usd_delta: number; total_input_cost_usd: number; by_provider: Record; by_model: Record; } type HeadroomUsageGranularity = 'hourly' | 'daily' | 'weekly' | 'monthly'; /** Lifetime / current-window totals. */ interface HeadroomUsageTotals { requests: number; tokens_saved: number; compression_savings_usd: number; total_input_tokens: number; total_input_cost_usd: number; } /** * The token-usage report the CLI relays for the `headroom_usage` command — a * TRIMMED projection of the proxy's `GET /stats-history` (schema_version 3, * verified live against headroom 0.27.0). * * ⚠️ Trimmed ON THE BOX before it ever leaves, for three reasons: * - the raw response is ~150 KB (and `history_mode=full` is ~1.1 MB) — too * heavy for the command relay; dropping the raw `history[]` and capping the * hourly series brings it to ~23 KB. * - `history[]` carries CUMULATIVE counters that would have to be diffed, * while `series[]` already provides per-bucket deltas AND the * `by_model` / `by_provider` breakdown (richer than Headroom's own CSV * export, which has no model column). * - the proxy's `storage_path` leaks a local filesystem path (including the * OS username) and is stripped. */ interface HeadroomUsageReport { /** Headroom's own payload schema version (3 at time of writing). */ schemaVersion: number; /** When the proxy generated the snapshot (UTC ISO-8601). */ generatedAt: string; /** Proxy version that produced it, when known. */ proxyVersion?: string; /** Durable, all-time totals across proxy restarts. */ lifetime: HeadroomUsageTotals; /** The proxy's current display session (rolls over after inactivity). */ currentSession?: HeadroomUsageTotals & { savings_percent?: number; started_at?: string | null; last_activity_at?: string | null; }; /** Rollups. `hourly` is capped to the most recent buckets to bound size. */ series: Partial>; /** The proxy's retention policy, so the UI can state the window honestly. */ retention?: { max_history_points?: number; max_history_age_days?: number; }; } /** Result of the `headroom_usage` relay command. */ interface HeadroomUsageResult { /** False when the proxy isn't reachable / Headroom isn't active here. */ available: boolean; report?: HeadroomUsageReport; /** Human-readable reason when `available` is false. */ error?: string; } /** Relay command type for pulling the token-usage report. */ declare const HEADROOM_USAGE_COMMAND = "headroom_usage"; /** * Session agent switch — wire types for the `switch_agent` relay command and * its progress/status SSE events. * * A live session can swap its coding agent (e.g. claude → codex) in-process: * mobile sends `switch_agent { agentId }` over the command relay; the CLI * pulls the vaulted credential (+ install script when the binary is missing), * restarts the ACP client on the new adapter, and reports progress via * `POST /api/agent-switch/events` → the per-user SSE bus * (`switch_agent_progress` / `switch_agent_status`). * * CANONICAL WIRE OWNER: this file (`@codeam/shared`), per the cross-repo * rule. The backend/mobile repo consumes it through the published package. * * Continuity contract: the conversation does NOT resume cross-agent (ACP * `session/load` ids are per-agent). Instead the CLI captures a bounded tail * of the prior conversation and prefixes it to the first post-switch prompt * (the "context handoff"), so the new agent continues with the session's * context in a fresh conversation. */ /** Relay command type mobile sends to switch the session's agent. */ declare const SWITCH_AGENT_COMMAND = "switch_agent"; /** Payload of the `switch_agent` relay command. */ interface SwitchAgentCommand { /** Public agent id to switch to (catalog id, e.g. 'codex'). */ agentId: string; } /** * Install-progress milestone emitted on the `switch_agent_progress` SSE event * while a switch is running. Must stay aligned with the backend's validator * (`apps/api-v2/src/agent-switch/agent-switch.controller.ts`) and the mobile * store — the backend 400s on any value outside this set. * * - `credential` — fetching + writing the vaulted credential for the target. * - `install` — target binary missing; running its install script. * - `restart` — old client stopped; new adapter starting (`session/new`). */ type SwitchAgentStep = 'credential' | 'install' | 'restart'; /** * Terminal/steady switch state — carried on `switch_agent_status` and used by * mobile to flip the session's agent chip (`ready`) or render an actionable * error (`error`). `switching` is published once up-front so other paired * devices see the transition too. */ interface SwitchAgentStatus { state: 'switching' | 'ready' | 'error'; /** Target public agent id of the switch. */ agentId: string; /** Public agent id that was running before the switch was attempted. */ fromAgentId?: string; /** Human-readable reason when `state === 'error'`. */ error?: string; } /** Result payload the CLI acks the `switch_agent` command with. */ interface SwitchAgentResult { ok: boolean; agentId: string; /** Set when `ok === false`. */ error?: string; } /** * Agent Squad wire types — @-mention routing, roster, and agent-proposed * handoffs. Canonical owner (api-v2 + mobile consume the published npm * package; the cm-repo packages/shared mirror re-exports). * Spec: docs/superpowers/specs/2026-08-13-agent-squad-mentions-handoffs-design.md */ /** start_task payload — makes the previously-dead agentId field a real wire contract. */ interface StartTaskPayload { prompt?: string; files?: Array<{ filename: string; base64?: string; mimeType?: string; }>; /** Internal runtime id. Present + ≠ active agent → the runner swaps before running. */ agentId?: string; } interface SquadRosterAgent { agentId: string; displayName: string; } /** Response data of POST /api/plugin/agents/roster. */ interface SquadRosterData { agents: SquadRosterAgent[]; handoffsEnabled: boolean; } /** The fence tag the active agent uses to propose a handoff (PRO). */ declare const HANDOFF_FENCE_TAG = "codeam-handoff"; interface HandoffProposal { proposalId: string; fromAgentId: string; toAgentId: string; reason: string; prompt: string; /** * Autonomous mode (P2-2): the CLI accepted this proposal ITSELF instead of * emitting the tap-to-accept card. Mobile renders a passive timeline notice * for `auto: true`, never the accept card. Absent = the v1 card flow. */ auto?: boolean; /** * Hops left in the chain AFTER this one (`auto` only) — the hop is already * spent when this is emitted, so `0` means "this is the last auto hop; any * further proposal falls back to the card". Mobile renders it verbatim as * " hops left". */ hopsRemaining?: number; } /** `handoff_resolved` event payload. `auto` mirrors {@link HandoffProposal}. */ interface HandoffResolution { proposalId: string; accepted: boolean; auto?: boolean; } /** Relay command: read/write the session's autonomous-handoff mode. */ declare const SQUAD_CONFIGURE_COMMAND = "squad_configure"; /** Relay command: per-member activity for the "Squad activity" screen. */ declare const SQUAD_STATS_COMMAND = "squad_stats"; declare const SQUAD_HOP_BUDGET_DEFAULT = 3; declare const SQUAD_HOP_BUDGET_MIN = 1; declare const SQUAD_HOP_BUDGET_MAX = 10; /** * Clamp a caller-supplied hop budget into the supported range, falling back to * the default for a missing / non-finite value. The ONE place the bound lives — * the CLI's config mutator and its `squad_configure` handler both call it. */ declare function clampHopBudget(value: unknown): number; /** Persisted per-session autonomous-handoff mode. Default OFF. */ interface SquadAutoConfig { enabled: boolean; hopBudget: number; } type SquadConfigurePayload = { action: 'set'; autoHandoffs: boolean; hopBudget?: number; } | { action: 'status'; }; /** Ack of {@link SQUAD_CONFIGURE_COMMAND} — the state AFTER the command. */ interface SquadConfigureResult extends SquadAutoConfig { /** Hops left in the current chain (resets on every user prompt). */ hopsRemaining: number; } interface SquadMemberActivity { agentId: string; turns: number; /** DISTINCT paths this member touched across its journaled turns. */ filesTouched: number; } /** Ack of {@link SQUAD_STATS_COMMAND}. No cost attribution in v1. */ interface SquadStatsResult { members: SquadMemberActivity[]; handoffs: { proposed: number; accepted: number; auto: number; }; sinceTurn: number; } /** Per-agent specialty blurbs for the team preamble. Copy, not routing. */ declare const SQUAD_SPECIALTIES: Readonly>>; /** * Headroom provisioning manifest — the SINGLE source of truth for what a * Headroom install consists of, rendered by every provisioning surface: * * - codespace bootstrap (bash composer in the backend repo, * `apps/api-v2/src/codespaces/github-ssh.service.ts` — adopts in PR-2), * - self-hosted deploy (TS installer, CLI `commands/host-agent.ts` * `setupHeadroomForSelfHosted`), * - on-demand local sessions ("Session add-ons → Cost-saving", CLI * `services/headroom/configure.ts`). * * Values are DATA-first (arrays/records, plus tiny pure renderers) so both * the TS installer and a bash composer can interpolate from them. Renderers * are byte-exact with the literals they replaced — guarded by * `packages/shared/__tests__/headroom-manifest.test.ts`. * * ⚠️ The extras matter: `[proxy,code]` pulls the ONNX compression engines * (Kompress + tree-sitter CodeCompressor). NEVER add `[ml]` — that's * multi-GB PyTorch, and a broken/cold torch wedges every prompt at * "Thinking…". The models are pre-downloaded at provision time because the * proxy eager-loads with `allow_download=False` and a cold cache defers the * ~840 MB download to the first prompt (blowing the agent's ~90 s idle * timeout). */ /** Local proxy port the agent's config is routed to. */ declare const HEADROOM_PROXY_PORT = 8787; /** * Env that pins the ONNX backend on the proxy process — never imports * torch. Spread into the proxy launch env on every surface. */ declare const HEADROOM_BACKEND_ENV: { readonly HEADROOM_KOMPRESS_BACKEND: "onnx_cpu"; }; /** * The proxy's HTTP/server companion packages, installed alongside the * `headroom-ai[...]` package. The COMPRESSION ENGINES come from the * headroom-ai extras — NOT this list. */ declare const HEADROOM_PIP_COMPANIONS: readonly string[]; /** The three provisioning surfaces (see module doc). */ type HeadroomSurface = 'codespace' | 'selfHosted' | 'onDemand'; /** * pip extras per surface. `onDemand` additionally ships `image` * (image-compression support, added with the Session add-ons path in * codeam-cli@2.49.0); the older codespace/self-hosted install strings * remain `[proxy,code]` byte-for-byte. */ declare const HEADROOM_EXTRAS_BY_SURFACE: Readonly>; /** `headroom-ai[]` — the pip requirement string. */ declare function headroomPipPackage(extras: readonly string[]): string; /** One HuggingFace repo to pre-warm into the HF cache at provision time. */ interface HeadroomModelSpec { repo: string; /** `snapshot_download(..., allow_patterns=[…])` filter. */ allowPatterns: readonly string[]; } /** * The two HF repos Kompress needs. kompress-v2-base is the ONNX model * (skip its .pt/.safetensors torch artifacts); ModernBERT-base is the * TOKENIZER ONLY (skip its model weights). */ declare const HEADROOM_MODELS: readonly HeadroomModelSpec[]; /** Formatting knob so each surface can stay byte-identical to its * historical literal (the CLI joins patterns with `,`, the codespace * bash composer with `, `). */ interface HeadroomPythonRenderOpts { /** Put a space after the commas between allow_patterns entries. */ spaceAfterComma?: boolean; } /** Render one `snapshot_download(...)` python line for a model. */ declare function headroomSnapshotDownloadLine(model: HeadroomModelSpec, opts?: HeadroomPythonRenderOpts): string; /** * The full model pre-download python snippet (import + one * `snapshot_download` per model), newline-joined — what the surfaces pass * to `python -c` / a heredoc. */ declare function headroomModelPredownloadScript(opts?: HeadroomPythonRenderOpts): string; /** * Canonical names of the per-user SSE bus events (`/api/users/me/stream`). * * The authoritative list is the `UserEvent` discriminated union in the * backend repo: codeagent-mobile/apps/api-v2/src/user-events/user-events.types.ts. * Every `type:` literal of that union appears here exactly once — when a new * variant lands on the union, add its name here (and in the backend mirror of * this file at codeagent-mobile/packages/shared/src/types/events.ts). * * Producers (CLI event posts, backend `userEvents.publish` calls) and * consumers (the `useUserEventsSSE` hooks' switch cases) should reference * `USER_EVENTS.*` instead of re-typing the string, so a typo becomes a * compile error instead of a silently dropped event. */ declare const USER_EVENTS: { readonly PAIRED_SESSION_STATUS: "paired_session_status"; readonly PAIRED_SESSION_ADDED: "paired_session_added"; readonly PAIRED_SESSION_REMOVED: "paired_session_removed"; readonly PAIRED_SESSION_BRANCH_CHANGED: "paired_session_branch_changed"; readonly SHARED_WITH_ME_ADDED: "shared_with_me_added"; readonly SHARED_WITH_ME_REVOKED: "shared_with_me_revoked"; readonly USAGE_CHANGED: "usage_changed"; readonly TASK_DONE: "task_done"; readonly HUNK_PENDING_REVIEW_ADDED: "hunk_pending_review_added"; readonly HUNK_REVIEW_RESOLVED: "hunk_review_resolved"; readonly FILE_CHANGED: "file_changed"; readonly FILES_BATCH_CHANGED: "files_batch_changed"; readonly AGENT_STREAMING_CHUNK: "agent_streaming_chunk"; readonly AGENT_AWAITING_ANSWER: "agent_awaiting_answer"; readonly AWAITING_INPUT_ADDED: "awaiting_input_added"; readonly AGENT_ANSWER_RESOLVED: "agent_answer_resolved"; readonly TEMPLATE_ADDED: "template_added"; readonly TEMPLATE_REMOVED: "template_removed"; readonly TEMPLATE_UPDATED: "template_updated"; readonly AGENT_TASK_DISPATCHED: "agent_task_dispatched"; readonly AGENT_TASK_COMPLETED: "agent_task_completed"; readonly LINKED_AGENT_ADDED: "linked_agent_added"; readonly QUOTA_REACHED: "quota_reached"; readonly LINKED_AGENT_LINK_FAILED: "linked_agent_link_failed"; readonly CODESPACE_AGENT_INSTALLED: "codespace_agent_installed"; readonly AGENT_CREDENTIALS_REFRESHED: "agent_credentials_refreshed"; readonly CREDENTIAL_INVALID: "credential_invalid"; readonly CODESPACE_WAKING: "codespace_waking"; readonly CODESPACE_BILLING_BLOCKED: "codespace_billing_blocked"; readonly COST_SAVING_UPDATED: "cost_saving_updated"; readonly COMMAND_COMPLETED: "command_completed"; readonly AI_SUMMARY_PENDING: "ai_summary_pending"; readonly AI_SUMMARY_READY: "ai_summary_ready"; readonly AI_INSIGHT_PENDING: "ai_insight_pending"; readonly AI_INSIGHT_READY: "ai_insight_ready"; readonly PUSH_TOKEN_INVALIDATED: "push_token_invalidated"; readonly PREVIEW_DETECTION_PENDING: "preview_detection_pending"; readonly PREVIEW_DETECTION_READY: "preview_detection_ready"; readonly PREVIEW_STARTING: "preview_starting"; readonly PREVIEW_READY: "preview_ready"; readonly PREVIEW_STOPPED: "preview_stopped"; readonly PREVIEW_ERROR: "preview_error"; readonly PREVIEW_PROGRESS: "preview_progress"; readonly BEADS_STATE_CHANGED: "beads_state_changed"; readonly BEADS_PROVISIONING: "beads_provisioning"; readonly BEADS_TEAM_MEMORY_CHANGED: "beads_team_memory_changed"; readonly AUDIT_EVENT_ADDED: "audit_event_added"; readonly SELF_HOSTED_HOST_ADDED: "self_hosted_host_added"; readonly SELF_HOSTED_HOST_STATUS: "self_hosted_host_status"; readonly SELF_HOSTED_HOST_REMOVED: "self_hosted_host_removed"; readonly SELF_HOSTED_HOST_TELEMETRY: "self_hosted_host_telemetry"; readonly SELF_HOSTED_HOST_METRICS: "self_hosted_host_metrics"; readonly SELF_HOSTED_HOST_SESSIONS: "self_hosted_host_sessions"; readonly SELF_HOSTED_DEPLOY_PROGRESS: "self_hosted_deploy_progress"; /** Fleet rescue: the user's CodeAgent Box reached RUNNING (host enrolled * + online). Drives the mobile "Use a free CodeAgent Box" flow to * auto-deploy the user's presets instead of hanging on a paired session * a box never creates. */ readonly FLEET_BOX_READY: "fleet_box_ready"; readonly REFERRAL_REWARD_EARNED: "referral_reward_earned"; readonly HEADROOM_PROGRESS: "headroom_progress"; readonly HEADROOM_STATUS: "headroom_status"; readonly BEADS_STATUS: "beads_status"; readonly LINKED_AGENT_HEADROOM_BUDGET_UPDATED: "linked_agent_headroom_budget_updated"; readonly CLI_UPDATE_AVAILABLE: "cli_update_available"; readonly AGENT_INSTALL_PROGRESS: "agent_install_progress"; readonly AGENT_INSTALL_FAILED: "agent_install_failed"; readonly CLI_UPDATE_PROGRESS: "cli_update_progress"; readonly CLI_UPDATE_FAILED: "cli_update_failed"; readonly BATON_STATE: "baton_state"; readonly INTEGRATION_LINKED: "integration_linked"; readonly INTEGRATION_UNLINKED: "integration_unlinked"; readonly INTEGRATION_CREDENTIAL_INVALID: "integration_credential_invalid"; readonly SESSION_INTEGRATIONS_CHANGED: "session_integrations_changed"; readonly CODERABBIT_PROGRESS: "coderabbit_progress"; readonly CODERABBIT_STATUS: "coderabbit_status"; readonly CODERABBIT_REVIEW: "coderabbit_review"; readonly SWITCH_AGENT_PROGRESS: "switch_agent_progress"; readonly SWITCH_AGENT_STATUS: "switch_agent_status"; readonly HANDOFF_PROPOSED: "handoff_proposed"; readonly HANDOFF_RESOLVED: "handoff_resolved"; readonly VCS_AGENT_REVIEW_COMPLETE: "vcs_agent_review_complete"; /** PR-review launch progress toast — the "Review with an agent" flow shows a * toast when the review runs server-side (Inngest). Mobile-only surface, * produced by api-v2 (the CLI neither produces nor consumes it). Mirrored in * repo A. */ readonly PR_REVIEW_LAUNCH: "pr_review_launch"; /** Agent Packs — full `PackRunState` republished by the backend on every * pipeline transition (stage start/done, pause, stall, completion). CLI * posts to /api/packs/events; mobile's pack.store renders the pipeline. * Mirrored in repo A's app-shared events.ts. */ readonly PACK_STATE: "pack_state"; }; type UserEventName = (typeof USER_EVENTS)[keyof typeof USER_EVENTS]; /** * Prompt the CLI sends to the user's linked agent (Claude, Codex, …) * in a headless one-shot to detect how to start the project's dev * server. Same pattern as the AI Insights "summary" prompt — the * agent runs locally with the user's auth, has read access to the * project, and returns a tiny JSON blob the CLI parses. * * Kept here (in `@codeam/shared`) so the CLI build inlines the * exact string at compile time without runtime fetch from the backend. */ declare const PREVIEW_DETECT_PROMPT: string; export { AGENT_REGISTRY, AGENT_STANDARD_BLOCK, AGENT_STANDARD_MARKER, AGENT_STANDARD_TEXT, type AgentAuth, type AgentAuthKind, type AgentId, type AgentMetadata, type AgentMode, type AgentModel, type AgentReviewFinding, type AgentReviewPlan, type AgentReviewReport, type AnswerResolvedEvent, type AwaitingAnswerEvent, type BeadsActionCommand, type BeadsActionKind, type BeadsActionPayload, type BeadsActionRequest, type BeadsActionType, type BeadsConfigureAction, type BeadsDependencyDto, type BeadsDependencyKind, type BeadsIngestPayload, type BeadsIssueDto, type BeadsIssueStatus, type BeadsMemoryDto, type BeadsProjectDto, type BeadsProvisioningPayload, type BeadsProvisioningStatus, type BeadsSnapshotDto, type BeadsStatus, type BeadsStatusState, type BeadsStatusSummary, type BlameLineWire, type BrokeredIntegrationToken, CODER_PROMPT, type ChromeStep, type ChromeToolType, type CommitEntryWire, DEFAULT_API_BASE_URL, DEFAULT_GUARDRAIL_POLICY, DEP_TO_INTEGRATION, DEV_API_BASE_URL, type DerivedCredentialSource, type EnvVar, type FileBlameEvent, type FileChangeStatus, type FileChangedEvent, type FileHistoryEvent, type FileReviewStatus, GUARDRAIL_CATEGORIES, GUARDRAIL_CATEGORY_META, GUARDRAIL_CONFIGURE_COMMAND, GUARDRAIL_DISPOSITIONS, type GuardrailCategory, type GuardrailCategoryMeta, type GuardrailDisposition, type GuardrailPolicy, HANDOFF_FENCE_TAG, HEADROOM_BACKEND_ENV, HEADROOM_EXTRAS_BY_SURFACE, HEADROOM_MODELS, HEADROOM_PIP_COMPANIONS, HEADROOM_PROXY_PORT, HEADROOM_USAGE_COMMAND, HEARTBEAT_INTERVAL_MS_DEFAULT, HOUSE_AGENT_ID, HOUSE_AGENT_NAME, HOUSE_AGENT_PROVIDER, HOUSE_AGENT_SUBTITLE, HOUSE_AGENT_VENDOR, type HandoffProposal, type HandoffResolution, type HeadroomBudgetCommand, type HeadroomBudgetPeriod, type HeadroomBudgetUsage, type HeadroomKind, type HeadroomModelSpec, type HeadroomPythonRenderOpts, type HeadroomStatus, type HeadroomStep, type HeadroomSurface, type HeadroomUsageBucket, type HeadroomUsageGranularity, type HeadroomUsageReport, type HeadroomUsageResult, type HeadroomUsageSlice, type HeadroomUsageTotals, type HunkLineType, INSTALL_SNIPPETS, INTEGRATION_BRANDING, INTEGRATION_REGISTRY, INTERNAL_TO_PUBLIC, type InputSuggestionChunk, type IntegrationApiKeyField, type IntegrationAuthKind, type IntegrationBranding, type IntegrationCategory, type IntegrationDefinition, type IntegrationDelivery, type IntegrationHealth, type IntegrationId, type IntegrationMcpDelivery, type IntegrationStatus, type IntegrationsManifest, type IntegrationsManifestEntry, LINKED_AGENT_IDS, type LinkedAgentId, MODEL_CONTEXT_WINDOW, MODEL_PRICING, type ModelPricing, type NormalizedMessage, OBSERVER_BRIDGE_PORT, PACK_ACTION_COMMAND, PACK_REGISTRY, PACK_START_COMMAND, PACK_STATUS_COMMAND, PACK_WORKFLOW_ARTICLE, PREVIEW_DETECT_PROMPT, PROTOCOL_VERSION, PUBLIC_TO_INTERNAL, type PackActionKind, type PackActionPayload, type PackDefinition, type PackHandoffRecord, type PackId, type PackRunState, type PackRunStatus, type PackStageDef, type PackStageState, type PackStageStatus, type PackStartPayload, type PendingReviewHunkEvent, type PendingReviewHunkLine, type PrCheck, type PrRef, type PrReviewEntry, type PrReviewVerdict, type PreviewDetection, type PreviewErrorStage, type PreviewState, type PreviewStatus, type PullRequestDetail, type PullRequestSummary, QA_PROMPT, REVIEWER_PROMPT, type RemoteCommand, type RepoStack, type RepoStackDetection, SKILL_REGISTRY, SPECIFIER_PROMPT, SQUAD_CONFIGURE_COMMAND, SQUAD_HOP_BUDGET_DEFAULT, SQUAD_HOP_BUDGET_MAX, SQUAD_HOP_BUDGET_MIN, SQUAD_SPECIALTIES, SQUAD_STATS_COMMAND, SSE_SOCKET_TIMEOUT_MS, STACK_TO_RECOMMENDED, SWITCH_AGENT_COMMAND, type SelectPrompt, type SkillDefinition, type SkillDelivery, type SkillFileDelivery, type SkillId, type SkillRail, type SkillsManifest, type SkillsManifestEntry, type SquadAutoConfig, type SquadConfigurePayload, type SquadConfigureResult, type SquadMemberActivity, type SquadRosterAgent, type SquadRosterData, type SquadStatsResult, type StartTaskPayload, type StreamingChunkEvent, type StreamingChunkKind, type SwitchAgentCommand, type SwitchAgentResult, type SwitchAgentStatus, type SwitchAgentStep, TERMINAL_AGENT_PREFIX, UNKNOWN_MODEL_PRICING, UPCOMING_INTEGRATION_IDS, USER_EVENTS, type UserEventName, clampHopBudget, classifyStack, detectedIntegrationsFromDeps, getAgent, getContextWindow, getEnabledAgents, getEnabledIntegrations, getIntegration, getIntegrationBranding, getIntegrationsByCategory, getPackDefinition, getPricing, getSkillDefinition, headroomKindFor, headroomModelPredownloadScript, headroomPipPackage, headroomSnapshotDownloadLine, installableAgentIds, internalToPublic, isGuardrailDisposition, isHeadroomWrappable, isKnownAgentId, isKnownIntegrationId, isKnownModel, isLinkedAgentId, isNoopInstallSnippet, isPackId, isSkillId, normalizeAgentId, normalizeGuardrailPolicy, publicToInternal, recommendForDeps, renderToLines, resolveApiBaseUrl, skillHasRail, toRemoteCommand, tryGetContextWindow };