@agenticmail/claudecode 0.2.8 → 0.2.10

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.
@@ -4,7 +4,7 @@ import {
4
4
  listPendingTasksForAgent,
5
5
  renderPersonaBody,
6
6
  resolveConfig
7
- } from "./chunk-4VQP57SO.js";
7
+ } from "./chunk-WTAITYZR.js";
8
8
 
9
9
  // src/persona-loader.ts
10
10
  import { existsSync, readFileSync } from "fs";
@@ -840,6 +840,9 @@ var Dispatcher = class {
840
840
  const bridgeName = this.cfg.bridgeAgentName.toLowerCase();
841
841
  if (account.name.toLowerCase() === bridgeName) return false;
842
842
  if (account.role === "bridge") return false;
843
+ const meta = account.metadata;
844
+ if (meta && meta.bridge === true) return false;
845
+ if (meta && typeof meta.host === "string" && meta.host.length > 0) return false;
843
846
  return true;
844
847
  }
845
848
  /** Re-fetch /accounts; open SSE for new ones, close for vanished ones. */
@@ -1205,7 +1208,10 @@ var Dispatcher = class {
1205
1208
  async fireWakeImmediately(account, event, threadId) {
1206
1209
  const verdict = this.chargeWake(account.id, threadId);
1207
1210
  if (!verdict.ok) {
1208
- this.log("warn", `[dispatcher] wake-budget exhausted for "${account.name}" on thread "${threadId}" \u2014 dropped uid=${event.uid}`);
1211
+ this.log(
1212
+ "warn",
1213
+ `[dispatcher] wake-budget exhausted for "${account.name}" on thread "${threadId}" \u2014 dropped uid=${event.uid} (cap=${this.maxWakesPerThread} per ${Math.round(this.wakeWindowMs / 6e4)}min; raise with AGENTICMAIL_DISPATCHER_MAX_WAKES_PER_THREAD env var, or via ~/.agenticmail/dispatcher.json)`
1214
+ );
1209
1215
  this.postSkipped(account, event, "budget-exhausted", `wake budget exhausted for thread "${threadId}" (count=${verdict.count}, cap=${this.maxWakesPerThread})`);
1210
1216
  return;
1211
1217
  }
@@ -0,0 +1,73 @@
1
+ // src/dispatcher-tuning.ts
2
+ import { readFileSync, existsSync, writeFileSync, mkdirSync, renameSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { homedir } from "os";
5
+ function defaultDispatcherConfigPath() {
6
+ return join(homedir(), ".agenticmail", "dispatcher.json");
7
+ }
8
+ function positiveInt(s) {
9
+ if (s === void 0 || s === null || s === "") return void 0;
10
+ const n = typeof s === "number" ? s : parseInt(String(s), 10);
11
+ return Number.isFinite(n) && n > 0 ? n : void 0;
12
+ }
13
+ function readTuningFile(path) {
14
+ if (!existsSync(path)) return {};
15
+ try {
16
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
17
+ if (!parsed || typeof parsed !== "object") return {};
18
+ if (parsed.version !== 1) return {};
19
+ return {
20
+ maxConcurrentWorkers: positiveInt(parsed.maxConcurrentWorkers),
21
+ maxWakesPerThread: positiveInt(parsed.maxWakesPerThread),
22
+ wakeWindowMs: positiveInt(parsed.wakeWindowMs),
23
+ wakeCoalesceMs: positiveInt(parsed.wakeCoalesceMs),
24
+ accountSyncIntervalMs: positiveInt(parsed.accountSyncIntervalMs)
25
+ };
26
+ } catch {
27
+ return {};
28
+ }
29
+ }
30
+ function resolveDispatcherTuning(opts = {}) {
31
+ const env = opts.env ?? process.env;
32
+ const configPath = opts.configPath ?? defaultDispatcherConfigPath();
33
+ const fileLayer = readTuningFile(configPath);
34
+ const envLayer = {
35
+ maxConcurrentWorkers: positiveInt(env.AGENTICMAIL_DISPATCHER_MAX),
36
+ maxWakesPerThread: positiveInt(env.AGENTICMAIL_DISPATCHER_MAX_WAKES_PER_THREAD),
37
+ wakeWindowMs: positiveInt(env.AGENTICMAIL_DISPATCHER_WAKE_WINDOW_MS),
38
+ wakeCoalesceMs: positiveInt(env.AGENTICMAIL_DISPATCHER_COALESCE_MS),
39
+ accountSyncIntervalMs: positiveInt(env.AGENTICMAIL_DISPATCHER_SYNC)
40
+ };
41
+ const explicit = opts.explicit ?? {};
42
+ return {
43
+ maxConcurrentWorkers: explicit.maxConcurrentWorkers ?? envLayer.maxConcurrentWorkers ?? fileLayer.maxConcurrentWorkers,
44
+ maxWakesPerThread: explicit.maxWakesPerThread ?? envLayer.maxWakesPerThread ?? fileLayer.maxWakesPerThread,
45
+ wakeWindowMs: explicit.wakeWindowMs ?? envLayer.wakeWindowMs ?? fileLayer.wakeWindowMs,
46
+ wakeCoalesceMs: explicit.wakeCoalesceMs ?? envLayer.wakeCoalesceMs ?? fileLayer.wakeCoalesceMs,
47
+ accountSyncIntervalMs: explicit.accountSyncIntervalMs ?? envLayer.accountSyncIntervalMs ?? fileLayer.accountSyncIntervalMs
48
+ };
49
+ }
50
+ function writeDispatcherTuning(patch, configPath = defaultDispatcherConfigPath()) {
51
+ const current = readTuningFile(configPath);
52
+ const merged = {
53
+ version: 1,
54
+ updatedAtMs: Date.now(),
55
+ maxConcurrentWorkers: positiveInt(patch.maxConcurrentWorkers) ?? current.maxConcurrentWorkers,
56
+ maxWakesPerThread: positiveInt(patch.maxWakesPerThread) ?? current.maxWakesPerThread,
57
+ wakeWindowMs: positiveInt(patch.wakeWindowMs) ?? current.wakeWindowMs,
58
+ wakeCoalesceMs: positiveInt(patch.wakeCoalesceMs) ?? current.wakeCoalesceMs,
59
+ accountSyncIntervalMs: positiveInt(patch.accountSyncIntervalMs) ?? current.accountSyncIntervalMs
60
+ };
61
+ const dir = dirname(configPath);
62
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
63
+ const tmp = `${configPath}.tmp`;
64
+ writeFileSync(tmp, JSON.stringify(merged, null, 2));
65
+ renameSync(tmp, configPath);
66
+ return merged;
67
+ }
68
+
69
+ export {
70
+ defaultDispatcherConfigPath,
71
+ resolveDispatcherTuning,
72
+ writeDispatcherTuning
73
+ };
@@ -10,7 +10,7 @@ import {
10
10
  deleteAccount,
11
11
  getAccountByName,
12
12
  resolveConfig
13
- } from "./chunk-4VQP57SO.js";
13
+ } from "./chunk-WTAITYZR.js";
14
14
 
15
15
  // src/uninstall.ts
16
16
  import { existsSync, readdirSync, readFileSync, unlinkSync } from "fs";
@@ -7,7 +7,7 @@ import {
7
7
  MANAGED_BY_MARKER,
8
8
  getAccountByName,
9
9
  resolveConfig
10
- } from "./chunk-4VQP57SO.js";
10
+ } from "./chunk-WTAITYZR.js";
11
11
 
12
12
  // src/status.ts
13
13
  import { existsSync, readFileSync, readdirSync } from "fs";
@@ -1,15 +1,15 @@
1
+ import {
2
+ status
3
+ } from "./chunk-KB2EHQW7.js";
1
4
  import {
2
5
  uninstall
3
- } from "./chunk-2RVJ2Q3W.js";
6
+ } from "./chunk-BIP2TBJ5.js";
4
7
  import {
5
8
  install
6
- } from "./chunk-OUYPF4ER.js";
7
- import {
8
- status
9
- } from "./chunk-Q5BA2J2C.js";
9
+ } from "./chunk-TSLDSGOD.js";
10
10
  import {
11
11
  AgenticMailApiError
12
- } from "./chunk-4VQP57SO.js";
12
+ } from "./chunk-WTAITYZR.js";
13
13
 
