broods 0.1.0 → 0.2.0

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;
@@ -125,6 +157,23 @@ interface AgentSessionCompactionConfig {
125
157
  interface AgentHooksConfig {
126
158
  /** Outbound event webhooks. An agent may register several independent endpoints. */
127
159
  webhooks?: AgentWebhookHookConfig[];
160
+ /**
161
+ * Uploaded code hooks. Each entry references an accountHooks bundle by id; the
162
+ * bundle runs in the V8 isolate at the matching fire-points and its validated
163
+ * return is folded into mutable harness state.
164
+ */
165
+ code?: AgentCodeHookConfig[];
166
+ [key: string]: unknown;
167
+ }
168
+ interface AgentCodeHookConfig {
169
+ hookId: string;
170
+ /**
171
+ * Optional narrowing of the events this reference reacts to. Omitted => the
172
+ * bundle's own declared `events` set. Any listed event outside the bundle's
173
+ * declared set is ignored at runtime.
174
+ */
175
+ events?: AgentHookEventName[];
176
+ enabled?: boolean;
128
177
  [key: string]: unknown;
129
178
  }
130
179
  interface AgentWebhookHookConfig {
@@ -135,6 +184,8 @@ interface AgentWebhookHookConfig {
135
184
  [key: string]: unknown;
136
185
  }
137
186
  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";
187
+ type AgentChannelHookEventName = "channel.message.received" | "channel.message.sending";
188
+ type AgentHookEventName = AgentLifecycleEventName | AgentChannelHookEventName;
138
189
  type AgentToolsConfig = Record<string, AgentToolConfig>;
139
190
  interface AgentToolConfig {
140
191
  enabled?: boolean;
@@ -152,54 +203,83 @@ interface AgentChannelsConfig {
152
203
  zalo?: AgentZaloChannelConfig;
153
204
  [key: string]: unknown;
154
205
  }
155
- type AgentChannelStreamingMode = ChannelStreamMode | "off";
156
- interface AgentChannelStreamingConfig {
157
- mode?: AgentChannelStreamingMode;
158
- [key: string]: unknown;
159
- }
206
+ type AgentChannelWorkspaceScope = {
207
+ level: "channel";
208
+ alias?: never;
209
+ } | {
210
+ level: "conversation";
211
+ alias: string;
212
+ };
160
213
  interface AgentTelegramChannelConfig {
161
- botToken?: string;
162
- webhookSecret?: string;
214
+ id?: string;
215
+ apiUrl?: TelegramAdapterConfig["apiUrl"];
216
+ botToken?: TelegramAdapterConfig["botToken"];
217
+ webhookSecret?: TelegramAdapterConfig["secretToken"];
163
218
  allowedChatIds?: number[];
164
219
  reactionEmoji?: string;
165
- streaming?: AgentChannelStreamingConfig;
220
+ workspaceScope?: AgentChannelWorkspaceScope;
166
221
  [key: string]: unknown;
167
222
  }
168
223
  interface AgentGitHubChannelConfig {
169
- webhookSecret?: string;
170
- appId?: string;
171
- privateKey?: string;
224
+ id?: string;
225
+ apiUrl?: Extract<GitHubAdapterConfig, {
226
+ appId: string;
227
+ }>["apiUrl"];
228
+ webhookSecret?: Extract<GitHubAdapterConfig, {
229
+ appId: string;
230
+ }>["webhookSecret"];
231
+ appId?: Extract<GitHubAdapterConfig, {
232
+ appId: string;
233
+ }>["appId"];
234
+ privateKey?: Extract<GitHubAdapterConfig, {
235
+ appId: string;
236
+ }>["privateKey"];
172
237
  allowedRepos?: string[];
238
+ /** Bot username for @-mention detection (e.g. "my-bot" or "my-bot[bot]"). */
239
+ userName?: string;
240
+ /** Bot's numeric GitHub user ID for self-message detection. */
241
+ botUserId?: number;
242
+ /** 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. */
243
+ triggerOnIssueOpen?: boolean;
244
+ /** 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. */
245
+ triggerOnPROpen?: boolean;
246
+ workspaceScope?: AgentChannelWorkspaceScope;
173
247
  [key: string]: unknown;
174
248
  }
175
249
  interface AgentSlackChannelConfig {
250
+ id?: string;
251
+ apiUrl?: SlackAdapterConfig["apiUrl"];
176
252
  botToken?: string;
177
- signingSecret?: string;
253
+ signingSecret?: SlackAdapterConfig["signingSecret"];
178
254
  allowedChannelIds?: string[];
179
- streaming?: AgentChannelStreamingConfig;
255
+ reactionEmoji?: string;
256
+ workspaceScope?: AgentChannelWorkspaceScope;
180
257
  [key: string]: unknown;
181
258
  }
182
259
  interface AgentDiscordChannelConfig {
183
- botToken?: string;
184
- publicKey?: string;
260
+ id?: string;
261
+ apiUrl?: DiscordAdapterConfig["apiUrl"];
262
+ botToken?: DiscordAdapterConfig["botToken"];
263
+ publicKey?: DiscordAdapterConfig["publicKey"];
185
264
  allowedGuildIds?: string[];
186
- streaming?: AgentChannelStreamingConfig;
265
+ workspaceScope?: AgentChannelWorkspaceScope;
187
266
  [key: string]: unknown;
188
267
  }
189
268
  interface AgentPancakeChannelConfig {
269
+ id?: string;
190
270
  pageId?: string;
191
271
  pageAccessToken?: string;
192
272
  webhookSecret?: string;
193
273
  senderId?: string;
194
- options?: Record<string, unknown>;
195
- streaming?: AgentChannelStreamingConfig;
274
+ workspaceScope?: AgentChannelWorkspaceScope;
196
275
  [key: string]: unknown;
197
276
  }
198
277
  interface AgentZaloChannelConfig {
278
+ id?: string;
199
279
  botToken?: string;
200
280
  webhookSecret?: string;
201
281
  allowedUserIds?: string[];
202
- streaming?: AgentChannelStreamingConfig;
282
+ workspaceScope?: AgentChannelWorkspaceScope;
203
283
  [key: string]: unknown;
204
284
  }
205
285
 
@@ -248,6 +328,21 @@ type UpdateCronInput = {
248
328
  input?: never;
249
329
  });
250
330
 
331
+ /**
332
+ * Predefined sandbox sizes — the canonical (vcpu, memoryMb, storageGb) catalog
333
+ * shared by sandbox config validation, the workdir resource mapping, and the
334
+ * Convex `sandboxInstances` mirror. Sizes are the user-facing knob (`config.size`)
335
+ * that reconciles issue #78's tiers with each backend's real limits.
336
+ *
337
+ * The specs are canonical/advisory: workdir applies them as create-time resources
338
+ * (clamping vcpu to its allowed set); MicroVM bakes size into the image so the
339
+ * specs are display-only there; daytona/e2b/vercel size natively. The control-plane
340
+ * mirror type lives here too so the Convex writer and the executors share one shape
341
+ * without importing across the _shared/harness boundary.
342
+ */
343
+
344
+ type SandboxSize = "tiny" | "xsmall" | "small" | "medium" | "large";
345
+
251
346
  /**
252
347
  * Sandbox config: account-scoped, reusable sandbox definitions referenced by
253
348
  * agents via `config.sandbox`. A sandbox is a collection of Claude-Code-style
@@ -256,7 +351,8 @@ type UpdateCronInput = {
256
351
  * DynamoDB / Convex stores call these at their create/update entry points.
257
352
  * Stored encrypted at rest because `envVars`/`options` may hold secrets.
258
353
  */
259
- type SandboxProvider = "lambda" | "e2b" | "daytona" | "kubernetes" | "vercel";
354
+
355
+ type SandboxProvider = "sandbox" | "lambda" | "e2b" | "daytona" | "vercel";
260
356
  type SandboxRuntimeName = "bash" | "python" | "node";
261
357
  type SandboxPermissionMode = "edit" | "ask" | "bypass";
262
358
  type SandboxNetworkMode = "allow-all" | "deny-all" | "restricted";
@@ -271,11 +367,12 @@ interface SandboxNetworkConfig {
271
367
  }
272
368
  interface SandboxConfig {
273
369
  provider: SandboxProvider;
370
+ size?: SandboxSize;
371
+ snapshot?: string;
274
372
  runtimes?: SandboxRuntimeName[];
275
373
  network?: SandboxNetworkConfig;
276
374
  permissionMode?: SandboxPermissionMode;
277
375
  persistent?: boolean;
278
- ephemeralHome?: boolean;
279
376
  lifecycle?: SandboxLifecycleConfig;
280
377
  onCreate?: string[];
281
378
  onResume?: string[];
@@ -296,15 +393,127 @@ interface SandboxConfig {
296
393
  */
297
394
  declare const WORKSPACE_STORAGE_PROVIDERS: readonly ["s3"];
298
395
  type WorkspaceStorageProvider = (typeof WORKSPACE_STORAGE_PROVIDERS)[number];
396
+ type WorkspaceStorageAuth = {
397
+ type: "managed";
398
+ } | {
399
+ type: "assumeRole";
400
+ roleArn: string;
401
+ externalId?: string;
402
+ };
403
+ interface WorkspaceStorageConfig {
404
+ provider: WorkspaceStorageProvider;
405
+ bucket?: string;
406
+ region?: string;
407
+ endpoint?: string;
408
+ prefix?: string;
409
+ auth?: WorkspaceStorageAuth;
410
+ }
299
411
  interface WorkspaceConfig {
300
- storage: {
301
- provider: WorkspaceStorageProvider;
302
- };
412
+ storage: WorkspaceStorageConfig;
413
+ isolation?: boolean;
303
414
  harness?: {
304
415
  enabled?: boolean;
305
416
  };
306
417
  }
307
418
 
419
+ /**
420
+ * Telegram channel adapter implementated as a ChannelAdapter.
421
+ * Implements Telegram auth, message normalization, and reply actions through the Chat SDK Telegram adapter.
422
+ */
423
+
424
+ interface TelegramSource {
425
+ chatId: number;
426
+ messageId: string;
427
+ threadId: string;
428
+ fromUserId?: number;
429
+ fromUsername?: string;
430
+ }
431
+
432
+ /**
433
+ * GitHub channel adapter.
434
+ * Keep Broods-specific event filtering/source mapping here; delegate GitHub auth and API calls to Chat SDK.
435
+ */
436
+
437
+ interface GitHubSource {
438
+ owner: string;
439
+ repo: string;
440
+ installationId: number;
441
+ threadId: string;
442
+ messageId?: string;
443
+ issueNumber?: number;
444
+ pullNumber?: number;
445
+ commentId?: number;
446
+ target: "issue" | "issue_comment" | "pull_request" | "pull_request_review_comment";
447
+ }
448
+
449
+ /**
450
+ * Slack channel adapter.
451
+ * Handle request verification, inbound normalization, and Slack Web API reply actions here.
452
+ */
453
+
454
+ interface SlackSource {
455
+ teamId: string;
456
+ channelId: string;
457
+ threadTs?: string;
458
+ messageTs?: string;
459
+ responseUrl?: string;
460
+ commandToken?: string;
461
+ userId?: string;
462
+ }
463
+
464
+ /**
465
+ * Discord channel adapter.
466
+ * Verify interaction signatures, normalize slash commands, and send replies through Chat SDK's Discord adapter.
467
+ */
468
+
469
+ interface DiscordSource {
470
+ applicationId: string;
471
+ interactionToken?: string;
472
+ interactionId?: string;
473
+ guildId?: string;
474
+ channelId?: string;
475
+ threadId?: string;
476
+ messageId?: string;
477
+ commandToken?: string;
478
+ userId?: string;
479
+ }
480
+
481
+ /**
482
+ * Pancake channel adapter.
483
+ * Keep Pancake webhook normalization and outbound message API calls here.
484
+ *
485
+ * Per-conversation policy (e.g. skipping human-owned conversations by tag) is
486
+ * not baked in: the parsed source carries `tagIds` so a user `onMessageReceived`
487
+ * hook can decide to drop the message.
488
+ */
489
+
490
+ interface PancakeSource {
491
+ pageId: string;
492
+ conversationId: string;
493
+ messageId: string;
494
+ messageType: "INBOX" | "COMMENT";
495
+ postId?: string;
496
+ fromId?: string;
497
+ fromName?: string;
498
+ pageCustomerId?: string;
499
+ tagIds?: string[];
500
+ }
501
+
502
+ /**
503
+ * Zalo channel adapter.
504
+ * Keep official Zalo Bot API webhook normalization and outbound API calls here.
505
+ */
506
+
507
+ interface ZaloSource {
508
+ chatId: string;
509
+ chatType: "PRIVATE";
510
+ messageId: string;
511
+ senderId: string;
512
+ senderName?: string;
513
+ eventName: string;
514
+ date?: number;
515
+ }
516
+
308
517
  /**
309
518
  * Canonical CLI manifest wire types — the single source of truth shared by the
310
519
  * backend (cliSync.ts, cliHttp.ts) and the SDK/CLI (packages/broods).
@@ -313,7 +522,7 @@ interface WorkspaceConfig {
313
522
  * import it without pulling the Convex server module graph into its typecheck.
314
523
  */
315
524
  type CliManifestResource = {
316
- kind: "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool";
525
+ kind: "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "hook" | "policy";
317
526
  name: string;
318
527
  description?: string;
319
528
  config: unknown;
@@ -325,6 +534,8 @@ type GeneratedIds = {
325
534
  crons: Record<string, string>;
326
535
  skills: Record<string, string>;
327
536
  tools: Record<string, string>;
537
+ hooks: Record<string, string>;
538
+ policies: Record<string, string>;
328
539
  };
329
540
  type CliManifest = {
330
541
  version: 1;
@@ -351,12 +562,12 @@ type AgentConfigDoc = Doc<"agentConfigs">;
351
562
  type WorkspaceConfigDoc = Doc<"workspaceConfigs">;
352
563
  type SandboxConfigDoc = Doc<"sandboxConfigs">;
353
564
  type CronDoc = Doc<"crons">;
354
- type CliResourceKind = "agent" | "workspace" | "sandbox" | "cron";
565
+ type CliResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy";
355
566
 
356
567
  /**
357
568
  * Wire types for the public account-manage and harness APIs. These mirror
358
569
  * the deployed API contract (docs/api-reference/openapi.yaml is the source
359
- * of truth); they are intentionally independent of the Lambda internals so
570
+ * of truth); they are intentionally independent of the runtime internals so
360
571
  * the SDK keeps working when the runtime is ported.
361
572
  */
362
573
 
@@ -443,10 +654,11 @@ interface CustomTool {
443
654
  name: string;
444
655
  description: string;
445
656
  sha256: string;
657
+ runtime?: "isolate" | "sandbox";
446
658
  }
447
659
 
448
660
  type StreamTextOptions = Parameters<typeof streamText>[0];
449
- type JsonCallSettings = Partial<Omit<CallSettings, "abortSignal" | "headers">>;
661
+ type JsonCallSettings = Partial<LanguageModelCallOptions & Pick<RequestOptions, "maxRetries" | "timeout">>;
450
662
  type AgentRunModelOverrides = JsonCallSettings & Pick<StreamTextOptions, "providerOptions">;
451
663
  type AgentRunOverrides = {
452
664
  system?: SystemModelMessage | SystemModelMessage[];
@@ -542,6 +754,7 @@ type ObservabilitySubscribeMessage = {
542
754
  type: "subscribe";
543
755
  stream: "logs" | "traces";
544
756
  backfill?: number;
757
+ liveOnly?: boolean;
545
758
  minLevel?: LogLevel;
546
759
  };
547
760
  type ObservabilityUnsubscribeMessage = {
@@ -636,6 +849,7 @@ interface ResourceApi {
636
849
  readonly crons?: Record<string, unknown>;
637
850
  readonly skills?: Record<string, unknown>;
638
851
  readonly tools?: Record<string, unknown>;
852
+ readonly policies?: Record<string, unknown>;
639
853
  }
640
854
  interface BroodsClientOptions {
641
855
  /**
@@ -816,12 +1030,14 @@ interface BroodsProjectConfig {
816
1030
  [name: string]: string | undefined;
817
1031
  };
818
1032
  dashboardUrl?: string;
1033
+ /** Convex control-plane base URL for sync/env calls; defaults to the URL discovered at login. */
1034
+ baseUrl?: string;
819
1035
  }
820
1036
  interface BroodsConfigDefinition {
821
1037
  readonly [CONFIG_MARKER]: true;
822
1038
  readonly config: BroodsProjectConfig;
823
1039
  }
824
- type ResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool";
1040
+ type ResourceKind = "agent" | "workspace" | "sandbox" | "cron" | "skill" | "tool" | "policy";
825
1041
  interface ResourceDefinition<Kind extends ResourceKind, Name extends string, Config> {
826
1042
  readonly [RESOURCE_MARKER]: true;
827
1043
  readonly kind: Kind;
@@ -858,65 +1074,45 @@ interface ToolDefinitionConfig {
858
1074
  path: string;
859
1075
  description: string;
860
1076
  inputSchema: Record<string, unknown>;
1077
+ runtime?: "isolate" | "sandbox";
861
1078
  defaultConfig?: Record<string, unknown>;
862
1079
  }
1080
+ type PolicyDefinitionConfig = Omit<AgentPolicyDocument, "version"> & {
1081
+ version?: AgentPolicyDocument["version"];
1082
+ };
863
1083
  type ChannelType = "telegram" | "github" | "slack" | "discord" | "pancake" | "zalo";
864
1084
  interface ChannelDefinition<Type extends ChannelType, Config> {
865
1085
  readonly [CHANNEL_MARKER]: true;
866
1086
  readonly kind: "channel";
867
1087
  readonly type: Type;
1088
+ readonly workspaceScope?: AgentChannelWorkspaceScope;
868
1089
  readonly config: Config;
869
1090
  }
1091
+ type RequiredChannelKeys<Config, Keys extends keyof Config> = Required<Pick<Config, Keys>> & Omit<Config, Keys>;
870
1092
  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 {
1093
+ type ChannelIdentityInput = {
1094
+ workspaceScope?: AgentChannelWorkspaceScope;
1095
+ };
1096
+ type TelegramChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentTelegramChannelConfig, "apiUrl" | "botToken" | "webhookSecret" | "allowedChatIds" | "reactionEmoji">, "botToken" | "webhookSecret" | "allowedChatIds">> & ChannelIdentityInput;
1097
+ type GitHubChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentGitHubChannelConfig, "apiUrl" | "webhookSecret" | "appId" | "privateKey" | "allowedRepos" | "userName" | "botUserId" | "triggerOnIssueOpen" | "triggerOnPROpen">, "webhookSecret" | "appId" | "privateKey">> & ChannelIdentityInput;
1098
+ type SlackChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentSlackChannelConfig, "apiUrl" | "botToken" | "signingSecret" | "allowedChannelIds" | "reactionEmoji">, "botToken" | "signingSecret">> & ChannelIdentityInput;
1099
+ type DiscordChannelInput = EnvRefString<RequiredChannelKeys<Pick<AgentDiscordChannelConfig, "apiUrl" | "botToken" | "publicKey" | "allowedGuildIds">, "botToken" | "publicKey">> & ChannelIdentityInput;
1100
+ interface PancakeChannelInput extends ChannelIdentityInput {
897
1101
  pageId: ChannelSecret;
898
1102
  pageAccessToken: ChannelSecret;
899
1103
  webhookSecret: ChannelSecret;
900
1104
  senderId?: string | EnvRef;
901
- ignoreTagIds?: readonly (string | EnvRef)[];
902
- streaming?: EnvRefString<NonNullable<AgentPancakeChannelConfig["streaming"]>>;
903
1105
  }
904
- type PancakeChannelDefinitionConfig = Omit<PancakeChannelInput, "ignoreTagIds"> & {
905
- options?: {
906
- ignoreTagIds?: readonly (string | EnvRef)[];
907
- };
908
- };
909
- interface ZaloChannelInput {
1106
+ interface ZaloChannelInput extends ChannelIdentityInput {
910
1107
  botToken: ChannelSecret;
911
1108
  webhookSecret: ChannelSecret;
912
1109
  allowedUserIds: readonly (string | EnvRef)[];
913
- streaming?: EnvRefString<NonNullable<AgentZaloChannelConfig["streaming"]>>;
914
1110
  }
915
1111
  type TelegramChannelDefinition = ChannelDefinition<"telegram", TelegramChannelInput>;
916
1112
  type GitHubChannelDefinition = ChannelDefinition<"github", GitHubChannelInput>;
917
1113
  type SlackChannelDefinition = ChannelDefinition<"slack", SlackChannelInput>;
918
1114
  type DiscordChannelDefinition = ChannelDefinition<"discord", DiscordChannelInput>;
919
- type PancakeChannelDefinition = ChannelDefinition<"pancake", PancakeChannelDefinitionConfig>;
1115
+ type PancakeChannelDefinition = ChannelDefinition<"pancake", PancakeChannelInput>;
920
1116
  type ZaloChannelDefinition = ChannelDefinition<"zalo", ZaloChannelInput>;
921
1117
  type AnyChannelDefinition = TelegramChannelDefinition | GitHubChannelDefinition | SlackChannelDefinition | DiscordChannelDefinition | PancakeChannelDefinition | ZaloChannelDefinition;
922
1118
  /**
@@ -941,6 +1137,133 @@ type AgentSubagentDefinitionConfig = Omit<NonNullable<AgentConfig["subagent"]>,
941
1137
  type AgentSkillsDefinitionConfig = Omit<NonNullable<AgentConfig["skills"]>, "allowed"> & {
942
1138
  allowed?: readonly (SkillResource | string)[];
943
1139
  };
1140
+ interface HookContext {
1141
+ fetch: typeof fetch;
1142
+ config: Record<string, unknown>;
1143
+ /**
1144
+ * Mutable per-request scratchpad shared across this agent request's hooks.
1145
+ * Seed it in an early hook (e.g. `onStart`) and read or modify it later —
1146
+ * every loop hook, `onSubagentFinish`, and the reply's `onMessageSending`
1147
+ * see the same state. Keep it JSON-serializable. `onMessageReceived`,
1148
+ * delayed background replies, and each subagent's own run get fresh state.
1149
+ */
1150
+ state: Record<string, unknown>;
1151
+ }
1152
+ type Handler<Event, Result> = (ctx: HookContext, event: Event) => Result | void | Promise<Result | void>;
1153
+ /**
1154
+ * Channel-specific routing data attached to an inbound message. Inherited from
1155
+ * the core channel adapters (via contracts.ts) so an `onMessageReceived` hook
1156
+ * that narrows on `event.channel` always sees exactly what core emits (e.g.
1157
+ * Pancake `tagIds`).
1158
+ */
1159
+ type TelegramMessageSource = TelegramSource;
1160
+ type GitHubMessageSource = GitHubSource;
1161
+ type SlackMessageSource = SlackSource;
1162
+ type DiscordMessageSource = DiscordSource;
1163
+ type PancakeMessageSource = PancakeSource;
1164
+ type ZaloMessageSource = ZaloSource;
1165
+ /**
1166
+ * Inbound channel message passed to `onMessageReceived`, discriminated on
1167
+ * `channel` so each variant exposes its channel's strongly-typed `source`.
1168
+ */
1169
+ type ChannelMessageReceived = {
1170
+ channel: "telegram";
1171
+ text: string;
1172
+ source: TelegramMessageSource;
1173
+ } | {
1174
+ channel: "github";
1175
+ text: string;
1176
+ source: GitHubMessageSource;
1177
+ } | {
1178
+ channel: "slack";
1179
+ text: string;
1180
+ source: SlackMessageSource;
1181
+ } | {
1182
+ channel: "discord";
1183
+ text: string;
1184
+ source: DiscordMessageSource;
1185
+ } | {
1186
+ channel: "pancake";
1187
+ text: string;
1188
+ source: PancakeMessageSource;
1189
+ } | {
1190
+ channel: "zalo";
1191
+ text: string;
1192
+ source: ZaloMessageSource;
1193
+ };
1194
+ /**
1195
+ * Inline agent hook callbacks. Handlers are serialized with `.toString()`,
1196
+ * bundled into one account hook, and run in a fresh V8 isolate. Keep them
1197
+ * self-contained: use only `ctx`, `event`, and JavaScript globals. Do not rely
1198
+ * on imports or closure variables. Arrow functions and function expressions are
1199
+ * preferred so the serialized source is valid as an object-literal value.
1200
+ *
1201
+ * Subagent runs fire hooks too: a registered subagent runs its own hooks, a
1202
+ * prompt-only (virtual) subagent inherits this bundle — always with fresh
1203
+ * `ctx.state`. `onSubagentFinish` fires on the parent with the parent's state.
1204
+ */
1205
+ interface AgentHooks {
1206
+ onStart?: Handler<{
1207
+ system: string;
1208
+ messages: unknown[];
1209
+ }, {
1210
+ system?: string;
1211
+ messages?: unknown[];
1212
+ }>;
1213
+ onStepFinish?: Handler<{
1214
+ stepNumber: number;
1215
+ finishReason: string;
1216
+ toolCallCount: number;
1217
+ }, void>;
1218
+ onToolCall?: Handler<{
1219
+ toolName: string;
1220
+ input: unknown;
1221
+ }, {
1222
+ decision?: "allow" | "deny";
1223
+ args?: Record<string, unknown>;
1224
+ denyReason?: string;
1225
+ }>;
1226
+ onToolResult?: Handler<{
1227
+ toolName: string;
1228
+ output: unknown;
1229
+ }, {
1230
+ output?: unknown;
1231
+ }>;
1232
+ onFinish?: Handler<{
1233
+ finishReason: string;
1234
+ response: unknown;
1235
+ }, {
1236
+ output?: unknown;
1237
+ }>;
1238
+ onApproval?: Handler<{
1239
+ approvals: unknown;
1240
+ }, {
1241
+ approve?: boolean;
1242
+ }>;
1243
+ onError?: Handler<{
1244
+ error: string;
1245
+ }, void>;
1246
+ onSubagentFinish?: Handler<{
1247
+ taskId: string;
1248
+ result: unknown;
1249
+ }, {
1250
+ visibleResult?: unknown;
1251
+ }>;
1252
+ onMessageReceived?: Handler<ChannelMessageReceived, {
1253
+ drop?: boolean;
1254
+ text?: string;
1255
+ }>;
1256
+ onMessageSending?: Handler<{
1257
+ channel: ChannelType;
1258
+ text: string;
1259
+ }, {
1260
+ drop?: boolean;
1261
+ text?: string;
1262
+ }>;
1263
+ }
1264
+ type AgentPolicyDefinitionConfig = Omit<AgentPolicyConfig, "policyIds"> & {
1265
+ policies?: readonly (PolicyResource | string)[];
1266
+ };
944
1267
  /**
945
1268
  * Code-first agent config surface. Built from an explicit `Pick` of `AgentConfig`
946
1269
  * (not `Omit`) so the SDK input type does NOT inherit `AgentConfig`'s
@@ -949,12 +1272,16 @@ type AgentSkillsDefinitionConfig = Omit<NonNullable<AgentConfig["skills"]>, "all
949
1272
  * `workspace:` instead of `workspaces:`. Add a key here when core's `AgentConfig`
950
1273
  * gains a new top-level field that should be code-definable.
951
1274
  */
952
- type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" | "provider" | "session" | "hooks" | "tools">> & {
1275
+ type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" | "provider" | "session" | "tools">> & {
1276
+ hooks?: AgentHooks & {
1277
+ webhooks?: readonly EnvRefString<AgentWebhookHookConfig>[];
1278
+ };
953
1279
  channels?: readonly AnyChannelDefinition[];
954
1280
  sandbox?: SandboxResource | string;
955
1281
  workspaces?: readonly AgentWorkspaceInput[];
956
1282
  subagent?: AgentSubagentDefinitionConfig;
957
1283
  skills?: AgentSkillsDefinitionConfig;
1284
+ policy?: AgentPolicyDefinitionConfig;
958
1285
  /**
959
1286
  * Opt the agent into the public runtime endpoint (SSE/WebSocket via the
960
1287
  * environment runtime key). Off by default — secured: when unset the public
@@ -971,8 +1298,9 @@ type WorkspaceResource<Name extends string = string> = ResourceDefinition<"works
971
1298
  type SandboxResource<Name extends string = string> = ResourceDefinition<"sandbox", Name, SandboxDefinitionConfig>;
972
1299
  type SkillResource<Name extends string = string> = ResourceDefinition<"skill", Name, SkillDefinitionConfig>;
973
1300
  type ToolResource<Name extends string = string> = ResourceDefinition<"tool", Name, ToolDefinitionConfig>;
1301
+ type PolicyResource<Name extends string = string> = ResourceDefinition<"policy", Name, PolicyDefinitionConfig>;
974
1302
  type CronResource<Name extends string = string> = ResourceDefinition<"cron", Name, CronDefinitionConfig>;
975
- type AnyResource = AgentResource | WorkspaceResource | SandboxResource | CronResource | SkillResource | ToolResource;
1303
+ type AnyResource = AgentResource | WorkspaceResource | SandboxResource | CronResource | SkillResource | ToolResource | PolicyResource;
976
1304
  /**
977
1305
  * References an account/environment variable resolved on the SERVER at runtime —
978
1306
  * set it with `broods env set <NAME>` or in the dashboard (the Convex-style
@@ -999,10 +1327,11 @@ declare function defineWorkspace<const Name extends string>(input: ResourceDefin
999
1327
  declare function defineSandbox<const Name extends string>(input: ResourceDefinitionInput<Name, SandboxDefinitionConfig>): SandboxResource<Name>;
1000
1328
  declare function defineSkill<const Name extends string>(input: ResourceDefinitionInput<Name, SkillDefinitionConfig>): SkillResource<Name>;
1001
1329
  declare function defineTool<const Name extends string>(input: ResourceDefinitionInput<Name, ToolDefinitionConfig>): ToolResource<Name>;
1330
+ declare function definePolicy<const Name extends string>(input: ResourceDefinitionInput<Name, PolicyDefinitionConfig>): PolicyResource<Name>;
1002
1331
  declare function defineCron<const Name extends string>(input: ResourceDefinitionInput<Name, CronDefinitionConfig>): CronResource<Name>;
1003
1332
  declare function isResource(value: unknown): value is AnyResource;
1004
1333
  declare function isChannelDefinition(value: unknown): value is AnyChannelDefinition;
1005
1334
  declare function isBroodsConfig(value: unknown): value is BroodsConfigDefinition;
1006
1335
 
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 };
1336
+ 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 };
1337
+ export type { Account, Agent, AgentChannelWorkspaceScope, AgentChannelsConfig, AgentCodeHookConfig, AgentConfig, AgentConfigDoc, AgentDefinitionConfig, AgentDiscordChannelConfig, AgentGitHubChannelConfig, AgentHandle, AgentHookEventName, AgentHooks, AgentHooksConfig, AgentPancakeChannelConfig, AgentPolicyConfig, AgentPolicyDefinitionConfig, AgentPolicyDocument, 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, 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 };