anyclaude-sdk 0.10.1 → 0.11.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
@@ -214,9 +214,29 @@ query({ prompt, workspace, llm, team: true, mailbox })
214
214
  // messages sent by one worker land in the addressed agent's inbox in another.
215
215
  ```
216
216
 
217
- Uses the global `BroadcastChannel` by default; pass `{ channel }` (e.g. the
218
- [`broadcast-channel`](https://www.npmjs.com/package/broadcast-channel) package)
219
- for cross-tab durability or older runtimes.
217
+ Uses the global `BroadcastChannel` by default. For durable cross-tab delivery
218
+ (IndexedDB/localStorage fallbacks, older browsers, Node) use the one-call helper
219
+ it's backed by the bundled [`broadcast-channel`](https://www.npmjs.com/package/broadcast-channel)
220
+ package, lazy-imported so it stays out of bundles that don't use it:
221
+
222
+ ```typescript
223
+ const mailbox = await BroadcastChannelMailbox.crossTab({ channelName: 'team', origin: 'planner' })
224
+ query({ prompt, workspace, llm, team: true, mailbox })
225
+ ```
226
+
227
+ **Push delivery to a running agent.** Messages addressed to an agent are
228
+ auto-injected into its transcript at the next turn boundary — same model as the
229
+ message queue, but from the shared mailbox. So a coordinator (or peer, or another
230
+ worker) can redirect a **running** sub-agent mid-task and it lands on the
231
+ sub-agent's next tool round, no polling tool needed. `dispatch_tasks` names each
232
+ worker `worker:<taskId>` so you can target a specific one:
233
+
234
+ ```typescript
235
+ mailbox.send('coordinator', 'worker:task_1', 'while you work: also add logging')
236
+ // worker:task_1 sees "[Team messages] - from coordinator: ..." on its next step.
237
+ ```
238
+
239
+ On by default with `team: true`; opt out via `query({ deliverTeamMessages: false })`.
220
240
 
221
241
  ## Pluggable backends
222
242
 
package/dist/agent.d.ts CHANGED
@@ -119,6 +119,11 @@ export interface AgentOptions {
119
119
  board?: TaskBoard;
120
120
  /** This agent's name/label for messaging (default 'coordinator'). */
121
121
  agentName?: string;
122
+ /** Auto-deliver unread mailbox messages addressed to this agent into the
123
+ * transcript at each turn boundary (push delivery, like the message queue).
124
+ * Default true when `team` is enabled. Set false to require an explicit
125
+ * pull instead. */
126
+ deliverTeamMessages?: boolean;
122
127
  /** Persist the transcript to this store (keyed by sessionId) for resume. */
123
128
  sessionStore?: SessionStoreLike;
124
129
  /** Load the stored transcript for sessionId before the first turn. */
package/dist/agent.js CHANGED
@@ -212,6 +212,7 @@ export async function* runAgent(options) {
212
212
  const mailbox = teamEnabled ? options.mailbox ?? new Mailbox() : undefined;
213
213
  const board = teamEnabled ? options.board ?? new TaskBoard() : undefined;
214
214
  const agentName = options.agentName ?? 'coordinator';
215
+ const deliverTeamMessages = options.deliverTeamMessages ?? true;
215
216
  let localTools = subagentsEnabled && !baseTools.some((t) => t.def.function.name === 'task')
216
217
  ? [...baseTools, taskTool]
217
218
  : baseTools;
@@ -344,7 +345,7 @@ export async function* runAgent(options) {
344
345
  // Wire sub-agent spawning. Each call runs a fresh, isolated runAgent to
345
346
  // completion and returns only its final text.
346
347
  if (subagentsEnabled) {
347
- ctx.runSubagent = async ({ prompt: subPrompt, agentType, signal: subSignal, onProgress }) => {
348
+ ctx.runSubagent = async ({ prompt: subPrompt, agentType, name: subName, signal: subSignal, onProgress }) => {
348
349
  const def = agentType ? agents?.[agentType] : undefined;
349
350
  const subSystem = def?.prompt ?? defaultSubagentPrompt(cwd);
350
351
  const subTools = def?.tools
@@ -389,7 +390,8 @@ export async function* runAgent(options) {
389
390
  team: teamEnabled,
390
391
  mailbox,
391
392
  board,
392
- agentName: agentType || 'worker',
393
+ deliverTeamMessages,
394
+ agentName: subName || agentType || 'worker',
393
395
  memory,
394
396
  skills,
395
397
  });
@@ -626,6 +628,29 @@ export async function* runAgent(options) {
626
628
  };
627
629
  }
628
630
  }
631
+ // Team mailbox: push unread messages addressed to THIS agent into the
632
+ // transcript at the turn boundary — same delivery model as the queue, but
633
+ // sourced from the shared mailbox. This lets a coordinator (or any peer)
634
+ // dispatch a message to a *running* sub-agent and have it land on the
635
+ // sub-agent's next tool round, no polling tool required. Cross-worker too:
636
+ // a BroadcastChannelMailbox replicates the message before it's drained.
637
+ if (mailbox && deliverTeamMessages) {
638
+ const unread = mailbox.inbox(agentName, { unreadOnly: true });
639
+ if (unread.length) {
640
+ mailbox.markRead(agentName);
641
+ const body = '[Team messages]\n' +
642
+ unread.map((m) => `- from ${m.from}: ${m.text}`).join('\n');
643
+ history.push({ role: 'user', content: body });
644
+ yield {
645
+ type: 'user',
646
+ message: { role: 'user', content: body },
647
+ parent_tool_use_id: null,
648
+ timestamp: new Date().toISOString(),
649
+ uuid: uuid(),
650
+ session_id: sessionId,
651
+ };
652
+ }
653
+ }
629
654
  // Auto-compaction: summarize when the transcript nears the context limit.
630
655
  // Circuit-breaker: stop after 3 compactions (avoids a summarize loop).
631
656
  if (options.autoCompact && autoCompactCount < 3 && history.length > 3) {
package/dist/query.d.ts CHANGED
@@ -87,6 +87,10 @@ export interface QueryOptions {
87
87
  board?: import('./team/index.js').TaskBoard;
88
88
  /** This agent's name/label for messaging (default 'coordinator'). */
89
89
  agentName?: string;
90
+ /** Auto-deliver unread mailbox messages addressed to this agent into the
91
+ * transcript at each turn boundary (push delivery, like the message queue).
92
+ * Default true when `team` is enabled. */
93
+ deliverTeamMessages?: boolean;
90
94
  /** Persist the transcript to this store (keyed by sessionId) for resume. */
91
95
  sessionStore?: SessionStoreLike;
92
96
  /** Load the stored transcript for sessionId before the first turn. */
package/dist/query.js CHANGED
@@ -49,6 +49,7 @@ export function query(options) {
49
49
  includePartialMessages: options.includePartialMessages,
50
50
  team: options.team,
51
51
  mailbox: options.mailbox,
52
+ deliverTeamMessages: options.deliverTeamMessages,
52
53
  board: options.board,
53
54
  agentName: options.agentName,
54
55
  sessionStore: options.sessionStore,
@@ -27,6 +27,20 @@ export declare class BroadcastChannelMailbox extends Mailbox {
27
27
  private readonly origin;
28
28
  private closed;
29
29
  constructor(opts?: BroadcastChannelMailboxOptions);
30
+ /**
31
+ * Cross-tab/cross-context mailbox in one call, backed by the bundled
32
+ * `broadcast-channel` package (durable across tabs via IndexedDB/localStorage
33
+ * fallbacks, and works in older browsers + Node). Async because the package
34
+ * is lazy-imported so it stays out of bundles that don't use it.
35
+ *
36
+ * const mb = await BroadcastChannelMailbox.crossTab({ channelName: 'team', origin: 'planner' })
37
+ * query({ prompt, workspace, llm, team: true, mailbox: mb })
38
+ */
39
+ static crossTab(opts?: {
40
+ channelName?: string;
41
+ origin?: string;
42
+ channelOptions?: unknown;
43
+ }): Promise<BroadcastChannelMailbox>;
30
44
  /** Send a message: append to the local replica and broadcast to peers. */
31
45
  send(from: string, to: string, text: string): string;
32
46
  /** Stop listening and release the channel. Safe to call more than once. */
@@ -63,6 +63,23 @@ export class BroadcastChannelMailbox extends Mailbox {
63
63
  this.channel.onmessage = (ev) => handle(ev);
64
64
  }
65
65
  }
66
+ /**
67
+ * Cross-tab/cross-context mailbox in one call, backed by the bundled
68
+ * `broadcast-channel` package (durable across tabs via IndexedDB/localStorage
69
+ * fallbacks, and works in older browsers + Node). Async because the package
70
+ * is lazy-imported so it stays out of bundles that don't use it.
71
+ *
72
+ * const mb = await BroadcastChannelMailbox.crossTab({ channelName: 'team', origin: 'planner' })
73
+ * query({ prompt, workspace, llm, team: true, mailbox: mb })
74
+ */
75
+ static async crossTab(opts = {}) {
76
+ const mod = (await import('broadcast-channel'));
77
+ const BC = mod.BroadcastChannel ?? mod.default?.BroadcastChannel;
78
+ if (!BC)
79
+ throw new Error('broadcast-channel package did not export BroadcastChannel');
80
+ const channel = new BC(opts.channelName ?? 'anyclaude-team', opts.channelOptions);
81
+ return new BroadcastChannelMailbox({ channel, origin: opts.origin });
82
+ }
66
83
  /** Send a message: append to the local replica and broadcast to peers. */
67
84
  send(from, to, text) {
68
85
  // Globally-unique id: origin-scoped so two workers never collide.
@@ -23,11 +23,20 @@ export const dispatchTasks = {
23
23
  if (!board || !runSubagent) {
24
24
  return { content: 'Teammates/sub-agents are not enabled for this session.', isError: true };
25
25
  }
26
- const spawn = (task) => runSubagent({
27
- description: task.subject,
28
- prompt: `Complete this task:\n# ${task.subject}\n${task.description ?? ''}\n\n` +
29
- 'When done, your final message should report exactly what you did.',
30
- });
26
+ const spawn = (task) => {
27
+ // Give each worker a unique, addressable name and reflect it as the task
28
+ // owner, so the coordinator can dispatch a message to this specific
29
+ // running worker (delivered on its next tool round via the mailbox).
30
+ const name = `worker:${task.id}`;
31
+ board.update(task.id, { owner: name });
32
+ return runSubagent({
33
+ description: task.subject,
34
+ name,
35
+ prompt: `You are teammate "${name}". Complete this task:\n# ${task.subject}\n${task.description ?? ''}\n\n` +
36
+ 'New instructions from the coordinator may arrive mid-task as `[Team messages]` — adapt when they do. ' +
37
+ 'When done, your final message should report exactly what you did.',
38
+ });
39
+ };
31
40
  const summary = await runTeamLoop(board, spawn, {
32
41
  concurrency: typeof input.concurrency === 'number' ? input.concurrency : undefined,
33
42
  signal: ctx.signal,
@@ -6,7 +6,7 @@ You are the coordinator of a team of agents. Plan and delegate rather than doing
6
6
 
7
7
  - Decompose the work into discrete tasks on the shared board with \`task_create\`. When a task depends on another, set \`blocked_by\` to the prerequisite task ids so execution is correctly gated.
8
8
  - Spawn workers with the \`task\` tool to execute board tasks — give each worker a focused prompt and, when useful, run independent workers in parallel. Prefer parallelism over sequential work whenever tasks are independent.
9
- - Use \`send_message\` to direct workers, hand off context, or coordinate between teammates. Check the board with \`board_list\` and \`task_get\` to track progress, and keep task status current with \`task_update\` (mark in_progress when started, completed/failed when done).
9
+ - Use \`send_message\` to direct workers, hand off context, or coordinate between teammates. Dispatched workers are named \`worker:<taskId>\` (shown as the task owner in \`board_list\`); a message addressed to that name is delivered to that worker on its next step, even while it is still running — use this to redirect or add context mid-task. Check the board with \`board_list\` and \`task_get\` to track progress, and keep task status current with \`task_update\` (mark in_progress when started, completed/failed when done).
10
10
  - Synthesize workers' results yourself before continuing. NEVER fabricate or assume a worker's output — rely only on what they actually returned.
11
11
  - Keep the board the source of truth: every meaningful unit of work should be a task with an owner and a status.`;
12
12
  }
@@ -1,5 +1,5 @@
1
1
  /** Bump on release so adoption can be bucketed by version. */
2
- export declare const TELEMETRY_SDK_VERSION = "0.10.0";
2
+ export declare const TELEMETRY_SDK_VERSION = "0.11.0";
3
3
  export interface TelemetryOptions {
4
4
  /** Force-disable for this call (highest precedence besides the global opt-outs). */
5
5
  disabled?: boolean;
package/dist/telemetry.js CHANGED
@@ -16,7 +16,7 @@
16
16
  // See TELEMETRY.md for the full disclosure.
17
17
  import { uuid } from './util/ids.js';
18
18
  /** Bump on release so adoption can be bucketed by version. */
19
- export const TELEMETRY_SDK_VERSION = '0.10.0';
19
+ export const TELEMETRY_SDK_VERSION = '0.11.0';
20
20
  // Aggregate-only collector (Puter Worker; see examples/telemetry-collector).
21
21
  // Override with `ANYCLAUDE_TELEMETRY_URL` / `telemetry: { url }`, or disable
22
22
  // entirely with the opt-outs above. Set to '' to make telemetry a no-op.
@@ -33,6 +33,10 @@ export interface ToolContext {
33
33
  description: string;
34
34
  prompt: string;
35
35
  agentType?: string;
36
+ /** Unique name for this sub-agent in the shared mailbox, so peers can
37
+ * address messages to this specific running worker. Defaults to the
38
+ * agentType (or 'worker'). */
39
+ name?: string;
36
40
  /** Abort signal (e.g. a background task's stop) — cancels the sub-agent. */
37
41
  signal?: AbortSignal;
38
42
  /** Streamed progress (assistant text + tool names) as the sub-agent works. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anyclaude-sdk",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Standalone, browser-compatible SDK providing Claude Code agent capabilities (tools, tool loop, multi-turn, MCP, sub-agents, sessions) against any OpenAI/Anthropic-compatible LLM endpoint. Runs in the browser (WebContainer), Node, and Bun — no backend required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -115,6 +115,7 @@
115
115
  "url": "https://github.com/pipilot-dev/anyclaude-sdk/issues"
116
116
  },
117
117
  "dependencies": {
118
+ "broadcast-channel": "^7.0.0",
118
119
  "comlink": "^4.4.2"
119
120
  },
120
121
  "peerDependencies": {
@@ -122,7 +123,6 @@
122
123
  "@daytonaio/sdk": "*",
123
124
  "@vercel/sandbox": "*",
124
125
  "@webcontainer/api": ">=1.1.0",
125
- "broadcast-channel": ">=4.0.0",
126
126
  "dexie": ">=3.2.0",
127
127
  "e2b": "*"
128
128
  },
@@ -130,9 +130,6 @@
130
130
  "@webcontainer/api": {
131
131
  "optional": true
132
132
  },
133
- "broadcast-channel": {
134
- "optional": true
135
- },
136
133
  "dexie": {
137
134
  "optional": true
138
135
  },
@@ -151,7 +148,6 @@
151
148
  },
152
149
  "devDependencies": {
153
150
  "@types/node": "^26.0.0",
154
- "broadcast-channel": "^7.0.0",
155
151
  "dexie": "^4.4.4",
156
152
  "typescript": "^5.4.0"
157
153
  }