@agenticmail/claudecode 0.2.18 → 0.2.20

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.
@@ -273,6 +273,35 @@ function renderPersonaBody(input) {
273
273
  "",
274
274
  `**Closing a thread.** When the work is genuinely done and no more contributions are needed, send a wrap-up reply with one of these markers in the subject: \`[FINAL]\`, \`[DONE]\`, \`[CLOSED]\`, or \`[WRAP]\` PAIRED with \`wake: []\`. The marker tells the dispatcher to stop waking workers; the empty wake confirms you mean it. Use this when YOU are the one signing off the work, not as a routine ack. ANY OTHER use of \`wake: []\` is a bug.`,
275
275
  "",
276
+ `**Handing off to the host bridge (\`claudecode\` / \`codex\`) \u2014 read this carefully too.** The host bridge inboxes belong to the HUMAN OPERATOR's host session, not to an automated worker. The dispatcher DOES NOT spawn worker turns for them \u2014 they're surfaced to the operator via host-session hooks (Stop / UserPromptSubmit) when the operator's CLI is actively running. If no host session is open, mail to the bridge sits unread.`,
277
+ "",
278
+ `Practical implications:`,
279
+ ` \xB7 DO NOT use bridge handoffs as a default "punt the baton when you don't know what to do" move. The chain dies if the operator isn't watching.`,
280
+ ` \xB7 BEFORE handing off to the bridge, ask: is there a teammate (another sub-agent) who could decide this instead? If yes, route to them and CC the bridge as an FYI observer (do not name the bridge in \`wake\`).`,
281
+ ` \xB7 ONLY hand off to the bridge when you genuinely need the operator's judgment \u2014 billing-side rate limits, scope changes, architectural calls only the human can make, ambiguous spec questions where no sub-agent has authority. Treat it like paging a human at 2am: legitimate sometimes, rude often.`,
282
+ ` \xB7 WHEN you must escalate, mark the subject with \`[NEEDS OPERATOR]\` or \`[BLOCKED]\` so the operator's notification feed surfaces it. State the exact question + the options + your recommendation in the body. Don't make them re-read the thread.`,
283
+ ` \xB7 You can name the bridge in \`wake: ["claudecode"]\` / \`wake: ["codex"]\` to signal intent \u2014 the dispatcher still won't spawn a worker (bridges are filtered), but the web UI's notification sound + system event still fires so an attentive operator hears it.`,
284
+ "",
285
+ `Example \u2014 correct operator-escalation:`,
286
+ "",
287
+ "```",
288
+ `${tool("reply_email")}({ uid: 42, replyAll: true, _account: "${agent.name}",`,
289
+ ` subject: "[NEEDS OPERATOR] Slice 4 \u2014 Stripe vs Lemon Squeezy",`,
290
+ ` text: "Team: I've prototyped both. Stripe = better SDK, more compliance work. Lemon = faster ship, 5% take.\\nThis is a business call, not a code call. Operator \u2014 your pick?\\n\\nMy recommendation: Lemon for v0, swap later.",`,
291
+ ` wake: ["claudecode"] }) // signals intent; doesn't spawn a worker`,
292
+ "```",
293
+ "",
294
+ `Example \u2014 WRONG (this is how threads die):`,
295
+ "",
296
+ "```",
297
+ `// You finished your slice and don't know who's next. DO NOT do this:`,
298
+ `${tool("reply_email")}({ uid: 42, replyAll: true, _account: "${agent.name}",`,
299
+ ` text: "Shipped my slice. Claudecode \u2014 over to you for next steps.",`,
300
+ ` wake: ["claudecode"] })`,
301
+ `// Bridge sits unread until the operator notices.`,
302
+ `// Instead: bounce the baton to a teammate who can act, e.g. wake: ["vesper"].`,
303
+ "```",
304
+ "",
276
305
  `**When to use \`${tool("call_agent")}\` instead:** only when you need ONE structured answer from ONE teammate, inline in your current turn \u2014 e.g. "give me a JSON list of X". For multi-step / multi-agent work, the thread pattern above is the right primitive.`,
277
306
  "",
278
307
  `On-demand (via invoke) examples \u2014 anything NOT in the pre-loaded list:`,
@@ -4,7 +4,7 @@ import {
4
4
  listPendingTasksForAgent,
5
5
  renderPersonaBody,
6
6
  resolveConfig
7
- } from "./chunk-O7S3FYJT.js";
7
+ } from "./chunk-7FQNH2YO.js";
8
8
 
