very-happy-cli 0.2.142 → 0.2.144

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 (49) hide show
  1. package/dist/{AcpBackend-D2LqY2to.mjs → AcpBackend-DLsPpWnc.mjs} +2 -2
  2. package/dist/{AcpBackend-DJsqfgjD.cjs → AcpBackend-DssrjZDS.cjs} +30 -30
  3. package/dist/{AcpSessionManager-CdeM5KD9.mjs → AcpSessionManager-1pAH8SdV.mjs} +1 -1
  4. package/dist/{AcpSessionManager-CTfDxziP.cjs → AcpSessionManager-DkBV80Rr.cjs} +9 -9
  5. package/dist/{agentGuidance-Cz8QVcX3.mjs → agentGuidance-CupQ9Lve.mjs} +1 -1
  6. package/dist/{agentGuidance-Bi8iBjy3.cjs → agentGuidance-D1PDVswF.cjs} +1 -1
  7. package/dist/agentHome-BaSxP7lQ.mjs +107 -0
  8. package/dist/agentHome-uf1zs2Fi.cjs +113 -0
  9. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  10. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  11. package/dist/{config-ZsBeq10u.mjs → config-CxHoQo52.mjs} +4 -4
  12. package/dist/{config-BIbYBmTd.cjs → config-kx4y9oAI.cjs} +4 -4
  13. package/dist/{index-Bom8mTuS.cjs → index-DIoSNgtt.cjs} +4 -4
  14. package/dist/{index-CYFendNf.mjs → index-Diu81aSC.mjs} +77 -28
  15. package/dist/{index-CSQZhQ0C.cjs → index-Dn0JMM8l.cjs} +250 -201
  16. package/dist/{index-BPTPrjhL.cjs → index-Dox9iD5R.cjs} +31 -31
  17. package/dist/{index-CNARLBax.mjs → index-DxEaiVzX.mjs} +4 -4
  18. package/dist/{index-CY0zmhIp.mjs → index-J6a8OHS6.mjs} +8 -8
  19. package/dist/index.cjs +4 -4
  20. package/dist/index.mjs +4 -4
  21. package/dist/{installTerminalHooks-BPJu2WVz.mjs → installTerminalHooks-BBMGk3q5.mjs} +4 -4
  22. package/dist/{installTerminalHooks-DA7SyFYW.cjs → installTerminalHooks-De7_iOOC.cjs} +4 -4
  23. package/dist/lib.cjs +5 -5
  24. package/dist/lib.mjs +2 -2
  25. package/dist/{limits-B3hngubn.cjs → limits-C_Sklt--.cjs} +7 -7
  26. package/dist/{limits-CHsz3Ba-.mjs → limits-DxArdmkZ.mjs} +8 -8
  27. package/dist/{mcp-BkWBRWFe.mjs → mcp-C1oNnlw5.mjs} +4 -4
  28. package/dist/{mcp-ySrfNOYl.cjs → mcp-xyb8dYIQ.cjs} +9 -9
  29. package/dist/{piRuntime-uX0WHVr4.cjs → piRuntime-3Vbdgv6X.cjs} +2 -2
  30. package/dist/{piRuntime-DBLUyXFr.mjs → piRuntime-CzJdE4z4.mjs} +2 -2
  31. package/dist/{piTerminal-DNr3hkzY.cjs → piTerminal-BFpwVGWr.cjs} +6 -6
  32. package/dist/{piTerminal-Bc3jSCnY.mjs → piTerminal-Do2GiKMN.mjs} +6 -6
  33. package/dist/{runGemini-eJ08cXk7.cjs → runGemini-CCdGxTF2.cjs} +24 -24
  34. package/dist/{runGemini-rgFHTnG7.mjs → runGemini-De5NRMT9.mjs} +6 -6
  35. package/dist/{runOpenClaw-GjLWwjas.mjs → runOpenClaw-C2t4DRyi.mjs} +5 -5
  36. package/dist/{runOpenClaw-D0LVzxqM.cjs → runOpenClaw-CRMUfrWk.cjs} +26 -26
  37. package/dist/{send-Bg2oqt7f.cjs → send-BA1Dgpgn.cjs} +4 -4
  38. package/dist/{send-DV8liaRN.mjs → send-l6Rj09c0.mjs} +4 -4
  39. package/dist/{sessions-DPimBZ8H.cjs → sessions-CtVcoXvG.cjs} +5 -5
  40. package/dist/{sessions-JiL4aJkC.mjs → sessions-MWLDqQk-.mjs} +4 -4
  41. package/dist/{spawn-COOcUwkG.cjs → spawn-Be4nsNuQ.cjs} +6 -6
  42. package/dist/{spawn-CnMk3LFG.mjs → spawn-DKWR-6Qc.mjs} +4 -4
  43. package/dist/{teams-DSQgHBLE.cjs → teams-CcjjNLi8.cjs} +4 -4
  44. package/dist/{teams-uHZcdX9j.mjs → teams-DMf9GNGE.mjs} +4 -4
  45. package/dist/{todo-oHWNpEx_.mjs → todo-BEXZRm1A.mjs} +3 -3
  46. package/dist/{todo-DeauuUd6.cjs → todo-EffYAnre.cjs} +5 -5
  47. package/dist/{types-Dnoxgb7G.mjs → types-D6YKR7wo.mjs} +372 -15
  48. package/dist/{types-Xd_LJRJN.cjs → types-DZ5c-9f3.cjs} +379 -13
  49. package/package.json +7 -7
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-CSQZhQ0C.cjs');
3
+ var index = require('./index-Dn0JMM8l.cjs');
4
4
  var claudeAgentSdk = require('@anthropic-ai/claude-agent-sdk');