14
14
  // src/http-routes.ts
15
15
  import { Router } from "express";
@@ -11,8 +11,9 @@ import {
11
11
  ensureAccount,
12
12
  listAccounts,
13
13
  renderSubagentMarkdown,
14
- resolveConfig
15
- } from "./chunk-4VQP57SO.js";
14
+ resolveConfig,
15
+ setAccountRole
16
+ } from "./chunk-WTAITYZR.js";
16
17
 
17
18
  // src/install.ts
18
19
  import { existsSync, mkdirSync, readdirSync, writeFileSync, readFileSync, unlinkSync } from "fs";
@@ -98,7 +99,14 @@ async function install(opts = {}) {
98
99
  "AgenticMail master key not found. Run `agenticmail setup` first to generate one, or pass { masterKey } to install() explicitly."
99
100
  );
100
101
  }
101
- const bridge = await ensureAccount(cfg.apiUrl, cfg.masterKey, cfg.bridgeAgentName, "assistant");
102
+ let bridge = await ensureAccount(cfg.apiUrl, cfg.masterKey, cfg.bridgeAgentName, "bridge");
103
+ if (bridge.role && bridge.role !== "bridge") {
104
+ try {
105
+ await setAccountRole(cfg.apiUrl, cfg.masterKey, bridge.id, "bridge");
106
+ bridge = { ...bridge, role: "bridge" };
107
+ } catch {
108
+ }
109
+ }
102
110
  const everyAccount = await listAccounts(cfg.apiUrl, cfg.masterKey);