9
9
  // src/persona-loader.ts
10
10
  import { existsSync, readFileSync } from "fs";
@@ -160,7 +160,95 @@ var DispatcherState = class {
160
160
  import { mkdirSync as mkdirSync2, createWriteStream, rmSync } from "fs";
161
161
  import { join as join3 } from "path";
162
162
  import { homedir as homedir2 } from "os";
163
- import { ThreadCache, AgentMemoryStore, threadIdFor, normalizeSubject } from "@agenticmail/core";
163
+ import { ThreadCache, AgentMemoryStore, threadIdFor, normalizeSubject, loadHostSession, forgetHostSession } from "@agenticmail/core";
164
+
165
+ // src/bridge-wake.ts
166
+ async function resumeBridgeSession(query, input, log) {
167
+ const startMs = Date.now();
168
+ const timeoutMs = input.timeoutMs ?? 5 * 60 * 1e3;
169
+ let result = { ok: false };
170
+ try {
171
+ log("info", `[bridge-wake] resuming Claude Code session ${input.sessionId.slice(0, 8)}\u2026 for ${input.bridge.name}`);
172
+ const opts = {
173
+ resume: input.sessionId,
174
+ cwd: input.cwd,
175
+ mcpServers: {
176
+ agenticmail: {
177
+ command: "agenticmail-mcp",
178
+ args: [],
179
+ env: input.mcpEnv
180
+ }
181
+ },
182
+ permissionMode: "bypassPermissions",
183
+ // Headless mode — no stdin tty.
184
+ includePartialMessages: false
185
+ };
186
+ let assistantText = "";
187
+ let timedOut = false;
188
+ const timeoutHandle = setTimeout(() => {
189
+ timedOut = true;
190
+ }, timeoutMs);
191
+ timeoutHandle.unref?.();
192
+ try {
193
+ const stream = query({ prompt: input.prompt, options: opts });
194
+ for await (const event of stream) {
195
+ if (timedOut) break;
196
+ const e = event;
197
+ if (e.type === "assistant" && Array.isArray(e.message?.content)) {
198
+ for (const block of e.message.content) {
199
+ const b = block;
200
+ if (b.type === "text" && typeof b.text === "string") {
201
+ assistantText = b.text;
202
+ }
203
+ }
204
+ }
205
+ }
206
+ } finally {
207
+ clearTimeout(timeoutHandle);
208
+ }
209
+ if (timedOut) {
210
+ log("warn", `[bridge-wake] timeout after ${timeoutMs}ms \u2014 bridge wake gave up`);
211
+ return { ok: false, error: "timeout", durationMs: Date.now() - startMs };
212
+ }
213
+ result = { ok: true, text: assistantText, durationMs: Date.now() - startMs };
214
+ log("info", `[bridge-wake] resumed session ok (${result.durationMs}ms, ${assistantText.length} chars)`);
215
+ return result;
216
+ } catch (err) {
217
+ const msg = err?.message ?? String(err);
218
+ const m = msg.toLowerCase();
219
+ const expired = m.includes("session not found") || m.includes("invalid session") || m.includes("session expired") || m.includes("no such session") || m.includes("unknown session");
220
+ const sdkMissing = m.includes("cannot find module") || m.includes("could not be found") || m.includes("command not found");
221
+ const error = expired ? "session-expired" : sdkMissing ? "sdk-missing" : "other";
222
+ log("warn", `[bridge-wake] resume failed (${error}): ${msg.slice(0, 200)}`);
223
+ return { ok: false, error, errorMessage: msg, durationMs: Date.now() - startMs };
224
+ }
225
+ }
226
+ function composeBridgeWakePrompt(args) {
227
+ const subject = args.subject ?? "(no subject)";
228
+ const from = args.from ?? "unknown";
229
+ const preview = (args.preview ?? "").slice(0, 600);
230
+ return [
231
+ `\u{1F380} Bridge mail arrived \u2014 headless wake.`,
232
+ "",
233
+ `You are being resumed against your last session because new mail landed in your bridge inbox (${args.bridgeName}@localhost) and you weren't actively at the keyboard.`,
234
+ "",
235
+ `Trigger:`,
236
+ ` UID: ${args.uid}`,
237
+ ` From: ${from}`,
238
+ ` Subject: ${subject}`,
239
+ ` Preview: ${preview}`,
240
+ "",
241
+ `Read it with mcp__agenticmail__read_email({ uid: ${args.uid} }) and decide:`,
242
+ ` \xB7 Does it need a reply from YOU (the operator's session)? Reply via mcp__agenticmail__reply_email.`,
243
+ ` \xB7 Does it need a teammate to act? Forward / re-route by replying with wake: ["<teammate>"].`,
244
+ ` \xB7 Is it [NEEDS OPERATOR] / [BLOCKED]? Then it's actually for the human \u2014 mark it unread, and the operator will see it on their next keystroke.`,
245
+ ` \xB7 Is it FYI noise? mark_read and exit.`,
246
+ "",
247
+ `Keep this turn SHORT. You're being resumed to handle ONE piece of mail, not to continue the prior conversation.`
248
+ ].join("\n");
249
+ }
250
+
251
+ // src/dispatcher.ts
164
252
  function extractSubject(event) {
165
253
  if (typeof event.subject === "string") return event.subject;
166
254
  if (event.message && typeof event.message.subject === "string") return event.message.subject;
@@ -761,6 +849,14 @@ var Dispatcher = class _Dispatcher {
761
849
  /** Public for tests — directly hand an event to the routing path. */
762
850
  async handleEvent(account, event) {
763
851
  if (this.stopped) return;
852
+ if (event.type === "new" && typeof event.uid === "number" && account.name.toLowerCase() === this.cfg.bridgeAgentName.toLowerCase()) {
853
+ const ch = this.channels.get(account.id);
854
+ if (ch?.seenUids.has(event.uid)) return;
855
+ if (ch) rememberBounded(ch.seenUids, event.uid);
856
+ this.state.markSeen(account.id, event.uid);
857
+ void this.handleBridgeMail(account, event);
858
+ return;
859
+ }
764
860
  if (event.type === "new" && typeof event.uid === "number") {
765
861
  const ch = this.channels.get(account.id);
766
862
  if (ch?.seenUids.has(event.uid)) return;
@@ -860,7 +956,7 @@ var Dispatcher = class _Dispatcher {
860
956
  shouldWatch(account) {
861
957
  const bridgeName = this.cfg.bridgeAgentName.toLowerCase();
862
958
  const meta = account.metadata;
863
- if (account.name.toLowerCase() === bridgeName) return false;
959
+ if (account.name.toLowerCase() === bridgeName) return true;
864
960
  if (account.role === "bridge") return false;
865
961
  if (meta && meta.bridge === true) return false;
866
962
  if (meta && typeof meta.host === "string" && meta.host.length > 0) {
@@ -1333,6 +1429,107 @@ var Dispatcher = class _Dispatcher {
1333
1429
  sections.push("this thread will load that memory into context for you.");
1334
1430
  return sections.join("\n");
1335
1431
  }
1432
+ /**
1433
+ * Handle mail that lands in the host's OWN bridge inbox.
1434
+ *
1435
+ * Unlike normal sub-agent wakes (which spawn a fresh worker turn),
1436
+ * bridge wakes resume the operator's last interactive session via
1437
+ * the Claude Agent SDK's `resume` option — keeping the operator's
1438
+ * context intact and avoiding a duplicate "second Claude trying to
1439
+ * be Claude Code". When no fresh session is available (operator
1440
+ * hasn't run `claude` in >24h, or `~/.agenticmail/host-sessions.json`
1441
+ * was wiped), the dispatcher emits an `urgent` system event so the
1442
+ * web UI's notification surface + any future SMS escalation hook
1443
+ * can pick it up. See packages/claudecode/src/bridge-wake.ts.
1444
+ *
1445
+ * Two short-circuits:
1446
+ * 1. If lastSeenMs is within 30s, the operator is actively at the
1447
+ * keyboard right now — their own UserPromptSubmit / Stop hook
1448
+ * will surface the mail on their next keystroke. We skip the
1449
+ * resume entirely to avoid token waste.
1450
+ * 2. If a previous bridge-wake for this same UID is already
1451
+ * in-flight (same key in `inFlightBridgeWakes`), skip — IMAP
1452
+ * IDLE replays + SSE reconnects can fire the same event
1453
+ * twice and we don't want to double-resume.
1454
+ */
1455
+ inFlightBridgeWakes = /* @__PURE__ */ new Set();
1456
+ async handleBridgeMail(account, event) {
1457
+ if (typeof event.uid !== "number") return;
1458
+ if (this.inFlightBridgeWakes.has(event.uid)) return;
1459
+ this.inFlightBridgeWakes.add(event.uid);
1460
+ const startMs = Date.now();
1461
+ const subject = extractSubject(event);
1462
+ const from = extractFrom(event);
1463
+ const preview = event.preview ?? event.message?.preview ?? "";
1464
+ try {
1465
+ const saved = loadHostSession("claudecode");
1466
+ if (saved && Date.now() - saved.lastSeenMs < 3e4) {
1467
+ this.log("info", `[bridge-wake] skip \u2014 operator is live (lastSeen=${Date.now() - saved.lastSeenMs}ms ago); their hook will surface uid=${event.uid}`);
1468
+ this.postActivity("/dispatcher/bridge-skipped", {
1469
+ uid: event.uid,
1470
+ agentName: account.name,
1471
+ reason: "operator-live"
1472
+ });
1473
+ return;
1474
+ }
1475
+ if (!saved) {
1476
+ this.log("warn", `[bridge-wake] no fresh Claude Code session recorded \u2014 bridge mail uid=${event.uid} cannot resume. Escalating via system-event.`);
1477
+ this.postActivity("/dispatcher/bridge-escalation", {
1478
+ uid: event.uid,
1479
+ agentName: account.name,
1480
+ subject,
1481
+ from,
1482
+ preview,
1483
+ reason: "no-fresh-session"
1484
+ });
1485
+ return;
1486
+ }
1487
+ const mcpEnv = await this.buildMcpEnv();
1488
+ const prompt = composeBridgeWakePrompt({
1489
+ bridgeName: account.name,
1490
+ uid: event.uid,
1491
+ subject,
1492
+ from,
1493
+ preview
1494
+ });
1495
+ const result = await resumeBridgeSession(this.query, {
1496
+ bridge: account,
1497
+ sessionId: saved.sessionId,
1498
+ cwd: saved.workspace,
1499
+ prompt,
1500
+ mcpEnv
1501
+ }, this.log);
1502
+ if (result.ok) {
1503
+ this.postActivity("/dispatcher/bridge-resumed", {
1504
+ uid: event.uid,
1505
+ agentName: account.name,
1506
+ subject,
1507
+ from,
1508
+ durationMs: result.durationMs,
1509
+ resultPreview: result.text?.slice(0, 240)
1510
+ });
1511
+ } else {
1512
+ if (result.error === "session-expired") {
1513
+ this.log("warn", `[bridge-wake] session expired; forgetting and escalating uid=${event.uid}`);
1514
+ forgetHostSession("claudecode");
1515
+ }
1516
+ this.postActivity("/dispatcher/bridge-escalation", {
1517
+ uid: event.uid,
1518
+ agentName: account.name,
1519
+ subject,
1520
+ from,
1521
+ preview,
1522
+ reason: result.error ?? "resume-failed",
1523
+ errorMessage: result.errorMessage
1524
+ });
1525
+ }
1526
+ } catch (err) {
1527
+ this.log("error", `[bridge-wake] uid=${event.uid} threw: ${err.message}`);
1528
+ } finally {
1529
+ this.inFlightBridgeWakes.delete(event.uid);
1530
+ this.log("info", `[bridge-wake] finished uid=${event.uid} in ${Date.now() - startMs}ms`);
1531
+ }
1532
+ }
1336
1533
  /** Acquire a concurrency slot, run a worker, release the slot. */
1337
1534
  async spawnWorker(account, prompt, ctx) {
1338
1535
  const releaseAgentLock = await this.acquireAgentSerial(account.id);
@@ -7,7 +7,7 @@ import {
7
7
  MANAGED_BY_MARKER,
8
8
  getAccountByName,
9
9
  resolveConfig
10
- } from "./chunk-O7S3FYJT.js";
10
+ } from "./chunk-7FQNH2YO.js";
11
11
 
12
12
  // src/status.ts
13
13
  import { existsSync, readFileSync, readdirSync } from "fs";
@@ -14,7 +14,7 @@ import {
14
14
  resolveConfig,
15
15
  setAccountHost,
16
16
  setAccountRole
17
- } from "./chunk-O7S3FYJT.js";
17
+ } from "./chunk-7FQNH2YO.js";
18
18
 
19
19
  // src/install.ts
20
20
  import { existsSync, mkdirSync, readdirSync, writeFileSync, readFileSync, unlinkSync } from "fs";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  status
3
- } from "./chunk-OMUB5FRY.js";
3
+ } from "./chunk-HCZTDR4T.js";
4
4
  import {
5
5
  uninstall
6
- } from "./chunk-7D3D7T34.js";
6
+ } from "./chunk-ZVIW6BL2.js";
7
7
  import {
8
8
  install
9
- } from "./chunk-4NKBITBR.js";
9
+ } from "./chunk-NCV65VKS.js";
10
10
  import {
11
11
  AgenticMailApiError
12
- } from "./chunk-O7S3FYJT.js";
12
+ } from "./chunk-7FQNH2YO.js";
13
13
 
14
14
  // src/http-routes.ts
15
15
  import { Router } from "express";
@@ -10,7 +10,7 @@ import {
10
10
  deleteAccount,
11
11
  getAccountByName,
12
12
  resolveConfig
13
- } from "./chunk-O7S3FYJT.js";
13
+ } from "./chunk-7FQNH2YO.js";
14
14
 
15
15
  // src/uninstall.ts
16
16
  import { existsSync, readdirSync, readFileSync, unlinkSync } from "fs";
package/dist/cli.js CHANGED
@@ -6,13 +6,13 @@ import {
6
6
  } from "./chunk-B5JDOV32.js";
7
7
  import {
8
8
  status
9
- } from "./chunk-OMUB5FRY.js";
9
+ } from "./chunk-HCZTDR4T.js";
10
10
  import {
11
11
  uninstall
12
- } from "./chunk-7D3D7T34.js";
12
+ } from "./chunk-ZVIW6BL2.js";
13
13
  import {
14
14
  install
15
- } from "./chunk-4NKBITBR.js";
15
+ } from "./chunk-NCV65VKS.js";
16
16
  import "./chunk-SWDS64K2.js";
