very-happy-cli 0.2.89 → 0.2.91

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 (31) hide show
  1. package/dist/{AcpBackend-DuxcHPyn.mjs → AcpBackend-B5RRlzLU.mjs} +1 -1
  2. package/dist/{AcpBackend-ClEvE-A-.cjs → AcpBackend-GqveWg8u.cjs} +1 -1
  3. package/dist/{AcpSessionManager-CGrA8bTm.mjs → AcpSessionManager-Ij1pI5Ex.mjs} +1 -1
  4. package/dist/{AcpSessionManager-DgbEf8FX.cjs → AcpSessionManager-YcrSZwvq.cjs} +1 -1
  5. package/dist/{config-BZNnPK1L.cjs → config-C4NCw7Dz.cjs} +2 -2
  6. package/dist/{config-CdoGLla8.mjs → config-ra8iKxdU.mjs} +2 -2
  7. package/dist/{index-Bx-UTV3I.cjs → index-C1HIm7NY.cjs} +4 -4
  8. package/dist/{index-BP2wylez.mjs → index-CB5H7YI6.mjs} +5 -5
  9. package/dist/{index-DYDAFqAN.cjs → index-FkXJNwiy.cjs} +141 -39
  10. package/dist/{index-vCgb7QnZ.mjs → index-UCYD5Wk3.mjs} +138 -36
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.mjs +2 -2
  13. package/dist/{installTerminalHooks-T4NE5fmC.cjs → installTerminalHooks-CoK5wNcA.cjs} +2 -2
  14. package/dist/{installTerminalHooks-C7JS6CBM.mjs → installTerminalHooks-TkkGzzbI.mjs} +2 -2
  15. package/dist/lib.cjs +1 -1
  16. package/dist/lib.d.cts +42 -0
  17. package/dist/lib.d.mts +42 -0
  18. package/dist/lib.mjs +1 -1
  19. package/dist/{mcp-B3EgenZh.mjs → mcp-C3MiqObi.mjs} +2 -2
  20. package/dist/{mcp-DgPg5mKa.cjs → mcp-DcRvswVI.cjs} +2 -2
  21. package/dist/{runGemini-B3iv1QRL.mjs → runGemini-BbC0uwUj.mjs} +4 -4
  22. package/dist/{runGemini-xZnGj7jx.cjs → runGemini-Dnvd2gWq.cjs} +4 -4
  23. package/dist/{runOpenClaw-CmM4ZLYi.mjs → runOpenClaw--nbqTW3K.mjs} +3 -3
  24. package/dist/{runOpenClaw-6ZLPeYYZ.cjs → runOpenClaw-CnuTBNQW.cjs} +3 -3
  25. package/dist/{send-M2ax7UGy.mjs → send-9nJxn09Y.mjs} +2 -2
  26. package/dist/{send-S9ibMiAt.cjs → send-DkKOPatx.cjs} +2 -2
  27. package/dist/{spawn-Dx_xnLlg.mjs → spawn-CRW4ioms.mjs} +2 -2
  28. package/dist/{spawn-ie_kfwQe.cjs → spawn-DE7WhS8Y.cjs} +2 -2
  29. package/dist/{types-d_104eQF.mjs → types-BKyjG23_.mjs} +304 -52
  30. package/dist/{types-DLI6sss2.cjs → types-ClDADuxP.cjs} +306 -52
  31. package/package.json +7 -7
@@ -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-d_104eQF.mjs';
4
+ import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-BKyjG23_.mjs';
5
5
 