5
5
  require('chalk');
6
6
  require('node:os');
7
7
  require('node:crypto');
8
- require('./types-Xd_LJRJN.cjs');
8
+ require('./types-DZ5c-9f3.cjs');
9
9
  require('axios');
10
- require('./limits-B3hngubn.cjs');
10
+ require('./limits-C_Sklt--.cjs');
11
11
  require('node:fs/promises');
12
12
  require('node:fs');
13
13
  require('node:path');
@@ -32,7 +32,7 @@ require('fs');
32
32
  require('node:readline');
33
33
  require('@anthropic-ai/sandbox-runtime');
34
34
  require('expo-server-sdk');
35
- require('./agentGuidance-Bi8iBjy3.cjs');
35
+ require('./agentGuidance-D1PDVswF.cjs');
36
36
  require('ink');
37
37
  require('react');
38
38
  require('ps-list');
@@ -1,14 +1,14 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
2
2
  import os, { homedir, userInfo, tmpdir } from 'node:os';
3
3
  import { randomUUID, randomInt, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
4
- import { o as notifyDaemonSessionEvent, q as getProjectPath, j as BUILTIN_TODO_DISCOVERY, r as initializeSandbox, w as wrapCommand, l as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, m as delay, t as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, u as notifyDaemonClaudeAuthFailed, v as commandOnPath, c as checkIfDaemonRunningAndCleanupStaleState, x as deriveLocalCliUpdateSummary, y as AGENT_VERSION_SOURCES, z as isClaudeSessionId, C as getLocalHappyAgentCredentialPath, D as readLocalHappyAgentCredentials, E as parseExactVersion, F as compareExactVersions, G as extractCompleteLines, H as decideBackfill, J as parseTerminalHookPayload, K as tmuxArgs, L as scrubTmuxClientEnv, N as decideMirrorBinding, O as MIRROR_BACKFILL_LINES_DEFAULT, P as mirrorLineKey, Q as mirrorLocalId, T as listDaemonSessions, U as stopDaemonSession, V as detectCLIAvailability, W as detectResumeSupport, X as isDaemonRunningCurrentlyInstalledHappyVersion, Y as stopDaemon, Z as TurnActivityTracker, _ as summarizeSpawnSessionForLog, $ as shellescape, A as ApiClient, a0 as fetchCliUpdateState, a1 as resolveCliUpdateCheckInterval, a2 as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, a3 as cleanupDaemonState, i as isValidSessionId, s as spawnDaemonSession, a4 as listVhTerminals, a5 as isValidTerminalId, a6 as readVhTerminal, a7 as sendToVhTerminal, a8 as ContextUsageSchema, a9 as TITLE_PROMPT_PREFIX, aa as BOARD_ANALYZER_PROMPT_PREFIX, h as connectionState, ab as startOfflineReconnection, n as notifyDaemonSessionStarted, d as createEnvelope, ac as CodexAppServerClient } from './types-Dnoxgb7G.mjs';
5
- import { l as logger, E as ensurePrivateDirectory, F as hardenPrivateFile, c as configuration, p as packageJson, f as readSettings, G as readCredentials, v as credentialRelayProblem, o as readDaemonState, g as encodeBase64, H as readSettingsOutcome, I as updateSettings, J as encodeBase64Url, m as decodeBase64, K as writeCredentialsLegacy, L as writeCredentialsDataKey, d as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, M as decryptWithDataKey, N as decryptLegacy, O as persistSession, k as decrypt, h as ensurePrivateDirectorySync, P as acquireDaemonLock, Q as deletePersistedSessions, R as writeDaemonState, S as writeSettings, T as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES, U as clearCredentials, V as clearMachineId, W as SandboxConfigSchema, X as getLatestDaemonLog } from './limits-CHsz3Ba-.mjs';
4
+ import { v as notifyDaemonSessionEvent, w as getProjectPath, o as BUILTIN_TODO_DISCOVERY, x as initializeSandbox, y as wrapCommand, t as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, u as delay, z as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, C as notifyDaemonClaudeAuthFailed, D as commandOnPath, c as checkIfDaemonRunningAndCleanupStaleState, E as deriveLocalCliUpdateSummary, F as AGENT_VERSION_SOURCES, G as isClaudeSessionId, H as getLocalHappyAgentCredentialPath, J as readLocalHappyAgentCredentials, K as parseExactVersion, L as compareExactVersions, N as extractCompleteLines, O as decideBackfill, P as parseTerminalHookPayload, Q as tmuxArgs, T as scrubTmuxClientEnv, U as decideMirrorBinding, V as MIRROR_BACKFILL_LINES_DEFAULT, W as mirrorLineKey, X as mirrorLocalId, Y as listDaemonSessions, Z as stopDaemonSession, _ as detectCLIAvailability, $ as detectResumeSupport, a0 as refreshAgentHomes, d as describeAgentHome, a1 as isDaemonRunningCurrentlyInstalledHappyVersion, a2 as stopDaemon, a3 as TurnActivityTracker, a4 as summarizeSpawnSessionForLog, a5 as locateClaudeConversation, a6 as agentHomeSpawnEnv, a7 as shellescape, A as ApiClient, a8 as fetchCliUpdateState, a9 as resolveCliUpdateCheckInterval, aa as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, ab as cleanupDaemonState, i as isValidSessionId, s as spawnDaemonSession, ac as listVhTerminals, ad as isValidTerminalId, ae as readVhTerminal, af as sendToVhTerminal, ag as ContextUsageSchema, ah as TITLE_PROMPT_PREFIX, ai as BOARD_ANALYZER_PROMPT_PREFIX, m as connectionState, aj as startOfflineReconnection, n as notifyDaemonSessionStarted, g as createEnvelope, ak as CodexAppServerClient } from './types-D6YKR7wo.mjs';
5
+ import { l as logger, F as ensurePrivateDirectory, G as hardenPrivateFile, c as configuration, p as packageJson, r as readSettings, H as readCredentials, x as credentialRelayProblem, o as readDaemonState, g as encodeBase64, I as readSettingsOutcome, u as updateSettings, J as encodeBase64Url, m as decodeBase64, K as writeCredentialsLegacy, L as writeCredentialsDataKey, e as readCredentialsForConfiguredRelay, j as encrypt, d as readPersistedSessions, w as writePrivateFileSync, M as decryptWithDataKey, N as decryptLegacy, O as persistSession, k as decrypt, h as ensurePrivateDirectorySync, P as acquireDaemonLock, Q as deletePersistedSessions, R as writeDaemonState, S as writeSettings, T as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, f as CLIPBOARD_MAX_BYTES, U as clearCredentials, V as clearMachineId, W as SandboxConfigSchema, X as getLatestDaemonLog } from './limits-DxArdmkZ.mjs';
6
6
  import spawn$1, { spawn } from 'cross-spawn';
7
7
  import path, { join, resolve, basename, isAbsolute, sep, dirname, delimiter, normalize } from 'node:path';
8
8
  import { createInterface } from 'node:readline';
9
9
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, unlinkSync, renameSync, writeFileSync, accessSync, constants, openSync, fsyncSync, closeSync, rmSync as rmSync$1, chmodSync as chmodSync$1 } from 'node:fs';
10
10
  import { p as projectPath } from './projectPath-CSpqWdxp.mjs';
