very-happy-cli 0.2.87 → 0.2.89

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.
Files changed (29) hide show
  1. package/dist/{AcpBackend-DxNg9T6Z.cjs → AcpBackend-ClEvE-A-.cjs} +1 -1
  2. package/dist/{AcpBackend-kotk9Pwu.mjs → AcpBackend-DuxcHPyn.mjs} +1 -1
  3. package/dist/{AcpSessionManager-DGY1Ss6H.mjs → AcpSessionManager-CGrA8bTm.mjs} +1 -1
  4. package/dist/{AcpSessionManager-Im0qDPE0.cjs → AcpSessionManager-DgbEf8FX.cjs} +1 -1
  5. package/dist/{config-DcoxG-LO.cjs → config-BZNnPK1L.cjs} +2 -2
  6. package/dist/{config-CDMM_UhY.mjs → config-CdoGLla8.mjs} +2 -2
  7. package/dist/{index-DLI9RIsu.mjs → index-BP2wylez.mjs} +5 -5
  8. package/dist/{index-DBYxKaw7.cjs → index-Bx-UTV3I.cjs} +4 -4
  9. package/dist/{index-DveDBCaA.cjs → index-DYDAFqAN.cjs} +149 -42
  10. package/dist/{index-UiX6h05D.mjs → index-vCgb7QnZ.mjs} +146 -39
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.mjs +2 -2
  13. package/dist/{installTerminalHooks-BXxMZXNx.mjs → installTerminalHooks-C7JS6CBM.mjs} +2 -2
  14. package/dist/{installTerminalHooks-fuWRm0w5.cjs → installTerminalHooks-T4NE5fmC.cjs} +2 -2
  15. package/dist/lib.cjs +1 -1
  16. package/dist/lib.mjs +1 -1
  17. package/dist/{mcp-CgOiXU_f.mjs → mcp-B3EgenZh.mjs} +2 -2
  18. package/dist/{mcp-YzpXuAUY.cjs → mcp-DgPg5mKa.cjs} +2 -2
  19. package/dist/{runGemini-C3jrBffn.mjs → runGemini-B3iv1QRL.mjs} +4 -4
  20. package/dist/{runGemini-BBAR1VMh.cjs → runGemini-xZnGj7jx.cjs} +4 -4
  21. package/dist/{runOpenClaw-CaA37yjx.cjs → runOpenClaw-6ZLPeYYZ.cjs} +3 -3
  22. package/dist/{runOpenClaw-B7pEvhUJ.mjs → runOpenClaw-CmM4ZLYi.mjs} +3 -3
  23. package/dist/{send-bhOlmnWQ.mjs → send-M2ax7UGy.mjs} +2 -2
  24. package/dist/{send-DHj783NY.cjs → send-S9ibMiAt.cjs} +2 -2
  25. package/dist/{spawn-DW_crRm0.mjs → spawn-Dx_xnLlg.mjs} +2 -2
  26. package/dist/{spawn-CVUvoWvq.cjs → spawn-ie_kfwQe.cjs} +2 -2
  27. package/dist/{types-1ZueD6ob.cjs → types-DLI6sss2.cjs} +10 -10
  28. package/dist/{types-DY9ef3Sf.mjs → types-d_104eQF.mjs} +9 -9
  29. package/package.json +7 -7
@@ -3,7 +3,7 @@
3
3
  var node_child_process = require('node:child_process');
4
4
  var sdk = require('@agentclientprotocol/sdk');
5
5
  var node_crypto = require('node:crypto');
6
- var persistence = require('./types-1ZueD6ob.cjs');
6
+ var persistence = require('./types-DLI6sss2.cjs');
7
7
 