6
6
  const DEFAULT_TIMEOUTS = {
7
7
  /** Default initialization timeout: 60 seconds */
@@ -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-DLI6sss2.cjs');
6
+ var persistence = require('./types-ClDADuxP.cjs');
7
7
 
8
8
  const DEFAULT_TIMEOUTS = {
9
9
  /** Default initialization timeout: 60 seconds */
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { b as createEnvelope } from './types-d_104eQF.mjs';
2
+ import { b as createEnvelope } from './types-BKyjG23_.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-DLI6sss2.cjs');
4
+ var persistence = require('./types-ClDADuxP.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-DLI6sss2.cjs');
8
- var index = require('./index-DYDAFqAN.cjs');
7
+ var persistence = require('./types-ClDADuxP.cjs');
8
+ var index = require('./index-FkXJNwiy.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-d_104eQF.mjs';
6
- import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-vCgb7QnZ.mjs';
5
+ import { l as logger } from './types-BKyjG23_.mjs';
6
+ import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-UCYD5Wk3.mjs';
7
7
  import 'axios';
8
8
  import 'chalk';
9
9
  import 'node:util';
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-ClEvE-A-.cjs');
4
- var persistence = require('./types-DLI6sss2.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-DgbEf8FX.cjs');
3
+ var AcpBackend = require('./AcpBackend-GqveWg8u.cjs');
4
+ var persistence = require('./types-ClDADuxP.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-YcrSZwvq.cjs');
6
6
  var node_crypto = require('node:crypto');
7
7
  var path = require('node:path');
8
- var index = require('./index-DYDAFqAN.cjs');
8
+ var index = require('./index-FkXJNwiy.cjs');
9
9
  require('node:child_process');
10
10
  require('@agentclientprotocol/sdk');
11
11
  require('axios');
@@ -1,10 +1,10 @@
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';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-B5RRlzLU.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-B5RRlzLU.mjs';
3
+ import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-BKyjG23_.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-Ij1pI5Ex.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-vCgb7QnZ.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-UCYD5Wk3.mjs';
8
8
  import 'node:child_process';
9
9
  import '@agentclientprotocol/sdk';
10
10
  import '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-DLI6sss2.cjs');
6
+ var persistence = require('./types-ClDADuxP.cjs');
7
7
  var spawn = require('cross-spawn');
8
8
  var path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -1655,6 +1655,12 @@ function resolveRemoteClaudePermissionMode(currentMode, incomingMode, sandboxEna
1655
1655
  }
1656
1656
  return nextMode;
1657
1657
  }
1658
+ function reconcilePublishedPermissionMode(input) {
1659
+ if (!input.effective) return null;
1660
+ const mismatch = input.effective !== input.intent;
1661
+ if (input.effective === input.published) return mismatch ? { publish: input.effective, mismatch } : null;
1662
+ return { publish: input.effective, mismatch };
1663
+ }
1658
1664
 
1659
1665
  function parseCompact(message) {
1660
1666
  const trimmed = message.trim();
@@ -1984,7 +1990,12 @@ async function claudeRemote(opts) {
1984
1990
  mcpServers: systemInit.mcp_servers?.map((s) => ({ name: s.name, status: s.status })),
1985
1991
  skills: systemInit.skills,
1986
1992
  model: systemInit.model,
1987
- modelIsDefault: initial.mode.model == null
1993
+ modelIsDefault: initial.mode.model == null,
1994
+ // The SDK's own verdict on the mode it will enforce —
1995
+ // settings (permissions.deny/ask/defaultMode/
1996
+ // disableBypassPermissionsMode) can override what we
1997
+ // asked for. runClaude publishes THIS, not our intent.
1998
+ permissionMode: systemInit.permissionMode
1988
1999
  });
1989
2000
  }
