very-happy-cli 0.2.137 → 0.2.138

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 (43) hide show
  1. package/dist/{AcpBackend-Bcmm51Ya.cjs → AcpBackend-BUoDXZnv.cjs} +2 -2
  2. package/dist/{AcpBackend-POcimRNQ.mjs → AcpBackend-bqVGMkPh.mjs} +2 -2
  3. package/dist/{AcpSessionManager-D54Juu2-.mjs → AcpSessionManager-BBAQ8EQf.mjs} +1 -1
  4. package/dist/{AcpSessionManager-CjrwSZbh.cjs → AcpSessionManager-DmmA52Ep.cjs} +1 -1
  5. package/dist/{agentGuidance-D1tKg2UW.cjs → agentGuidance-4HV-BOge.cjs} +1 -1
  6. package/dist/{agentGuidance-DulugsMk.mjs → agentGuidance-WHZsBKpm.mjs} +1 -1
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-C1gQprK_.mjs → config-BRXqgJJj.mjs} +4 -4
  10. package/dist/{config-Dr3A4IGp.cjs → config-DpuZN2Rs.cjs} +4 -4
  11. package/dist/{index-C3JqT3Ql.cjs → index-3N-qu-RG.cjs} +6 -6
  12. package/dist/{index-hGg5G5uO.cjs → index-CHP265gs.cjs} +4 -4
  13. package/dist/{index-B0KIJdSt.cjs → index-CRRK5P1r.cjs} +115 -23
  14. package/dist/{index-C3agc8KT.mjs → index-D8Q3OhdR.mjs} +4 -4
  15. package/dist/{index-B11rFE7f.mjs → index-DQe7ljLR.mjs} +7 -7
  16. package/dist/{index-D5MRxCHs.mjs → index-Dbl5U_N0.mjs} +111 -19
  17. package/dist/index.cjs +4 -4
  18. package/dist/index.mjs +4 -4
  19. package/dist/{installTerminalHooks-Culr259-.cjs → installTerminalHooks-CWGkoWDD.cjs} +4 -4
  20. package/dist/{installTerminalHooks-DfZTXVWO.mjs → installTerminalHooks-DfUGgeyb.mjs} +4 -4
  21. package/dist/lib.cjs +2 -2
  22. package/dist/lib.mjs +2 -2
  23. package/dist/{limits-CJrrAhB4.cjs → limits-CfzVummd.cjs} +7 -7
  24. package/dist/{limits-CAD4UF8w.mjs → limits-DEsg_gHi.mjs} +7 -7
  25. package/dist/{mcp-5Jf6V4eb.cjs → mcp-CN2UVfut.cjs} +4 -4
  26. package/dist/{mcp-CdnZbYKV.mjs → mcp-UnjB3I8_.mjs} +4 -4
  27. package/dist/{runGemini-GjBPzXEt.cjs → runGemini-BUVjTJKn.cjs} +6 -6
  28. package/dist/{runGemini-CwWEorS7.mjs → runGemini-BZ0xXRj-.mjs} +6 -6
  29. package/dist/{runOpenClaw-BZHHy9T7.mjs → runOpenClaw-BcUmnBpW.mjs} +5 -5
  30. package/dist/{runOpenClaw-DGe5c571.cjs → runOpenClaw-Bj2EgRHB.cjs} +5 -5
  31. package/dist/{send-teYpfTtw.cjs → send-BkypdBpK.cjs} +4 -4
  32. package/dist/{send-BN2PncAH.mjs → send-snK_NGAA.mjs} +4 -4
  33. package/dist/{sessions-qP1YUf5E.cjs → sessions-9A-uoJL_.cjs} +4 -4
  34. package/dist/{sessions-TLUfYdLX.mjs → sessions-DxlC4C4g.mjs} +4 -4
  35. package/dist/{spawn-GekaZ7h2.cjs → spawn-BORLpj60.cjs} +4 -4
  36. package/dist/{spawn-D3P8wJ_r.mjs → spawn-DFfHCafP.mjs} +4 -4
  37. package/dist/{teams-BSqAW2S5.cjs → teams-9x2FO9RW.cjs} +4 -4
  38. package/dist/{teams-WSjEXh4d.mjs → teams-Cfr-2Yd-.mjs} +4 -4
  39. package/dist/{todo-BblDgT77.mjs → todo-CU5cjQZU.mjs} +3 -3
  40. package/dist/{todo-DsnXWl77.cjs → todo-DydKcknX.cjs} +3 -3
  41. package/dist/{types-BAhQF6Xp.cjs → types-CRxf_3n8.cjs} +60 -2
  42. package/dist/{types-B8g_V-LQ.mjs → types-D5EsIAOe.mjs} +59 -1
  43. package/package.json +7 -7