103
111
  const exposable = selectExposableAgents(everyAccount, cfg);
104
112
  const accountKeys = {};
@@ -82,6 +82,15 @@ async function ensureAccount(apiUrl, masterKey, name, role = "assistant") {
82
82
  async function deleteAccount(apiUrl, masterKey, id) {
83
83
  await request(apiUrl, masterKey, `/accounts/${encodeURIComponent(id)}`, { method: "DELETE" });
84
84
  }
85
+ async function setAccountRole(apiUrl, masterKey, id, role) {
86
+ const updated = await request(
87
+ apiUrl,
88
+ masterKey,
89
+ `/accounts/${encodeURIComponent(id)}/role`,
90
+ { method: "PATCH", body: { role } }
91
+ );
92
+ return updated ?? { role };
93
+ }
85
94
  async function listInboxForAgent(apiUrl, agentApiKey, opts = {}) {
86
95
  const limit = Math.max(1, Math.min(opts.limit ?? 50, 100));
87
96
  const url = `${apiUrl.replace(/\/$/, "")}/api/agenticmail/mail/inbox?limit=${limit}`;
@@ -282,6 +291,7 @@ export {
282
291
  getAccountByName,
283
292
  ensureAccount,
284
293
  deleteAccount,
294
+ setAccountRole,
285
295
  listInboxForAgent,
286
296
  listPendingTasksForAgent,
287
297
  resolveConfig,
package/dist/cli.js CHANGED
@@ -1,18 +1,23 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ defaultDispatcherConfigPath,
4
+ resolveDispatcherTuning,
5
+ writeDispatcherTuning
6
+ } from "./chunk-B5JDOV32.js";
7
+ import {
8
+ status
9
+ } from "./chunk-KB2EHQW7.js";
2
10
  import {
3
11
  uninstall
4
- } from "./chunk-2RVJ2Q3W.js";
12
+ } from "./chunk-BIP2TBJ5.js";
5
13
  import {
6
14
  install
7
- } from "./chunk-OUYPF4ER.js";
15
+ } from "./chunk-TSLDSGOD.js";
8
16
  import "./chunk-LO5EQSQA.js";
9
- import {
10
- status
11
- } from "./chunk-Q5BA2J2C.js";
12
17
  import "./chunk-US5FT2UB.js";
13
18
  import {
14
19
  AgenticMailApiError
15
- } from "./chunk-4VQP57SO.js";
20
+ } from "./chunk-WTAITYZR.js";
16
21
 
17
22
  // src/cli.ts
