very-happy-cli 0.2.119 → 0.2.121

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 (35) hide show
  1. package/dist/{AcpBackend-DpsLyp2y.mjs → AcpBackend-BxvQ6O4k.mjs} +1 -1
  2. package/dist/{AcpBackend-BpwOAcgu.cjs → AcpBackend-Cdj56ZfU.cjs} +1 -1
  3. package/dist/{AcpSessionManager-Ck6heffK.cjs → AcpSessionManager-DahCU78z.cjs} +51 -11
  4. package/dist/{AcpSessionManager-CM0PcgwO.mjs → AcpSessionManager-DlWlQVhw.mjs} +51 -11
  5. package/dist/{config-CtQJKe8h.cjs → config-DxOhdFEx.cjs} +2 -2
  6. package/dist/{config-DpE-aSHW.mjs → config-osO37dR1.mjs} +2 -2
  7. package/dist/{index-BITXLEZo.mjs → index-BcwAnM9k.mjs} +25 -11
  8. package/dist/{index-CK-yge8m.cjs → index-Bv53Zygv.cjs} +24 -10
  9. package/dist/{index-CRhOD3CQ.cjs → index-DObePs_K.cjs} +176 -54
  10. package/dist/{index-CGpkGfDj.mjs → index-DtZFY8aS.mjs} +2 -2
  11. package/dist/{index-DNXCQz_A.mjs → index-Iz-M3qbz.mjs} +172 -51
  12. package/dist/{index-DY7g3UWq.cjs → index-Vjo1jIJH.cjs} +2 -2
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-Dmr3JUJR.mjs → installTerminalHooks-Bq11vOqF.mjs} +2 -2
  16. package/dist/{installTerminalHooks-BOOg7NNs.cjs → installTerminalHooks-DehsXLmV.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.d.cts +25 -1
  19. package/dist/lib.d.mts +25 -1
  20. package/dist/lib.mjs +1 -1
  21. package/dist/{mcp-D4N7yn98.mjs → mcp-Bx1B3uny.mjs} +2 -2
  22. package/dist/{mcp-CxIk4Mes.cjs → mcp-Cpx9Qlch.cjs} +2 -2
  23. package/dist/{runGemini-Bexbg8pA.mjs → runGemini-CMgudbJs.mjs} +9 -5
  24. package/dist/{runGemini-TbGYRy0V.cjs → runGemini-DI8DipUx.cjs} +9 -5
  25. package/dist/{runOpenClaw-B-TV4EkO.cjs → runOpenClaw-CetdjTl-.cjs} +3 -3
  26. package/dist/{runOpenClaw-BpoIQWbe.mjs → runOpenClaw-DR3fLtVf.mjs} +3 -3
  27. package/dist/{send-U9UJvqNA.cjs → send-Cq0NZCn8.cjs} +2 -2
  28. package/dist/{send-z5SeDtKA.mjs → send-M4b_hD7t.mjs} +2 -2
  29. package/dist/{sessions-DNALvNe8.mjs → sessions-CelkWhqX.mjs} +11 -4
  30. package/dist/{sessions-BAZGALtl.cjs → sessions-DW4lrLOp.cjs} +11 -4
  31. package/dist/{spawn-CsV0SKkw.mjs → spawn-C4fF0q3Z.mjs} +2 -2
  32. package/dist/{spawn-dBxu-RAv.cjs → spawn-D3cwBlWI.cjs} +2 -2
  33. package/dist/{types-Bw_6p8NP.cjs → types-COOYpI8_.cjs} +176 -33
  34. package/dist/{types-DdyNuB7D.mjs → types-P2cJWD0E.mjs} +174 -33
  35. package/package.json +7 -7
@@ -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, k as setTerminalTitleViaDaemon, m as registerAssistantSessionTools } from './index-DNXCQz_A.mjs';
4
+ import { p as pushClipboardViaDaemon, k as setTerminalTitleViaDaemon, m as registerAssistantSessionTools } from './index-Iz-M3qbz.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-DdyNuB7D.mjs';
6
+ import { l as logger } from './types-P2cJWD0E.mjs';
7
7
  import 'node:fs/promises';
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-CRhOD3CQ.cjs');
6
+ var index = require('./index-DObePs_K.cjs');
7
7
  var limits = require('./limits-KdWKmwtH.cjs');
