@agenticmail/claudecode 0.2.1 → 0.2.3

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.
@@ -12191,7 +12191,7 @@ function rememberBounded(set, item) {
12191
12191
  for (const x of drop) set.delete(x);
12192
12192
  }
12193
12193
  }
12194
- var DEFAULT_MAX_CONCURRENT = 10;
12194
+ var DEFAULT_MAX_CONCURRENT = 50;
12195
12195
  var DEFAULT_SYNC_INTERVAL_MS = 3e4;
12196
12196
  var DEFAULT_RECONNECT_BASE_MS = 2e3;
12197
12197
  var DEFAULT_RECONNECT_MAX_MS = 6e4;
@@ -12462,15 +12462,26 @@ function newMailPrompt(agent, event) {
12462
12462
  ` see ./foo.py, runs with \`python3 foo.py\`"); the filesystem is for`,
12463
12463
  ` DELIVERABLES. Then:`,
12464
12464
  ` reply_email({ uid: ${uid ?? "<uid>"}, replyAll: true, text: "...", _account: "${agent.name}" })`,
12465
- ` Sign with your name. Be substantive but concise. If you are handing off`,
12466
- ` to the next teammate, name them explicitly in your reply ("Orion \u2014 over to you, please \u2026").`,
12467
- ` **NAME the next actor in the \`wake\` parameter** so the dispatcher only`,
12468
- ` gives them a Claude turn \u2014 every other CC'd teammate still receives the`,
12469
- ` mail in their inbox but stays asleep, saving the project a lot of tokens.`,
12470
- ` Example: \`reply_email({ uid, replyAll: true, text: "Orion \u2014 your turn \u2026",`,
12471
- ` wake: ["orion"], _account: "${agent.name}" })\`. If nobody specific is`,
12472
- ` next (the work is complete and you're just signing off), pass \`wake: []\``,
12473
- ` to deliver silently with zero Claude turns spawned.`,
12465
+ ` Sign with your name. Be substantive but concise.`,
12466
+ ``,
12467
+ ` ## Reply addressing \u2014 CRITICAL for wake control`,
12468
+ ` reply_email({ replyAll: true }) automatically builds the right shape:`,
12469
+ ` the ORIGINAL SENDER ends up on To (so they wake by default),`,
12470
+ ` every other participant ends up on Cc (so they see it without`,
12471
+ ` waking). DO NOT pass a hand-rolled comma-separated address list`,
12472
+ ` via send_email \u2014 that puts every recipient on To and re-wakes`,
12473
+ ` the whole thread, defeating the wake gating. Trust replyAll.`,
12474
+ ``,
12475
+ ` If you want to wake someone OTHER than the original sender`,
12476
+ ` (e.g. you are handing off to a different next actor), name them`,
12477
+ ` explicitly in the reply body ("Orion \u2014 over to you, please\u2026")`,
12478
+ ` AND pass \`wake: ["orion"]\` so the dispatcher gives them a`,
12479
+ ` Claude turn instead. Example:`,
12480
+ ` reply_email({ uid, replyAll: true, text: "Orion \u2014 your turn \u2026",`,
12481
+ ` wake: ["orion"], _account: "${agent.name}" })`,
12482
+ ` If nobody specific is next (the work is complete and you're just`,
12483
+ ` signing off), pass \`wake: []\` to deliver silently \u2014 every`,
12484
+ ` participant still sees the reply, no Claude turn is spawned.`,
12474
12485
  ``,
12475
12486
  `7. **If you need additional help from a teammate not yet on the thread,**`,
12476
12487
  ` include them by CC'ing in your reply-all \u2014 DO NOT spin up a separate`,
@@ -13173,6 +13184,7 @@ var Dispatcher = class {
13173
13184
  }
13174
13185
  /** Acquire a concurrency slot, run a worker, release the slot. */
13175
13186
  async spawnWorker(account, prompt, ctx) {
13187
+ const releaseAgentLock = await this.acquireAgentSerial(account.id);
13176
13188
  await this.acquireSlot();
13177
13189
  const workerId = `${account.id}:${ctx.kind}:${ctx.uid ?? ctx.taskId ?? ""}:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
13178
13190
  let workerResult = null;
@@ -13255,6 +13267,10 @@ var Dispatcher = class {
13255
13267
  } finally {
13256
13268
  clearInterval(heartbeatHandle);
13257
13269
  this.releaseSlot();
13270
+ try {
13271
+ releaseAgentLock();
13272
+ } catch {
13273
+ }
13258
13274
  const ok = workerResult?.ok === true;
13259
13275
  const preview = workerResult?.ok ? workerResult.text : workerResult ? workerResult.error : "worker did not start";
13260
13276
  writeLog(`worker_finished ok=${ok} chars=${preview.length}`);
@@ -13360,6 +13376,41 @@ var Dispatcher = class {
13360
13376
  const next = this.waiters.shift();
13361
13377
  if (next) next();
13362
13378
  }
13379
+ /**
13380
+ * Per-agent serialization. At most ONE worker runs for any
13381
+ * given agent at a time. When a new wake fires for an agent
13382
+ * whose worker is still running, the new wake's spawnWorker
13383
+ * waits on the prior worker's tail before proceeding.
13384
+ *
13385
+ * This is the fix for the "dispatcher crashed when sender
13386
+ * broadcast to a 5-CC thread" failure mode: under the old
13387
+ * design, 5 emails landing for vesper-on-3-different-threads
13388
+ * in the same second spawned 5 simultaneous vesper workers,
13389
+ * each opening its own IMAP connection, each calling the
13390
+ * SDK, racing on the same inbox cache. With this gate they
13391
+ * queue tail-to-head and run sequentially.
13392
+ *
13393
+ * `nextRun` is a chained promise: each new spawn calls
13394
+ * `then()` on the previous tail so the order is preserved.
13395
+ * When the chain resolves to a no-op (empty queue), the
13396
+ * entry is garbage-collected from the map so memory stays
13397
+ * bounded at #active-agents.
13398
+ */
13399
+ agentSerial = /* @__PURE__ */ new Map();
13400
+ async acquireAgentSerial(agentId) {
13401
+ const prev = this.agentSerial.get(agentId);
13402
+ let release;
13403
+ const next = new Promise((resolve) => {
13404
+ release = resolve;
13405
+ });
13406
+ this.agentSerial.set(agentId, prev ? prev.then(() => next).catch(() => next) : next);
13407
+ if (prev) await prev.catch(() => {
13408
+ });
13409
+ return () => {
13410
+ release();
13411
+ if (this.agentSerial.get(agentId) === next) this.agentSerial.delete(agentId);
13412
+ };
13413
+ }
13363
13414
  };
13364
13415
  function sleep(ms) {
13365
13416
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Dispatcher
4
- } from "./chunk-TWFORAWT.js";
4
+ } from "./chunk-ZBDS3QNR.js";
5
5
  import "./chunk-DDJNA5HP.js";
6
6
  import "./chunk-B276KPVO.js";
7
7
  import "./chunk-RB5MGRT3.js";
@@ -31,7 +31,10 @@ async function main() {
31
31
  process.once("SIGINT", shutdown);
32
32
  process.once("SIGTERM", shutdown);
33
33
  process.on("unhandledRejection", (reason) => {
34
- console.error("[dispatcher-bin] unhandledRejection:", reason);
34
+ console.error("[dispatcher-bin] unhandledRejection (continuing):", reason);
35
+ });
36
+ process.on("uncaughtException", (err) => {
37
+ console.error("[dispatcher-bin] uncaughtException (continuing):", err);
35
38
  });
36
39
  await dispatcher.start();
37
40
  }
@@ -348,6 +348,28 @@ declare class Dispatcher {
348
348
  private buildMcpEnv;
349
349
  private acquireSlot;
350
350
  private releaseSlot;
351
+ /**
352
+ * Per-agent serialization. At most ONE worker runs for any
353
+ * given agent at a time. When a new wake fires for an agent
354
+ * whose worker is still running, the new wake's spawnWorker
355
+ * waits on the prior worker's tail before proceeding.
356
+ *
357
+ * This is the fix for the "dispatcher crashed when sender
358
+ * broadcast to a 5-CC thread" failure mode: under the old
359
+ * design, 5 emails landing for vesper-on-3-different-threads
360
+ * in the same second spawned 5 simultaneous vesper workers,
361
+ * each opening its own IMAP connection, each calling the
362
+ * SDK, racing on the same inbox cache. With this gate they
363
+ * queue tail-to-head and run sequentially.
364
+ *
365
+ * `nextRun` is a chained promise: each new spawn calls
366
+ * `then()` on the previous tail so the order is preserved.
367
+ * When the chain resolves to a no-op (empty queue), the
368
+ * entry is garbage-collected from the map so memory stays
369
+ * bounded at #active-agents.
370
+ */
371
+ private agentSerial;
372
+ private acquireAgentSerial;
351
373
  }
352
374
 
353
375
  export { Dispatcher, type DispatcherOptions, type QueryFn, type WorkerObserver };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dispatcher
3
- } from "./chunk-TWFORAWT.js";
3
+ } from "./chunk-ZBDS3QNR.js";
4
4
  import "./chunk-DDJNA5HP.js";
5
5
  import "./chunk-B276KPVO.js";
6
6
  import "./chunk-RB5MGRT3.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Dispatcher,
3
3
  loadPersonaForAgent
4
- } from "./chunk-TWFORAWT.js";
4
+ } from "./chunk-ZBDS3QNR.js";
5
5
  import "./chunk-DDJNA5HP.js";
6
6
  import "./chunk-B276KPVO.js";
7
7
  import "./chunk-RB5MGRT3.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/claudecode",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Claude Code integration for AgenticMail — surfaces every AgenticMail agent as a native Claude Code subagent so any Claude Code session can delegate to them with the Agent tool",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "prepublishOnly": "npm run build"
48
48
  },
49
49
  "dependencies": {
50
- "@agenticmail/mcp": "^0.9.1",
50
+ "@agenticmail/mcp": "^0.9.2",
51
51
  "@anthropic-ai/claude-agent-sdk": "^0.2.140"
52
52
  },
53
53
  "peerDependencies": {