broods 0.3.0 → 0.4.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/README.md CHANGED
@@ -41,8 +41,8 @@ writes `BROODS_API_KEY` to `.env.local`; the SDK also accepts `apiKey`,
41
41
  ## Two ways to configure agents
42
42
 
43
43
  **Config-first (`broods dev` / `broods deploy`).** Resources declared in your
44
- `broods/` folder with `defineAgent`, `defineWorkspace`, etc. are *predefined
45
- configs*: the CLI syncs them to your project on deploy and codegen gives you
44
+ `broods/` folder with `defineAgent`, `defineWorkspace`, etc. are _predefined
45
+ configs_: the CLI syncs them to your project on deploy and codegen gives you
46
46
  typed references. Use this when the set of agents is fixed and versioned with
47
47
  your code.
48
48
 
@@ -59,7 +59,7 @@ such as Convex actions and Cloudflare Workers where the main SDK entry (which
59
59
  reads `.env` files from disk) cannot load:
60
60
 
61
61
  ```ts
62
- import { BroodsAccountClient } from "broods/account";
62
+ import { BroodsAccountClient, envPlaceholder } from "broods/account";
63
63
 
64
64
  // baseUrl defaults to https://gateway.broods.app (override with BROODS_BASE_URL);
65
65
  // the secret falls back to BROODS_ACCOUNT_SECRET from the runtime's environment.
@@ -76,6 +76,13 @@ const created = await account.createAgent({
76
76
  publicAccess: true,
77
77
  },
78
78
  });
79
+
80
+ // Store an account secret once, then reference it from any dynamic agent.
81
+ // Reads list only names/timestamps; secret values are never returned.
82
+ await account.setEnvVar("OVH_API_KEY", process.env.OVH_API_KEY!);
83
+ await account.updateAgent(created.agentId, {
84
+ config: { provider: { custom: { apiKey: envPlaceholder("OVH_API_KEY") } } },
85
+ });
79
86
  await account.updateAgent(created.agentId, {
80
87
  config: { channels: { slack: { id: "conn-1", botToken: "xoxb-…" } } },
81
88
  });
@@ -96,8 +103,16 @@ const sandbox = await account.createSandbox({
96
103
  name: "reserved",
97
104
  config: { provider: "lambda", persistent: true, permissionMode: "ask" },
98
105
  });
99
- await account.uploadWorkspaceFile("ws_1", { path: "memory/seed.md", contentBase64: "IyBTZWVk" });
100
- await account.createSkill({ source: "json", name: "triage", description: "Triage flow", content: "# Triage" });
106
+ await account.uploadWorkspaceFile("ws_1", {
107
+ path: "memory/seed.md",
108
+ contentBase64: "IyBTZWVk",
109
+ });
110
+ await account.createSkill({
111
+ source: "json",
112
+ name: "triage",
113
+ description: "Triage flow",
114
+ content: "# Triage",
115
+ });
101
116
  const runs = await account.listCronRuns("cron_1", { limit: 20 });
102
117
 
103
118
  // Persistent sandbox lifecycle is driven by reservationKey.
package/dist/account.d.ts CHANGED
@@ -1,8 +1,19 @@
1
1
  import { SystemModelMessage, LanguageModelCallOptions, RequestOptions, streamText, JSONSchema7, ModelMessage } 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
+
3
+ /**
4
+ * Supported account model provider names.
5
+ * Keep provider identifiers here so config validation and model resolution share one source.
6
+ */
7
+ declare const ACCOUNT_MODEL_PROVIDERS: {
8
+ readonly google: true;
9
+ readonly openai: true;
10
+ readonly anthropic: true;
11
+ readonly bedrock: true;
12
+ readonly gateway: true;
13
+ readonly minimax: true;
14
+ readonly custom: true;
15
+ };
16
+ type AccountModelProviderName = keyof typeof ACCOUNT_MODEL_PROVIDERS;
6
17
 
7
18
  /**
8
19
  * Agent policy contracts and validation.
@@ -43,21 +54,6 @@ interface AgentPolicyConfig {
43
54
  mode?: AgentPolicyMode;
44
55
  }
45
56
 
46
- /**
47
- * Supported account model provider names.
48
- * Keep provider identifiers here so config validation and model resolution share one source.
49
- */
50
- declare const ACCOUNT_MODEL_PROVIDERS: {
51
- readonly google: true;
52
- readonly openai: true;
53
- readonly anthropic: true;
54
- readonly bedrock: true;
55
- readonly gateway: true;
56
- readonly minimax: true;
57
- readonly custom: true;
58
- };
59
- type AccountModelProviderName = keyof typeof ACCOUNT_MODEL_PROVIDERS;
60
-
61
57
  /**
62
58
  * Agent configuration: types for the per-agent settings object, input
63
59
  * normalization, encryption helpers, patch-merge, and redaction.
@@ -232,9 +228,9 @@ type AgentChannelWorkspaceScope = {
232
228
  };
233
229
  interface AgentTelegramChannelConfig {
234
230
  id?: string;
235
- apiUrl?: TelegramAdapterConfig["apiUrl"];
236
- botToken?: TelegramAdapterConfig["botToken"];
237
- webhookSecret?: TelegramAdapterConfig["secretToken"];
231
+ apiUrl?: string;
232
+ botToken?: string;
233
+ webhookSecret?: string;
238
234
  allowedChatIds?: number[];
239
235
  reactionEmoji?: string;
240
236
  workspaceScope?: AgentChannelWorkspaceScope;
@@ -242,18 +238,10 @@ interface AgentTelegramChannelConfig {
242
238
  }
243
239
  interface AgentGitHubChannelConfig {
244
240
  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"];
241
+ apiUrl?: string;
242
+ webhookSecret?: string;
243
+ appId?: string;
244
+ privateKey?: string;
257
245
  allowedRepos?: string[];
258
246
  /** Bot username for @-mention detection (e.g. "my-bot" or "my-bot[bot]"). */
259
247
  userName?: string;
@@ -268,9 +256,9 @@ interface AgentGitHubChannelConfig {
268
256
  }
269
257
  interface AgentSlackChannelConfig {
270
258
  id?: string;
271
- apiUrl?: SlackAdapterConfig["apiUrl"];
259
+ apiUrl?: string;
272
260
  botToken?: string;
273
- signingSecret?: SlackAdapterConfig["signingSecret"];
261
+ signingSecret?: string;
274
262
  allowedChannelIds?: string[];
275
263
  reactionEmoji?: string;
276
264
  workspaceScope?: AgentChannelWorkspaceScope;
@@ -278,9 +266,9 @@ interface AgentSlackChannelConfig {
278
266
  }
279
267
  interface AgentDiscordChannelConfig {
280
268
  id?: string;
281
- apiUrl?: DiscordAdapterConfig["apiUrl"];
282
- botToken?: DiscordAdapterConfig["botToken"];
283
- publicKey?: DiscordAdapterConfig["publicKey"];
269
+ apiUrl?: string;
270
+ botToken?: string;
271
+ publicKey?: string;
284
272
  allowedGuildIds?: string[];
285
273
  workspaceScope?: AgentChannelWorkspaceScope;
286
274
  [key: string]: unknown;
@@ -304,10 +292,7 @@ interface AgentZaloChannelConfig {
304
292
  }
305
293
 
306
294
  /**
307
- * Cron-job types, input normalization, and patch-merge helpers.
308
- * Provider-agnostic — both the DynamoDB and Convex stores import the
309
- * normalizer at their create/update entry points so behaviour is
310
- * symmetric across modes.
295
+ * Cron-job records, input normalization, and patch-merge helpers.
311
296
  */
312
297
 
313
298
  type CronStatus = "active" | "paused";
@@ -366,9 +351,8 @@ type SandboxSize = "tiny" | "xsmall" | "small" | "medium" | "large";
366
351
  /**
367
352
  * Sandbox config: account-scoped, reusable sandbox definitions referenced by
368
353
  * agents via `config.sandbox`. A sandbox is a collection of Claude-Code-style
369
- * tools (bash/read/write/edit/glob/grep) backed by a provider. Validation +
370
- * the public projection live here; the
371
- * DynamoDB / Convex stores call these at their create/update entry points.
354
+ * tools (bash/read/write/edit/glob/grep) backed by a provider. Validation and
355
+ * the public projection live here.
372
356
  * Stored encrypted at rest because `envVars`/`options` may hold secrets.
373
357
  */
374
358
 
@@ -408,8 +392,8 @@ interface SandboxConfig {
408
392
  * agents via `config.workspaces[].workspaceId`. A workspace is the persistent
409
393
  * S3-backed filesystem mounted into a sandbox; agents referencing the same
410
394
  * workspaceId share the same files. Holds no secrets, so it is stored in
411
- * plaintext (unlike sandbox config). Validation + the public projection live
412
- * here; the DynamoDB / Convex stores call these at create/update.
395
+ * plaintext (unlike sandbox config). Validation and the public projection live
396
+ * here.
413
397
  */
414
398
  declare const WORKSPACE_STORAGE_PROVIDERS: readonly ["s3"];
415
399
  type WorkspaceStorageProvider = (typeof WORKSPACE_STORAGE_PROVIDERS)[number];
@@ -428,12 +412,18 @@ interface WorkspaceStorageConfig {
428
412
  prefix?: string;
429
413
  auth?: WorkspaceStorageAuth;
430
414
  }
415
+ interface WorkspaceHarnessConfig {
416
+ workspace?: {
417
+ enabled?: boolean;
418
+ };
419
+ memory?: {
420
+ enabled?: boolean;
421
+ };
422
+ }
431
423
  interface WorkspaceConfig {
432
424
  storage: WorkspaceStorageConfig;
433
425
  isolation?: boolean;
434
- harness?: {
435
- enabled?: boolean;
436
- };
426
+ harness?: WorkspaceHarnessConfig;
437
427
  }
438
428
 
439
429
  /**
@@ -534,6 +524,13 @@ interface CreateAgentResult {
534
524
  name: string;
535
525
  description?: string;
536
526
  }
527
+ /** Write-only account environment variable metadata. */
528
+ interface AccountEnvVar {
529
+ name: string;
530
+ updatedAt: number;
531
+ }
532
+ /** Build a validated account env-var reference for use in an agent config. */
533
+ declare function envPlaceholder(name: string): string;
537
534
  /** Fields accepted by `PATCH /v1/agents/{id}`. `config` is deep-merged; `null` values delete keys. */
538
535
  interface UpdateAgentInput {
539
536
  name?: string;
@@ -701,6 +698,12 @@ declare class BroodsAccountClient {
701
698
  /** PATCH an agent. `config` deep-merges into the stored config; `null` leaves delete keys. Returns null when the agent is gone. */
702
699
  updateAgent(agentId: string, patch: UpdateAgentInput): Promise<AccountAgent | null>;
703
700
  deleteAgent(agentId: string): Promise<boolean>;
701
+ /** List account environment variable names and update timestamps; values are never returned. */
702
+ listEnvVars(): Promise<AccountEnvVar[]>;
703
+ /** Create or replace one write-only account environment variable. */
704
+ setEnvVar(name: string, value: string): Promise<void>;
705
+ /** Delete one account environment variable. Returns false when it is already absent. */
706
+ deleteEnvVar(name: string): Promise<boolean>;
704
707
  listCrons(): Promise<Cron[]>;
705
708
  createCron(input: CreateCronInput): Promise<Cron>;
706
709
  getCron(cronId: string): Promise<Cron | null>;
@@ -795,5 +798,5 @@ declare class BroodsAccountClient {
795
798
  private request;
796
799
  }
797
800
 
798
- export { BroodsAccountApiError, BroodsAccountClient };
799
- export type { AccountAgent, AccountPolicy, AccountSandbox, AccountTool, AccountWorkspace, BroodsAccount, BroodsAccountClientOptions, CreateAgentResult, CreateToolInput, DeleteAccountResult, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, UpdateAgentInput, UpdateToolInput, WorkspaceFileEntry };
801
+ export { BroodsAccountApiError, BroodsAccountClient, envPlaceholder };
802
+ export type { AccountAgent, AccountEnvVar, AccountPolicy, AccountSandbox, AccountTool, AccountWorkspace, BroodsAccount, BroodsAccountClientOptions, CreateAgentResult, CreateToolInput, DeleteAccountResult, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, UpdateAgentInput, UpdateToolInput, WorkspaceFileEntry };
package/dist/account.js CHANGED
@@ -1,6 +1,13 @@
1
1
  // @bun
2
2
  // src/account.ts
3
3
  var DEFAULT_ACCOUNT_BASE_URL = "https://gateway.broods.app";
4
+ var ACCOUNT_ENV_VAR_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
5
+ function envPlaceholder(name) {
6
+ if (!ACCOUNT_ENV_VAR_NAME_PATTERN.test(name) || name.length > 64) {
7
+ throw new Error("envPlaceholder name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");
8
+ }
9
+ return `\${${name}}`;
10
+ }
4
11
 
5
12
  class BroodsAccountApiError extends Error {
6
13
  status;
@@ -73,6 +80,17 @@ class BroodsAccountClient {
73
80
  const result = await this.request("DELETE", `/v1/agents/${encodeURIComponent(agentId)}`);
74
81
  return result?.deleted ?? false;
75
82
  }
83
+ async listEnvVars() {
84
+ const result = await this.request("GET", "/v1/env");
85
+ return result?.env ?? [];
86
+ }
87
+ async setEnvVar(name, value) {
88
+ await this.request("PUT", `/v1/env/${encodeURIComponent(name)}`, { value });
89
+ }
90
+ async deleteEnvVar(name) {
91
+ const result = await this.request("DELETE", `/v1/env/${encodeURIComponent(name)}`);
92
+ return result?.deleted ?? false;
93
+ }
76
94
  async listCrons() {
77
95
  const result = await this.request("GET", "/v1/crons");
78
96
  return result?.crons ?? [];
@@ -266,6 +284,7 @@ class BroodsAccountClient {
266
284
  }
267
285
  }
268
286
  export {
287
+ envPlaceholder,
269
288
  BroodsAccountClient,
270
289
  BroodsAccountApiError
271
290
  };