@@ -3,15 +3,15 @@
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('./limits-CJrrAhB4.cjs');
7
- var index = require('./index-B0KIJdSt.cjs');
6
+ var persistence = require('./limits-CfzVummd.cjs');
7
+ var index = require('./index-CRRK5P1r.cjs');
8
8
  require('node:fs/promises');
9
9
  require('node:os');
10
10
  require('zod');
11
11
  require('node:crypto');
12
12
  require('tweetnacl');
13
13
  require('node:util');
14
- require('./types-BAhQF6Xp.cjs');
14
+ require('./types-CRxf_3n8.cjs');
15
15
  require('axios');
16
16
  require('node:events');
17
17
  require('socket.io-client');
@@ -30,7 +30,7 @@ require('fs');
30
30
  require('node:readline');
31
31
  require('@anthropic-ai/sandbox-runtime');
32
32
  require('expo-server-sdk');
33
- require('./agentGuidance-D1tKg2UW.cjs');
33
+ require('./agentGuidance-4HV-BOge.cjs');
34
34
  require('ink');
35
35
  require('react');
36
36
  require('@anthropic-ai/claude-agent-sdk');
@@ -1,15 +1,15 @@
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 './limits-CAD4UF8w.mjs';
5
- import { d as sendUserMessage, a as sessionWebUrl } from './index-D5MRxCHs.mjs';
4
+ import { r as readPersistedSessions, c as configuration } from './limits-DEsg_gHi.mjs';
5
+ import { d as sendUserMessage, a as sessionWebUrl } from './index-Dbl5U_N0.mjs';
6
6
  import 'node:fs/promises';
7
7
  import 'node:os';
8
8
  import 'zod';
9
9
  import 'node:crypto';
10
10
  import 'tweetnacl';
11
11
  import 'node:util';
12
- import './types-B8g_V-LQ.mjs';
12
+ import './types-D5EsIAOe.mjs';
13
13
  import 'axios';
14
14
  import 'node:events';
15
15
  import 'socket.io-client';
@@ -28,7 +28,7 @@ import 'fs';
28
28
  import 'node:readline';
29
29
  import '@anthropic-ai/sandbox-runtime';
30
30
  import 'expo-server-sdk';
31
- import './agentGuidance-DulugsMk.mjs';
31
+ import './agentGuidance-WHZsBKpm.mjs';
32
32
  import 'ink';
33
33
  import 'react';
34
34
  import '@anthropic-ai/claude-agent-sdk';
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var index = require('./index-B0KIJdSt.cjs');
5
- var api = require('./types-BAhQF6Xp.cjs');
4
+ var index = require('./index-CRRK5P1r.cjs');
5
+ var api = require('./types-CRxf_3n8.cjs');
6
6
  require('node:os');
7
7
  require('node:crypto');
8
- require('./limits-CJrrAhB4.cjs');
8
+ require('./limits-CfzVummd.cjs');
9
9
  require('node:fs/promises');
10
10
  require('node:fs');
11
11
  require('node:path');
@@ -14,7 +14,7 @@ require('tweetnacl');
14
14
  require('node:util');
15
15
  require('cross-spawn');
16
16
  require('node:readline');
17
- require('./agentGuidance-D1tKg2UW.cjs');
17
+ require('./agentGuidance-4HV-BOge.cjs');
18
18
  require('fs/promises');
19
19
  require('ink');
20
20
  require('react');
@@ -1,9 +1,9 @@
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-D5MRxCHs.mjs';
3
- import { i as isValidSessionId } from './types-B8g_V-LQ.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-Dbl5U_N0.mjs';
3
+ import { i as isValidSessionId } from './types-D5EsIAOe.mjs';
4
4
  import 'node:os';
5
5
  import 'node:crypto';
6
- import './limits-CAD4UF8w.mjs';
6
+ import './limits-DEsg_gHi.mjs';
7
7
  import 'node:fs/promises';
8
8
  import 'node:fs';
9
9
  import 'node:path';
