import { SystemModelMessage, LanguageModelCallOptions, RequestOptions, streamText, JSONSchema7, ModelMessage } from 'ai'; /** * Account-owned code hook upload validation for Convex config-plane sync. * Mirrors core's account hook upload contract without requiring the Node runtime. */ declare const AGENT_HOOK_EVENT_NAMES: readonly ["agent.started", "agent.step.finished", "agent.finished", "agent.failed", "agent.approval.required", "tool.call.started", "tool.call.finished", "tool.result", "subagent.task.started", "subagent.task.finished", "channel.message.received", "channel.message.sending"]; type AgentHookEventName = (typeof AGENT_HOOK_EVENT_NAMES)[number]; /** * Every Vercel AI SDK provider that ships language models, plus `custom` * (any OpenAI-compatible endpoint) and `minimax`. Image-, speech- and * transcription-only providers are deliberately absent: they cannot back * `config.model`. */ declare const MODEL_PROVIDERS: { readonly anthropic: { readonly label: "Anthropic"; readonly modelPlaceholder: "claude-sonnet-4-5-20250929"; }; readonly azure: { readonly label: "Azure OpenAI"; readonly modelPlaceholder: "gpt-4.1-mini"; }; readonly baseten: { readonly label: "Baseten"; readonly modelPlaceholder: "deepseek-ai/DeepSeek-V3"; }; readonly bedrock: { readonly label: "Amazon Bedrock"; readonly modelPlaceholder: "anthropic.claude-sonnet-4-5-20250929-v1:0"; }; readonly cerebras: { readonly label: "Cerebras"; readonly modelPlaceholder: "llama3.1-8b"; }; readonly cohere: { readonly label: "Cohere"; readonly modelPlaceholder: "command-a-03-2025"; }; readonly custom: { readonly label: "Custom OpenAI-compatible"; readonly modelPlaceholder: "gpt-oss-120b"; }; readonly deepinfra: { readonly label: "DeepInfra"; readonly modelPlaceholder: "deepseek-ai/DeepSeek-V3"; }; readonly deepseek: { readonly label: "DeepSeek"; readonly modelPlaceholder: "deepseek-chat"; }; readonly fireworks: { readonly label: "Fireworks"; readonly modelPlaceholder: "accounts/fireworks/models/deepseek-v3"; }; readonly google: { readonly label: "Google"; readonly modelPlaceholder: "gemini-2.5-flash"; }; readonly groq: { readonly label: "Groq"; readonly modelPlaceholder: "llama-3.3-70b-versatile"; }; readonly minimax: { readonly label: "MiniMax"; readonly modelPlaceholder: "MiniMax-M2.7"; }; readonly mistral: { readonly label: "Mistral"; readonly modelPlaceholder: "mistral-large-latest"; }; readonly openai: { readonly label: "OpenAI"; readonly modelPlaceholder: "gpt-4.1-mini"; }; readonly perplexity: { readonly label: "Perplexity"; readonly modelPlaceholder: "sonar-pro"; }; readonly togetherai: { readonly label: "Together.ai"; readonly modelPlaceholder: "deepseek-ai/DeepSeek-V3"; }; readonly v0: { readonly label: "Vercel v0"; readonly modelPlaceholder: "v0-1.0-md"; }; readonly vercel: { readonly label: "Vercel AI Gateway"; readonly modelPlaceholder: "openai/gpt-4.1-mini"; }; readonly vertex: { readonly label: "Google Vertex AI"; readonly modelPlaceholder: "gemini-2.5-flash"; }; readonly xai: { readonly label: "xAI Grok"; readonly modelPlaceholder: "grok-4"; }; }; type AccountModelProviderName = keyof typeof MODEL_PROVIDERS; /** * Agent config validation, the only copy of it: Convex config HTTP checks a * config on write and core re-checks the stored config on every run. Pure * module: safe for the default Convex runtime. The public projection lives in * ./responses.ts. */ declare const AGENT_HARNESS_TYPES: readonly ["claude-code", "codex", "deepagents", "opencode", "pi"]; declare const AGENT_HARNESS_DEBUG_LEVELS: readonly ["error", "warn", "info", "debug", "trace"]; declare const AGENT_HARNESS_PERMISSION_MODES: readonly ["allow-reads", "allow-edits", "allow-all"]; declare const AGENT_LIFECYCLE_EVENT_NAMES: readonly ["agent.started", "agent.step.finished", "agent.finished", "agent.failed", "agent.approval.required", "tool.call.started", "tool.call.finished", "tool.result", "subagent.task.started", "subagent.task.finished"]; /** * Shared validation for MCP server registrations (#331). One normalizer * serves every write path (CLI sync, direct API, dashboard). A `url` makes an "http" row * core connects to over the stateless 2026-07-28 transport; a `bundle` makes * a "hosted" row served by the mcp-runner Lambda, hashed here so sha256 * always travels with the bundle. Auth header values may carry ${NAME} * account env refs; they resolve into the encrypted agent config at sync * time, never on this row, and credential-bearing headers must use one * instead of an inline secret. `oauth` follows the same rule: clientSecret * and refreshToken must be ${NAME} refs, so the row never holds a secret. */ /** * OAuth 2.0 refresh-token grant for an external row. Core mints access tokens * at connect time and sends `Authorization: Bearer `, so a server * whose tokens expire (Google's Workspace MCP endpoints) still works where a * static header cannot. Secret fields hold ${NAME} refs on the row. */ interface McpOauth { clientId: string; clientSecret: string; refreshToken: string; /** Token endpoint, https only; core defaults to https://oauth2.googleapis.com/token. */ tokenUrl?: string; } /** * Agent configuration: types for the per-agent settings object, the runtime * projection of a stored config, and encryption helpers. The validation rules * are the config plane's (`@broods/convex/model/agentRules`), so a config is * judged the same on write and on every run. * Account types and auth live in `./accounts.ts` and `../auth.ts`. */ interface AgentConfig { agent?: AgentBehaviorConfig; harness?: AgentHarnessConfig; model?: AgentModelConfig; provider?: AgentProviderConfig; sandboxes?: string[]; workspaces?: AgentWorkspaceRef[]; session?: AgentSessionConfig; hooks?: AgentHooksConfig; channels?: AgentChannelsConfig; tools?: AgentToolsConfig; /** Connected MCP servers, keyed by their config-plane row id (#331). */ mcp?: AgentMcpConfig; /** * Tool names withheld for this run, applied after the tool set is built. * Set by a channel record; a channel can take a tool away, never add one. * Names that are not present are ignored. */ denyTools?: string[]; skills?: AgentSkillsConfig; subagent?: AgentSubagentConfig; scheduler?: AgentSchedulerConfig; /** Policies that gate this agent. Each one carries its own enforcement mode. */ policies?: string[]; publicAccess?: boolean; allowRunOverrides?: boolean; [key: string]: unknown; } interface AgentBehaviorConfig { maxTurn?: number; system?: string | SystemModelMessage | SystemModelMessage[]; [key: string]: unknown; } interface AgentHarnessConfig { activeTools?: string[]; debug?: AgentHarnessDebugConfig; inactiveTools?: string[]; type: (typeof AGENT_HARNESS_TYPES)[number]; permissionMode?: (typeof AGENT_HARNESS_PERMISSION_MODES)[number]; startupTimeoutMs?: number; webSearch?: boolean; } interface AgentHarnessDebugConfig { enabled?: boolean; level?: (typeof AGENT_HARNESS_DEBUG_LEVELS)[number]; subsystems?: string[]; } type StreamTextOptions = Parameters[0]; type AgentModelProviderOptions = StreamTextOptions["providerOptions"]; interface AgentSkillsConfig { enabled?: boolean; allowed?: string[]; [key: string]: unknown; } /** * Opt-in for the `schedule` tool. Off by default: a scheduled task starts * billable agent runs long after the turn that asked for it. */ interface AgentSchedulerConfig { enabled?: boolean; [key: string]: unknown; } interface AgentSubagentConfig { enabled?: boolean; allowed?: string[]; context?: "new" | "inherited"; mode?: "ephemeral" | "persistent"; /** * Publishes child reasoning, text, and tool stream parts to the existing * WebSocket/JetStream response path. Off by default. */ stream?: boolean; /** * Controls what the parent agent sees from a finished subagent (AI SDK * "controlling what the model sees"): `full` = the child's whole transcript, * `result` = only its final result (default), `none` = nothing. A * `subagent.task.finished` code hook overrides this for custom shaping. */ visibility?: "full" | "result" | "none"; [key: string]: unknown; } interface AgentModelConfig extends LanguageModelCallOptions, Partial> { provider?: AccountModelProviderName; modelId?: string; /** * Speech-to-text model for inbound audio, on the same provider and key as * `modelId`. Defaults to the widest-container model the provider ships. */ transcriptionModelId?: string; providerOptions?: AgentModelProviderOptions; output?: AgentModelOutputConfig; } type AgentModelOutputConfig = ({ type: "text"; } & AgentModelOutputMetadata) | ({ type: "object"; schema: JSONSchema7; } & AgentModelOutputMetadata) | ({ type: "array"; element: JSONSchema7; } & AgentModelOutputMetadata) | ({ type: "choice"; options: string[]; } & AgentModelOutputMetadata) | ({ type: "json"; } & AgentModelOutputMetadata); type AgentModelOutputMetadata = { name?: string; description?: string; [key: string]: unknown; }; type AgentProviderConfig = Partial>; interface AgentProviderSettings { apiKey?: string; /** OpenAI-compatible endpoint (`custom`). Snake form, as documented. */ base_url?: string; /** OpenAI-compatible endpoint (`custom`). AI-SDK form; the dashboard writes both. */ baseURL?: string; headers?: Record; /** Endpoint label; becomes the provider id and the pi harness env prefix. */ name?: string; organization?: string; project?: string; [key: string]: unknown; } interface AgentWorkspaceRef { name: string; workspaceId: string; sandbox?: string | null; } interface AgentSessionConfig { pruning?: AgentSessionPruningConfig; compaction?: AgentSessionCompactionConfig; [key: string]: unknown; } interface AgentSessionPruningConfig { enabled?: boolean; [key: string]: unknown; } interface AgentSessionCompactionConfig { enabled?: boolean; maxContextLength?: number; [key: string]: unknown; } interface AgentHooksConfig { /** Outbound event webhooks. An agent may register several independent endpoints. */ webhooks?: AgentWebhookHookConfig[]; /** * Uploaded code hooks. Each entry references an accountHooks bundle by id; the * bundle runs in the V8 isolate at the matching fire-points and its validated * return is folded into mutable harness state. */ code?: AgentCodeHookConfig[]; [key: string]: unknown; } interface AgentCodeHookConfig { hookId: string; /** * Optional narrowing of the events this reference reacts to. Omitted => the * bundle's own declared `events` set. Any listed event outside the bundle's * declared set is ignored at runtime. */ events?: AgentHookEventName[]; enabled?: boolean; [key: string]: unknown; } interface AgentWebhookHookConfig { enabled?: boolean; url?: string; secret?: string; events?: AgentLifecycleEventName[]; [key: string]: unknown; } type AgentLifecycleEventName = (typeof AGENT_LIFECYCLE_EVENT_NAMES)[number]; type AgentToolsConfig = Record; interface AgentToolConfig { enabled?: boolean; needsApproval?: boolean; async?: boolean; config?: Record; [key: string]: unknown; } type AgentMcpConfig = Record; interface AgentMcpEntry { enabled?: boolean; /** Applies to every tool the server exposes. */ needsApproval?: boolean; /** Extra request headers; values resolved from account env vars at sync. */ headers?: Record; /** * Overrides for the row's oauth credentials; values resolved from account * env vars at sync, so the row's ${NAME} refs never reach the token * endpoint. tokenUrl stays on the row, where registration checked it. */ oauth?: Partial>; [key: string]: unknown; } interface AgentChannelsConfig { telegram?: AgentTelegramChannelConfig; github?: AgentGitHubChannelConfig; slack?: AgentSlackChannelConfig; discord?: AgentDiscordChannelConfig; pancake?: AgentPancakeChannelConfig; zalo?: AgentZaloChannelConfig; matrix?: AgentMatrixChannelConfig; [key: string]: unknown; } /** * How an attached partitioned workspace splits its folders for runs arriving * through this door. `shared` mounts the workspace root; `conversation` mounts * a private child folder per thread, issue or chat under `alias`. */ type ChannelPartition = { by: "shared"; alias?: never; } | { by: "conversation"; alias: string; }; interface AgentTelegramChannelConfig { id?: string; apiUrl?: string; botToken?: string; webhookSecret?: string; allowedChannelIds?: string[]; allowedUserIds?: string[]; /** Bot's @username, e.g. `tracy_bot`. Set it to answer only when the agent is mentioned. */ botUsername?: string; reactionEmoji?: string; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } interface AgentGitHubChannelConfig { id?: string; apiUrl?: string; webhookSecret?: string; appId?: string; privateKey?: string; allowedChannelIds?: string[]; allowedUserIds?: string[]; /** Bot username for @-mention detection (e.g. "my-bot" or "my-bot[bot]"). */ botUserName?: string; /** Bot's numeric GitHub user ID for self-message detection. */ botUserId?: number; /** When false, the bot does not auto-trigger on new issues (opened/edited/reopened). Defaults to true. The bot still triggers when assigned to an issue. */ triggerOnIssueOpen?: boolean; /** When false, the bot does not auto-trigger on new PRs (opened/edited/reopened). Defaults to true. The bot still triggers when assigned to a PR. */ triggerOnPROpen?: boolean; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } interface AgentSlackChannelConfig { id?: string; apiUrl?: string; botToken?: string; signingSecret?: string; allowedChannelIds?: string[]; allowedUserIds?: string[]; reactionEmoji?: string; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } interface AgentDiscordChannelConfig { id?: string; apiUrl?: string; botToken?: string; publicKey?: string; allowedChannelIds?: string[]; allowedUserIds?: string[]; /** Bot's Discord user id. Set it to answer only when the agent is mentioned. */ botUserId?: string; /** Role ids that count as mentioning the agent, e.g. an on-call role. */ mentionRoleIds?: string[]; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } /** * A Matrix account the agent speaks as. Matrix has no bot concept, so this is * usually a person's own account: replies carry a per-message profile named * `botName`, and `mentionText` decides what addresses the agent, because a * mention of the account would also be a mention of its owner. */ interface AgentMatrixChannelConfig { id?: string; /** Homeserver base URL, e.g. `https://matrix.org`. */ apiUrl?: string; /** Access token of the account; `apps/matrix-forwarder` syncs with it. */ botToken?: string; /** Room ids. */ allowedChannelIds?: string[]; /** Matrix user ids, e.g. `@alice:matrix.org`. */ allowedUserIds?: string[]; /** Name replies are shown under. Unset, replies look like the account's own messages. */ botName?: string; /** * Text that addresses the agent, e.g. `@georgi-ai`. Unset, a mention of the * account does. Other messages are stored as context either way. */ mentionText?: string; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } interface AgentPancakeChannelConfig { allowedChannelIds?: string[]; allowedUserIds?: string[]; id?: string; pageId?: string; pageAccessToken?: string; webhookSecret?: string; senderId?: string; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } interface AgentZaloChannelConfig { allowedChannelIds?: string[]; allowedUserIds?: string[]; id?: string; botToken?: string; webhookSecret?: string; trace?: "enabled" | "disabled"; partition?: ChannelPartition; [key: string]: unknown; } /** * Cron-job records and patch-merge helpers for the runtime. Input * normalization lives in the config plane (packages/convex/model/cronRules.ts, * the single home of those rules). */ type CronStatus = "active" | "paused"; type CronLastStatus = "started" | "completed" | "failed"; /** * One-of run payload mirroring the agent direct API's AgentRunInput: provide a * single `input` string (wrapped into one user message) or a full `events` list. */ type CronRunInput = { input: string; events?: never; } | { events: ModelMessage[]; input?: never; }; type CreateCronInput = { name: string; description?: string; agentId: string; conversationKey?: string; scheduleExpression: string; timezone?: string; status?: CronStatus; } & CronRunInput; type UpdateCronInput = { name?: string; description?: string | null; agentId?: string; conversationKey?: string | null; scheduleExpression?: string; timezone?: string | null; status?: CronStatus; } & ({ input?: string; events?: never; } | { events?: ModelMessage[]; input?: never; }); /** * Sandbox-config validation for the Convex config plane. Ports core's former * storage/sandbox-config.ts normalizer so the public /v1/sandboxes contract * is unchanged. The public projection lives in ./responses.ts. */ declare const SANDBOX_PROVIDERS: readonly ["sandbox", "lambda", "e2b", "daytona", "vercel", "machine"]; declare const SANDBOX_RUNTIMES: readonly ["bash", "python", "node"]; declare const SANDBOX_PERMISSION_MODES: readonly ["edit", "ask", "bypass"]; declare const SANDBOX_NETWORK_MODES: readonly ["allow-all", "deny-all", "restricted"]; declare const SANDBOX_SIZE_NAMES: readonly ["tiny", "xsmall", "small", "medium", "large"]; type SandboxProvider = (typeof SANDBOX_PROVIDERS)[number]; type RuntimeName = (typeof SANDBOX_RUNTIMES)[number]; type PermissionMode = (typeof SANDBOX_PERMISSION_MODES)[number]; type NetworkMode = (typeof SANDBOX_NETWORK_MODES)[number]; type SandboxSize = (typeof SANDBOX_SIZE_NAMES)[number]; /** * Idle and maximum lifetime controls for a persistent sandbox. */ interface SandboxLifecycleConfig { idleTimeoutSeconds?: number; maxLifetimeSeconds?: number; } interface SandboxNetworkConfig { mode: NetworkMode; allowDomains?: string[]; allowCidrs?: string[]; } /** * Account-scoped reusable sandbox configuration referenced by agents. */ interface SandboxConfig { provider: SandboxProvider; fallbackProvider?: SandboxProvider; size?: SandboxSize; snapshot?: string; runtimes?: RuntimeName[]; network?: SandboxNetworkConfig; permissionMode?: PermissionMode; persistent?: boolean; lifecycle?: SandboxLifecycleConfig; onCreate?: string[]; onResume?: string[]; timeout?: number; memoryLimit?: number; outputLimitBytes?: number; envVars?: Record; options?: Record; } declare const WORKSPACE_STORAGE_PROVIDERS: readonly ["s3"]; type WorkspaceStorageProvider = (typeof WORKSPACE_STORAGE_PROVIDERS)[number]; type WorkspaceStorageAuth = { type: "managed"; } | { type: "assumeRole"; roleArn: string; externalId?: string; }; interface WorkspaceStorageConfig { provider: WorkspaceStorageProvider; bucket?: string; region?: string; endpoint?: string; prefix?: string; auth?: WorkspaceStorageAuth; } interface WorkspaceConfig { storage: WorkspaceStorageConfig; isolation?: boolean; harness?: { workspace?: { enabled?: boolean; }; memory?: { enabled?: boolean; }; }; } /** * Agent-policy validation for the Convex config plane. Ports core's public * CRUD normalizer so policy documents keep the account-management API * contract. The public projection lives in ./responses.ts. */ declare const AGENT_POLICY_ACTIONS: readonly ["agent.invoke", "tool.call", "workspace.read", "workspace.write", "workspace.exec", "subagent.run", "skill.load"]; /** * API action namespace for account roles: one read/write pair per config-plane * resource route. Roles carry the same PolicyDocument shape as agent policies; * each caller passes its action set to `normalizePolicyDocument`. */ declare const API_POLICY_ACTIONS: readonly ["account:read", "account:write", "agents:read", "agents:write", "channels:read", "channels:write", "crons:read", "crons:write", "env:read", "env:write", "hooks:read", "hooks:write", "mcp:read", "mcp:write", "policies:read", "policies:write", "sandboxes:read", "sandboxes:write", "skills:read", "skills:write", "tools:read", "tools:write", "workspaces:read", "workspaces:write"]; type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number]; type ApiPolicyAction = (typeof API_POLICY_ACTIONS)[number]; type PolicyAction = AgentPolicyAction | ApiPolicyAction; interface PolicyCondition { attribute: string; operator: PolicyConditionOperator; value: string | number | boolean | string[] | number[] | boolean[]; } type PolicyConditionOperator = "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains"; /** * Versioned policy document accepted by account-management CRUD. */ interface PolicyDocument { version: 1; /** How hard this policy bites where it is attached. Omitted reads as `audit`. */ mode?: "enforce" | "audit"; rules: PolicyRule[]; } type PolicyEffect = "allow" | "deny"; interface PolicyResourceSelector { toolNames?: string[]; /** MCP registration ids, for scoping tool.call rules per server (#331). */ mcpIds?: string[]; workspaceIds?: string[]; workspaceNames?: string[]; filePaths?: string[]; subagentIds?: string[]; skillPaths?: string[]; /** Config-plane resource ids for API-action rules; "*" matches every id. */ resourceIds?: string[]; } interface PolicyRule { id: string; effect: PolicyEffect; actions: PolicyAction[]; resources?: PolicyResourceSelector; conditions?: PolicyCondition[]; } /** * Channel record validation for the config plane, the single home of the * normalizers core's `shared/domain/channel-record.ts` re-exports. Kept free * of Convex imports so the rules stay unit-testable; the public projection * lives in ./responses.ts. */ /** Where a reply lands: its own thread, or wherever the message came from. */ declare const CHANNEL_REPLY_TARGETS: readonly ["thread", "source"]; type ChannelReplyIn = (typeof CHANNEL_REPLY_TARGETS)[number]; /** * Wire types for the public account-manage and harness APIs. These mirror * the deployed API contract (docs/api-reference/openapi.yaml is the source * of truth); they are intentionally independent of the runtime internals so * the SDK keeps working when the runtime is ported. */ interface Cron { accountId: string; cronId: string; name: string; description?: string; agentId: string; events: ModelMessage[]; conversationKey?: string; scheduleExpression: string; timezone?: string; status: CronStatus; createdAt: string; updatedAt: string; lastInvokedAt?: string; lastStatus?: CronLastStatus; lastError?: string; } interface CronRun { accountId: string; cronId: string; runId: string; eventId: string; conversationKey: string; status: CronLastStatus; result?: unknown; error?: string; startedAt: string; completedAt?: string; } interface Skill { path: string; name: string; description: string; files?: Array<{ path: string; size?: number; }>; } /** * Account config-plane client for the broods public account REST API. * * This is the DYNAMIC counterpart to the config-first `broods dev` / `broods * deploy` flow: `broods dev` syncs the predefined resources declared in your * `broods/` folder, while `BroodsAccountClient` creates and mutates the full * account config plane at runtime: agents, sandboxes (config + lifecycle), * workspaces (config + files), tools, policies, skills, crons, and the account * itself. One caller is a multi-tenant app provisioning one agent per customer * from its own backend. * * Kept intentionally standalone (import from `broods/account`): pure fetch, * no Node built-ins, no `.env` file loading, so it runs in edge/worker * runtimes such as Convex actions, Cloudflare Workers, and the browser-less * server runtimes, as well as Node and Bun. * * Auth: every call sends `Authorization: Bearer {accountSecret}` to * `{baseUrl}/v1/...`, or a short-lived `fp_sts_` role session token from * `assumeRole()`, limited to what the role's policy allows. Secrets inside * agent configs are encrypted at rest by the platform and come back redacted * (`********`) on reads. */ interface BroodsAccountClientOptions { /** Base URL of the broods gateway. Falls back to `BROODS_BASE_URL`, then `https://gateway.broods.app`. */ baseUrl?: string; /** Account secret used as the Bearer token. Falls back to `BROODS_ACCOUNT_SECRET`. */ accountSecret?: string; /** * Short-lived `fp_sts_` role session token (from {@link BroodsAccountClient.assumeRole}) * used as the Bearer instead of the account secret. The session can only do * what its role's policy allows. Falls back to `BROODS_SESSION_TOKEN`. */ sessionToken?: string; fetch?: typeof fetch; } /** Public account record returned by `GET /v1/account`. */ interface BroodsAccount { accountId: string; username: string; status: string; [key: string]: unknown; } /** Public agent record; `config` comes back with secret values redacted. */ interface AccountAgent { accountId: string; agentId: string; name: string; description?: string; status: string; config: AgentConfig; createdAt: string; updatedAt: string; } interface CreateAgentResult { accountId: string; agentId: string; name: string; description?: string; } /** Write-only account environment variable metadata. */ interface AccountEnvVar { name: string; /** ISO 8601, like every other timestamp in the API. */ updatedAt: string; } /** Fields accepted by `PATCH /v1/agents/{id}`. `config` is deep-merged; `null` values delete keys. */ interface UpdateAgentInput { name?: string; description?: string | null; config?: unknown; } /** Public workspace record returned by the workspaces routes. */ interface AccountWorkspace { accountId: string; workspaceId: string; name: string; description?: string; config: WorkspaceConfig; createdAt: string; updatedAt: string; } /** One entry of a workspace file listing (`GET /v1/workspaces/{id}/files`). */ interface WorkspaceFileEntry { path: string; name: string; isFolder: boolean; sizeBytes?: number; updatedAt?: string; } /** Public sandbox config record; `config` comes back with secret values (e.g. `envVars`) redacted. */ interface AccountSandbox { accountId: string; sandboxId: string; name: string; description?: string; config: SandboxConfig; createdAt: string; updatedAt: string; [key: string]: unknown; } /** Public agent-policy record returned by the policies routes. */ interface AccountPolicy { accountId: string; policyId: string; name: string; description?: string; document: PolicyDocument; status: string; createdAt: string; updatedAt: string; } /** * Public account-role record returned by the roles routes. The policy uses the * API action namespace (`"agents:read"`, `"crons:write"`, ...); `projectId` and * `stageId` bound which stage runtime keys may assume the role. */ interface AccountRole { accountId: string; roleId: string; name: string; projectId?: string; stageId?: string; status: "active" | "disabled"; policy: PolicyDocument; createdAt: string; updatedAt: string; } /** Short-lived role session minted by `POST /v1/account/assume-role`. */ interface AssumeRoleResult { /** `fp_sts_` bearer token; pass it as `sessionToken` to a new client. */ token: string; /** ISO timestamp when the session stops working. */ expiresAt: string; } /** * One real place a team talks, bound to an agent: a Slack channel, a Discord * channel, a repo. The runtime reads it on the inbound webhook to decide who * answers there and with what instructions, workspaces and policies. */ interface AccountChannel { accountId: string; channelId: string; platform: string; externalId: string; workspaceRef?: string; name: string; description?: string; config: ChannelRecordConfig; status: "active" | "deleted"; createdAt: string; updatedAt: string; } /** A channel record narrows and adds; it never grants capability the agent lacks. */ interface ChannelRecordConfig { /** Appended after the agent's own system prompt. */ instructions?: string; agentBindings: Array<{ agentId: string; isDefault?: boolean; }>; workspaces?: Array<{ name: string; workspaceId: string; }>; /** Added to whatever the agent already carries. Each policy holds its own mode. */ policies?: string[]; /** * Tool names withheld in this channel, applied after the tool set is built, * so it also covers sandbox tools (`bash`, `read`, …) that `config.tools` * cannot name. Narrowing only; unknown names are ignored. */ denyTools?: string[]; /** * Where the reply lands. `source` answers wherever the message came from, and * threads only when the message already did. Slack only. No other provider * gives the runtime a second place to reply. */ replyIn?: ChannelReplyIn; partition?: ChannelPartition; /** Images the agent may stand a sandbox up from for a thread here. */ sandboxImages?: string[]; /** Named groups of people, readable from policy conditions as `userRoles`. */ tagRoles?: Array<{ roleId: string; userIds: string[]; }>; } /** The stage a resource belongs to. Same name in two stages = two resources. */ interface StageScope { project: string; stage: string; } /** * OAuth 2.0 refresh-token grant on an external MCP row. The runtime mints and * refreshes access tokens and sends `Authorization: Bearer ` itself. * `clientSecret` and `refreshToken` must be `${NAME}` account env refs; * `tokenUrl` defaults to https://oauth2.googleapis.com/token. */ interface McpOauthInput { clientId: string; clientSecret: string; refreshToken: string; tokenUrl?: string; } /** Public MCP server registration returned by the `/v1/mcp` routes (#331). */ interface AccountMcp { accountId: string; serverId: string; projectId: string; stageId: string; name: string; description?: string; transport: "http" | "hosted"; /** External servers only; a hosted row has no endpoint of its own. */ url?: string; /** Hosted servers only: content hash of the uploaded bundle. */ sha256?: string; headers?: Record; oauth?: McpOauthInput; allowedTools?: string[]; disabled: boolean; status: string; createdAt: string; updatedAt: string; deletedAt?: string; } /** * Fields accepted by `POST /v1/mcp`: `url` connects, `bundle` uploads inline * (≤10 MB); a larger bundle goes through `uploadMcpBundle` first. */ interface CreateMcpInput { name: string; description?: string; url?: string; bundle?: string; bundleStorageId?: string; sha256?: string; headers?: Record; oauth?: McpOauthInput; allowedTools?: string[]; } /** Fields accepted by `PATCH /v1/mcp/{serverId}`; every field is optional. */ interface UpdateMcpInput { name?: string; description?: string; url?: string; bundle?: string; bundleStorageId?: string; sha256?: string; headers?: Record; oauth?: McpOauthInput; allowedTools?: string[]; disabled?: boolean; } /** * Body of a skill upload (`POST /v1/skills`, `PUT /v1/skills/{skillName}`). * `json` needs `name`/`description`/`content`; `files` needs base64 `files` * including a root `SKILL.md`; `github` needs a tree `url`. */ interface SkillUploadInput { source: "json" | "files" | "github"; name?: string; description?: string; content?: string; files?: Array<{ path: string; contentBase64: string; contentType?: string; }>; url?: string; } /** Result of `POST /v1/account/rotate-secret`. The returned `secret` is shown once; the old secret stops working immediately. */ interface RotateSecretResult { account: BroodsAccount; secret: string; } /** Result of `DELETE /v1/account`: the account and all account-scoped data are removed; `cleanup` reports per-resource deletion counts. */ interface DeleteAccountResult { deleted: boolean; cleanup?: Record; } /** Result of a suspend/resume/terminate sandbox lifecycle action. */ interface SandboxLifecycleResult { status: string; } /** Result of `POST /v1/sandboxes/{id}/snapshot`. */ interface SandboxSnapshotResult { status: string; snapshotId?: string; externalImageId?: string; } /** Sealed ticket from `POST /v1/sandboxes/{id}/terminal`; hand `token` to the gateway terminal WebSocket at `websocketPath`. */ interface SandboxTerminalTicket { token: string; expiresAt: number; websocketPath: string; } /** Non-2xx response from the account API (404s on id routes return null instead). */ declare class BroodsAccountApiError extends Error { readonly status: number; readonly body: string; constructor(method: string, path: string, status: number, body: string); } /** Build a validated account env-var reference for use in an agent config. */ declare function envPlaceholder(name: string): string; /** * The credential the environment supplies, with a role session winning over * the account secret. The constructor throws through this same resolution, so * callers that can run without an account credential (`broods mcp` with only a * stored login) probe here instead of catching the constructor. */ declare function resolveEnvCredential(): string | undefined; /** * Typed client for the broods account config API. All `get`/`update`/`delete` * methods return `null`/`false` when the resource does not exist (HTTP 404) so * callers can implement upsert flows without try/catch; every other non-2xx * status throws {@link BroodsAccountApiError}. */ declare class BroodsAccountClient { private readonly baseUrl; private readonly bearerToken; private readonly fetchImpl; constructor(options?: BroodsAccountClientOptions); /** The account this secret belongs to. Its `accountId` is the first segment of channel webhook URLs. */ getAccount(): Promise; /** Update account metadata (username/description). Returns null when the account is gone. Runtime config is managed through the agent endpoints. */ updateAccount(patch: { username?: string; description?: string | null; }): Promise; /** * Exchange a role for a short-lived `fp_sts_` session token. Callable with * the account secret, a CLI login token, or a stage runtime key (the latter * only into roles scoped to the key's own project/stage). Construct a new * client with `{ sessionToken: result.token }` to act as the role. */ assumeRole(roleId: string, options?: { ttlSeconds?: number; }): Promise; /** Rotate the account secret. The returned `secret` is shown once and the current secret stops working immediately, so persist it before the process exits. */ rotateSecret(): Promise; /** Delete this account and cascade-clean every account-scoped resource. `cleanup` reports per-resource deletion counts. */ deleteAccount(): Promise; /** * Provider webhook URL for one of the account's channels. Paste this into the * provider's webhook settings (Slack Event Subscriptions, Zalo OA webhook, * Pancake page webhook). Routing is per account + channel: the credentials * that verify the request pick the receiving agent, and a channel record * binds each place to the agent that should answer there. */ webhookUrl(accountId: string, channelType: string): string; listAgents(): Promise; createAgent(input: { name: string; description?: string; config: unknown; }): Promise; getAgent(agentId: string): Promise; /** PATCH an agent. `config` deep-merges into the stored config; `null` leaves delete keys. Returns null when the agent is gone. */ updateAgent(agentId: string, patch: UpdateAgentInput): Promise; deleteAgent(agentId: string): Promise; /** List account environment variable names and update timestamps; values are never returned. */ listEnvVars(): Promise; /** Create or replace one write-only account environment variable. */ setEnvVar(name: string, value: string): Promise; /** Delete one account environment variable. Returns false when it is already absent. */ deleteEnvVar(name: string): Promise; listCrons(): Promise; createCron(input: CreateCronInput): Promise; getCron(cronId: string): Promise; updateCron(cronId: string, patch: UpdateCronInput): Promise; deleteCron(cronId: string): Promise; /** Run history for a cron, newest first. Returns [] when the cron is gone. */ listCronRuns(cronId: string, options?: { limit?: number; }): Promise; listWorkspaces(): Promise; createWorkspace(input: { name: string; description?: string; config?: unknown; }): Promise; getWorkspace(workspaceId: string): Promise; updateWorkspace(workspaceId: string, patch: { name?: string; description?: string | null; config?: unknown; }): Promise; deleteWorkspace(workspaceId: string): Promise; /** Flat listing of every file in the workspace's S3-backed filesystem. Returns [] when the workspace is gone. */ listWorkspaceFiles(workspaceId: string): Promise; /** Short-lived download URL for one workspace file. Returns null when the workspace or file is gone. */ getWorkspaceFileUrl(workspaceId: string, path: string): Promise; /** Upload or replace one workspace file from base64 content. Throws when the workspace is gone (404). */ uploadWorkspaceFile(workspaceId: string, input: { path: string; contentBase64: string; contentType?: string; }): Promise; /** Rename a workspace file or folder. Returns false when the workspace or source path is gone. */ renameWorkspaceFile(workspaceId: string, path: string, newPath: string): Promise; /** Delete a workspace file or folder. Returns false when the workspace or path is gone. */ deleteWorkspaceFile(workspaceId: string, path: string): Promise; listSandboxes(): Promise; createSandbox(input: { name: string; description?: string; config?: unknown; }): Promise; getSandbox(sandboxId: string): Promise; /** PATCH a sandbox config. `config` fully replaces the stored config. Returns null when the sandbox is gone. */ updateSandbox(sandboxId: string, patch: { name?: string; description?: string | null; config?: unknown; }): Promise; deleteSandbox(sandboxId: string): Promise; /** Suspend a persistent sandbox reservation. Throws on 404/403/409 (missing sandbox, foreign reservation, or unsupported provider). */ suspendSandbox(sandboxId: string, reservationKey: string): Promise; /** Resume a persistent sandbox reservation. Throws on 404/403/409. */ resumeSandbox(sandboxId: string, reservationKey: string): Promise; /** Terminate a persistent sandbox reservation and drop its live-instance row. Throws on 404/403/409. */ terminateSandbox(sandboxId: string, reservationKey: string): Promise; /** Snapshot a persistent sandbox reservation into a reusable image (self-hosted `sandbox` provider). Throws on 404/403/409. */ snapshotSandbox(sandboxId: string, reservationKey: string, name: string): Promise; /** Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox (`sandbox`/`lambda` providers). Throws on 404/403/409. */ openSandboxTerminal(sandboxId: string, reservationKey: string): Promise; /** MCP servers live in one stage, so the collection routes need a scope. */ listMcp(scope: StageScope): Promise; createMcp(scope: StageScope, input: CreateMcpInput): Promise; /** * Upload a hosted MCP bundle too large for the JSON body; returns the * `bundleStorageId` + `sha256` pair `createMcp`/`updateMcp` accept. */ uploadMcpBundle(bundle: string): Promise<{ bundleStorageId: string; sha256: string; }>; getMcp(serverId: string): Promise; updateMcp(serverId: string, patch: UpdateMcpInput): Promise; deleteMcp(serverId: string): Promise; listPolicies(): Promise; createPolicy(input: { name: string; description?: string; document: PolicyDocument; }): Promise; getPolicy(policyId: string): Promise; /** PATCH a policy. `description: null` clears it. Returns null when the policy is gone. */ updatePolicy(policyId: string, patch: { name?: string; description?: string | null; document?: PolicyDocument; status?: string; }): Promise; deletePolicy(policyId: string): Promise; /** Roles are account-secret only: a session cannot list, mint, or edit roles. */ listRoles(): Promise; /** Create a role whose policy uses the API action namespace. `projectId`/`stageId` must be provided together. */ createRole(input: { name: string; policy: PolicyDocument; projectId?: string; stageId?: string; }): Promise; getRole(roleId: string): Promise; /** PATCH a role. `status: "disabled"` kills every live session of the role. Returns null when the role is gone. */ updateRole(roleId: string, patch: { name?: string; policy?: PolicyDocument; status?: "active" | "disabled"; }): Promise; deleteRole(roleId: string): Promise; listChannels(): Promise; /** Bind one real chat channel to an agent. One active record per place. */ createChannel(input: { platform: string; externalId: string; workspaceRef?: string; name: string; description?: string; config: ChannelRecordConfig; }): Promise; getChannel(channelId: string): Promise; /** PATCH a channel. `description: null` clears it. Returns null when it is gone. */ updateChannel(channelId: string, patch: { name?: string; description?: string | null; workspaceRef?: string | null; config?: ChannelRecordConfig; status?: "active" | "deleted"; }): Promise; deleteChannel(channelId: string): Promise; /** All skills for the account, each with its `/` path. */ listSkills(): Promise; /** Upload a new skill from JSON content, a base64 file bundle, or a GitHub tree URL. Every bundle must include a root `SKILL.md`. */ createSkill(input: SkillUploadInput): Promise; getSkill(skillName: string): Promise; /** Replace a skill's bundle in place (`PUT`). Throws when the skill is gone (404). */ uploadSkill(skillName: string, input: SkillUploadInput): Promise; deleteSkill(skillName: string): Promise; /** POST a sandbox lifecycle action, throwing on any non-2xx (including 404, since these are not upsert flows). */ private sandboxAction; private request; } export { BroodsAccountApiError, BroodsAccountClient, envPlaceholder, resolveEnvCredential }; export type { AccountAgent, AccountChannel, AccountEnvVar, AccountMcp, AccountPolicy, AccountRole, AccountSandbox, AccountWorkspace, AssumeRoleResult, BroodsAccount, BroodsAccountClientOptions, ChannelRecordConfig, CreateAgentResult, CreateMcpInput, DeleteAccountResult, McpOauthInput, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, StageScope, UpdateAgentInput, UpdateMcpInput, WorkspaceFileEntry };