8
- var persistence = require('./types-Bw_6p8NP.cjs');
8
+ var persistence = require('./types-COOYpI8_.cjs');
9
9
  require('node:fs/promises');
10
10
  require('node:os');
11
11
  require('node:crypto');
@@ -2,10 +2,10 @@ import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { join } from 'node:path';
5
- import { l as logger, d as connectionState, A as ApiClient, e as readSettings, h as errorLogMetadata, f as encodeBase64, p as projectPath, g as contentLogMetadata } from './types-DdyNuB7D.mjs';
6
- import { I as GEMINI_API_KEY_ENV, J as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, C as BasePermissionHandler, K as BaseReasoningProcessor, n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as MessageQueue2, y as hashObject, L as MessageBuffer, N as CHANGE_TITLE_INSTRUCTION, B as registerKillSessionHandler, z as startHappyServer } from './index-DNXCQz_A.mjs';
7
- import { A as AcpBackend } from './AcpBackend-DpsLyp2y.mjs';
8
- import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-DpE-aSHW.mjs';
5
+ import { l as logger, d as connectionState, A as ApiClient, e as readSettings, h as errorLogMetadata, f as encodeBase64, p as projectPath, g as contentLogMetadata } from './types-P2cJWD0E.mjs';
6
+ import { J as GEMINI_API_KEY_ENV, K as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, E as BasePermissionHandler, L as BaseReasoningProcessor, n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, y as MessageQueue2, z as hashObject, N as MessageBuffer, O as CHANGE_TITLE_INSTRUCTION, C as registerKillSessionHandler, B as startHappyServer } from './index-Iz-M3qbz.mjs';
7
+ import { A as AcpBackend } from './AcpBackend-BxvQ6O4k.mjs';
8
+ import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-osO37dR1.mjs';
9
9
  import 'axios';
10
10
  import 'chalk';
11
11
  import 'node:util';
@@ -960,6 +960,10 @@ async function runGemini(opts) {
960
960
  permissionMode: mode.permissionMode,
961
961
  model: mode.model
962
962
  }));
963
+ messageQueue.setOnDiscard((entries) => {
964
+ const keys = entries.map((entry) => entry.sourceId).filter((key) => typeof key === "string");
965
+ if (keys.length > 0) session.sendQueueCancelReason(keys, entries[0].reason);
966
+ });
963
967
  const conversationHistory = new ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
964
968
  let currentPermissionMode = void 0;
965
969
  let currentModel = void 0;
@@ -1010,7 +1014,7 @@ async function runGemini(opts) {
1010
1014
  originalUserMessage
1011
1015
  // Store original message separately
1012
1016
  };
1013
- messageQueue.push(fullPrompt, mode);
1017
+ messageQueue.push(fullPrompt, mode, void 0, message.localKey);
1014
1018
  conversationHistory.addUserMessage(originalUserMessage);
1015
1019
  });
1016
1020
  let thinking = false;
@@ -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-Bw_6p8NP.cjs');
8
- var index = require('./index-CRhOD3CQ.cjs');
9
- var AcpBackend = require('./AcpBackend-BpwOAcgu.cjs');
10
- var config = require('./config-CtQJKe8h.cjs');
7
+ var persistence = require('./types-COOYpI8_.cjs');
8
+ var index = require('./index-DObePs_K.cjs');
9
+ var AcpBackend = require('./AcpBackend-Cdj56ZfU.cjs');
10
+ var config = require('./config-DxOhdFEx.cjs');
11
11
  require('axios');
12
12
  require('chalk');
13
13
  require('node:util');
@@ -962,6 +962,10 @@ async function runGemini(opts) {
962
962
  permissionMode: mode.permissionMode,
963
963
  model: mode.model
964
964
  }));
965
+ messageQueue.setOnDiscard((entries) => {
966
+ const keys = entries.map((entry) => entry.sourceId).filter((key) => typeof key === "string");
967
+ if (keys.length > 0) session.sendQueueCancelReason(keys, entries[0].reason);
968
+ });
965
969
  const conversationHistory = new ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
