very-happy-cli 0.2.92 → 0.2.94

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-BP7id8L6.cjs → AcpBackend-DoSXb9La.cjs} +1 -1
  2. package/dist/{AcpBackend-DDEsgc1c.mjs → AcpBackend-Ds4mP02F.mjs} +1 -1
  3. package/dist/{AcpSessionManager-C29t6wb_.mjs → AcpSessionManager-B6RqjyQS.mjs} +1 -1
  4. package/dist/{AcpSessionManager-Cys1Hsb8.cjs → AcpSessionManager-DQ6j5fp4.cjs} +1 -1
  5. package/dist/{config-B77vhdwT.cjs → config-IdMPfmwy.cjs} +2 -2
  6. package/dist/{config-CJQw0z1o.mjs → config-yrZNEia7.mjs} +2 -2
  7. package/dist/{index-8LDXVYWi.mjs → index-27948eGz.mjs} +133 -12
  8. package/dist/{index-L3o5Gz-2.cjs → index-BfCdbWQI.cjs} +4 -4
  9. package/dist/{index-BP9pfzdZ.mjs → index-DXpQrR4U.mjs} +5 -5
  10. package/dist/{index-B63Vjt4W.cjs → index-bqUSeHWU.cjs} +136 -15
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.mjs +2 -2
  13. package/dist/{installTerminalHooks-BBc2nij4.cjs → installTerminalHooks-D0gCqzZu.cjs} +2 -2
  14. package/dist/{installTerminalHooks-DDRrUG_n.mjs → installTerminalHooks-dVZoe8om.mjs} +2 -2
  15. package/dist/lib.cjs +1 -1
  16. package/dist/lib.d.cts +9 -0
  17. package/dist/lib.d.mts +9 -0
  18. package/dist/lib.mjs +1 -1
  19. package/dist/{mcp-CYJAl5pk.mjs → mcp-BvDgylsM.mjs} +2 -2
  20. package/dist/{mcp-DbYXyw1U.cjs → mcp-CRcmpUpX.cjs} +2 -2
  21. package/dist/{runGemini-BDSWSID6.cjs → runGemini-BoVDs_V6.cjs} +4 -4
  22. package/dist/{runGemini-DHYs_Beq.mjs → runGemini-DIAChFOe.mjs} +4 -4
  23. package/dist/{runOpenClaw-DC9gb1bd.cjs → runOpenClaw-Cdsug1sI.cjs} +3 -3
  24. package/dist/{runOpenClaw-DTaXxvNd.mjs → runOpenClaw-DcC_uOjt.mjs} +3 -3
  25. package/dist/{send-BEdGwmA-.cjs → send-DoFm4-OB.cjs} +2 -2
  26. package/dist/{send-BQSaVz9r.mjs → send-DvDOKQ4V.mjs} +2 -2
  27. package/dist/{spawn-CK1ma_qc.cjs → spawn-C7QRBKkQ.cjs} +2 -2
  28. package/dist/{spawn-DS-Uz_Hw.mjs → spawn-CEWzROeD.mjs} +2 -2
  29. package/dist/{types-B_8X_rZk.mjs → types-CSOwbjq5.mjs} +37 -17
  30. package/dist/{types-CjlB_fKT.cjs → types-CcaZTXxx.cjs} +37 -17
  31. package/package.json +7 -7
@@ -3,7 +3,7 @@
3
3
  var node_child_process = require('node:child_process');
4
4
  var sdk = require('@agentclientprotocol/sdk');
5
5
  var node_crypto = require('node:crypto');
6
- var persistence = require('./types-CjlB_fKT.cjs');
6
+ var persistence = require('./types-CcaZTXxx.cjs');
7
7
 
