@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 =
|
|
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
|
|
12466
|
-
|
|
12467
|
-
`
|
|
12468
|
-
`
|
|
12469
|
-
`
|
|
12470
|
-
`
|
|
12471
|
-
`
|
|
12472
|
-
`
|
|
12473
|
-
`
|
|
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));
|
package/dist/dispatcher-bin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Dispatcher
|
|
4
|
-
} from "./chunk-
|
|
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
|
}
|
package/dist/dispatcher.d.ts
CHANGED
|
@@ -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 };
|
package/dist/dispatcher.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agenticmail/claudecode",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
50
|
+
"@agenticmail/mcp": "^0.9.2",
|
|
51
51
|
"@anthropic-ai/claude-agent-sdk": "^0.2.140"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|