broods 0.5.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@ type AccountModelProviderName = keyof typeof ACCOUNT_MODEL_PROVIDERS;
19
19
  * Agent policy contracts and validation.
20
20
  * Runtime decisions are made by OPA using the same document/input shape.
21
21
  */
22
- declare const AGENT_POLICY_ACTIONS: readonly ["tool.call", "workspace.read", "workspace.write", "workspace.exec", "subagent.run", "skill.load"];
22
+ declare const AGENT_POLICY_ACTIONS: readonly ["agent.invoke", "tool.call", "workspace.read", "workspace.write", "workspace.exec", "subagent.run", "skill.load"];
23
23
  type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number];
24
24
  type AgentPolicyEffect = "allow" | "deny";
25
25
  type AgentPolicyMode = "enforce" | "audit";
@@ -60,8 +60,12 @@ interface AgentPolicyConfig {
60
60
  * Account types and auth live in `./accounts.ts` and `../auth.ts`.
61
61
  */
62
62
 
63
+ declare const AGENT_HARNESS_TYPES: readonly ["claude-code", "codex", "deepagents", "opencode", "pi"];
64
+ declare const AGENT_HARNESS_DEBUG_LEVELS: readonly ["error", "warn", "info", "debug", "trace"];
65
+ declare const AGENT_HARNESS_PERMISSION_MODES: readonly ["allow-reads", "allow-edits", "allow-all"];
63
66
  interface AgentConfig {
64
67
  agent?: AgentBehaviorConfig;
68
+ harness?: AgentHarnessConfig;
65
69
  model?: AgentModelConfig;
66
70
  provider?: AgentProviderConfig;
67
71
  sandbox?: string;
@@ -70,6 +74,12 @@ interface AgentConfig {
70
74
  hooks?: AgentHooksConfig;
71
75
  channels?: AgentChannelsConfig;
72
76
  tools?: AgentToolsConfig;
77
+ /**
78
+ * Tool names withheld for this run, applied after the tool set is built.
79
+ * Set by a channel record; a channel can take a tool away, never add one.
80
+ * Names that are not present are simply ignored.
81
+ */
82
+ denyTools?: string[];
73
83
  skills?: AgentSkillsConfig;
74
84
  subagent?: AgentSubagentConfig;
75
85
  policy?: AgentPolicyConfig;
@@ -81,6 +91,20 @@ interface AgentBehaviorConfig {
81
91
  system?: string | SystemModelMessage | SystemModelMessage[];
82
92
  [key: string]: unknown;
83
93
  }
94
+ interface AgentHarnessConfig {
95
+ activeTools?: string[];
96
+ debug?: AgentHarnessDebugConfig;
97
+ inactiveTools?: string[];
98
+ type: (typeof AGENT_HARNESS_TYPES)[number];
99
+ permissionMode?: (typeof AGENT_HARNESS_PERMISSION_MODES)[number];
100
+ startupTimeoutMs?: number;
101
+ webSearch?: boolean;
102
+ }
103
+ interface AgentHarnessDebugConfig {
104
+ enabled?: boolean;
105
+ level?: (typeof AGENT_HARNESS_DEBUG_LEVELS)[number];
106
+ subsystems?: string[];
107
+ }
84
108
  type StreamTextOptions$1 = Parameters<typeof streamText>[0];
85
109
  type AgentModelProviderOptions = StreamTextOptions$1["providerOptions"];
86
110
  interface AgentSkillsConfig {
@@ -275,6 +299,10 @@ interface AgentDiscordChannelConfig {
275
299
  botToken?: string;
276
300
  publicKey?: string;
277
301
  allowedGuildIds?: string[];
302
+ /** Bot's Discord user id. Set it to answer only when the agent is mentioned. */
303
+ botUserId?: string;
304
+ /** Role ids that count as mentioning the agent, e.g. an on-call role. */
305
+ mentionRoleIds?: string[];
278
306
  workspaceScope?: AgentChannelWorkspaceScope;
279
307
  [key: string]: unknown;
280
308
  }
@@ -469,7 +497,10 @@ interface GitHubSource {
469
497
  interface SlackSource {
470
498
  teamId: string;
471
499
  channelId: string;
500
+ /** Where the reply goes. Unset posts to the channel. */
472
501
  threadTs?: string;
502
+ /** The thread the message arrived in, unset when it arrived in the channel. */
503
+ inThreadTs?: string;
473
504
  messageTs?: string;
474
505
  responseUrl?: string;
475
506
  commandToken?: string;
@@ -537,7 +568,7 @@ interface ZaloSource {
537
568
  * import it without pulling the Convex server module graph into its typecheck.
538
569
  */
539
570
  type CliManifestResource = {
540
- kind: "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "hook" | "policy";
571
+ kind: "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "hook" | "policy" | "channelRecord";
541
572
  name: string;
542
573
  description?: string;
543
574
  config: unknown;
@@ -551,6 +582,7 @@ type GeneratedIds = {
551
582
  tools: Record<string, string>;
552
583
  hooks: Record<string, string>;
553
584
  policies: Record<string, string>;
585
+ channelRecords: Record<string, string>;
554
586
  };
555
587
  type CliManifest = {
556
588
  version: 1;
@@ -577,7 +609,7 @@ type AgentConfigDoc = Doc<"agentConfigs">;
577
609
  type WorkspaceConfigDoc = Doc<"workspaceConfigs">;
578
610
  type SandboxConfigDoc = Doc<"sandboxConfigs">;
579
611
  type CronDoc = Doc<"crons">;
580
- type CliResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy";
612
+ type CliResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy" | "channelRecord";
581
613
 
582
614
  /**
583
615
  * Wire types for the public account-manage and harness APIs. These mirror
@@ -973,6 +1005,12 @@ declare class BroodsClient {
973
1005
  constructor(options?: BroodsClientOptions);
974
1006
  /** Return the public provider webhook URL for a generated channel reference. */
975
1007
  channelWebhookUrl(ref: ChannelReference): string;
1008
+ /**
1009
+ * Return the provider webhook URL for an account's channel. The URL names no
1010
+ * agent — every channel of one type in an account shares it — so this is what
1011
+ * to paste into the provider when several agents sit behind the same app.
1012
+ */
1013
+ accountWebhookUrl(accountId: string, channelType: ChannelReference["type"]): string;
976
1014
  agent<const Name extends string>(ref: AgentReference<Name>): AgentHandle;
977
1015
  agent(name: string, agentId: string): AgentHandle;
978
1016
  /** Run an agent and accumulate the streamed text and raw parts. */
@@ -1129,10 +1167,71 @@ interface AccountPolicy {
1129
1167
  createdAt: string;
1130
1168
  updatedAt: string;
1131
1169
  }
1170
+ /**
1171
+ * One real place a team talks — a Slack channel, a Discord channel, a repo —
1172
+ * bound to an agent. The runtime reads it on the inbound webhook to decide who
1173
+ * answers there and with what instructions, workspaces and policies.
1174
+ */
1175
+ interface AccountChannel {
1176
+ accountId: string;
1177
+ channelId: string;
1178
+ platform: string;
1179
+ externalId: string;
1180
+ workspaceRef?: string;
1181
+ name: string;
1182
+ description?: string;
1183
+ config: ChannelRecordConfig;
1184
+ status: "active" | "deleted";
1185
+ createdAt: string;
1186
+ updatedAt: string;
1187
+ }
1188
+ /** A channel record narrows and adds; it never grants capability the agent lacks. */
1189
+ interface ChannelRecordConfig {
1190
+ /** Appended after the agent's own system prompt. */
1191
+ instructions?: string;
1192
+ agentBindings: Array<{
1193
+ agentId: string;
1194
+ isDefault?: boolean;
1195
+ }>;
1196
+ workspaces?: Array<{
1197
+ name: string;
1198
+ workspaceId: string;
1199
+ }>;
1200
+ policyIds?: string[];
1201
+ /** Enforcement stage here. `audit` watches a rule before it refuses anyone. */
1202
+ policyMode?: "enforce" | "audit";
1203
+ /**
1204
+ * Tool names withheld in this channel, applied after the tool set is built —
1205
+ * so it also covers sandbox tools (`bash`, `read`, …) that `config.tools`
1206
+ * cannot name. Narrowing only; unknown names are ignored.
1207
+ */
1208
+ denyTools?: string[];
1209
+ /**
1210
+ * Where the reply lands. `inline` answers in the channel and threads only
1211
+ * when the message already did. Slack only — no other provider gives the
1212
+ * runtime a second place to reply.
1213
+ */
1214
+ threadPolicy?: "always-thread" | "inline";
1215
+ workspaceScope?: AgentChannelWorkspaceScope;
1216
+ /** Images the agent may stand a sandbox up from for a thread here. */
1217
+ sandboxImages?: string[];
1218
+ /** Named groups of people, readable from policy conditions as `actorRoles`. */
1219
+ tagRoles?: Array<{
1220
+ roleId: string;
1221
+ actorIds: string[];
1222
+ }>;
1223
+ }
1224
+ /** The environment a tool belongs to. Tools with the same name in two environments are two tools. */
1225
+ interface ToolScope {
1226
+ project: string;
1227
+ environment: string;
1228
+ }
1132
1229
  /** Public uploaded-tool record returned by the tools routes. */
1133
1230
  interface AccountTool {
1134
1231
  accountId: string;
1135
1232
  toolId: string;
1233
+ projectId: string;
1234
+ environmentId: string;
1136
1235
  name: string;
1137
1236
  description: string;
1138
1237
  inputSchema: unknown;
@@ -1234,12 +1333,13 @@ declare class BroodsAccountClient {
1234
1333
  /** Delete this account and cascade-clean every account-scoped resource. `cleanup` reports per-resource deletion counts. */
1235
1334
  deleteAccount(): Promise<DeleteAccountResult>;
1236
1335
  /**
1237
- * Provider webhook URL for one of an agent's channels. Paste this into the
1336
+ * Provider webhook URL for one of the account's channels. Paste this into the
1238
1337
  * provider's webhook settings (Slack Event Subscriptions, Zalo OA webhook,
1239
- * Pancake page webhook). Routing is per account + agent, so each agent's
1240
- * channels are isolated from every other agent's.
1338
+ * Pancake page webhook). Routing is per account + channel: the credentials
1339
+ * that verify the request pick the receiving agent, and a channel record
1340
+ * binds each place to the agent that should answer there.
1241
1341
  */
1242
- webhookUrl(accountId: string, agentId: string, channelType: string): string;
1342
+ webhookUrl(accountId: string, channelType: string): string;
1243
1343
  listAgents(): Promise<AccountAgent[]>;
1244
1344
  createAgent(input: {
1245
1345
  name: string;
@@ -1316,8 +1416,9 @@ declare class BroodsAccountClient {
1316
1416
  snapshotSandbox(sandboxId: string, reservationKey: string, name: string): Promise<SandboxSnapshotResult>;
1317
1417
  /** Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox (`sandbox`/`lambda` providers). Throws on 404/403/409. */
1318
1418
  openSandboxTerminal(sandboxId: string, reservationKey: string): Promise<SandboxTerminalTicket>;
1319
- listTools(): Promise<AccountTool[]>;
1320
- createTool(input: CreateToolInput): Promise<AccountTool>;
1419
+ /** Tools live in one environment, so the collection routes need a scope. Both fields are required. */
1420
+ listTools(scope: ToolScope): Promise<AccountTool[]>;
1421
+ createTool(scope: ToolScope, input: CreateToolInput): Promise<AccountTool>;
1321
1422
  getTool(toolId: string): Promise<AccountTool | null>;
1322
1423
  /** PATCH an uploaded tool. Omitting `bundle` keeps the stored bundle and runtime. Returns null when the tool is gone. */
1323
1424
  updateTool(toolId: string, patch: UpdateToolInput): Promise<AccountTool | null>;
@@ -1337,6 +1438,26 @@ declare class BroodsAccountClient {
1337
1438
  status?: string;
1338
1439
  }): Promise<AccountPolicy | null>;
1339
1440
  deletePolicy(policyId: string): Promise<boolean>;
1441
+ listChannels(): Promise<AccountChannel[]>;
1442
+ /** Bind one real chat channel to an agent. One active record per place. */
1443
+ createChannel(input: {
1444
+ platform: string;
1445
+ externalId: string;
1446
+ workspaceRef?: string;
1447
+ name: string;
1448
+ description?: string;
1449
+ config: ChannelRecordConfig;
1450
+ }): Promise<AccountChannel>;
1451
+ getChannel(channelId: string): Promise<AccountChannel | null>;
1452
+ /** PATCH a channel. `description: null` clears it. Returns null when it is gone. */
1453
+ updateChannel(channelId: string, patch: {
1454
+ name?: string;
1455
+ description?: string | null;
1456
+ workspaceRef?: string | null;
1457
+ config?: ChannelRecordConfig;
1458
+ status?: "active" | "deleted";
1459
+ }): Promise<AccountChannel | null>;
1460
+ deleteChannel(channelId: string): Promise<boolean>;
1340
1461
  /** All skills for the account, each with its `<accountId>/<name>` path. */
1341
1462
  listSkills(): Promise<Skill[]>;
1342
1463
  /** Upload a new skill from JSON content, a base64 file bundle, or a GitHub tree URL. Every bundle must include a root `SKILL.md`. */
@@ -1476,7 +1597,7 @@ interface BroodsConfigDefinition {
1476
1597
  readonly [CONFIG_MARKER]: true;
1477
1598
  readonly config: BroodsProjectConfig;
1478
1599
  }
1479
- type ResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy";
1600
+ type ResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy" | "channelRecord";
1480
1601
  interface ResourceDefinition<Kind extends ResourceKind, Name extends string, Config> {
1481
1602
  readonly [RESOURCE_MARKER]: true;
1482
1603
  readonly kind: Kind;
@@ -1501,6 +1622,11 @@ type ResourceInput<Name extends string, Config> = {
1501
1622
  type SandboxDefinitionConfig = Omit<SandboxConfig, "envVars"> & {
1502
1623
  envVars?: Record<string, string | EnvRef | undefined>;
1503
1624
  };
1625
+ type HarnessType = NonNullable<AgentConfig["harness"]>["type"];
1626
+ type HarnessDefinition = Omit<NonNullable<AgentConfig["harness"]>, "type"> & {
1627
+ type: HarnessType;
1628
+ sandbox: SandboxResource | string;
1629
+ };
1504
1630
  interface SkillDefinitionConfig {
1505
1631
  /**
1506
1632
  * Folder containing SKILL.md plus optional scripts/assets. Relative paths are
@@ -1569,7 +1695,7 @@ type ChannelIdentityInput = {
1569
1695
  type TelegramChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentTelegramChannelConfig, "apiUrl" | "botToken" | "webhookSecret" | "allowedChatIds" | "reactionEmoji">, "botToken" | "webhookSecret" | "allowedChatIds">> & ChannelIdentityInput;
1570
1696
  type GitHubChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentGitHubChannelConfig, "apiUrl" | "webhookSecret" | "appId" | "privateKey" | "allowedRepos" | "userName" | "botUserId" | "triggerOnIssueOpen" | "triggerOnPROpen">, "webhookSecret" | "appId" | "privateKey">> & ChannelIdentityInput;
1571
1697
  type SlackChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentSlackChannelConfig, "apiUrl" | "botToken" | "signingSecret" | "allowedChannelIds" | "reactionEmoji">, "botToken" | "signingSecret">> & ChannelIdentityInput;
1572
- type DiscordChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentDiscordChannelConfig, "apiUrl" | "botToken" | "publicKey" | "allowedGuildIds">, "botToken" | "publicKey">> & ChannelIdentityInput;
1698
+ type DiscordChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentDiscordChannelConfig, "apiUrl" | "botToken" | "publicKey" | "allowedGuildIds" | "botUserId" | "mentionRoleIds">, "botToken" | "publicKey">> & ChannelIdentityInput;
1573
1699
  interface PancakeChannelInput extends ChannelIdentityInput {
1574
1700
  pageId: ChannelSecret;
1575
1701
  pageAccessToken: ChannelSecret;
@@ -1773,6 +1899,7 @@ type ProviderConfigInput = Partial<Record<keyof NonNullable<AgentConfig["provide
1773
1899
  type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" | "session" | "tools">> & {
1774
1900
  provider?: ProviderConfigInput;
1775
1901
  } & {
1902
+ harness?: HarnessDefinition;
1776
1903
  hooks?: AgentHooks & {
1777
1904
  webhooks?: readonly EnvRefString<AgentWebhookHookConfig>[];
1778
1905
  };
@@ -1793,6 +1920,36 @@ type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" |
1793
1920
  type CronDefinitionConfig = Omit<CreateCronInput, "agentId" | "name"> & {
1794
1921
  agent: AgentResource | string;
1795
1922
  };
1923
+ /**
1924
+ * Binds one real place — a Slack channel, a Discord channel, a repo — to the
1925
+ * agent that answers there. Distinct from `channels` on an agent, which carries
1926
+ * one adapter's credentials: those say how to reach Slack, this says who replies
1927
+ * in #product-eng. A record narrows and adds, never granting what the agent lacks.
1928
+ */
1929
+ type ChannelRecordDefinitionConfig = {
1930
+ platform: ChannelType;
1931
+ /** Provider id of the place: a Slack channel id, or an owner/repo. */
1932
+ externalId: string;
1933
+ /** Team or guild the place sits in, when the provider has one. */
1934
+ workspaceRef?: string;
1935
+ /** Who answers here. The first is the default when a turn matches no other. */
1936
+ agents: readonly (AgentResource | string)[];
1937
+ /** Appended after the bound agent's own system prompt. */
1938
+ instructions?: string;
1939
+ /** Selects from what the agent already attaches; anything else is dropped. */
1940
+ workspaces?: readonly AgentWorkspaceInput[];
1941
+ policies?: readonly (PolicyResource | string)[];
1942
+ policyMode?: AgentPolicyConfig["mode"];
1943
+ denyTools?: readonly string[];
1944
+ /** Where the reply lands. Slack only — see the channel-records docs. */
1945
+ threadPolicy?: "always-thread" | "inline";
1946
+ workspaceScope?: AgentChannelWorkspaceScope;
1947
+ sandboxImages?: readonly string[];
1948
+ tagRoles?: readonly {
1949
+ roleId: string;
1950
+ actorIds: readonly string[];
1951
+ }[];
1952
+ };
1796
1953
  type AgentResource<Name extends string = string> = ResourceDefinition<"agent", Name, AgentDefinitionConfig>;
1797
1954
  type WorkspaceResource<Name extends string = string> = ResourceDefinition<"workspace", Name, WorkspaceConfig>;
1798
1955
  type SandboxResource<Name extends string = string> = ResourceDefinition<"sandbox", Name, SandboxDefinitionConfig>;
@@ -1800,7 +1957,8 @@ type SkillResource<Name extends string = string> = ResourceDefinition<"skill", N
1800
1957
  type ToolResource<Name extends string = string> = ResourceDefinition<"tool", Name, ToolDefinitionConfig>;
1801
1958
  type PolicyResource<Name extends string = string> = ResourceDefinition<"policy", Name, PolicyDefinitionConfig>;
1802
1959
  type CronResource<Name extends string = string> = ResourceDefinition<"cron", Name, CronDefinitionConfig>;
1803
- type AnyResource = AgentResource | WorkspaceResource | SandboxResource | CronResource | SkillResource | ToolResource | PolicyResource;
1960
+ type ChannelRecordResource<Name extends string = string> = ResourceDefinition<"channelRecord", Name, ChannelRecordDefinitionConfig>;
1961
+ type AnyResource = AgentResource | WorkspaceResource | SandboxResource | CronResource | SkillResource | ToolResource | PolicyResource | ChannelRecordResource;
1804
1962
  /**
1805
1963
  * References an account/environment variable resolved on the SERVER at runtime —
1806
1964
  * set it with `broods env set <NAME>` or in the dashboard (the Convex-style
@@ -1822,6 +1980,7 @@ declare function definePancakeChannel(config: PancakeChannelInput): PancakeChann
1822
1980
  declare function defineZaloChannel(config: ZaloChannelInput): ZaloChannelDefinition;
1823
1981
  declare function defineBroods(config: BroodsProjectConfig): BroodsConfigDefinition;
1824
1982
  declare function defineAgent<const Name extends string>(input: ResourceInput<Name, AgentDefinitionConfig>): AgentResource<Name>;
1983
+ declare function defineHarness<const Definition extends HarnessDefinition>(definition: Definition): Definition;
1825
1984
  declare function defineWorkspace<const Name extends string>(input: ResourceInput<Name, WorkspaceConfig>): WorkspaceResource<Name>;
1826
1985
  declare function defineSandbox<const Name extends string>(input: ResourceInput<Name, SandboxDefinitionConfig>): SandboxResource<Name>;
1827
1986
  declare function defineSkill<const Name extends string>(input: ResourceInput<Name, SkillDefinitionConfig>): SkillResource<Name>;
@@ -1829,10 +1988,11 @@ declare function defineTool<const Name extends string, Input = Record<string, un
1829
1988
  name: Name;
1830
1989
  } & ToolDefinitionConfig<Input>): ToolResource<Name>;
1831
1990
  declare function definePolicy<const Name extends string>(input: ResourceInput<Name, PolicyDefinitionConfig>): PolicyResource<Name>;
1991
+ declare function defineChannelRecord<const Name extends string>(input: ResourceInput<Name, ChannelRecordDefinitionConfig>): ChannelRecordResource<Name>;
1832
1992
  declare function defineCron<const Name extends string>(input: ResourceInput<Name, CronDefinitionConfig>): CronResource<Name>;
1833
1993
  declare function isResource(value: unknown): value is AnyResource;
1834
1994
  declare function isChannelDefinition(value: unknown): value is AnyChannelDefinition;
1835
1995
  declare function isBroodsConfig(value: unknown): value is BroodsConfigDefinition;
1836
1996
 
1837
- export { BroodsAccountApiError, BroodsAccountClient, BroodsClient, BroodsWebSocketClient, DEFAULT_CORE_BASE_URL, IngressAcceptedError, MAX_OBSERVABILITY_BACKFILL, BroodsWebSocketClient as WebSocketClient, BroodsWebSocketClient as WebsocketClient, defineAgent, defineBroods, defineCron, defineDiscordChannel, defineGitHubChannel, definePancakeChannel, definePolicy, defineSandbox, defineSkill, defineSlackChannel, defineTelegramChannel, defineTool, defineWorkspace, defineZaloChannel, env, envPlaceholder, isBroodsConfig, isChannelDefinition, isObservabilityClientMessage, isResource, normalizeHttpServiceUrl, readSseStream, resolveRunEvents, toWebSocketBaseUrl };
1838
- export type { Account, AccountAgent, AccountEnvVar, AccountPolicy, AccountSandbox, AccountTool, AccountWorkspace, Agent, AgentChannelWorkspaceScope, AgentChannelsConfig, AgentCodeHookConfig, AgentConfig, AgentConfigDoc, AgentDefinitionConfig, AgentDiscordChannelConfig, AgentGitHubChannelConfig, AgentHandle, AgentHookEventName, AgentHooks, AgentHooksConfig, AgentPancakeChannelConfig, AgentPolicyConfig, AgentPolicyDefinitionConfig, AgentPolicyDocument, AgentProviderSettings, AgentReference, AgentResource, AgentRunEventInput, AgentRunInput, AgentRunModelOverrides, AgentRunOverrides, AgentRunResult, AgentSkillsDefinitionConfig, AgentSlackChannelConfig, AgentStreamPart, AgentSubagentDefinitionConfig, AgentTelegramChannelConfig, AgentWebhookHookConfig, AgentWorkspaceInput, AgentWorkspaceRef, AgentWorkspaceRefInput, AgentZaloChannelConfig, AnyChannelDefinition, AnyResource, AsyncAgentRun, AsyncPollOptions, AsyncRequestAccepted, AsyncStatus, BroodsAccount, BroodsAccountClientOptions, BroodsClientOptions, BroodsConfigDefinition, BroodsProjectConfig, BroodsWebSocketClientOptions, ChannelDefinition, ChannelMessageReceived, ChannelReference, ChannelType, CliManifest, CliManifestResource, CliResourceKind, CreateAgentResult, CreateClientCronInput, CreateCronInput, CreateToolInput, Cron, CronDefinitionConfig, CronDoc, CronLastStatus, CronResource, CronRun, CronStatus, CustomTool, DeleteAccountResult, DiscordChannelDefinition, DiscordChannelInput, DiscordMessageSource, DiscordSource, Doc, EnvAccessor, EnvRef, EnvRefString, EnvironmentDoc, GeneratedIds, GitHubChannelDefinition, GitHubChannelInput, GitHubMessageSource, GitHubSource, HookContext, Id, IngressMode, IngressStatus, LogLevel, ObservabilityBackfillMessage, ObservabilityClientMessage, ObservabilityErrorMessage, ObservabilityLogEntry, ObservabilityLogMessage, ObservabilityReadyMessage, ObservabilityServerMessage, ObservabilitySpanMessage, ObservabilitySpanRow, ObservabilitySubscribeMessage, ObservabilityUnsubscribeMessage, PancakeChannelDefinition, PancakeChannelInput, PancakeMessageSource, PancakeSource, PolicyDefinitionConfig, PolicyResource, ProjectDoc, ProviderConfigInput, ProviderSettingsInput, ResourceApi, ResourceDefinition, ResourceInput, ResourceKind, RotateSecretResult, Sandbox, SandboxConfig, SandboxConfigDoc, SandboxDefinitionConfig, SandboxLifecycleResult, SandboxResource, SandboxSnapshotResult, SandboxTerminalTicket, Skill, SkillDefinitionConfig, SkillResource, SkillUploadInput, SlackChannelDefinition, SlackChannelInput, SlackMessageSource, SlackSource, TelegramChannelDefinition, TelegramChannelInput, TelegramMessageSource, TelegramSource, ToolApprovalSummary, ToolDefinitionConfig, ToolExecuteContext, ToolExecuteOptions, ToolResource, UpdateAgentInput, UpdateCronInput, UpdateToolInput, WebSocketAttachInput, WebSocketClientAttachMessage, WebSocketClientCancelMessage, WebSocketClientControlMessage, WebSocketClientExecuteMessage, WebSocketClientMessage, WebSocketConstructorLike, WebSocketHandlers, WebSocketLike, WebSocketOutputMessage, WebSocketRunInput, WebSocketServerMessage, WebSocketStreamMessage, WebSocketSubscription, Workspace, WorkspaceConfig, WorkspaceConfigDoc, WorkspaceFileEntry, WorkspaceResource, ZaloChannelDefinition, ZaloChannelInput, ZaloMessageSource, ZaloSource };
1997
+ export { BroodsAccountApiError, BroodsAccountClient, BroodsClient, BroodsWebSocketClient, DEFAULT_CORE_BASE_URL, IngressAcceptedError, MAX_OBSERVABILITY_BACKFILL, BroodsWebSocketClient as WebSocketClient, BroodsWebSocketClient as WebsocketClient, defineAgent, defineBroods, defineChannelRecord, defineCron, defineDiscordChannel, defineGitHubChannel, defineHarness, definePancakeChannel, definePolicy, defineSandbox, defineSkill, defineSlackChannel, defineTelegramChannel, defineTool, defineWorkspace, defineZaloChannel, env, envPlaceholder, isBroodsConfig, isChannelDefinition, isObservabilityClientMessage, isResource, normalizeHttpServiceUrl, readSseStream, resolveRunEvents, toWebSocketBaseUrl };
1998
+ export type { Account, AccountAgent, AccountChannel, AccountEnvVar, AccountPolicy, AccountSandbox, AccountTool, AccountWorkspace, Agent, AgentChannelWorkspaceScope, AgentChannelsConfig, AgentCodeHookConfig, AgentConfig, AgentConfigDoc, AgentDefinitionConfig, AgentDiscordChannelConfig, AgentGitHubChannelConfig, AgentHandle, AgentHookEventName, AgentHooks, AgentHooksConfig, AgentPancakeChannelConfig, AgentPolicyConfig, AgentPolicyDefinitionConfig, AgentPolicyDocument, AgentProviderSettings, AgentReference, AgentResource, AgentRunEventInput, AgentRunInput, AgentRunModelOverrides, AgentRunOverrides, AgentRunResult, AgentSkillsDefinitionConfig, AgentSlackChannelConfig, AgentStreamPart, AgentSubagentDefinitionConfig, AgentTelegramChannelConfig, AgentWebhookHookConfig, AgentWorkspaceInput, AgentWorkspaceRef, AgentWorkspaceRefInput, AgentZaloChannelConfig, AnyChannelDefinition, AnyResource, AsyncAgentRun, AsyncPollOptions, AsyncRequestAccepted, AsyncStatus, BroodsAccount, BroodsAccountClientOptions, BroodsClientOptions, BroodsConfigDefinition, BroodsProjectConfig, BroodsWebSocketClientOptions, ChannelDefinition, ChannelMessageReceived, ChannelRecordConfig, ChannelRecordDefinitionConfig, ChannelRecordResource, ChannelReference, ChannelType, CliManifest, CliManifestResource, CliResourceKind, CreateAgentResult, CreateClientCronInput, CreateCronInput, CreateToolInput, Cron, CronDefinitionConfig, CronDoc, CronLastStatus, CronResource, CronRun, CronStatus, CustomTool, DeleteAccountResult, DiscordChannelDefinition, DiscordChannelInput, DiscordMessageSource, DiscordSource, Doc, EnvAccessor, EnvRef, EnvRefString, EnvironmentDoc, GeneratedIds, GitHubChannelDefinition, GitHubChannelInput, GitHubMessageSource, GitHubSource, HarnessDefinition, HarnessType, HookContext, Id, IngressMode, IngressStatus, LogLevel, ObservabilityBackfillMessage, ObservabilityClientMessage, ObservabilityErrorMessage, ObservabilityLogEntry, ObservabilityLogMessage, ObservabilityReadyMessage, ObservabilityServerMessage, ObservabilitySpanMessage, ObservabilitySpanRow, ObservabilitySubscribeMessage, ObservabilityUnsubscribeMessage, PancakeChannelDefinition, PancakeChannelInput, PancakeMessageSource, PancakeSource, PolicyDefinitionConfig, PolicyResource, ProjectDoc, ProviderConfigInput, ProviderSettingsInput, ResourceApi, ResourceDefinition, ResourceInput, ResourceKind, RotateSecretResult, Sandbox, SandboxConfig, SandboxConfigDoc, SandboxDefinitionConfig, SandboxLifecycleResult, SandboxResource, SandboxSnapshotResult, SandboxTerminalTicket, Skill, SkillDefinitionConfig, SkillResource, SkillUploadInput, SlackChannelDefinition, SlackChannelInput, SlackMessageSource, SlackSource, TelegramChannelDefinition, TelegramChannelInput, TelegramMessageSource, TelegramSource, ToolApprovalSummary, ToolDefinitionConfig, ToolExecuteContext, ToolExecuteOptions, ToolResource, ToolScope, UpdateAgentInput, UpdateCronInput, UpdateToolInput, WebSocketAttachInput, WebSocketClientAttachMessage, WebSocketClientCancelMessage, WebSocketClientControlMessage, WebSocketClientExecuteMessage, WebSocketClientMessage, WebSocketConstructorLike, WebSocketHandlers, WebSocketLike, WebSocketOutputMessage, WebSocketRunInput, WebSocketServerMessage, WebSocketStreamMessage, WebSocketSubscription, Workspace, WorkspaceConfig, WorkspaceConfigDoc, WorkspaceFileEntry, WorkspaceResource, ZaloChannelDefinition, ZaloChannelInput, ZaloMessageSource, ZaloSource };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as M,b as h,c as O}from"./chunk-9qtppg6j.js";import{A as _,B as P,C as H,D as U,E as B,F as K,G as j,H as F,I as G,J as z,K as Z,d as x,e as I,n as v,o as k,p as S,q as A,r as R,s as W,t as L,u as w,v as E,w as D,x as T,y as q,z as N}from"./chunk-18hxt2an.js";import"./chunk-xv4t40dh.js";var o=500;function a(s){if(typeof s!=="object"||s===null)return!1;let e=s;if(e.type==="subscribe"){let t=e.stream;if(t!=="logs"&&t!=="traces")return!1;let r=e.backfill;if(r!==void 0&&(typeof r!=="number"||!Number.isSafeInteger(r)||r<0||r>500))return!1;if(e.liveOnly!==void 0&&typeof e.liveOnly!=="boolean")return!1;let n=e.minLevel;if(n!==void 0&&n!=="INFO"&&n!=="WARN"&&n!=="ERROR")return!1;return!0}if(e.type==="unsubscribe"){let t=e.stream;return t==="logs"||t==="traces"}return!1}export{W as toWebSocketBaseUrl,x as resolveRunEvents,I as readSseStream,A as normalizeHttpServiceUrl,G as isResource,a as isObservabilityClientMessage,z as isChannelDefinition,Z as isBroodsConfig,M as envPlaceholder,L as env,N as defineZaloChannel,H as defineWorkspace,K as defineTool,w as defineTelegramChannel,D as defineSlackChannel,B as defineSkill,U as defineSandbox,j as definePolicy,q as definePancakeChannel,E as defineGitHubChannel,T as defineDiscordChannel,F as defineCron,_ as defineBroods,P as defineAgent,R as WebsocketClient,R as WebSocketClient,o as MAX_OBSERVABILITY_BACKFILL,k as IngressAcceptedError,v as DEFAULT_CORE_BASE_URL,R as BroodsWebSocketClient,S as BroodsClient,O as BroodsAccountClient,h as BroodsAccountApiError};
1
+ import{a as M,b as h,c as O}from"./chunk-vmm9rm5v.js";import{A as _,B as P,C as H,D as U,E as B,F as K,G as j,H as F,I as G,J as z,K as Z,L as V,M as X,d as x,e as I,n as v,o as k,p as S,q as A,r as R,s as W,t as L,u as w,v as E,w as D,x as T,y as q,z as N}from"./chunk-qjfqqhve.js";import"./chunk-51a2zca3.js";var o=500;function a(s){if(typeof s!=="object"||s===null)return!1;let e=s;if(e.type==="subscribe"){let t=e.stream;if(t!=="logs"&&t!=="traces")return!1;let r=e.backfill;if(r!==void 0&&(typeof r!=="number"||!Number.isSafeInteger(r)||r<0||r>500))return!1;if(e.liveOnly!==void 0&&typeof e.liveOnly!=="boolean")return!1;let n=e.minLevel;if(n!==void 0&&n!=="INFO"&&n!=="WARN"&&n!=="ERROR")return!1;return!0}if(e.type==="unsubscribe"){let t=e.stream;return t==="logs"||t==="traces"}return!1}export{h as BroodsAccountApiError,O as BroodsAccountClient,S as BroodsClient,R as BroodsWebSocketClient,v as DEFAULT_CORE_BASE_URL,k as IngressAcceptedError,o as MAX_OBSERVABILITY_BACKFILL,R as WebSocketClient,R as WebsocketClient,P as defineAgent,_ as defineBroods,G as defineChannelRecord,z as defineCron,T as defineDiscordChannel,E as defineGitHubChannel,H as defineHarness,q as definePancakeChannel,F as definePolicy,B as defineSandbox,K as defineSkill,D as defineSlackChannel,w as defineTelegramChannel,j as defineTool,U as defineWorkspace,N as defineZaloChannel,L as env,M as envPlaceholder,X as isBroodsConfig,V as isChannelDefinition,a as isObservabilityClientMessage,Z as isResource,A as normalizeHttpServiceUrl,I as readSseStream,x as resolveRunEvents,W as toWebSocketBaseUrl};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "broods",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
4
4
  "description": "CLI + TypeScript SDK for the broods agent platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -50,16 +50,11 @@
50
50
  "node": ">=22"
51
51
  },
52
52
  "dependencies": {
53
- "esbuild": "^0.27.0"
53
+ "esbuild": "^0.28.1"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "ai": "^7.0.40"
57
57
  },
58
- "peerDependenciesMeta": {
59
- "ai": {
60
- "optional": true
61
- }
62
- },
63
58
  "devDependencies": {
64
59
  "@ai-sdk/tui": "^1.0.41",
65
60
  "ai": "^7.0.40",
@@ -1,5 +0,0 @@
1
- function b(e){if(e.events&&e.input!==void 0)throw Error("Run input accepts either `input` or `events`, not both");if(e.events&&e.events.length>0)return e.events;if(typeof e.input==="string")return[{role:"user",content:[{type:"text",text:e.input}]}];throw Error("Run input requires `input` (string) or a non-empty `events` array")}async function*x(e){let n=e.getReader(),t=new TextDecoder,o="";try{while(!0){let{done:r,value:s}=await n.read();if(r)break;o+=t.decode(s,{stream:!0});let i=o.split(`
2
- `);o=i.pop()??"";for(let a of i)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}import{mkdir as J,readFile as F,writeFile as Z}from"node:fs/promises";import{homedir as z}from"node:os";import{dirname as V,join as Y}from"node:path";var we="broods",Ie="_generated",m=Y(z(),".broods","config.json"),E="broods.app",T="dashboard",X="gateway";async function xe(){let e=process.env.BROODS_TOKEN,n=process.env.BROODS_BASE_URL;if(e&&n)return{baseUrl:u(n),token:e,createdAt:new Date().toISOString()};try{let t=JSON.parse(await F(m,"utf8"));if(typeof t.baseUrl!=="string"||!t.baseUrl)return null;return t}catch{return null}}async function Ee(e){await J(V(m),{recursive:!0}),await Z(m,`${JSON.stringify(e,null,2)}
3
- `,"utf8")}function u(e){return e.replace(/\/+$/,"")}function Te(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${T}.`))return;let t=n.hostname.slice(T.length+1);if(t!==E&&!t.endsWith(`.${E}`))return;return n.hostname=`${X}.${t}`,u(n.origin)}catch{return}}import{existsSync as Q,readFileSync as P,statSync as ee}from"node:fs";import{join as ne,resolve as te}from"node:path";var I=new Map,oe=new Set(Object.keys(process.env)),D=new Set;function k(e=process.cwd()){re(e);let n=ae();return{dashboardUrl:process.env.BROODS_DASHBOARD_URL??n?.dashboardUrl,baseUrl:process.env.BROODS_BASE_URL??n?.baseUrl,token:process.env.BROODS_TOKEN??n?.token,project:process.env.BROODS_PROJECT,environment:process.env.BROODS_ENVIRONMENT}}function re(e){let n=te(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=ne(n,o);if(!Q(r)){I.delete(r);continue}let s=ee(r).mtimeMs,i=I.get(r);if(i&&i.mtimeMs===s)continue;I.set(r,{mtimeMs:s});for(let[a,l]of Object.entries(se(P(r,"utf8"))))if(t||!oe.has(a)||D.has(a))process.env[a]=l,D.add(a)}}function se(e){let n={};for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;let r=o.startsWith("export ")?o.slice(7).trim():o,s=r.indexOf("=");if(s<=0)continue;let i=r.slice(0,s).trim();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i))continue;n[i]=ie(r.slice(s+1).trim())}return n}function ie(e){if(e.startsWith('"')&&e.endsWith('"'))return e.slice(1,-1).replace(/\\n/g,`
4
- `).replace(/\\"/g,'"');if(e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);let n=e.indexOf(" #");return n>=0?e.slice(0,n).trimEnd():e}function ae(){try{let e=JSON.parse(P(m,"utf8"));if(typeof e.baseUrl!=="string"||typeof e.token!=="string")return null;return{baseUrl:u(e.baseUrl),...typeof e.dashboardUrl==="string"?{dashboardUrl:u(e.dashboardUrl)}:{},token:e.token}}catch{return null}}var A="https://gateway.broods.app";class U extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class ce{baseUrl;apiKey;fetchImpl;constructor(e={}){k(),this.baseUrl=v(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||A),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||void 0,this.fetchImpl=e.fetch??fetch}channelWebhookUrl(e){return`${this.baseUrl}${e.webhookPath.startsWith("/")?"":"/"}${e.webhookPath}`}agent(e,n){if(typeof e==="string"){let o=e,r=n??"";if(!r)throw Error(`Agent ${o} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:r,run:(s)=>this.run({...s,agentName:o,agentId:r}),runAsync:(s)=>this.runAsync({...s,agentName:o,agentId:r}),stream:(s)=>this.stream({...s,agentName:o,agentId:r})}}let t=e;if(!t.id)throw Error(`Agent ${t.name} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:t.id,run:(o)=>this.run(t,o),runAsync:(o)=>this.runAsync(t,o),stream:(o)=>this.stream(t,o)}}async run(e,n){let t=[],o="",r=n?this.stream(e,n):this.stream(e);for await(let s of r)if(t.push(s),s.type==="text-delta")o+=s.text;return{text:o,events:t}}async*stream(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=O(t,"cli"),r=n?this.scopedUrl(e):this.baseUrl,s=await this.openStream(o,r);if(!s.ok)throw Error(`Run failed: ${s.status} ${await s.text()}`);if(s.status===202)throw new U(N(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of x(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${fe(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=O(t,"async"),r=n?this.scopedUrl(e,"/async"):`${this.baseUrl}/async`,s=await this.fetchJson(r,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(o)});if(s.status!==202)throw Error(`Async run failed: ${s.status} ${await ge(s,"202 JSON")}`);let i=N(await s.json(),o);return{...i,conversationKey:o.conversationKey,poll:()=>this.getAsyncStatus(i),wait:(a)=>this.waitForAsyncStatus(i,a)}}async getAsyncStatus(e,n={}){let t=this.resolveStatusUrl(e,n),o=await this.fetchJson(t,{method:"GET",headers:this.apiKeyHeaders()});if(o.status===404)return{status:"not_found"};if(!o.ok)throw Error(`Status check failed: ${o.status} ${await o.text()}`);return await o.json()}async waitForAsyncStatus(e,n={}){let t=Date.now()+(n.timeoutMs??180000),o=n.intervalMs??2000;while(Date.now()<t){if(n.signal?.aborted)throw Error("Async status polling aborted.");let r=await this.getAsyncStatus(e,n);if(r.status==="awaiting_approval"||r.status==="completed"||r.status==="failed"||r.status==="expired"||r.status==="not_found")return r;await pe(o,n.signal)}throw Error("Polling timeout")}async createCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(de(e))});if(n.status!==201)throw Error(`Create cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCrons(){let e=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"GET",headers:this.apiKeyHeaders()});if(!e.ok)throw Error(`List cron jobs failed: ${e.status} ${await p(e)}`);return(await e.json()).crons}async getCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"GET",headers:this.apiKeyHeaders()});if(n.status===404)return null;if(!n.ok)throw Error(`Get cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCronRuns(e,n={}){let t=new URLSearchParams;if(n.limit!==void 0)t.set("limit",String(n.limit));let o=t.size>0?`?${t}`:"",r=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}/runs${o}`,{method:"GET",headers:this.apiKeyHeaders()});if(!r.ok)throw Error(`List cron job runs failed: ${r.status} ${await p(r)}`);return(await r.json()).runs}async updateCron(e,n){let t=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"PATCH",headers:this.apiKeyHeaders(),body:JSON.stringify(n)});if(!t.ok)throw Error(`Update cron job failed: ${t.status} ${await p(t)}`);return await t.json()}async deleteCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"DELETE",headers:this.apiKeyHeaders()});if(n.status===404)return!1;if(!n.ok)throw Error(`Delete cron job failed: ${n.status} ${await p(n)}`);return(await n.json()).deleted}scopedUrl(e,n=""){if(e.projectSlug&&e.environmentSlug&&e.endpointId)return`${this.baseUrl}/v1/${encodeURIComponent(e.projectSlug)}/agents/${encodeURIComponent(e.environmentSlug)}/${encodeURIComponent(e.endpointId)}${n}`;return`${this.baseUrl}${n}`}async openStream(e,n){if(this.apiKey)return await this.fetchCore(e,n,{Authorization:`Bearer ${this.apiKey}`});throw Error(`BroodsClient streams directly from the core service at ${this.baseUrl}. Provide apiKey. For a self-hosted core service, set host/baseUrl or BROODS_HOST/BROODS_BASE_URL.`)}async fetchCore(e,n,t){try{return await this.fetchImpl(n,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream",...t},body:JSON.stringify(e)})}catch(o){throw Error(`Cannot access the broods core service at ${n}. The SDK uses ${A} by default; set host/baseUrl or BROODS_HOST/BROODS_BASE_URL to your own core service URL if your account uses a custom deployment. Cause: ${o instanceof Error?o.message:String(o)}`)}}apiKeyHeaders(){if(!this.apiKey)throw Error("BroodsClient requires apiKey or BROODS_API_KEY.");return{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}}async fetchJson(e,n){try{return await this.fetchImpl(e,{...n,headers:{"Content-Type":"application/json",...n.headers??{}}})}catch(t){throw Error(`Cannot access the broods core service at ${e}. Cause: ${t instanceof Error?t.message:String(t)}`)}}resolveStatusUrl(e,n){if(typeof e!=="string")return e.statusUrl;if(/^https?:\/\//.test(e))return e;if(!n.agentId)throw Error("Polling by status id requires agentId.");return ue(this.baseUrl,e,n.agentId)}}function v(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return u(t)}function O(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:b(e),...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}}function N(e,n){if(!e||typeof e!=="object")throw Error("Async response must be an object");let t=e.statusUrl;if(typeof t!=="string"||t.length===0)throw Error("Async response missing statusUrl");let o=le(t);if(!o.statusId)throw Error("Async response statusUrl missing status id");let r=typeof e.eventId==="string"?e.eventId:o.statusId,s=e.status,i=e.requestedMode;return{statusUrl:t,statusId:o.statusId,eventId:r,agentId:o.agentId??n.agentId,...s==="accepted"||s==="queued"||s==="applied"||s==="processing"?{status:s}:{},...i==="reject"||i==="followup"||i==="collect"||i==="steer"?{requestedMode:i}:{}}}function le(e){let n=new URL(e),t=n.pathname.match(/\/status\/([^/]+)$/);return{statusId:t?.[1]?decodeURIComponent(t[1]):void 0,agentId:n.searchParams.get("agentId")??void 0}}function ue(e,n,t){return`${v(e)}/status/${encodeURIComponent(n)}?agentId=${encodeURIComponent(t)}`}async function ge(e,n){if((e.headers.get("content-type")??"").toLowerCase().includes("text/event-stream"))return await e.body?.cancel().catch(()=>{}),`expected ${n}, but the server returned an SSE stream. This usually means the core deployment routed /async to the direct streaming runner instead of the async handler.`;let o=await e.text();if(o.length<=2000)return o;return`${o.slice(0,2000)}... [truncated ${o.length-2000} chars]`}async function p(e){let n=await e.text();if(n.includes("Request body must include eventId and conversationKey"))return`${n}. Cron job APIs must be served by the configured baseUrl. Prefer defining stable cron jobs with defineCron(...) in broods/ and syncing with \`broods dev\` or \`broods deploy\`.`;return n}function de(e){if("agentId"in e)return e;let n=e.agent,t=typeof n==="string"?n:n.id,{agent:o,...r}=e;return{...r,agentId:t}}function pe(e,n){return new Promise((t,o)=>{if(n?.aborted){o(Error("Async status polling aborted."));return}let r=setTimeout(t,e);n?.addEventListener("abort",()=>{clearTimeout(r),o(Error("Async status polling aborted."))},{once:!0})})}function fe(e){if(typeof e==="string")return e;if(!e||typeof e!=="object")return String(e);let n=e,t=n.data?.error?.message??n.message??n.responseBody??JSON.stringify(e),o=n.name?`${n.name}: `:"",r=n.statusCode?` (HTTP ${n.statusCode})`:"";return`${o}${t}${r}`}var ye=2000,W=0,h=1;class me{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(k(),this.baseUrl=B(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||A),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||"",!this.apiKey)throw Error("BroodsWebSocketClient requires apiKey or BROODS_API_KEY.");this.WebSocketImpl=e.WebSocket,this.connectTimeoutMs=e.connectTimeoutMs??ye}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=he(e),s=M(this.baseUrl),i=new t(o),a=!1,l=!1,g=setTimeout(()=>{if(!a)y(s)},this.connectTimeoutMs),w=(c=1000,R="client closed")=>{if(l)return;if(l=!0,clearTimeout(g),e.signal?.removeEventListener("abort",S),i.readyState===h||i.readyState===W)i.close(c,R)},y=(c)=>{n.onError?.(c),w(1011,"websocket error")},G=()=>{n.onDone?.(),w(1000,"done")},S=()=>{if(i.readyState===h)i.send(JSON.stringify({type:"cancel"}));y(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",S,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(g),e.signal?.aborted){S();return}i.send(JSON.stringify({type:"execute",agentId:r,events:b(e),sessionId:e.sessionId,...e.eventId!==void 0?{eventId:e.eventId}:{},...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}))},i.onmessage=(c)=>{if(typeof c.data!=="string")return;let R=_(c.data);if(!R)return;let C=$(R,n);switch(n.onMessage?.(C),C.type){case"meta":n.onMeta?.(C);break;case"done":G();break;case"error":y(Error(K(C.error)));break}},i.onerror=()=>{y(s)},i.onclose=(c)=>{if(clearTimeout(g),e.signal?.removeEventListener("abort",S),l||c.code===1000)return;y(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl(c){if(i.readyState!==h)throw Error("WebSocket is not open");i.send(JSON.stringify({type:"control",...c}))},close:w}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o),s=!1,i=(a=1000,l="client closed")=>{if(s)return;if(s=!0,r.readyState===h||r.readyState===W)r.close(a,l)};return r.onopen=()=>r.send(JSON.stringify({type:"attach",...e,signal:void 0,agent:void 0})),r.onmessage=(a)=>{if(typeof a.data!=="string")return;let l=_(a.data);if(!l)return;let g=$(l,n);if(n.onMessage?.(g),g.type==="done")n.onDone?.();if(g.type==="error")n.onError?.(Error(K(g.error)))},r.onerror=()=>n.onError?.(M(this.baseUrl)),r.onclose=(a)=>{if(!s&&a.code!==1000)n.onError?.(Error(a.reason||"WebSocket connection closed."))},{url:o,sendControl(a){if(r.readyState!==h)throw Error("WebSocket is not open");r.send(JSON.stringify({type:"control",...a}))},close:i}}async*stream(e){let n=[],t=!1,o=null,r=null,s=()=>{r?.(),r=null},i=this.subscribe(e,{onMessage(a){n.push(a),s()},onDone(){t=!0,s()},onError(a){o=a,s()}});try{while(!0){if(n.length>0){yield n.shift();continue}if(o)throw o;if(t)return;await new Promise((a)=>{r=a})}}finally{i.close()}}buildUrl(e){let n=Se(e),t=e.projectSlug??e.agent?.projectSlug,o=e.environmentSlug??e.agent?.environmentSlug,r=t?`/${t}`:"",s=o?`/${o}`:"";return`${Re(this.baseUrl)}/v1${r}/agents${s}/${encodeURIComponent(n)}/ws?token=${encodeURIComponent(this.apiKey)}`}resolveWebSocket(){let e=this.WebSocketImpl??globalThis.WebSocket;if(!e)throw Error("WebSocket is not available. Pass a WebSocket implementation in BroodsWebSocketClient options.");return e}}function he(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function Se(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function Re(e){let n=new URL(B(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return u(n.toString())}function B(e){let n=e.trim();if(/^wss?:\/\//.test(n))return u(n);return v(n)}function M(e){return Error(`Cannot access the WebSocket service at ${e}.`)}function $(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function _(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function K(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&typeof e.message==="string")return e.message;return e===void 0?"WebSocket stream error.":JSON.stringify(e)}var j=Symbol.for("broods.resource"),L=Symbol.for("broods.config"),q=Symbol.for("broods.channel"),H=/^[A-Z][A-Z0-9_]{0,63}$/;var Ge=new Proxy((e)=>{if(!H.test(e))throw Error("env name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return{__beeblastEnv:!0,name:e}},{get(e,n,t){if(typeof n==="string"&&H.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function d(e,n,t,o){return{[j]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}function f(e,n){let{workspaceScope:t,...o}=n;return{[q]:!0,kind:"channel",type:e,...t?{workspaceScope:t}:{},config:{...o,...t?{workspaceScope:t}:{}}}}function Je(e){return f("telegram",e)}function Fe(e){return f("github",e)}function Ze(e){return f("slack",e)}function ze(e){return f("discord",e)}function Ve(e){return f("pancake",e)}function Ye(e){return f("zalo",e)}function Xe(e){return{[L]:!0,config:e}}function Qe(e){let{name:n,description:t,...o}=e;return d("agent",n,t,o)}function en(e){let{name:n,description:t,...o}=e;return d("workspace",n,t,o)}function nn(e){let{name:n,description:t,...o}=e;return d("sandbox",n,t,o)}function tn(e){let{name:n,description:t,...o}=e;return d("skill",n,t,o)}function on(e){let{name:n,...t}=e;return d("tool",n,void 0,t)}function rn(e){let{name:n,description:t,...o}=e;return d("policy",n,t,o)}function sn(e){let{name:n,description:t,...o}=e;return d("cron",n,t,o)}function an(e){return Boolean(e&&typeof e==="object"&&e[j])}function cn(e){return Boolean(e&&typeof e==="object"&&e[q])}function ln(e){return Boolean(e&&typeof e==="object"&&e[L])}
5
- export{b as d,x as e,we as f,Ie as g,m as h,xe as i,Ee as j,u as k,Te as l,k as m,A as n,U as o,ce as p,v as q,me as r,Re as s,Ge as t,Je as u,Fe as v,Ze as w,ze as x,Ve as y,Ye as z,Xe as A,Qe as B,en as C,nn as D,tn as E,on as F,rn as G,sn as H,an as I,cn as J,ln as K};
@@ -1 +0,0 @@
1
- import{M as a}from"./chunk-m6935tq1.js";import"./chunk-xv4t40dh.js";export default a();
@@ -1,2 +0,0 @@
1
- var a=/^[A-Z][A-Z0-9_]*$/;function u(e){if(!a.test(e)||e.length>64)throw Error("envPlaceholder name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return`\${${e}}`}class o extends Error{status;body;constructor(e,t,n,s){super(`${e} ${t} failed with ${n}: ${s}`);this.name="BroodsAccountApiError",this.status=n,this.body=s}}function i(e){return typeof process<"u"?process?.env?.[e]:void 0}class c{baseUrl;accountSecret;fetchImpl;constructor(e={}){let t=e.baseUrl??i("BROODS_BASE_URL")??"https://gateway.broods.app",n=e.accountSecret??i("BROODS_ACCOUNT_SECRET");if(!n)throw Error("BroodsAccountClient requires an accountSecret (or BROODS_ACCOUNT_SECRET).");this.baseUrl=t.replace(/\/+$/,""),this.accountSecret=n,this.fetchImpl=e.fetch??fetch}async getAccount(){let e=await this.request("GET","/v1/account");if(!e)throw new o("GET","/v1/account",404,"Account not found");return e.account}async updateAccount(e){return(await this.request("PATCH","/v1/account",e))?.account??null}async rotateSecret(){let e=await this.request("POST","/v1/account/rotate-secret");if(!e)throw new o("POST","/v1/account/rotate-secret",404,"Account not found");return e}async deleteAccount(){return await this.request("DELETE","/v1/account")??{deleted:!1}}webhookUrl(e,t,n){let s=[e,t,n].map(encodeURIComponent);return`${this.baseUrl}/webhooks/${s.join("/")}`}async listAgents(){return(await this.request("GET","/v1/agents"))?.agents??[]}async createAgent(e){let t=await this.request("POST","/v1/agents",e);if(!t)throw new o("POST","/v1/agents",404,"Not found");return t}async getAgent(e){return await this.request("GET",`/v1/agents/${encodeURIComponent(e)}`)}async updateAgent(e,t){return await this.request("PATCH",`/v1/agents/${encodeURIComponent(e)}`,t)}async deleteAgent(e){return(await this.request("DELETE",`/v1/agents/${encodeURIComponent(e)}`))?.deleted??!1}async listEnvVars(){return(await this.request("GET","/v1/env"))?.env??[]}async setEnvVar(e,t){await this.request("PUT",`/v1/env/${encodeURIComponent(e)}`,{value:t})}async deleteEnvVar(e){return(await this.request("DELETE",`/v1/env/${encodeURIComponent(e)}`))?.deleted??!1}async listCrons(){return(await this.request("GET","/v1/crons"))?.crons??[]}async createCron(e){let t=await this.request("POST","/v1/crons",e);if(!t)throw new o("POST","/v1/crons",404,"Not found");return t}async getCron(e){return await this.request("GET",`/v1/crons/${encodeURIComponent(e)}`)}async updateCron(e,t){return await this.request("PATCH",`/v1/crons/${encodeURIComponent(e)}`,t)}async deleteCron(e){return(await this.request("DELETE",`/v1/crons/${encodeURIComponent(e)}`))?.deleted??!1}async listCronRuns(e,t={}){let n=t.limit!==void 0?`?limit=${t.limit}`:"";return(await this.request("GET",`/v1/crons/${encodeURIComponent(e)}/runs${n}`))?.runs??[]}async listWorkspaces(){return(await this.request("GET","/v1/workspaces"))?.workspaces??[]}async createWorkspace(e){let t=await this.request("POST","/v1/workspaces",e);if(!t)throw new o("POST","/v1/workspaces",404,"Not found");return t}async getWorkspace(e){return await this.request("GET",`/v1/workspaces/${encodeURIComponent(e)}`)}async updateWorkspace(e,t){return await this.request("PATCH",`/v1/workspaces/${encodeURIComponent(e)}`,t)}async deleteWorkspace(e){return(await this.request("DELETE",`/v1/workspaces/${encodeURIComponent(e)}`))?.deleted??!1}async listWorkspaceFiles(e){return(await this.request("GET",`/v1/workspaces/${encodeURIComponent(e)}/files`))?.files??[]}async getWorkspaceFileUrl(e,t){let n=`?path=${encodeURIComponent(t)}`;return(await this.request("GET",`/v1/workspaces/${encodeURIComponent(e)}/files${n}`))?.url??null}async uploadWorkspaceFile(e,t){let n=`/v1/workspaces/${encodeURIComponent(e)}/files`,s=await this.request("POST",n,t);if(!s)throw new o("POST",n,404,"Workspace not found");return s.file}async renameWorkspaceFile(e,t,n){return(await this.request("PATCH",`/v1/workspaces/${encodeURIComponent(e)}/files`,{path:t,newPath:n}))?.renamed??!1}async deleteWorkspaceFile(e,t){return(await this.request("DELETE",`/v1/workspaces/${encodeURIComponent(e)}/files`,{path:t}))?.deleted??!1}async listSandboxes(){return(await this.request("GET","/v1/sandboxes"))?.sandboxes??[]}async createSandbox(e){let t=await this.request("POST","/v1/sandboxes",e);if(!t)throw new o("POST","/v1/sandboxes",404,"Not found");return t}async getSandbox(e){return await this.request("GET",`/v1/sandboxes/${encodeURIComponent(e)}`)}async updateSandbox(e,t){return await this.request("PATCH",`/v1/sandboxes/${encodeURIComponent(e)}`,t)}async deleteSandbox(e){return(await this.request("DELETE",`/v1/sandboxes/${encodeURIComponent(e)}`))?.deleted??!1}async suspendSandbox(e,t){return await this.sandboxAction(e,"suspend",{reservationKey:t})}async resumeSandbox(e,t){return await this.sandboxAction(e,"resume",{reservationKey:t})}async terminateSandbox(e,t){return await this.sandboxAction(e,"terminate",{reservationKey:t})}async snapshotSandbox(e,t,n){return await this.sandboxAction(e,"snapshot",{reservationKey:t,name:n})}async openSandboxTerminal(e,t){return await this.sandboxAction(e,"terminal",{reservationKey:t})}async listTools(){return(await this.request("GET","/v1/tools"))?.tools??[]}async createTool(e){let t=await this.request("POST","/v1/tools",e);if(!t)throw new o("POST","/v1/tools",404,"Not found");return t}async getTool(e){return await this.request("GET",`/v1/tools/${encodeURIComponent(e)}`)}async updateTool(e,t){return await this.request("PATCH",`/v1/tools/${encodeURIComponent(e)}`,t)}async deleteTool(e){return(await this.request("DELETE",`/v1/tools/${encodeURIComponent(e)}`))?.deleted??!1}async listPolicies(){return(await this.request("GET","/v1/policies"))?.policies??[]}async createPolicy(e){let t=await this.request("POST","/v1/policies",e);if(!t)throw new o("POST","/v1/policies",404,"Not found");return t}async getPolicy(e){return await this.request("GET",`/v1/policies/${encodeURIComponent(e)}`)}async updatePolicy(e,t){return await this.request("PATCH",`/v1/policies/${encodeURIComponent(e)}`,t)}async deletePolicy(e){return(await this.request("DELETE",`/v1/policies/${encodeURIComponent(e)}`))?.deleted??!1}async listSkills(){return(await this.request("GET","/v1/skills"))?.skills??[]}async createSkill(e){let t=await this.request("POST","/v1/skills",e);if(!t)throw new o("POST","/v1/skills",404,"Not found");return t}async getSkill(e){return await this.request("GET",`/v1/skills/${encodeURIComponent(e)}`)}async uploadSkill(e,t){let n=`/v1/skills/${encodeURIComponent(e)}`,s=await this.request("PUT",n,t);if(!s)throw new o("PUT",n,404,"Skill not found");return s}async deleteSkill(e){return(await this.request("DELETE",`/v1/skills/${encodeURIComponent(e)}`))?.deleted??!1}async sandboxAction(e,t,n){let s=`/v1/sandboxes/${encodeURIComponent(e)}/${t}`,r=await this.request("POST",s,n);if(!r)throw new o("POST",s,404,"Sandbox not found");return r}async request(e,t,n){let s=await this.fetchImpl(`${this.baseUrl}${t}`,{method:e,headers:{Authorization:`Bearer ${this.accountSecret}`,...n!==void 0?{"Content-Type":"application/json"}:{}},...n!==void 0?{body:JSON.stringify(n)}:{}});if(s.status===404)return null;if(!s.ok)throw new o(e,t,s.status,await s.text());return await s.json()}}
2
- export{u as a,o as b,c};