very-happy-cli 0.2.94 → 0.2.96

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-Ds4mP02F.mjs → AcpBackend-B5Eh8G3W.mjs} +1 -1
  2. package/dist/{AcpBackend-DoSXb9La.cjs → AcpBackend-BaCt4_zS.cjs} +1 -1
  3. package/dist/{AcpSessionManager-DQ6j5fp4.cjs → AcpSessionManager-CvgmPlXB.cjs} +1 -1
  4. package/dist/{AcpSessionManager-B6RqjyQS.mjs → AcpSessionManager-grr_aJEI.mjs} +1 -1
  5. package/dist/{config-yrZNEia7.mjs → config-B69TZlJj.mjs} +2 -2
  6. package/dist/{config-IdMPfmwy.cjs → config-BjsUeQPA.cjs} +2 -2
  7. package/dist/{index-27948eGz.mjs → index-BMmbxCTq.mjs} +210 -47
  8. package/dist/{index-DXpQrR4U.mjs → index-C-uJXSm3.mjs} +5 -5
  9. package/dist/{index-BfCdbWQI.cjs → index-DID1FV_n.cjs} +4 -4
  10. package/dist/{index-bqUSeHWU.cjs → index-uc-dtu-3.cjs} +212 -49
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.mjs +2 -2
  13. package/dist/{installTerminalHooks-dVZoe8om.mjs → installTerminalHooks-CBHjEQ-a.mjs} +2 -2
  14. package/dist/{installTerminalHooks-D0gCqzZu.cjs → installTerminalHooks-CepXxAM_.cjs} +2 -2
  15. package/dist/lib.cjs +1 -1
  16. package/dist/lib.mjs +1 -1
  17. package/dist/{mcp-BvDgylsM.mjs → mcp-BdSHqi2d.mjs} +2 -2
  18. package/dist/{mcp-CRcmpUpX.cjs → mcp-CAtbtTXF.cjs} +2 -2
  19. package/dist/{runGemini-DIAChFOe.mjs → runGemini-cMZ3_CRf.mjs} +4 -4
  20. package/dist/{runGemini-BoVDs_V6.cjs → runGemini-qStPY5fH.cjs} +4 -4
  21. package/dist/{runOpenClaw-Cdsug1sI.cjs → runOpenClaw-B8HR6GnX.cjs} +3 -3
  22. package/dist/{runOpenClaw-DcC_uOjt.mjs → runOpenClaw-BJ36wEbk.mjs} +3 -3
  23. package/dist/{send-DoFm4-OB.cjs → send-6eY55o3C.cjs} +2 -2
  24. package/dist/{send-DvDOKQ4V.mjs → send-TX13v8Xe.mjs} +2 -2
  25. package/dist/{spawn-CEWzROeD.mjs → spawn-CfCE6JAv.mjs} +2 -2
  26. package/dist/{spawn-C7QRBKkQ.cjs → spawn-vOZA0kOi.cjs} +2 -2
  27. package/dist/{types-CSOwbjq5.mjs → types-CLRl0ET3.mjs} +18 -8
  28. package/dist/{types-CcaZTXxx.cjs → types-okYCRlgl.cjs} +19 -9
  29. 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-CSOwbjq5.mjs';
4
+ import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-CLRl0ET3.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-CcaZTXxx.cjs');
6
+ var persistence = require('./types-okYCRlgl.cjs');
7
7
 