966
970
  let currentPermissionMode = void 0;
967
971
  let currentModel = void 0;
@@ -1012,7 +1016,7 @@ async function runGemini(opts) {
1012
1016
  originalUserMessage
1013
1017
  // Store original message separately
1014
1018
  };
1015
- messageQueue.push(fullPrompt, mode);
1019
+ messageQueue.push(fullPrompt, mode, void 0, message.localKey);
1016
1020
  conversationHistory.addUserMessage(originalUserMessage);
1017
1021
  });
1018
1022
  let thinking = false;
@@ -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-Bw_6p8NP.cjs');
9
- var AcpSessionManager = require('./AcpSessionManager-Ck6heffK.cjs');
10
- var index = require('./index-CRhOD3CQ.cjs');
8
+ var persistence = require('./types-COOYpI8_.cjs');
9
+ var AcpSessionManager = require('./AcpSessionManager-DahCU78z.cjs');
10
+ var index = require('./index-DObePs_K.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 { m as ensurePrivateDirectorySync, n as hardenPrivateFileSync, w as writePrivateFileSync, h as errorLogMetadata, g as contentLogMetadata, d as connectionState, A as ApiClient, e as readSettings, f as encodeBase64, l as logger, c as configuration } from './types-DdyNuB7D.mjs';
7
- import { A as AcpSessionManager } from './AcpSessionManager-CM0PcgwO.mjs';
8
- import { n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as MessageQueue2, B as registerKillSessionHandler } from './index-DNXCQz_A.mjs';
6
+ import { m as ensurePrivateDirectorySync, n as hardenPrivateFileSync, w as writePrivateFileSync, h as errorLogMetadata, g as contentLogMetadata, d as connectionState, A as ApiClient, e as readSettings, f as encodeBase64, l as logger, c as configuration } from './types-P2cJWD0E.mjs';
7
+ import { A as AcpSessionManager } from './AcpSessionManager-DlWlQVhw.mjs';
8
+ import { n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, y as MessageQueue2, C as registerKillSessionHandler } from './index-Iz-M3qbz.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-Bw_6p8NP.cjs');
7
- var index = require('./index-CRhOD3CQ.cjs');
6
+ var persistence = require('./types-COOYpI8_.cjs');
7
+ var index = require('./index-DObePs_K.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-DdyNuB7D.mjs';
5
- import { d as sendUserMessage, a as sessionWebUrl } from './index-DNXCQz_A.mjs';
4
+ import { r as readPersistedSessions, c as configuration } from './types-P2cJWD0E.mjs';
5
+ import { d as sendUserMessage, a as sessionWebUrl } from './index-Iz-M3qbz.mjs';
6
6
  import 'axios';
7
7
  import 'node:util';
8
8
  import 'node:os';
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
- import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listAccountSessions, e as listSessions, r as readSessionTranscript, f as stopSession, g as resolvePermissionRequest, h as archiveSession } from './index-DNXCQz_A.mjs';
3
- import { i as isValidSessionId } from './types-DdyNuB7D.mjs';
2
+ import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listAccountSessions, e as listSessions, r as readSessionTranscript, f as stopSession, g as resolvePermissionRequest, h as archiveSession } from './index-Iz-M3qbz.mjs';
3
+ import { i as isValidSessionId } from './types-P2cJWD0E.mjs';
4
4
  import 'node:os';
5
5
  import 'node:crypto';
6
6
  import 'cross-spawn';