18
23
  var GREEN = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -42,17 +47,31 @@ function usage() {
42
47
  print(` install Register AgenticMail with Claude Code (default)`);
43
48
  print(` uninstall Remove the registration`);
44
49
  print(` status Show what's currently installed`);
50
+ print(` tune View / change dispatcher tuning knobs (rate limits, concurrency)`);
45
51
  print("");
46
52
  print(` ${BOLD("Flags:")}`);
47
- print(` --json (status) Emit machine-readable JSON instead of prose`);
53
+ print(` --json (status / tune) Emit machine-readable JSON instead of prose`);
48
54
  print(` --purge-bridge (uninstall) Also delete the AgenticMail bridge agent`);
55
+ print("");
56
+ print(` ${BOLD("Tune flags:")}`);
57
+ print(` --max-concurrent N Cap total simultaneous workers across all agents (default 50)`);
58
+ print(` --max-wakes-per-thread N Wakes a single (agent, thread) pair gets per window (default 10)`);
59
+ print(` --wake-window-ms N Rolling window for the above counter, ms (default 86400000 = 24h)`);
60
+ print(` --wake-coalesce-ms N Burst-debounce \u2014 collapse rapid replies into one wake (default 30000 = 30s, set 0 to disable)`);
61
+ print(` --sync-ms N How often the dispatcher polls /accounts for new agents (default 30000)`);
62
+ print(` --reset Delete ~/.agenticmail/dispatcher.json, return to defaults`);
49
63
  print(` -h, --help Show this help and exit`);
50
64
  print("");
51
- print(` ${BOLD("Environment overrides:")}`);
52
- print(` AGENTICMAIL_API_URL Override AgenticMail master API URL`);
53
- print(` AGENTICMAIL_MASTER_KEY Override master key (otherwise read from ~/.agenticmail/config.json)`);
54
- print(` CLAUDE_CODE_CONFIG_PATH Override Claude Code config path (default ~/.claude.json)`);
55
- print(` CLAUDE_CODE_AGENTS_DIR Override Claude Code agents dir (default ~/.claude/agents)`);
65
+ print(` ${BOLD("Environment overrides (same effect as the flags above, for PM2 setups):")}`);
66
+ print(` AGENTICMAIL_API_URL Override AgenticMail master API URL`);
67
+ print(` AGENTICMAIL_MASTER_KEY Override master key`);
68
+ print(` CLAUDE_CODE_CONFIG_PATH Override Claude Code config path`);
69
+ print(` CLAUDE_CODE_AGENTS_DIR Override Claude Code agents dir`);
70
+ print(` AGENTICMAIL_DISPATCHER_MAX Same as --max-concurrent`);
71
+ print(` AGENTICMAIL_DISPATCHER_MAX_WAKES_PER_THREAD Same as --max-wakes-per-thread`);
72
+ print(` AGENTICMAIL_DISPATCHER_WAKE_WINDOW_MS Same as --wake-window-ms`);
73
+ print(` AGENTICMAIL_DISPATCHER_COALESCE_MS Same as --wake-coalesce-ms`);
74
+ print(` AGENTICMAIL_DISPATCHER_SYNC Same as --sync-ms`);
56
75
  print("");
57
76
  }
58
77
  function envOptions() {
@@ -148,6 +167,92 @@ async function runStatus(asJson) {
148
167
  return 2;
149
168
  }
150
169
  }
