happy-coder 0.9.0-0 → 0.9.0-2

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/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var types$1 = require('./types-D8nxJTr8.cjs');
4
+ var types$1 = require('./types-a-nJyP-e.cjs');
5
5
  var node_crypto = require('node:crypto');
6
6
  var node_child_process = require('node:child_process');
7
7
  var node_path = require('node:path');
@@ -1623,11 +1623,20 @@ class PermissionHandler {
1623
1623
  pendingRequests = /* @__PURE__ */ new Map();
1624
1624
  session;
1625
1625
  allowedTools = /* @__PURE__ */ new Set();
1626
+ allowedBashLiterals = /* @__PURE__ */ new Set();
1627
+ allowedBashPrefixes = /* @__PURE__ */ new Set();
1626
1628
  permissionMode = "default";
1629
+ onPermissionRequestCallback;
1627
1630
  constructor(session) {
1628
1631
  this.session = session;
1629
1632
  this.setupClientHandler();
1630
1633
  }
1634
+ /**
1635
+ * Set callback to trigger when permission request is made
1636
+ */
1637
+ setOnPermissionRequest(callback) {
1638
+ this.onPermissionRequestCallback = callback;
1639
+ }
1631
1640
  handleModeChange(mode) {
1632
1641
  this.permissionMode = mode;
1633
1642
  }
@@ -1636,7 +1645,13 @@ class PermissionHandler {
1636
1645
  */
1637
1646
  handlePermissionResponse(response, pending) {
1638
1647
  if (response.allowTools && response.allowTools.length > 0) {
1639
- response.allowTools.forEach((tool) => this.allowedTools.add(tool));
1648
+ response.allowTools.forEach((tool) => {
1649
+ if (tool.startsWith("Bash(") || tool === "Bash") {
1650
+ this.parseBashPermission(tool);
1651
+ } else {
1652
+ this.allowedTools.add(tool);
1653
+ }
1654
+ });
1640
1655
  }
1641
1656
  if (response.mode) {
1642
1657
  this.permissionMode = response.mode;
@@ -1663,7 +1678,19 @@ class PermissionHandler {
1663
1678
  * Creates the canCallTool callback for the SDK
1664
1679
  */
1665
1680
  handleToolCall = async (toolName, input, mode, options) => {
1666
- if (this.allowedTools.has(toolName)) {
1681
+ if (toolName === "Bash") {
1682
+ const inputObj = input;
1683
+ if (inputObj?.command) {
1684
+ if (this.allowedBashLiterals.has(inputObj.command)) {
1685
+ return { behavior: "allow", updatedInput: input };
1686
+ }
1687
+ for (const prefix of this.allowedBashPrefixes) {
1688
+ if (inputObj.command.startsWith(prefix)) {
1689
+ return { behavior: "allow", updatedInput: input };
1690
+ }
1691
+ }
1692
+ }
1693
+ } else if (this.allowedTools.has(toolName)) {
1667
1694
  return { behavior: "allow", updatedInput: input };
1668
1695
  }
1669
1696
  const descriptor = getToolDescriptor(toolName);
@@ -1705,6 +1732,9 @@ class PermissionHandler {
1705
1732
  toolName,
1706
1733
  input
1707
1734
  });
1735
+ if (this.onPermissionRequestCallback) {
1736
+ this.onPermissionRequestCallback(id);
1737
+ }
1708
1738
  this.session.api.push().sendToAllDevices(
1709
1739
  "Permission Request",
1710
1740
  `Claude wants to ${getToolName(toolName)}`,
@@ -1729,6 +1759,26 @@ class PermissionHandler {
1729
1759
  types$1.logger.debug(`Permission request sent for tool call ${id}: ${toolName}`);
1730
1760
  });
1731
1761
  }
1762
+ /**
1763
+ * Parses Bash permission strings into literal and prefix sets
1764
+ */
1765
+ parseBashPermission(permission) {
1766
+ if (permission === "Bash") {
1767
+ return;
1768
+ }
1769
+ const bashPattern = /^Bash\((.+?)\)$/;
1770
+ const match = permission.match(bashPattern);
1771
+ if (!match) {
1772
+ return;
1773
+ }
1774
+ const command = match[1];
1775
+ if (command.endsWith(":*")) {
1776
+ const prefix = command.slice(0, -2);
1777
+ this.allowedBashPrefixes.add(prefix);
1778
+ } else {
1779
+ this.allowedBashLiterals.add(command);
1780
+ }
1781
+ }
1732
1782
  /**
1733
1783
  * Resolves tool call ID based on tool name and input
1734
1784
  */
@@ -1797,6 +1847,9 @@ class PermissionHandler {
1797
1847
  reset() {
1798
1848
  this.toolCalls = [];
1799
1849
  this.responses.clear();
1850
+ this.allowedTools.clear();
1851
+ this.allowedBashLiterals.clear();
1852
+ this.allowedBashPrefixes.clear();
1800
1853
  for (const [, pending] of this.pendingRequests.entries()) {
1801
1854
  pending.reject(new Error("Session reset"));
1802
1855
  }
@@ -2203,6 +2256,136 @@ class SDKToLogConverter {
2203
2256
  }
2204
2257
  }
2205
2258
 
2259
+ class OutgoingMessageQueue {
2260
+ constructor(sendFunction) {
2261
+ this.sendFunction = sendFunction;
2262
+ }
2263
+ queue = [];
2264
+ nextId = 1;
2265
+ lock = new types$1.AsyncLock();
2266
+ processTimer;
2267
+ delayTimers = /* @__PURE__ */ new Map();
2268
+ /**
2269
+ * Add message to queue
2270
+ */
2271
+ enqueue(logMessage, options) {
2272
+ this.lock.inLock(async () => {
2273
+ const item = {
2274
+ id: this.nextId++,
2275
+ logMessage,
2276
+ delayed: !!options?.delay,
2277
+ delayMs: options?.delay || 0,
2278
+ toolCallIds: options?.toolCallIds,
2279
+ released: !options?.delay,
2280
+ // Not delayed = already released
2281
+ sent: false
2282
+ };
2283
+ this.queue.push(item);
2284
+ if (item.delayed) {
2285
+ const timer = setTimeout(() => {
2286
+ this.releaseItem(item.id);
2287
+ }, item.delayMs);
2288
+ this.delayTimers.set(item.id, timer);
2289
+ }
2290
+ });
2291
+ this.scheduleProcessing();
2292
+ }
2293
+ /**
2294
+ * Release specific item by ID
2295
+ */
2296
+ async releaseItem(itemId) {
2297
+ await this.lock.inLock(async () => {
2298
+ const item = this.queue.find((i) => i.id === itemId);
2299
+ if (item && !item.released) {
2300
+ item.released = true;
2301
+ const timer = this.delayTimers.get(itemId);
2302
+ if (timer) {
2303
+ clearTimeout(timer);
2304
+ this.delayTimers.delete(itemId);
2305
+ }
2306
+ }
2307
+ });
2308
+ this.scheduleProcessing();
2309
+ }
2310
+ /**
2311
+ * Release all messages with specific tool call ID
2312
+ */
2313
+ async releaseToolCall(toolCallId) {
2314
+ await this.lock.inLock(async () => {
2315
+ for (const item of this.queue) {
2316
+ if (item.toolCallIds?.includes(toolCallId) && !item.released) {
2317
+ item.released = true;
2318
+ const timer = this.delayTimers.get(item.id);
2319
+ if (timer) {
2320
+ clearTimeout(timer);
2321
+ this.delayTimers.delete(item.id);
2322
+ }
2323
+ }
2324
+ }
2325
+ });
2326
+ this.scheduleProcessing();
2327
+ }
2328
+ /**
2329
+ * Process queue - send messages in ID order that are released
2330
+ */
2331
+ async processQueue() {
2332
+ await this.lock.inLock(async () => {
2333
+ this.queue.sort((a, b) => a.id - b.id);
2334
+ while (this.queue.length > 0) {
2335
+ const item = this.queue[0];
2336
+ if (!item.released) {
2337
+ break;
2338
+ }
2339
+ if (!item.sent) {
2340
+ if (item.logMessage.type !== "system") {
2341
+ this.sendFunction(item.logMessage);
2342
+ }
2343
+ item.sent = true;
2344
+ }
2345
+ this.queue.shift();
2346
+ }
2347
+ });
2348
+ }
2349
+ /**
2350
+ * Flush all messages immediately (for cleanup)
2351
+ */
2352
+ async flush() {
2353
+ await this.lock.inLock(async () => {
2354
+ for (const timer of this.delayTimers.values()) {
2355
+ clearTimeout(timer);
2356
+ }
2357
+ this.delayTimers.clear();
2358
+ for (const item of this.queue) {
2359
+ item.released = true;
2360
+ }
2361
+ await this.processQueue();
2362
+ });
2363
+ }
2364
+ /**
2365
+ * Schedule processing on next tick
2366
+ */
2367
+ scheduleProcessing() {
2368
+ if (this.processTimer) {
2369
+ clearTimeout(this.processTimer);
2370
+ }
2371
+ this.processTimer = setTimeout(() => {
2372
+ this.processQueue();
2373
+ }, 0);
2374
+ }
2375
+ /**
2376
+ * Cleanup timers and resources
2377
+ */
2378
+ destroy() {
2379
+ if (this.processTimer) {
2380
+ clearTimeout(this.processTimer);
2381
+ }
2382
+ for (const timer of this.delayTimers.values()) {
2383
+ clearTimeout(timer);
2384
+ }
2385
+ this.delayTimers.clear();
2386
+ }
2387
+ }
2388
+
2206
2389
  async function claudeRemoteLauncher(session) {
2207
2390
  types$1.logger.debug("[claudeRemoteLauncher] Starting remote launcher");
2208
2391
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
@@ -2260,6 +2443,12 @@ async function claudeRemoteLauncher(session) {
2260
2443
  session.client.setHandler("abort", doAbort);
2261
2444
  session.client.setHandler("switch", doSwitch);
2262
2445
  const permissionHandler = new PermissionHandler(session);
2446
+ const messageQueue = new OutgoingMessageQueue(
2447
+ (logMessage) => session.client.sendClaudeSessionMessage(logMessage)
2448
+ );
2449
+ permissionHandler.setOnPermissionRequest((toolCallId) => {
2450
+ messageQueue.releaseToolCall(toolCallId);
2451
+ });
2263
2452
  const sdkToLogConverter = new SDKToLogConverter({
2264
2453
  sessionId: session.sessionId || "unknown",
2265
2454
  cwd: session.path,
@@ -2298,6 +2487,7 @@ async function claudeRemoteLauncher(session) {
2298
2487
  for (let c of umessage.message.content) {
2299
2488
  if (c.type === "tool_result" && c.tool_use_id) {
2300
2489
  ongoingToolCalls.delete(c.tool_use_id);
2490
+ messageQueue.releaseToolCall(c.tool_use_id);
2301
2491
  }
2302
2492
  }
2303
2493
  }
@@ -2359,9 +2549,28 @@ async function claudeRemoteLauncher(session) {
2359
2549
  }
2360
2550
  }
2361
2551
  }
2362
- if (logMessage.type !== "system") {
2363
- session.client.sendClaudeSessionMessage(logMessage);
2552
+ if (logMessage.type === "assistant" && message.type === "assistant") {
2553
+ const assistantMsg = message;
2554
+ const toolCallIds = [];
2555
+ if (assistantMsg.message.content && Array.isArray(assistantMsg.message.content)) {
2556
+ for (const block of assistantMsg.message.content) {
2557
+ if (block.type === "tool_use" && block.id) {
2558
+ toolCallIds.push(block.id);
2559
+ }
2560
+ }
2561
+ }
2562
+ if (toolCallIds.length > 0) {
2563
+ const isSidechain = assistantMsg.parent_tool_use_id !== void 0;
2564
+ if (!isSidechain) {
2565
+ messageQueue.enqueue(logMessage, {
2566
+ delay: 250,
2567
+ toolCallIds
2568
+ });
2569
+ return;
2570
+ }
2571
+ }
2364
2572
  }
2573
+ messageQueue.enqueue(logMessage);
2365
2574
  }
2366
2575
  if (message.type === "assistant") {
2367
2576
  let umessage = message;
@@ -2370,7 +2579,7 @@ async function claudeRemoteLauncher(session) {
2370
2579
  if (c.type === "tool_use" && c.name === "Task" && c.input && typeof c.input.prompt === "string") {
2371
2580
  const logMessage2 = sdkToLogConverter.convertSidechainUserMessage(c.id, c.input.prompt);
2372
2581
  if (logMessage2) {
2373
- session.client.sendClaudeSessionMessage(logMessage2);
2582
+ messageQueue.enqueue(logMessage2);
2374
2583
  }
2375
2584
  }
2376
2585
  }
@@ -2461,6 +2670,8 @@ async function claudeRemoteLauncher(session) {
2461
2670
  }
2462
2671
  }
2463
2672
  ongoingToolCalls.clear();
2673
+ await messageQueue.flush();
2674
+ messageQueue.destroy();
2464
2675
  abortController = null;
2465
2676
  abortFuture?.resolve(void 0);
2466
2677
  abortFuture = null;
@@ -2534,7 +2745,7 @@ async function loop(opts) {
2534
2745
  }
2535
2746
 
2536
2747
  var name = "happy-coder";
2537
- var version = "0.9.0-0";
2748
+ var version = "0.9.0-2";
2538
2749
  var description = "Claude Code session sharing CLI";
2539
2750
  var author = "Kirill Dubovitskiy";
2540
2751
  var license = "MIT";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, c as configuration, e as encodeBase64, f as encodeBase64Url, g as decodeBase64, A as ApiClient } from './types-CJU8o8xd.mjs';
2
+ import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, e as AsyncLock, c as configuration, f as encodeBase64, g as encodeBase64Url, h as decodeBase64, A as ApiClient } from './types-DJOX-XG-.mjs';
3
3
  import { randomUUID, randomBytes } from 'node:crypto';
4
4
  import { spawn, execSync } from 'node:child_process';
5
5
  import { resolve, join, dirname as dirname$1 } from 'node:path';
@@ -1602,11 +1602,20 @@ class PermissionHandler {
1602
1602
  pendingRequests = /* @__PURE__ */ new Map();
1603
1603
  session;
1604
1604
  allowedTools = /* @__PURE__ */ new Set();
1605
+ allowedBashLiterals = /* @__PURE__ */ new Set();
1606
+ allowedBashPrefixes = /* @__PURE__ */ new Set();
1605
1607
  permissionMode = "default";
1608
+ onPermissionRequestCallback;
1606
1609
  constructor(session) {
1607
1610
  this.session = session;
1608
1611
  this.setupClientHandler();
1609
1612
  }
1613
+ /**
1614
+ * Set callback to trigger when permission request is made
1615
+ */
1616
+ setOnPermissionRequest(callback) {
1617
+ this.onPermissionRequestCallback = callback;
1618
+ }
1610
1619
  handleModeChange(mode) {
1611
1620
  this.permissionMode = mode;
1612
1621
  }
@@ -1615,7 +1624,13 @@ class PermissionHandler {
1615
1624
  */
1616
1625
  handlePermissionResponse(response, pending) {
1617
1626
  if (response.allowTools && response.allowTools.length > 0) {
1618
- response.allowTools.forEach((tool) => this.allowedTools.add(tool));
1627
+ response.allowTools.forEach((tool) => {
1628
+ if (tool.startsWith("Bash(") || tool === "Bash") {
1629
+ this.parseBashPermission(tool);
1630
+ } else {
1631
+ this.allowedTools.add(tool);
1632
+ }
1633
+ });
1619
1634
  }
1620
1635
  if (response.mode) {
1621
1636
  this.permissionMode = response.mode;
@@ -1642,7 +1657,19 @@ class PermissionHandler {
1642
1657
  * Creates the canCallTool callback for the SDK
1643
1658
  */
1644
1659
  handleToolCall = async (toolName, input, mode, options) => {
1645
- if (this.allowedTools.has(toolName)) {
1660
+ if (toolName === "Bash") {
1661
+ const inputObj = input;
1662
+ if (inputObj?.command) {
1663
+ if (this.allowedBashLiterals.has(inputObj.command)) {
1664
+ return { behavior: "allow", updatedInput: input };
1665
+ }
1666
+ for (const prefix of this.allowedBashPrefixes) {
1667
+ if (inputObj.command.startsWith(prefix)) {
1668
+ return { behavior: "allow", updatedInput: input };
1669
+ }
1670
+ }
1671
+ }
1672
+ } else if (this.allowedTools.has(toolName)) {
1646
1673
  return { behavior: "allow", updatedInput: input };
1647
1674
  }
1648
1675
  const descriptor = getToolDescriptor(toolName);
@@ -1684,6 +1711,9 @@ class PermissionHandler {
1684
1711
  toolName,
1685
1712
  input
1686
1713
  });
1714
+ if (this.onPermissionRequestCallback) {
1715
+ this.onPermissionRequestCallback(id);
1716
+ }
1687
1717
  this.session.api.push().sendToAllDevices(
1688
1718
  "Permission Request",
1689
1719
  `Claude wants to ${getToolName(toolName)}`,
@@ -1708,6 +1738,26 @@ class PermissionHandler {
1708
1738
  logger.debug(`Permission request sent for tool call ${id}: ${toolName}`);
1709
1739
  });
1710
1740
  }
1741
+ /**
1742
+ * Parses Bash permission strings into literal and prefix sets
1743
+ */
1744
+ parseBashPermission(permission) {
1745
+ if (permission === "Bash") {
1746
+ return;
1747
+ }
1748
+ const bashPattern = /^Bash\((.+?)\)$/;
1749
+ const match = permission.match(bashPattern);
1750
+ if (!match) {
1751
+ return;
1752
+ }
1753
+ const command = match[1];
1754
+ if (command.endsWith(":*")) {
1755
+ const prefix = command.slice(0, -2);
1756
+ this.allowedBashPrefixes.add(prefix);
1757
+ } else {
1758
+ this.allowedBashLiterals.add(command);
1759
+ }
1760
+ }
1711
1761
  /**
1712
1762
  * Resolves tool call ID based on tool name and input
1713
1763
  */
@@ -1776,6 +1826,9 @@ class PermissionHandler {
1776
1826
  reset() {
1777
1827
  this.toolCalls = [];
1778
1828
  this.responses.clear();
1829
+ this.allowedTools.clear();
1830
+ this.allowedBashLiterals.clear();
1831
+ this.allowedBashPrefixes.clear();
1779
1832
  for (const [, pending] of this.pendingRequests.entries()) {
1780
1833
  pending.reject(new Error("Session reset"));
1781
1834
  }
@@ -2182,6 +2235,136 @@ class SDKToLogConverter {
2182
2235
  }
2183
2236
  }
2184
2237
 
2238
+ class OutgoingMessageQueue {
2239
+ constructor(sendFunction) {
2240
+ this.sendFunction = sendFunction;
2241
+ }
2242
+ queue = [];
2243
+ nextId = 1;
2244
+ lock = new AsyncLock();
2245
+ processTimer;
2246
+ delayTimers = /* @__PURE__ */ new Map();
2247
+ /**
2248
+ * Add message to queue
2249
+ */
2250
+ enqueue(logMessage, options) {
2251
+ this.lock.inLock(async () => {
2252
+ const item = {
2253
+ id: this.nextId++,
2254
+ logMessage,
2255
+ delayed: !!options?.delay,
2256
+ delayMs: options?.delay || 0,
2257
+ toolCallIds: options?.toolCallIds,
2258
+ released: !options?.delay,
2259
+ // Not delayed = already released
2260
+ sent: false
2261
+ };
2262
+ this.queue.push(item);
2263
+ if (item.delayed) {
2264
+ const timer = setTimeout(() => {
2265
+ this.releaseItem(item.id);
2266
+ }, item.delayMs);
2267
+ this.delayTimers.set(item.id, timer);
2268
+ }
2269
+ });
2270
+ this.scheduleProcessing();
2271
+ }
2272
+ /**
2273
+ * Release specific item by ID
2274
+ */
2275
+ async releaseItem(itemId) {
2276
+ await this.lock.inLock(async () => {
2277
+ const item = this.queue.find((i) => i.id === itemId);
2278
+ if (item && !item.released) {
2279
+ item.released = true;
2280
+ const timer = this.delayTimers.get(itemId);
2281
+ if (timer) {
2282
+ clearTimeout(timer);
2283
+ this.delayTimers.delete(itemId);
2284
+ }
2285
+ }
2286
+ });
2287
+ this.scheduleProcessing();
2288
+ }
2289
+ /**
2290
+ * Release all messages with specific tool call ID
2291
+ */
2292
+ async releaseToolCall(toolCallId) {
2293
+ await this.lock.inLock(async () => {
2294
+ for (const item of this.queue) {
2295
+ if (item.toolCallIds?.includes(toolCallId) && !item.released) {
2296
+ item.released = true;
2297
+ const timer = this.delayTimers.get(item.id);
2298
+ if (timer) {
2299
+ clearTimeout(timer);
2300
+ this.delayTimers.delete(item.id);
2301
+ }
2302
+ }
2303
+ }
2304
+ });
2305
+ this.scheduleProcessing();
2306
+ }
2307
+ /**
2308
+ * Process queue - send messages in ID order that are released
2309
+ */
2310
+ async processQueue() {
2311
+ await this.lock.inLock(async () => {
2312
+ this.queue.sort((a, b) => a.id - b.id);
2313
+ while (this.queue.length > 0) {
2314
+ const item = this.queue[0];
2315
+ if (!item.released) {
2316
+ break;
2317
+ }
2318
+ if (!item.sent) {
2319
+ if (item.logMessage.type !== "system") {
2320
+ this.sendFunction(item.logMessage);
2321
+ }
2322
+ item.sent = true;
2323
+ }
2324
+ this.queue.shift();
2325
+ }
2326
+ });
2327
+ }
2328
+ /**
2329
+ * Flush all messages immediately (for cleanup)
2330
+ */
2331
+ async flush() {
2332
+ await this.lock.inLock(async () => {
2333
+ for (const timer of this.delayTimers.values()) {
2334
+ clearTimeout(timer);
2335
+ }
2336
+ this.delayTimers.clear();
2337
+ for (const item of this.queue) {
2338
+ item.released = true;
2339
+ }
2340
+ await this.processQueue();
2341
+ });
2342
+ }
2343
+ /**
2344
+ * Schedule processing on next tick
2345
+ */
2346
+ scheduleProcessing() {
2347
+ if (this.processTimer) {
2348
+ clearTimeout(this.processTimer);
2349
+ }
2350
+ this.processTimer = setTimeout(() => {
2351
+ this.processQueue();
2352
+ }, 0);
2353
+ }
2354
+ /**
2355
+ * Cleanup timers and resources
2356
+ */
2357
+ destroy() {
2358
+ if (this.processTimer) {
2359
+ clearTimeout(this.processTimer);
2360
+ }
2361
+ for (const timer of this.delayTimers.values()) {
2362
+ clearTimeout(timer);
2363
+ }
2364
+ this.delayTimers.clear();
2365
+ }
2366
+ }
2367
+
2185
2368
  async function claudeRemoteLauncher(session) {
2186
2369
  logger.debug("[claudeRemoteLauncher] Starting remote launcher");
2187
2370
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
@@ -2239,6 +2422,12 @@ async function claudeRemoteLauncher(session) {
2239
2422
  session.client.setHandler("abort", doAbort);
2240
2423
  session.client.setHandler("switch", doSwitch);
2241
2424
  const permissionHandler = new PermissionHandler(session);
2425
+ const messageQueue = new OutgoingMessageQueue(
2426
+ (logMessage) => session.client.sendClaudeSessionMessage(logMessage)
2427
+ );
2428
+ permissionHandler.setOnPermissionRequest((toolCallId) => {
2429
+ messageQueue.releaseToolCall(toolCallId);
2430
+ });
2242
2431
  const sdkToLogConverter = new SDKToLogConverter({
2243
2432
  sessionId: session.sessionId || "unknown",
2244
2433
  cwd: session.path,
@@ -2277,6 +2466,7 @@ async function claudeRemoteLauncher(session) {
2277
2466
  for (let c of umessage.message.content) {
2278
2467
  if (c.type === "tool_result" && c.tool_use_id) {
2279
2468
  ongoingToolCalls.delete(c.tool_use_id);
2469
+ messageQueue.releaseToolCall(c.tool_use_id);
2280
2470
  }
2281
2471
  }
2282
2472
  }
@@ -2338,9 +2528,28 @@ async function claudeRemoteLauncher(session) {
2338
2528
  }
2339
2529
  }
2340
2530
  }
2341
- if (logMessage.type !== "system") {
2342
- session.client.sendClaudeSessionMessage(logMessage);
2531
+ if (logMessage.type === "assistant" && message.type === "assistant") {
2532
+ const assistantMsg = message;
2533
+ const toolCallIds = [];
2534
+ if (assistantMsg.message.content && Array.isArray(assistantMsg.message.content)) {
2535
+ for (const block of assistantMsg.message.content) {
2536
+ if (block.type === "tool_use" && block.id) {
2537
+ toolCallIds.push(block.id);
2538
+ }
2539
+ }
2540
+ }
2541
+ if (toolCallIds.length > 0) {
2542
+ const isSidechain = assistantMsg.parent_tool_use_id !== void 0;
2543
+ if (!isSidechain) {
2544
+ messageQueue.enqueue(logMessage, {
2545
+ delay: 250,
2546
+ toolCallIds
2547
+ });
2548
+ return;
2549
+ }
2550
+ }
2343
2551
  }
2552
+ messageQueue.enqueue(logMessage);
2344
2553
  }
2345
2554
  if (message.type === "assistant") {
2346
2555
  let umessage = message;
@@ -2349,7 +2558,7 @@ async function claudeRemoteLauncher(session) {
2349
2558
  if (c.type === "tool_use" && c.name === "Task" && c.input && typeof c.input.prompt === "string") {
2350
2559
  const logMessage2 = sdkToLogConverter.convertSidechainUserMessage(c.id, c.input.prompt);
2351
2560
  if (logMessage2) {
2352
- session.client.sendClaudeSessionMessage(logMessage2);
2561
+ messageQueue.enqueue(logMessage2);
2353
2562
  }
2354
2563
  }
2355
2564
  }
@@ -2440,6 +2649,8 @@ async function claudeRemoteLauncher(session) {
2440
2649
  }
2441
2650
  }
2442
2651
  ongoingToolCalls.clear();
2652
+ await messageQueue.flush();
2653
+ messageQueue.destroy();
2443
2654
  abortController = null;
2444
2655
  abortFuture?.resolve(void 0);
2445
2656
  abortFuture = null;
@@ -2513,7 +2724,7 @@ async function loop(opts) {
2513
2724
  }
2514
2725
 
2515
2726
  var name = "happy-coder";
2516
- var version = "0.9.0-0";
2727
+ var version = "0.9.0-2";
2517
2728
  var description = "Claude Code session sharing CLI";
2518
2729
  var author = "Kirill Dubovitskiy";
2519
2730
  var license = "MIT";
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var types = require('./types-D8nxJTr8.cjs');
3
+ var types = require('./types-a-nJyP-e.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('fs');
package/dist/lib.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-CJU8o8xd.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-DJOX-XG-.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -1317,4 +1317,4 @@ const RawJSONLinesSchema = z.discriminatedUnion("type", [
1317
1317
  }).passthrough()
1318
1318
  ]);
1319
1319
 
1320
- export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, encodeBase64 as e, encodeBase64Url as f, decodeBase64 as g, logger as l };
1320
+ export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, AsyncLock as e, encodeBase64 as f, encodeBase64Url as g, decodeBase64 as h, logger as l };
@@ -1321,6 +1321,7 @@ const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
1321
1321
 
1322
1322
  exports.ApiClient = ApiClient;
1323
1323
  exports.ApiSessionClient = ApiSessionClient;
1324
+ exports.AsyncLock = AsyncLock;
1324
1325
  exports.RawJSONLinesSchema = RawJSONLinesSchema;
1325
1326
  exports.backoff = backoff;
1326
1327
  exports.configuration = configuration;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.9.0-0",
3
+ "version": "0.9.0-2",
4
4
  "description": "Claude Code session sharing CLI",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",