8
8
  const DEFAULT_TIMEOUTS = {
9
9
  /** Default initialization timeout: 60 seconds */
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-DY9ef3Sf.mjs';
4
+ import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-d_104eQF.mjs';
5
5
 
6
6
  const DEFAULT_TIMEOUTS = {
7
7
  /** Default initialization timeout: 60 seconds */
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { b as createEnvelope } from './types-DY9ef3Sf.mjs';
2
+ import { b as createEnvelope } from './types-d_104eQF.mjs';
3
3
 
4
4
  function turnOptions(turnId, time) {
5
5
  return turnId ? { turn: turnId, time } : { time };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var cuid2 = require('@paralleldrive/cuid2');
4
- var persistence = require('./types-1ZueD6ob.cjs');
4
+ var persistence = require('./types-DLI6sss2.cjs');
5
5
 
6
6
  function turnOptions(turnId, time) {
7
7
  return turnId ? { turn: turnId, time } : { time };
@@ -4,8 +4,8 @@ var fs = require('fs');
4
4
  var path = require('path');
5
5
  var os = require('os');
6
6
  var child_process = require('child_process');
7
- var persistence = require('./types-1ZueD6ob.cjs');
8
- var index = require('./index-DveDBCaA.cjs');
7
+ var persistence = require('./types-DLI6sss2.cjs');
8
+ var index = require('./index-DYDAFqAN.cjs');
9
9
  require('axios');
10
10
  require('chalk');
11
11
  require('node:util');
@@ -2,8 +2,8 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { execSync } from 'child_process';
5
- import { l as logger } from './types-DY9ef3Sf.mjs';
6
- import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-UiX6h05D.mjs';
5
+ import { l as logger } from './types-d_104eQF.mjs';
6
+ import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-vCgb7QnZ.mjs';
7
7
  import 'axios';
8
8
  import 'chalk';
9
9
  import 'node:util';
@@ -1,10 +1,10 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-kotk9Pwu.mjs';
2
- export { a as DEFAULT_IDLE_TIMEOUT_MS, b as DEFAULT_TOOL_CALL_TIMEOUT_MS, e as extractErrorDetail, f as formatDuration, c as formatDurationMinutes, h as handleAgentMessageChunk, d as handleAgentThoughtChunk, g as handleLegacyMessageChunk, i as handlePlanUpdate, j as handleThinkingUpdate, k as handleToolCall, l as handleToolCallUpdate, p as parseArgsFromContent } from './AcpBackend-kotk9Pwu.mjs';
3
- import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-DY9ef3Sf.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-DGY1Ss6H.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-DuxcHPyn.mjs';
2
+ export { a as DEFAULT_IDLE_TIMEOUT_MS, b as DEFAULT_TOOL_CALL_TIMEOUT_MS, e as extractErrorDetail, f as formatDuration, c as formatDurationMinutes, h as handleAgentMessageChunk, d as handleAgentThoughtChunk, g as handleLegacyMessageChunk, i as handlePlanUpdate, j as handleThinkingUpdate, k as handleToolCall, l as handleToolCallUpdate, p as parseArgsFromContent } from './AcpBackend-DuxcHPyn.mjs';
3
+ import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-d_104eQF.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-CGrA8bTm.mjs';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { join } from 'node:path';
7
- import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, f as startHappyServer, r as registerKillSessionHandler, B as BasePermissionHandler } from './index-UiX6h05D.mjs';
7
+ import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, f as startHappyServer, r as registerKillSessionHandler, B as BasePermissionHandler } from './index-vCgb7QnZ.mjs';
8
8
  import 'node:child_process';
9
9
  import '@agentclientprotocol/sdk';
10
10
  import 'axios';
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-DxNg9T6Z.cjs');
4
- var persistence = require('./types-1ZueD6ob.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-Im0qDPE0.cjs');
3
+ var AcpBackend = require('./AcpBackend-ClEvE-A-.cjs');
4
+ var persistence = require('./types-DLI6sss2.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-DgbEf8FX.cjs');
6
6
  var node_crypto = require('node:crypto');
7
7
  var path = require('node:path');
8
- var index = require('./index-DveDBCaA.cjs');
8
+ var index = require('./index-DYDAFqAN.cjs');
9
9
  require('node:child_process');
10
10
  require('@agentclientprotocol/sdk');
11
11
  require('axios');
@@ -3,7 +3,7 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var persistence = require('./types-1ZueD6ob.cjs');
6
+ var persistence = require('./types-DLI6sss2.cjs');
7
7
  var spawn = require('cross-spawn');
8
8
  var path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -1528,7 +1528,7 @@ function query(params) {
1528
1528
  fallbackModel: opts?.fallbackModel,
1529
1529
  maxTurns: opts?.maxTurns,
1530
1530
  permissionMode: opts?.permissionMode,
1531
- allowDangerouslySkipPermissions: opts?.permissionMode === "bypassPermissions",
1531
+ allowDangerouslySkipPermissions: opts?.allowDangerouslySkipPermissions ?? opts?.permissionMode === "bypassPermissions",
1532
1532
  allowedTools: opts?.allowedTools,
1533
1533
  disallowedTools: opts?.disallowedTools,
1534
1534
  mcpServers: opts?.mcpServers,
@@ -1603,6 +1603,9 @@ const VALID_PERMISSION_MODES = [
1603
1603
  function isPermissionMode(value) {
1604
1604
  return !!value && VALID_PERMISSION_MODES.includes(value);
1605
1605
  }
1606
+ function parseClaudePermissionMode(value) {
1607
+ return typeof value === "string" && isPermissionMode(value) ? mapToClaudeMode(value) : null;
1608
+ }
1606
1609
  function extractPermissionModeFromClaudeArgs(claudeArgs) {
1607
1610
  if (!claudeArgs || claudeArgs.length === 0) {
1608
1611
  return void 0;
@@ -1903,6 +1906,10 @@ async function claudeRemote(opts) {
1903
1906
  resume: startFrom ?? void 0,
1904
1907
  mcpServers: opts.mcpServers,
1905
1908
  permissionMode: mapToClaudeMode(initial.mode.permissionMode),
1909
+ // This is only the SDK safety opt-in; permissionMode/canUseTool still
1910
+ // enforce the selected policy. It must be enabled at Query creation so
1911
+ // a later explicit live switch to bypassPermissions can succeed.
1912
+ allowDangerouslySkipPermissions: true,
1906
1913
  model: initial.mode.model,
1907
1914
  fallbackModel: initial.mode.fallbackModel,
1908
1915
  customSystemPrompt: initial.mode.customSystemPrompt ? initial.mode.customSystemPrompt + "\n\n" + systemPrompt : void 0,
@@ -2078,6 +2085,8 @@ class PermissionHandler {
2078
2085
  onPermissionRequestCallback;
2079
2086
  /** Callback to change permission mode on the active query (set by claudeRemote) */
2080
2087
  setPermissionModeCallback;
2088
+ modeChangeQueue = Promise.resolve();
2089
+ deferredPermissionMode;
2081
2090
  constructor(session) {
2082
2091
  this.session = session;
2083
2092
  this.setupClientHandler();
@@ -2089,7 +2098,7 @@ class PermissionHandler {
2089
2098
  this.onPermissionRequestCallback = callback;
2090
2099
  }
2091
2100
  handleModeChange(mode) {
2092
- this.permissionMode = mode;
2101
+ this.permissionMode = mapToClaudeMode(mode);
2093
2102
  }
2094
2103
  /**
2095
2104
  * Set callback to dynamically change permission mode on the active query.
@@ -2098,6 +2107,74 @@ class PermissionHandler {
2098
2107
  setPermissionModeUpdater(callback) {
2099
2108
  this.setPermissionModeCallback = callback;
2100
2109
  }
2110
+ /**
2111
+ * Apply a user-requested mode to the active SDK Query. Mode changes are
2112
+ * serialized so rapid menu selections cannot finish out of order.
2113
+ *
2114
+ * A live Query can be blocked inside canUseTool. When the requested mode
2115
+ * would have auto-allowed that ordinary tool, finish the callback first;
2116
+ * interactive prompts and ExitPlanMode deliberately remain pending.
2117
+ */
2118
+ setLivePermissionMode(mode) {
2119
+ const task = this.modeChangeQueue.then(() => this.applyLivePermissionMode(mode));
2120
+ this.modeChangeQueue = task.catch(() => void 0);
2121
+ return task;
2122
+ }
2123
+ async applyLivePermissionMode(mode) {
2124
+ if (!this.setPermissionModeCallback) {
2125
+ throw new Error("active Claude query is not ready");
2126
+ }
2127
+ const previousMode = this.permissionMode;
2128
+ this.permissionMode = mode;
2129
+ this.deferredPermissionMode = void 0;
2130
+ try {
2131
+ const autoApproved = [...this.pendingRequests.entries()].filter(([, pending]) => pending.kind === "tool" && this.modeAutoAllowsTool(mode, pending.toolName));
2132
+ for (const [id, pending] of autoApproved) {
2133
+ if (pending.kind !== "tool" || !this.pendingRequests.delete(id)) continue;
2134
+ const response = {
2135
+ id,
2136
+ approved: true,
2137
+ decision: "approved",
2138
+ reason: `Approved by live permission mode ${mode}`
2139
+ };
2140
+ const effectiveResponse = await this.handlePermissionResponse(response, pending);
2141
+ this.responses.set(id, { ...effectiveResponse, receivedAt: Date.now() });
2142
+ this.recordCompletedRequest(id, effectiveResponse);
2143
+ }
2144
+ if (this.pendingRequests.size > 0) {
2145
+ this.deferredPermissionMode = mode;
2146
+ return;
2147
+ }
2148
+ await new Promise((resolve) => setImmediate(resolve));
2149
+ await this.setPermissionModeCallback(mode);
2150
+ } catch (error) {
2151
+ this.permissionMode = previousMode;
2152
+ if (this.deferredPermissionMode === mode) this.deferredPermissionMode = void 0;
2153
+ throw error;
2154
+ }
2155
+ }
2156
+ scheduleDeferredPermissionModeUpdate() {
2157
+ const mode = this.deferredPermissionMode;
2158
+ if (!mode || this.pendingRequests.size > 0) return;
2159
+ this.deferredPermissionMode = void 0;
2160
+ setImmediate(() => {
2161
+ const task = this.modeChangeQueue.then(async () => {
2162
+ if (!this.setPermissionModeCallback || this.permissionMode !== mode) return;
2163
+ await this.setPermissionModeCallback(mode);
2164
+ });
2165
+ this.modeChangeQueue = task.catch((error) => {
2166
+ persistence.logger.debug("[PermissionHandler] Deferred SDK permission mode update failed", error);
2167
+ });
2168
+ });
2169
+ }
2170
+ modeAutoAllowsTool(mode, toolName) {
2171
+ if (toolName === "AskUserQuestion") return false;
2172
+ const descriptor = getToolDescriptor(toolName);
2173
+ if (descriptor.exitPlan) return false;
2174
+ if (mode === "bypassPermissions") return true;
2175
+ if (mode === "acceptEdits") return descriptor.edit;
2176
+ return mode === "plan" && !descriptor.dangerous;
2177
+ }
2101
2178
  /**
2102
2179
  * Handler response
2103
2180
  */
@@ -2342,6 +2419,30 @@ class PermissionHandler {
2342
2419
  };
2343
2420
  });
2344
2421
  }
2422
+ recordCompletedRequest(id, response) {
2423
+ this.session.client.updateAgentState((currentState) => {
2424
+ const request = currentState.requests?.[id];
2425
+ if (!request) return currentState;
2426
+ const requests = { ...currentState.requests };
2427
+ delete requests[id];
2428
+ return {
2429
+ ...currentState,
2430
+ requests,
2431
+ completedRequests: {
2432
+ ...currentState.completedRequests,
2433
+ [id]: {
2434
+ ...request,
2435
+ completedAt: Date.now(),
2436
+ status: response.approved ? "approved" : "denied",
2437
+ reason: response.reason,
2438
+ mode: response.mode,
2439
+ decision: response.decision,
2440
+ allowedTools: response.allowTools
2441
+ }
2442
+ }
2443
+ };
2444
+ });
2445
+ }
2345
2446
  /**
2346
2447
  * Parses Bash permission strings into literal and prefix sets
2347
2448
  */
@@ -2380,6 +2481,7 @@ class PermissionHandler {
2380
2481
  this.allowedBashLiterals.clear();
2381
2482
  this.allowedBashPrefixes.clear();
2382
2483
  this.permissionMode = "default";
2484
+ this.deferredPermissionMode = void 0;
2383
2485
  for (const [, pending] of this.pendingRequests.entries()) {
2384
2486
  pending.reject(new Error("Session reset"));
2385
2487
  }
@@ -2432,28 +2534,8 @@ class PermissionHandler {
2432
2534
  } else {
2433
2535
  pending.resolve(message.approved ? { behavior: "completed", result: message.updatedInput?.result ?? "retry_fallback" } : { behavior: "cancelled" });
2434
2536
  }
2435
- this.session.client.updateAgentState((currentState) => {
2436
- const request = currentState.requests?.[id];
2437
- if (!request) return currentState;
2438
- let r = { ...currentState.requests };
2439
- delete r[id];
2440
- return {
2441
- ...currentState,
2442
- requests: r,
2443
- completedRequests: {
2444
- ...currentState.completedRequests,
2445
- [id]: {
2446
- ...request,
2447
- completedAt: Date.now(),
2448
- status: effectiveResponse.approved ? "approved" : "denied",
2449
- reason: effectiveResponse.reason,
2450
- mode: effectiveResponse.mode,
2451
- decision: effectiveResponse.decision,
2452
- allowedTools: effectiveResponse.allowTools
2453
- }
2454
- }
2455
- };
2456
- });
2537
+ this.recordCompletedRequest(id, effectiveResponse);
2538
+ this.scheduleDeferredPermissionModeUpdate();
2457
2539
  });
2458
2540
  }
2459
2541
  /**
@@ -3229,7 +3311,7 @@ function createTurnSteeringController() {
3229
3311
  };
3230
3312
  }
3231
3313
 
3232
- async function claudeRemoteLauncher(session) {
3314
+ async function claudeRemoteLauncher(session, onPermissionModeChange) {
3233
3315
  persistence.logger.debug("[claudeRemoteLauncher] Starting remote launcher");
3234
3316
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
3235
3317
  persistence.logger.debug(`[claudeRemoteLauncher] TTY available: ${hasTTY}`);
@@ -3290,6 +3372,16 @@ async function claudeRemoteLauncher(session) {
3290
3372
  await turnSteering.steer();
3291
3373
  });
3292
3374
  session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
3375
+ let livePermissionModeHandler = null;
3376
+ session.client.rpcHandlerManager.registerHandler(
3377
+ "set-permission-mode",
3378
+ async (request) => {
3379
+ const requestedMode = parseClaudePermissionMode(request?.mode);
3380
+ if (!requestedMode) throw new Error("Invalid Claude permission mode");
3381
+ if (!livePermissionModeHandler) throw new Error("No active Claude query");
3382
+ return { mode: await livePermissionModeHandler(requestedMode) };
3383
+ }
3384
+ );
3293
3385
  const permissionHandler = new PermissionHandler(session);
3294
3386
  permissionHandler.reset("Previous CLI process exited before responding");
3295
3387
  const messageQueue = new OutgoingMessageQueue(
@@ -3526,6 +3618,15 @@ async function claudeRemoteLauncher(session) {
3526
3618
  permissionHandler.setPermissionModeUpdater(async (mode2) => {
3527
3619
  await q.setPermissionMode(mode2);
3528
3620
  });
3621
+ livePermissionModeHandler = async (nextMode) => {
3622
+ await permissionHandler.setLivePermissionMode(nextMode);
3623
+ if (mode) {
3624
+ mode = { ...mode, permissionMode: nextMode };
3625
+ modeHash = session.queue.modeHasher(mode);
3626
+ }
3627
+ onPermissionModeChange?.(nextMode);
3628
+ return nextMode;
3629
+ };
3529
3630
  },
3530
3631
  onThinkingChange: session.onThinkingChange,
3531
3632
  claudeEnvVars: session.claudeEnvVars,
@@ -3587,6 +3688,8 @@ async function claudeRemoteLauncher(session) {
3587
3688
  } finally {
3588
3689
  turnSteering.reset();
3589
3690
  session.setSteerHandler(null);
3691
+ livePermissionModeHandler = null;
3692
+ permissionHandler.setPermissionModeUpdater(void 0);
3590
3693
  sdkToLogConverter.resetTransientState();
3591
3694
  persistence.logger.debug("[remote]: launch finally");
3592
3695
  for (let [toolCallId, { parentToolCallId }] of ongoingToolCalls) {
@@ -3671,7 +3774,7 @@ async function loop(opts) {
3671
3774
  break;
3672
3775
  }
3673
3776
  case "remote": {
3674
- const reason = await claudeRemoteLauncher(session);
3777
+ const reason = await claudeRemoteLauncher(session, opts.onPermissionModeChange);
3675
3778
  switch (reason) {
3676
3779
  case "exit":
3677
3780
  return 0;
@@ -8704,7 +8807,7 @@ const MAX_TITLE_CHARS = 60;
8704
8807
  const GENERATION_TIMEOUT_MS = 3e4;
8705
8808
  function resolveClaudeBinary() {
8706
8809
  try {
8707
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DveDBCaA.cjs', document.baseURI).href)));
8810
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DYDAFqAN.cjs', document.baseURI).href)));
8708
8811
  const utilsPath = path.resolve(path.join(persistence.projectPath(), "scripts", "claude_version_utils.cjs"));
8709
8812
  const { getClaudeCliPath } = require$1(utilsPath);
8710
8813
  const path$1 = getClaudeCliPath();
@@ -9161,7 +9264,7 @@ async function runClaude(credentials, options = {}) {
9161
9264
  // a brand-new session cannot pick a PDF until after sending once.
9162
9265
  attachmentKinds: [...CLAUDE_ATTACHMENT_KINDS],
9163
9266
  queueCancellation: true,
9164
- capabilities: ["claude-steer-v1"],
9267
+ capabilities: ["claude-steer-v1", "claude-live-permission-v1"],
9165
9268
  sandbox: sandboxConfig?.enabled ? sandboxConfig : null,
9166
9269
  dangerouslySkipPermissions,
9167
9270
  ...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
@@ -9706,6 +9809,10 @@ async function runClaude(credentials, options = {}) {
9706
9809
  currentSession = sessionInstance;
9707
9810
  },
9708
9811
  onAbort: resetCurrentModeDefaults,
9812
+ onPermissionModeChange: (mode) => {
9813
+ currentPermissionMode = mode;
9814
+ persistence.logger.debug(`[loop] Permission mode updated from live session RPC to: ${mode}`);
9815
+ },
9709
9816
  mcpServers: {
9710
9817
  "happy": {
9711
9818
  type: "http",
@@ -10848,9 +10955,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
10848
10955
  };
10849
10956
  }
10850
10957
 
10851
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DveDBCaA.cjs', document.baseURI).href)));
10958
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DYDAFqAN.cjs', document.baseURI).href)));
10852
10959
  const __dirname$1 = path.dirname(__filename$1);
10853
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DveDBCaA.cjs', document.baseURI).href)));
10960
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DYDAFqAN.cjs', document.baseURI).href)));
10854
10961
  const PRISMA_QUERY_ENGINE_FILES = {
10855
10962
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
10856
10963
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -13462,7 +13569,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13462
13569
  process.exit(0);
13463
13570
  } else if (subcommand === "install-terminal-hooks") {
13464
13571
  try {
13465
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-fuWRm0w5.cjs'); });
13572
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-T4NE5fmC.cjs'); });
13466
13573
  const command = parseTerminalHooksArgs(args.slice(1));
13467
13574
  if (command.action === "help") {
13468
13575
  console.log(TERMINAL_HOOKS_HELP);
@@ -13479,7 +13586,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13479
13586
  }
13480
13587
  } else if (subcommand === "spawn") {
13481
13588
  try {
13482
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-CVUvoWvq.cjs'); });
13589
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-ie_kfwQe.cjs'); });
13483
13590
  await handleSpawnCommand(args.slice(1));
13484
13591
  } catch (error) {
13485
13592
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13491,7 +13598,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13491
13598
  return;
13492
13599
  } else if (subcommand === "send") {
13493
13600
  try {
13494
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DHj783NY.cjs'); });
13601
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-S9ibMiAt.cjs'); });
13495
13602
  await handleSendCommand(args.slice(1));
13496
13603
  } catch (error) {
13497
13604
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13597,9 +13704,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13597
13704
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
13598
13705
  const projectId = args[3];
13599
13706
  try {
13600
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-DcoxG-LO.cjs'); });
13601
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types-1ZueD6ob.cjs'); }).then(function (n) { return n.persistence; });
13602
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-1ZueD6ob.cjs'); }).then(function (n) { return n.api; });
13707
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-BZNnPK1L.cjs'); });
13708
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types-DLI6sss2.cjs'); }).then(function (n) { return n.persistence; });
13709
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-DLI6sss2.cjs'); }).then(function (n) { return n.api; });
13603
13710
  let userEmail = void 0;
13604
13711
  try {
13605
13712
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -13630,7 +13737,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13630
13737
  }
13631
13738
  if (geminiSubcommand === "project" && args[2] === "get") {
13632
13739
  try {
13633
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-DcoxG-LO.cjs'); });
13740
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-BZNnPK1L.cjs'); });
13634
13741
  const config = readGeminiLocalConfig();
13635
13742
  if (config.googleCloudProject) {
13636
13743
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -13670,7 +13777,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13670
13777
  process.exit(0);
13671
13778
  }
13672
13779
  try {
13673
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-BBAR1VMh.cjs'); });
13780
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-xZnGj7jx.cjs'); });
13674
13781
  let startedBy = void 0;
13675
13782
  for (let i = 1; i < args.length; i++) {
13676
13783
  if (args[i] === "--started-by") {
@@ -13692,7 +13799,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13692
13799
  return;
13693
13800
  } else if (subcommand === "acp") {
13694
13801
  try {
13695
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DBYxKaw7.cjs'); });
13802
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-Bx-UTV3I.cjs'); });
13696
13803
  let startedBy = void 0;
13697
13804
  let verbose = false;
13698
13805
  const acpArgs = [];
@@ -13732,7 +13839,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13732
13839
  return;
13733
13840
  } else if (subcommand === "openclaw") {
13734
13841
  try {
13735
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-CaA37yjx.cjs'); });
13842
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-6ZLPeYYZ.cjs'); });
13736
13843
  let startedBy = void 0;
13737
13844
  let verbose = false;
13738
13845
  let gatewayUrl;
@@ -13783,7 +13890,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13783
13890
  return;
13784
13891
  } else if (subcommand === "mcp" && args.length === 1) {
13785
13892
  try {
13786
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-YzpXuAUY.cjs'); });
13893
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-DgPg5mKa.cjs'); });
13787
13894
  await handleMcpCommand();
13788
13895
  } catch (error) {
13789
13896
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -1,7 +1,7 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
2
2
  import os, { homedir } from 'node:os';
3
3
  import { randomUUID, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
4
- import { n as readDaemonState, l as logger, c as configuration, o as clearDaemonState, q as getProjectPath, p as projectPath, s as initializeSandbox, t as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, g as delay, u as AsyncLock, v as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, x as hardenPrivateFile, f as packageJson, i as readSettings, y as readCredentials, z as credentialRelayProblem, B as deriveLocalCliUpdateSummary, j as encodeBase64, C as updateSettings, D as encodeBase64Url, E as decodeBase64, F as writeCredentialsLegacy, G as writeCredentialsDataKey, H as readCredentialsForConfiguredRelay, J as encrypt, r as readPersistedSessions, K as getLocalHappyAgentCredentialPath, L as readLocalHappyAgentCredentials, N as decryptWithDataKey, O as decryptLegacy, P as extractCompleteLines, Q as decideBackfill, S as parseTerminalHookPayload, T as decideMirrorBinding, U as persistSession, V as MIRROR_BACKFILL_LINES_DEFAULT, W as mirrorLineKey, X as mirrorLocalId, Y as detectResumeSupport, Z as detectCLIAvailability, _ as acquireDaemonLock, $ as summarizeSpawnSessionForLog, a0 as deletePersistedSessions, a1 as decrypt, a2 as writeDaemonState, A as ApiClient, a3 as fetchCliUpdateState, a4 as resolveCliUpdateCheckInterval, a5 as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, a6 as releaseDaemonLock, a7 as isValidSessionId, a8 as listVhTerminals, a9 as isValidTerminalId, aa as readVhTerminal, ab as sendToVhTerminal, h as connectionState, ac as startOfflineReconnection, ad as clearCredentials, ae as clearMachineId, k as ensurePrivateDirectorySync, w as writePrivateFileSync, af as SandboxConfigSchema, b as createEnvelope, ag as CodexAppServerClient, ah as getLatestDaemonLog } from './types-DY9ef3Sf.mjs';
4
+ import { n as readDaemonState, l as logger, c as configuration, o as clearDaemonState, q as getProjectPath, p as projectPath, s as initializeSandbox, t as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, g as delay, u as AsyncLock, v as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, x as hardenPrivateFile, f as packageJson, i as readSettings, y as readCredentials, z as credentialRelayProblem, B as deriveLocalCliUpdateSummary, j as encodeBase64, C as updateSettings, D as encodeBase64Url, E as decodeBase64, F as writeCredentialsLegacy, G as writeCredentialsDataKey, H as readCredentialsForConfiguredRelay, J as encrypt, r as readPersistedSessions, K as getLocalHappyAgentCredentialPath, L as readLocalHappyAgentCredentials, N as decryptWithDataKey, O as decryptLegacy, P as extractCompleteLines, Q as decideBackfill, S as parseTerminalHookPayload, T as decideMirrorBinding, U as persistSession, V as MIRROR_BACKFILL_LINES_DEFAULT, W as mirrorLineKey, X as mirrorLocalId, Y as detectResumeSupport, Z as detectCLIAvailability, _ as acquireDaemonLock, $ as summarizeSpawnSessionForLog, a0 as deletePersistedSessions, a1 as decrypt, a2 as writeDaemonState, A as ApiClient, a3 as fetchCliUpdateState, a4 as resolveCliUpdateCheckInterval, a5 as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, a6 as releaseDaemonLock, a7 as isValidSessionId, a8 as listVhTerminals, a9 as isValidTerminalId, aa as readVhTerminal, ab as sendToVhTerminal, h as connectionState, ac as startOfflineReconnection, ad as clearCredentials, ae as clearMachineId, k as ensurePrivateDirectorySync, w as writePrivateFileSync, af as SandboxConfigSchema, b as createEnvelope, ag as CodexAppServerClient, ah as getLatestDaemonLog } from './types-d_104eQF.mjs';
5
5
  import spawn$1, { spawn } from 'cross-spawn';
6
6
  import path, { join, resolve, basename, isAbsolute, normalize, sep, dirname } from 'node:path';
7
7
  import { createInterface } from 'node:readline';
@@ -1506,7 +1506,7 @@ function query(params) {
1506
1506
  fallbackModel: opts?.fallbackModel,
1507
1507
  maxTurns: opts?.maxTurns,
1508
1508
  permissionMode: opts?.permissionMode,
1509
- allowDangerouslySkipPermissions: opts?.permissionMode === "bypassPermissions",
1509
+ allowDangerouslySkipPermissions: opts?.allowDangerouslySkipPermissions ?? opts?.permissionMode === "bypassPermissions",
1510
1510
  allowedTools: opts?.allowedTools,
1511
1511
  disallowedTools: opts?.disallowedTools,
1512
1512
  mcpServers: opts?.mcpServers,
@@ -1581,6 +1581,9 @@ const VALID_PERMISSION_MODES = [
1581
1581
  function isPermissionMode(value) {
1582
1582
  return !!value && VALID_PERMISSION_MODES.includes(value);
1583
1583
  }
1584
+ function parseClaudePermissionMode(value) {
1585
+ return typeof value === "string" && isPermissionMode(value) ? mapToClaudeMode(value) : null;
1586
+ }
1584
1587
  function extractPermissionModeFromClaudeArgs(claudeArgs) {
1585
1588
  if (!claudeArgs || claudeArgs.length === 0) {
1586
1589
  return void 0;
@@ -1881,6 +1884,10 @@ async function claudeRemote(opts) {
1881
1884
  resume: startFrom ?? void 0,
1882
1885
  mcpServers: opts.mcpServers,
1883
1886
  permissionMode: mapToClaudeMode(initial.mode.permissionMode),
1887
+ // This is only the SDK safety opt-in; permissionMode/canUseTool still
1888
+ // enforce the selected policy. It must be enabled at Query creation so
1889
+ // a later explicit live switch to bypassPermissions can succeed.
1890
+ allowDangerouslySkipPermissions: true,
1884
1891
  model: initial.mode.model,
1885
1892
  fallbackModel: initial.mode.fallbackModel,
1886
1893
  customSystemPrompt: initial.mode.customSystemPrompt ? initial.mode.customSystemPrompt + "\n\n" + systemPrompt : void 0,
@@ -2056,6 +2063,8 @@ class PermissionHandler {
2056
2063
  onPermissionRequestCallback;
2057
2064
  /** Callback to change permission mode on the active query (set by claudeRemote) */
2058
2065
  setPermissionModeCallback;
2066
+ modeChangeQueue = Promise.resolve();
2067
+ deferredPermissionMode;
2059
2068
  constructor(session) {
2060
2069
  this.session = session;
2061
2070
  this.setupClientHandler();
@@ -2067,7 +2076,7 @@ class PermissionHandler {
2067
2076
  this.onPermissionRequestCallback = callback;
2068
2077
  }
2069
2078
  handleModeChange(mode) {
2070
- this.permissionMode = mode;
2079
+ this.permissionMode = mapToClaudeMode(mode);
2071
2080
  }
2072
2081
  /**
2073
2082
  * Set callback to dynamically change permission mode on the active query.
@@ -2076,6 +2085,74 @@ class PermissionHandler {
2076
2085
  setPermissionModeUpdater(callback) {
2077
2086
  this.setPermissionModeCallback = callback;
2078
2087
  }
2088
+ /**
2089
+ * Apply a user-requested mode to the active SDK Query. Mode changes are
2090
+ * serialized so rapid menu selections cannot finish out of order.
2091
+ *
2092
+ * A live Query can be blocked inside canUseTool. When the requested mode
2093
+ * would have auto-allowed that ordinary tool, finish the callback first;
2094
+ * interactive prompts and ExitPlanMode deliberately remain pending.
2095
+ */
2096
+ setLivePermissionMode(mode) {
2097
+ const task = this.modeChangeQueue.then(() => this.applyLivePermissionMode(mode));
2098
+ this.modeChangeQueue = task.catch(() => void 0);
2099
+ return task;
2100
+ }
2101
+ async applyLivePermissionMode(mode) {
2102
+ if (!this.setPermissionModeCallback) {
2103
+ throw new Error("active Claude query is not ready");
2104
+ }
2105
+ const previousMode = this.permissionMode;
2106
+ this.permissionMode = mode;
2107
+ this.deferredPermissionMode = void 0;
2108
+ try {
2109
+ const autoApproved = [...this.pendingRequests.entries()].filter(([, pending]) => pending.kind === "tool" && this.modeAutoAllowsTool(mode, pending.toolName));
2110
+ for (const [id, pending] of autoApproved) {
2111
+ if (pending.kind !== "tool" || !this.pendingRequests.delete(id)) continue;
2112
+ const response = {
2113
+ id,
2114
+ approved: true,
2115
+ decision: "approved",
2116
+ reason: `Approved by live permission mode ${mode}`
2117
+ };
2118
+ const effectiveResponse = await this.handlePermissionResponse(response, pending);
2119
+ this.responses.set(id, { ...effectiveResponse, receivedAt: Date.now() });
2120
+ this.recordCompletedRequest(id, effectiveResponse);
2121
+ }
2122
+ if (this.pendingRequests.size > 0) {
2123
+ this.deferredPermissionMode = mode;
2124
+ return;
2125
+ }
2126
+ await new Promise((resolve) => setImmediate(resolve));
2127
+ await this.setPermissionModeCallback(mode);
2128
+ } catch (error) {
2129
+ this.permissionMode = previousMode;
2130
+ if (this.deferredPermissionMode === mode) this.deferredPermissionMode = void 0;
2131
+ throw error;
2132
+ }
2133
+ }
2134
+ scheduleDeferredPermissionModeUpdate() {
2135
+ const mode = this.deferredPermissionMode;
2136
+ if (!mode || this.pendingRequests.size > 0) return;
2137
+ this.deferredPermissionMode = void 0;
2138
+ setImmediate(() => {
2139
+ const task = this.modeChangeQueue.then(async () => {
2140
+ if (!this.setPermissionModeCallback || this.permissionMode !== mode) return;
2141
+ await this.setPermissionModeCallback(mode);
2142
+ });
2143
+ this.modeChangeQueue = task.catch((error) => {
2144
+ logger.debug("[PermissionHandler] Deferred SDK permission mode update failed", error);
2145
+ });
2146
+ });
2147
+ }
2148
+ modeAutoAllowsTool(mode, toolName) {
2149
+ if (toolName === "AskUserQuestion") return false;
2150
+ const descriptor = getToolDescriptor(toolName);
2151
+ if (descriptor.exitPlan) return false;
2152
+ if (mode === "bypassPermissions") return true;
2153
+ if (mode === "acceptEdits") return descriptor.edit;
2154
+ return mode === "plan" && !descriptor.dangerous;
2155
+ }
2079
2156
  /**
2080
2157
  * Handler response
2081
2158
  */
@@ -2320,6 +2397,30 @@ class PermissionHandler {
2320
2397
  };
2321
2398
  });
2322
2399
  }
2400
+ recordCompletedRequest(id, response) {
2401
+ this.session.client.updateAgentState((currentState) => {
2402
+ const request = currentState.requests?.[id];
2403
+ if (!request) return currentState;
2404
+ const requests = { ...currentState.requests };
2405
+ delete requests[id];
2406
+ return {
2407
+ ...currentState,
2408
+ requests,
2409
+ completedRequests: {
2410
+ ...currentState.completedRequests,
2411
+ [id]: {
2412
+ ...request,
2413
+ completedAt: Date.now(),
2414
+ status: response.approved ? "approved" : "denied",
2415
+ reason: response.reason,
2416
+ mode: response.mode,
2417
+ decision: response.decision,
2418
+ allowedTools: response.allowTools
2419
+ }
2420
+ }
2421
+ };
2422
+ });
2423
+ }
2323
2424
  /**
2324
2425
  * Parses Bash permission strings into literal and prefix sets
2325
2426
  */
@@ -2358,6 +2459,7 @@ class PermissionHandler {
2358
2459
  this.allowedBashLiterals.clear();
2359
2460
  this.allowedBashPrefixes.clear();
2360
2461
  this.permissionMode = "default";
2462
+ this.deferredPermissionMode = void 0;
2361
2463
  for (const [, pending] of this.pendingRequests.entries()) {
2362
2464
  pending.reject(new Error("Session reset"));
2363
2465
  }
@@ -2410,28 +2512,8 @@ class PermissionHandler {
2410
2512
  } else {
2411
2513
  pending.resolve(message.approved ? { behavior: "completed", result: message.updatedInput?.result ?? "retry_fallback" } : { behavior: "cancelled" });
2412
2514
  }
2413
- this.session.client.updateAgentState((currentState) => {
2414
- const request = currentState.requests?.[id];
2415
- if (!request) return currentState;
2416
- let r = { ...currentState.requests };
2417
- delete r[id];
2418
- return {
2419
- ...currentState,
2420
- requests: r,
2421
- completedRequests: {
2422
- ...currentState.completedRequests,
2423
- [id]: {
2424
- ...request,
2425
- completedAt: Date.now(),
2426
- status: effectiveResponse.approved ? "approved" : "denied",
2427
- reason: effectiveResponse.reason,
2428
- mode: effectiveResponse.mode,
2429
- decision: effectiveResponse.decision,
2430
- allowedTools: effectiveResponse.allowTools
2431
- }
2432
- }
2433
- };
2434
- });
2515
+ this.recordCompletedRequest(id, effectiveResponse);
2516
+ this.scheduleDeferredPermissionModeUpdate();
2435
2517
  });
2436
2518
  }