@@ -12,7 +12,7 @@ import 'tweetnacl';
12
12
  import 'node:util';
13
13
  import 'cross-spawn';
14
14
  import 'node:readline';
15
- import './agentGuidance-DulugsMk.mjs';
15
+ import './agentGuidance-WHZsBKpm.mjs';
16
16
  import 'fs/promises';
17
17
  import 'ink';
18
18
  import 'react';
@@ -3,11 +3,11 @@
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-B0KIJdSt.cjs');
7
- var persistence = require('./limits-CJrrAhB4.cjs');
6
+ var index = require('./index-CRRK5P1r.cjs');
7
+ var persistence = require('./limits-CfzVummd.cjs');
8
8
  require('node:os');
9
9
  require('node:crypto');
10
- require('./types-BAhQF6Xp.cjs');
10
+ require('./types-CRxf_3n8.cjs');
11
11
  require('axios');
12
12
  require('node:events');
13
13
  require('socket.io-client');
@@ -29,7 +29,7 @@ require('tweetnacl');
29
29
  require('node:readline');
30
30
  require('@anthropic-ai/sandbox-runtime');
31
31
  require('expo-server-sdk');
32
- require('./agentGuidance-D1tKg2UW.cjs');
32
+ require('./agentGuidance-4HV-BOge.cjs');
33
33
  require('ink');
34
34
  require('react');
35
35
  require('@anthropic-ai/claude-agent-sdk');
@@ -1,11 +1,11 @@
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-D5MRxCHs.mjs';
5
- import { l as logger } from './limits-CAD4UF8w.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-Dbl5U_N0.mjs';
5
+ import { l as logger } from './limits-DEsg_gHi.mjs';
6
6
  import 'node:os';
7
7
  import 'node:crypto';
8
- import './types-B8g_V-LQ.mjs';
8
+ import './types-D5EsIAOe.mjs';
9
9
  import 'axios';
10
10
  import 'node:events';
11
11
  import 'socket.io-client';
@@ -27,7 +27,7 @@ import 'tweetnacl';
27
27
  import 'node:readline';
28
28
  import '@anthropic-ai/sandbox-runtime';
29
29
  import 'expo-server-sdk';
30
- import './agentGuidance-DulugsMk.mjs';
30
+ import './agentGuidance-WHZsBKpm.mjs';
31
31
  import 'ink';
32
32
  import 'react';
33
33
  import '@anthropic-ai/claude-agent-sdk';
@@ -4,15 +4,15 @@ var os = require('node:os');
4
4
  var promises = require('node:fs/promises');
5
5
  var z = require('zod');
6
6
  var node_crypto = require('node:crypto');
7
- var agentGuidance = require('./agentGuidance-D1tKg2UW.cjs');
8
- var persistence = require('./limits-CJrrAhB4.cjs');
7
+ var agentGuidance = require('./agentGuidance-4HV-BOge.cjs');
8
+ var persistence = require('./limits-CfzVummd.cjs');
9
9
  var path = require('node:path');
10
- var index = require('./index-B0KIJdSt.cjs');
10
+ var index = require('./index-CRRK5P1r.cjs');
11
11
  require('node:fs');
12
12
  require('tweetnacl');
13
13
  require('chalk');
14
14
  require('node:util');
15
- require('./types-BAhQF6Xp.cjs');
15
+ require('./types-CRxf_3n8.cjs');
16
16
  require('axios');
17
17
  require('node:events');
18
18
  require('socket.io-client');
@@ -2,15 +2,15 @@ import { homedir } from 'node:os';
2
2
  import { unlink, rmdir, mkdir, lstat, readFile, writeFile, rename } from 'node:fs/promises';
3
3
  import { z } from 'zod';
4
4
  import { createHash, randomUUID } from 'node:crypto';
5
- import { d as teamRequest, e as createTeamsClient } from './agentGuidance-DulugsMk.mjs';
6
- import { d as readCredentialsForConfiguredRelay } from './limits-CAD4UF8w.mjs';
5
+ import { d as teamRequest, e as createTeamsClient } from './agentGuidance-WHZsBKpm.mjs';
6
+ import { d as readCredentialsForConfiguredRelay } from './limits-DEsg_gHi.mjs';
7
7
  import { join, resolve, dirname } from 'node:path';
8
- import { T as TEAM_SKILL } from './index-D5MRxCHs.mjs';
8
+ import { T as TEAM_SKILL } from './index-Dbl5U_N0.mjs';
9
9
  import 'node:fs';