@@ -286,14 +286,21 @@ async function handleSessionsCommand(args) {
286
286
  const verdict = options.action === "approve" ? { kind: "approve", forSession: options.forSession } : { kind: "deny", reason: options.reason };
287
287
  let result;
288
288
  try {
289
- result = await resolvePermissionRequest(sessionId, requestId, verdict);
289
+ result = await resolvePermissionRequest(sessionId, requestId, verdict, {
290
+ // T-014: a rate refusal waits (bounded) before re-sending;
291
+ // say so on stderr so the wait does not look like a hang.
292
+ onRateLimited: ({ attempt, waitMs, serverError }) => {
293
+ console.error(chalk.yellow("Rate limited:"), `${serverError} \u2014 waiting ${Math.ceil(waitMs / 1e3)}s before retry ${attempt}`);
294
+ }
295
+ });
290
296
  } catch (error) {
291
297
  if (options.json) console.log(JSON.stringify(permissionFailureRecord(sessionId, requestId, error)));
292
298
  throw error;
293
299
  }
294
300
  if (result.outcome.status !== "acknowledged") {
295
301
  if (options.json) console.log(JSON.stringify(result));
296
- console.error(chalk.red("Error:"), `${options.action} of ${requestId} on ${sessionId} was not delivered (${result.outcome.status}): ${result.outcome.message}`);
302
+ const hint = result.outcome.status === "rate-limited" ? ` \u2014 the request is still pending; re-run in ~${Math.ceil(result.outcome.retryAfterMs / 1e3)}s` : "";
303
+ console.error(chalk.red("Error:"), `${options.action} of ${requestId} on ${sessionId} was not delivered (${result.outcome.status}): ${result.outcome.message}${hint}`);
297
304
  process.exit(1);
298
305
  }
299
306
  if (options.json) {
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var index = require('./index-CRhOD3CQ.cjs');
5
- var persistence = require('./types-Bw_6p8NP.cjs');
4
+ var index = require('./index-DObePs_K.cjs');
5
+ var persistence = require('./types-COOYpI8_.cjs');
6
6
  require('node:os');
7
7
  require('node:crypto');
8
8
  require('cross-spawn');
@@ -288,14 +288,21 @@ async function handleSessionsCommand(args) {
288
288
  const verdict = options.action === "approve" ? { kind: "approve", forSession: options.forSession } : { kind: "deny", reason: options.reason };
289
289
  let result;
290
290
  try {
291
- result = await index.resolvePermissionRequest(sessionId, requestId, verdict);
291
+ result = await index.resolvePermissionRequest(sessionId, requestId, verdict, {
292
+ // T-014: a rate refusal waits (bounded) before re-sending;
293
+ // say so on stderr so the wait does not look like a hang.
294
+ onRateLimited: ({ attempt, waitMs, serverError }) => {
295
+ console.error(chalk.yellow("Rate limited:"), `${serverError} \u2014 waiting ${Math.ceil(waitMs / 1e3)}s before retry ${attempt}`);
296
+ }
297
+ });
292
298
  } catch (error) {
293
299
  if (options.json) console.log(JSON.stringify(permissionFailureRecord(sessionId, requestId, error)));
294
300
  throw error;
295
301
  }
296
302
  if (result.outcome.status !== "acknowledged") {
297
303
  if (options.json) console.log(JSON.stringify(result));
298
- console.error(chalk.red("Error:"), `${options.action} of ${requestId} on ${sessionId} was not delivered (${result.outcome.status}): ${result.outcome.message}`);
304
+ const hint = result.outcome.status === "rate-limited" ? ` \u2014 the request is still pending; re-run in ~${Math.ceil(result.outcome.retryAfterMs / 1e3)}s` : "";
305
+ console.error(chalk.red("Error:"), `${options.action} of ${requestId} on ${sessionId} was not delivered (${result.outcome.status}): ${result.outcome.message}${hint}`);
299
306
  process.exit(1);
300
307
  }
301
308
  if (options.json) {
@@ -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, i as isValidSpawnOrigin, b as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, S as SPAWN_AGENTS, w as waitForSessionKey, d as sendUserMessage } from './index-DNXCQz_A.mjs';
5
- import { l as logger } from './types-DdyNuB7D.mjs';
4
+ import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, i as isValidSpawnOrigin, b as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, S as SPAWN_AGENTS, w as waitForSessionKey, d as sendUserMessage } from './index-Iz-M3qbz.mjs';
5
+ import { l as logger } from './types-P2cJWD0E.mjs';
6
6
  import 'node:os';
7
7
  import 'node:crypto';
8
8
  import 'cross-spawn';
@@ -3,8 +3,8 @@
3
3
  var chalk = require('chalk');
4
4
  var node_fs = require('node:fs');
5
5
  var path = require('node:path');
6
- var index = require('./index-CRhOD3CQ.cjs');
7
- var persistence = require('./types-Bw_6p8NP.cjs');
6
+ var index = require('./index-DObePs_K.cjs');
7
+ var persistence = require('./types-COOYpI8_.cjs');
8
8
  require('node:os');
9
9
  require('node:crypto');
10
10
  require('cross-spawn');
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
51
51
  var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
52
52
 
53
53
  var name = "very-happy-cli";
54
- var version = "0.2.119";
54
+ var version = "0.2.121";
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.119",
168
- "very-happy-tools-x64-darwin": "0.2.119",
169
- "very-happy-tools-arm64-linux": "0.2.119",
170
- "very-happy-tools-x64-linux": "0.2.119",
171
- "very-happy-tools-arm64-win32": "0.2.119",
172
- "very-happy-tools-x64-win32": "0.2.119"
167
+ "very-happy-tools-arm64-darwin": "0.2.121",
168
+ "very-happy-tools-x64-darwin": "0.2.121",
169
+ "very-happy-tools-arm64-linux": "0.2.121",
170
+ "very-happy-tools-x64-linux": "0.2.121",
171
+ "very-happy-tools-arm64-win32": "0.2.121",
172
+ "very-happy-tools-x64-win32": "0.2.121"
173
173
  };
174
174
  var devDependencies = {
175
175
  "@slopus/happy-wire": "workspace:*",
@@ -742,6 +742,11 @@ const sessionProgressEventSchema = z__namespace.object({
742
742
  durationMs: z__namespace.number().optional(),
743
743
  summary: z__namespace.string().optional()
744
744
  });
745
+ const sessionQueueCancelEventSchema = z__namespace.object({
746
+ t: z__namespace.literal("queue-cancel"),
747
+ targetLocalKeys: z__namespace.array(z__namespace.string().min(1)).min(1),
748
+ reason: z__namespace.string().min(1).optional()
749
+ });
745
750
  const sessionEventSchema = z__namespace.discriminatedUnion("t", [
746
751
  sessionTextEventSchema,
747
752
  sessionServiceMessageEventSchema,
@@ -752,7 +757,8 @@ const sessionEventSchema = z__namespace.discriminatedUnion("t", [
752
757
  sessionStartEventSchema,
753
758
  sessionTurnEndEventSchema,
754
759
  sessionStopEventSchema,
755
- sessionProgressEventSchema
760
+ sessionProgressEventSchema,
761
+ sessionQueueCancelEventSchema
756
762
  ]);
757
763
  const sessionEnvelopeSchema = z__namespace.object({
758
764
  id: z__namespace.string(),
@@ -1685,7 +1691,7 @@ class RpcHandlerManager {
1685
1691
  }
1686
1692
  }
1687
1693
 
1688
- 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-Bw_6p8NP.cjs', document.baseURI).href))));
1694
+ 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-COOYpI8_.cjs', document.baseURI).href))));
1689
1695
  function projectPath() {
1690
1696
  const path = path$1.resolve(__dirname$1, "..");
1691
1697
  return path;
@@ -3135,6 +3141,48 @@ class InvalidateSync {
3135
3141
  };
3136
3142
  }
3137
3143
 
3144
+ function sessionEnvelopeOf(body) {
3145
+ if (!body || typeof body !== "object") return null;
3146
+ const record = body;
3147
+ if (record.role !== "session" || !record.content || typeof record.content !== "object") return null;
3148
+ const content = record.content;
3149
+ if (content.type === "session" && content.data && typeof content.data === "object") {
3150
+ return content.data;
3151
+ }
3152
+ if (content.ev !== void 0) return content;
3153
+ return null;
3154
+ }
3155
+ function isQueuedUserText(body) {
3156
+ if (!body || typeof body !== "object") return false;
3157
+ const record = body;
3158
+ return record.role === "user" && record.content?.type === "text" && typeof record.meta?.queuedAt === "number";
3159
+ }
3160
+ function scanUndeliveredQueuedInputs(records, prior) {
3161
+ const canceled = prior?.canceled ?? /* @__PURE__ */ new Set();
3162
+ const undelivered = [];
3163
+ let reachedBoundary = false;
3164
+ for (const record of records) {
3165
+ const envelope = sessionEnvelopeOf(record.body);
3166
+ if (envelope) {
3167
+ const t = envelope.ev?.t;
3168
+ if (t === "turn-end") {
3169
+ reachedBoundary = true;
3170
+ break;
3171
+ }
3172
+ if (t === "queue-cancel" && Array.isArray(envelope.ev?.targetLocalKeys)) {
3173
+ for (const key of envelope.ev.targetLocalKeys) {
3174
+ if (typeof key === "string") canceled.add(key);
3175
+ }
3176
+ }
3177
+ continue;
3178
+ }
3179
+ if (isQueuedUserText(record.body) && record.localId && !canceled.has(record.localId)) {
3180
+ undelivered.push(record.localId);
3181
+ }
3182
+ }
3183
+ return { undeliveredLocalKeys: undelivered, reachedBoundary, canceled };
3184
+ }
3185
+
3138
3186
  function record(value) {
3139
3187
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
3140
3188
  }
@@ -3778,8 +3826,75 @@ class ApiSessionClient extends node_events.EventEmitter {
3778
3826
  this.sendSync.invalidate();
3779
3827
  }
3780
3828
  }
3829
+ /** B-332: emit a queue-cancel tombstone, mirroring the web's own signal. */
3830
+ sendQueueCancelReason(localKeys, reason) {
3831
+ if (localKeys.length === 0) return;
3832
+ const envelope = createEnvelope("user", { t: "queue-cancel", targetLocalKeys: localKeys, reason });
3833
+ this.sendSessionProtocolMessage(envelope);
3834
+ }
3781
3835
  /**
3782
- * Send message to session
3836
+ * B-332 site ③: find queued user input this wrapper destroyed (it was still
3837
+ * in the previous wrapper's in-memory queue when it died) and tell the web.
3838
+ *
3839
+ * Walks NEWEST → OLDEST via `before_seq`, decrypting each page, and stops at
3840
+ * the first turn-end. The runner calls this on EVERY reconnect (runClaude /
3841
+ * runCodex), seeded or not: a cursor seeded at the server's latest seq skips
3842
+ * the undelivered tail just as thoroughly as skipExistingMessages does —
3843
+ * and the daemon's resume/restart always seeds, so gating on the skip path
3844
+ * would miss the common case. Best-effort: a failed scan must not take a
3845
+ * session down. Needs no socket; POSTs through the same session-message
3846
+ * channel so the web receives it identically to a cancel-button tombstone.
3847
+ */
3848
+ async cancelUndeliveredQueuedInputs() {
3849
+ const MAX_SCAN_PAGES = 10;
3850
+ const canceled = /* @__PURE__ */ new Set();
3851
+ const undelivered = [];
3852
+ let beforeSeq = 2147483647;
3853
+ let reachedBoundary = false;
3854
+ let pages = 0;
3855
+ try {
3856
+ while (!reachedBoundary && pages < MAX_SCAN_PAGES) {
3857
+ pages += 1;
3858
+ const response = await axios.get(
3859
+ `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
3860
+ {
3861
+ params: { before_seq: beforeSeq, limit: 100 },
3862
+ headers: this.authHeaders(),
3863
+ timeout: 6e4
3864
+ }
3865
+ );
3866
+ const messages = Array.isArray(response.data.messages) ? response.data.messages : [];
3867
+ if (messages.length === 0) break;
3868
+ const records = messages.map((message) => {
3869
+ let body = null;
3870
+ try {
3871
+ body = message.content?.t === "encrypted" ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(message.content.c)) : null;
3872
+ } catch (error) {
3873
+ logger.debug("[API] Failed to decrypt during undelivered scan:", error);
3874
+ }
3875
+ return { seq: message.seq, localId: message.localId, body };
3876
+ });
3877
+ const scan = scanUndeliveredQueuedInputs(records, { canceled });
3878
+ for (const key of scan.canceled) canceled.add(key);
3879
+ undelivered.push(...scan.undeliveredLocalKeys);
3880
+ reachedBoundary = scan.reachedBoundary;
3881
+ const oldest = records.at(-1)?.seq;
3882
+ if (oldest === void 0 || messages.length < 100) break;
3883
+ beforeSeq = oldest;
3884
+ }
3885
+ } catch (error) {
3886
+ logger.debug("[API] cancelUndeliveredQueuedInputs scan failed:", error);
3887
+ return;
3888
+ }
3889
+ if (!reachedBoundary) {
3890
+ logger.debug("[API] cancelUndeliveredQueuedInputs found no turn boundary; leaving messages as-is");
3891
+ return;
3892
+ }
3893
+ undelivered.reverse();
3894
+ this.sendQueueCancelReason(undelivered, "restarted");
3895
+ }
3896
+ /**
3897
+ * Send a message to session
3783
3898
  * @param body - Message body (can be MessageContent or raw content for agent messages)
3784
3899
  */
3785
3900
  sendClaudeSessionMessage(body, opts) {
@@ -4472,34 +4587,44 @@ function migrateSettings(raw, fromVersion) {
4472
4587
  let migrated = { ...raw };
4473
4588
  return migrated;
4474
4589
  }
4475
- async function readSettings() {
4590
+ async function readSettingsOutcome() {
4476
4591
  if (!node_fs.existsSync(configuration.settingsFile)) {
4477
- return { ...defaultSettings };
4592
+ return { kind: "absent" };
4478
4593
  }
4479
4594
  try {
4480
- const content = await promises.readFile(configuration.settingsFile, "utf8");
4481
- const raw = JSON.parse(content);
4482
- const schemaVersion = raw.schemaVersion ?? 1;
4483
- if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
4484
- logger.warn(
4485
- `\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
4486
- );
4487
- }
4488
- const migrated = migrateSettings(raw, schemaVersion);
4489
- if (migrated.sandboxConfig !== void 0) {
4490
- try {
4491
- migrated.sandboxConfig = SandboxConfigSchema.parse(migrated.sandboxConfig);
4492
- } catch (error) {
4493
- logger.warn(`\u26A0\uFE0F Invalid sandbox config - skipping. Error: ${error.message}`);
4494
- migrated.sandboxConfig = void 0;
4495
- }
4496
- }
4497
- return { ...defaultSettings, ...migrated };
4595
+ return { kind: "ok", settings: await parseSettingsFile() };
4498
4596
  } catch (error) {
4499
- logger.warn(`Failed to read settings: ${error.message}`);
4500
- return { ...defaultSettings };
4597
+ return { kind: "unreadable", error: error?.message ?? String(error) };
4501
4598
  }
4502
4599
  }
4600
+ async function readSettings() {
4601
+ const outcome = await readSettingsOutcome();
4602
+ if (outcome.kind === "ok") return outcome.settings;
4603
+ if (outcome.kind === "unreadable") {
4604
+ logger.warn(`Failed to read settings: ${outcome.error}`);
4605
+ }
4606
+ return { ...defaultSettings };
4607
+ }
4608
+ async function parseSettingsFile() {
4609
+ const content = await promises.readFile(configuration.settingsFile, "utf8");
4610
+ const raw = JSON.parse(content);
4611
+ const schemaVersion = raw.schemaVersion ?? 1;
4612
+ if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
4613
+ logger.warn(
4614
+ `\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
4615
+ );
4616
+ }
4617
+ const migrated = migrateSettings(raw);
4618
+ if (migrated.sandboxConfig !== void 0) {
4619
+ try {
4620
+ migrated.sandboxConfig = SandboxConfigSchema.parse(migrated.sandboxConfig);
4621
+ } catch (error) {
4622
+ logger.warn(`\u26A0\uFE0F Invalid sandbox config - skipping. Error: ${error.message}`);
4623
+ migrated.sandboxConfig = void 0;
4624
+ }
4625
+ }
4626
+ return { ...defaultSettings, ...migrated };
4627
+ }
4503
4628
  async function writeSettings(settings) {
4504
4629
  await ensurePrivateDirectory(configuration.happyHomeDir);
4505
4630
  const settingsWithVersion = {
@@ -4541,7 +4666,13 @@ async function updateSettings(updater) {
4541
4666
  throw new Error(`Failed to acquire settings lock after ${MAX_LOCK_ATTEMPTS * LOCK_RETRY_INTERVAL_MS / 1e3} seconds`);
4542
4667
  }
4543
4668
  try {
4544
- const current = await readSettings() || { ...defaultSettings };
4669
+ const outcome = await readSettingsOutcome();
4670
+ if (outcome.kind === "unreadable") {
4671
+ throw new Error(
4672
+ `Settings file ${configuration.settingsFile} exists but could not be read (${outcome.error}). Refusing to overwrite it \u2014 fix or remove the file (a removed file is recreated with defaults).`
4673
+ );
4674
+ }
4675
+ const current = outcome.kind === "ok" ? outcome.settings : { ...defaultSettings };
4545
4676
  const updated = await updater(current);
4546
4677
  await ensurePrivateDirectory(configuration.happyHomeDir);
4547
4678
  await writePrivateFile(tmpFile, JSON.stringify(updated, null, 2));
@@ -4788,6 +4919,7 @@ var persistence = /*#__PURE__*/Object.freeze({
4788
4919
  readDaemonState: readDaemonState,
4789
4920
  readPersistedSessions: readPersistedSessions,
4790
4921
  readSettings: readSettings,
4922
+ readSettingsOutcome: readSettingsOutcome,
4791
4923
  readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
4792
4924
  releaseDaemonLock: releaseDaemonLock,
4793
4925
  updateSettings: updateSettings,
@@ -14535,6 +14667,7 @@ async function withCodexAppServerClient(handler) {
14535
14667
  await client.disconnect();
14536
14668
  }
14537
14669
  }
14670
+ const CLI_AVAILABILITY_RECHECK_MS = 5 * 60 * 1e3;
14538
14671
  class ApiMachineClient {
14539
14672
  constructor(token, machine) {
14540
14673
  this.token = token;
@@ -14558,6 +14691,10 @@ class ApiMachineClient {
14558
14691
  keepAliveInterval = null;
14559
14692
  lastKnownCLIAvailability = null;
14560
14693
  lastKnownResumeSupport = null;
14694
+ /** Wall-clock of the last CLI availability probe. Initialised as "already
14695
+ * stale" so the FIRST keep-alive tick always probes and populates the
14696
+ * machine metadata that a connecting/reconnecting web client reads. */
14697
+ lastCliProbeAt = Date.now() - CLI_AVAILABILITY_RECHECK_MS;
14561
14698
  cliUpdateState = null;
14562
14699
  cliUpdatePushChain = Promise.resolve();
14563
14700
  claudeAuthState = null;
@@ -15425,6 +15562,10 @@ class ApiMachineClient {
15425
15562
  }
15426
15563
  this.socket.emit("machine-alive", payload);
15427
15564
  void this.refreshRelayConnection();
15565
+ if (Date.now() - this.lastCliProbeAt < CLI_AVAILABILITY_RECHECK_MS) {
15566
+ return;
15567
+ }
15568
+ this.lastCliProbeAt = Date.now();
15428
15569
  const newAvailability = detectCLIAvailability();
15429
15570
  const prev = this.lastKnownCLIAvailability;
15430
15571
  const newResumeSupport = detectResumeSupport();
@@ -16720,6 +16861,7 @@ exports.readDaemonState = readDaemonState;
16720
16861
  exports.readLocalHappyAgentCredentials = readLocalHappyAgentCredentials;
16721
16862
  exports.readPersistedSessions = readPersistedSessions;
16722
16863
  exports.readSettings = readSettings;
16864
+ exports.readSettingsOutcome = readSettingsOutcome;
16723
16865
  exports.readVhTerminal = readVhTerminal;
16724
16866
  exports.releaseDaemonLock = releaseDaemonLock;
16725
16867
  exports.resolveCliUpdateCheckInterval = resolveCliUpdateCheckInterval;
@@ -16727,6 +16869,7 @@ exports.scrubTmuxClientEnv = scrubTmuxClientEnv;
16727
16869
  exports.sendToVhTerminal = sendToVhTerminal;
16728
16870
  exports.shellescape = shellescape;
16729
16871
  exports.startOfflineReconnection = startOfflineReconnection;
16872
+ exports.streamKeyOf = streamKeyOf;
16730
16873
  exports.summarizeSpawnSessionForLog = summarizeSpawnSessionForLog;
16731
16874
  exports.tmuxArgs = tmuxArgs;
16732
16875
  exports.updateSettings = updateSettings;