broods 0.1.1 → 0.2.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
@@ -1,24 +1,47 @@
1
- import { SystemModelMessage, CallSettings, streamText, JSONSchema7, ModelMessage, TextStreamPart, ToolSet } from 'ai';
1
+ import { SystemModelMessage, LanguageModelCallOptions, RequestOptions, streamText, JSONSchema7, ModelMessage, TextStreamPart, ToolSet } from 'ai';
2
+ import { DiscordAdapterConfig } from '@chat-adapter/discord';
3
+ import { GitHubAdapterConfig } from '@chat-adapter/github';
4
+ import { SlackAdapterConfig } from '@chat-adapter/slack';
5
+ import { TelegramAdapterConfig } from '@chat-adapter/telegram';
2
6
 
3
7
  /**
4
- * Shared channel streaming driver.
5
- * Turns the agent's streamed output into incremental channel updates so a chat
6
- * reply appears live instead of only after the whole turn. Three modes:
7
- * - "edit": post one placeholder message, then edit it in place on a throttled
8
- * cadence (needs the channel's beginMessage/editMessage primitives); when the
9
- * reply outgrows the channel's message-length cap the current message is frozen
10
- * and streaming continues in a fresh one (rotation).
11
- * - "progress": one live preview for the whole turn (openclaw's progress draft).
12
- * While the model works it shows a compact status (💭 reasoning + 🛠 tool lines);
13
- * when the answer starts streaming the text takes over the same message; finish()
14
- * finalizes that one message in place. One message per turn — never per-block.
15
- * - "chunk": send a new message as each paragraph completes (uses sendText, so it
16
- * works for every channel).
17
- * Accumulation + throttling live here so each channel adapter stays thin; a channel
18
- * that cannot edit a posted message falls back to "chunk" for edit/progress modes.
8
+ * Agent policy contracts and validation.
9
+ * Runtime decisions are made by OPA using the same document/input shape.
19
10
  */
20
-
21
- type ChannelStreamMode = "edit" | "chunk" | "progress";
11
+ declare const AGENT_POLICY_ACTIONS: readonly ["tool.call", "workspace.read", "workspace.write", "workspace.exec", "subagent.run", "skill.load"];
12
+ type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number];
13
+ type AgentPolicyEffect = "allow" | "deny";
14
+ type AgentPolicyMode = "enforce" | "audit";
15
+ type AgentPolicyConditionOperator = "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
16
+ interface AgentPolicyCondition {
17
+ attribute: string;
18
+ operator: AgentPolicyConditionOperator;
19
+ value: string | number | boolean | string[] | number[] | boolean[];
20
+ }
21
+ interface AgentPolicyResourceSelector {
22
+ toolNames?: string[];
23
+ toolIds?: string[];
24
+ workspaceIds?: string[];
25
+ workspaceNames?: string[];
26
+ filePaths?: string[];
27
+ subagentIds?: string[];
28
+ skillPaths?: string[];
29
+ }
30
+ interface AgentPolicyRule {
31
+ id: string;
32
+ effect: AgentPolicyEffect;
33
+ actions: AgentPolicyAction[];
34
+ resources?: AgentPolicyResourceSelector;
35
+ conditions?: AgentPolicyCondition[];
36
+ }
37
+ interface AgentPolicyDocument {
38
+ version: 1;
39
+ rules: AgentPolicyRule[];
40
+ }
41
+ interface AgentPolicyConfig {
42
+ policyIds?: string[];
43
+ mode?: AgentPolicyMode;
44
+ }
22
45
 
23
46
  /**
24
47
  * Supported account model provider names.
@@ -31,6 +54,7 @@ declare const ACCOUNT_MODEL_PROVIDERS: {
31
54
  readonly bedrock: true;
32
55
  readonly gateway: true;
33
56
  readonly minimax: true;
57
+ readonly custom: true;
34
58
  };
35
59
  type AccountModelProviderName = keyof typeof ACCOUNT_MODEL_PROVIDERS;
36
60
 
@@ -52,6 +76,7 @@ interface AgentConfig {
52
76
  tools?: AgentToolsConfig;
53
77
  skills?: AgentSkillsConfig;
54
78
  subagent?: AgentSubagentConfig;
79
+ policy?: AgentPolicyConfig;
55
80
  publicAccess?: boolean;
56
81
  [key: string]: unknown;
57
82
  }
@@ -72,9 +97,16 @@ interface AgentSubagentConfig {
72
97
  allowed?: string[];
73
98
  context?: "new" | "inherited";
74
99
  mode?: "ephemeral" | "persistent";
100
+ /**
101
+ * Controls what the parent agent sees from a finished subagent (AI SDK
102
+ * "controlling what the model sees"): `full` = the child's whole transcript,
103
+ * `result` = only its final result (default), `none` = nothing. A
104
+ * `subagent.task.finished` code hook overrides this for custom shaping.
105
+ */
106
+ visibility?: "full" | "result" | "none";
75
107
  [key: string]: unknown;
76
108
  }