10
10
  import 'tweetnacl';
11
11
  import 'chalk';
12
12
  import 'node:util';
13
- import './types-B8g_V-LQ.mjs';
13
+ import './types-D5EsIAOe.mjs';
14
14
  import 'axios';
15
15
  import 'node:events';
16
16
  import 'socket.io-client';
@@ -1,6 +1,6 @@
1
- import { d as readCredentialsForConfiguredRelay, c as configuration } from './limits-CAD4UF8w.mjs';
2
- import { b as createBuiltinTodoClient, B as BUILTIN_TODO_SKILL } from './types-B8g_V-LQ.mjs';
3
- import { c as readTeamScope, t as teamScopePath } from './agentGuidance-DulugsMk.mjs';
1
+ import { d as readCredentialsForConfiguredRelay, c as configuration } from './limits-DEsg_gHi.mjs';
2
+ import { b as createBuiltinTodoClient, B as BUILTIN_TODO_SKILL } from './types-D5EsIAOe.mjs';
3
+ import { c as readTeamScope, t as teamScopePath } from './agentGuidance-WHZsBKpm.mjs';
4
4
  import 'node:fs/promises';
5
5
  import 'node:fs';
6
6
  import 'node:os';
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var persistence = require('./limits-CJrrAhB4.cjs');
4
- var api = require('./types-BAhQF6Xp.cjs');
5
- var agentGuidance = require('./agentGuidance-D1tKg2UW.cjs');
3
+ var persistence = require('./limits-CfzVummd.cjs');
4
+ var api = require('./types-CRxf_3n8.cjs');
5
+ var agentGuidance = require('./agentGuidance-4HV-BOge.cjs');
6
6
  require('node:fs/promises');
7
7
  require('node:fs');
8
8
  require('node:os');
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var axios = require('axios');
4
- var persistence = require('./limits-CJrrAhB4.cjs');
4
+ var persistence = require('./limits-CfzVummd.cjs');
5
5
  var node_events = require('node:events');
6
6
  var socket_ioClient = require('socket.io-client');
7
7
  var z = require('zod');
@@ -1369,7 +1369,7 @@ class RpcHandlerManager {
1369
1369
  }
1370
1370
  }
1371
1371
 