8
8
  const DEFAULT_TIMEOUTS = {
9
9
  /** Default initialization timeout: 60 seconds */
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-B_8X_rZk.mjs';
4
+ import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-CSOwbjq5.mjs';
5
5
 
6
6
  const DEFAULT_TIMEOUTS = {
7
7
  /** Default initialization timeout: 60 seconds */
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { b as createEnvelope } from './types-B_8X_rZk.mjs';
2
+ import { b as createEnvelope } from './types-CSOwbjq5.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-CjlB_fKT.cjs');
4
+ var persistence = require('./types-CcaZTXxx.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-CjlB_fKT.cjs');
8
- var index = require('./index-B63Vjt4W.cjs');
7
+ var persistence = require('./types-CcaZTXxx.cjs');
8
+ var index = require('./index-bqUSeHWU.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-B_8X_rZk.mjs';
6
- import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-8LDXVYWi.mjs';
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';
7
7
  import 'axios';
8
8
  import 'chalk';
9
9
  import 'node:util';
@@ -1,7 +1,7 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
2
2
  import os, { homedir } from 'node:os';
3
3
  import { randomUUID, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
4
- import { n as readDaemonState, l as logger, c as configuration, o as clearDaemonState, q as getProjectPath, p as projectPath, s as initializeSandbox, t as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, g as delay, u as AsyncLock, v as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, x as hardenPrivateFile, f as packageJson, i as readSettings, y as readCredentials, z as credentialRelayProblem, B as deriveLocalCliUpdateSummary, j as encodeBase64, C as updateSettings, D as encodeBase64Url, E as decodeBase64, F as writeCredentialsLegacy, G as writeCredentialsDataKey, H as readCredentialsForConfiguredRelay, J as encrypt, r as readPersistedSessions, K as 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 persistSession, Y as MIRROR_BACKFILL_LINES_DEFAULT, 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-B_8X_rZk.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, $ 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';
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';
@@ -6766,10 +6766,20 @@ function createMirrorScanner(opts) {
6766
6766
  }
6767
6767
 
6768
6768
  const TERMINAL_MIRROR_FLAVOR = "terminal-mirror";
6769
+ const RECONCILE_HYSTERESIS_MS = 2e4;
6769
6770
  function createMirrorManager(deps) {
6770
6771
  const backfillLines = deps.backfillLines ?? MIRROR_BACKFILL_LINES_DEFAULT;
6771
6772
  const bindings = /* @__PURE__ */ new Map();
6773
+ const lastEndedAt = /* @__PURE__ */ new Map();
6772
6774
  let chain = Promise.resolve();
6775
+ const reactivate = async (binding) => {
6776
+ try {
6777
+ const ok = await deps.api.reactivateSession(binding.happySessionId);
6778
+ binding.needsReactivate = !ok;
6779
+ } catch {
6780
+ binding.needsReactivate = true;
6781
+ }
6782
+ };
6773
6783
  const isTerminalAlive = deps.isTerminalAlive ?? ((terminalId) => {
6774
6784
  try {
6775
6785
  return spawnSync("tmux", tmuxArgs(["has-session", "-t", `=vh-${terminalId}:`]), { stdio: "ignore", timeout: 3e3, env: scrubTmuxClientEnv({ ...process.env }) }).status === 0;
@@ -6821,6 +6831,8 @@ function createMirrorManager(deps) {
6821
6831
  const endBinding = async (binding, reason) => {
6822
6832
  if (binding.status === "ended") return;
6823
6833
  binding.status = "ended";
6834
+ lastEndedAt.set(binding.terminalId, Date.now());
6835
+ binding.needsReactivate = false;
6824
6836
  logger.debug(`[MIRROR] ending binding for terminal ${binding.terminalId} (${reason})`);
6825
6837
  await binding.scanner?.cleanup();
6826
6838
  binding.scanner = null;
@@ -6848,6 +6860,76 @@ function createMirrorManager(deps) {
6848
6860
  }
6849
6861
  await binding.client.close();
6850
6862
  };
6863
+ const adoptPersisted = async (terminalId, persisted) => {
6864
+ if (bindings.has(terminalId)) return;
6865
+ let best = null;
6866
+ for (const [sessionId2, entry2] of Object.entries(persisted)) {
6867
+ const meta2 = entry2.metadata;
6868
+ if (meta2?.flavor !== TERMINAL_MIRROR_FLAVOR) continue;
6869
+ if (meta2.machineId !== deps.machineId) continue;
6870
+ if (meta2.terminalId !== terminalId) continue;
6871
+ if (!meta2.claudeSessionId) continue;
6872
+ if (!best || entry2.savedAt > best.entry.savedAt) best = { sessionId: sessionId2, entry: entry2 };
6873
+ }
6874
+ if (!best) return;
6875
+ const { sessionId, entry } = best;
6876
+ const meta = entry.metadata;
6877
+ const session = {
6878
+ id: sessionId,
6879
+ seq: entry.seq,
6880
+ metadata: meta,
6881
+ metadataVersion: entry.metadataVersion,
6882
+ agentState: null,
6883
+ agentStateVersion: entry.agentStateVersion,
6884
+ encryptionKey: decodeBase64(entry.encryptionKey),
6885
+ encryptionVariant: entry.encryptionVariant
6886
+ };
6887
+ const client = deps.api.sessionSyncClient(session);
6888
+ client.skipExistingMessages();
6889
+ const binding = {
6890
+ terminalId,
6891
+ happySessionId: sessionId,
6892
+ claudeSessionId: meta.claudeSessionId,
6893
+ status: "active",
6894
+ client,
6895
+ scanner: null,
6896
+ persist: {
6897
+ encryptionKey: entry.encryptionKey,
6898
+ encryptionVariant: entry.encryptionVariant,
6899
+ seq: entry.seq,
6900
+ metadataVersion: entry.metadataVersion,
6901
+ agentStateVersion: entry.agentStateVersion
6902
+ },
6903
+ metadata: meta
6904
+ };
6905
+ bindings.set(terminalId, binding);
6906
+ await reactivate(binding);
6907
+ updateBindingMetadata(binding, {
6908
+ lifecycleState: "running",
6909
+ lifecycleStateSince: Date.now(),
6910
+ archivedBy: void 0,
6911
+ archiveReason: void 0
6912
+ });
6913
+ const scanner = attachScanner(binding, { withTruncationNotice: false });
6914
+ scanner.addFile(join(getProjectPath(meta.path), `${meta.claudeSessionId}.jsonl`), "backfill-tail");
6915
+ logger.debug(`[MIRROR] reconcile adopted terminal ${terminalId} \u2192 ${sessionId}`);
6916
+ deps.onBindingsChanged?.();
6917
+ };
6918
+ const reactivateInPlace = async (binding) => {
6919
+ if (binding.status !== "ended") return;
6920
+ binding.status = "active";
6921
+ await reactivate(binding);
6922
+ updateBindingMetadata(binding, {
6923
+ lifecycleState: "running",
6924
+ lifecycleStateSince: Date.now(),
6925
+ archivedBy: void 0,
6926
+ archiveReason: void 0
6927
+ });
6928
+ const scanner = binding.scanner ?? attachScanner(binding, { withTruncationNotice: false });
6929
+ scanner.addFile(join(getProjectPath(binding.metadata.path), `${binding.claudeSessionId}.jsonl`), "backfill-tail");
6930
+ logger.debug(`[MIRROR] reconcile reactivated (in place) terminal ${binding.terminalId}`);
6931
+ deps.onBindingsChanged?.();
6932
+ };
6851
6933
  const createBinding = async (event, replaces) => {
6852
6934
  if (replaces) {
6853
6935
  await teardownBinding(replaces, "superseded by a fresh conversation");
@@ -6908,6 +6990,7 @@ function createMirrorManager(deps) {
6908
6990
  binding.claudeSessionId = event.claudeSessionId;
6909
6991
  if (binding.status === "ended") {
6910
6992
  binding.status = "active";
6993
+ await reactivate(binding);
6911
6994
  updateBindingMetadata(binding, {
6912
6995
  lifecycleState: "running",
6913
6996
  lifecycleStateSince: Date.now(),
@@ -6977,6 +7060,43 @@ function createMirrorManager(deps) {
6977
7060
  }
6978
7061
  }
6979
7062
  },
7063
+ reconcile(list) {
7064
+ let persisted = null;
7065
+ const now = Date.now();
7066
+ for (const item of list) {
7067
+ const binding = bindings.get(item.id) ?? null;
7068
+ if (item.agentState === "shell") {
7069
+ if (binding && binding.status === "active") {
7070
+ chain = chain.then(() => {
7071
+ const b = bindings.get(item.id);
7072
+ return b && b.status === "active" ? endBinding(b, "claude exited (pane observation)") : void 0;
7073
+ }).catch((e) => logger.debug(`[MIRROR] reconcile end failed for ${item.id}:`, e));
7074
+ }
7075
+ continue;
7076
+ }
7077
+ if (binding && binding.status === "active" && binding.needsReactivate) {
7078
+ chain = chain.then(() => {
7079
+ const b = bindings.get(item.id);
7080
+ return b && b.status === "active" && b.needsReactivate ? reactivate(b) : void 0;
7081
+ }).catch((e) => logger.debug(`[MIRROR] reconcile reactivate-retry failed for ${item.id}:`, e));
7082
+ }
7083
+ if (!item.claudeConfident) continue;
7084
+ const endedAt = lastEndedAt.get(item.id);
7085
+ if (endedAt !== void 0 && now - endedAt < RECONCILE_HYSTERESIS_MS) continue;
7086
+ if (binding && binding.status === "ended") {
7087
+ chain = chain.then(() => {
7088
+ const b = bindings.get(item.id);
7089
+ return b && b.status === "ended" ? reactivateInPlace(b) : void 0;
7090
+ }).catch((e) => logger.debug(`[MIRROR] reconcile in-place reactivate failed for ${item.id}:`, e));
7091
+ } else if (!binding) {
7092
+ persisted ??= readPersistedSessions();
7093
+ const p = persisted;
7094
+ chain = chain.then(() => {
7095
+ return bindings.has(item.id) ? void 0 : adoptPersisted(item.id, p);
7096
+ }).catch((e) => logger.debug(`[MIRROR] reconcile adopt failed for ${item.id}:`, e));
7097
+ }
7098
+ }
7099
+ },
6980
7100
  resolveMirrorSessionId(terminalId) {
6981
7101
  return bindings.get(terminalId)?.happySessionId;
6982
7102
  },
@@ -8074,6 +8194,7 @@ async function startDaemon() {
8074
8194
  resolveMirrorSessionId: (terminalId) => mirrorManager.resolveMirrorSessionId(terminalId),
8075
8195
  onTerminalClosed: (terminalId) => mirrorManager.onTerminalClosed(terminalId),
8076
8196
  onTerminalList: (terminals) => mirrorManager.observeTerminalList(terminals),
8197
+ onTerminalListTick: (terminals) => mirrorManager.reconcile(terminals),
8077
8198
  isMirrorInputAllowed: (terminalId) => mirrorManager.isMirrorInputAllowed(terminalId)
8078
8199
  });
8079
8200
  mirrorManager.restore().catch((error) => {
@@ -13951,7 +14072,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13951
14072
  process.exit(0);
13952
14073
  } else if (subcommand === "install-terminal-hooks") {
13953
14074
  try {
13954
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-DDRrUG_n.mjs');
14075
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-dVZoe8om.mjs');
13955
14076
  const command = parseTerminalHooksArgs(args.slice(1));
13956
14077
  if (command.action === "help") {
13957
14078
  console.log(TERMINAL_HOOKS_HELP);
@@ -13968,7 +14089,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13968
14089
  }
13969
14090
  } else if (subcommand === "spawn") {
13970
14091
  try {
13971
- const { handleSpawnCommand } = await import('./spawn-DS-Uz_Hw.mjs');
14092
+ const { handleSpawnCommand } = await import('./spawn-CEWzROeD.mjs');
13972
14093
  await handleSpawnCommand(args.slice(1));
13973
14094
  } catch (error) {
13974
14095
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -13980,7 +14101,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13980
14101
  return;
13981
14102
  } else if (subcommand === "send") {
13982
14103
  try {
13983
- const { handleSendCommand } = await import('./send-BQSaVz9r.mjs');
14104
+ const { handleSendCommand } = await import('./send-DvDOKQ4V.mjs');
13984
14105
  await handleSendCommand(args.slice(1));
13985
14106
  } catch (error) {
13986
14107
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -14086,9 +14207,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14086
14207
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
14087
14208
  const projectId = args[3];
14088
14209
  try {
14089
- const { saveGoogleCloudProjectToConfig } = await import('./config-CJQw0z1o.mjs');
14090
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-B_8X_rZk.mjs').then(function (n) { return n.al; });
14091
- const { ApiClient: ApiClient2 } = await import('./types-B_8X_rZk.mjs').then(function (n) { return n.am; });
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; });
14092
14213
  let userEmail = void 0;
14093
14214
  try {
14094
14215
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -14119,7 +14240,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14119
14240
  }
14120
14241
  if (geminiSubcommand === "project" && args[2] === "get") {
14121
14242
  try {
14122
- const { readGeminiLocalConfig } = await import('./config-CJQw0z1o.mjs');
14243
+ const { readGeminiLocalConfig } = await import('./config-yrZNEia7.mjs');
14123
14244
  const config = readGeminiLocalConfig();
14124
14245
  if (config.googleCloudProject) {
14125
14246
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -14159,7 +14280,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14159
14280
  process.exit(0);
14160
14281
  }
14161
14282
  try {
14162
- const { runGemini } = await import('./runGemini-DHYs_Beq.mjs');
14283
+ const { runGemini } = await import('./runGemini-DIAChFOe.mjs');
14163
14284
  let startedBy = void 0;
14164
14285
  for (let i = 1; i < args.length; i++) {
14165
14286
  if (args[i] === "--started-by") {
@@ -14181,7 +14302,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14181
14302
  return;
14182
14303
  } else if (subcommand === "acp") {
14183
14304
  try {
14184
- const { runAcp, resolveAcpAgentConfig } = await import('./index-BP9pfzdZ.mjs');
14305
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-DXpQrR4U.mjs');
14185
14306
  let startedBy = void 0;
14186
14307
  let verbose = false;
14187
14308
  const acpArgs = [];
@@ -14221,7 +14342,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14221
14342
  return;
14222
14343
  } else if (subcommand === "openclaw") {
14223
14344
  try {
14224
- const { runOpenClaw } = await import('./runOpenClaw-DTaXxvNd.mjs');
14345
+ const { runOpenClaw } = await import('./runOpenClaw-DcC_uOjt.mjs');
14225
14346
  let startedBy = void 0;
14226
14347
  let verbose = false;
14227
14348
  let gatewayUrl;
@@ -14272,7 +14393,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14272
14393
  return;
14273
14394
  } else if (subcommand === "mcp" && args.length === 1) {
14274
14395
  try {
14275
- const { handleMcpCommand } = await import('./mcp-CYJAl5pk.mjs');
14396
+ const { handleMcpCommand } = await import('./mcp-BvDgylsM.mjs');
14276
14397
  await handleMcpCommand();
14277
14398
  } catch (error) {
14278
14399
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-BP7id8L6.cjs');
4
- var persistence = require('./types-CjlB_fKT.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-Cys1Hsb8.cjs');
3
+ var AcpBackend = require('./AcpBackend-DoSXb9La.cjs');
4
+ var persistence = require('./types-CcaZTXxx.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-DQ6j5fp4.cjs');
6
6
  var node_crypto = require('node:crypto');
7
7
  var path = require('node:path');
8
- var index = require('./index-B63Vjt4W.cjs');
8
+ var index = require('./index-bqUSeHWU.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-DDEsgc1c.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-DDEsgc1c.mjs';
3
- import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-B_8X_rZk.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-C29t6wb_.mjs';
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';
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-8LDXVYWi.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-27948eGz.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-CjlB_fKT.cjs');
6
+ var persistence = require('./types-CcaZTXxx.cjs');
7
7
  var spawn = require('cross-spawn');
8
8
  var path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -6788,10 +6788,20 @@ function createMirrorScanner(opts) {
6788
6788
  }
6789
6789
 
6790
6790
  const TERMINAL_MIRROR_FLAVOR = "terminal-mirror";
6791
+ const RECONCILE_HYSTERESIS_MS = 2e4;
6791
6792
  function createMirrorManager(deps) {
6792
6793
  const backfillLines = deps.backfillLines ?? persistence.MIRROR_BACKFILL_LINES_DEFAULT;
6793
6794
  const bindings = /* @__PURE__ */ new Map();
6795
+ const lastEndedAt = /* @__PURE__ */ new Map();
6794
6796
  let chain = Promise.resolve();
6797
+ const reactivate = async (binding) => {
6798
+ try {
6799
+ const ok = await deps.api.reactivateSession(binding.happySessionId);
6800
+ binding.needsReactivate = !ok;
6801
+ } catch {
6802
+ binding.needsReactivate = true;
6803
+ }
6804
+ };
6795
6805
  const isTerminalAlive = deps.isTerminalAlive ?? ((terminalId) => {
6796
6806
  try {
6797
6807
  return node_child_process.spawnSync("tmux", persistence.tmuxArgs(["has-session", "-t", `=vh-${terminalId}:`]), { stdio: "ignore", timeout: 3e3, env: persistence.scrubTmuxClientEnv({ ...process.env }) }).status === 0;
@@ -6843,6 +6853,8 @@ function createMirrorManager(deps) {
6843
6853
  const endBinding = async (binding, reason) => {
6844
6854
  if (binding.status === "ended") return;
6845
6855
  binding.status = "ended";
6856
+ lastEndedAt.set(binding.terminalId, Date.now());
6857
+ binding.needsReactivate = false;
6846
6858
  persistence.logger.debug(`[MIRROR] ending binding for terminal ${binding.terminalId} (${reason})`);
6847
6859
  await binding.scanner?.cleanup();
6848
6860
  binding.scanner = null;
@@ -6870,6 +6882,76 @@ function createMirrorManager(deps) {
6870
6882
  }
6871
6883
  await binding.client.close();
6872
6884
  };
6885
+ const adoptPersisted = async (terminalId, persisted) => {
6886
+ if (bindings.has(terminalId)) return;
6887
+ let best = null;
6888
+ for (const [sessionId2, entry2] of Object.entries(persisted)) {
6889
+ const meta2 = entry2.metadata;
6890
+ if (meta2?.flavor !== TERMINAL_MIRROR_FLAVOR) continue;
6891
+ if (meta2.machineId !== deps.machineId) continue;
6892
+ if (meta2.terminalId !== terminalId) continue;
6893
+ if (!meta2.claudeSessionId) continue;
6894
+ if (!best || entry2.savedAt > best.entry.savedAt) best = { sessionId: sessionId2, entry: entry2 };
6895
+ }
6896
+ if (!best) return;
6897
+ const { sessionId, entry } = best;
6898
+ const meta = entry.metadata;
6899
+ const session = {
6900
+ id: sessionId,
6901
+ seq: entry.seq,
6902
+ metadata: meta,
6903
+ metadataVersion: entry.metadataVersion,
6904
+ agentState: null,
6905
+ agentStateVersion: entry.agentStateVersion,
6906
+ encryptionKey: persistence.decodeBase64(entry.encryptionKey),
6907
+ encryptionVariant: entry.encryptionVariant
6908
+ };
6909
+ const client = deps.api.sessionSyncClient(session);
6910
+ client.skipExistingMessages();
6911
+ const binding = {
6912
+ terminalId,
6913
+ happySessionId: sessionId,
6914
+ claudeSessionId: meta.claudeSessionId,
6915
+ status: "active",
6916
+ client,
6917
+ scanner: null,
6918
+ persist: {
6919
+ encryptionKey: entry.encryptionKey,
6920
+ encryptionVariant: entry.encryptionVariant,
6921
+ seq: entry.seq,
6922
+ metadataVersion: entry.metadataVersion,
6923
+ agentStateVersion: entry.agentStateVersion
6924
+ },
6925
+ metadata: meta
6926
+ };
6927
+ bindings.set(terminalId, binding);
6928
+ await reactivate(binding);
6929
+ updateBindingMetadata(binding, {
6930
+ lifecycleState: "running",
6931
+ lifecycleStateSince: Date.now(),
6932
+ archivedBy: void 0,
6933
+ archiveReason: void 0
6934
+ });
6935
+ const scanner = attachScanner(binding, { withTruncationNotice: false });
6936
+ scanner.addFile(path.join(persistence.getProjectPath(meta.path), `${meta.claudeSessionId}.jsonl`), "backfill-tail");
6937
+ persistence.logger.debug(`[MIRROR] reconcile adopted terminal ${terminalId} \u2192 ${sessionId}`);
6938
+ deps.onBindingsChanged?.();
6939
+ };
6940
+ const reactivateInPlace = async (binding) => {
6941
+ if (binding.status !== "ended") return;
6942
+ binding.status = "active";
6943
+ await reactivate(binding);
6944
+ updateBindingMetadata(binding, {
6945
+ lifecycleState: "running",
6946
+ lifecycleStateSince: Date.now(),
6947
+ archivedBy: void 0,
6948
+ archiveReason: void 0
6949
+ });
6950
+ const scanner = binding.scanner ?? attachScanner(binding, { withTruncationNotice: false });
6951
+ scanner.addFile(path.join(persistence.getProjectPath(binding.metadata.path), `${binding.claudeSessionId}.jsonl`), "backfill-tail");
6952
+ persistence.logger.debug(`[MIRROR] reconcile reactivated (in place) terminal ${binding.terminalId}`);
6953
+ deps.onBindingsChanged?.();
6954
+ };
6873
6955
  const createBinding = async (event, replaces) => {
6874
6956
  if (replaces) {
6875
6957
  await teardownBinding(replaces, "superseded by a fresh conversation");
@@ -6930,6 +7012,7 @@ function createMirrorManager(deps) {
6930
7012
  binding.claudeSessionId = event.claudeSessionId;
6931
7013
  if (binding.status === "ended") {
6932
7014
  binding.status = "active";
7015
+ await reactivate(binding);
6933
7016
  updateBindingMetadata(binding, {
6934
7017
  lifecycleState: "running",
6935
7018
  lifecycleStateSince: Date.now(),
@@ -6999,6 +7082,43 @@ function createMirrorManager(deps) {
6999
7082
  }
7000
7083
  }
7001
7084
  },
7085
+ reconcile(list) {
7086
+ let persisted = null;
7087
+ const now = Date.now();
7088
+ for (const item of list) {
7089
+ const binding = bindings.get(item.id) ?? null;
7090
+ if (item.agentState === "shell") {
7091
+ if (binding && binding.status === "active") {
7092
+ chain = chain.then(() => {
7093
+ const b = bindings.get(item.id);
7094
+ return b && b.status === "active" ? endBinding(b, "claude exited (pane observation)") : void 0;
7095
+ }).catch((e) => persistence.logger.debug(`[MIRROR] reconcile end failed for ${item.id}:`, e));
7096
+ }
7097
+ continue;
7098
+ }
7099
+ if (binding && binding.status === "active" && binding.needsReactivate) {
7100
+ chain = chain.then(() => {
7101
+ const b = bindings.get(item.id);
7102
+ return b && b.status === "active" && b.needsReactivate ? reactivate(b) : void 0;
7103
+ }).catch((e) => persistence.logger.debug(`[MIRROR] reconcile reactivate-retry failed for ${item.id}:`, e));
7104
+ }
7105
+ if (!item.claudeConfident) continue;
7106
+ const endedAt = lastEndedAt.get(item.id);
7107
+ if (endedAt !== void 0 && now - endedAt < RECONCILE_HYSTERESIS_MS) continue;
7108
+ if (binding && binding.status === "ended") {
7109
+ chain = chain.then(() => {
7110
+ const b = bindings.get(item.id);
7111
+ return b && b.status === "ended" ? reactivateInPlace(b) : void 0;
7112
+ }).catch((e) => persistence.logger.debug(`[MIRROR] reconcile in-place reactivate failed for ${item.id}:`, e));
7113
+ } else if (!binding) {
7114
+ persisted ??= persistence.readPersistedSessions();
7115
+ const p = persisted;
7116
+ chain = chain.then(() => {
7117
+ return bindings.has(item.id) ? void 0 : adoptPersisted(item.id, p);
7118
+ }).catch((e) => persistence.logger.debug(`[MIRROR] reconcile adopt failed for ${item.id}:`, e));
7119
+ }
7120
+ }
7121
+ },
7002
7122
  resolveMirrorSessionId(terminalId) {
7003
7123
  return bindings.get(terminalId)?.happySessionId;
7004
7124
  },
@@ -8096,6 +8216,7 @@ async function startDaemon() {
8096
8216
  resolveMirrorSessionId: (terminalId) => mirrorManager.resolveMirrorSessionId(terminalId),
8097
8217
  onTerminalClosed: (terminalId) => mirrorManager.onTerminalClosed(terminalId),
8098
8218
  onTerminalList: (terminals) => mirrorManager.observeTerminalList(terminals),
8219
+ onTerminalListTick: (terminals) => mirrorManager.reconcile(terminals),
8099
8220
  isMirrorInputAllowed: (terminalId) => mirrorManager.isMirrorInputAllowed(terminalId)
8100
8221
  });
8101
8222
  mirrorManager.restore().catch((error) => {
@@ -9156,7 +9277,7 @@ const MAX_TITLE_CHARS = 60;
9156
9277
  const GENERATION_TIMEOUT_MS = 3e4;
9157
9278
  function resolveClaudeBinary() {
9158
9279
  try {
9159
- 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-B63Vjt4W.cjs', document.baseURI).href)));
9280
+ 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-bqUSeHWU.cjs', document.baseURI).href)));
9160
9281
  const utilsPath = path.resolve(path.join(persistence.projectPath(), "scripts", "claude_version_utils.cjs"));
9161
9282
  const { getClaudeCliPath } = require$1(utilsPath);
9162
9283
  const path$1 = getClaudeCliPath();
@@ -11350,9 +11471,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
11350
11471
  };
11351
11472
  }
11352
11473
 
11353
- 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-B63Vjt4W.cjs', document.baseURI).href)));
11474
+ 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-bqUSeHWU.cjs', document.baseURI).href)));
11354
11475
  const __dirname$1 = path.dirname(__filename$1);
11355
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-B63Vjt4W.cjs', document.baseURI).href)));
11476
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-bqUSeHWU.cjs', document.baseURI).href)));
11356
11477
  const PRISMA_QUERY_ENGINE_FILES = {
11357
11478
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
11358
11479
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -13973,7 +14094,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13973
14094
  process.exit(0);
13974
14095
  } else if (subcommand === "install-terminal-hooks") {
13975
14096
  try {
13976
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-BBc2nij4.cjs'); });
14097
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-D0gCqzZu.cjs'); });
13977
14098
  const command = parseTerminalHooksArgs(args.slice(1));
13978
14099
  if (command.action === "help") {
13979
14100
  console.log(TERMINAL_HOOKS_HELP);
@@ -13990,7 +14111,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
13990
14111
  }
13991
14112
  } else if (subcommand === "spawn") {
13992
14113
  try {
13993
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-CK1ma_qc.cjs'); });
14114
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-C7QRBKkQ.cjs'); });
13994
14115
  await handleSpawnCommand(args.slice(1));
13995
14116
  } catch (error) {
13996
14117
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -14002,7 +14123,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14002
14123
  return;
14003
14124
  } else if (subcommand === "send") {
14004
14125
  try {
14005
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-BEdGwmA-.cjs'); });
14126
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DoFm4-OB.cjs'); });
14006
14127
  await handleSendCommand(args.slice(1));
14007
14128
  } catch (error) {
14008
14129
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -14108,9 +14229,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14108
14229
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
14109
14230
  const projectId = args[3];
14110
14231
  try {
14111
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-B77vhdwT.cjs'); });
14112
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types-CjlB_fKT.cjs'); }).then(function (n) { return n.persistence; });
14113
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CjlB_fKT.cjs'); }).then(function (n) { return n.api; });
14232
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-IdMPfmwy.cjs'); });
14233
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types-CcaZTXxx.cjs'); }).then(function (n) { return n.persistence; });
14234
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CcaZTXxx.cjs'); }).then(function (n) { return n.api; });
14114
14235
  let userEmail = void 0;
14115
14236
  try {
14116
14237
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -14141,7 +14262,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14141
14262
  }
14142
14263
  if (geminiSubcommand === "project" && args[2] === "get") {
14143
14264
  try {
14144
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-B77vhdwT.cjs'); });
14265
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-IdMPfmwy.cjs'); });
14145
14266
  const config = readGeminiLocalConfig();
14146
14267
  if (config.googleCloudProject) {
14147
14268
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -14181,7 +14302,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14181
14302
  process.exit(0);
14182
14303
  }
14183
14304
  try {
14184
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-BDSWSID6.cjs'); });
14305
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-BoVDs_V6.cjs'); });
14185
14306
  let startedBy = void 0;
14186
14307
  for (let i = 1; i < args.length; i++) {
14187
14308
  if (args[i] === "--started-by") {
@@ -14203,7 +14324,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14203
14324
  return;
14204
14325
  } else if (subcommand === "acp") {
14205
14326
  try {
14206
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-L3o5Gz-2.cjs'); });
14327
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BfCdbWQI.cjs'); });
14207
14328
  let startedBy = void 0;
14208
14329
  let verbose = false;
14209
14330
  const acpArgs = [];
@@ -14243,7 +14364,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14243
14364
  return;
14244
14365
  } else if (subcommand === "openclaw") {
14245
14366
  try {
14246
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-DC9gb1bd.cjs'); });
14367
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-Cdsug1sI.cjs'); });
14247
14368
  let startedBy = void 0;
14248
14369
  let verbose = false;
14249
14370
  let gatewayUrl;
@@ -14294,7 +14415,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
14294
14415
  return;
14295
14416
  } else if (subcommand === "mcp" && args.length === 1) {
14296
14417
  try {
14297
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-DbYXyw1U.cjs'); });
14418
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-CRcmpUpX.cjs'); });
14298
14419
  await handleMcpCommand();
14299
14420
  } catch (error) {
14300
14421
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  require('chalk');
4
- require('./index-B63Vjt4W.cjs');
5
- require('./types-CjlB_fKT.cjs');
4
+ require('./index-bqUSeHWU.cjs');
5
+ require('./types-CcaZTXxx.cjs');
6
6
  require('zod');
7
7
  require('node:child_process');
8
8
  require('node:os');
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'chalk';
2
- import './index-8LDXVYWi.mjs';
3
- import './types-B_8X_rZk.mjs';
2
+ import './index-27948eGz.mjs';
3
+ import './types-CSOwbjq5.mjs';
4
4
  import 'zod';
5
5
  import 'node:child_process';
6
6
  import 'node:os';
@@ -5,8 +5,8 @@ var path = require('node:path');
5
5
  var os = require('node:os');
6
6
  var node_child_process = require('node:child_process');
7
7
  var chalk = require('chalk');
8
- var persistence = require('./types-CjlB_fKT.cjs');
9
- var index = require('./index-B63Vjt4W.cjs');
8
+ var persistence = require('./types-CcaZTXxx.cjs');
9
+ var index = require('./index-bqUSeHWU.cjs');
10
10
  require('axios');
11
11
  require('node:util');
12
12
  require('node:fs/promises');
@@ -3,8 +3,8 @@ import { join, resolve } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import chalk from 'chalk';
6
- import { p as projectPath } from './types-B_8X_rZk.mjs';
7
- import { t as tmuxSupportsSessionEnv } from './index-8LDXVYWi.mjs';
6
+ import { p as projectPath } from './types-CSOwbjq5.mjs';
7
+ import { t as tmuxSupportsSessionEnv } from './index-27948eGz.mjs';
8
8
  import 'axios';
9
9
  import 'node:util';
10
10
  import 'node:fs/promises';
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var persistence = require('./types-CjlB_fKT.cjs');
3
+ var persistence = require('./types-CcaZTXxx.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('node:util');
package/dist/lib.d.cts CHANGED
@@ -1055,6 +1055,12 @@ interface TerminalListItem {
1055
1055
  createdAt?: number;
1056
1056
  activityAt?: number;
1057
1057
  agentState?: AgentState;
1058
+ /** Mirror reconcile (design v3): stricter "claude is really here" gate (its
1059
+ * own TUI footer/dialog present) — daemon-internal, NOT pushed to web and
1060
+ * NOT part of the list signature. Only this may trigger mirror adopt (which
1061
+ * re-opens the B-107 input gate); `agentState` alone would false-positive
1062
+ * on a bare `node` process. */
1063
+ claudeConfident?: boolean;
1058
1064
  /** Terminal mirror (B-105): shadow session id of the hand-typed claude
1059
1065
  * running inside this terminal (set via the daemon's mirror resolver).
1060
1066
  * The web shows the xterm ↔ structured toggle when present. */
@@ -1147,6 +1153,9 @@ declare class ApiMachineClient {
1147
1153
  resolveMirrorSessionId: (terminalId: string) => string | undefined;
1148
1154
  onTerminalClosed: (terminalId: string) => void;
1149
1155
  onTerminalList: (terminals: TerminalListItem[]) => void;
1156
+ /** design v3: unconditional per-tick reconcile (self-heals lost bindings
1157
+ * on an unchanged-signature list). */
1158
+ onTerminalListTick: (terminals: TerminalListItem[]) => void;
1150
1159
  isMirrorInputAllowed: (terminalId: string) => boolean;
1151
1160
  }): void;
1152
1161
  /** Mirror bindings changed → re-derive and (on diff) push the list now. */
package/dist/lib.d.mts CHANGED
@@ -1055,6 +1055,12 @@ interface TerminalListItem {
1055
1055
  createdAt?: number;
1056
1056
  activityAt?: number;
1057
1057
  agentState?: AgentState;
1058
+ /** Mirror reconcile (design v3): stricter "claude is really here" gate (its
1059
+ * own TUI footer/dialog present) — daemon-internal, NOT pushed to web and
1060
+ * NOT part of the list signature. Only this may trigger mirror adopt (which
1061
+ * re-opens the B-107 input gate); `agentState` alone would false-positive
1062
+ * on a bare `node` process. */
1063
+ claudeConfident?: boolean;
1058
1064
  /** Terminal mirror (B-105): shadow session id of the hand-typed claude
1059
1065
  * running inside this terminal (set via the daemon's mirror resolver).
1060
1066
  * The web shows the xterm ↔ structured toggle when present. */
@@ -1147,6 +1153,9 @@ declare class ApiMachineClient {
1147
1153
  resolveMirrorSessionId: (terminalId: string) => string | undefined;
1148
1154
  onTerminalClosed: (terminalId: string) => void;
1149
1155
  onTerminalList: (terminals: TerminalListItem[]) => void;
1156
+ /** design v3: unconditional per-tick reconcile (self-heals lost bindings
1157
+ * on an unchanged-signature list). */
1158
+ onTerminalListTick: (terminals: TerminalListItem[]) => void;
1150
1159
  isMirrorInputAllowed: (terminalId: string) => boolean;
1151
1160
  }): void;
1152
1161
  /** Mirror bindings changed → re-derive and (on diff) push the list now. */
package/dist/lib.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-B_8X_rZk.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-CSOwbjq5.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'node:util';
@@ -1,9 +1,9 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { z } from 'zod';
4
- import { p as pushClipboardViaDaemon } from './index-8LDXVYWi.mjs';
4
+ import { p as pushClipboardViaDaemon } from './index-27948eGz.mjs';
5
5
  import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, c as CLIPBOARD_MAX_BYTES } from './limits-CwFb5S2b.mjs';
6
- import { l as logger } from './types-B_8X_rZk.mjs';
6
+ import { l as logger } from './types-CSOwbjq5.mjs';
7
7
  import 'chalk';
8
8
  import 'node:os';
9
9
  import 'node:crypto';
@@ -3,9 +3,9 @@
3
3
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
4
4
  var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
5
5
  var z = require('zod');
6
- var index = require('./index-B63Vjt4W.cjs');
6
+ var index = require('./index-bqUSeHWU.cjs');
7
7
  var limits = require('./limits-KdWKmwtH.cjs');
8
- var persistence = require('./types-CjlB_fKT.cjs');
8
+ var persistence = require('./types-CcaZTXxx.cjs');
9
9
  require('chalk');
10
10
  require('node:os');
11
11
  require('node:crypto');
@@ -4,10 +4,10 @@ var ink = require('ink');
4
4
  var React = require('react');
5
5
  var node_crypto = require('node:crypto');
6
6
  var path = require('node:path');
7
- var persistence = require('./types-CjlB_fKT.cjs');
8
- var index = require('./index-B63Vjt4W.cjs');
9
- var AcpBackend = require('./AcpBackend-BP7id8L6.cjs');
10
- var config = require('./config-B77vhdwT.cjs');
7
+ var persistence = require('./types-CcaZTXxx.cjs');
8
+ var index = require('./index-bqUSeHWU.cjs');
9
+ var AcpBackend = require('./AcpBackend-DoSXb9La.cjs');
10
+ var config = require('./config-IdMPfmwy.cjs');
11
11
  require('axios');
12
12
  require('chalk');
13
13
  require('node:util');
@@ -2,10 +2,10 @@ import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { join } from 'node:path';
5
- import { l as logger, h as connectionState, A as ApiClient, i as readSettings, e as errorLogMetadata, j as encodeBase64, p as projectPath, d as contentLogMetadata } from './types-B_8X_rZk.mjs';
6
- import { g as GEMINI_API_KEY_ENV, j as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, B as BasePermissionHandler, k as BaseReasoningProcessor, i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, l as MessageBuffer, C as CHANGE_TITLE_INSTRUCTION, r as registerKillSessionHandler, f as startHappyServer } from './index-8LDXVYWi.mjs';
7
- import { A as AcpBackend } from './AcpBackend-DDEsgc1c.mjs';
8
- import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-CJQw0z1o.mjs';
5
+ import { l as logger, h as connectionState, A as ApiClient, i as readSettings, e as errorLogMetadata, j as encodeBase64, p as projectPath, d as contentLogMetadata } from './types-CSOwbjq5.mjs';
6
+ import { g as GEMINI_API_KEY_ENV, j as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, B as BasePermissionHandler, k as BaseReasoningProcessor, i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, l as MessageBuffer, C as CHANGE_TITLE_INSTRUCTION, r as registerKillSessionHandler, f as startHappyServer } from './index-27948eGz.mjs';
7
+ import { A as AcpBackend } from './AcpBackend-Ds4mP02F.mjs';
8
+ import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-yrZNEia7.mjs';
9
9
  import 'axios';
10
10
  import 'chalk';
11
11
  import 'node:util';
@@ -5,9 +5,9 @@ var node_child_process = require('node:child_process');
5
5
  var os = require('node:os');
6
6
  var node_fs = require('node:fs');
7
7
  var path = require('node:path');
8
- var persistence = require('./types-CjlB_fKT.cjs');
9
- var AcpSessionManager = require('./AcpSessionManager-Cys1Hsb8.cjs');
10
- var index = require('./index-B63Vjt4W.cjs');
8
+ var persistence = require('./types-CcaZTXxx.cjs');
9
+ var AcpSessionManager = require('./AcpSessionManager-DQ6j5fp4.cjs');
10
+ var index = require('./index-bqUSeHWU.cjs');
11
11
  var WebSocket = require('ws');
12
12
  var ed = require('@noble/ed25519');
13
13
  var sha2_js = require('@noble/hashes/sha2.js');
@@ -3,9 +3,9 @@ import { execFileSync } from 'node:child_process';
3
3
  import os from 'node:os';
4
4
  import { existsSync, readFileSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
- import { k as ensurePrivateDirectorySync, m as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, c as configuration } from './types-B_8X_rZk.mjs';
7
- import { A as AcpSessionManager } from './AcpSessionManager-C29t6wb_.mjs';
8
- import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, r as registerKillSessionHandler } from './index-8LDXVYWi.mjs';
6
+ import { k as ensurePrivateDirectorySync, m as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, c as configuration } from './types-CSOwbjq5.mjs';
7
+ import { A as AcpSessionManager } from './AcpSessionManager-B6RqjyQS.mjs';
8
+ import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, r as registerKillSessionHandler } from './index-27948eGz.mjs';
9
9
  import WebSocket from 'ws';
10
10
  import * as ed from '@noble/ed25519';
11
11
  import { sha512 } from '@noble/hashes/sha2.js';
@@ -3,8 +3,8 @@
3
3
  var chalk = require('chalk');
4
4
  var node_fs = require('node:fs');
5
5
  var path = require('node:path');
6
- var persistence = require('./types-CjlB_fKT.cjs');
7
- var index = require('./index-B63Vjt4W.cjs');
6
+ var persistence = require('./types-CcaZTXxx.cjs');
7
+ var index = require('./index-bqUSeHWU.cjs');
8
8
  require('axios');
9
9
  require('node:util');
10
10
  require('node:os');
@@ -1,8 +1,8 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
- import { r as readPersistedSessions, c as configuration } from './types-B_8X_rZk.mjs';
5
- import { b as sendUserMessage, a as sessionWebUrl } from './index-8LDXVYWi.mjs';
4
+ import { r as readPersistedSessions, c as configuration } from './types-CSOwbjq5.mjs';
5
+ import { b as sendUserMessage, a as sessionWebUrl } from './index-27948eGz.mjs';
6
6
  import 'axios';
7
7
  import 'node:util';
8
8
  import 'node:os';
@@ -3,8 +3,8 @@
3
3
  var chalk = require('chalk');
4
4
  var node_fs = require('node:fs');
5
5
  var path = require('node:path');
6
- var index = require('./index-B63Vjt4W.cjs');
7
- var persistence = require('./types-CjlB_fKT.cjs');
6
+ var index = require('./index-bqUSeHWU.cjs');
7
+ var persistence = require('./types-CcaZTXxx.cjs');
8
8
  require('node:os');
9
9
  require('node:crypto');
10
10
  require('cross-spawn');
@@ -1,8 +1,8 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
- import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, w as waitForSessionKey, b as sendUserMessage } from './index-8LDXVYWi.mjs';
5
- import { l as logger } from './types-B_8X_rZk.mjs';
4
+ import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, w as waitForSessionKey, b as sendUserMessage } from './index-27948eGz.mjs';
5
+ import { l as logger } from './types-CSOwbjq5.mjs';
6
6
  import 'node:os';
7
7
  import 'node:crypto';
8
8
  import 'cross-spawn';
@@ -29,7 +29,7 @@ import { SandboxManager } from '@anthropic-ai/sandbox-runtime';
29
29
  import { Expo } from 'expo-server-sdk';
30
30
 
31
31
  var name = "very-happy-cli";
32
- var version = "0.2.92";
32
+ var version = "0.2.94";
33
33
  var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
34
34
  var author = "Kirill Dubovitskiy";
35
35
  var contributors = [
@@ -142,12 +142,12 @@ var dependencies = {
142
142
  zod: "^4.0.0"
143
143
  };
144
144
  var optionalDependencies = {
145
- "very-happy-tools-arm64-darwin": "0.2.92",
146
- "very-happy-tools-x64-darwin": "0.2.92",
147
- "very-happy-tools-arm64-linux": "0.2.92",
148
- "very-happy-tools-x64-linux": "0.2.92",
149
- "very-happy-tools-arm64-win32": "0.2.92",
150
- "very-happy-tools-x64-win32": "0.2.92"
145
+ "very-happy-tools-arm64-darwin": "0.2.94",
146
+ "very-happy-tools-x64-darwin": "0.2.94",
147
+ "very-happy-tools-arm64-linux": "0.2.94",
148
+ "very-happy-tools-x64-linux": "0.2.94",
149
+ "very-happy-tools-arm64-win32": "0.2.94",
150
+ "very-happy-tools-x64-win32": "0.2.94"
151
151
  };
152
152
  var devDependencies = {
153
153
  "@slopus/happy-wire": "workspace:*",
@@ -10257,6 +10257,13 @@ function looksLikeClaudeCommand(currentCommand) {
10257
10257
  const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
10258
10258
  return cmd === "claude" || cmd === "node" || /^\d+\.\d+(\.\d+)?$/.test(cmd);
10259
10259
  }
10260
+ function isClaudeConfident(tail) {
10261
+ const lines = tail.replace(/\r/g, "").split("\n").map((l) => l.trimEnd());
10262
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
10263
+ const text = lines.join("\n");
10264
+ const last15 = lines.slice(-15).join("\n");
10265
+ return last15.includes("Do you want") || last15.includes("Would you like to proceed") || /^[\s│]*[❯>]\s*1\.\s/m.test(last15) || text.includes("esc to interrupt") || text.includes("? for shortcuts") || text.includes("bypass permissions on") || text.includes("\u23F5\u23F5");
10266
+ }
10260
10267
  function planScrollAction(paneInMode, alternateOn, paneWantsMouse, lines, claudeLike = false, paneRows = 24) {
10261
10268
  const count = Math.min(Math.abs(Math.trunc(lines)), 200);
10262
10269
  if (count === 0) return { kind: "none" };
@@ -10643,9 +10650,16 @@ class WebTerminalManager {
10643
10650
  /** terminalId → shadow session id (daemon's mirror manager). Feeds the
10644
10651
  * list push (toggle availability) and closed-terminal records. */
10645
10652
  mirrorResolver = null;
10653
+ mirrorTickObserver = null;
10646
10654
  /** Fired once per terminal whose close was just recorded — the mirror
10647
10655
  * manager archives + tears down that terminal's binding. */
10648
10656
  onTerminalClosedCb = null;
10657
+ /** Mirror reconcile (design v3): fired every list-track tick, BEFORE the
10658
+ * signature short-circuit, so a claude-running terminal whose binding was
10659
+ * lost self-heals even when nothing about the list changed. */
10660
+ setMirrorTickObserver(fn) {
10661
+ this.mirrorTickObserver = fn;
10662
+ }
10649
10663
  setMirrorSessionResolver(fn) {
10650
10664
  this.mirrorResolver = fn;
10651
10665
  this.kickListRefresh();
@@ -10828,6 +10842,7 @@ class WebTerminalManager {
10828
10842
  this.emitActivity(Object.fromEntries(
10829
10843
  list.filter((t) => t.activityAt).map((t) => [t.id, t.activityAt])
10830
10844
  ));
10845
+ this.mirrorTickObserver?.(list);
10831
10846
  const sig = terminalListSignature(list);
10832
10847
  if (sig === this.lastListSignature) return;
10833
10848
  this.lastListSignature = sig;
@@ -11472,7 +11487,7 @@ class WebTerminalManager {
11472
11487
  if (session.transport.kind !== "control") throw new Error("capture requires a control client");
11473
11488
  const { client } = session.transport;
11474
11489
  const batch = buildCaptureBatch({
11475
- paneTarget: `=${session.tmuxSession}:.0`,
11490
+ paneTarget: `=${session.tmuxSession}:`,
11476
11491
  historyLines: HEADLESS_SCROLLBACK,
11477
11492
  smallLines: SNAPSHOT_SCROLLBACK,
11478
11493
  cols: session.cols,
@@ -11567,7 +11582,7 @@ class WebTerminalManager {
11567
11582
  session.transport.pty.write(text);
11568
11583
  return;
11569
11584
  }
11570
- const target = session.paneId ?? `=${session.tmuxSession}:.0`;
11585
+ const target = session.paneId ?? `=${session.tmuxSession}:`;
11571
11586
  const { commands, dropped } = encodeTerminalWrite(text, target, { normalizeKeyNames: true });
11572
11587
  if (dropped) {
11573
11588
  logger.debug(`[WEB TERMINAL] dropped terminal auto-reply for ${terminalId}`);
@@ -11596,7 +11611,7 @@ class WebTerminalManager {
11596
11611
  session.transport.pty.write(text);
11597
11612
  return;
11598
11613
  }
11599
- const target = session.paneId ?? `=${session.tmuxSession}:.0`;
11614
+ const target = session.paneId ?? `=${session.tmuxSession}:`;
11600
11615
  const plan = buildPastePlan(text, target, { dir: pasteSpoolDir() });
11601
11616
  writeFileSync(plan.path, plan.bytes, { mode: 384 });
11602
11617
  try {
@@ -11767,6 +11782,7 @@ class WebTerminalManager {
11767
11782
  } catch {
11768
11783
  }
11769
11784
  }
11785
+ const probe = this.probeAgentState(s.name, s.paneCurrentCommand);
11770
11786
  out.push({
11771
11787
  id,
11772
11788
  title,
@@ -11777,7 +11793,10 @@ class WebTerminalManager {
11777
11793
  // tmux last-activity (epoch s) → ms; optional so old daemons
11778
11794
  // simply omit it and web clients fall back to createdAt.
11779
11795
  activityAt: s.activity,
11780
- agentState: this.probeAgentState(s.name, s.paneCurrentCommand),
11796
+ agentState: probe.agentState,
11797
+ // Daemon-internal (not pushed to web / not in the signature):
11798
+ // the mirror reconciler's stricter "claude is really here" gate.
11799
+ claudeConfident: probe.claudeConfident,
11781
11800
  manual: s.manual
11782
11801
  });
11783
11802
  }
@@ -11799,7 +11818,7 @@ class WebTerminalManager {
11799
11818
  if (live) {
11800
11819
  try {
11801
11820
  const { command, tail } = live.agentProbeInput();
11802
- return classifyPane(command || polledCommand || "", tail);
11821
+ return { agentState: classifyPane(command || polledCommand || "", tail), claudeConfident: isClaudeConfident(tail) };
11803
11822
  } catch {
11804
11823
  }
11805
11824
  }
@@ -11815,16 +11834,16 @@ class WebTerminalManager {
11815
11834
  tmuxArgs(["display-message", "-p", "-t", sessionName, "#{pane_current_command}"]),
11816
11835
  { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
11817
11836
  );
11818
- if (cmd.status !== 0 || typeof cmd.stdout !== "string") return void 0;
11837
+ if (cmd.status !== 0 || typeof cmd.stdout !== "string") return { agentState: void 0, claudeConfident: false };
11819
11838
  const cap = spawnSync(
11820
11839
  "tmux",
11821
11840
  tmuxArgs(["capture-pane", "-p", "-t", sessionName, "-S", "-40"]),
11822
11841
  { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
11823
11842
  );
11824
- if (cap.status !== 0 || typeof cap.stdout !== "string") return void 0;
11825
- return classifyPane(cmd.stdout.trim(), cap.stdout);
11843
+ if (cap.status !== 0 || typeof cap.stdout !== "string") return { agentState: void 0, claudeConfident: false };
11844
+ return { agentState: classifyPane(cmd.stdout.trim(), cap.stdout), claudeConfident: isClaudeConfident(cap.stdout) };
11826
11845
  } catch {
11827
- return void 0;
11846
+ return { agentState: void 0, claudeConfident: false };
11828
11847
  }
11829
11848
  }
11830
11849
  /**
@@ -14151,6 +14170,7 @@ class ApiMachineClient {
14151
14170
  setMirrorIntegration(integration) {
14152
14171
  this.webTerminal.setMirrorSessionResolver(integration.resolveMirrorSessionId);
14153
14172
  this.webTerminal.setOnTerminalClosed(integration.onTerminalClosed);
14173
+ this.webTerminal.setMirrorTickObserver(integration.onTerminalListTick);
14154
14174
  this.mirrorListObserver = integration.onTerminalList;
14155
14175
  this.mirrorInputAllowed = integration.isMirrorInputAllowed;
14156
14176
  }
@@ -15644,4 +15664,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
15644
15664
  }).passthrough()
15645
15665
  ]);
15646
15666
 
15647
- export { detectResumeSupport as $, ApiClient as A, deriveLocalCliUpdateSummary as B, updateSettings as C, encodeBase64Url as D, decodeBase64 as E, writeCredentialsLegacy as F, writeCredentialsDataKey as G, readCredentialsForConfiguredRelay as H, InvalidateSync as I, encrypt as J, isClaudeSessionId as K, getLocalHappyAgentCredentialPath as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, readLocalHappyAgentCredentials as N, decryptWithDataKey as O, decryptLegacy as P, extractCompleteLines as Q, RawJSONLinesSchema as R, decideBackfill as S, parseTerminalHookPayload as T, tmuxArgs as U, scrubTmuxClientEnv as V, decideMirrorBinding as W, persistSession as X, MIRROR_BACKFILL_LINES_DEFAULT as Y, mirrorLineKey as Z, mirrorLocalId as _, ApiSessionClient as a, detectCLIAvailability as a0, acquireDaemonLock as a1, summarizeSpawnSessionForLog as a2, deletePersistedSessions as a3, decrypt as a4, writeDaemonState as a5, fetchCliUpdateState as a6, resolveCliUpdateCheckInterval as a7, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as a8, releaseDaemonLock as a9, isValidSessionId as aa, listVhTerminals as ab, isValidTerminalId as ac, readVhTerminal as ad, sendToVhTerminal as ae, startOfflineReconnection as af, clearCredentials as ag, clearMachineId as ah, SandboxConfigSchema as ai, CodexAppServerClient as aj, getLatestDaemonLog as ak, persistence as al, api as am, createEnvelope as b, configuration as c, contentLogMetadata as d, errorLogMetadata as e, packageJson as f, delay as g, connectionState as h, readSettings as i, encodeBase64 as j, ensurePrivateDirectorySync as k, logger as l, hardenPrivateFileSync as m, readDaemonState as n, clearDaemonState as o, projectPath as p, getProjectPath as q, readPersistedSessions as r, initializeSandbox as s, wrapCommand as t, AsyncLock as u, ensurePrivateDirectory as v, writePrivateFileSync as w, hardenPrivateFile as x, readCredentials as y, credentialRelayProblem as z };
15667
+ export { detectResumeSupport as $, ApiClient as A, deriveLocalCliUpdateSummary as B, updateSettings as C, encodeBase64Url as D, decodeBase64 as E, writeCredentialsLegacy as F, writeCredentialsDataKey as G, readCredentialsForConfiguredRelay as H, InvalidateSync as I, encrypt as J, isClaudeSessionId as K, getLocalHappyAgentCredentialPath as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, readLocalHappyAgentCredentials as N, decryptWithDataKey as O, decryptLegacy as P, extractCompleteLines as Q, RawJSONLinesSchema as R, decideBackfill as S, parseTerminalHookPayload as T, tmuxArgs as U, scrubTmuxClientEnv as V, decideMirrorBinding as W, MIRROR_BACKFILL_LINES_DEFAULT as X, persistSession as Y, mirrorLineKey as Z, mirrorLocalId as _, ApiSessionClient as a, detectCLIAvailability as a0, acquireDaemonLock as a1, summarizeSpawnSessionForLog as a2, deletePersistedSessions as a3, decrypt as a4, writeDaemonState as a5, fetchCliUpdateState as a6, resolveCliUpdateCheckInterval as a7, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as a8, releaseDaemonLock as a9, isValidSessionId as aa, listVhTerminals as ab, isValidTerminalId as ac, readVhTerminal as ad, sendToVhTerminal as ae, startOfflineReconnection as af, clearCredentials as ag, clearMachineId as ah, SandboxConfigSchema as ai, CodexAppServerClient as aj, getLatestDaemonLog as ak, persistence as al, api as am, createEnvelope as b, configuration as c, contentLogMetadata as d, errorLogMetadata as e, packageJson as f, delay as g, connectionState as h, readSettings as i, encodeBase64 as j, ensurePrivateDirectorySync as k, logger as l, hardenPrivateFileSync as m, readDaemonState as n, clearDaemonState as o, projectPath as p, getProjectPath as q, readPersistedSessions as r, initializeSandbox as s, wrapCommand as t, AsyncLock as u, ensurePrivateDirectory as v, writePrivateFileSync as w, hardenPrivateFile as x, readCredentials as y, credentialRelayProblem as z };
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
51
51
  var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
52
52
 
53
53
  var name = "very-happy-cli";
54
- var version = "0.2.92";
54
+ var version = "0.2.94";
55
55
  var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
56
56
  var author = "Kirill Dubovitskiy";
57
57
  var contributors = [
@@ -164,12 +164,12 @@ var dependencies = {
164
164
  zod: "^4.0.0"
165
165
  };
166
166
  var optionalDependencies = {
167
- "very-happy-tools-arm64-darwin": "0.2.92",
168
- "very-happy-tools-x64-darwin": "0.2.92",
169
- "very-happy-tools-arm64-linux": "0.2.92",
170
- "very-happy-tools-x64-linux": "0.2.92",
171
- "very-happy-tools-arm64-win32": "0.2.92",
172
- "very-happy-tools-x64-win32": "0.2.92"
167
+ "very-happy-tools-arm64-darwin": "0.2.94",
168
+ "very-happy-tools-x64-darwin": "0.2.94",
169
+ "very-happy-tools-arm64-linux": "0.2.94",
170
+ "very-happy-tools-x64-linux": "0.2.94",
171
+ "very-happy-tools-arm64-win32": "0.2.94",
172
+ "very-happy-tools-x64-win32": "0.2.94"
173
173
  };
174
174
  var devDependencies = {
175
175
  "@slopus/happy-wire": "workspace:*",
@@ -1545,7 +1545,7 @@ class RpcHandlerManager {
1545
1545
  }
1546
1546
  }
1547
1547
 
1548
- const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-CjlB_fKT.cjs', document.baseURI).href))));
1548
+ const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-CcaZTXxx.cjs', document.baseURI).href))));
1549
1549
  function projectPath() {
1550
1550
  const path = path$1.resolve(__dirname$1, "..");
1551
1551
  return path;
@@ -10279,6 +10279,13 @@ function looksLikeClaudeCommand(currentCommand) {
10279
10279
  const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
10280
10280
  return cmd === "claude" || cmd === "node" || /^\d+\.\d+(\.\d+)?$/.test(cmd);
10281
10281
  }
10282
+ function isClaudeConfident(tail) {
10283
+ const lines = tail.replace(/\r/g, "").split("\n").map((l) => l.trimEnd());
10284
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
10285
+ const text = lines.join("\n");
10286
+ const last15 = lines.slice(-15).join("\n");
10287
+ return last15.includes("Do you want") || last15.includes("Would you like to proceed") || /^[\s│]*[❯>]\s*1\.\s/m.test(last15) || text.includes("esc to interrupt") || text.includes("? for shortcuts") || text.includes("bypass permissions on") || text.includes("\u23F5\u23F5");
10288
+ }
10282
10289
  function planScrollAction(paneInMode, alternateOn, paneWantsMouse, lines, claudeLike = false, paneRows = 24) {
10283
10290
  const count = Math.min(Math.abs(Math.trunc(lines)), 200);
10284
10291
  if (count === 0) return { kind: "none" };
@@ -10665,9 +10672,16 @@ class WebTerminalManager {
10665
10672
  /** terminalId → shadow session id (daemon's mirror manager). Feeds the
10666
10673
  * list push (toggle availability) and closed-terminal records. */
10667
10674
  mirrorResolver = null;
10675
+ mirrorTickObserver = null;
10668
10676
  /** Fired once per terminal whose close was just recorded — the mirror
10669
10677
  * manager archives + tears down that terminal's binding. */
10670
10678
  onTerminalClosedCb = null;
10679
+ /** Mirror reconcile (design v3): fired every list-track tick, BEFORE the
10680
+ * signature short-circuit, so a claude-running terminal whose binding was
10681
+ * lost self-heals even when nothing about the list changed. */
10682
+ setMirrorTickObserver(fn) {
10683
+ this.mirrorTickObserver = fn;
10684
+ }
10671
10685
  setMirrorSessionResolver(fn) {
10672
10686
  this.mirrorResolver = fn;
10673
10687
  this.kickListRefresh();
@@ -10850,6 +10864,7 @@ class WebTerminalManager {
10850
10864
  this.emitActivity(Object.fromEntries(
10851
10865
  list.filter((t) => t.activityAt).map((t) => [t.id, t.activityAt])
10852
10866
  ));
10867
+ this.mirrorTickObserver?.(list);
10853
10868
  const sig = terminalListSignature(list);
10854
10869
  if (sig === this.lastListSignature) return;
10855
10870
  this.lastListSignature = sig;
@@ -11494,7 +11509,7 @@ class WebTerminalManager {
11494
11509
  if (session.transport.kind !== "control") throw new Error("capture requires a control client");
11495
11510
  const { client } = session.transport;
11496
11511
  const batch = buildCaptureBatch({
11497
- paneTarget: `=${session.tmuxSession}:.0`,
11512
+ paneTarget: `=${session.tmuxSession}:`,
11498
11513
  historyLines: HEADLESS_SCROLLBACK,
11499
11514
  smallLines: SNAPSHOT_SCROLLBACK,
11500
11515
  cols: session.cols,
@@ -11589,7 +11604,7 @@ class WebTerminalManager {
11589
11604
  session.transport.pty.write(text);
11590
11605
  return;
11591
11606
  }
11592
- const target = session.paneId ?? `=${session.tmuxSession}:.0`;
11607
+ const target = session.paneId ?? `=${session.tmuxSession}:`;
11593
11608
  const { commands, dropped } = encodeTerminalWrite(text, target, { normalizeKeyNames: true });
11594
11609
  if (dropped) {
11595
11610
  logger.debug(`[WEB TERMINAL] dropped terminal auto-reply for ${terminalId}`);
@@ -11618,7 +11633,7 @@ class WebTerminalManager {
11618
11633
  session.transport.pty.write(text);
11619
11634
  return;
11620
11635
  }
11621
- const target = session.paneId ?? `=${session.tmuxSession}:.0`;
11636
+ const target = session.paneId ?? `=${session.tmuxSession}:`;
11622
11637
  const plan = buildPastePlan(text, target, { dir: pasteSpoolDir() });
11623
11638
  node_fs.writeFileSync(plan.path, plan.bytes, { mode: 384 });
11624
11639
  try {
@@ -11789,6 +11804,7 @@ class WebTerminalManager {
11789
11804
  } catch {
11790
11805
  }
11791
11806
  }
11807
+ const probe = this.probeAgentState(s.name, s.paneCurrentCommand);
11792
11808
  out.push({
11793
11809
  id,
11794
11810
  title,
@@ -11799,7 +11815,10 @@ class WebTerminalManager {
11799
11815
  // tmux last-activity (epoch s) → ms; optional so old daemons
11800
11816
  // simply omit it and web clients fall back to createdAt.
11801
11817
  activityAt: s.activity,
11802
- agentState: this.probeAgentState(s.name, s.paneCurrentCommand),
11818
+ agentState: probe.agentState,
11819
+ // Daemon-internal (not pushed to web / not in the signature):
11820
+ // the mirror reconciler's stricter "claude is really here" gate.
11821
+ claudeConfident: probe.claudeConfident,
11803
11822
  manual: s.manual
11804
11823
  });
11805
11824
  }
@@ -11821,7 +11840,7 @@ class WebTerminalManager {
11821
11840
  if (live) {
11822
11841
  try {
11823
11842
  const { command, tail } = live.agentProbeInput();
11824
- return classifyPane(command || polledCommand || "", tail);
11843
+ return { agentState: classifyPane(command || polledCommand || "", tail), claudeConfident: isClaudeConfident(tail) };
11825
11844
  } catch {
11826
11845
  }
11827
11846
  }
@@ -11837,16 +11856,16 @@ class WebTerminalManager {
11837
11856
  tmuxArgs(["display-message", "-p", "-t", sessionName, "#{pane_current_command}"]),
11838
11857
  { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
11839
11858
  );
11840
- if (cmd.status !== 0 || typeof cmd.stdout !== "string") return void 0;
11859
+ if (cmd.status !== 0 || typeof cmd.stdout !== "string") return { agentState: void 0, claudeConfident: false };
11841
11860
  const cap = node_child_process.spawnSync(
11842
11861
  "tmux",
11843
11862
  tmuxArgs(["capture-pane", "-p", "-t", sessionName, "-S", "-40"]),
11844
11863
  { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
11845
11864
  );
11846
- if (cap.status !== 0 || typeof cap.stdout !== "string") return void 0;
11847
- return classifyPane(cmd.stdout.trim(), cap.stdout);
11865
+ if (cap.status !== 0 || typeof cap.stdout !== "string") return { agentState: void 0, claudeConfident: false };
11866
+ return { agentState: classifyPane(cmd.stdout.trim(), cap.stdout), claudeConfident: isClaudeConfident(cap.stdout) };
11848
11867
  } catch {
11849
- return void 0;
11868
+ return { agentState: void 0, claudeConfident: false };
11850
11869
  }
11851
11870
  }
11852
11871
  /**
@@ -14173,6 +14192,7 @@ class ApiMachineClient {
14173
14192
  setMirrorIntegration(integration) {
14174
14193
  this.webTerminal.setMirrorSessionResolver(integration.resolveMirrorSessionId);
14175
14194
  this.webTerminal.setOnTerminalClosed(integration.onTerminalClosed);
14195
+ this.webTerminal.setMirrorTickObserver(integration.onTerminalListTick);
14176
14196
  this.mirrorListObserver = integration.onTerminalList;
14177
14197
  this.mirrorInputAllowed = integration.isMirrorInputAllowed;
14178
14198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "very-happy-cli",
3
- "version": "0.2.92",
3
+ "version": "0.2.94",
4
4
  "description": "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "contributors": [
@@ -113,12 +113,12 @@
113
113
  "zod": "^4.0.0"
114
114
  },
115
115
  "optionalDependencies": {
116
- "very-happy-tools-arm64-darwin": "0.2.92",
117
- "very-happy-tools-x64-darwin": "0.2.92",
118
- "very-happy-tools-arm64-linux": "0.2.92",
119
- "very-happy-tools-x64-linux": "0.2.92",
120
- "very-happy-tools-arm64-win32": "0.2.92",
121
- "very-happy-tools-x64-win32": "0.2.92"
116
+ "very-happy-tools-arm64-darwin": "0.2.94",
117
+ "very-happy-tools-x64-darwin": "0.2.94",
118
+ "very-happy-tools-arm64-linux": "0.2.94",
119
+ "very-happy-tools-x64-linux": "0.2.94",
120
+ "very-happy-tools-arm64-win32": "0.2.94",
121
+ "very-happy-tools-x64-win32": "0.2.94"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@slopus/happy-wire": "workspace:*",