1990
2001
  if (systemInit.session_id) {
@@ -2002,6 +2013,10 @@ async function claudeRemote(opts) {
2002
2013
  if (message.type === "result") {
2003
2014
  updateThinking(false);
2004
2015
  persistence.logger.debug("[claudeRemote] Result received");
2016
+ const denials = message.permission_denials;
2017
+ if (denials && denials.length > 0) {
2018
+ persistence.logger.warn(`[claudeRemote] ${denials.length} tool call(s) auto-denied by Claude Code policy: ${denials.map((d) => d.tool_name ?? "?").join(", ")}`);
2019
+ }
2005
2020
  if (isCompactCommand) {
2006
2021
  const compactSucceeded = message.subtype === "success";
2007
2022
  persistence.logger.debug(`[claudeRemote] Compaction ${compactSucceeded ? "completed" : "failed"}`);
@@ -2087,6 +2102,8 @@ class PermissionHandler {
2087
2102
  setPermissionModeCallback;
2088
2103
  modeChangeQueue = Promise.resolve();
2089
2104
  deferredPermissionMode;
2105
+ /** Fired when an approval response (plan approval / approve-with-mode) moved the mode. */
2106
+ onModeChangedCallback;
2090
2107
  constructor(session) {
2091
2108
  this.session = session;
2092
2109
  this.setupClientHandler();
@@ -2100,6 +2117,18 @@ class PermissionHandler {
2100
2117
  handleModeChange(mode) {
2101
2118
  this.permissionMode = mapToClaudeMode(mode);
2102
2119
  }
2120
+ /** Current locally enforced mode (what canUseTool applies right now). */
2121
+ getPermissionMode() {
2122
+ return this.permissionMode;
2123
+ }
2124
+ /**
2125
+ * Observe mode transitions that originate inside the approval flow
2126
+ * (ExitPlanMode approval, approve-with-mode). Live/idle RPC changes and
2127
+ * queue-driven changes are owned by the launcher and do not fire this.
2128
+ */
2129
+ setOnModeChanged(callback) {
2130
+ this.onModeChangedCallback = callback;
2131
+ }
2103
2132
  /**
2104
2133
  * Set callback to dynamically change permission mode on the active query.
2105
2134
  * Called by claudeRemote after the Query object is created.
@@ -2188,24 +2217,19 @@ class PermissionHandler {
2188
2217
  updatedInput: persistence.contentLogMetadata(response.updatedInput)
2189
2218
  });
2190
2219
  if (response.approved) {
2191
- const newMode = response.mode && ["default", "acceptEdits", "bypassPermissions"].includes(response.mode) ? response.mode : "default";
2220
+ const newMode = response.mode && ["default", "acceptEdits", "bypassPermissions"].includes(response.mode) ? response.mode : "bypassPermissions";
2192
2221
  persistence.logger.debug(`Plan approved - allowing ExitPlanMode with ${newMode} as the local follow-up mode`);
2193
2222
  this.permissionMode = newMode;
2223
+ this.onModeChangedCallback?.(newMode);
2194
2224
  pending.resolve({ behavior: "allow", updatedInput: pending.input || {} });
2195
2225
  } else {
2196
2226
  pending.resolve({ behavior: "deny", message: response.reason || "Plan rejected" });
2197
2227
  }
2198
2228
  } else {
2199
2229
  if (response.approved && response.mode) {
2200
- try {
2201
- if (!this.setPermissionModeCallback) throw new Error("permission mode updater unavailable");
2202
- await this.setPermissionModeCallback(response.mode);
2203
- this.permissionMode = response.mode;
2204
- } catch (err) {
2205
- const reason = `Failed to switch permission mode: ${err instanceof Error ? err.message : String(err)}`;
2206
- pending.resolve({ behavior: "deny", message: reason });
2207
- return { ...response, approved: false, reason };
2208
- }
2230
+ this.permissionMode = response.mode;
2231
+ this.deferredPermissionMode = response.mode;
2232
+ this.onModeChangedCallback?.(response.mode);
2209
2233
  }
2210
2234
  if (response.approved && !pending.suggestions?.length && response.allowTools?.length) {
2211
2235
  response.allowTools.forEach((tool) => {
@@ -2546,6 +2570,17 @@ class PermissionHandler {
2546
2570
  }
2547
2571
  }
2548
2572
 
2573
+ function rewriteQueuedPermissionMode(queue, hasher, permissionMode) {
2574
+ let rewritten = 0;
2575
+ for (const item of queue) {
2576
+ if (item.mode.permissionMode === permissionMode) continue;
2577
+ item.mode = { ...item.mode, permissionMode };
2578
+ item.modeHash = hasher(item.mode);
2579
+ rewritten += 1;
2580
+ }
2581
+ return rewritten;
2582
+ }
2583
+
2549
2584
  function formatClaudeMessageForInk(message, messageBuffer, onAssistantResult) {
2550
2585
  persistence.logger.debugLargeJson("[CLAUDE INK] Message from remote mode:", message);
2551
2586
  switch (message.type) {
@@ -2800,7 +2835,13 @@ class SDKToLogConverter {
2800
2835
  type: "user",
2801
2836
  message: userMsg.message,
2802
2837
  ...userMsg.parent_tool_use_id ? { parent_tool_use_id: userMsg.parent_tool_use_id } : {},
2803
- ...meta ? { isMeta: true } : {}
2838
+ ...meta ? { isMeta: true } : {},
2839
+ // B-260-P2: structured tool output (Agent/Task final report +
2840
+ // run totals, or the `async_launched` stub marker) and the
2841
+ // message origin (task-notification). Both are consumed by
2842
+ // sessionProtocolMapper; the raw-line schema is passthrough.
2843
+ ...userMsg.tool_use_result !== void 0 ? { tool_use_result: userMsg.tool_use_result } : {},
2844
+ ...userMsg.origin ? { origin: userMsg.origin } : {}
2804
2845
  };
2805
2846
  if (Array.isArray(userMsg.message.content)) {
2806
2847
  for (const content of userMsg.message.content) {
@@ -3311,7 +3352,7 @@ function createTurnSteeringController() {
3311
3352
  };
3312
3353
  }
3313
3354
 
3314
- async function claudeRemoteLauncher(session, onPermissionModeChange) {
3355
+ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffectivePermissionMode) {
3315
3356
  persistence.logger.debug("[claudeRemoteLauncher] Starting remote launcher");
3316
3357
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
3317
3358
  persistence.logger.debug(`[claudeRemoteLauncher] TTY available: ${hasTTY}`);
@@ -3373,16 +3414,23 @@ async function claudeRemoteLauncher(session, onPermissionModeChange) {
3373
3414
  });
3374
3415
  session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
3375
3416
  let livePermissionModeHandler = null;
3417
+ let idlePermissionModeHandler = null;
3376
3418
  session.client.rpcHandlerManager.registerHandler(
3377
3419
  "set-permission-mode",
3378
3420
  async (request) => {
3379
3421
  const requestedMode = parseClaudePermissionMode(request?.mode);
3380
3422
  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) };
3423
+ if (livePermissionModeHandler) return { mode: await livePermissionModeHandler(requestedMode) };
3424
+ if (idlePermissionModeHandler) return { mode: idlePermissionModeHandler(requestedMode) };
3425
+ throw new Error("No active Claude query");
3383
3426
  }
3384
3427
  );
3385
3428
  const permissionHandler = new PermissionHandler(session);
3429
+ idlePermissionModeHandler = (nextMode) => {
3430
+ permissionHandler.handleModeChange(nextMode);
3431
+ onPermissionModeChange?.(nextMode);
3432
+ return nextMode;
3433
+ };
3386
3434
  permissionHandler.reset("Previous CLI process exited before responding");
3387
3435
  const messageQueue = new OutgoingMessageQueue(
3388
3436
  (logMessage) => session.client.sendClaudeSessionMessage(logMessage)
@@ -3532,6 +3580,23 @@ async function claudeRemoteLauncher(session, onPermissionModeChange) {
3532
3580
  abortFuture = new Future();
3533
3581
  let modeHash = null;
3534
3582
  let mode = null;
3583
+ const commitPermissionMode = (nextMode) => {
3584
+ permissionHandler.handleModeChange(nextMode);
3585
+ if (mode) {
3586
+ mode = { ...mode, permissionMode: nextMode };
3587
+ modeHash = session.queue.modeHasher(mode);
3588
+ }
3589
+ rewriteQueuedPermissionMode(session.queue.queue, session.queue.modeHasher, nextMode);
3590
+ if (pending) {
3591
+ pending = { ...pending, mode: { ...pending.mode, permissionMode: nextMode } };
3592
+ }
3593
+ onPermissionModeChange?.(nextMode);
3594
+ return nextMode;
3595
+ };
3596
+ idlePermissionModeHandler = commitPermissionMode;
3597
+ permissionHandler.setOnModeChanged((nextMode) => {
3598
+ commitPermissionMode(nextMode);
3599
+ });
3535
3600
  try {
3536
3601
  const attachmentDirectory = chatAttachmentDirectory(persistence.configuration.happyHomeDir, session.client.sessionId);
3537
3602
  await persistence.ensurePrivateDirectory(attachmentDirectory);
@@ -3597,6 +3662,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange) {
3597
3662
  skillCount: metadata.skills?.length ?? 0
3598
3663
  });
3599
3664
  session.client.updateMetadata((currentMetadata) => applyClaudeSdkMetadata(currentMetadata, metadata));
3665
+ if (metadata.permissionMode) onEffectivePermissionMode?.(metadata.permissionMode);
3600
3666
  },
3601
3667
  onQueryReady: (q) => {
3602
3668
  turnSteering.setInterrupt(q.interrupt);
@@ -3774,7 +3840,7 @@ async function loop(opts) {
3774
3840
  break;
3775
3841
  }
3776
3842
  case "remote": {
3777
- const reason = await claudeRemoteLauncher(session, opts.onPermissionModeChange);
3843
+ const reason = await claudeRemoteLauncher(session, opts.onPermissionModeChange, opts.onEffectivePermissionMode);
3778
3844
  switch (reason) {
3779
3845
  case "exit":
3780
3846
  return 0;
@@ -6636,7 +6702,7 @@ function createMirrorManager(deps) {
6636
6702
  let chain = Promise.resolve();
6637
6703
  const isTerminalAlive = deps.isTerminalAlive ?? ((terminalId) => {
6638
6704
  try {
6639
- return node_child_process.spawnSync("tmux", ["has-session", "-t", `=vh-${terminalId}:`], { stdio: "ignore", timeout: 3e3 }).status === 0;
6705
+ return node_child_process.spawnSync("tmux", persistence.tmuxArgs(["has-session", "-t", `=vh-${terminalId}:`]), { stdio: "ignore", timeout: 3e3, env: persistence.scrubTmuxClientEnv({ ...process.env }) }).status === 0;
6640
6706
  } catch {
6641
6707
  return false;
6642
6708
  }
@@ -8807,7 +8873,7 @@ const MAX_TITLE_CHARS = 60;
8807
8873
  const GENERATION_TIMEOUT_MS = 3e4;
8808
8874
  function resolveClaudeBinary() {
8809
8875
  try {
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)));
8876
+ 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-FkXJNwiy.cjs', document.baseURI).href)));
8811
8877
  const utilsPath = path.resolve(path.join(persistence.projectPath(), "scripts", "claude_version_utils.cjs"));
8812
8878
  const { getClaudeCliPath } = require$1(utilsPath);
8813
8879
  const path$1 = getClaudeCliPath();
@@ -9264,7 +9330,10 @@ async function runClaude(credentials, options = {}) {
9264
9330
  // a brand-new session cannot pick a PDF until after sending once.
9265
9331
  attachmentKinds: [...CLAUDE_ATTACHMENT_KINDS],
9266
9332
  queueCancellation: true,
9267
- capabilities: ["claude-steer-v1", "claude-live-permission-v1"],
9333
+ capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2"],
9334
+ // Effective mode this process enforces. Kept current by
9335
+ // publishPermissionMode below; the web renders it instead of guessing.
9336
+ permissionMode: mapToClaudeMode(initialPermissionMode ?? "default"),
9268
9337
  sandbox: sandboxConfig?.enabled ? sandboxConfig : null,
9269
9338
  dangerouslySkipPermissions,
9270
9339
  ...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
@@ -9369,7 +9438,11 @@ async function runClaude(credentials, options = {}) {
9369
9438
  ...meta,
9370
9439
  lifecycleState: "running",
9371
9440
  archivedBy: void 0,
9372
- queueCancellation: true
9441
+ queueCancellation: true,
9442
+ // B-262 batch 2 (B5): a reconnected process re-publishes the mode
9443
+ // IT enforces; the server copy may still carry the previous
9444
+ // process's value.
9445
+ permissionMode: mapToClaudeMode(initialPermissionMode ?? "default")
9373
9446
  }));
9374
9447
  }
9375
9448
  const forkClaudeSessionId = process.env.HAPPY_FORK_CLAUDE_SESSION_ID;
@@ -9501,6 +9574,30 @@ async function runClaude(credentials, options = {}) {
9501
9574
  )
9502
9575
  }));
9503
9576
  let currentPermissionMode = initialPermissionMode;
9577
+ let publishedPermissionMode = mapToClaudeMode(initialPermissionMode ?? "default");
9578
+ const publishPermissionMode = (mode) => {
9579
+ currentPermissionMode = mode;
9580
+ const published = mapToClaudeMode(mode ?? "default");
9581
+ if (published === publishedPermissionMode) return;
9582
+ publishedPermissionMode = published;
9583
+ session.updateMetadata((meta) => ({ ...meta, permissionMode: published }));
9584
+ };
9585
+ const publishEffectivePermissionMode = (effective) => {
9586
+ const decision = reconcilePublishedPermissionMode({
9587
+ intent: mapToClaudeMode(currentPermissionMode ?? "default"),
9588
+ published: publishedPermissionMode,
9589
+ effective
9590
+ });
9591
+ if (!decision) return;
9592
+ if (decision.mismatch) {
9593
+ persistence.logger.warn(`[loop] Claude Code enforces permission mode '${effective}' but this process asked for '${mapToClaudeMode(currentPermissionMode ?? "default")}' \u2014 check ~/.claude settings (permissions.deny/ask/defaultMode/disableBypassPermissionsMode). Publishing the effective mode.`);
9594
+ }
9595
+ if (decision.publish !== publishedPermissionMode) {
9596
+ publishedPermissionMode = decision.publish;
9597
+ session.updateMetadata((meta) => ({ ...meta, permissionMode: decision.publish }));
9598
+ }
9599
+ };
9600
+ let lastExplicitModeSwitchAt = 0;
9504
9601
  let currentModel = options.model ?? DEFAULT_CLAUDE_MODEL;
9505
9602
  let currentFallbackModel = void 0;
9506
9603
  let currentCustomSystemPrompt = void 0;
@@ -9509,7 +9606,6 @@ async function runClaude(credentials, options = {}) {
9509
9606
  let currentDisallowedTools = withAssistantDenylist(void 0, isAssistantVariant);
9510
9607
  let currentEffort = DEFAULT_CLAUDE_EFFORT;
9511
9608
  const resetCurrentModeDefaults = () => {
9512
- currentPermissionMode = initialPermissionMode;
9513
9609
  currentModel = options.model ?? DEFAULT_CLAUDE_MODEL;
9514
9610
  currentFallbackModel = void 0;
9515
9611
  currentCustomSystemPrompt = void 0;
@@ -9546,14 +9642,18 @@ async function runClaude(credentials, options = {}) {
9546
9642
  }
9547
9643
  const attachmentsForThisMessage = await session.drainAttachmentsForUserMessage();
9548
9644
  let messagePermissionMode = currentPermissionMode;
9549
- if (message.meta?.permissionMode) {
9645
+ const messageCreatedAt = message.createdAt;
9646
+ const staleModeSnapshot = message.meta?.permissionMode !== void 0 && typeof messageCreatedAt === "number" && messageCreatedAt < lastExplicitModeSwitchAt;
9647
+ if (staleModeSnapshot) {
9648
+ persistence.logger.debug(`[loop] Ignoring stale meta.permissionMode=${message.meta?.permissionMode} (message ${messageCreatedAt} predates explicit switch ${lastExplicitModeSwitchAt}); keeping ${currentPermissionMode}`);
9649
+ } else if (message.meta?.permissionMode) {
9550
9650
  const previousPermissionMode = currentPermissionMode;
9551
9651
  messagePermissionMode = resolveRemoteClaudePermissionMode(
9552
9652
  currentPermissionMode,
9553
9653
  message.meta.permissionMode,
9554
9654
  sandboxEnabled
9555
9655
  );
9556
- currentPermissionMode = messagePermissionMode;
9656
+ publishPermissionMode(messagePermissionMode);
9557
9657
  const ignoredDefaultDowngrade = (previousPermissionMode === "bypassPermissions" || previousPermissionMode === "yolo") && message.meta.permissionMode === "default" && currentPermissionMode === previousPermissionMode;
9558
9658
  if (ignoredDefaultDowngrade) {
9559
9659
  persistence.logger.debug(`[loop] Ignoring permission mode downgrade from ${previousPermissionMode} to default`);
@@ -9810,9 +9910,11 @@ async function runClaude(credentials, options = {}) {
9810
9910
  },
9811
9911
  onAbort: resetCurrentModeDefaults,
9812
9912
  onPermissionModeChange: (mode) => {
9813
- currentPermissionMode = mode;
9814
- persistence.logger.debug(`[loop] Permission mode updated from live session RPC to: ${mode}`);
9913
+ lastExplicitModeSwitchAt = Date.now();
9914
+ publishPermissionMode(mode);
9915
+ persistence.logger.debug(`[loop] Permission mode updated from session RPC / approval to: ${mode}`);
9815
9916
  },
9917
+ onEffectivePermissionMode: (mode) => publishEffectivePermissionMode(mode),
9816
9918
  mcpServers: {
9817
9919
  "happy": {
9818
9920
  type: "http",
@@ -10955,9 +11057,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
10955
11057
  };
10956
11058
  }
10957
11059
 
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)));
11060
+ 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-FkXJNwiy.cjs', document.baseURI).href)));
10959
11061
  const __dirname$1 = path.dirname(__filename$1);
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)));
11062
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-FkXJNwiy.cjs', document.baseURI).href)));
10961
11063
  const PRISMA_QUERY_ENGINE_FILES = {
10962
11064
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
10963
11065
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -13569,7 +13671,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13569
13671
  process.exit(0);
13570
13672
  } else if (subcommand === "install-terminal-hooks") {
13571
13673
  try {
13572
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-T4NE5fmC.cjs'); });
13674
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-CoK5wNcA.cjs'); });
13573
13675
  const command = parseTerminalHooksArgs(args.slice(1));
13574
13676
  if (command.action === "help") {
13575
13677
  console.log(TERMINAL_HOOKS_HELP);
@@ -13586,7 +13688,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13586
13688
  }
13587
13689
  } else if (subcommand === "spawn") {
13588
13690
  try {
13589
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-ie_kfwQe.cjs'); });
13691
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DE7WhS8Y.cjs'); });
13590
13692
  await handleSpawnCommand(args.slice(1));
13591
13693
  } catch (error) {
13592
13694
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13598,7 +13700,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13598
13700
  return;
13599
13701
  } else if (subcommand === "send") {
13600
13702
  try {
13601
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-S9ibMiAt.cjs'); });
13703
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DkKOPatx.cjs'); });
13602
13704
  await handleSendCommand(args.slice(1));
13603
13705
  } catch (error) {
13604
13706
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13704,9 +13806,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13704
13806
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
13705
13807
  const projectId = args[3];
13706
13808
  try {
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; });
13809
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-C4NCw7Dz.cjs'); });
13810
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types-ClDADuxP.cjs'); }).then(function (n) { return n.persistence; });
13811
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-ClDADuxP.cjs'); }).then(function (n) { return n.api; });
13710
13812
  let userEmail = void 0;
13711
13813
  try {
13712
13814
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -13737,7 +13839,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13737
13839
  }
13738
13840
  if (geminiSubcommand === "project" && args[2] === "get") {
13739
13841
  try {
13740
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-BZNnPK1L.cjs'); });
13842
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-C4NCw7Dz.cjs'); });
13741
13843
  const config = readGeminiLocalConfig();
13742
13844
  if (config.googleCloudProject) {
13743
13845
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -13777,7 +13879,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13777
13879
  process.exit(0);
13778
13880
  }
13779
13881
  try {
13780
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-xZnGj7jx.cjs'); });
13882
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-Dnvd2gWq.cjs'); });
13781
13883
  let startedBy = void 0;
13782
13884
  for (let i = 1; i < args.length; i++) {
13783
13885
  if (args[i] === "--started-by") {
@@ -13799,7 +13901,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13799
13901
  return;
13800
13902
  } else if (subcommand === "acp") {
13801
13903
  try {
13802
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-Bx-UTV3I.cjs'); });
13904
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-C1HIm7NY.cjs'); });
13803
13905
  let startedBy = void 0;
13804
13906
  let verbose = false;
13805
13907
  const acpArgs = [];
@@ -13839,7 +13941,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13839
13941
  return;
13840
13942
  } else if (subcommand === "openclaw") {
13841
13943
  try {
13842
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-6ZLPeYYZ.cjs'); });
13944
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-CnuTBNQW.cjs'); });
13843
13945
  let startedBy = void 0;
13844
13946
  let verbose = false;
13845
13947
  let gatewayUrl;
@@ -13890,7 +13992,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13890
13992
  return;
13891
13993
  } else if (subcommand === "mcp" && args.length === 1) {
13892
13994
  try {
13893
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-DgPg5mKa.cjs'); });
13995
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-DcRvswVI.cjs'); });
13894
13996
  await handleMcpCommand();
13895
13997
  } catch (error) {
13896
13998
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}