1372
- const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-BAhQF6Xp.cjs', document.baseURI).href))));
1372
+ const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-CRxf_3n8.cjs', document.baseURI).href))));
1373
1373
  function projectPath() {
1374
1374
  const path$1 = path.resolve(__dirname$1, "..");
1375
1375
  return path$1;
@@ -12980,6 +12980,9 @@ class CodexAppServerClient {
12980
12980
  // Tracks in-flight interruptTurn() RPCs so sendTurnAndWait can wait for them
12981
12981
  // before starting a new turn (prevents stale turn/interrupt from aborting the next turn).
12982
12982
  pendingInterrupt = null;
12983
+ // In-flight idle release (B-461); ensureConnected() waits for it so a message
12984
+ // arriving mid-release cannot spawn a second app-server against a dying one.
12985
+ idleRelease = null;
12983
12986
  notificationProtocol = "unknown";
12984
12987
  completedTurnIds = /* @__PURE__ */ new Set();
12985
12988
  rawFileChangesByItemId = /* @__PURE__ */ new Map();
@@ -13313,6 +13316,53 @@ class CodexAppServerClient {
13313
13316
  async disconnect() {
13314
13317
  await this.disconnectInternal();
13315
13318
  }
13319
+ /** True while an app-server child process is owned by this client. */
13320
+ get hasProcess() {
13321
+ return this.process !== null;
13322
+ }
13323
+ /**
13324
+ * Drop the app-server process while the session is idle, keeping the thread
13325
+ * id and defaults so the next turn can `thread/resume` it (B-461). Refuses —
13326
+ * returning false — whenever anything is still in flight: an active turn, a
13327
+ * pending interrupt, or any outstanding RPC. See codexIdleRelease.ts.
13328
+ */
13329
+ async releaseIdleProcess() {
13330
+ if (this.idleRelease) return this.idleRelease;
13331
+ if (!this.connected && !this.process) return false;
13332
+ if (this.pendingTurnCompletion || this.pendingInterrupt || this.pending.size > 0) {
13333
+ persistence.logger.debug("[CodexAppServer] Idle release skipped: work still in flight");
13334
+ return false;
13335
+ }
13336
+ const run = (async () => {
13337
+ const threadId = this._threadId;
13338
+ persistence.logger.debug(`[CodexAppServer] Releasing idle app-server; thread=${threadId ?? "none"} kept for resume`);
13339
+ await this.disconnectInternal({ preserveThreadState: !!threadId });
13340
+ return true;
13341
+ })();
13342
+ this.idleRelease = run;
13343
+ try {
13344
+ return await run;
13345
+ } finally {
13346
+ if (this.idleRelease === run) this.idleRelease = null;
13347
+ }
13348
+ }
13349
+ /**
13350
+ * Make sure an app-server is running and, if a thread is remembered, resumed
13351
+ * in it. Cheap no-op while connected. Covers both the idle release above and
13352
+ * an app-server that exited on its own between turns.
13353
+ */
13354
+ async ensureConnected() {
13355
+ if (this.idleRelease) await this.idleRelease;
13356
+ if (this.connected) return;
13357
+ const threadId = this._threadId;
13358
+ if (this.process) {
13359
+ await this.disconnectInternal({ preserveThreadState: !!threadId });
13360
+ }
13361
+ await this.connect();
13362
+ if (threadId) {
13363
+ await this.resumeThread({ threadId });
13364
+ }
13365
+ }
13316
13366
  buildThreadConfig(mcpServers) {
13317
13367
  return mcpServers ? { mcp_servers: mcpServers } : null;
13318
13368
  }
@@ -13327,6 +13377,7 @@ class CodexAppServerClient {
13327
13377
  }
13328
13378
  // ─── Thread management ──────────────────────────────────────
13329
13379
  async startThread(opts) {
13380
+ await this.ensureConnected();
13330
13381
  const params = {
13331
13382
  model: opts.model ?? null,
13332
13383
  modelProvider: null,
@@ -13354,6 +13405,7 @@ class CodexAppServerClient {
13354
13405
  if (!threadId) {
13355
13406
  throw new Error("No thread available to resume.");
13356
13407
  }
13408
+ await this.ensureConnected();
13357
13409
  const defaults = this.threadDefaults ?? {};
13358
13410
  const params = {
13359
13411
  threadId,
@@ -13381,6 +13433,7 @@ class CodexAppServerClient {
13381
13433
  return { threadId: result.thread.id, model: result.model };
13382
13434
  }
13383
13435
  async forkThread(opts) {
13436
+ await this.ensureConnected();
13384
13437
  const defaults = this.threadDefaults ?? {};
13385
13438
  const params = {
13386
13439
  threadId: opts.threadId,
@@ -13409,6 +13462,7 @@ class CodexAppServerClient {
13409
13462
  return { threadId: result.thread.id, model: result.model, thread: result.thread };
13410
13463
  }
13411
13464
  async readThread(opts) {
13465
+ await this.ensureConnected();
13412
13466
  const params = {
13413
13467
  threadId: opts.threadId,
13414
13468
  includeTurns: opts.includeTurns ?? true
@@ -13416,6 +13470,7 @@ class CodexAppServerClient {
13416
13470
  return await this.request("thread/read", params);
13417
13471
  }
13418
13472
  async rollbackThread(opts) {
13473
+ await this.ensureConnected();
13419
13474
  const params = {
13420
13475
  threadId: opts.threadId,
13421
13476
  numTurns: opts.numTurns
@@ -13423,6 +13478,7 @@ class CodexAppServerClient {
13423
13478
  return await this.request("thread/rollback", params);
13424
13479
  }
13425
13480
  async injectItems(opts) {
13481
+ await this.ensureConnected();
13426
13482
  const params = {
13427
13483
  threadId: opts.threadId,
13428
13484
  items: opts.items
@@ -13527,6 +13583,7 @@ class CodexAppServerClient {
13527
13583
  if (!this._threadId) {
13528
13584
  throw new Error("No active thread. Call startThread first.");
13529
13585
  }
13586
+ await this.ensureConnected();
13530
13587
  const input = [
13531
13588
  { type: "text", text: prompt },
13532
13589
  ...(opts?.images ?? []).map((path) => ({ type: "localImage", path }))
@@ -13572,6 +13629,7 @@ class CodexAppServerClient {
13572
13629
  await this.pendingInterrupt;
13573
13630
  await new Promise((resolve) => setTimeout(resolve, 0));
13574
13631
  }
13632
+ await this.ensureConnected();
13575
13633
  const timeoutMs = opts?.turnTimeoutMs ?? CodexAppServerClient.TURN_TIMEOUT_MS;
13576
13634
  let timer = null;
13577
13635
  const completion = new Promise((resolve) => {
@@ -1,5 +1,5 @@
1
1
  import axios from 'axios';
2
- import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as decryptBlob, q as prepareClipboardText, g as readSettings, r as readPersistedSessions, s as credentialRelayProblem, p as packageJson, t as readTrackedClaudeSessionIds, u as libsodiumEncryptForPublicKey, v as libsodiumPublicKeyFromSecretKey, x as getRandomBytes, y as redactLogValue } from './limits-CAD4UF8w.mjs';
2
+ import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as decryptBlob, q as prepareClipboardText, g as readSettings, r as readPersistedSessions, s as credentialRelayProblem, p as packageJson, t as readTrackedClaudeSessionIds, u as libsodiumEncryptForPublicKey, v as libsodiumPublicKeyFromSecretKey, x as getRandomBytes, y as redactLogValue } from './limits-DEsg_gHi.mjs';
3
3
  import { EventEmitter } from 'node:events';
4
4
  import { io } from 'socket.io-client';
5
5
  import * as z from 'zod';
@@ -12958,6 +12958,9 @@ class CodexAppServerClient {
12958
12958
  // Tracks in-flight interruptTurn() RPCs so sendTurnAndWait can wait for them
12959
12959
  // before starting a new turn (prevents stale turn/interrupt from aborting the next turn).
12960
12960
  pendingInterrupt = null;
12961
+ // In-flight idle release (B-461); ensureConnected() waits for it so a message
12962
+ // arriving mid-release cannot spawn a second app-server against a dying one.
12963
+ idleRelease = null;
12961
12964
  notificationProtocol = "unknown";
12962
12965
  completedTurnIds = /* @__PURE__ */ new Set();
12963
12966
  rawFileChangesByItemId = /* @__PURE__ */ new Map();
@@ -13291,6 +13294,53 @@ class CodexAppServerClient {
13291
13294
  async disconnect() {
13292
13295
  await this.disconnectInternal();
13293
13296
  }
13297
+ /** True while an app-server child process is owned by this client. */
13298
+ get hasProcess() {
13299
+ return this.process !== null;
13300
+ }
13301
+ /**
13302
+ * Drop the app-server process while the session is idle, keeping the thread
13303
+ * id and defaults so the next turn can `thread/resume` it (B-461). Refuses —
13304
+ * returning false — whenever anything is still in flight: an active turn, a
13305
+ * pending interrupt, or any outstanding RPC. See codexIdleRelease.ts.
13306
+ */
13307
+ async releaseIdleProcess() {
13308
+ if (this.idleRelease) return this.idleRelease;
13309
+ if (!this.connected && !this.process) return false;
13310
+ if (this.pendingTurnCompletion || this.pendingInterrupt || this.pending.size > 0) {
13311
+ logger.debug("[CodexAppServer] Idle release skipped: work still in flight");
13312
+ return false;
13313
+ }
13314
+ const run = (async () => {
13315
+ const threadId = this._threadId;
13316
+ logger.debug(`[CodexAppServer] Releasing idle app-server; thread=${threadId ?? "none"} kept for resume`);
13317
+ await this.disconnectInternal({ preserveThreadState: !!threadId });
13318
+ return true;
13319
+ })();
13320
+ this.idleRelease = run;
13321
+ try {
13322
+ return await run;
13323
+ } finally {
13324
+ if (this.idleRelease === run) this.idleRelease = null;
13325
+ }
13326
+ }
13327
+ /**
13328
+ * Make sure an app-server is running and, if a thread is remembered, resumed
13329
+ * in it. Cheap no-op while connected. Covers both the idle release above and
13330
+ * an app-server that exited on its own between turns.
13331
+ */
13332
+ async ensureConnected() {
13333
+ if (this.idleRelease) await this.idleRelease;
13334
+ if (this.connected) return;
13335
+ const threadId = this._threadId;
13336
+ if (this.process) {
13337
+ await this.disconnectInternal({ preserveThreadState: !!threadId });
13338
+ }
13339
+ await this.connect();
13340
+ if (threadId) {
13341
+ await this.resumeThread({ threadId });
13342
+ }
13343
+ }
13294
13344
  buildThreadConfig(mcpServers) {
13295
13345
  return mcpServers ? { mcp_servers: mcpServers } : null;
13296
13346
  }
@@ -13305,6 +13355,7 @@ class CodexAppServerClient {
13305
13355
  }
13306
13356
  // ─── Thread management ──────────────────────────────────────
13307
13357
  async startThread(opts) {
13358
+ await this.ensureConnected();
13308
13359
  const params = {
13309
13360
  model: opts.model ?? null,
13310
13361
  modelProvider: null,
@@ -13332,6 +13383,7 @@ class CodexAppServerClient {
13332
13383
  if (!threadId) {
13333
13384
  throw new Error("No thread available to resume.");
13334
13385
  }
13386
+ await this.ensureConnected();
13335
13387
  const defaults = this.threadDefaults ?? {};
13336
13388
  const params = {
13337
13389
  threadId,
@@ -13359,6 +13411,7 @@ class CodexAppServerClient {
13359
13411
  return { threadId: result.thread.id, model: result.model };
13360
13412
  }
13361
13413
  async forkThread(opts) {
13414
+ await this.ensureConnected();
13362
13415
  const defaults = this.threadDefaults ?? {};
13363
13416
  const params = {
13364
13417
  threadId: opts.threadId,
@@ -13387,6 +13440,7 @@ class CodexAppServerClient {
13387
13440
  return { threadId: result.thread.id, model: result.model, thread: result.thread };
13388
13441
  }
13389
13442
  async readThread(opts) {
13443
+ await this.ensureConnected();
13390
13444
  const params = {
13391
13445
  threadId: opts.threadId,
13392
13446
  includeTurns: opts.includeTurns ?? true
@@ -13394,6 +13448,7 @@ class CodexAppServerClient {
13394
13448
  return await this.request("thread/read", params);
13395
13449
  }
13396
13450
  async rollbackThread(opts) {
13451
+ await this.ensureConnected();
13397
13452
  const params = {
13398
13453
  threadId: opts.threadId,
13399
13454
  numTurns: opts.numTurns
@@ -13401,6 +13456,7 @@ class CodexAppServerClient {
13401
13456
  return await this.request("thread/rollback", params);
13402
13457
  }
13403
13458
  async injectItems(opts) {
13459
+ await this.ensureConnected();
13404
13460
  const params = {
13405
13461
  threadId: opts.threadId,
13406
13462
  items: opts.items
@@ -13505,6 +13561,7 @@ class CodexAppServerClient {
13505
13561
  if (!this._threadId) {
13506
13562
  throw new Error("No active thread. Call startThread first.");
13507
13563
  }
13564
+ await this.ensureConnected();
13508
13565
  const input = [
13509
13566
  { type: "text", text: prompt },
13510
13567
  ...(opts?.images ?? []).map((path) => ({ type: "localImage", path }))
@@ -13550,6 +13607,7 @@ class CodexAppServerClient {
13550
13607
  await this.pendingInterrupt;
13551
13608
  await new Promise((resolve) => setTimeout(resolve, 0));
13552
13609
  }
13610
+ await this.ensureConnected();
13553
13611
  const timeoutMs = opts?.turnTimeoutMs ?? CodexAppServerClient.TURN_TIMEOUT_MS;
13554
13612
  let timer = null;
13555
13613
  const completion = new Promise((resolve) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "very-happy-cli",
3
- "version": "0.2.137",
3
+ "version": "0.2.138",
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.137",
117
- "very-happy-tools-x64-darwin": "0.2.137",
118
- "very-happy-tools-arm64-linux": "0.2.137",
119
- "very-happy-tools-x64-linux": "0.2.137",
120
- "very-happy-tools-arm64-win32": "0.2.137",
121
- "very-happy-tools-x64-win32": "0.2.137"
116
+ "very-happy-tools-arm64-darwin": "0.2.138",
117
+ "very-happy-tools-x64-darwin": "0.2.138",
118
+ "very-happy-tools-arm64-linux": "0.2.138",
119
+ "very-happy-tools-x64-linux": "0.2.138",
120
+ "very-happy-tools-arm64-win32": "0.2.138",
121
+ "very-happy-tools-x64-win32": "0.2.138"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@slopus/happy-wire": "workspace:*",