17
17
  import "./chunk-7DGWW72Q.js";
18
18
  import {
@@ -20,7 +20,7 @@ import {
20
20
  listAccounts,
21
21
  resolveConfig,
22
22
  setAccountHost
23
- } from "./chunk-O7S3FYJT.js";
23
+ } from "./chunk-7FQNH2YO.js";
24
24
 
25
25
  // src/cli.ts
26
26
  var GREEN = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-B5JDOV32.js";
5
5
  import {
6
6
  Dispatcher
7
- } from "./chunk-WC5ZK4RG.js";
8
- import "./chunk-O7S3FYJT.js";
7
+ } from "./chunk-ALWSCQFP.js";
8
+ import "./chunk-7FQNH2YO.js";
9
9
 
10
10
  // src/dispatcher-bin.ts
11
11
  async function main() {
@@ -370,6 +370,31 @@ declare class Dispatcher {
370
370
  subject?: string;
371
371
  uid?: number;
372
372
  }, prompt: string): string;
373
+ /**
374
+ * Handle mail that lands in the host's OWN bridge inbox.
375
+ *
376
+ * Unlike normal sub-agent wakes (which spawn a fresh worker turn),
377
+ * bridge wakes resume the operator's last interactive session via
378
+ * the Claude Agent SDK's `resume` option — keeping the operator's
379
+ * context intact and avoiding a duplicate "second Claude trying to
380
+ * be Claude Code". When no fresh session is available (operator
381
+ * hasn't run `claude` in >24h, or `~/.agenticmail/host-sessions.json`
382
+ * was wiped), the dispatcher emits an `urgent` system event so the
383
+ * web UI's notification surface + any future SMS escalation hook
384
+ * can pick it up. See packages/claudecode/src/bridge-wake.ts.
385
+ *
386
+ * Two short-circuits:
387
+ * 1. If lastSeenMs is within 30s, the operator is actively at the
388
+ * keyboard right now — their own UserPromptSubmit / Stop hook
389
+ * will surface the mail on their next keystroke. We skip the
390
+ * resume entirely to avoid token waste.
391
+ * 2. If a previous bridge-wake for this same UID is already
392
+ * in-flight (same key in `inFlightBridgeWakes`), skip — IMAP
393
+ * IDLE replays + SSE reconnects can fire the same event
394
+ * twice and we don't want to double-resume.
395
+ */
396
+ private inFlightBridgeWakes;
397
+ private handleBridgeMail;
373
398
  /** Acquire a concurrency slot, run a worker, release the slot. */