170
+ function argNum(args, flag) {
171
+ for (let i = 0; i < args.length; i++) {
172
+ const a = args[i];
173
+ if (a === flag && i + 1 < args.length) {
174
+ const n = parseInt(args[i + 1], 10);
175
+ return Number.isFinite(n) && n > 0 ? n : void 0;
176
+ }
177
+ if (a.startsWith(`${flag}=`)) {
178
+ const n = parseInt(a.slice(flag.length + 1), 10);
179
+ return Number.isFinite(n) && n > 0 ? n : void 0;
180
+ }
181
+ }
182
+ return void 0;
183
+ }
184
+ async function runTune(args) {
185
+ const { existsSync, unlinkSync } = await import("fs");
186
+ const path = defaultDispatcherConfigPath();
187
+ const asJson = args.includes("--json");
188
+ if (args.includes("--reset")) {
189
+ try {
190
+ if (existsSync(path)) unlinkSync(path);
191
+ } catch {
192
+ }
193
+ if (asJson) {
194
+ print(JSON.stringify({ reset: true, path }, null, 2));
195
+ } else {
196
+ print("");
197
+ print(` ${PINK("\u{1F380} Dispatcher tuning")}`);
198
+ print("");
199
+ ok(`Reset: deleted ${path}`);
200
+ print(` ${DIM("The dispatcher will use built-in defaults until you re-tune.")}`);
201
+ print("");
202
+ }
203
+ return 0;
204
+ }
205
+ const patch = {
206
+ maxConcurrentWorkers: argNum(args, "--max-concurrent"),
207
+ maxWakesPerThread: argNum(args, "--max-wakes-per-thread"),
208
+ wakeWindowMs: argNum(args, "--wake-window-ms"),
209
+ wakeCoalesceMs: argNum(args, "--wake-coalesce-ms"),
210
+ accountSyncIntervalMs: argNum(args, "--sync-ms")
211
+ };
212
+ const anyFlag = Object.values(patch).some((v) => v !== void 0);
213
+ if (anyFlag) {
214
+ const merged = writeDispatcherTuning(patch);
215
+ if (asJson) {
216
+ print(JSON.stringify(merged, null, 2));
217
+ return 0;
218
+ }
219
+ print("");
220
+ print(` ${PINK("\u{1F380} Dispatcher tuning saved")}`);
221
+ print("");
222
+ ok(`Wrote ${path}`);
223
+ dim(` maxConcurrentWorkers: ${merged.maxConcurrentWorkers ?? "(default)"}`);
224
+ dim(` maxWakesPerThread: ${merged.maxWakesPerThread ?? "(default)"}`);
225
+ dim(` wakeWindowMs: ${merged.wakeWindowMs ?? "(default)"}`);
226
+ dim(` wakeCoalesceMs: ${merged.wakeCoalesceMs ?? "(default)"}`);
227
+ dim(` accountSyncIntervalMs: ${merged.accountSyncIntervalMs ?? "(default)"}`);
228
+ print("");
229
+ print(` ${BOLD("Next:")} restart the dispatcher daemon to apply.`);
230
+ print(` ${DIM("pm2 restart agenticmail-claudecode-dispatcher")}`);
231
+ print("");
232
+ return 0;
233
+ }
234
+ const resolved = resolveDispatcherTuning();
235
+ if (asJson) {
236
+ print(JSON.stringify({ resolved, path }, null, 2));
237
+ return 0;
238
+ }
239
+ print("");
240
+ print(` ${PINK("\u{1F380} Dispatcher tuning (current)")}`);
241
+ print("");
242
+ print(` Config file: ${path}`);
243
+ print("");
244
+ print(` ${BOLD("Effective values (env > file > built-in default):")}`);
245
+ dim(` maxConcurrentWorkers: ${resolved.maxConcurrentWorkers ?? "50 (default)"}`);
246
+ dim(` maxWakesPerThread: ${resolved.maxWakesPerThread ?? "10 (default)"}`);
247
+ dim(` wakeWindowMs: ${resolved.wakeWindowMs ?? "86400000 (default \u2014 24h)"}`);
248
+ dim(` wakeCoalesceMs: ${resolved.wakeCoalesceMs ?? "30000 (default \u2014 30s)"}`);
249
+ dim(` accountSyncIntervalMs: ${resolved.accountSyncIntervalMs ?? "30000 (default)"}`);
250
+ print("");
251
+ print(` ${BOLD("Change with flags, e.g.:")}`);
252
+ dim(` agenticmail-claudecode tune --max-wakes-per-thread 100 --max-concurrent 200`);
253
+ print("");
254
+ return 0;
255
+ }
151
256
  async function main() {
152
257
  const args = process.argv.slice(2);
153
258
  if (args.includes("-h") || args.includes("--help") || args[0] === "help") {
@@ -163,6 +268,8 @@ async function main() {
163
268
  return runUninstall(args.includes("--purge-bridge"));
164
269
  case "status":
165
270
  return runStatus(args.includes("--json"));
271
+ case "tune":
272
+ return runTune(args);
166
273
  default:
167
274
  fail(`Unknown command: ${command}`);
168
275
  usage();
@@ -1,17 +1,27 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ resolveDispatcherTuning
4
+ } from "./chunk-B5JDOV32.js";
2
5
  import {
3
6
  Dispatcher
4
- } from "./chunk-JOK76WRC.js";
5
- import "./chunk-4VQP57SO.js";
7
+ } from "./chunk-APRMWXZB.js";
8
+ import "./chunk-WTAITYZR.js";
6
9
 
7
10
  // src/dispatcher-bin.ts
8
11
  async function main() {
12
+ const tuning = resolveDispatcherTuning();
13
+ console.error(
14
+ `[dispatcher-bin] tuning: maxConcurrentWorkers=${tuning.maxConcurrentWorkers ?? "(default)"} maxWakesPerThread=${tuning.maxWakesPerThread ?? "(default)"} wakeWindowMs=${tuning.wakeWindowMs ?? "(default)"} wakeCoalesceMs=${tuning.wakeCoalesceMs ?? "(default)"} accountSyncIntervalMs=${tuning.accountSyncIntervalMs ?? "(default)"}`
15
+ );
9
16
  const dispatcher = new Dispatcher({
10
17
  apiUrl: process.env.AGENTICMAIL_API_URL,
11
18
  masterKey: process.env.AGENTICMAIL_MASTER_KEY,
12
19
  agentsDir: process.env.CLAUDE_CODE_AGENTS_DIR,
13
- maxConcurrentWorkers: positiveInt(process.env.AGENTICMAIL_DISPATCHER_MAX),
14
- accountSyncIntervalMs: positiveInt(process.env.AGENTICMAIL_DISPATCHER_SYNC)
20
+ maxConcurrentWorkers: tuning.maxConcurrentWorkers,
21
+ maxWakesPerThread: tuning.maxWakesPerThread,
22
+ wakeWindowMs: tuning.wakeWindowMs,
23
+ wakeCoalesceMs: tuning.wakeCoalesceMs,
24
+ accountSyncIntervalMs: tuning.accountSyncIntervalMs
15
25
  });
16
26
  const shutdown = async (sig) => {
17
27
  console.error(`[dispatcher-bin] received ${sig} \u2014 shutting down`);
@@ -32,11 +42,6 @@ async function main() {
32
42
  });
33
43
  await dispatcher.start();
34
44
  }
35
- function positiveInt(s) {
36
- if (!s) return void 0;
37
- const n = parseInt(s, 10);
38
- return Number.isFinite(n) && n > 0 ? n : void 0;
39
- }
40
45
  main().catch((err) => {
41
46
  console.error(`[dispatcher-bin] fatal: ${err.message}`);
42
47
  process.exit(1);
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Tuning knobs for the dispatcher daemon.
3
+ *
4
+ * # Why this exists
5
+ *
6
+ * The dispatcher has several rate-limit / concurrency knobs that need
7
+ * to be tunable WITHOUT a code change:
8
+ *
9
+ * - `maxConcurrentWorkers` — global worker cap (default 50)
10
+ * - `maxWakesPerThread` — wakes per (agent, thread) per window (default 10)
11
+ * - `wakeWindowMs` — the window itself (default 24h)
12
+ * - `wakeCoalesceMs` — burst-debounce window (default 30s)
13
+ * - `accountSyncIntervalMs` — how often to poll /accounts (default 30s)
14
+ *
15
+ * The DEFAULTS are conservative — protect a fresh install from runaway
16
+ * cost. Power users running active coordination on a single thread
17
+ * routinely hit the 10/24h wake cap and need it raised. Today (before
18
+ * this module) the only way to do that was edit dispatcher.ts and rebuild,
19
+ * which is absurd.
20
+ *
21
+ * # Three input sources, in precedence order
22
+ *
23
+ * 1. Explicit constructor args (programmatic callers, tests)
24
+ * 2. Env vars (PM2 ecosystem.config.cjs lives here)
25
+ * 3. `~/.agenticmail/dispatcher.json` (persistent operator preference,
26
+ * written by the CLI's `agenticmail dispatcher tune` command)
27
+ * 4. Hard-coded defaults
28
+ *
29
+ * Earlier sources win.
30
+ *
31
+ * # File format
32
+ *
33
+ * { "version": 1,
34
+ * "maxConcurrentWorkers": 200,
35
+ * "maxWakesPerThread": 50,
36
+ * "wakeWindowMs": 86400000,
37
+ * "wakeCoalesceMs": 30000,
38
+ * "accountSyncIntervalMs": 30000 }
39
+ *
40
+ * Missing keys fall through to the next precedence level. All values
41
+ * are integers; non-positive / non-finite values fall through (so a
42
+ * broken edit produces a slightly-stale config, not a broken
43
+ * dispatcher).
44
+ */
45
+ interface DispatcherTuning {
46
+ maxConcurrentWorkers?: number;
47
+ maxWakesPerThread?: number;
48
+ wakeWindowMs?: number;
49
+ wakeCoalesceMs?: number;
50
+ accountSyncIntervalMs?: number;
51
+ }
52
+ declare function defaultDispatcherConfigPath(): string;
53
+ /**
54
+ * Resolve final tuning values by merging the three precedence layers:
55
+ * explicit args > env vars > file > defaults (left to the consumer).
56
+ *
57
+ * Returns ONLY the keys that were explicitly set — the caller passes
58
+ * the result through to the Dispatcher constructor, whose defaults
59
+ * fill in anything still undefined.
60
+ */
61
+ declare function resolveDispatcherTuning(opts?: {
62
+ explicit?: DispatcherTuning;
63
+ env?: NodeJS.ProcessEnv;
64
+ configPath?: string;
65
+ }): DispatcherTuning;
66
+ /**
67
+ * Persist the operator's preferences to ~/.agenticmail/dispatcher.json
68
+ * atomically (.tmp + rename) so a power outage mid-write never produces
69
+ * a half-written config. Only writes the keys that are explicitly set
70
+ * in the patch — preserves keys the user already configured.
71
+ *
72
+ * Returns the resulting on-disk shape so the caller can echo it back.
73
+ */
74
+ declare function writeDispatcherTuning(patch: DispatcherTuning, configPath?: string): DispatcherTuning & {
75
+ version: number;
76
+ updatedAtMs: number;
77
+ };
78
+
79
+ export { type DispatcherTuning, defaultDispatcherConfigPath, resolveDispatcherTuning, writeDispatcherTuning };
@@ -0,0 +1,10 @@
1
+ import {
2
+ defaultDispatcherConfigPath,
3
+ resolveDispatcherTuning,
4
+ writeDispatcherTuning
5
+ } from "./chunk-B5JDOV32.js";
6
+ export {
7
+ defaultDispatcherConfigPath,
8
+ resolveDispatcherTuning,
9
+ writeDispatcherTuning
10
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Dispatcher
3
- } from "./chunk-JOK76WRC.js";
4
- import "./chunk-4VQP57SO.js";
3
+ } from "./chunk-APRMWXZB.js";
4
+ import "./chunk-WTAITYZR.js";
5
5
  export {
6
6
  Dispatcher
7
7
  };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  createIntegrationRoutes
3
- } from "./chunk-TV7VG7YM.js";
4
- import "./chunk-2RVJ2Q3W.js";
5
- import "./chunk-OUYPF4ER.js";
3
+ } from "./chunk-THUNXHMQ.js";
4
+ import "./chunk-KB2EHQW7.js";
5
+ import "./chunk-BIP2TBJ5.js";
6
+ import "./chunk-TSLDSGOD.js";
6
7
  import "./chunk-LO5EQSQA.js";
7
- import "./chunk-Q5BA2J2C.js";
8
8
  import "./chunk-US5FT2UB.js";
9
- import "./chunk-4VQP57SO.js";
9
+ import "./chunk-WTAITYZR.js";
10
10
  export {
11
11
  createIntegrationRoutes
12
12
  };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import { A as AgenticMailAccount } from './config-CXW3gXFC.js';
5
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
+ export { DispatcherTuning, defaultDispatcherConfigPath, resolveDispatcherTuning, writeDispatcherTuning } from './dispatcher-tuning.js';
8
9
  import 'express';
9
10
 
10
11
  /**
package/dist/index.js CHANGED
@@ -1,20 +1,25 @@
1
+ import {
2
+ defaultDispatcherConfigPath,
3
+ resolveDispatcherTuning,
4
+ writeDispatcherTuning
5
+ } from "./chunk-B5JDOV32.js";
1
6
  import {
2
7
  Dispatcher,
3
8
  loadPersonaForAgent
4
- } from "./chunk-JOK76WRC.js";
9
+ } from "./chunk-APRMWXZB.js";
5
10
  import {
6
11
  createIntegrationRoutes
7
- } from "./chunk-TV7VG7YM.js";
12
+ } from "./chunk-THUNXHMQ.js";
13
+ import {
14
+ status
15
+ } from "./chunk-KB2EHQW7.js";
8
16
  import {
9
17
  uninstall
10
- } from "./chunk-2RVJ2Q3W.js";
18
+ } from "./chunk-BIP2TBJ5.js";
11
19
  import {
12
20
  install
13
- } from "./chunk-OUYPF4ER.js";
21
+ } from "./chunk-TSLDSGOD.js";
14
22
  import "./chunk-LO5EQSQA.js";
15
- import {
16
- status
17
- } from "./chunk-Q5BA2J2C.js";
18
23
  import "./chunk-US5FT2UB.js";
19
24
  import {
20
25
  AgenticMailApiError,
@@ -27,13 +32,14 @@ import {
27
32
  renderPersonaBody,
28
33
  renderSubagentMarkdown,
29
34
  resolveConfig
30
- } from "./chunk-4VQP57SO.js";
35
+ } from "./chunk-WTAITYZR.js";
31
36
  export {
32
37
  AgenticMailApiError,
33
38
  Dispatcher,
34
39
  MANAGED_BY_MARKER,
35
40
  checkApiHealth,
36
41
  createIntegrationRoutes,
42
+ defaultDispatcherConfigPath,
37
43
  deleteAccount,
38
44
  ensureAccount,
39
45
  getAccountByName,
@@ -43,6 +49,8 @@ export {
43
49
  renderPersonaBody,
44
50
  renderSubagentMarkdown,
45
51
  resolveConfig,
52
+ resolveDispatcherTuning,
46
53
  status,
47
- uninstall
54
+ uninstall,
55
+ writeDispatcherTuning
48
56
  };
package/dist/install.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  install,
3
3
  selectExposableAgents
4
- } from "./chunk-OUYPF4ER.js";
4
+ } from "./chunk-TSLDSGOD.js";
5
5
  import "./chunk-LO5EQSQA.js";
6
6
  import "./chunk-US5FT2UB.js";
7
- import "./chunk-4VQP57SO.js";
7
+ import "./chunk-WTAITYZR.js";
8
8
  export {
9
9
  install,
10
10
  selectExposableAgents
package/dist/status.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  status
3
- } from "./chunk-Q5BA2J2C.js";
3
+ } from "./chunk-KB2EHQW7.js";
4
4
  import "./chunk-US5FT2UB.js";
5
- import "./chunk-4VQP57SO.js";
5
+ import "./chunk-WTAITYZR.js";
6
6
  export {
7
7
  status
8
8
  };
package/dist/uninstall.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  uninstall
3
- } from "./chunk-2RVJ2Q3W.js";
3
+ } from "./chunk-BIP2TBJ5.js";
4
4
  import "./chunk-LO5EQSQA.js";
5
5
  import "./chunk-US5FT2UB.js";
6
- import "./chunk-4VQP57SO.js";
6
+ import "./chunk-WTAITYZR.js";
7
7
  export {
8
8
  uninstall
9
9
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/claudecode",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
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",
@@ -41,13 +41,13 @@
41
41
  "LICENSE"
42
42
  ],
43
43
  "scripts": {
44
- "build": "tsup src/index.ts src/cli.ts src/install.ts src/uninstall.ts src/status.ts src/http-routes.ts src/dispatcher.ts src/dispatcher-bin.ts src/mail-hook.ts --format esm --dts --clean",
44
+ "build": "tsup src/index.ts src/cli.ts src/install.ts src/uninstall.ts src/status.ts src/http-routes.ts src/dispatcher.ts src/dispatcher-bin.ts src/dispatcher-tuning.ts src/mail-hook.ts --format esm --dts --clean",
45
45
  "test": "vitest run --passWithNoTests",
46
46
  "preuninstall": "node scripts/uninstall.mjs",
47
47
  "prepublishOnly": "npm run build"
48
48
  },
49
49
  "dependencies": {
50
- "@agenticmail/core": "^0.9.2",
50
+ "@agenticmail/core": "^0.9.3",
51
51
  "@agenticmail/mcp": "^0.9.2",
52
52
  "@anthropic-ai/claude-agent-sdk": "^0.2.140"
53
53
  },