77
- interface AgentModelConfig extends Omit<CallSettings, "abortSignal" | "headers"> {
109
+ interface AgentModelConfig extends LanguageModelCallOptions, Pick<RequestOptions, "maxRetries" | "timeout"> {
78
110
  provider?: AccountModelProviderName;
79
111
  modelId?: string;
80
112
  providerOptions?: AgentModelProviderOptions;
@@ -100,8 +132,28 @@ type AgentModelOutputMetadata = {
100
132
  [key: string]: unknown;
101
133
  };
102
134
  type AgentProviderConfig = Partial<Record<AccountModelProviderName, AgentProviderSettings>>;
135
+ /**
136
+ * Constructor settings for a model provider. The keys are an explicit allow-list
137
+ * (no open index signature) so a misspelled option — most commonly the camel
138
+ * `baseUrl` instead of the canonical `base_url`/`baseURL` — is a compile-time
139
+ * error in the SDK and is caught by `normalizeProviderSettings` at runtime.
140
+ * Keep this list in sync with `normalizeProviderSettings` and the SDK's
141
+ * `KNOWN_PROVIDER_SETTING_KEYS`.
142
+ */
103
143
  interface AgentProviderSettings {
104
- [key: string]: unknown;
144
+ apiKey?: string;
145
+ /** OpenAI-compatible endpoint (`custom`). Snake form, as documented. */
146
+ base_url?: string;
147
+ /** OpenAI-compatible endpoint (`custom`). AI-SDK form; the dashboard writes both. */
148
+ baseURL?: string;
149
+ headers?: Record<string, string>;
150
+ organization?: string;
151
+ project?: string;
152
+ name?: string;
153
+ region?: string;
154
+ accessKeyId?: string;
155
+ secretAccessKey?: string;
156
+ sessionToken?: string;
105
157
  }
106
158
  interface AgentWorkspaceRef {
107
159
  name: string;
@@ -125,6 +177,23 @@ interface AgentSessionCompactionConfig {
125
177
  interface AgentHooksConfig {
126
178
  /** Outbound event webhooks. An agent may register several independent endpoints. */
127
179
  webhooks?: AgentWebhookHookConfig[];
180
+ /**
181
+ * Uploaded code hooks. Each entry references an accountHooks bundle by id; the
182
+ * bundle runs in the V8 isolate at the matching fire-points and its validated
183
+ * return is folded into mutable harness state.
184
+ */
185
+ code?: AgentCodeHookConfig[];
186
+ [key: string]: unknown;
187
+ }
188
+ interface AgentCodeHookConfig {
189
+ hookId: string;
190
+ /**
191
+ * Optional narrowing of the events this reference reacts to. Omitted => the
192
+ * bundle's own declared `events` set. Any listed event outside the bundle's
193
+ * declared set is ignored at runtime.
194
+ */
195
+ events?: AgentHookEventName[];
196
+ enabled?: boolean;
128
197
  [key: string]: unknown;
129
198
  }
130
199
  interface AgentWebhookHookConfig {
@@ -135,6 +204,8 @@ interface AgentWebhookHookConfig {
135
204
  [key: string]: unknown;
136
205
  }
137
206
  type AgentLifecycleEventName = "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";
207
+ type AgentChannelHookEventName = "channel.message.received" | "channel.message.sending";
208
+ type AgentHookEventName = AgentLifecycleEventName | AgentChannelHookEventName;
138
209
  type AgentToolsConfig = Record<string, AgentToolConfig>;
139
210
  interface AgentToolConfig {
140
211
  enabled?: boolean;
@@ -152,54 +223,83 @@ interface AgentChannelsConfig {
152
223
  zalo?: AgentZaloChannelConfig;
153
224
  [key: string]: unknown;
154
225
  }
155
- type AgentChannelStreamingMode = ChannelStreamMode | "off";
156
- interface AgentChannelStreamingConfig {
157
- mode?: AgentChannelStreamingMode;
158
- [key: string]: unknown;
159
- }
226
+ type AgentChannelWorkspaceScope = {
227
+ level: "channel";
228
+ alias?: never;
229
+ } | {
230
+ level: "conversation";
231
+ alias: string;
232
+ };
160
233
  interface AgentTelegramChannelConfig {
161
- botToken?: string;
162
- webhookSecret?: string;
234
+ id?: string;
235
+ apiUrl?: TelegramAdapterConfig["apiUrl"];
236
+ botToken?: TelegramAdapterConfig["botToken"];
237
+ webhookSecret?: TelegramAdapterConfig["secretToken"];
163
238
  allowedChatIds?: number[];
164
239
  reactionEmoji?: string;
165
- streaming?: AgentChannelStreamingConfig;
240
+ workspaceScope?: AgentChannelWorkspaceScope;
166
241
  [key: string]: unknown;
167
242
  }
168
243
  interface AgentGitHubChannelConfig {
169
- webhookSecret?: string;
170
- appId?: string;
171
- privateKey?: string;
244
+ id?: string;
245
+ apiUrl?: Extract<GitHubAdapterConfig, {
246
+ appId: string;
247
+ }>["apiUrl"];
248
+ webhookSecret?: Extract<GitHubAdapterConfig, {
249
+ appId: string;
250
+ }>["webhookSecret"];
251
+ appId?: Extract<GitHubAdapterConfig, {
252
+ appId: string;
253
+ }>["appId"];
254
+ privateKey?: Extract<GitHubAdapterConfig, {
255
+ appId: string;
256
+ }>["privateKey"];
172
257
  allowedRepos?: string[];
258
+ /** Bot username for @-mention detection (e.g. "my-bot" or "my-bot[bot]"). */
259
+ userName?: string;
260
+ /** Bot's numeric GitHub user ID for self-message detection. */
261
+ botUserId?: number;
262
+ /** 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. */
263
+ triggerOnIssueOpen?: boolean;
264
+ /** 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. */
265
+ triggerOnPROpen?: boolean;
266
+ workspaceScope?: AgentChannelWorkspaceScope;
173
267
  [key: string]: unknown;
174
268
  }
175
269
  interface AgentSlackChannelConfig {
270
+ id?: string;
271
+ apiUrl?: SlackAdapterConfig["apiUrl"];
176
272
  botToken?: string;
177
- signingSecret?: string;
273
+ signingSecret?: SlackAdapterConfig["signingSecret"];
178
274
  allowedChannelIds?: string[];
179
- streaming?: AgentChannelStreamingConfig;
275
+ reactionEmoji?: string;
276
+ workspaceScope?: AgentChannelWorkspaceScope;
180
277
  [key: string]: unknown;
181
278
  }
182
279
  interface AgentDiscordChannelConfig {
183
- botToken?: string;
184
- publicKey?: string;
280
+ id?: string;
281
+ apiUrl?: DiscordAdapterConfig["apiUrl"];
282
+ botToken?: DiscordAdapterConfig["botToken"];
283
+ publicKey?: DiscordAdapterConfig["publicKey"];
185
284
  allowedGuildIds?: string[];
186
- streaming?: AgentChannelStreamingConfig;
285
+ workspaceScope?: AgentChannelWorkspaceScope;
187
286
  [key: string]: unknown;
188
287
  }
189
288
  interface AgentPancakeChannelConfig {
289
+ id?: string;
190
290
  pageId?: string;
191
291
  pageAccessToken?: string;
192
292
  webhookSecret?: string;
193
293
  senderId?: string;
194
- options?: Record<string, unknown>;
195
- streaming?: AgentChannelStreamingConfig;
294
+ workspaceScope?: AgentChannelWorkspaceScope;
196
295
  [key: string]: unknown;
197
296
  }
198
297
  interface AgentZaloChannelConfig {
298
+ id?: string;
199
299
  botToken?: string;
200
300
  webhookSecret?: string;
201
301
  allowedUserIds?: string[];
202
- streaming?: AgentChannelStreamingConfig;
302
+ workspaceScope?: AgentChannelWorkspaceScope;
203
303
  [key: string]: unknown;
204
304
  }
205
305
 
@@ -248,6 +348,21 @@ type UpdateCronInput = {
248
348
  input?: never;
249
349
  });
250
350
 
351
+ /**
352
+ * Predefined sandbox sizes — the canonical (vcpu, memoryMb, storageGb) catalog
353
+ * shared by sandbox config validation, the workdir resource mapping, and the
354
+ * Convex `sandboxInstances` mirror. Sizes are the user-facing knob (`config.size`)
355
+ * that reconciles issue #78's tiers with each backend's real limits.
356
+ *
357
+ * The specs are canonical/advisory: workdir applies them as create-time resources
358
+ * (clamping vcpu to its allowed set); MicroVM bakes size into the image so the
359
+ * specs are display-only there; daytona/e2b/vercel size natively. The control-plane
360
+ * mirror type lives here too so the Convex writer and the executors share one shape
361
+ * without importing across the _shared/harness boundary.
362
+ */
363
+
364
+ type SandboxSize = "tiny" | "xsmall" | "small" | "medium" | "large";
365
+
251
366
  /**
252
367
  * Sandbox config: account-scoped, reusable sandbox definitions referenced by
253
368
  * agents via `config.sandbox`. A sandbox is a collection of Claude-Code-style
@@ -256,7 +371,8 @@ type UpdateCronInput = {
256
371
  * DynamoDB / Convex stores call these at their create/update entry points.
257
372
  * Stored encrypted at rest because `envVars`/`options` may hold secrets.
258
373
  */
259
- type SandboxProvider = "lambda" | "e2b" | "daytona" | "kubernetes" | "vercel";
374
+
375
+ type SandboxProvider = "sandbox" | "lambda" | "e2b" | "daytona" | "vercel";
260
376
  type SandboxRuntimeName = "bash" | "python" | "node";
261
377
  type SandboxPermissionMode = "edit" | "ask" | "bypass";
262
378
  type SandboxNetworkMode = "allow-all" | "deny-all" | "restricted";
@@ -271,11 +387,12 @@ interface SandboxNetworkConfig {
271
387
  }
272
388
  interface SandboxConfig {
273
389
  provider: SandboxProvider;
390
+ size?: SandboxSize;
391
+ snapshot?: string;
274
392
  runtimes?: SandboxRuntimeName[];
275
393
  network?: SandboxNetworkConfig;
276
394
  permissionMode?: SandboxPermissionMode;
277
395
  persistent?: boolean;
278
- ephemeralHome?: boolean;
279
396
  lifecycle?: SandboxLifecycleConfig;
280
397
  onCreate?: string[];
281
398
  onResume?: string[];
@@ -296,15 +413,127 @@ interface SandboxConfig {
296
413
  */
297
414
  declare const WORKSPACE_STORAGE_PROVIDERS: readonly ["s3"];
298
415
  type WorkspaceStorageProvider = (typeof WORKSPACE_STORAGE_PROVIDERS)[number];
416
+ type WorkspaceStorageAuth = {
417
+ type: "managed";
418
+ } | {
419
+ type: "assumeRole";
420
+ roleArn: string;
421
+ externalId?: string;
422
+ };
423
+ interface WorkspaceStorageConfig {
424
+ provider: WorkspaceStorageProvider;
425
+ bucket?: string;
426
+ region?: string;
427
+ endpoint?: string;
428
+ prefix?: string;
429
+ auth?: WorkspaceStorageAuth;
430
+ }
299
431
  interface WorkspaceConfig {
300
- storage: {
301
- provider: WorkspaceStorageProvider;
302
- };
432
+ storage: WorkspaceStorageConfig;
433
+ isolation?: boolean;
303
434
  harness?: {
304
435
  enabled?: boolean;
305
436
  };
306
437
  }
307
438
 
439
+ /**
440
+ * Telegram channel adapter implementated as a ChannelAdapter.
441
+ * Implements Telegram auth, message normalization, and reply actions through the Chat SDK Telegram adapter.
442
+ */
443
+
444
+ interface TelegramSource {
445
+ chatId: number;
446
+ messageId: string;
447
+ threadId: string;
448
+ fromUserId?: number;
449
+ fromUsername?: string;
450
+ }
451
+
452
+ /**
453
+ * GitHub channel adapter.
454
+ * Keep Broods-specific event filtering/source mapping here; delegate GitHub auth and API calls to Chat SDK.
455
+ */
456
+
457
+ interface GitHubSource {
458
+ owner: string;
459
+ repo: string;
460
+ installationId: number;
461
+ threadId: string;
462
+ messageId?: string;
463
+ issueNumber?: number;
464
+ pullNumber?: number;
465
+ commentId?: number;
466
+ target: "issue" | "issue_comment" | "pull_request" | "pull_request_review_comment";
467
+ }
468
+
469
+ /**
470
+ * Slack channel adapter.
471
+ * Handle request verification, inbound normalization, and Slack Web API reply actions here.
472
+ */
473
+
474
+ interface SlackSource {
475
+ teamId: string;
476
+ channelId: string;
477
+ threadTs?: string;
478
+ messageTs?: string;
479
+ responseUrl?: string;
480
+ commandToken?: string;
481
+ userId?: string;
482
+ }
483
+
484
+ /**
485
+ * Discord channel adapter.
486
+ * Verify interaction signatures, normalize slash commands, and send replies through Chat SDK's Discord adapter.
487
+ */
488
+
489
+ interface DiscordSource {
490
+ applicationId: string;
491
+ interactionToken?: string;
492
+ interactionId?: string;
493
+ guildId?: string;
494
+ channelId?: string;
495
+ threadId?: string;
496
+ messageId?: string;
497
+ commandToken?: string;
498
+ userId?: string;
499
+ }
500
+
501
+ /**
502
+ * Pancake channel adapter.
503
+ * Keep Pancake webhook normalization and outbound message API calls here.
504
+ *
505
+ * Per-conversation policy (e.g. skipping human-owned conversations by tag) is
506
+ * not baked in: the parsed source carries `tagIds` so a user `onMessageReceived`
507
+ * hook can decide to drop the message.
508
+ */
509
+
510
+ interface PancakeSource {
511
+ pageId: string;
512
+ conversationId: string;
513
+ messageId: string;
514
+ messageType: "INBOX" | "COMMENT";
515
+ postId?: string;
516
+ fromId?: string;
517
+ fromName?: string;
518
+ pageCustomerId?: string;
519
+ tagIds?: string[];
520
+ }
521
+
522
+ /**
523
+ * Zalo channel adapter.
524
+ * Keep official Zalo Bot API webhook normalization and outbound API calls here.
525
+ */
526
+
527
+ interface ZaloSource {
528
+ chatId: string;
529
+ chatType: "PRIVATE";
530
+ messageId: string;
531
+ senderId: string;
532
+ senderName?: string;
533
+ eventName: string;
534
+ date?: number;
535
+ }
536
+
308
537
  /**
309
538
  * Canonical CLI manifest wire types — the single source of truth shared by the
310
539
  * backend (cliSync.ts, cliHttp.ts) and the SDK/CLI (packages/broods).
@@ -313,7 +542,7 @@ interface WorkspaceConfig {
313
542
  * import it without pulling the Convex server module graph into its typecheck.
314
543
  */
315
544
  type CliManifestResource = {
316
- kind: "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool";
545
+ kind: "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "hook" | "policy";
317
546
  name: string;
318
547
  description?: string;
319
548
  config: unknown;
@@ -325,6 +554,8 @@ type GeneratedIds = {
325
554
  crons: Record<string, string>;
326
555
  skills: Record<string, string>;
327
556
  tools: Record<string, string>;
557
+ hooks: Record<string, string>;
558
+ policies: Record<string, string>;
328
559
  };
329
560
  type CliManifest = {
330
561
  version: 1;
@@ -351,12 +582,12 @@ type AgentConfigDoc = Doc<"agentConfigs">;
351
582
  type WorkspaceConfigDoc = Doc<"workspaceConfigs">;
352
583
  type SandboxConfigDoc = Doc<"sandboxConfigs">;
353
584
  type CronDoc = Doc<"crons">;
354
- type CliResourceKind = "agent" | "workspace" | "sandbox" | "cron";
585
+ type CliResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy";
355
586
 
356
587
  /**
357
588
  * Wire types for the public account-manage and harness APIs. These mirror
358
589
  * the deployed API contract (docs/api-reference/openapi.yaml is the source
359
- * of truth); they are intentionally independent of the Lambda internals so
590
+ * of truth); they are intentionally independent of the runtime internals so
360
591
  * the SDK keeps working when the runtime is ported.
361
592
  */
362
593
 
@@ -443,10 +674,11 @@ interface CustomTool {
443
674
  name: string;
444
675
  description: string;
445
676
  sha256: string;
677
+ runtime?: "isolate" | "sandbox";
446
678
  }
447
679
 
448
680
  type StreamTextOptions = Parameters<typeof streamText>[0];
449
- type JsonCallSettings = Partial<Omit<CallSettings, "abortSignal" | "headers">>;
681
+ type JsonCallSettings = Partial<LanguageModelCallOptions & Pick<RequestOptions, "maxRetries" | "timeout">>;
450
682
  type AgentRunModelOverrides = JsonCallSettings & Pick<StreamTextOptions, "providerOptions">;
451
683
  type AgentRunOverrides = {
452
684
  system?: SystemModelMessage | SystemModelMessage[];
@@ -542,6 +774,7 @@ type ObservabilitySubscribeMessage = {
542
774
  type: "subscribe";
543
775
  stream: "logs" | "traces";
544
776
  backfill?: number;
777
+ liveOnly?: boolean;
545
778
  minLevel?: LogLevel;
546
779
  };
547
780
  type ObservabilityUnsubscribeMessage = {
@@ -636,6 +869,7 @@ interface ResourceApi {
636
869
  readonly crons?: Record<string, unknown>;
637
870
  readonly skills?: Record<string, unknown>;
638
871
  readonly tools?: Record<string, unknown>;
872
+ readonly policies?: Record<string, unknown>;
639
873
  }
640
874
  interface BroodsClientOptions {
641
875
  /**
@@ -816,12 +1050,14 @@ interface BroodsProjectConfig {
816
1050
  [name: string]: string | undefined;
817
1051
  };
818
1052
  dashboardUrl?: string;
1053
+ /** Convex control-plane base URL for sync/env calls; defaults to the URL discovered at login. */
1054
+ baseUrl?: string;
819
1055
  }
820
1056
  interface BroodsConfigDefinition {
821
1057
  readonly [CONFIG_MARKER]: true;
822
1058
  readonly config: BroodsProjectConfig;
823
1059
  }
824
- type ResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool";
1060
+ type ResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy";
825
1061
  interface ResourceDefinition<Kind extends ResourceKind, Name extends string, Config> {
826
1062
  readonly [RESOURCE_MARKER]: true;
827
1063
  readonly kind: Kind;
@@ -858,65 +1094,45 @@ interface ToolDefinitionConfig {
858
1094
  path: string;
859
1095
  description: string;
860
1096
  inputSchema: Record<string, unknown>;
1097
+ runtime?: "isolate" | "sandbox";
861
1098
  defaultConfig?: Record<string, unknown>;
862
1099
  }
1100
+ type PolicyDefinitionConfig = Omit<AgentPolicyDocument, "version"> & {
1101
+ version?: AgentPolicyDocument["version"];
1102
+ };
863
1103
  type ChannelType = "telegram" | "github" | "slack" | "discord" | "pancake" | "zalo";
864
1104
  interface ChannelDefinition<Type extends ChannelType, Config> {
865
1105
  readonly [CHANNEL_MARKER]: true;
866
1106
  readonly kind: "channel";
867
1107
  readonly type: Type;
1108
+ readonly workspaceScope?: AgentChannelWorkspaceScope;
868
1109
  readonly config: Config;
869
1110
  }
1111
+ type RequiredChannelKeys<Config, Keys extends keyof Config> = Required<Pick<Config, Keys>> & Omit<Config, Keys>;
870
1112
  type ChannelSecret = string | EnvRef | undefined;
871
- interface TelegramChannelInput {
872
- botToken: ChannelSecret;
873
- webhookSecret: ChannelSecret;
874
- allowedChatIds: readonly number[];
875
- reactionEmoji?: string | EnvRef;
876
- streaming?: EnvRefString<NonNullable<AgentTelegramChannelConfig["streaming"]>>;
877
- }
878
- interface GitHubChannelInput {
879
- webhookSecret: ChannelSecret;
880
- appId: ChannelSecret;
881
- privateKey: ChannelSecret;
882
- allowedRepos?: readonly (string | EnvRef)[];
883
- }
884
- interface SlackChannelInput {
885
- botToken: ChannelSecret;
886
- signingSecret: ChannelSecret;
887
- allowedChannelIds?: readonly (string | EnvRef)[];
888
- streaming?: EnvRefString<NonNullable<AgentSlackChannelConfig["streaming"]>>;
889
- }
890
- interface DiscordChannelInput {
891
- botToken: ChannelSecret;
892
- publicKey: ChannelSecret;
893
- allowedGuildIds?: readonly (string | EnvRef)[];
894
- streaming?: EnvRefString<NonNullable<AgentDiscordChannelConfig["streaming"]>>;
895
- }
896
- interface PancakeChannelInput {
1113
+ type ChannelIdentityInput = {
1114
+ workspaceScope?: AgentChannelWorkspaceScope;
1115
+ };
1116
+ type TelegramChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentTelegramChannelConfig, "apiUrl" | "botToken" | "webhookSecret" | "allowedChatIds" | "reactionEmoji">, "botToken" | "webhookSecret" | "allowedChatIds">> & ChannelIdentityInput;
1117
+ type GitHubChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentGitHubChannelConfig, "apiUrl" | "webhookSecret" | "appId" | "privateKey" | "allowedRepos" | "userName" | "botUserId" | "triggerOnIssueOpen" | "triggerOnPROpen">, "webhookSecret" | "appId" | "privateKey">> & ChannelIdentityInput;
1118
+ type SlackChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentSlackChannelConfig, "apiUrl" | "botToken" | "signingSecret" | "allowedChannelIds" | "reactionEmoji">, "botToken" | "signingSecret">> & ChannelIdentityInput;
1119
+ type DiscordChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentDiscordChannelConfig, "apiUrl" | "botToken" | "publicKey" | "allowedGuildIds">, "botToken" | "publicKey">> & ChannelIdentityInput;
1120
+ interface PancakeChannelInput extends ChannelIdentityInput {
897
1121
  pageId: ChannelSecret;
898
1122
  pageAccessToken: ChannelSecret;
899
1123
  webhookSecret: ChannelSecret;
900
1124
  senderId?: string | EnvRef;
901
- ignoreTagIds?: readonly (string | EnvRef)[];
902
- streaming?: EnvRefString<NonNullable<AgentPancakeChannelConfig["streaming"]>>;
903
1125
  }
904
- type PancakeChannelDefinitionConfig = Omit<PancakeChannelInput, "ignoreTagIds"> & {
905
- options?: {
906
- ignoreTagIds?: readonly (string | EnvRef)[];
907
- };
908
- };
909
- interface ZaloChannelInput {
1126
+ interface ZaloChannelInput extends ChannelIdentityInput {
910
1127
  botToken: ChannelSecret;
911
1128
  webhookSecret: ChannelSecret;
912
1129
  allowedUserIds: readonly (string | EnvRef)[];
913
- streaming?: EnvRefString<NonNullable<AgentZaloChannelConfig["streaming"]>>;
914
1130
  }
915
1131
  type TelegramChannelDefinition = ChannelDefinition<"telegram", TelegramChannelInput>;
916
1132
  type GitHubChannelDefinition = ChannelDefinition<"github", GitHubChannelInput>;
917
1133
  type SlackChannelDefinition = ChannelDefinition<"slack", SlackChannelInput>;
918
1134
  type DiscordChannelDefinition = ChannelDefinition<"discord", DiscordChannelInput>;
919
- type PancakeChannelDefinition = ChannelDefinition<"pancake", PancakeChannelDefinitionConfig>;
1135
+ type PancakeChannelDefinition = ChannelDefinition<"pancake", PancakeChannelInput>;
920
1136
  type ZaloChannelDefinition = ChannelDefinition<"zalo", ZaloChannelInput>;
921
1137
  type AnyChannelDefinition = TelegramChannelDefinition | GitHubChannelDefinition | SlackChannelDefinition | DiscordChannelDefinition | PancakeChannelDefinition | ZaloChannelDefinition;
922
1138
  /**
@@ -941,6 +1157,133 @@ type AgentSubagentDefinitionConfig = Omit<NonNullable<AgentConfig["subagent"]>,
941
1157
  type AgentSkillsDefinitionConfig = Omit<NonNullable<AgentConfig["skills"]>, "allowed"> & {
942
1158
  allowed?: readonly (SkillResource | string)[];
943
1159
  };
1160
+ interface HookContext {
1161
+ fetch: typeof fetch;
1162
+ config: Record<string, unknown>;
1163
+ /**
1164
+ * Mutable per-request scratchpad shared across this agent request's hooks.
1165
+ * Seed it in an early hook (e.g. `onStart`) and read or modify it later —
1166
+ * every loop hook, `onSubagentFinish`, and the reply's `onMessageSending`
1167
+ * see the same state. Keep it JSON-serializable. `onMessageReceived`,
1168
+ * delayed background replies, and each subagent's own run get fresh state.
1169
+ */
1170
+ state: Record<string, unknown>;
1171
+ }
1172
+ type Handler<Event, Result> = (ctx: HookContext, event: Event) => Result | void | Promise<Result | void>;
1173
+ /**
1174
+ * Channel-specific routing data attached to an inbound message. Inherited from
1175
+ * the core channel adapters (via contracts.ts) so an `onMessageReceived` hook
1176
+ * that narrows on `event.channel` always sees exactly what core emits (e.g.
1177
+ * Pancake `tagIds`).
1178
+ */
1179
+ type TelegramMessageSource = TelegramSource;
1180
+ type GitHubMessageSource = GitHubSource;
1181
+ type SlackMessageSource = SlackSource;
1182
+ type DiscordMessageSource = DiscordSource;
1183
+ type PancakeMessageSource = PancakeSource;
1184
+ type ZaloMessageSource = ZaloSource;
1185
+ /**
1186
+ * Inbound channel message passed to `onMessageReceived`, discriminated on
1187
+ * `channel` so each variant exposes its channel's strongly-typed `source`.
1188
+ */
1189
+ type ChannelMessageReceived = {
1190
+ channel: "telegram";
1191
+ text: string;
1192
+ source: TelegramMessageSource;
1193
+ } | {
1194
+ channel: "github";
1195
+ text: string;
1196
+ source: GitHubMessageSource;
1197
+ } | {
1198
+ channel: "slack";
1199
+ text: string;
1200
+ source: SlackMessageSource;
1201
+ } | {
1202
+ channel: "discord";
1203
+ text: string;
1204
+ source: DiscordMessageSource;
1205
+ } | {
1206
+ channel: "pancake";
1207
+ text: string;
1208
+ source: PancakeMessageSource;
1209
+ } | {
1210
+ channel: "zalo";
1211
+ text: string;
1212
+ source: ZaloMessageSource;
1213
+ };
1214
+ /**
1215
+ * Inline agent hook callbacks. Handlers are serialized with `.toString()`,
1216
+ * bundled into one account hook, and run in a fresh V8 isolate. Keep them
1217
+ * self-contained: use only `ctx`, `event`, and JavaScript globals. Do not rely
1218
+ * on imports or closure variables. Arrow functions and function expressions are
1219
+ * preferred so the serialized source is valid as an object-literal value.
1220
+ *
1221
+ * Subagent runs fire hooks too: a registered subagent runs its own hooks, a
1222
+ * prompt-only (virtual) subagent inherits this bundle — always with fresh
1223
+ * `ctx.state`. `onSubagentFinish` fires on the parent with the parent's state.
1224
+ */
1225
+ interface AgentHooks {
1226
+ onStart?: Handler<{
1227
+ system: string;
1228
+ messages: unknown[];
1229
+ }, {
1230
+ system?: string;
1231
+ messages?: unknown[];
1232
+ }>;
1233
+ onStepFinish?: Handler<{
1234
+ stepNumber: number;
1235
+ finishReason: string;
1236
+ toolCallCount: number;
1237
+ }, void>;
1238
+ onToolCall?: Handler<{
1239
+ toolName: string;
1240
+ input: unknown;
1241
+ }, {
1242
+ decision?: "allow" | "deny";
1243
+ args?: Record<string, unknown>;
1244
+ denyReason?: string;
1245
+ }>;
1246
+ onToolResult?: Handler<{
1247
+ toolName: string;
1248
+ output: unknown;
1249
+ }, {
1250
+ output?: unknown;
1251
+ }>;
1252
+ onFinish?: Handler<{
1253
+ finishReason: string;
1254
+ response: unknown;
1255
+ }, {
1256
+ output?: unknown;
1257
+ }>;
1258
+ onApproval?: Handler<{
1259
+ approvals: unknown;
1260
+ }, {
1261
+ approve?: boolean;
1262
+ }>;
1263
+ onError?: Handler<{
1264
+ error: string;
1265
+ }, void>;
1266
+ onSubagentFinish?: Handler<{
1267
+ taskId: string;
1268
+ result: unknown;
1269
+ }, {
1270
+ visibleResult?: unknown;
1271
+ }>;
1272
+ onMessageReceived?: Handler<ChannelMessageReceived, {
1273
+ drop?: boolean;
1274
+ text?: string;
1275
+ }>;
1276
+ onMessageSending?: Handler<{
1277
+ channel: ChannelType;
1278
+ text: string;
1279
+ }, {
1280
+ drop?: boolean;
1281
+ text?: string;
1282
+ }>;
1283
+ }
1284
+ type AgentPolicyDefinitionConfig = Omit<AgentPolicyConfig, "policyIds"> & {
1285
+ policies?: readonly (PolicyResource | string)[];
1286
+ };
944
1287
  /**
945
1288
  * Code-first agent config surface. Built from an explicit `Pick` of `AgentConfig`
946
1289
  * (not `Omit`) so the SDK input type does NOT inherit `AgentConfig`'s
@@ -949,12 +1292,42 @@ type AgentSkillsDefinitionConfig = Omit<NonNullable<AgentConfig["skills"]>, "all
949
1292
  * `workspace:` instead of `workspaces:`. Add a key here when core's `AgentConfig`
950
1293
  * gains a new top-level field that should be code-definable.
951
1294
  */
952
- type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" | "provider" | "session" | "hooks" | "tools">> & {
1295
+ /**
1296
+ * SDK-facing model-provider constructor settings. Written as an explicit
1297
+ * interface — NOT `EnvRefString<AgentProviderSettings>` — because TypeScript
1298
+ * suppresses excess-property checks through mapped types, which would let a
1299
+ * typo like the camel `baseUrl` (instead of `base_url`/`baseURL`) slip past
1300
+ * `tsc`. Keep the keys in lockstep with core's `AgentProviderSettings`; the
1301
+ * `_ProviderKeyParity` assertion below fails `broods check` if they drift.
1302
+ * Every string field also accepts an `env(...)` reference.
1303
+ */
1304
+ interface ProviderSettingsInput {
1305
+ apiKey?: string | EnvRef;
1306
+ base_url?: string | EnvRef;
1307
+ baseURL?: string | EnvRef;
1308
+ headers?: Record<string, string | EnvRef>;
1309
+ organization?: string | EnvRef;
1310
+ project?: string | EnvRef;
1311
+ name?: string | EnvRef;
1312
+ region?: string | EnvRef;
1313
+ accessKeyId?: string | EnvRef;
1314
+ secretAccessKey?: string | EnvRef;
1315
+ sessionToken?: string | EnvRef;
1316
+ }
1317
+ /** Per-provider settings; provider names stay synced with core's `AgentConfig`. */
1318
+ type ProviderConfigInput = Partial<Record<keyof NonNullable<AgentConfig["provider"]>, ProviderSettingsInput>>;
1319
+ type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" | "session" | "tools">> & {
1320
+ provider?: ProviderConfigInput;
1321
+ } & {
1322
+ hooks?: AgentHooks & {
1323
+ webhooks?: readonly EnvRefString<AgentWebhookHookConfig>[];
1324
+ };
953
1325
  channels?: readonly AnyChannelDefinition[];
954
1326
  sandbox?: SandboxResource | string;
955
1327
  workspaces?: readonly AgentWorkspaceInput[];
956
1328
  subagent?: AgentSubagentDefinitionConfig;
957
1329
  skills?: AgentSkillsDefinitionConfig;
1330
+ policy?: AgentPolicyDefinitionConfig;
958
1331
  /**
959
1332
  * Opt the agent into the public runtime endpoint (SSE/WebSocket via the
960
1333
  * environment runtime key). Off by default — secured: when unset the public
@@ -971,8 +1344,9 @@ type WorkspaceResource<Name extends string = string> = ResourceDefinition<"works
971
1344
  type SandboxResource<Name extends string = string> = ResourceDefinition<"sandbox", Name, SandboxDefinitionConfig>;
972
1345
  type SkillResource<Name extends string = string> = ResourceDefinition<"skill", Name, SkillDefinitionConfig>;
973
1346
  type ToolResource<Name extends string = string> = ResourceDefinition<"tool", Name, ToolDefinitionConfig>;
1347
+ type PolicyResource<Name extends string = string> = ResourceDefinition<"policy", Name, PolicyDefinitionConfig>;
974
1348
  type CronResource<Name extends string = string> = ResourceDefinition<"cron", Name, CronDefinitionConfig>;
975
- type AnyResource = AgentResource | WorkspaceResource | SandboxResource | CronResource | SkillResource | ToolResource;
1349
+ type AnyResource = AgentResource | WorkspaceResource | SandboxResource | CronResource | SkillResource | ToolResource | PolicyResource;
976
1350
  /**
977
1351
  * References an account/environment variable resolved on the SERVER at runtime —
978
1352
  * set it with `broods env set <NAME>` or in the dashboard (the Convex-style
@@ -999,10 +1373,11 @@ declare function defineWorkspace<const Name extends string>(input: ResourceDefin
999
1373
  declare function defineSandbox<const Name extends string>(input: ResourceDefinitionInput<Name, SandboxDefinitionConfig>): SandboxResource<Name>;
1000
1374
  declare function defineSkill<const Name extends string>(input: ResourceDefinitionInput<Name, SkillDefinitionConfig>): SkillResource<Name>;
1001
1375
  declare function defineTool<const Name extends string>(input: ResourceDefinitionInput<Name, ToolDefinitionConfig>): ToolResource<Name>;
1376
+ declare function definePolicy<const Name extends string>(input: ResourceDefinitionInput<Name, PolicyDefinitionConfig>): PolicyResource<Name>;
1002
1377
  declare function defineCron<const Name extends string>(input: ResourceDefinitionInput<Name, CronDefinitionConfig>): CronResource<Name>;
1003
1378
  declare function isResource(value: unknown): value is AnyResource;
1004
1379
  declare function isChannelDefinition(value: unknown): value is AnyChannelDefinition;
1005
1380
  declare function isBroodsConfig(value: unknown): value is BroodsConfigDefinition;
1006
1381
 
1007
- export { BroodsClient, BroodsWebSocketClient, DEFAULT_CORE_BASE_URL, MAX_OBSERVABILITY_BACKFILL, BroodsWebSocketClient as WebSocketClient, BroodsWebSocketClient as WebsocketClient, defineAgent, defineBroods, defineCron, defineDiscordChannel, defineGitHubChannel, definePancakeChannel, defineSandbox, defineSkill, defineSlackChannel, defineTelegramChannel, defineTool, defineWorkspace, defineZaloChannel, env, isBroodsConfig, isChannelDefinition, isObservabilityClientMessage, isResource, normalizeHttpServiceUrl, readSseStream, resolveRunEvents, toWebSocketBaseUrl };
1008
- export type { Account, Agent, AgentChannelsConfig, AgentConfig, AgentConfigDoc, AgentDefinitionConfig, AgentDiscordChannelConfig, AgentGitHubChannelConfig, AgentHandle, AgentPancakeChannelConfig, AgentReference, AgentResource, AgentRunEventInput, AgentRunInput, AgentRunModelOverrides, AgentRunOverrides, AgentRunResult, AgentSkillsDefinitionConfig, AgentSlackChannelConfig, AgentStreamPart, AgentSubagentDefinitionConfig, AgentTelegramChannelConfig, AgentWorkspaceInput, AgentWorkspaceRef, AgentWorkspaceRefInput, AgentZaloChannelConfig, AnyChannelDefinition, AnyResource, AsyncAgentRun, AsyncPollOptions, AsyncRequestAccepted, AsyncStatus, BroodsClientOptions, BroodsConfigDefinition, BroodsProjectConfig, BroodsWebSocketClientOptions, ChannelDefinition, ChannelReference, ChannelType, CliManifest, CliManifestResource, CliResourceKind, CreateClientCronInput, CreateCronInput, Cron, CronDefinitionConfig, CronDoc, CronLastStatus, CronResource, CronRun, CronStatus, CustomTool, DiscordChannelDefinition, DiscordChannelInput, Doc, EnvAccessor, EnvRef, EnvRefString, EnvironmentDoc, GeneratedIds, GitHubChannelDefinition, GitHubChannelInput, Id, LogLevel, ObservabilityBackfillMessage, ObservabilityClientMessage, ObservabilityErrorMessage, ObservabilityLogEntry, ObservabilityLogMessage, ObservabilityReadyMessage, ObservabilityServerMessage, ObservabilitySpanMessage, ObservabilitySpanRow, ObservabilitySubscribeMessage, ObservabilityUnsubscribeMessage, PancakeChannelDefinition, PancakeChannelInput, ProjectDoc, ResourceApi, ResourceDefinition, ResourceDefinitionInput, ResourceKind, Sandbox, SandboxConfig, SandboxConfigDoc, SandboxDefinitionConfig, SandboxResource, Skill, SkillDefinitionConfig, SkillResource, SlackChannelDefinition, SlackChannelInput, TelegramChannelDefinition, TelegramChannelInput, ToolApprovalSummary, ToolDefinitionConfig, ToolResource, UpdateCronInput, WebSocketClientCancelMessage, WebSocketClientExecuteMessage, WebSocketClientMessage, WebSocketConstructorLike, WebSocketHandlers, WebSocketLike, WebSocketRunInput, WebSocketServerMessage, WebSocketStreamMessage, WebSocketSubscription, Workspace, WorkspaceConfig, WorkspaceConfigDoc, WorkspaceResource, ZaloChannelDefinition, ZaloChannelInput };
1382
+ export { BroodsClient, BroodsWebSocketClient, DEFAULT_CORE_BASE_URL, MAX_OBSERVABILITY_BACKFILL, BroodsWebSocketClient as WebSocketClient, BroodsWebSocketClient as WebsocketClient, defineAgent, defineBroods, defineCron, defineDiscordChannel, defineGitHubChannel, definePancakeChannel, definePolicy, defineSandbox, defineSkill, defineSlackChannel, defineTelegramChannel, defineTool, defineWorkspace, defineZaloChannel, env, isBroodsConfig, isChannelDefinition, isObservabilityClientMessage, isResource, normalizeHttpServiceUrl, readSseStream, resolveRunEvents, toWebSocketBaseUrl };
1383
+ export type { Account, 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, BroodsClientOptions, BroodsConfigDefinition, BroodsProjectConfig, BroodsWebSocketClientOptions, ChannelDefinition, ChannelMessageReceived, ChannelReference, ChannelType, CliManifest, CliManifestResource, CliResourceKind, CreateClientCronInput, CreateCronInput, Cron, CronDefinitionConfig, CronDoc, CronLastStatus, CronResource, CronRun, CronStatus, CustomTool, DiscordChannelDefinition, DiscordChannelInput, DiscordMessageSource, DiscordSource, Doc, EnvAccessor, EnvRef, EnvRefString, EnvironmentDoc, GeneratedIds, GitHubChannelDefinition, GitHubChannelInput, GitHubMessageSource, GitHubSource, HookContext, Id, LogLevel, ObservabilityBackfillMessage, ObservabilityClientMessage, ObservabilityErrorMessage, ObservabilityLogEntry, ObservabilityLogMessage, ObservabilityReadyMessage, ObservabilityServerMessage, ObservabilitySpanMessage, ObservabilitySpanRow, ObservabilitySubscribeMessage, ObservabilityUnsubscribeMessage, PancakeChannelDefinition, PancakeChannelInput, PancakeMessageSource, PancakeSource, PolicyDefinitionConfig, PolicyResource, ProjectDoc, ProviderConfigInput, ProviderSettingsInput, ResourceApi, ResourceDefinition, ResourceDefinitionInput, ResourceKind, Sandbox, SandboxConfig, SandboxConfigDoc, SandboxDefinitionConfig, SandboxResource, Skill, SkillDefinitionConfig, SkillResource, SlackChannelDefinition, SlackChannelInput, SlackMessageSource, SlackSource, TelegramChannelDefinition, TelegramChannelInput, TelegramMessageSource, TelegramSource, ToolApprovalSummary, ToolDefinitionConfig, ToolResource, UpdateCronInput, WebSocketClientCancelMessage, WebSocketClientExecuteMessage, WebSocketClientMessage, WebSocketConstructorLike, WebSocketHandlers, WebSocketLike, WebSocketRunInput, WebSocketServerMessage, WebSocketStreamMessage, WebSocketSubscription, Workspace, WorkspaceConfig, WorkspaceConfigDoc, WorkspaceResource, ZaloChannelDefinition, ZaloChannelInput, ZaloMessageSource, ZaloSource };