8
8
  const DEFAULT_TIMEOUTS = {
9
9
  /** Default initialization timeout: 60 seconds */
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var cuid2 = require('@paralleldrive/cuid2');
4
- var persistence = require('./types-CcaZTXxx.cjs');
4
+ var persistence = require('./types-okYCRlgl.cjs');
5
5
 
6
6
  function turnOptions(turnId, time) {
7
7
  return turnId ? { turn: turnId, time } : { time };
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { b as createEnvelope } from './types-CSOwbjq5.mjs';
2
+ import { b as createEnvelope } from './types-CLRl0ET3.mjs';
3
3
 
4
4
  function turnOptions(turnId, time) {
5
5
  return turnId ? { turn: turnId, time } : { time };
@@ -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-CSOwbjq5.mjs';
6
- import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-27948eGz.mjs';
5
+ import { l as logger } from './types-CLRl0ET3.mjs';
6
+ import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-BMmbxCTq.mjs';
7
7
  import 'axios';
8
8
  import 'chalk';
9
9
  import 'node:util';
@@ -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-CcaZTXxx.cjs');
8
- var index = require('./index-bqUSeHWU.cjs');
7
+ var persistence = require('./types-okYCRlgl.cjs');
8
+ var index = require('./index-uc-dtu-3.cjs');
9
9
  require('axios');
10
10
  require('chalk');
11
11
  require('node:util');
@@ -1,11 +1,11 @@
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 isClaudeSessionId, L as getLocalHappyAgentCredentialPath, N as readLocalHappyAgentCredentials, O as decryptWithDataKey, P as decryptLegacy, Q as extractCompleteLines, S as decideBackfill, T as parseTerminalHookPayload, U as tmuxArgs, V as scrubTmuxClientEnv, W as decideMirrorBinding, X as MIRROR_BACKFILL_LINES_DEFAULT, Y as persistSession, Z as mirrorLineKey, _ as mirrorLocalId, $ as detectResumeSupport, a0 as detectCLIAvailability, a1 as acquireDaemonLock, a2 as summarizeSpawnSessionForLog, a3 as deletePersistedSessions, a4 as decrypt, a5 as writeDaemonState, A as ApiClient, a6 as fetchCliUpdateState, a7 as resolveCliUpdateCheckInterval, a8 as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, a9 as releaseDaemonLock, aa as isValidSessionId, ab as listVhTerminals, ac as isValidTerminalId, ad as readVhTerminal, ae as sendToVhTerminal, h as connectionState, af as startOfflineReconnection, ag as clearCredentials, ah as clearMachineId, k as ensurePrivateDirectorySync, w as writePrivateFileSync, ai as SandboxConfigSchema, b as createEnvelope, aj as CodexAppServerClient, ak as getLatestDaemonLog } from './types-CSOwbjq5.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 isClaudeSessionId, L as getLocalHappyAgentCredentialPath, N as readLocalHappyAgentCredentials, O as decryptWithDataKey, P as decryptLegacy, Q as extractCompleteLines, S as decideBackfill, T as parseTerminalHookPayload, U as tmuxArgs, V as scrubTmuxClientEnv, W as decideMirrorBinding, X as MIRROR_BACKFILL_LINES_DEFAULT, Y as persistSession, Z as mirrorLineKey, _ as mirrorLocalId, w as writePrivateFileSync, $ as detectResumeSupport, a0 as detectCLIAvailability, a1 as acquireDaemonLock, a2 as summarizeSpawnSessionForLog, a3 as deletePersistedSessions, a4 as decrypt, a5 as writeDaemonState, A as ApiClient, a6 as fetchCliUpdateState, a7 as resolveCliUpdateCheckInterval, a8 as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, a9 as releaseDaemonLock, aa as isValidSessionId, ab as listVhTerminals, ac as isValidTerminalId, ad as readVhTerminal, ae as sendToVhTerminal, h as connectionState, af as startOfflineReconnection, ag as clearCredentials, ah as clearMachineId, k as ensurePrivateDirectorySync, ai as SandboxConfigSchema, b as createEnvelope, aj as CodexAppServerClient, ak as getLatestDaemonLog } from './types-CLRl0ET3.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';
8
- import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, unlinkSync, writeFileSync, rmSync, chmodSync as chmodSync$1 } from 'node:fs';
8
+ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, unlinkSync, renameSync, writeFileSync, rmSync, chmodSync as chmodSync$1 } from 'node:fs';
9
9
  import { t as trimIdent, A as AGENT_GUIDANCE, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, c as REPORT_PROGRESS_TOOL_TITLE, d as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-C2_pkgRI.mjs';
10
10
  import { readFile, writeFile, rename, unlink, mkdir, stat, open as open$1, appendFile } from 'node:fs/promises';
11
11
  import fs, { watch, access } from 'fs/promises';
@@ -7211,6 +7211,144 @@ function recoverableSessionPid(metadata, liveHappyPids) {
7211
7211
  const pid = metadata?.hostPid;
7212
7212
  return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && liveHappyPids.has(pid) ? pid : null;
7213
7213
  }
7214
+ function escapeRegExp(value) {
7215
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7216
+ }
7217
+ function sessionAgentConversationId(metadata) {
7218
+ if (!metadata) return null;
7219
+ if (metadata.flavor === "codex") return metadata.codexThreadId ?? null;
7220
+ return metadata.claudeSessionId ?? metadata.codexThreadId ?? null;
7221
+ }
7222
+ function isDaemonWrapperForConversation(command, conversationId) {
7223
+ if (!command || !conversationId) return false;
7224
+ const startedByDaemon = /(?:^|\s)--started-by\s+daemon(?:\s|$)/.test(command);
7225
+ if (!startedByDaemon) return false;
7226
+ const resume = new RegExp(`(?:^|\\s)--resume(?:\\s+|=)${escapeRegExp(conversationId)}(?:\\s|$)`);
7227
+ return resume.test(command);
7228
+ }
7229
+ function findSessionWrapperPids(metadata, liveHappyProcesses, options = {}) {
7230
+ const out = [];
7231
+ const livePids = new Set(liveHappyProcesses.map((p) => p.pid));
7232
+ const lockPid = options.lockPid ?? null;
7233
+ if (lockPid !== null && lockPid !== options.excludePid && livePids.has(lockPid)) out.push(lockPid);
7234
+ const persisted = recoverableSessionPid(metadata, livePids);
7235
+ if (persisted !== null && persisted !== options.excludePid && !out.includes(persisted)) out.push(persisted);
7236
+ const conversationId = sessionAgentConversationId(metadata);
7237
+ if (conversationId) {
7238
+ for (const proc of liveHappyProcesses) {
7239
+ if (proc.pid === options.excludePid || out.includes(proc.pid)) continue;
7240
+ if (isDaemonWrapperForConversation(proc.command, conversationId)) out.push(proc.pid);
7241
+ }
7242
+ }
7243
+ return out;
7244
+ }
7245
+ function mergeRestoreMetadata(stale, reported) {
7246
+ if (!reported) return stale;
7247
+ return { ...stale, ...processIdentityFields(reported) };
7248
+ }
7249
+
7250
+ const SESSION_LOCK_DIR = "session-locks";
7251
+ const defaultRuntime = () => ({
7252
+ dir: join(configuration.happyHomeDir, SESSION_LOCK_DIR),
7253
+ selfPid: process.pid,
7254
+ now: () => Date.now(),
7255
+ signal: (pid, signal) => process.kill(pid, signal),
7256
+ isAlive: (pid) => {
7257
+ try {
7258
+ process.kill(pid, 0);
7259
+ return true;
7260
+ } catch {
7261
+ return false;
7262
+ }
7263
+ },
7264
+ schedule: (callback, delayMs) => {
7265
+ setTimeout(callback, delayMs);
7266
+ }
7267
+ });
7268
+ function sessionLockPath(happySessionId, dir = join(configuration.happyHomeDir, SESSION_LOCK_DIR)) {
7269
+ return join(dir, `${happySessionId}.json`);
7270
+ }
7271
+ function readSessionLock(happySessionId, dir) {
7272
+ try {
7273
+ const path = sessionLockPath(happySessionId, dir);
7274
+ if (!existsSync(path)) return null;
7275
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
7276
+ if (typeof parsed?.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
7277
+ return {
7278
+ pid: parsed.pid,
7279
+ startedAt: typeof parsed.startedAt === "number" ? parsed.startedAt : 0,
7280
+ version: typeof parsed.version === "string" ? parsed.version : "",
7281
+ ...typeof parsed.flavor === "string" ? { flavor: parsed.flavor } : {}
7282
+ };
7283
+ } catch {
7284
+ return null;
7285
+ }
7286
+ }
7287
+ function liveSessionLockHolder(happySessionId, runtime = defaultRuntime()) {
7288
+ const record = readSessionLock(happySessionId, runtime.dir);
7289
+ if (!record || record.pid === runtime.selfPid) return null;
7290
+ return runtime.isAlive(record.pid) ? record : null;
7291
+ }
7292
+ function writeSessionLock(happySessionId, record, dir) {
7293
+ mkdirSync(dir, { recursive: true, mode: 448 });
7294
+ const path = sessionLockPath(happySessionId, dir);
7295
+ const tmp = `${path}.${record.pid}.tmp`;
7296
+ writePrivateFileSync(tmp, JSON.stringify(record));
7297
+ renameSync(tmp, path);
7298
+ }
7299
+ function acquireSessionLock(happySessionId, options, runtime = defaultRuntime()) {
7300
+ const record = () => ({
7301
+ pid: runtime.selfPid,
7302
+ startedAt: runtime.now(),
7303
+ version: options.version,
7304
+ ...options.flavor ? { flavor: options.flavor } : {}
7305
+ });
7306
+ const holder = liveSessionLockHolder(happySessionId, runtime);
7307
+ if (!holder) {
7308
+ writeSessionLock(happySessionId, record(), runtime.dir);
7309
+ return Promise.resolve({ ok: true, replaced: null });
7310
+ }
7311
+ if (!options.takeover) {
7312
+ return Promise.resolve({ ok: false, holder });
7313
+ }
7314
+ return new Promise((resolve) => {
7315
+ const requested = terminateProcess(holder.pid, (stopped) => {
7316
+ if (!stopped) {
7317
+ resolve({ ok: false, holder });
7318
+ return;
7319
+ }
7320
+ writeSessionLock(happySessionId, record(), runtime.dir);
7321
+ resolve({ ok: true, replaced: holder });
7322
+ }, runtime, options.graceMs ?? 2e3);
7323
+ if (!requested) resolve({ ok: false, holder });
7324
+ });
7325
+ }
7326
+ function releaseSessionLock(happySessionId, runtime = defaultRuntime()) {
7327
+ try {
7328
+ const record = readSessionLock(happySessionId, runtime.dir);
7329
+ if (record && record.pid === runtime.selfPid) {
7330
+ unlinkSync(sessionLockPath(happySessionId, runtime.dir));
7331
+ }
7332
+ } catch {
7333
+ }
7334
+ }
7335
+ async function claimSessionOrExit(happySessionId, options) {
7336
+ const result = await acquireSessionLock(happySessionId, {
7337
+ takeover: options.takeover,
7338
+ version: configuration.currentCliVersion,
7339
+ flavor: options.flavor
7340
+ });
7341
+ if (!result.ok) {
7342
+ const message = `Session ${happySessionId} is already run by very-happy pid ${result.holder.pid} (v${result.holder.version || "?"}); this process yields.`;
7343
+ logger.debug(`[SESSION LOCK] ${message}`);
7344
+ console.error(message);
7345
+ process.exit(0);
7346
+ }
7347
+ if (result.replaced) {
7348
+ logger.debug(`[SESSION LOCK] Took over session ${happySessionId} from pid ${result.replaced.pid} (v${result.replaced.version || "?"})`);
7349
+ }
7350
+ process.on("exit", () => releaseSessionLock(happySessionId));
7351
+ }
7214
7352
 
7215
7353
  function shellescape(s) {
7216
7354
  return "'" + s.replace(/'/g, "'\\''") + "'";
@@ -7302,7 +7440,7 @@ async function startDaemon() {
7302
7440
  const pidToTrackedSession = /* @__PURE__ */ new Map();
7303
7441
  const sessionIdToFinishedSession = /* @__PURE__ */ new Map();
7304
7442
  const persisted = readPersistedSessions();
7305
- const liveHappyPids = new Set((await findAllHappyProcesses()).map((entry) => entry.pid));
7443
+ const liveHappyProcesses = await findAllHappyProcesses();
7306
7444
  for (const [id, s] of Object.entries(persisted)) {
7307
7445
  const tracked = {
7308
7446
  startedBy: "persisted",
@@ -7317,12 +7455,20 @@ async function startDaemon() {
7317
7455
  },
7318
7456
  pid: 0
7319
7457
  };
7320
- const livePid = recoverableSessionPid(s.metadata, liveHappyPids);
7321
- if (livePid !== null) {
7458
+ const livePids = findSessionWrapperPids(s.metadata, liveHappyProcesses, { excludePid: process.pid, lockPid: readSessionLock(id)?.pid });
7459
+ if (livePids.length > 0) {
7460
+ const [livePid, ...duplicates] = livePids;
7322
7461
  tracked.pid = livePid;
7323
7462
  tracked.startedBy = "recovered after daemon restart";
7324
7463
  pidToTrackedSession.set(livePid, tracked);
7325
- persistSession(id, { ...s, savedAt: Date.now() });
7464
+ if (livePid !== s.metadata?.hostPid) {
7465
+ logger.debug(`[DAEMON RUN] Session ${id}: persisted hostPid ${s.metadata?.hostPid} is stale; adopted live wrapper ${livePid} by its --resume command line`);
7466
+ }
7467
+ for (const dup of duplicates) {
7468
+ logger.warn(`[DAEMON RUN] Session ${id} has a duplicate live wrapper ${dup} (kept ${livePid}); restart the session to collapse them`);
7469
+ pidToTrackedSession.set(dup, { ...tracked, pid: dup, startedBy: "recovered after daemon restart (duplicate wrapper)" });
7470
+ }
7471
+ persistSession(id, { ...s, metadata: { ...s.metadata, hostPid: livePid }, savedAt: Date.now() });
7326
7472
  } else {
7327
7473
  sessionIdToFinishedSession.set(id, tracked);
7328
7474
  }
@@ -7352,6 +7498,11 @@ async function startDaemon() {
7352
7498
  savedAt: Date.now()
7353
7499
  });
7354
7500
  }
7501
+ for (const [otherPid, other] of pidToTrackedSession.entries()) {
7502
+ if (other.happySessionId === sessionId && otherPid !== pid && isPidAlive(otherPid)) {
7503
+ logger.warn(`[DAEMON RUN] Session ${sessionId} now has two live wrappers (${otherPid} and ${pid}); restart the session to collapse them`);
7504
+ }
7505
+ }
7355
7506
  const existingSession = pidToTrackedSession.get(pid);
7356
7507
  if (existingSession && existingSession.startedBy === "daemon") {
7357
7508
  existingSession.happySessionId = sessionId;
@@ -7779,6 +7930,21 @@ async function startDaemon() {
7779
7930
  }
7780
7931
  return sessionIdToFinishedSession.get(happySessionId);
7781
7932
  };
7933
+ const persistRestoreRecord = (happySessionId, fallback, metadata) => {
7934
+ const candidates = [...pidToTrackedSession.values()].filter((s) => s.happySessionId === happySessionId && isPidAlive(s.pid));
7935
+ const live = candidates.find((s) => s.childProcess) ?? candidates[0];
7936
+ const encryption = live?.encryption ?? fallback.encryption;
7937
+ if (!encryption) return;
7938
+ persistSession(happySessionId, {
7939
+ encryptionKey: encodeBase64(encryption.encryptionKey),
7940
+ encryptionVariant: encryption.encryptionVariant,
7941
+ seq: encryption.seq,
7942
+ metadataVersion: encryption.metadataVersion,
7943
+ agentStateVersion: encryption.agentStateVersion,
7944
+ metadata: mergeRestoreMetadata(metadata, live?.happySessionMetadataFromLocalWebhook),
7945
+ savedAt: Date.now()
7946
+ });
7947
+ };
7782
7948
  const fetchServerSessionMetadata = async (sessionId, encryptionKey, encryptionVariant) => {
7783
7949
  try {
7784
7950
  const byId = await axios.get(`${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}`, {
@@ -7835,6 +8001,14 @@ async function startDaemon() {
7835
8001
  if (!tracked) {
7836
8002
  return { type: "error", errorMessage: `resume-precheck:not-tracked: Session ${happySessionId} is not tracked by this daemon. It may have been started before the daemon, more than 14 days ago, or on another machine.` };
7837
8003
  }
8004
+ const orphans = findSessionWrapperPids(tracked.happySessionMetadataFromLocalWebhook, await findAllHappyProcesses(), { excludePid: process.pid, lockPid: readSessionLock(happySessionId)?.pid }).filter((pid) => isPidAlive(pid) && !pidToTrackedSession.has(pid));
8005
+ if (orphans.length > 0) {
8006
+ for (const pid of orphans) {
8007
+ pidToTrackedSession.set(pid, { ...tracked, pid, startedBy: "adopted untracked wrapper" });
8008
+ }
8009
+ logger.debug(`[DAEMON RUN] resume ${happySessionId}: adopted untracked live wrapper(s) ${orphans.join(", ")} \u2014 idempotent success`);
8010
+ return { type: "success", sessionId: happySessionId };
8011
+ }
7838
8012
  if (!tracked.happySessionMetadataFromLocalWebhook) {
7839
8013
  return { type: "error", errorMessage: `resume-precheck:no-metadata: Session ${happySessionId} has no metadata. Cannot resume.` };
7840
8014
  }
@@ -7882,15 +8056,7 @@ async function startDaemon() {
7882
8056
  }
7883
8057
  });
7884
8058
  if (result.type === "success") {
7885
- persistSession(happySessionId, {
7886
- encryptionKey: encodeBase64(tracked.encryption.encryptionKey),
7887
- encryptionVariant: tracked.encryption.encryptionVariant,
7888
- seq: tracked.encryption.seq,
7889
- metadataVersion: tracked.encryption.metadataVersion,
7890
- agentStateVersion: tracked.encryption.agentStateVersion,
7891
- metadata,
7892
- savedAt: Date.now()
7893
- });
8059
+ persistRestoreRecord(happySessionId, tracked, metadata);
7894
8060
  }
7895
8061
  return result;
7896
8062
  } catch (error) {
@@ -7903,17 +8069,18 @@ async function startDaemon() {
7903
8069
  }
7904
8070
  };
7905
8071
  const restartCounts = /* @__PURE__ */ new Map();
7906
- const stopSessionAndWait = (happySessionId, timeoutMs = 8e3) => {
7907
- let targetPid = null;
7908
- for (const [pid2, s] of pidToTrackedSession.entries()) {
7909
- if (s.happySessionId === happySessionId && isPidAlive(pid2)) {
7910
- targetPid = pid2;
7911
- break;
7912
- }
7913
- }
7914
- if (targetPid === null) return Promise.resolve();
7915
- const pid = targetPid;
7916
- return new Promise((resolve) => {
8072
+ const stopSessionAndWait = async (happySessionId, timeoutMs = 8e3) => {
8073
+ const targets = /* @__PURE__ */ new Set();
8074
+ for (const [pid, s] of pidToTrackedSession.entries()) {
8075
+ if (s.happySessionId === happySessionId && isPidAlive(pid)) targets.add(pid);
8076
+ }
8077
+ const known = findTrackedSessionById(happySessionId)?.happySessionMetadataFromLocalWebhook;
8078
+ for (const pid of findSessionWrapperPids(known, await findAllHappyProcesses(), { excludePid: process.pid, lockPid: readSessionLock(happySessionId)?.pid })) {
8079
+ if (isPidAlive(pid)) targets.add(pid);
8080
+ }
8081
+ if (targets.size === 0) return;
8082
+ logger.debug(`[DAEMON RUN] restart ${happySessionId}: stopping live wrapper(s) ${[...targets].join(", ")}`);
8083
+ await Promise.all([...targets].map((pid) => new Promise((resolve) => {
7917
8084
  let settled = false;
7918
8085
  const done = () => {
7919
8086
  if (!settled) {
@@ -7933,7 +8100,7 @@ async function startDaemon() {
7933
8100
  });
7934
8101
  if (!requested) done();
7935
8102
  setTimeout(done, timeoutMs).unref?.();
7936
- });
8103
+ })));
7937
8104
  };
7938
8105
  const restartSession = async (happySessionId, options) => {
7939
8106
  let gate = resumeGates.get(happySessionId);
@@ -8015,15 +8182,7 @@ async function startDaemon() {
8015
8182
  }
8016
8183
  });
8017
8184
  if (result.type === "success") {
8018
- persistSession(happySessionId, {
8019
- encryptionKey: encodeBase64(tracked.encryption.encryptionKey),
8020
- encryptionVariant: tracked.encryption.encryptionVariant,
8021
- seq: tracked.encryption.seq,
8022
- metadataVersion: tracked.encryption.metadataVersion,
8023
- agentStateVersion: tracked.encryption.agentStateVersion,
8024
- metadata,
8025
- savedAt: Date.now()
8026
- });
8185
+ persistRestoreRecord(happySessionId, tracked, metadata);
8027
8186
  }
8028
8187
  return result;
8029
8188
  } catch (error) {
@@ -9792,6 +9951,7 @@ async function runClaude(credentials, options = {}) {
9792
9951
  process.exit(0);
9793
9952
  }
9794
9953
  logger.debug(`Session created: ${response.id}`);
9954
+ await claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "claude" });
9795
9955
  if (reconnectSessionId && !await api.reactivateSession(response.id)) {
9796
9956
  throw new Error(`Failed to reactivate archived session ${response.id}`);
9797
9957
  }
@@ -13306,6 +13466,9 @@ async function runCodex(opts) {
13306
13466
  } else {
13307
13467
  response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
13308
13468
  }
13469
+ if (response) {
13470
+ await claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "codex" });
13471
+ }
13309
13472
  if (reconnectSessionId && response && !await api.reactivateSession(response.id)) {
13310
13473
  throw new Error(`Failed to reactivate archived session ${response.id}`);
13311
13474
  }
@@ -14072,7 +14235,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14072
14235
  process.exit(0);
14073
14236
  } else if (subcommand === "install-terminal-hooks") {
14074
14237
  try {
14075
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-dVZoe8om.mjs');
14238
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-CBHjEQ-a.mjs');
14076
14239
  const command = parseTerminalHooksArgs(args.slice(1));
14077
14240
  if (command.action === "help") {
14078
14241
  console.log(TERMINAL_HOOKS_HELP);
@@ -14089,7 +14252,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14089
14252
  }
14090
14253
  } else if (subcommand === "spawn") {
14091
14254
  try {
14092
- const { handleSpawnCommand } = await import('./spawn-CEWzROeD.mjs');
14255
+ const { handleSpawnCommand } = await import('./spawn-CfCE6JAv.mjs');
14093
14256
  await handleSpawnCommand(args.slice(1));
14094
14257
  } catch (error) {
14095
14258
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -14101,7 +14264,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14101
14264
  return;
14102
14265
  } else if (subcommand === "send") {
14103
14266
  try {
14104
- const { handleSendCommand } = await import('./send-DvDOKQ4V.mjs');
14267
+ const { handleSendCommand } = await import('./send-TX13v8Xe.mjs');
14105
14268
  await handleSendCommand(args.slice(1));
14106
14269
  } catch (error) {
14107
14270
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -14207,9 +14370,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14207
14370
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
14208
14371
  const projectId = args[3];
14209
14372
  try {
14210
- const { saveGoogleCloudProjectToConfig } = await import('./config-yrZNEia7.mjs');
14211
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-CSOwbjq5.mjs').then(function (n) { return n.al; });
14212
- const { ApiClient: ApiClient2 } = await import('./types-CSOwbjq5.mjs').then(function (n) { return n.am; });
14373
+ const { saveGoogleCloudProjectToConfig } = await import('./config-B69TZlJj.mjs');
14374
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-CLRl0ET3.mjs').then(function (n) { return n.al; });
14375
+ const { ApiClient: ApiClient2 } = await import('./types-CLRl0ET3.mjs').then(function (n) { return n.am; });
14213
14376
  let userEmail = void 0;
14214
14377
  try {
14215
14378
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -14240,7 +14403,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14240
14403
  }
14241
14404
  if (geminiSubcommand === "project" && args[2] === "get") {
14242
14405
  try {
14243
- const { readGeminiLocalConfig } = await import('./config-yrZNEia7.mjs');
14406
+ const { readGeminiLocalConfig } = await import('./config-B69TZlJj.mjs');
14244
14407
  const config = readGeminiLocalConfig();
14245
14408
  if (config.googleCloudProject) {
14246
14409
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -14280,7 +14443,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14280
14443
  process.exit(0);
14281
14444
  }
14282
14445
  try {
14283
- const { runGemini } = await import('./runGemini-DIAChFOe.mjs');
14446
+ const { runGemini } = await import('./runGemini-cMZ3_CRf.mjs');
14284
14447
  let startedBy = void 0;
14285
14448
  for (let i = 1; i < args.length; i++) {
14286
14449
  if (args[i] === "--started-by") {
@@ -14302,7 +14465,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14302
14465
  return;
14303
14466
  } else if (subcommand === "acp") {
14304
14467
  try {
14305
- const { runAcp, resolveAcpAgentConfig } = await import('./index-DXpQrR4U.mjs');
14468
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-C-uJXSm3.mjs');
14306
14469
  let startedBy = void 0;
14307
14470
  let verbose = false;
14308
14471
  const acpArgs = [];
@@ -14342,7 +14505,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14342
14505
  return;
14343
14506
  } else if (subcommand === "openclaw") {
14344
14507
  try {
14345
- const { runOpenClaw } = await import('./runOpenClaw-DcC_uOjt.mjs');
14508
+ const { runOpenClaw } = await import('./runOpenClaw-BJ36wEbk.mjs');
14346
14509
  let startedBy = void 0;
14347
14510
  let verbose = false;
14348
14511
  let gatewayUrl;
@@ -14393,7 +14556,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14393
14556
  return;
14394
14557
  } else if (subcommand === "mcp" && args.length === 1) {
14395
14558
  try {
14396
- const { handleMcpCommand } = await import('./mcp-BvDgylsM.mjs');
14559
+ const { handleMcpCommand } = await import('./mcp-BdSHqi2d.mjs');
14397
14560
  await handleMcpCommand();
14398
14561
  } catch (error) {
14399
14562
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -1,10 +1,10 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-Ds4mP02F.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-Ds4mP02F.mjs';
3
- import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-CSOwbjq5.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-B6RqjyQS.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-B5Eh8G3W.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-B5Eh8G3W.mjs';
3
+ import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-CLRl0ET3.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-grr_aJEI.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-27948eGz.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-BMmbxCTq.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-DoSXb9La.cjs');
4
- var persistence = require('./types-CcaZTXxx.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-DQ6j5fp4.cjs');
3
+ var AcpBackend = require('./AcpBackend-BaCt4_zS.cjs');
4
+ var persistence = require('./types-okYCRlgl.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-CvgmPlXB.cjs');
6
6
  var node_crypto = require('node:crypto');
7
7
  var path = require('node:path');
8
- var index = require('./index-bqUSeHWU.cjs');
8
+ var index = require('./index-uc-dtu-3.cjs');
9
9
  require('node:child_process');
10
10
  require('@agentclientprotocol/sdk');
11
11
  require('axios');