2437
2519
  /**
@@ -3207,7 +3289,7 @@ function createTurnSteeringController() {
3207
3289
  };
3208
3290
  }
3209
3291
 
3210
- async function claudeRemoteLauncher(session) {
3292
+ async function claudeRemoteLauncher(session, onPermissionModeChange) {
3211
3293
  logger.debug("[claudeRemoteLauncher] Starting remote launcher");
3212
3294
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
3213
3295
  logger.debug(`[claudeRemoteLauncher] TTY available: ${hasTTY}`);
@@ -3268,6 +3350,16 @@ async function claudeRemoteLauncher(session) {
3268
3350
  await turnSteering.steer();
3269
3351
  });
3270
3352
  session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
3353
+ let livePermissionModeHandler = null;
3354
+ session.client.rpcHandlerManager.registerHandler(
3355
+ "set-permission-mode",
3356
+ async (request) => {
3357
+ const requestedMode = parseClaudePermissionMode(request?.mode);
3358
+ if (!requestedMode) throw new Error("Invalid Claude permission mode");
3359
+ if (!livePermissionModeHandler) throw new Error("No active Claude query");
3360
+ return { mode: await livePermissionModeHandler(requestedMode) };
3361
+ }
3362
+ );
3271
3363
  const permissionHandler = new PermissionHandler(session);
3272
3364
  permissionHandler.reset("Previous CLI process exited before responding");
3273
3365
  const messageQueue = new OutgoingMessageQueue(
@@ -3504,6 +3596,15 @@ async function claudeRemoteLauncher(session) {
3504
3596
  permissionHandler.setPermissionModeUpdater(async (mode2) => {
3505
3597
  await q.setPermissionMode(mode2);
3506
3598
  });
3599
+ livePermissionModeHandler = async (nextMode) => {
3600
+ await permissionHandler.setLivePermissionMode(nextMode);
3601
+ if (mode) {
3602
+ mode = { ...mode, permissionMode: nextMode };
3603
+ modeHash = session.queue.modeHasher(mode);
3604
+ }
3605
+ onPermissionModeChange?.(nextMode);
3606
+ return nextMode;
3607
+ };
3507
3608
  },
3508
3609
  onThinkingChange: session.onThinkingChange,
3509
3610
  claudeEnvVars: session.claudeEnvVars,
@@ -3565,6 +3666,8 @@ async function claudeRemoteLauncher(session) {
3565
3666
  } finally {
3566
3667
  turnSteering.reset();
3567
3668
  session.setSteerHandler(null);
3669
+ livePermissionModeHandler = null;
3670
+ permissionHandler.setPermissionModeUpdater(void 0);
3568
3671
  sdkToLogConverter.resetTransientState();
3569
3672
  logger.debug("[remote]: launch finally");
3570
3673
  for (let [toolCallId, { parentToolCallId }] of ongoingToolCalls) {
@@ -3649,7 +3752,7 @@ async function loop(opts) {
3649
3752
  break;
3650
3753
  }
3651
3754
  case "remote": {
3652
- const reason = await claudeRemoteLauncher(session);
3755
+ const reason = await claudeRemoteLauncher(session, opts.onPermissionModeChange);
3653
3756
  switch (reason) {
3654
3757
  case "exit":
3655
3758
  return 0;
@@ -9139,7 +9242,7 @@ async function runClaude(credentials, options = {}) {
9139
9242
  // a brand-new session cannot pick a PDF until after sending once.
9140
9243
  attachmentKinds: [...CLAUDE_ATTACHMENT_KINDS],
9141
9244
  queueCancellation: true,
9142
- capabilities: ["claude-steer-v1"],
9245
+ capabilities: ["claude-steer-v1", "claude-live-permission-v1"],
9143
9246
  sandbox: sandboxConfig?.enabled ? sandboxConfig : null,
9144
9247
  dangerouslySkipPermissions,
9145
9248
  ...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
@@ -9684,6 +9787,10 @@ async function runClaude(credentials, options = {}) {
9684
9787
  currentSession = sessionInstance;
9685
9788
  },
9686
9789
  onAbort: resetCurrentModeDefaults,
9790
+ onPermissionModeChange: (mode) => {
9791
+ currentPermissionMode = mode;
9792
+ logger.debug(`[loop] Permission mode updated from live session RPC to: ${mode}`);
9793
+ },
9687
9794
  mcpServers: {
9688
9795
  "happy": {
9689
9796
  type: "http",
@@ -13440,7 +13547,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13440
13547
  process.exit(0);
13441
13548
  } else if (subcommand === "install-terminal-hooks") {
13442
13549
  try {
13443
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-BXxMZXNx.mjs');
13550
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-C7JS6CBM.mjs');
13444
13551
  const command = parseTerminalHooksArgs(args.slice(1));
13445
13552
  if (command.action === "help") {
13446
13553
  console.log(TERMINAL_HOOKS_HELP);
@@ -13457,7 +13564,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13457
13564
  }
13458
13565
  } else if (subcommand === "spawn") {
13459
13566
  try {
13460
- const { handleSpawnCommand } = await import('./spawn-DW_crRm0.mjs');
13567
+ const { handleSpawnCommand } = await import('./spawn-Dx_xnLlg.mjs');
13461
13568
  await handleSpawnCommand(args.slice(1));
13462
13569
  } catch (error) {
13463
13570
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13469,7 +13576,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13469
13576
  return;
13470
13577
  } else if (subcommand === "send") {
13471
13578
  try {
13472
- const { handleSendCommand } = await import('./send-bhOlmnWQ.mjs');
13579
+ const { handleSendCommand } = await import('./send-M2ax7UGy.mjs');
13473
13580
  await handleSendCommand(args.slice(1));
13474
13581
  } catch (error) {
13475
13582
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13575,9 +13682,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13575
13682
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
13576
13683
  const projectId = args[3];
13577
13684
  try {
13578
- const { saveGoogleCloudProjectToConfig } = await import('./config-CDMM_UhY.mjs');
13579
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-DY9ef3Sf.mjs').then(function (n) { return n.ai; });
13580
- const { ApiClient: ApiClient2 } = await import('./types-DY9ef3Sf.mjs').then(function (n) { return n.aj; });
13685
+ const { saveGoogleCloudProjectToConfig } = await import('./config-CdoGLla8.mjs');
13686
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-d_104eQF.mjs').then(function (n) { return n.ai; });
13687
+ const { ApiClient: ApiClient2 } = await import('./types-d_104eQF.mjs').then(function (n) { return n.aj; });
13581
13688
  let userEmail = void 0;
13582
13689
  try {
13583
13690
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -13608,7 +13715,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13608
13715
  }
13609
13716
  if (geminiSubcommand === "project" && args[2] === "get") {
13610
13717
  try {
13611
- const { readGeminiLocalConfig } = await import('./config-CDMM_UhY.mjs');
13718
+ const { readGeminiLocalConfig } = await import('./config-CdoGLla8.mjs');
13612
13719
  const config = readGeminiLocalConfig();
13613
13720
  if (config.googleCloudProject) {
13614
13721
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -13648,7 +13755,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13648
13755
  process.exit(0);
13649
13756
  }
13650
13757
  try {
13651
- const { runGemini } = await import('./runGemini-C3jrBffn.mjs');
13758
+ const { runGemini } = await import('./runGemini-B3iv1QRL.mjs');
13652
13759
  let startedBy = void 0;
13653
13760
  for (let i = 1; i < args.length; i++) {
13654
13761
  if (args[i] === "--started-by") {
@@ -13670,7 +13777,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13670
13777
  return;
13671
13778
  } else if (subcommand === "acp") {
13672
13779
  try {
13673
- const { runAcp, resolveAcpAgentConfig } = await import('./index-DLI9RIsu.mjs');
13780
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-BP2wylez.mjs');
13674
13781
  let startedBy = void 0;
13675
13782
  let verbose = false;
13676
13783
  const acpArgs = [];
@@ -13710,7 +13817,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13710
13817
  return;
13711
13818
  } else if (subcommand === "openclaw") {
13712
13819
  try {
13713
- const { runOpenClaw } = await import('./runOpenClaw-B7pEvhUJ.mjs');
13820
+ const { runOpenClaw } = await import('./runOpenClaw-CmM4ZLYi.mjs');
13714
13821
  let startedBy = void 0;
13715
13822
  let verbose = false;
13716
13823
  let gatewayUrl;
@@ -13761,7 +13868,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13761
13868
  return;
13762
13869
  } else if (subcommand === "mcp" && args.length === 1) {
13763
13870
  try {
13764
- const { handleMcpCommand } = await import('./mcp-CgOiXU_f.mjs');
13871
+ const { handleMcpCommand } = await import('./mcp-B3EgenZh.mjs');
13765
13872
  await handleMcpCommand();
13766
13873
  } catch (error) {
13767
13874
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  require('chalk');
4
- require('./index-DveDBCaA.cjs');
5
- require('./types-1ZueD6ob.cjs');
4
+ require('./index-DYDAFqAN.cjs');
5
+ require('./types-DLI6sss2.cjs');
6
6
  require('zod');
7
7
  require('node:child_process');
8
8
  require('node:os');
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'chalk';
2
- import './index-UiX6h05D.mjs';
3
- import './types-DY9ef3Sf.mjs';
2
+ import './index-vCgb7QnZ.mjs';
3
+ import './types-d_104eQF.mjs';
4
4
  import 'zod';
5
5
  import 'node:child_process';
6
6
  import 'node:os';
@@ -3,8 +3,8 @@ import { join, resolve } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import chalk from 'chalk';
6
- import { p as projectPath } from './types-DY9ef3Sf.mjs';
7
- import { t as tmuxSupportsSessionEnv } from './index-UiX6h05D.mjs';
6
+ import { p as projectPath } from './types-d_104eQF.mjs';
7
+ import { t as tmuxSupportsSessionEnv } from './index-vCgb7QnZ.mjs';
8
8
  import 'axios';
9
9
  import 'node:util';
10
10
  import 'node:fs/promises';
@@ -5,8 +5,8 @@ var path = require('node:path');
5
5
  var os = require('node:os');
6
6
  var node_child_process = require('node:child_process');
7
7
  var chalk = require('chalk');
8
- var persistence = require('./types-1ZueD6ob.cjs');
9
- var index = require('./index-DveDBCaA.cjs');
8
+ var persistence = require('./types-DLI6sss2.cjs');
9
+ var index = require('./index-DYDAFqAN.cjs');
10
10
  require('axios');
11
11
  require('node:util');
12
12
  require('node:fs/promises');
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var persistence = require('./types-1ZueD6ob.cjs');
3
+ var persistence = require('./types-DLI6sss2.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('node:util');
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-DY9ef3Sf.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-d_104eQF.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'node:util';
@@ -1,9 +1,9 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { z } from 'zod';
4
- import { p as pushClipboardViaDaemon } from './index-UiX6h05D.mjs';
4
+ import { p as pushClipboardViaDaemon } from './index-vCgb7QnZ.mjs';
5
5
  import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, c as CLIPBOARD_MAX_BYTES } from './limits-CwFb5S2b.mjs';
6
- import { l as logger } from './types-DY9ef3Sf.mjs';
6
+ import { l as logger } from './types-d_104eQF.mjs';
7
7
  import 'chalk';
8
8
  import 'node:os';
9
9
  import 'node:crypto';
@@ -3,9 +3,9 @@
3
3
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
4
4
  var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
5
5
  var z = require('zod');
6
- var index = require('./index-DveDBCaA.cjs');
6
+ var index = require('./index-DYDAFqAN.cjs');
7
7
  var limits = require('./limits-KdWKmwtH.cjs');
8
- var persistence = require('./types-1ZueD6ob.cjs');
8
+ var persistence = require('./types-DLI6sss2.cjs');
9
9
  require('chalk');
10
10
  require('node:os');
11
11
  require('node:crypto');
@@ -2,10 +2,10 @@ import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { join } from 'node:path';
5
- import { l as logger, h as connectionState, A as ApiClient, i as readSettings, e as errorLogMetadata, j as encodeBase64, p as projectPath, d as contentLogMetadata } from './types-DY9ef3Sf.mjs';
6
- import { g as GEMINI_API_KEY_ENV, j as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, B as BasePermissionHandler, k as BaseReasoningProcessor, i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, l as MessageBuffer, C as CHANGE_TITLE_INSTRUCTION, r as registerKillSessionHandler, f as startHappyServer } from './index-UiX6h05D.mjs';
7
- import { A as AcpBackend } from './AcpBackend-kotk9Pwu.mjs';
8
- import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-CDMM_UhY.mjs';
5
+ import { l as logger, h as connectionState, A as ApiClient, i as readSettings, e as errorLogMetadata, j as encodeBase64, p as projectPath, d as contentLogMetadata } from './types-d_104eQF.mjs';
6
+ import { g as GEMINI_API_KEY_ENV, j as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, B as BasePermissionHandler, k as BaseReasoningProcessor, i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, l as MessageBuffer, C as CHANGE_TITLE_INSTRUCTION, r as registerKillSessionHandler, f as startHappyServer } from './index-vCgb7QnZ.mjs';
7
+ import { A as AcpBackend } from './AcpBackend-DuxcHPyn.mjs';
8
+ import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-CdoGLla8.mjs';
9
9
  import 'axios';
10
10
  import 'chalk';
11
11
  import 'node:util';
@@ -4,10 +4,10 @@ var ink = require('ink');
4
4
  var React = require('react');
5
5
  var node_crypto = require('node:crypto');
6
6
  var path = require('node:path');
7
- var persistence = require('./types-1ZueD6ob.cjs');
8
- var index = require('./index-DveDBCaA.cjs');
9
- var AcpBackend = require('./AcpBackend-DxNg9T6Z.cjs');
10
- var config = require('./config-DcoxG-LO.cjs');
7
+ var persistence = require('./types-DLI6sss2.cjs');
8
+ var index = require('./index-DYDAFqAN.cjs');
9
+ var AcpBackend = require('./AcpBackend-ClEvE-A-.cjs');
10
+ var config = require('./config-BZNnPK1L.cjs');
11
11
  require('axios');
12
12
  require('chalk');
13
13
  require('node:util');
@@ -5,9 +5,9 @@ var node_child_process = require('node:child_process');
5
5
  var os = require('node:os');
6
6
  var node_fs = require('node:fs');
7
7
  var path = require('node:path');
8
- var persistence = require('./types-1ZueD6ob.cjs');
9
- var AcpSessionManager = require('./AcpSessionManager-Im0qDPE0.cjs');
10
- var index = require('./index-DveDBCaA.cjs');
8
+ var persistence = require('./types-DLI6sss2.cjs');
9
+ var AcpSessionManager = require('./AcpSessionManager-DgbEf8FX.cjs');
10
+ var index = require('./index-DYDAFqAN.cjs');
11
11
  var WebSocket = require('ws');
12
12
  var ed = require('@noble/ed25519');
13
13
  var sha2_js = require('@noble/hashes/sha2.js');
@@ -3,9 +3,9 @@ import { execFileSync } from 'node:child_process';
3
3
  import os from 'node:os';
4
4
  import { existsSync, readFileSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
- import { k as ensurePrivateDirectorySync, m as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, c as configuration } from './types-DY9ef3Sf.mjs';
7
- import { A as AcpSessionManager } from './AcpSessionManager-DGY1Ss6H.mjs';
8
- import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, r as registerKillSessionHandler } from './index-UiX6h05D.mjs';
6
+ import { k as ensurePrivateDirectorySync, m as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, c as configuration } from './types-d_104eQF.mjs';
7
+ import { A as AcpSessionManager } from './AcpSessionManager-CGrA8bTm.mjs';
8
+ import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, r as registerKillSessionHandler } from './index-vCgb7QnZ.mjs';
9
9
  import WebSocket from 'ws';
10
10
  import * as ed from '@noble/ed25519';
11
11
  import { sha512 } from '@noble/hashes/sha2.js';
@@ -1,8 +1,8 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
- import { r as readPersistedSessions, c as configuration } from './types-DY9ef3Sf.mjs';
5
- import { b as sendUserMessage, a as sessionWebUrl } from './index-UiX6h05D.mjs';
4
+ import { r as readPersistedSessions, c as configuration } from './types-d_104eQF.mjs';
5
+ import { b as sendUserMessage, a as sessionWebUrl } from './index-vCgb7QnZ.mjs';
6
6
  import 'axios';
7
7
  import 'node:util';
8
8
  import 'node:os';
@@ -3,8 +3,8 @@
3
3
  var chalk = require('chalk');
4
4
  var node_fs = require('node:fs');
5
5
  var path = require('node:path');
6
- var persistence = require('./types-1ZueD6ob.cjs');
7
- var index = require('./index-DveDBCaA.cjs');
6
+ var persistence = require('./types-DLI6sss2.cjs');
7
+ var index = require('./index-DYDAFqAN.cjs');
8
8
  require('axios');
9
9
  require('node:util');
10
10
  require('node:os');
@@ -1,8 +1,8 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
- import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, w as waitForSessionKey, b as sendUserMessage } from './index-UiX6h05D.mjs';
5
- import { l as logger } from './types-DY9ef3Sf.mjs';
4
+ import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, w as waitForSessionKey, b as sendUserMessage } from './index-vCgb7QnZ.mjs';
5
+ import { l as logger } from './types-d_104eQF.mjs';
6
6
  import 'node:os';
7
7
  import 'node:crypto';
8
8
  import 'cross-spawn';
@@ -3,8 +3,8 @@
3
3
  var chalk = require('chalk');
4
4
  var node_fs = require('node:fs');
5
5
  var path = require('node:path');
6
- var index = require('./index-DveDBCaA.cjs');
7
- var persistence = require('./types-1ZueD6ob.cjs');
6
+ var index = require('./index-DYDAFqAN.cjs');
7
+ var persistence = require('./types-DLI6sss2.cjs');
8
8
  require('node:os');
9
9
  require('node:crypto');
10
10
  require('cross-spawn');
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
51
51
  var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
52
52
 
53
53
  var name = "very-happy-cli";
54
- var version = "0.2.87";
54
+ var version = "0.2.89";
55
55
  var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
56
56
  var author = "Kirill Dubovitskiy";
57
57
  var contributors = [
@@ -164,12 +164,12 @@ var dependencies = {
164
164
  zod: "^4.0.0"
165
165
  };
166
166
  var optionalDependencies = {
167
- "very-happy-tools-arm64-darwin": "0.2.87",
168
- "very-happy-tools-x64-darwin": "0.2.87",
169
- "very-happy-tools-arm64-linux": "0.2.87",
170
- "very-happy-tools-x64-linux": "0.2.87",
171
- "very-happy-tools-arm64-win32": "0.2.87",
172
- "very-happy-tools-x64-win32": "0.2.87"
167
+ "very-happy-tools-arm64-darwin": "0.2.89",
168
+ "very-happy-tools-x64-darwin": "0.2.89",
169
+ "very-happy-tools-arm64-linux": "0.2.89",
170
+ "very-happy-tools-x64-linux": "0.2.89",
171
+ "very-happy-tools-arm64-win32": "0.2.89",
172
+ "very-happy-tools-x64-win32": "0.2.89"
173
173
  };
174
174
  var devDependencies = {
175
175
  "@slopus/happy-wire": "workspace:*",
@@ -1484,7 +1484,7 @@ class RpcHandlerManager {
1484
1484
  }
1485
1485
  }
1486
1486
 
1487
- const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-1ZueD6ob.cjs', document.baseURI).href))));
1487
+ const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-DLI6sss2.cjs', document.baseURI).href))));
1488
1488
  function projectPath() {
1489
1489
  const path = path$1.resolve(__dirname$1, "..");
1490
1490
  return path;
@@ -2556,11 +2556,11 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
2556
2556
  const usage = pickAssistantUsage(message);
2557
2557
  const blocks = Array.isArray(message.message?.content) ? message.message.content : [];
2558
2558
  for (const block of blocks) {
2559
- if (block.type === "text" && typeof block.text === "string") {
2559
+ if (block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) {
2560
2560
  envelopes.push(createEnvelope("agent", { t: "text", text: block.text }, { turn: turnId, subagent, claudeUuid, usage }));
2561
2561
  continue;
2562
2562
  }
2563
- if (block.type === "thinking" && typeof block.thinking === "string") {
2563
+ if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim().length > 0) {
2564
2564
  envelopes.push(createEnvelope("agent", { t: "text", text: block.thinking, thinking: true }, { turn: turnId, subagent, claudeUuid, usage }));
2565
2565
  continue;
2566
2566
  }
@@ -29,7 +29,7 @@ import { SandboxManager } from '@anthropic-ai/sandbox-runtime';
29
29
  import { Expo } from 'expo-server-sdk';
30
30
 
31
31
  var name = "very-happy-cli";
32
- var version = "0.2.87";
32
+ var version = "0.2.89";
33
33
  var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
34
34
  var author = "Kirill Dubovitskiy";
35
35
  var contributors = [
@@ -142,12 +142,12 @@ var dependencies = {
142
142
  zod: "^4.0.0"
143
143
  };
144
144
  var optionalDependencies = {
145
- "very-happy-tools-arm64-darwin": "0.2.87",
146
- "very-happy-tools-x64-darwin": "0.2.87",
147
- "very-happy-tools-arm64-linux": "0.2.87",
148
- "very-happy-tools-x64-linux": "0.2.87",
149
- "very-happy-tools-arm64-win32": "0.2.87",
150
- "very-happy-tools-x64-win32": "0.2.87"
145
+ "very-happy-tools-arm64-darwin": "0.2.89",
146
+ "very-happy-tools-x64-darwin": "0.2.89",
147
+ "very-happy-tools-arm64-linux": "0.2.89",
148
+ "very-happy-tools-x64-linux": "0.2.89",
149
+ "very-happy-tools-arm64-win32": "0.2.89",
150
+ "very-happy-tools-x64-win32": "0.2.89"
151
151
  };
152
152
  var devDependencies = {
153
153
  "@slopus/happy-wire": "workspace:*",
@@ -2534,11 +2534,11 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
2534
2534
  const usage = pickAssistantUsage(message);
2535
2535
  const blocks = Array.isArray(message.message?.content) ? message.message.content : [];
2536
2536
  for (const block of blocks) {
2537
- if (block.type === "text" && typeof block.text === "string") {
2537
+ if (block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) {
2538
2538
  envelopes.push(createEnvelope("agent", { t: "text", text: block.text }, { turn: turnId, subagent, claudeUuid, usage }));
2539
2539
  continue;
2540
2540
  }
2541
- if (block.type === "thinking" && typeof block.thinking === "string") {
2541
+ if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim().length > 0) {
2542
2542
  envelopes.push(createEnvelope("agent", { t: "text", text: block.thinking, thinking: true }, { turn: turnId, subagent, claudeUuid, usage }));
2543
2543
  continue;
2544
2544
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "very-happy-cli",
3
- "version": "0.2.87",
3
+ "version": "0.2.89",
4
4
  "description": "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "contributors": [
@@ -113,12 +113,12 @@
113
113
  "zod": "^4.0.0"
114
114
  },
115
115
  "optionalDependencies": {
116
- "very-happy-tools-arm64-darwin": "0.2.87",
117
- "very-happy-tools-x64-darwin": "0.2.87",
118
- "very-happy-tools-arm64-linux": "0.2.87",
119
- "very-happy-tools-x64-linux": "0.2.87",
120
- "very-happy-tools-arm64-win32": "0.2.87",
121
- "very-happy-tools-x64-win32": "0.2.87"
116
+ "very-happy-tools-arm64-darwin": "0.2.89",
117
+ "very-happy-tools-x64-darwin": "0.2.89",
118
+ "very-happy-tools-arm64-linux": "0.2.89",
119
+ "very-happy-tools-x64-linux": "0.2.89",
120
+ "very-happy-tools-arm64-win32": "0.2.89",
121
+ "very-happy-tools-x64-win32": "0.2.89"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@slopus/happy-wire": "workspace:*",