11
- import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-Cz8QVcX3.mjs';
11
+ import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-CupQ9Lve.mjs';
12
12
  import { readFile, writeFile, rename, unlink, mkdir, symlink, rm, readlink, lstat, stat, open as open$1, appendFile } from 'node:fs/promises';
13
13
  import fs, { watch, access } from 'fs/promises';
14
14
  import { useStdout, useInput, Box, Text, render } from 'ink';
@@ -5294,6 +5294,16 @@ ${typeLabels[type] || type}:`));
5294
5294
  console.log(chalk.gray(" OS-keychain credentials cannot be verified here; see /docs/configuration#claude-credentials."));
5295
5295
  }
5296
5296
  console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
5297
+ try {
5298
+ const { resolveAgentHomesForCli } = await import('./agentHome-BaSxP7lQ.mjs');
5299
+ const { describeAgentHome } = await import('./types-D6YKR7wo.mjs').then(function (n) { return n.al; });
5300
+ const homes = await resolveAgentHomesForCli();
5301
+ console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
5302
+ console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));
5303
+ console.log(chalk.gray(" Resolved from settings.json, then your login shell, then the daemon environment; pin with `very-happy agent-home`."));
5304
+ } catch {
5305
+ console.log(chalk.yellow("\u25CB Could not resolve the Claude/Codex home directories"));
5306
+ }
5297
5307
  if (availableAgents.length === 0) {
5298
5308
  console.log(chalk.yellow("\u25CB No external agent command found on this daemon PATH"));
5299
5309
  console.log(chalk.gray(" Codex, Gemini, OpenCode, OpenClaw, and native Claude terminal paths need their local command or gateway."));
@@ -10016,6 +10026,10 @@ Use the teams tools to inspect current task state before acting. This message is
10016
10026
  } };
10017
10027
  }
10018
10028
 
10029
+ function daemonExitCode(source) {
10030
+ return source === "exception" ? 1 : 0;
10031
+ }
10032
+
10019
10033
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10020
10034
  function sanitizeImportTitle(value) {
10021
10035
  if (typeof value !== "string") return null;
@@ -10090,14 +10104,20 @@ async function startDaemon() {
10090
10104
  let cliUpdateStateRef = null;
10091
10105
  let lastHandoverHold = null;
10092
10106
  let claudeCredentialStoreSetting = (await readSettings()).claudeCredentialStore === "file" ? "file" : "auto";
10107
+ try {
10108
+ const homes = await refreshAgentHomes();
10109
+ logger.debug(`[DAEMON RUN] Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}; Codex home: ${describeAgentHome(homes.codexHome)}`);
10110
+ } catch (error) {
10111
+ logger.debug("[DAEMON RUN] agent home resolution failed; using the daemon environment as-is", error);
10112
+ }
10093
10113
  let requestShutdown;
10094
10114
  let resolvesWhenShutdownRequested = new Promise((resolve) => {
10095
10115
  requestShutdown = (source, errorMessage) => {
10096
10116
  logger.debug(`[DAEMON RUN] Requesting shutdown (source: ${source}, errorMessage: ${errorMessage})`);
10097
10117
  setTimeout(async () => {
10098
- logger.debug("[DAEMON RUN] Startup malfunctioned, forcing exit with code 1");
10118
+ logger.debug(`[DAEMON RUN] Shutdown did not complete in time, forcing exit with code ${daemonExitCode(source)}`);
10099
10119
  await new Promise((resolve2) => setTimeout(resolve2, 100));
10100
- process.exit(1);
10120
+ process.exit(daemonExitCode(source));
10101
10121
  }, 1e3);
10102
10122
  resolve({ source, errorMessage });
10103
10123
  };
@@ -10255,7 +10275,8 @@ async function startDaemon() {
10255
10275
  }
10256
10276
  };
10257
10277
  const assistantSpawnGate = createSpawnGate();
10258
- const spawnSession = (options) => {
10278
+ const spawnSession = async (options) => {
10279
+ await refreshAgentHomes();
10259
10280
  if (assistantSpawnMode(options) !== "claude-singleton") {
10260
10281
  return spawnSessionImpl(options);
10261
10282
  }
@@ -10328,7 +10349,7 @@ async function startDaemon() {
10328
10349
  s.encryptionVariant
10329
10350
  );
10330
10351
  const claudeSessionId = resolveAssistantClaudeSessionId(s.metadata, serverMetadata);
10331
- const canResumeClaude = !!claudeSessionId && existsSync$1(join$1(getProjectPath(assistantHome()), `${claudeSessionId}.jsonl`));
10352
+ const canResumeClaude = !!claudeSessionId && locateClaudeConversation(assistantHome(), claudeSessionId) !== null;
10332
10353
  return spawnTrackedHappyProcess({
10333
10354
  args: [
10334
10355
  "claude",
@@ -10347,6 +10368,8 @@ async function startDaemon() {
10347
10368
  // B-297: see the resume/restart paths — every spawn carries the
10348
10369
  // credentialStore=file shim, not just fresh sessions (B-276 D8).
10349
10370
  ...claudeAuthServiceRef?.claudeProcessEnvOverrides() ?? {},
10371
+ // B-478: point Claude at the directory the transcript was found in.
10372
+ ...canResumeClaude ? agentHomeSpawnEnv({ workingDirectory: assistantHome(), claudeSessionId }) : {},
10350
10373
  HAPPY_SESSION_VARIANT: "assistant",
10351
10374
  HAPPY_RECONNECT_SESSION_ID: assistantSessionId,
10352
10375
  HAPPY_RECONNECT_ENCRYPTION_KEY: s.encryptionKey,
@@ -10501,6 +10524,7 @@ async function startDaemon() {
10501
10524
  tmuxEnv[key] = value;
10502
10525
  }
10503
10526
  }
10527
+ Object.assign(tmuxEnv, agentHomeSpawnEnv({ workingDirectory: directory, claudeSessionId: options.resumeClaudeSessionId, codexThreadId: options.resumeCodexThreadId }));
10504
10528
  Object.assign(tmuxEnv, extraEnv);
10505
10529
  const tmuxResult = await tmux.spawnInTmux([fullCommand], {
10506
10530
  sessionName: tmuxSessionName,
@@ -10595,6 +10619,9 @@ async function startDaemon() {
10595
10619
  cwd: directory,
10596
10620
  env: {
10597
10621
  ...process.env,
10622
+ // B-478: a fork/duplicate resumes a transcript that may live in
10623
+ // a moved config dir. Explicit environmentVariables still win.
10624
+ ...agentHomeSpawnEnv({ workingDirectory: directory, claudeSessionId: options.resumeClaudeSessionId, codexThreadId: options.resumeCodexThreadId }),
10598
10625
  ...extraEnv
10599
10626
  },
10600
10627
  directoryCreated,
@@ -10745,6 +10772,7 @@ async function startDaemon() {
10745
10772
  gate = createSpawnGate();
10746
10773
  resumeGates.set(happySessionId, gate);
10747
10774
  }
10775
+ await refreshAgentHomes();
10748
10776
  try {
10749
10777
  return await gate.join(() => resumeSessionImpl(happySessionId, options));
10750
10778
  } finally {
@@ -10791,7 +10819,9 @@ async function startDaemon() {
10791
10819
  }
10792
10820
  const precheck = resumePrecheck(metadata, {
10793
10821
  cwdExists: (p) => existsSync$1(p),
10794
- conversationExists: (cwd, claudeSessionId) => existsSync$1(join$1(getProjectPath(cwd), `${claudeSessionId}.jsonl`))
10822
+ // B-478: also looks in every other Claude config dir the user's shell
10823
+ // or settings point at, not just the one the daemon was started with.
10824
+ conversationExists: (cwd, claudeSessionId) => locateClaudeConversation(cwd, claudeSessionId) !== null
10795
10825
  });
10796
10826
  let freshConversation = false;
10797
10827
  if (!precheck.ok) {
@@ -10822,6 +10852,9 @@ async function startDaemon() {
10822
10852
  // fell back to `auto` reads a different credential store than the
10823
10853
  // fresh one next to it (B-276 D8).
10824
10854
  ...claudeAuthServiceRef?.claudeProcessEnvOverrides() ?? {},
10855
+ // B-478: if the transcript lives in a Claude/Codex home other than
10856
+ // the resolved one, this spawn is pointed there.
10857
+ ...freshConversation ? {} : agentHomeSpawnEnv({ workingDirectory: launch.cwd, claudeSessionId: metadata.claudeSessionId, codexThreadId: metadata.codexThreadId }),
10825
10858
  HAPPY_RECONNECT_SESSION_ID: happySessionId,
10826
10859
  HAPPY_RECONNECT_ENCRYPTION_KEY: encodeBase64(tracked.encryption.encryptionKey),
10827
10860
  HAPPY_RECONNECT_ENCRYPTION_VARIANT: tracked.encryption.encryptionVariant,
@@ -10883,6 +10916,7 @@ async function startDaemon() {
10883
10916
  gate = createSpawnGate();
10884
10917
  resumeGates.set(happySessionId, gate);
10885
10918
  }
10919
+ await refreshAgentHomes();
10886
10920
  try {
10887
10921
  return await gate.replace(() => restartSessionImpl(happySessionId, options));
10888
10922
  } finally {
@@ -10918,7 +10952,9 @@ async function startDaemon() {
10918
10952
  }
10919
10953
  const precheck = resumePrecheck(metadata, {
10920
10954
  cwdExists: (p) => existsSync$1(p),
10921
- conversationExists: (cwd2, claudeSessionId) => existsSync$1(join$1(getProjectPath(cwd2), `${claudeSessionId}.jsonl`))
10955
+ // B-478: also looks in every other Claude config dir the user's shell
10956
+ // or settings point at, not just the one the daemon was started with.
10957
+ conversationExists: (cwd2, claudeSessionId) => locateClaudeConversation(cwd2, claudeSessionId) !== null
10922
10958
  });
10923
10959
  let args;
10924
10960
  let cwd;
@@ -10953,6 +10989,8 @@ async function startDaemon() {
10953
10989
  // fell back to `auto` reads a different credential store than the
10954
10990
  // fresh one next to it (B-276 D8).
10955
10991
  ...claudeAuthServiceRef?.claudeProcessEnvOverrides() ?? {},
10992
+ // B-478: see resumeSession.
10993
+ ...precheck.ok ? agentHomeSpawnEnv({ workingDirectory: cwd, claudeSessionId: metadata.claudeSessionId, codexThreadId: metadata.codexThreadId }) : {},
10956
10994
  HAPPY_RECONNECT_SESSION_ID: happySessionId,
10957
10995
  HAPPY_RECONNECT_ENCRYPTION_KEY: encodeBase64(tracked.encryption.encryptionKey),
10958
10996
  HAPPY_RECONNECT_ENCRYPTION_VARIANT: tracked.encryption.encryptionVariant,
@@ -11355,8 +11393,8 @@ async function startDaemon() {
11355
11393
  await cleanupDaemonState();
11356
11394
  await stopCaffeinate();
11357
11395
  await releaseDaemonLock(daemonLockHandle);
11358
- logger.debug("[DAEMON RUN] Cleanup completed, exiting process");
11359
- process.exit(0);
11396
+ logger.debug(`[DAEMON RUN] Cleanup completed, exiting process with code ${daemonExitCode(source)}`);
11397
+ process.exit(daemonExitCode(source));
11360
11398
  };
11361
11399
  logger.debug("[DAEMON RUN] Daemon started successfully, waiting for shutdown request");
11362
11400
  const shutdownRequest = await resolvesWhenShutdownRequested;
@@ -12350,7 +12388,7 @@ function registerSideQuestionHandler(rpc, deps) {
12350
12388
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12351
12389
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12352
12390
  const run = deps.run ?? (async (input) => {
12353
- const { query } = await import('./index-CNARLBax.mjs');
12391
+ const { query } = await import('./index-DxEaiVzX.mjs');
12354
12392
  return runSideQuestion(query, input);
12355
12393
  });
12356
12394
  const slots = /* @__PURE__ */ new Map();
@@ -17595,7 +17633,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17595
17633
  return;
17596
17634
  } else if (subcommand === "install-terminal-hooks") {
17597
17635
  try {
17598
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-BPJu2WVz.mjs');
17636
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-BBMGk3q5.mjs');
17599
17637
  const command = parseTerminalHooksArgs(args.slice(1));
17600
17638
  if (command.action === "help") {
17601
17639
  console.log(TERMINAL_HOOKS_HELP);
@@ -17610,9 +17648,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17610
17648
  }
17611
17649
  process.exit(1);
17612
17650
  }
17651
+ } else if (subcommand === "agent-home") {
17652
+ try {
17653
+ const { parseAgentHomeArgs, runAgentHomeCommand } = await import('./agentHome-BaSxP7lQ.mjs');
17654
+ await runAgentHomeCommand(parseAgentHomeArgs(args.slice(1)));
17655
+ process.exit(0);
17656
+ } catch (error) {
17657
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
17658
+ process.exit(1);
17659
+ }
17613
17660
  } else if (subcommand === "spawn") {
17614
17661
  try {
17615
- const { handleSpawnCommand } = await import('./spawn-CnMk3LFG.mjs');
17662
+ const { handleSpawnCommand } = await import('./spawn-DKWR-6Qc.mjs');
17616
17663
  await handleSpawnCommand(args.slice(1));
17617
17664
  } catch (error) {
17618
17665
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17624,7 +17671,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17624
17671
  return;
17625
17672
  } else if (subcommand === "sessions") {
17626
17673
  try {
17627
- const { handleSessionsCommand } = await import('./sessions-JiL4aJkC.mjs');
17674
+ const { handleSessionsCommand } = await import('./sessions-MWLDqQk-.mjs');
17628
17675
  await handleSessionsCommand(args.slice(1));
17629
17676
  } catch (error) {
17630
17677
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17636,7 +17683,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17636
17683
  return;
17637
17684
  } else if (subcommand === "send") {
17638
17685
  try {
17639
- const { handleSendCommand } = await import('./send-DV8liaRN.mjs');
17686
+ const { handleSendCommand } = await import('./send-l6Rj09c0.mjs');
17640
17687
  await handleSendCommand(args.slice(1));
17641
17688
  } catch (error) {
17642
17689
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17742,9 +17789,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17742
17789
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
17743
17790
  const projectId = args[3];
17744
17791
  try {
17745
- const { saveGoogleCloudProjectToConfig } = await import('./config-ZsBeq10u.mjs');
17746
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-CHsz3Ba-.mjs').then(function (n) { return n.Y; });
17747
- const { ApiClient: ApiClient2 } = await import('./types-Dnoxgb7G.mjs').then(function (n) { return n.ad; });
17792
+ const { saveGoogleCloudProjectToConfig } = await import('./config-CxHoQo52.mjs');
17793
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-DxArdmkZ.mjs').then(function (n) { return n.Y; });
17794
+ const { ApiClient: ApiClient2 } = await import('./types-D6YKR7wo.mjs').then(function (n) { return n.am; });
17748
17795
  let userEmail = void 0;
17749
17796
  try {
17750
17797
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -17775,7 +17822,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17775
17822
  }
17776
17823
  if (geminiSubcommand === "project" && args[2] === "get") {
17777
17824
  try {
17778
- const { readGeminiLocalConfig } = await import('./config-ZsBeq10u.mjs');
17825
+ const { readGeminiLocalConfig } = await import('./config-CxHoQo52.mjs');
17779
17826
  const config = readGeminiLocalConfig();
17780
17827
  if (config.googleCloudProject) {
17781
17828
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -17815,7 +17862,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17815
17862
  process.exit(0);
17816
17863
  }
17817
17864
  try {
17818
- const { runGemini } = await import('./runGemini-rgFHTnG7.mjs');
17865
+ const { runGemini } = await import('./runGemini-De5NRMT9.mjs');
17819
17866
  let startedBy = void 0;
17820
17867
  for (let i = 1; i < args.length; i++) {
17821
17868
  if (args[i] === "--started-by") {
@@ -17838,7 +17885,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17838
17885
  } else if (subcommand === "pi") {
17839
17886
  if (args[1] === "--terminal") {
17840
17887
  try {
17841
- const { runPiTerminal } = await import('./piTerminal-Bc3jSCnY.mjs');
17888
+ const { runPiTerminal } = await import('./piTerminal-Do2GiKMN.mjs');
17842
17889
  process.exitCode = await runPiTerminal(args.slice(2));
17843
17890
  } catch (error) {
17844
17891
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17855,7 +17902,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17855
17902
  }
17856
17903
  };
17857
17904
  try {
17858
- const { runAcp, resolveAcpAgentConfig } = await import('./index-CY0zmhIp.mjs');
17905
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-J6a8OHS6.mjs');
17859
17906
  const parsed = parsePiRunnerArgs(args.slice(1));
17860
17907
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17861
17908
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17883,7 +17930,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17883
17930
  return;
17884
17931
  } else if (subcommand === "acp") {
17885
17932
  try {
17886
- const { runAcp, resolveAcpAgentConfig } = await import('./index-CY0zmhIp.mjs');
17933
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-J6a8OHS6.mjs');
17887
17934
  let startedBy = void 0;
17888
17935
  let verbose = false;
17889
17936
  const acpArgs = [];
@@ -17923,7 +17970,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17923
17970
  return;
17924
17971
  } else if (subcommand === "openclaw") {
17925
17972
  try {
17926
- const { runOpenClaw } = await import('./runOpenClaw-GjLWwjas.mjs');
17973
+ const { runOpenClaw } = await import('./runOpenClaw-C2t4DRyi.mjs');
17927
17974
  let startedBy = void 0;
17928
17975
  let verbose = false;
17929
17976
  let gatewayUrl;
@@ -17974,7 +18021,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17974
18021
  return;
17975
18022
  } else if (subcommand === "todo") {
17976
18023
  try {
17977
- const { handleTodoCommand } = await import('./todo-oHWNpEx_.mjs');
18024
+ const { handleTodoCommand } = await import('./todo-BEXZRm1A.mjs');
17978
18025
  await handleTodoCommand(args.slice(1));
17979
18026
  } catch (error) {
17980
18027
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -17983,7 +18030,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17983
18030
  return;
17984
18031
  } else if (subcommand === "teams") {
17985
18032
  try {
17986
- const { handleTeamsCommand } = await import('./teams-uHZcdX9j.mjs');
18033
+ const { handleTeamsCommand } = await import('./teams-DMf9GNGE.mjs');
17987
18034
  await handleTeamsCommand(args.slice(1));
17988
18035
  } catch (error) {
17989
18036
  console.error(error instanceof Error ? error.message : "Teams command failed");
@@ -17992,7 +18039,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17992
18039
  return;
17993
18040
  } else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
17994
18041
  try {
17995
- const { handleMcpCommand } = await import('./mcp-BkWBRWFe.mjs');
18042
+ const { handleMcpCommand } = await import('./mcp-C1oNnlw5.mjs');
17996
18043
  await handleMcpCommand(args[1] === "--terminal-tools");
17997
18044
  } catch (error) {
17998
18045
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -18222,6 +18269,8 @@ ${chalk.bold("Usage:")}
18222
18269
  very-happy install-terminal-hooks
18223
18270
  Install optional Claude terminal mirror hooks
18224
18271
  (add --remove to uninstall them)
18272
+ very-happy agent-home Show or pin where Claude Code / Codex keep their
18273
+ conversations on this machine (agent-home --help)
18225
18274
  very-happy connect Connect AI vendor API keys
18226
18275
  very-happy todo Built-in Todos and official agent skill (todo --help)
18227
18276
  very-happy sandbox Configure and manage OS-level sandboxing