@agenticmail/claudecode 0.2.2 → 0.2.4
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;
|
|
@@ -13184,6 +13184,7 @@ var Dispatcher = class {
|
|
|
13184
13184
|
}
|
|
13185
13185
|
/** Acquire a concurrency slot, run a worker, release the slot. */
|
|
13186
13186
|
async spawnWorker(account, prompt, ctx) {
|
|
13187
|
+
const releaseAgentLock = await this.acquireAgentSerial(account.id);
|
|
13187
13188
|
await this.acquireSlot();
|
|
13188
13189
|
const workerId = `${account.id}:${ctx.kind}:${ctx.uid ?? ctx.taskId ?? ""}:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
13189
13190
|
let workerResult = null;
|
|
@@ -13221,12 +13222,18 @@ var Dispatcher = class {
|
|
|
13221
13222
|
let turnCount = 0;
|
|
13222
13223
|
let lastTool = "";
|
|
13223
13224
|
let lastUsage;
|
|
13225
|
+
const digestedUids = /* @__PURE__ */ new Set();
|
|
13224
13226
|
const observer = {
|
|
13225
13227
|
onMessage: (tag, summary) => {
|
|
13226
13228
|
writeLog(`${tag} ${summary}`);
|
|
13227
13229
|
if (tag === "tool_use") {
|
|
13228
13230
|
lastTool = summary.split(" ")[0];
|
|
13229
13231
|
turnCount++;
|
|
13232
|
+
const m = /read_email\b[^}]*"uid"\s*:\s*(\d+)/.exec(summary);
|
|
13233
|
+
if (m) {
|
|
13234
|
+
const uid = parseInt(m[1], 10);
|
|
13235
|
+
if (Number.isFinite(uid) && uid > 0) digestedUids.add(uid);
|
|
13236
|
+
}
|
|
13230
13237
|
}
|
|
13231
13238
|
if (tag === "usage") lastUsage = summary;
|
|
13232
13239
|
}
|
|
@@ -13266,6 +13273,30 @@ var Dispatcher = class {
|
|
|
13266
13273
|
} finally {
|
|
13267
13274
|
clearInterval(heartbeatHandle);
|
|
13268
13275
|
this.releaseSlot();
|
|
13276
|
+
if (digestedUids.size > 0) {
|
|
13277
|
+
const prefix = `${account.id}::`;
|
|
13278
|
+
for (const [key, entry] of this.wakeCoalesce.entries()) {
|
|
13279
|
+
if (!key.startsWith(prefix)) continue;
|
|
13280
|
+
const before = entry.events.length;
|
|
13281
|
+
entry.events = entry.events.filter((e) => !(typeof e.uid === "number" && digestedUids.has(e.uid)));
|
|
13282
|
+
if (entry.events.length < before) {
|
|
13283
|
+
this.log("info", `[dispatcher] dropped ${before - entry.events.length} queued wake(s) for "${account.name}" \u2014 UIDs already digested this turn`);
|
|
13284
|
+
}
|
|
13285
|
+
if (entry.events.length === 0) {
|
|
13286
|
+
try {
|
|
13287
|
+
clearTimeout(entry.timer);
|
|
13288
|
+
} catch {
|
|
13289
|
+
}
|
|
13290
|
+
this.wakeCoalesce.delete(key);
|
|
13291
|
+
}
|
|
13292
|
+
}
|
|
13293
|
+
const ch = this.channels.get(account.id);
|
|
13294
|
+
if (ch) for (const uid of digestedUids) rememberBounded(ch.seenUids, uid);
|
|
13295
|
+
}
|
|
13296
|
+
try {
|
|
13297
|
+
releaseAgentLock();
|
|
13298
|
+
} catch {
|
|
13299
|
+
}
|
|
13269
13300
|
const ok = workerResult?.ok === true;
|
|
13270
13301
|
const preview = workerResult?.ok ? workerResult.text : workerResult ? workerResult.error : "worker did not start";
|
|
13271
13302
|
writeLog(`worker_finished ok=${ok} chars=${preview.length}`);
|
|
@@ -13371,6 +13402,41 @@ var Dispatcher = class {
|
|
|
13371
13402
|
const next = this.waiters.shift();
|
|
13372
13403
|
if (next) next();
|
|
13373
13404
|
}
|
|
13405
|
+
/**
|
|
13406
|
+
* Per-agent serialization. At most ONE worker runs for any
|
|
13407
|
+
* given agent at a time. When a new wake fires for an agent
|
|
13408
|
+
* whose worker is still running, the new wake's spawnWorker
|
|
13409
|
+
* waits on the prior worker's tail before proceeding.
|
|
13410
|
+
*
|
|
13411
|
+
* This is the fix for the "dispatcher crashed when sender
|
|
13412
|
+
* broadcast to a 5-CC thread" failure mode: under the old
|
|
13413
|
+
* design, 5 emails landing for vesper-on-3-different-threads
|
|
13414
|
+
* in the same second spawned 5 simultaneous vesper workers,
|
|
13415
|
+
* each opening its own IMAP connection, each calling the
|
|
13416
|
+
* SDK, racing on the same inbox cache. With this gate they
|
|
13417
|
+
* queue tail-to-head and run sequentially.
|
|
13418
|
+
*
|
|
13419
|
+
* `nextRun` is a chained promise: each new spawn calls
|
|
13420
|
+
* `then()` on the previous tail so the order is preserved.
|
|
13421
|
+
* When the chain resolves to a no-op (empty queue), the
|
|
13422
|
+
* entry is garbage-collected from the map so memory stays
|
|
13423
|
+
* bounded at #active-agents.
|
|
13424
|
+
*/
|
|
13425
|
+
agentSerial = /* @__PURE__ */ new Map();
|
|
13426
|
+
async acquireAgentSerial(agentId) {
|
|
13427
|
+
const prev = this.agentSerial.get(agentId);
|
|
13428
|
+
let release;
|
|
13429
|
+
const next = new Promise((resolve) => {
|
|
13430
|
+
release = resolve;
|
|
13431
|
+
});
|
|
13432
|
+
this.agentSerial.set(agentId, prev ? prev.then(() => next).catch(() => next) : next);
|
|
13433
|
+
if (prev) await prev.catch(() => {
|
|
13434
|
+
});
|
|
13435
|
+
return () => {
|
|
13436
|
+
release();
|
|
13437
|
+
if (this.agentSerial.get(agentId) === next) this.agentSerial.delete(agentId);
|
|
13438
|
+
};
|
|
13439
|
+
}
|
|
13374
13440
|
};
|
|
13375
13441
|
function sleep(ms) {
|
|
13376
13442
|
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-V5R2D3QD.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.4",
|
|
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",
|