@agenticmail/claudecode 0.2.0 → 0.2.1
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/dist/{chunk-UCI2HLHM.js → chunk-TWFORAWT.js} +113 -21
- package/dist/{config-CjEDSvVy.d.ts → config-CXW3gXFC.d.ts} +4 -0
- package/dist/dispatcher-bin.js +1 -1
- package/dist/dispatcher.d.ts +29 -1
- package/dist/dispatcher.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/install.d.ts +1 -1
- package/dist/status.d.ts +1 -1
- package/dist/uninstall.d.ts +1 -1
- package/package.json +2 -2
|
@@ -12345,9 +12345,17 @@ async function runWorker(query, persona, userPrompt, agent, mcpServerName, mcpCo
|
|
|
12345
12345
|
}
|
|
12346
12346
|
}
|
|
12347
12347
|
}
|
|
12348
|
-
if (m.type === "result"
|
|
12349
|
-
|
|
12350
|
-
if (
|
|
12348
|
+
if (m.type === "result") {
|
|
12349
|
+
const r = m;
|
|
12350
|
+
if (typeof r.result === "string") {
|
|
12351
|
+
collectedText.push(r.result);
|
|
12352
|
+
if (observer) observer.onMessage("result", r.result.slice(0, 240).replace(/\s+/g, " ").trim());
|
|
12353
|
+
}
|
|
12354
|
+
if (r.usage && observer) {
|
|
12355
|
+
const u = r.usage;
|
|
12356
|
+
const summary = `in=${u.input_tokens ?? 0} out=${u.output_tokens ?? 0} cacheR=${u.cache_read_input_tokens ?? 0} cacheW=${u.cache_creation_input_tokens ?? 0}${typeof r.total_cost_usd === "number" ? ` cost=$${r.total_cost_usd.toFixed(4)}` : ""}`;
|
|
12357
|
+
observer.onMessage("usage", summary);
|
|
12358
|
+
}
|
|
12351
12359
|
}
|
|
12352
12360
|
}
|
|
12353
12361
|
const text = collectedText.join("\n").trim();
|
|
@@ -12565,6 +12573,13 @@ var Dispatcher = class {
|
|
|
12565
12573
|
*/
|
|
12566
12574
|
wakeCoalesce = /* @__PURE__ */ new Map();
|
|
12567
12575
|
wakeCoalesceMs;
|
|
12576
|
+
/** Wall-clock timestamp the dispatcher started. Surfaced via
|
|
12577
|
+
* process-heartbeat so check_activity can show uptime. */
|
|
12578
|
+
startedAtMs = Date.now();
|
|
12579
|
+
/** Periodic timer that posts a process-heartbeat to the API.
|
|
12580
|
+
* Without this, a hung dispatcher looks identical to "no
|
|
12581
|
+
* events to wake on" — the host has no liveness signal. */
|
|
12582
|
+
processHeartbeatTimer = null;
|
|
12568
12583
|
constructor(opts = {}) {
|
|
12569
12584
|
this.cfg = resolveConfig(opts);
|
|
12570
12585
|
this.maxConcurrent = opts.maxConcurrentWorkers ?? DEFAULT_MAX_CONCURRENT;
|
|
@@ -12636,16 +12651,38 @@ var Dispatcher = class {
|
|
|
12636
12651
|
}
|
|
12637
12652
|
async start() {
|
|
12638
12653
|
this.log("info", `[dispatcher] starting (maxConcurrent=${this.maxConcurrent}, syncEvery=${this.syncIntervalMs}ms)`);
|
|
12654
|
+
this.startedAtMs = Date.now();
|
|
12639
12655
|
await this.syncAccounts();
|
|
12640
12656
|
this.accountSyncTimer = setInterval(() => {
|
|
12641
12657
|
this.syncAccounts().catch((err) => this.log("warn", `[dispatcher] account sync failed: ${err}`));
|
|
12642
12658
|
}, this.syncIntervalMs);
|
|
12659
|
+
this.processHeartbeatTimer = setInterval(() => {
|
|
12660
|
+
this.postActivity("/dispatcher/process-heartbeat", {
|
|
12661
|
+
startedAtMs: this.startedAtMs,
|
|
12662
|
+
uptimeMs: Date.now() - this.startedAtMs,
|
|
12663
|
+
channels: this.channels.size,
|
|
12664
|
+
coalesceQueueSize: this.wakeCoalesce.size,
|
|
12665
|
+
running: this.running,
|
|
12666
|
+
maxConcurrent: this.maxConcurrent
|
|
12667
|
+
});
|
|
12668
|
+
}, 3e4);
|
|
12669
|
+
this.processHeartbeatTimer.unref?.();
|
|
12670
|
+
this.postActivity("/dispatcher/process-heartbeat", {
|
|
12671
|
+
startedAtMs: this.startedAtMs,
|
|
12672
|
+
uptimeMs: 0,
|
|
12673
|
+
channels: this.channels.size,
|
|
12674
|
+
coalesceQueueSize: 0,
|
|
12675
|
+
running: 0,
|
|
12676
|
+
maxConcurrent: this.maxConcurrent
|
|
12677
|
+
});
|
|
12643
12678
|
void this.runSystemChannel();
|
|
12644
12679
|
}
|
|
12645
12680
|
async stop() {
|
|
12646
12681
|
this.stopped = true;
|
|
12647
12682
|
if (this.accountSyncTimer) clearInterval(this.accountSyncTimer);
|
|
12648
12683
|
this.accountSyncTimer = null;
|
|
12684
|
+
if (this.processHeartbeatTimer) clearInterval(this.processHeartbeatTimer);
|
|
12685
|
+
this.processHeartbeatTimer = null;
|
|
12649
12686
|
if (this.systemChannelController) {
|
|
12650
12687
|
try {
|
|
12651
12688
|
this.systemChannelController.abort();
|
|
@@ -12695,6 +12732,7 @@ var Dispatcher = class {
|
|
|
12695
12732
|
if (ch) rememberBounded(ch.seenUids, event.uid);
|
|
12696
12733
|
if (isThreadClosedSubject(subject)) {
|
|
12697
12734
|
this.log("info", `[dispatcher] thread closed (subject="${subject ?? ""}") \u2014 skipping wake for "${account.name}" uid=${event.uid}`);
|
|
12735
|
+
this.postSkipped(account, event, "thread-closed", `subject contains a thread-close marker: "${subject ?? ""}"`);
|
|
12698
12736
|
try {
|
|
12699
12737
|
this.threadCache.delete(cacheThreadId);
|
|
12700
12738
|
} catch {
|
|
@@ -12708,8 +12746,18 @@ var Dispatcher = class {
|
|
|
12708
12746
|
const allowlist = extractWakeAllowlist(event);
|
|
12709
12747
|
if (!isAgentOnWakeAllowlist(account.name, allowlist)) {
|
|
12710
12748
|
this.log("info", `[dispatcher] wake allowlist excludes "${account.name}" (list=${JSON.stringify(allowlist)}) \u2014 mail delivered, no Claude turn`);
|
|
12749
|
+
this.postSkipped(account, event, "allowlist-excluded", `wake list ${JSON.stringify(allowlist)} did not include "${account.name}"`);
|
|
12711
12750
|
return;
|
|
12712
12751
|
}
|
|
12752
|
+
const wakeOnCc = account.wakeOnCc !== false;
|
|
12753
|
+
if (!wakeOnCc) {
|
|
12754
|
+
const wasOnTo = event.wasOnTo === true;
|
|
12755
|
+
if (!wasOnTo) {
|
|
12756
|
+
this.log("info", `[dispatcher] "${account.name}" has wake_on_cc:false and was not on To \u2014 mail delivered, no Claude turn (uid=${event.uid})`);
|
|
12757
|
+
this.postSkipped(account, event, "wake-on-cc", `"${account.name}" has wake_on_cc:false; not on To`);
|
|
12758
|
+
return;
|
|
12759
|
+
}
|
|
12760
|
+
}
|
|
12713
12761
|
const threadId = threadIdFromSubject(subject);
|
|
12714
12762
|
await this.scheduleCoalescedWake(account, event, threadId);
|
|
12715
12763
|
return;
|
|
@@ -12985,27 +13033,37 @@ var Dispatcher = class {
|
|
|
12985
13033
|
}
|
|
12986
13034
|
const key = `${account.id}::${threadId}`;
|
|
12987
13035
|
const existing = this.wakeCoalesce.get(key);
|
|
12988
|
-
if (existing) {
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
|
|
12994
|
-
|
|
12995
|
-
this.fireCoalescedWake(key)
|
|
12996
|
-
}
|
|
12997
|
-
|
|
13036
|
+
if (!existing) {
|
|
13037
|
+
const entry = {
|
|
13038
|
+
events: [],
|
|
13039
|
+
// empty — first event already fired
|
|
13040
|
+
account,
|
|
13041
|
+
threadId,
|
|
13042
|
+
firstScheduledAt: this.now(),
|
|
13043
|
+
timer: setTimeout(() => this.fireCoalescedWake(key), this.wakeCoalesceMs)
|
|
13044
|
+
};
|
|
13045
|
+
entry.timer.unref?.();
|
|
13046
|
+
this.wakeCoalesce.set(key, entry);
|
|
13047
|
+
await this.fireWakeImmediately(account, event, threadId);
|
|
12998
13048
|
return;
|
|
12999
13049
|
}
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13050
|
+
clearTimeout(existing.timer);
|
|
13051
|
+
existing.events.push(event);
|
|
13052
|
+
this.postActivity("/dispatcher/worker-queued", {
|
|
13053
|
+
agentName: account.name,
|
|
13054
|
+
agentId: account.id,
|
|
13003
13055
|
threadId,
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
this.
|
|
13056
|
+
queuedCount: existing.events.length,
|
|
13057
|
+
fireAtMs: this.now() + this.wakeCoalesceMs,
|
|
13058
|
+
reason: "coalescing subsequent burst events"
|
|
13059
|
+
});
|
|
13060
|
+
existing.timer = setTimeout(() => this.fireCoalescedWake(key), this.wakeCoalesceMs);
|
|
13061
|
+
existing.timer.unref?.();
|
|
13062
|
+
const elapsedFromFirst = this.now() - existing.firstScheduledAt;
|
|
13063
|
+
if (elapsedFromFirst > this.wakeCoalesceMs * 5) {
|
|
13064
|
+
clearTimeout(existing.timer);
|
|
13065
|
+
this.fireCoalescedWake(key);
|
|
13066
|
+
}
|
|
13009
13067
|
}
|
|
13010
13068
|
/**
|
|
13011
13069
|
* Pre-0.9.0 fast path used when coalescing is disabled. Same
|
|
@@ -13016,6 +13074,7 @@ var Dispatcher = class {
|
|
|
13016
13074
|
const verdict = this.chargeWake(account.id, threadId);
|
|
13017
13075
|
if (!verdict.ok) {
|
|
13018
13076
|
this.log("warn", `[dispatcher] wake-budget exhausted for "${account.name}" on thread "${threadId}" \u2014 dropped uid=${event.uid}`);
|
|
13077
|
+
this.postSkipped(account, event, "budget-exhausted", `wake budget exhausted for thread "${threadId}" (count=${verdict.count}, cap=${this.maxWakesPerThread})`);
|
|
13019
13078
|
return;
|
|
13020
13079
|
}
|
|
13021
13080
|
await this.spawnWorker(account, newMailPrompt(account, event), {
|
|
@@ -13150,6 +13209,7 @@ var Dispatcher = class {
|
|
|
13150
13209
|
}
|
|
13151
13210
|
let turnCount = 0;
|
|
13152
13211
|
let lastTool = "";
|
|
13212
|
+
let lastUsage;
|
|
13153
13213
|
const observer = {
|
|
13154
13214
|
onMessage: (tag, summary) => {
|
|
13155
13215
|
writeLog(`${tag} ${summary}`);
|
|
@@ -13157,6 +13217,7 @@ var Dispatcher = class {
|
|
|
13157
13217
|
lastTool = summary.split(" ")[0];
|
|
13158
13218
|
turnCount++;
|
|
13159
13219
|
}
|
|
13220
|
+
if (tag === "usage") lastUsage = summary;
|
|
13160
13221
|
}
|
|
13161
13222
|
};
|
|
13162
13223
|
const heartbeatHandle = setInterval(() => {
|
|
@@ -13210,6 +13271,11 @@ var Dispatcher = class {
|
|
|
13210
13271
|
agentName: account.name,
|
|
13211
13272
|
ok,
|
|
13212
13273
|
turnCount,
|
|
13274
|
+
// Context-budget telemetry: the SDK-reported usage line
|
|
13275
|
+
// (input/output/cache tokens + cost). Forwarded so
|
|
13276
|
+
// check_activity can show real cost per worker and the
|
|
13277
|
+
// cache+memory savings vs pre-0.9.0 become measurable.
|
|
13278
|
+
usage: lastUsage,
|
|
13213
13279
|
resultPreview: typeof preview === "string" ? preview.slice(0, 240) : void 0
|
|
13214
13280
|
});
|
|
13215
13281
|
}
|
|
@@ -13240,6 +13306,32 @@ var Dispatcher = class {
|
|
|
13240
13306
|
} catch {
|
|
13241
13307
|
}
|
|
13242
13308
|
}
|
|
13309
|
+
/**
|
|
13310
|
+
* Post a "skipped wake" notification with the reason the
|
|
13311
|
+
* dispatcher decided not to fire a Claude turn. Surfaced in
|
|
13312
|
+
* `check_activity` so the host can see the decision instead
|
|
13313
|
+
* of just observing silence ("did my mail land? did the
|
|
13314
|
+
* dispatcher skip it? is the dispatcher even alive?").
|
|
13315
|
+
*
|
|
13316
|
+
* Reasons cover every filter that drops a wake:
|
|
13317
|
+
* - thread-closed — subject had [FINAL]/[DONE]/[CLOSED]/[WRAP]
|
|
13318
|
+
* - allowlist-excluded — sender's `wake` list did not include the agent
|
|
13319
|
+
* - wake-on-cc — agent registered wake_on_cc:false and was on Cc
|
|
13320
|
+
* - dedup — duplicate UID seen recently
|
|
13321
|
+
* - rpc-suppress — RPC-notification mail right after a task event
|
|
13322
|
+
* - budget-exhausted — per-(agent, thread) wake budget hit the cap
|
|
13323
|
+
*/
|
|
13324
|
+
postSkipped(account, event, reason, detail) {
|
|
13325
|
+
this.postActivity("/dispatcher/worker-skipped", {
|
|
13326
|
+
agentId: account.id,
|
|
13327
|
+
agentName: account.name,
|
|
13328
|
+
uid: event.uid,
|
|
13329
|
+
subject: extractSubject(event),
|
|
13330
|
+
from: extractFrom(event),
|
|
13331
|
+
reason,
|
|
13332
|
+
detail
|
|
13333
|
+
});
|
|
13334
|
+
}
|
|
13243
13335
|
/** Build the env block we pass to the worker's MCP server child process. */
|
|
13244
13336
|
async buildMcpEnv() {
|
|
13245
13337
|
return {
|
|
@@ -12,6 +12,10 @@ interface AgenticMailAccount {
|
|
|
12
12
|
apiKey: string;
|
|
13
13
|
role?: string;
|
|
14
14
|
metadata?: Record<string, unknown>;
|
|
15
|
+
/** Per-agent wake preference. When false, the dispatcher
|
|
16
|
+
* drops wakes where this agent was on Cc/Bcc but not To,
|
|
17
|
+
* regardless of the sender's wake list. Defaults to true. */
|
|
18
|
+
wakeOnCc?: boolean;
|
|
15
19
|
}
|
|
16
20
|
/** Resolved configuration for everything the package does. */
|
|
17
21
|
interface ClaudeCodeIntegrationConfig {
|
package/dist/dispatcher-bin.js
CHANGED
package/dist/dispatcher.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as ResolveConfigOptions, A as AgenticMailAccount } from './config-
|
|
1
|
+
import { R as ResolveConfigOptions, A as AgenticMailAccount } from './config-CXW3gXFC.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* AgenticMail → Claude Code event dispatcher.
|
|
@@ -68,6 +68,11 @@ interface SSEEvent {
|
|
|
68
68
|
* Claude turn. When absent, every CC'd recipient wakes (v0.8.x default).
|
|
69
69
|
*/
|
|
70
70
|
wakeAllowlist?: string[];
|
|
71
|
+
/** Per-recipient "was I on the To field?" flag emitted by the
|
|
72
|
+
* API in 0.9.1+. Pairs with the recipient's `wake_on_cc`
|
|
73
|
+
* preference: when the agent registered with wake_on_cc:false
|
|
74
|
+
* and `wasOnTo !== true`, the dispatcher drops the wake. */
|
|
75
|
+
wasOnTo?: boolean;
|
|
71
76
|
[key: string]: unknown;
|
|
72
77
|
}
|
|
73
78
|
interface DispatcherOptions extends ResolveConfigOptions {
|
|
@@ -191,6 +196,13 @@ declare class Dispatcher {
|
|
|
191
196
|
*/
|
|
192
197
|
private wakeCoalesce;
|
|
193
198
|
private wakeCoalesceMs;
|
|
199
|
+
/** Wall-clock timestamp the dispatcher started. Surfaced via
|
|
200
|
+
* process-heartbeat so check_activity can show uptime. */
|
|
201
|
+
private startedAtMs;
|
|
202
|
+
/** Periodic timer that posts a process-heartbeat to the API.
|
|
203
|
+
* Without this, a hung dispatcher looks identical to "no
|
|
204
|
+
* events to wake on" — the host has no liveness signal. */
|
|
205
|
+
private processHeartbeatTimer;
|
|
194
206
|
constructor(opts?: DispatcherOptions);
|
|
195
207
|
/**
|
|
196
208
|
* Charge one wake against the (agent, thread) budget. Returns true
|
|
@@ -316,6 +328,22 @@ declare class Dispatcher {
|
|
|
316
328
|
* load-bearing state.
|
|
317
329
|
*/
|
|
318
330
|
private postActivity;
|
|
331
|
+
/**
|
|
332
|
+
* Post a "skipped wake" notification with the reason the
|
|
333
|
+
* dispatcher decided not to fire a Claude turn. Surfaced in
|
|
334
|
+
* `check_activity` so the host can see the decision instead
|
|
335
|
+
* of just observing silence ("did my mail land? did the
|
|
336
|
+
* dispatcher skip it? is the dispatcher even alive?").
|
|
337
|
+
*
|
|
338
|
+
* Reasons cover every filter that drops a wake:
|
|
339
|
+
* - thread-closed — subject had [FINAL]/[DONE]/[CLOSED]/[WRAP]
|
|
340
|
+
* - allowlist-excluded — sender's `wake` list did not include the agent
|
|
341
|
+
* - wake-on-cc — agent registered wake_on_cc:false and was on Cc
|
|
342
|
+
* - dedup — duplicate UID seen recently
|
|
343
|
+
* - rpc-suppress — RPC-notification mail right after a task event
|
|
344
|
+
* - budget-exhausted — per-(agent, thread) wake budget hit the cap
|
|
345
|
+
*/
|
|
346
|
+
private postSkipped;
|
|
319
347
|
/** Build the env block we pass to the worker's MCP server child process. */
|
|
320
348
|
private buildMcpEnv;
|
|
321
349
|
private acquireSlot;
|
package/dist/dispatcher.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { install } from './install.js';
|
|
2
2
|
export { UninstallOptions, uninstall } from './uninstall.js';
|
|
3
3
|
export { status } from './status.js';
|
|
4
|
-
import { A as AgenticMailAccount } from './config-
|
|
5
|
-
export { C as ClaudeCodeIntegrationConfig, I as InstallResult, a as InstallStatus, R as ResolveConfigOptions, U as UninstallResult, r as resolveConfig } from './config-
|
|
4
|
+
import { A as AgenticMailAccount } from './config-CXW3gXFC.js';
|
|
5
|
+
export { C as ClaudeCodeIntegrationConfig, I as InstallResult, a as InstallStatus, R as ResolveConfigOptions, U as UninstallResult, r as resolveConfig } from './config-CXW3gXFC.js';
|
|
6
6
|
export { createIntegrationRoutes } from './http-routes.js';
|
|
7
7
|
export { Dispatcher, DispatcherOptions, QueryFn } from './dispatcher.js';
|
|
8
8
|
import 'express';
|
package/dist/index.js
CHANGED
package/dist/install.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as ResolveConfigOptions, I as InstallResult, A as AgenticMailAccount, C as ClaudeCodeIntegrationConfig } from './config-
|
|
1
|
+
import { R as ResolveConfigOptions, I as InstallResult, A as AgenticMailAccount, C as ClaudeCodeIntegrationConfig } from './config-CXW3gXFC.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Install AgenticMail into Claude Code.
|
package/dist/status.d.ts
CHANGED
package/dist/uninstall.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agenticmail/claudecode",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
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.1",
|
|
51
51
|
"@anthropic-ai/claude-agent-sdk": "^0.2.140"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|