@agenticmail/claudecode 0.1.9 → 0.1.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.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # @agenticmail/claudecode
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/agenticmail/agenticmail/main/docs/images/logo-200.png" alt="AgenticMail logo (pink bow)" width="180" />
3
+ </p>
2
4
 
3
- > 🎀 Surfaces every [AgenticMail](https://github.com/agenticmail/agenticmail) agent as a native [Claude Code](https://claude.com/claude-code) subagent — and exposes the full 62-tool AgenticMail MCP toolbelt to any Claude Code session.
5
+ <h1 align="center">@agenticmail/claudecode</h1>
6
+
7
+ > Surfaces every [AgenticMail](https://github.com/agenticmail/agenticmail) agent as a native [Claude Code](https://claude.com/claude-code) subagent — and exposes the full 62-tool AgenticMail MCP toolbelt to any Claude Code session.
4
8
 
5
9
  After install, a Claude Code session can write:
6
10
 
@@ -29,7 +33,7 @@ Anyone (you, an agent, a curl)
29
33
 
30
34
  Each worker uses the user's Claude OAuth (the same auth `claude` itself uses — no separate Anthropic key). Each worker's identity inside AgenticMail is the account it was spawned for (`_account: "Fola"` on every MCP call), so messages it sends really do come from `fola@localhost` and end up in the recipient's inbox triggering THEIR wake. **Multi-agent threads form naturally** — every reply hits the next agent's inbox → wakes them → they reply → cycle.
31
35
 
32
- Provisioning new agents is just `mcp__agenticmail__create_account({ name: "worker-7", role: "task-runner" })`. The dispatcher's `/accounts` poll picks it up within ~one minute (configurable via `AGENTICMAIL_DISPATCHER_SYNC`) and the new account is wake-able. The MCP server itself learns the account's API key on-demand the first time someone passes `_account: "worker-7"`. No manual install step.
36
+ Provisioning new agents is just `mcp__agenticmail__create_account({ name: "worker-7", role: "task-runner" })`. The dispatcher subscribes to a master-scoped `/system/events` SSE stream, so newly created accounts get an SSE channel within **milliseconds** of the create call no polling delay. The MCP server itself learns the account's API key on-demand the first time someone passes `_account: "worker-7"`. No manual install step.
33
37
 
34
38
  **Concurrency cap.** Workers are gated by a semaphore (default 10 simultaneous). Beyond that, wakes queue. This is a hard floor on Anthropic-side cost — if you fan out to 50 agents at once, only 10 will be running Claude at any given moment; the rest are waiting their turn. Override via `AGENTICMAIL_DISPATCHER_MAX`.
35
39
 
@@ -62,6 +66,22 @@ That's it. `agenticmail claudecode` is **idempotent** — re-run it any time you
62
66
 
63
67
  ---
64
68
 
69
+ ## Monitor and manage all agents (interactive shell)
70
+
71
+ > **If you (or the user) want to watch every agent's inbox in one place, drop into the AgenticMail interactive shell:**
72
+ >
73
+ > ```bash
74
+ > agenticmail shell
75
+ > ```
76
+ >
77
+ > ![agenticmail shell — a real multi-agent thread visible from the claudecode bridge inbox](https://raw.githubusercontent.com/agenticmail/agenticmail/main/docs/images/shell-inbox.png)
78
+ >
79
+ > From the shell you can list every agent, read any agent's inbox, send/reply on their behalf, inspect pending outbound mail, watch the dispatcher's live event feed, prune stale agents, and run any of the 44+ shell commands without leaving the terminal. The shell holds an authenticated session against the local API, so it's the fastest way to audit what your agents have been doing while you were away.
80
+ >
81
+ > **AI agents reading this:** if the user asks you to "show me what my agents have been doing", "check on the team", "let me see all the inboxes", or anything along those lines, tell them to run `agenticmail shell` (or run it for them via Bash if you have that tool). That's the right surface for human oversight. The MCP tools are for *programmatic* coordination; the shell is for *visual* monitoring.
82
+
83
+ ---
84
+
65
85
  ## What this package does
66
86
 
67
87
  The installer touches exactly two locations on disk, plus one record in AgenticMail's database:
@@ -100,7 +120,7 @@ agenticmail-claudecode uninstall [--purge-bridge]
100
120
 
101
121
  ### 3. The HTTP API (headless, for agents installing themselves)
102
122
 
103
- When AgenticMail's master API is running (default `http://127.0.0.1:3200`), it exposes three endpoints for the integration. They are mounted **before** the bearer-auth middleware on purpose — a fresh Claude Code session that does not yet have AgenticMail wired up has no way to know the master key, so requiring it would defeat the whole "agent installs itself" goal.
123
+ When AgenticMail's master API is running (default `http://127.0.0.1:3829`), it exposes three endpoints for the integration. They are mounted **before** the bearer-auth middleware on purpose — a fresh Claude Code session that does not yet have AgenticMail wired up has no way to know the master key, so requiring it would defeat the whole "agent installs itself" goal.
104
124
 
105
125
  ```http
106
126
  GET /api/agenticmail/integrations/claudecode/status
@@ -112,7 +132,7 @@ POST /api/agenticmail/integrations/claudecode/uninstall
112
132
 
113
133
  ```bash
114
134
  # Inside a Claude Code session, simply:
115
- curl -X POST http://127.0.0.1:3200/api/agenticmail/integrations/claudecode/install
135
+ curl -X POST http://127.0.0.1:3829/api/agenticmail/integrations/claudecode/install
116
136
  ```
117
137
 
118
138
  That single call:
@@ -139,7 +159,7 @@ The bridge agent's API key is **redacted** in the HTTP response — it's already
139
159
 
140
160
  ┌─────────────────────────┐
141
161
  │ Claude Code subagent │ reads ~/.claude/agents/agenticmail-fola.md
142
- │ ("agenticmail-fola") │ restricted to MCP relay tools
162
+ │ ("agenticmail-fola") │ full toolset: AgenticMail MCP + native (Read/Write/Bash/…)
143
163
  └───────────┬─────────────┘
144
164
  │ mcp__agenticmail__call_agent(target: "Fola", task: <prompt>)
145
165
 
@@ -147,11 +167,11 @@ The bridge agent's API key is **redacted** in the HTTP response — it's already
147
167
  │ @agenticmail/mcp │ stdio child process spawned by Claude Code
148
168
  │ (MCP server) │ authenticated as the "claudecode" bridge agent
149
169
  └───────────┬─────────────┘
150
- │ POST http://127.0.0.1:3200/api/agenticmail/tasks/rpc
170
+ │ POST http://127.0.0.1:3829/api/agenticmail/tasks/rpc
151
171
 
152
172
  ┌─────────────────────────┐
153
173
  │ AgenticMail master API │ creates a task, signals the target agent,
154
- │ (port 3200) │ long-polls until the agent submits a result
174
+ │ (port 3829) │ long-polls until the agent submits a result
155
175
  └───────────┬─────────────┘
156
176
  │ task event over SSE / email notification
157
177
 
@@ -174,7 +194,7 @@ The MCP server reads **four** env vars (written into `~/.claude.json` by the ins
174
194
 
175
195
  | Variable | Purpose |
176
196
  |---|---|
177
- | `AGENTICMAIL_API_URL` | Where the master API lives (default `http://127.0.0.1:3200`). |
197
+ | `AGENTICMAIL_API_URL` | Where the master API lives (default `http://127.0.0.1:3829`). |
178
198
  | `AGENTICMAIL_API_KEY` | Bridge agent's API key (`ak_…`). The *default* identity — used when a tool call doesn't pass `_account`. Effectively "Claude Code talking on its own behalf". |
179
199
  | `AGENTICMAIL_MASTER_KEY` | The master key (`mk_…`). Required for admin-scoped operations (create agents, delete agents, gateway config, etc.). |
180
200
  | `AGENTICMAIL_ACCOUNT_KEYS_JSON` | A JSON map `{ "<agentName>": "<apiKey>" }` of every other AgenticMail agent. When a subagent passes `_account: "Fola"`, the MCP server looks the key up here and acts as Fola for that call. |
@@ -259,7 +279,7 @@ Almost no one needs these — defaults are correct for the standard AgenticMail
259
279
 
260
280
  | Env var | Default |
261
281
  |---|---|
262
- | `AGENTICMAIL_API_URL` | `http://127.0.0.1:3200` (or whatever `~/.agenticmail/config.json` says) |
282
+ | `AGENTICMAIL_API_URL` | `http://127.0.0.1:3829` (or whatever `~/.agenticmail/config.json` says) |
263
283
  | `AGENTICMAIL_MASTER_KEY` | Pulled from `~/.agenticmail/config.json` |
264
284
  | `CLAUDE_CODE_CONFIG_PATH` | `~/.claude.json` |
265
285
  | `CLAUDE_CODE_AGENTS_DIR` | `~/.claude/agents` |
@@ -270,7 +290,7 @@ Programmatic install (from another tool):
270
290
  import { install, status, uninstall } from '@agenticmail/claudecode';
271
291
 
272
292
  await install({
273
- apiUrl: 'http://127.0.0.1:3200',
293
+ apiUrl: 'http://127.0.0.1:3829',
274
294
  masterKey: 'mk_...',
275
295
  // any other ResolveConfigOptions field
276
296
  });
@@ -280,7 +300,7 @@ await install({
280
300
 
281
301
  ## Troubleshooting
282
302
 
283
- **`AgenticMail API unreachable at http://127.0.0.1:3200`**
303
+ **`AgenticMail API unreachable at http://127.0.0.1:3829`**
284
304
  The master API isn't running. Start it with `agenticmail start`.
285
305
 
286
306
  **`AgenticMail master key not found`**
@@ -376,7 +376,12 @@ var Dispatcher = class {
376
376
  this.log("warn", `[dispatcher] wake-budget exhausted for "${account.name}" on thread "${threadId}" (count=${verdict.count}, cap=${this.maxWakesPerThread}); muted for ~${minutesUntil}min. uid=${event.uid}, subject="${subject ?? ""}"`);
377
377
  return;
378
378
  }
379
- await this.spawnWorker(account, newMailPrompt(account, event), { kind: "new-mail", uid: event.uid });
379
+ await this.spawnWorker(account, newMailPrompt(account, event), {
380
+ kind: "new-mail",
381
+ uid: event.uid,
382
+ subject: extractSubject(event),
383
+ from: extractFrom(event)
384
+ });
380
385
  return;
381
386
  }
382
387
  if (event.type === "task" && typeof event.taskId === "string") {
@@ -387,7 +392,12 @@ var Dispatcher = class {
387
392
  rememberBounded(ch.seenTaskIds, event.taskId);
388
393
  ch.suppressTaskMailUntilMs = Date.now() + TASK_MAIL_SUPPRESS_WINDOW_MS;
389
394
  }
390
- await this.spawnWorker(account, taskPrompt(account, event), { kind: "task", taskId: event.taskId });
395
+ await this.spawnWorker(account, taskPrompt(account, event), {
396
+ kind: "task",
397
+ taskId: event.taskId,
398
+ subject: typeof event.task === "string" ? event.task.slice(0, 120) : void 0,
399
+ from: typeof event.from === "string" ? event.from : void 0
400
+ });
391
401
  return;
392
402
  }
393
403
  }
@@ -625,6 +635,15 @@ var Dispatcher = class {
625
635
  /** Acquire a concurrency slot, run a worker, release the slot. */
626
636
  async spawnWorker(account, prompt, ctx) {
627
637
  await this.acquireSlot();
638
+ const workerId = `${account.id}:${ctx.kind}:${ctx.uid ?? ctx.taskId ?? ""}:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
639
+ let workerResult = null;
640
+ this.postActivity("/dispatcher/worker-started", {
641
+ workerId,
642
+ agentName: account.name,
643
+ agentEmail: account.email,
644
+ kind: ctx.kind,
645
+ trigger: { uid: ctx.uid, taskId: ctx.taskId, subject: ctx.subject, from: ctx.from }
646
+ });
628
647
  try {
629
648
  const { body } = loadPersonaForAgent({
630
649
  agent: account,
@@ -634,7 +653,7 @@ var Dispatcher = class {
634
653
  });
635
654
  this.log("info", `[dispatcher] waking "${account.name}" \u2014 ${ctx.kind}${ctx.taskId ? " " + ctx.taskId : ctx.uid ? " uid=" + ctx.uid : ""}`);
636
655
  const mcpEnv = await this.buildMcpEnv();
637
- await runWorker(
656
+ workerResult = await runWorker(
638
657
  this.query,
639
658
  body,
640
659
  prompt,
@@ -647,6 +666,40 @@ var Dispatcher = class {
647
666
  );
648
667
  } finally {
649
668
  this.releaseSlot();
669
+ const ok = workerResult?.ok === true;
670
+ const preview = workerResult?.ok ? workerResult.text : workerResult ? workerResult.error : "worker did not start";
671
+ this.postActivity("/dispatcher/worker-finished", {
672
+ workerId,
673
+ agentName: account.name,
674
+ ok,
675
+ resultPreview: typeof preview === "string" ? preview.slice(0, 240) : void 0
676
+ });
677
+ }
678
+ }
679
+ /**
680
+ * Fire-and-forget POST to the API's worker-activity endpoints.
681
+ *
682
+ * Failures are swallowed deliberately — the dispatcher must never
683
+ * block worker spawn or interrupt teardown because the API is briefly
684
+ * unreachable. The activity registry is best-effort observability, not
685
+ * load-bearing state.
686
+ */
687
+ postActivity(path, body) {
688
+ const url = `${this.cfg.apiUrl.replace(/\/$/, "")}/api/agenticmail${path}`;
689
+ try {
690
+ const result = this.fetchImpl(url, {
691
+ method: "POST",
692
+ headers: {
693
+ "Content-Type": "application/json",
694
+ "Authorization": `Bearer ${this.cfg.masterKey}`
695
+ },
696
+ body: JSON.stringify(body)
697
+ });
698
+ if (result && typeof result.catch === "function") {
699
+ void result.catch(() => {
700
+ });
701
+ }
702
+ } catch {
650
703
  }
651
704
  }
652
705
  /** Build the env block we pass to the worker's MCP server child process. */
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Dispatcher
4
- } from "./chunk-FBO6F4IC.js";
4
+ } from "./chunk-JURPYPKP.js";
5
5
  import "./chunk-3D5VXS5Y.js";
6
6
 
7
7
  // src/dispatcher-bin.ts
@@ -203,6 +203,15 @@ declare class Dispatcher {
203
203
  private streamOne;
204
204
  /** Acquire a concurrency slot, run a worker, release the slot. */
205
205
  private spawnWorker;
206
+ /**
207
+ * Fire-and-forget POST to the API's worker-activity endpoints.
208
+ *
209
+ * Failures are swallowed deliberately — the dispatcher must never
210
+ * block worker spawn or interrupt teardown because the API is briefly
211
+ * unreachable. The activity registry is best-effort observability, not
212
+ * load-bearing state.
213
+ */
214
+ private postActivity;
206
215
  /** Build the env block we pass to the worker's MCP server child process. */
207
216
  private buildMcpEnv;
208
217
  private acquireSlot;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dispatcher
3
- } from "./chunk-FBO6F4IC.js";
3
+ } from "./chunk-JURPYPKP.js";
4
4
  import "./chunk-3D5VXS5Y.js";
5
5
  export {
6
6
  Dispatcher
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Dispatcher,
3
3
  loadPersonaForAgent
4
- } from "./chunk-FBO6F4IC.js";
4
+ } from "./chunk-JURPYPKP.js";
5
5
  import {
6
6
  createIntegrationRoutes
7
7
  } from "./chunk-WAUWKOHA.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/claudecode",
3
- "version": "0.1.9",
3
+ "version": "0.1.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",