374
399
  private spawnWorker;
375
400
  /**
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Dispatcher
3
- } from "./chunk-WC5ZK4RG.js";
4
- import "./chunk-O7S3FYJT.js";
3
+ } from "./chunk-ALWSCQFP.js";
4
+ import "./chunk-7FQNH2YO.js";
5
5
  export {
6
6
  Dispatcher
7
7
  };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  createIntegrationRoutes
3
- } from "./chunk-EWHWSNAQ.js";
4
- import "./chunk-OMUB5FRY.js";
5
- import "./chunk-7D3D7T34.js";
6
- import "./chunk-4NKBITBR.js";
3
+ } from "./chunk-QXFCUEN3.js";
4
+ import "./chunk-HCZTDR4T.js";
5
+ import "./chunk-ZVIW6BL2.js";
6
+ import "./chunk-NCV65VKS.js";
7
7
  import "./chunk-SWDS64K2.js";
8
8
  import "./chunk-7DGWW72Q.js";
9
- import "./chunk-O7S3FYJT.js";
9
+ import "./chunk-7FQNH2YO.js";
10
10
  export {
11
11
  createIntegrationRoutes
12
12
  };
package/dist/index.js CHANGED
@@ -6,19 +6,19 @@ import {
6
6
  import {
7
7
  Dispatcher,
8
8
  loadPersonaForAgent
9
- } from "./chunk-WC5ZK4RG.js";
9
+ } from "./chunk-ALWSCQFP.js";
10
10
  import {
11
11
  createIntegrationRoutes
12
- } from "./chunk-EWHWSNAQ.js";
12
+ } from "./chunk-QXFCUEN3.js";
13
13
  import {
14
14
  status
15
- } from "./chunk-OMUB5FRY.js";
15
+ } from "./chunk-HCZTDR4T.js";
16
16
  import {
17
17
  uninstall
18
- } from "./chunk-7D3D7T34.js";
18
+ } from "./chunk-ZVIW6BL2.js";
19
19
  import {
20
20
  install
21
- } from "./chunk-4NKBITBR.js";
21
+ } from "./chunk-NCV65VKS.js";
22
22
  import "./chunk-SWDS64K2.js";
23
23
  import "./chunk-7DGWW72Q.js";
24
24
  import {
@@ -32,7 +32,7 @@ import {
32
32
  renderPersonaBody,
33
33
  renderSubagentMarkdown,
34
34
  resolveConfig
35
- } from "./chunk-O7S3FYJT.js";
35
+ } from "./chunk-7FQNH2YO.js";
36
36
  export {
37
37
  AgenticMailApiError,
38
38
  Dispatcher,
package/dist/install.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  install,
3
3
  selectExposableAgents
4
- } from "./chunk-4NKBITBR.js";
4
+ } from "./chunk-NCV65VKS.js";
5
5
  import "./chunk-SWDS64K2.js";
6
6
  import "./chunk-7DGWW72Q.js";
7
- import "./chunk-O7S3FYJT.js";
7
+ import "./chunk-7FQNH2YO.js";
8
8
  export {
9
9
  install,
10
10
  selectExposableAgents
package/dist/mail-hook.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
5
  import { homedir } from "os";
6
6
  import { join, dirname } from "path";
7
+ import { saveHostSession } from "@agenticmail/core";
7
8
  var AGENTICMAIL_DIR = join(homedir(), ".agenticmail");
8
9
  var CONFIG_PATH = join(AGENTICMAIL_DIR, "config.json");
9
10
  var CURSOR_PATH = join(AGENTICMAIL_DIR, "claudecode-hook-cursor.json");
@@ -92,6 +93,12 @@ async function main() {
92
93
  const input = await readStdinJson();
93
94
  const eventName = input?.hook_event_name ?? "UserPromptSubmit";
94
95
  const sessionId = typeof input?.session_id === "string" ? input.session_id : "";
96
+ if (sessionId) {
97
+ try {
98
+ saveHostSession("claudecode", { sessionId, workspace: process.cwd() });
99
+ } catch {
100
+ }
101
+ }
95
102
  if (eventName === "SessionStart") {
96
103
  process.stdout.write(JSON.stringify({
97
104
  hookSpecificOutput: {
package/dist/status.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  status
3
- } from "./chunk-OMUB5FRY.js";
3
+ } from "./chunk-HCZTDR4T.js";
4
4
  import "./chunk-7DGWW72Q.js";
5
- import "./chunk-O7S3FYJT.js";
5
+ import "./chunk-7FQNH2YO.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-7D3D7T34.js";
3
+ } from "./chunk-ZVIW6BL2.js";
4
4
  import "./chunk-SWDS64K2.js";
5
5
  import "./chunk-7DGWW72Q.js";
6
- import "./chunk-O7S3FYJT.js";
6
+ import "./chunk-7FQNH2YO.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.18",
3
+ "version": "0.2.20",
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/core": "^0.9.5",
50
+ "@agenticmail/core": "^0.9.6",
51
51
  "@agenticmail/mcp": "^0.9.7",
52
52
  "@anthropic-ai/claude-agent-sdk": "^0.2.140"
53
53
  },