@ctliz/agent-intercom-claude 0.13.1 → 0.14.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.
@@ -1847,6 +1847,12 @@ function loadConfig() {
1847
1847
  // claude/inbox.ts
1848
1848
  import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
1849
1849
  import { dirname as dirname3, join as join5 } from "node:path";
1850
+ function sanitizeInboxSegment(value) {
1851
+ return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "session";
1852
+ }
1853
+ function defaultInboxPath(sessionId, intercomDir = getIntercomDirPath()) {
1854
+ return join5(intercomDir, `inbox-${sanitizeInboxSegment(sessionId)}.jsonl`);
1855
+ }
1850
1856
  function toInboxEntry(from, message, now = Date.now()) {
1851
1857
  return {
1852
1858
  ts: now,
@@ -2436,6 +2442,7 @@ var ClaudeIntercomRuntime = class {
2436
2442
  reconnectTimer = null;
2437
2443
  reconnectAttempt = 0;
2438
2444
  reconnectEnabled = true;
2445
+ registrationConflict = null;
2439
2446
  identity;
2440
2447
  unread = [];
2441
2448
  unresolvedAsks = /* @__PURE__ */ new Map();
@@ -2460,7 +2467,34 @@ var ClaudeIntercomRuntime = class {
2460
2467
  getIdentity() {
2461
2468
  return this.identity;
2462
2469
  }
2470
+ async syncSession(identity) {
2471
+ const changed = identity.sessionId !== this.identity.sessionId || identity.cwd !== this.identity.cwd;
2472
+ if (changed) {
2473
+ await this.disconnect();
2474
+ this.unread = [];
2475
+ this.unresolvedAsks.clear();
2476
+ this.registrationConflict = null;
2477
+ }
2478
+ const renamed = identity.name !== this.identity.name;
2479
+ const modelChanged = identity.model !== this.identity.model;
2480
+ this.identity = identity;
2481
+ if (changed) await this.connect();
2482
+ else if (renamed || modelChanged) this.client?.updatePresence({ name: identity.name, model: identity.model });
2483
+ }
2484
+ pauseOnRegistrationConflict(error2) {
2485
+ let cause = error2;
2486
+ for (let depth = 0; cause instanceof Error && depth < 8; depth++, cause = cause.cause) {
2487
+ if (cause.code === "SESSION_ID_IN_USE") {
2488
+ this.registrationConflict = cause;
2489
+ this.reconnectEnabled = false;
2490
+ this.clearReconnectTimer();
2491
+ return true;
2492
+ }
2493
+ }
2494
+ return false;
2495
+ }
2463
2496
  async connect() {
2497
+ if (this.registrationConflict) throw this.registrationConflict;
2464
2498
  this.reconnectEnabled = true;
2465
2499
  this.clearReconnectTimer();
2466
2500
  if (this.client?.isConnected()) return this.client;
@@ -2468,6 +2502,9 @@ var ClaudeIntercomRuntime = class {
2468
2502
  this.connectPromise = this.connectOnce();
2469
2503
  try {
2470
2504
  return await this.connectPromise;
2505
+ } catch (error2) {
2506
+ this.pauseOnRegistrationConflict(error2);
2507
+ throw error2;
2471
2508
  } finally {
2472
2509
  this.connectPromise = null;
2473
2510
  }
@@ -2479,32 +2516,36 @@ var ClaudeIntercomRuntime = class {
2479
2516
  this.handleIncomingMessage(from, message);
2480
2517
  client.acknowledgeMessage(deliveryId);
2481
2518
  });
2519
+ client.on("error", (error2) => {
2520
+ this.pauseOnRegistrationConflict(error2);
2521
+ process.stderr.write(`claude-intercom: ${error2.message}
2522
+ `);
2523
+ });
2482
2524
  client.on("disconnected", (error2) => {
2483
- for (const waiter of this.replyWaiters.values()) {
2484
- clearTimeout(waiter.timeout);
2485
- waiter.cleanup?.();
2486
- waiter.reject(new Error(`Disconnected while waiting for reply: ${error2.message}`, { cause: error2 }));
2487
- }
2488
- this.replyWaiters.clear();
2525
+ this.rejectReplyWaiters(new Error(`Disconnected while waiting for reply: ${error2.message}`, { cause: error2 }));
2489
2526
  if (this.client === client) this.client = null;
2490
2527
  this.scheduleReconnect();
2491
2528
  });
2529
+ const registeredIdentity = this.identity;
2492
2530
  await client.connect({
2493
- name: this.identity.name,
2494
- cwd: this.identity.cwd,
2495
- model: this.identity.model,
2531
+ name: registeredIdentity.name,
2532
+ cwd: registeredIdentity.cwd,
2533
+ model: registeredIdentity.model,
2496
2534
  pid: process.pid,
2497
- startedAt: this.identity.startedAt,
2535
+ startedAt: registeredIdentity.startedAt,
2498
2536
  lastActivity: Date.now(),
2499
2537
  status: "idle"
2500
- }, this.identity.sessionId);
2538
+ }, registeredIdentity.sessionId);
2501
2539
  this.client = client;
2540
+ if (registeredIdentity.name !== this.identity.name || registeredIdentity.model !== this.identity.model) {
2541
+ client.updatePresence({ name: this.identity.name, model: this.identity.model });
2542
+ }
2502
2543
  this.reconnectAttempt = 0;
2503
2544
  return client;
2504
2545
  }
2505
2546
  scheduleReconnect() {
2506
2547
  if (!this.reconnectEnabled || this.reconnectTimer) return;
2507
- const delay = this.reconnectDelays[Math.min(this.reconnectAttempt, this.reconnectDelays.length - 1)];
2548
+ const delay2 = this.reconnectDelays[Math.min(this.reconnectAttempt, this.reconnectDelays.length - 1)];
2508
2549
  this.reconnectTimer = setTimeout(() => {
2509
2550
  this.reconnectTimer = null;
2510
2551
  void this.connect().then((client) => {
@@ -2516,7 +2557,7 @@ var ClaudeIntercomRuntime = class {
2516
2557
  this.reconnectAttempt += 1;
2517
2558
  this.scheduleReconnect();
2518
2559
  });
2519
- }, delay);
2560
+ }, delay2);
2520
2561
  this.reconnectTimer.unref?.();
2521
2562
  }
2522
2563
  clearReconnectTimer() {
@@ -2524,8 +2565,17 @@ var ClaudeIntercomRuntime = class {
2524
2565
  clearTimeout(this.reconnectTimer);
2525
2566
  this.reconnectTimer = null;
2526
2567
  }
2568
+ rejectReplyWaiters(error2) {
2569
+ for (const waiter of this.replyWaiters.values()) {
2570
+ clearTimeout(waiter.timeout);
2571
+ waiter.cleanup?.();
2572
+ waiter.reject(error2);
2573
+ }
2574
+ this.replyWaiters.clear();
2575
+ }
2527
2576
  async disconnect() {
2528
2577
  this.reconnectEnabled = false;
2578
+ this.rejectReplyWaiters(new Error("Claude session disconnected while waiting for reply"));
2529
2579
  this.clearReconnectTimer();
2530
2580
  if (this.connectPromise) {
2531
2581
  try {
@@ -2600,12 +2650,9 @@ var ClaudeIntercomRuntime = class {
2600
2650
  if (message.expectsReply) {
2601
2651
  this.unresolvedAsks.set(message.id, entry);
2602
2652
  }
2603
- const inboxPath = process.env.CLAUDE_INTERCOM_INBOX;
2604
- if (inboxPath) {
2605
- try {
2606
- appendInboxMessage(inboxPath, from, message);
2607
- } catch {
2608
- }
2653
+ try {
2654
+ appendInboxMessage(process.env.CLAUDE_INTERCOM_INBOX || defaultInboxPath(this.identity.sessionId), from, message);
2655
+ } catch {
2609
2656
  }
2610
2657
  }
2611
2658
  waitForReply(from, replyTo, timeoutMs = getAskTimeoutMs(), signal) {
@@ -2657,6 +2704,17 @@ cwd: ${this.identity.cwd}`,
2657
2704
  return textResult(formatIntercomTeam(team), team);
2658
2705
  }
2659
2706
  async status() {
2707
+ if (this.registrationConflict) {
2708
+ return textResult(`Connected: No
2709
+ Session ID: ${this.identity.sessionId}
2710
+ Registration conflict: ${this.registrationConflict.message}
2711
+ Automatic reconnect paused; the incumbent session was not replaced.`, {
2712
+ connected: false,
2713
+ session_id: this.identity.sessionId,
2714
+ registration_conflict: { code: "SESSION_ID_IN_USE", message: this.registrationConflict.message },
2715
+ reconnect_paused: true
2716
+ }, true);
2717
+ }
2660
2718
  const client = await this.connect();
2661
2719
  const sessions = await client.listSessions();
2662
2720
  return textResult(
@@ -2813,21 +2871,21 @@ var attachmentsSchema = {
2813
2871
  additionalProperties: false
2814
2872
  }
2815
2873
  };
2816
- function buildToolDefinitions(runtime2) {
2874
+ function buildToolDefinitions(runtime) {
2817
2875
  const tools = [
2818
2876
  {
2819
2877
  name: "intercom_whoami",
2820
2878
  description: "Return this Claude session's intercom identity for reliable targeting.",
2821
2879
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
2822
2880
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
2823
- handler: async () => runtime2.whoami()
2881
+ handler: async () => runtime.whoami()
2824
2882
  },
2825
2883
  {
2826
2884
  name: "intercom_team",
2827
2885
  description: "Show your current manager and the live coworkers owned by that manager. No arguments are required.",
2828
2886
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
2829
2887
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
2830
- handler: async () => runtime2.team()
2888
+ handler: async () => runtime.team()
2831
2889
  },
2832
2890
  {
2833
2891
  name: "intercom_join",
@@ -2841,7 +2899,7 @@ function buildToolDefinitions(runtime2) {
2841
2899
  additionalProperties: false
2842
2900
  },
2843
2901
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
2844
- handler: async (args) => runtime2.join(
2902
+ handler: async (args) => runtime.join(
2845
2903
  typeof args.name === "string" ? args.name : void 0,
2846
2904
  args.create === true
2847
2905
  )
@@ -2851,7 +2909,7 @@ function buildToolDefinitions(runtime2) {
2851
2909
  description: "Show intercom connection status, active sessions, unread messages, and pending asks.",
2852
2910
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
2853
2911
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
2854
- handler: async () => runtime2.status()
2912
+ handler: async () => runtime.status()
2855
2913
  },
2856
2914
  {
2857
2915
  name: "intercom_list",
@@ -2865,7 +2923,7 @@ function buildToolDefinitions(runtime2) {
2865
2923
  additionalProperties: false
2866
2924
  },
2867
2925
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
2868
- handler: async (args) => runtime2.list(
2926
+ handler: async (args) => runtime.list(
2869
2927
  args.scope === "directory" || args.scope === "repo" ? args.scope : "machine",
2870
2928
  asBoolean(args.include_self, false)
2871
2929
  )
@@ -2880,7 +2938,7 @@ function buildToolDefinitions(runtime2) {
2880
2938
  additionalProperties: false
2881
2939
  },
2882
2940
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
2883
- handler: async (args) => runtime2.setSummary(asString(args.summary, "summary"))
2941
+ handler: async (args) => runtime.setSummary(asString(args.summary, "summary"))
2884
2942
  },
2885
2943
  {
2886
2944
  name: "intercom_send",
@@ -2896,7 +2954,7 @@ function buildToolDefinitions(runtime2) {
2896
2954
  additionalProperties: false
2897
2955
  },
2898
2956
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
2899
- handler: async (args) => runtime2.send(asString(args.to, "to"), asString(args.message, "message"), asAttachmentArray(args.attachments))
2957
+ handler: async (args) => runtime.send(asString(args.to, "to"), asString(args.message, "message"), asAttachmentArray(args.attachments))
2900
2958
  },
2901
2959
  {
2902
2960
  name: "intercom_ask",
@@ -2913,7 +2971,7 @@ function buildToolDefinitions(runtime2) {
2913
2971
  additionalProperties: false
2914
2972
  },
2915
2973
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
2916
- handler: async (args, signal) => runtime2.ask(
2974
+ handler: async (args, signal) => runtime.ask(
2917
2975
  asString(args.to, "to"),
2918
2976
  asString(args.message, "message"),
2919
2977
  asAttachmentArray(args.attachments),
@@ -2930,7 +2988,7 @@ function buildToolDefinitions(runtime2) {
2930
2988
  additionalProperties: false
2931
2989
  },
2932
2990
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
2933
- handler: async (args) => runtime2.pending(asBoolean(args.mark_read, false))
2991
+ handler: async (args) => runtime.pending(asBoolean(args.mark_read, false))
2934
2992
  },
2935
2993
  {
2936
2994
  name: "intercom_reply",
@@ -2946,7 +3004,7 @@ function buildToolDefinitions(runtime2) {
2946
3004
  additionalProperties: false
2947
3005
  },
2948
3006
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
2949
- handler: async (args) => runtime2.reply(asString(args.message, "message"), typeof args.to === "string" ? args.to : void 0, args.which === "oldest" || args.which === "latest" ? args.which : void 0)
3007
+ handler: async (args) => runtime.reply(asString(args.message, "message"), typeof args.to === "string" ? args.to : void 0, args.which === "oldest" || args.which === "latest" ? args.which : void 0)
2950
3008
  }
2951
3009
  ];
2952
3010
  return tools;
@@ -2957,7 +3015,7 @@ function ok(id, result) {
2957
3015
  function error(id, code, message) {
2958
3016
  return { jsonrpc: "2.0", id: id ?? null, error: { code, message } };
2959
3017
  }
2960
- async function handleMcpRequest(request, runtime2) {
3018
+ async function handleMcpRequest(request, runtime) {
2961
3019
  if (!request.method) {
2962
3020
  return error(request.id, -32600, "Invalid request");
2963
3021
  }
@@ -2971,13 +3029,13 @@ async function handleMcpRequest(request, runtime2) {
2971
3029
  if (request.id === void 0 && request.method.startsWith("notifications/")) {
2972
3030
  return void 0;
2973
3031
  }
2974
- const tools = buildToolDefinitions(runtime2);
3032
+ const tools = buildToolDefinitions(runtime);
2975
3033
  switch (request.method) {
2976
3034
  case "initialize":
2977
3035
  return ok(request.id, {
2978
3036
  protocolVersion: "2025-06-18",
2979
3037
  capabilities: { tools: {} },
2980
- serverInfo: { name: "claude-intercom", version: "0.1.0" }
3038
+ serverInfo: { name: "claude-intercom", version: "0.14.0" }
2981
3039
  });
2982
3040
  case "ping":
2983
3041
  return ok(request.id, {});
@@ -3011,19 +3069,169 @@ async function handleMcpRequest(request, runtime2) {
3011
3069
  }
3012
3070
  }
3013
3071
 
3072
+ // claude/session-lifecycle.ts
3073
+ import { execFileSync } from "node:child_process";
3074
+ import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync7, readSync, renameSync as renameSync3, statSync, writeFileSync as writeFileSync3 } from "node:fs";
3075
+ import { basename as basename2, dirname as dirname4, join as join8 } from "node:path";
3076
+ import { StringDecoder } from "node:string_decoder";
3077
+ import { setTimeout as delay } from "node:timers/promises";
3078
+ function findClaudeHost(pid = process.ppid) {
3079
+ for (let depth = 0; pid > 1 && depth < 16; depth++) {
3080
+ let parts;
3081
+ try {
3082
+ parts = execFileSync(
3083
+ "ps",
3084
+ ["-p", String(pid), "-o", "ppid=", "-o", "lstart=", "-o", "comm="],
3085
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
3086
+ ).trim().split(/\s+/);
3087
+ } catch {
3088
+ return void 0;
3089
+ }
3090
+ const command = basename2(parts.slice(6).join(" "));
3091
+ if (/^claude(?:\.exe)?$/i.test(command)) {
3092
+ return { pid, startedAt: parts.slice(1, 6).join(" ") };
3093
+ }
3094
+ if (!/^(sh|bash|zsh|dash|fish)$/.test(command)) return void 0;
3095
+ pid = Number(parts[0]);
3096
+ }
3097
+ return void 0;
3098
+ }
3099
+ function claudeSessionMetadataPath(host) {
3100
+ return join8(getIntercomDirPath(), `claude-host-${host.pid}.json`);
3101
+ }
3102
+ function readClaudeSessionMetadata(path, host) {
3103
+ try {
3104
+ const data = JSON.parse(readFileSync7(path, "utf8"));
3105
+ if (data.hostStartedAt !== host.startedAt || typeof data.sessionId !== "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(data.sessionId) || typeof data.cwd !== "string" || typeof data.transcriptPath !== "string" || typeof data.startedAt !== "number" || typeof data.ended !== "boolean" || data.name !== void 0 && typeof data.name !== "string" || data.model !== void 0 && typeof data.model !== "string") return void 0;
3106
+ return data;
3107
+ } catch {
3108
+ return void 0;
3109
+ }
3110
+ }
3111
+ function claudeIdentityForSession(metadata, env = process.env, pid = process.pid) {
3112
+ const identity = buildClaudeRuntimeIdentity(env, metadata?.cwd ?? env.PWD ?? process.cwd(), pid);
3113
+ if (!metadata) return identity;
3114
+ if (!env.CLAUDE_INTERCOM_SESSION_ID?.trim() && !env.CLAUDE_PEER_ID?.trim() && !env.AGENT_INTERCOM_SESSION_ID?.trim()) {
3115
+ identity.sessionId = `claude-${metadata.sessionId}`;
3116
+ }
3117
+ identity.name = metadata.name?.trim() || identity.name;
3118
+ if (!env.CLAUDE_INTERCOM_MODEL?.trim()) identity.model = metadata.model?.trim() || identity.model;
3119
+ identity.startedAt = metadata.startedAt;
3120
+ return identity;
3121
+ }
3122
+ var ClaudeSessionReader = class {
3123
+ constructor(path, host) {
3124
+ this.path = path;
3125
+ this.host = host;
3126
+ }
3127
+ path;
3128
+ host;
3129
+ transcriptPath = "";
3130
+ sessionId = "";
3131
+ offset = 0;
3132
+ partial = "";
3133
+ decoder = new StringDecoder("utf8");
3134
+ name;
3135
+ startedAt = 0;
3136
+ read() {
3137
+ const metadata = readClaudeSessionMetadata(this.path, this.host);
3138
+ if (!metadata || metadata.ended) return metadata;
3139
+ if (metadata.sessionId !== this.sessionId || metadata.transcriptPath !== this.transcriptPath || metadata.startedAt !== this.startedAt) {
3140
+ this.sessionId = metadata.sessionId;
3141
+ this.transcriptPath = metadata.transcriptPath;
3142
+ this.startedAt = metadata.startedAt;
3143
+ this.name = void 0;
3144
+ this.partial = "";
3145
+ this.decoder = new StringDecoder("utf8");
3146
+ try {
3147
+ this.offset = Math.max(0, statSync(this.transcriptPath).size - 64 * 1024);
3148
+ } catch {
3149
+ this.offset = 0;
3150
+ }
3151
+ }
3152
+ let fd;
3153
+ try {
3154
+ fd = openSync2(this.transcriptPath, "r");
3155
+ const size = statSync(this.transcriptPath).size;
3156
+ if (size < this.offset) {
3157
+ this.offset = 0;
3158
+ this.partial = "";
3159
+ this.decoder = new StringDecoder("utf8");
3160
+ }
3161
+ const buffer = Buffer.alloc(Math.min(size - this.offset, 64 * 1024));
3162
+ const count = readSync(fd, buffer, 0, buffer.length, this.offset);
3163
+ this.offset += count;
3164
+ const lines = (this.partial + this.decoder.write(buffer.subarray(0, count))).split("\n");
3165
+ this.partial = lines.pop() ?? "";
3166
+ if (this.partial.length > 64 * 1024) this.partial = "";
3167
+ for (const line of lines) {
3168
+ if (!line.includes('"custom-title"')) continue;
3169
+ try {
3170
+ const record = JSON.parse(line);
3171
+ if (record.type === "custom-title" && record.sessionId === metadata.sessionId && typeof record.customTitle === "string" && record.customTitle.trim()) this.name = record.customTitle;
3172
+ } catch {
3173
+ }
3174
+ }
3175
+ } catch {
3176
+ } finally {
3177
+ if (fd !== void 0) closeSync2(fd);
3178
+ }
3179
+ return { ...metadata, name: this.name ?? metadata.name };
3180
+ }
3181
+ };
3182
+ async function waitForClaudeSession(reader) {
3183
+ for (let attempt = 0; attempt < 20; attempt++) {
3184
+ const metadata = reader.read();
3185
+ if (metadata) return metadata;
3186
+ await delay(50);
3187
+ }
3188
+ return reader.read();
3189
+ }
3190
+
3014
3191
  // claude/server.ts
3015
- var runtime = new ClaudeIntercomRuntime();
3016
- if (process.env.CLAUDE_INTERCOM_INBOX) {
3017
- void runtime.connect().catch((error2) => {
3018
- process.stderr.write(`claude-intercom: eager connect failed: ${error2 instanceof Error ? error2.message : String(error2)}
3192
+ var shuttingDown = false;
3193
+ var stopPolling = () => {
3194
+ };
3195
+ var pendingSync = Promise.resolve();
3196
+ var report = (error2) => {
3197
+ process.stderr.write(`claude-intercom: ${error2 instanceof Error ? error2.message : String(error2)}
3019
3198
  `);
3020
- });
3021
- }
3199
+ };
3200
+ var runtimeReady = (async () => {
3201
+ const host = findClaudeHost();
3202
+ const reader = host ? new ClaudeSessionReader(claudeSessionMetadataPath(host), host) : void 0;
3203
+ const metadata = reader ? await waitForClaudeSession(reader) : void 0;
3204
+ const runtime = new ClaudeIntercomRuntime(claudeIdentityForSession(metadata, process.env, host?.pid));
3205
+ if (!shuttingDown && !metadata?.ended) void runtime.connect().catch(report);
3206
+ if (reader && !shuttingDown) {
3207
+ let previous = JSON.stringify(metadata);
3208
+ let syncing = false;
3209
+ const timer = setInterval(() => {
3210
+ if (syncing || shuttingDown) return;
3211
+ const next = reader.read();
3212
+ const signature = JSON.stringify(next);
3213
+ if (!next || signature === previous) return;
3214
+ previous = signature;
3215
+ syncing = true;
3216
+ pendingSync = (async () => {
3217
+ if (next.ended) await runtime.disconnect();
3218
+ else {
3219
+ await runtime.syncSession(claudeIdentityForSession(next, process.env, host.pid));
3220
+ await runtime.connect();
3221
+ }
3222
+ })().catch(report).finally(() => {
3223
+ syncing = false;
3224
+ });
3225
+ }, 250);
3226
+ timer.unref();
3227
+ stopPolling = () => clearInterval(timer);
3228
+ }
3229
+ return runtime;
3230
+ })();
3022
3231
  var rl = readline.createInterface({
3023
3232
  input: stdin,
3024
3233
  crlfDelay: Infinity
3025
3234
  });
3026
- var shuttingDown = false;
3027
3235
  var pendingRequests = 0;
3028
3236
  function writeResponse(response) {
3029
3237
  if (!response) return;
@@ -3032,7 +3240,11 @@ function writeResponse(response) {
3032
3240
  }
3033
3241
  function maybeShutdown() {
3034
3242
  if (!shuttingDown || pendingRequests > 0) return;
3035
- void runtime.disconnect().finally(() => process.exit(0));
3243
+ stopPolling();
3244
+ void runtimeReady.then(async (runtime) => {
3245
+ await pendingSync;
3246
+ await runtime.disconnect();
3247
+ }).finally(() => process.exit(0));
3036
3248
  }
3037
3249
  rl.on("line", (line) => {
3038
3250
  const trimmed = line.trim();
@@ -3041,7 +3253,7 @@ rl.on("line", (line) => {
3041
3253
  void (async () => {
3042
3254
  try {
3043
3255
  const request = JSON.parse(trimmed);
3044
- writeResponse(await handleMcpRequest(request, runtime));
3256
+ writeResponse(await handleMcpRequest(request, await runtimeReady));
3045
3257
  } catch (error2) {
3046
3258
  writeResponse({
3047
3259
  jsonrpc: "2.0",
@@ -3067,3 +3279,6 @@ rl.on("close", shutdown);
3067
3279
  process.on("SIGINT", shutdown);
3068
3280
  process.on("SIGTERM", shutdown);
3069
3281
  process.on("disconnect", shutdown);
3282
+ export {
3283
+ runtimeReady
3284
+ };