very-happy-cli 0.2.120 → 0.2.122

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-tou8FQXI.cjs → AcpBackend-YPVwACkW.cjs} +1 -1
  2. package/dist/{AcpBackend-DiIJaJWG.mjs → AcpBackend-pgDnK76l.mjs} +1 -1
  3. package/dist/{AcpSessionManager-fF_HFB98.cjs → AcpSessionManager-6rypahwH.cjs} +51 -11
  4. package/dist/{AcpSessionManager-CZ0Az0GG.mjs → AcpSessionManager-Bq5Tiky1.mjs} +51 -11
  5. package/dist/{config-CGDxsTZg.mjs → config-BTEW041n.mjs} +2 -2
  6. package/dist/{config-CEbOao_0.cjs → config-rQdv_Yr0.cjs} +2 -2
  7. package/dist/{index-CYizH-JC.cjs → index-BHg0P9cY.cjs} +2 -2
  8. package/dist/{index-CUJ6faw1.mjs → index-BTnlYGaP.mjs} +139 -41
  9. package/dist/{index-D4_LW69g.mjs → index-BeHr5uMY.mjs} +25 -11
  10. package/dist/{index-DKj_0dqB.mjs → index-CaIsj1q7.mjs} +2 -2
  11. package/dist/{index-BlkIz9BQ.cjs → index-DKRU6E-x.cjs} +143 -44
  12. package/dist/{index-HKcuQ_fQ.cjs → index-uPpFZhhr.cjs} +24 -10
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-MWVPJDxV.cjs → installTerminalHooks-CGqopr1A.cjs} +2 -2
  16. package/dist/{installTerminalHooks-B3EGNaJr.mjs → installTerminalHooks-DFmKhCto.mjs} +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-CZ1Z6AI5.mjs → mcp-DnKBKXYa.mjs} +2 -2
  22. package/dist/{mcp-DBKk5L6_.cjs → mcp-FlbfMw6_.cjs} +2 -2
  23. package/dist/{runGemini-C3BCKTMp.mjs → runGemini-BsfadDSa.mjs} +9 -5
  24. package/dist/{runGemini-BRUmjZgV.cjs → runGemini-DYawZQTu.cjs} +9 -5
  25. package/dist/{runOpenClaw-o1ImBlcJ.cjs → runOpenClaw-C2XjFP2m.cjs} +3 -3
  26. package/dist/{runOpenClaw-DEYzcGSV.mjs → runOpenClaw-xGtbJ7f8.mjs} +3 -3
  27. package/dist/{send-B9fh96vR.cjs → send-B0Wojz18.cjs} +2 -2
  28. package/dist/{send-DFGH_-s_.mjs → send-DRZppEr2.mjs} +2 -2
  29. package/dist/{sessions-CKVfXHyi.mjs → sessions-BuMgxuxd.mjs} +11 -4
  30. package/dist/{sessions-BNyEJ2_6.cjs → sessions-CTuUV0TW.cjs} +11 -4
  31. package/dist/{spawn-xPV7-s4S.cjs → spawn-7U_4QTHw.cjs} +2 -2
  32. package/dist/{spawn-B6kP4YjA.mjs → spawn-BRdfNDsu.mjs} +2 -2
  33. package/dist/{types-7ZSxm6Pi.cjs → types-Jag0GwhO.cjs} +209 -41
  34. package/dist/{types-Di0o0BdV.mjs → types-q7wtjfxb.mjs} +208 -41
  35. package/package.json +7 -7
@@ -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, userInfo, tmpdir } from 'node:os';
3
3
  import { randomUUID, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
4
- import { o as readDaemonState, l as logger, c as configuration, q as clearDaemonState, s as getProjectPath, p as projectPath, t as initializeSandbox, u as wrapCommand, g as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, k as delay, v as AsyncLock, S as STREAM_DELTA_MAX_CHARS, x as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, y as hardenPrivateFile, j as packageJson, z as commandOnPath, e as readSettings, B as readCredentials, C as credentialRelayProblem, D as deriveLocalCliUpdateSummary, f as encodeBase64, E as readSettingsOutcome, F as updateSettings, G as encodeBase64Url, H as decodeBase64, J as writeCredentialsLegacy, K as writeCredentialsDataKey, L as readCredentialsForConfiguredRelay, N as encrypt, r as readPersistedSessions, O as isClaudeSessionId, w as writePrivateFileSync, P as getLocalHappyAgentCredentialPath, Q as readLocalHappyAgentCredentials, T as decryptWithDataKey, U as decryptLegacy, V as compareExactVersions, W as extractCompleteLines, X as decideBackfill, Y as parseTerminalHookPayload, Z as tmuxArgs, _ as scrubTmuxClientEnv, $ as decideMirrorBinding, a0 as MIRROR_BACKFILL_LINES_DEFAULT, a1 as persistSession, a2 as mirrorLineKey, a3 as mirrorLocalId, a4 as detectCLIAvailability, a5 as detectResumeSupport, a6 as acquireDaemonLock, a7 as summarizeSpawnSessionForLog, a8 as deletePersistedSessions, a9 as shellescape, aa as decrypt, ab as writeDaemonState, A as ApiClient, ac as writeSettings, ad as fetchCliUpdateState, ae as resolveCliUpdateCheckInterval, af as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, ag as releaseDaemonLock, i as isValidSessionId, ah as listVhTerminals, ai as isValidTerminalId, aj as readVhTerminal, ak as sendToVhTerminal, al as TITLE_PROMPT_PREFIX, am as BOARD_ANALYZER_PROMPT_PREFIX, d as connectionState, an as startOfflineReconnection, ao as clearCredentials, ap as clearMachineId, m as ensurePrivateDirectorySync, aq as SandboxConfigSchema, b as createEnvelope, ar as CodexAppServerClient, as as getLatestDaemonLog } from './types-Di0o0BdV.mjs';
4
+ import { o as readDaemonState, l as logger, c as configuration, q as clearDaemonState, t as getProjectPath, p as projectPath, u as initializeSandbox, v as wrapCommand, g as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, k as delay, x as AsyncLock, S as STREAM_DELTA_MAX_CHARS, y as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, z as hardenPrivateFile, j as packageJson, B as commandOnPath, e as readSettings, C as readCredentials, D as credentialRelayProblem, E as deriveLocalCliUpdateSummary, f as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, J as decodeBase64, K as writeCredentialsLegacy, L as writeCredentialsDataKey, N as readCredentialsForConfiguredRelay, O as encrypt, r as readPersistedSessions, P as isClaudeSessionId, w as writePrivateFileSync, Q as getLocalHappyAgentCredentialPath, T as readLocalHappyAgentCredentials, U as decryptWithDataKey, V as decryptLegacy, W as compareExactVersions, X as extractCompleteLines, Y as decideBackfill, Z as parseTerminalHookPayload, _ as tmuxArgs, $ as scrubTmuxClientEnv, a0 as decideMirrorBinding, a1 as MIRROR_BACKFILL_LINES_DEFAULT, a2 as persistSession, a3 as mirrorLineKey, a4 as mirrorLocalId, a5 as detectCLIAvailability, a6 as detectResumeSupport, a7 as acquireDaemonLock, a8 as summarizeSpawnSessionForLog, a9 as deletePersistedSessions, aa as shellescape, ab as decrypt, ac as writeDaemonState, A as ApiClient, ad as writeSettings, ae as fetchCliUpdateState, af as resolveCliUpdateCheckInterval, ag as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, ah as releaseDaemonLock, i as isValidSessionId, ai as listVhTerminals, aj as isValidTerminalId, ak as readVhTerminal, al as sendToVhTerminal, am as TITLE_PROMPT_PREFIX, an as BOARD_ANALYZER_PROMPT_PREFIX, d as connectionState, ao as startOfflineReconnection, ap as clearCredentials, aq as clearMachineId, m as ensurePrivateDirectorySync, ar as SandboxConfigSchema, b as createEnvelope, as as CodexAppServerClient, at as getLatestDaemonLog } from './types-q7wtjfxb.mjs';
5
5
  import spawn$1, { spawn } from 'cross-spawn';
6
6
  import path, { join, resolve, basename, delimiter, isAbsolute, normalize, sep, dirname } from 'node:path';
7
7
  import { createInterface } from 'node:readline';
@@ -3384,9 +3384,7 @@ class StreamRelay {
3384
3384
  if (e.type === "content_block_start") {
3385
3385
  const kind = e.content_block?.type === "thinking" ? "thinking" : e.content_block?.type === "text" ? "text" : null;
3386
3386
  if (!kind || !this.currentMid || typeof e.index !== "number") return;
3387
- this.flushPending();
3388
- this.openBlocks.add(e.index);
3389
- this.send({ t: "block-start", mid: this.currentMid, idx: e.index, kind });
3387
+ this.openBlock(this.currentMid, e.index, kind);
3390
3388
  return;
3391
3389
  }
3392
3390
  if (e.type === "content_block_delta") {
@@ -3394,16 +3392,12 @@ class StreamRelay {
3394
3392
  const text = delta?.type === "text_delta" ? delta.text : delta?.type === "thinking_delta" ? delta.thinking : null;
3395
3393
  if (typeof text !== "string" || text.length === 0) return;
3396
3394
  if (!this.currentMid || typeof e.index !== "number") return;
3397
- if (!this.openBlocks.has(e.index)) return;
3398
3395
  this.appendDelta(this.currentMid, e.index, text);
3399
3396
  return;
3400
3397
  }
3401
3398
  if (e.type === "content_block_stop") {
3402
3399
  if (!this.currentMid || typeof e.index !== "number") return;
3403
- if (!this.openBlocks.has(e.index)) return;
3404
- this.flushPending();
3405
- this.openBlocks.delete(e.index);
3406
- this.send({ t: "block-end", mid: this.currentMid, idx: e.index });
3400
+ this.closeBlock(this.currentMid, e.index);
3407
3401
  return;
3408
3402
  }
3409
3403
  if (e.type === "message_delta") {
@@ -3414,7 +3408,29 @@ class StreamRelay {
3414
3408
  return;
3415
3409
  }
3416
3410
  }
3411
+ // ---- Block-level API (see module comment) --------------------------------
3412
+ /** Open a draft block. A different `mid` than the one currently open closes
3413
+ * whatever the previous message left open, exactly like the SDK's
3414
+ * `message_start` does. Re-opening an already open block is a no-op. */
3415
+ openBlock(mid, idx, kind) {
3416
+ this.idleSinceTurnEnd = false;
3417
+ if (this.currentMid !== mid) {
3418
+ this.flushPending();
3419
+ this.closeOpenBlocks();
3420
+ this.currentMid = mid;
3421
+ }
3422
+ if (this.openBlocks.has(idx)) return;
3423
+ this.flushPending();
3424
+ this.openBlocks.add(idx);
3425
+ this.send({ t: "block-start", mid, idx, kind });
3426
+ }
3427
+ /** Append text to an OPEN block; deltas for a block that was never opened
3428
+ * (or belongs to another message) are dropped — the SDK path relies on
3429
+ * this to ignore `input_json_delta` for tool_use blocks. */
3417
3430
  appendDelta(mid, idx, text) {
3431
+ this.idleSinceTurnEnd = false;
3432
+ if (text.length === 0) return;
3433
+ if (this.currentMid !== mid || !this.openBlocks.has(idx)) return;
3418
3434
  if (this.pending && (this.pending.mid !== mid || this.pending.idx !== idx)) {
3419
3435
  this.flushPending();
3420
3436
  }
@@ -3434,6 +3450,22 @@ class StreamRelay {
3434
3450
  }, STREAM_FLUSH_MS);
3435
3451
  }
3436
3452
  }
3453
+ /** Close an open block: pending text goes out first, then `block-end`. */
3454
+ closeBlock(mid, idx) {
3455
+ this.idleSinceTurnEnd = false;
3456
+ if (this.currentMid !== mid || !this.openBlocks.has(idx)) return;
3457
+ this.flushPending();
3458
+ this.openBlocks.delete(idx);
3459
+ this.send({ t: "block-end", mid, idx });
3460
+ }
3461
+ /** Merge quantified progress; flushed on the progress cadence. */
3462
+ setProgress(patch) {
3463
+ this.idleSinceTurnEnd = false;
3464
+ this.progress = { ...this.progress, ...patch };
3465
+ this.progressDirty = true;
3466
+ this.scheduleProgress();
3467
+ }
3468
+ // -------------------------------------------------------------------------
3437
3469
  flushPending() {
3438
3470
  if (this.flushHandle !== null) {
3439
3471
  this.clearTimer(this.flushHandle);
@@ -3456,11 +3488,6 @@ class StreamRelay {
3456
3488
  }
3457
3489
  this.openBlocks.clear();
3458
3490
  }
3459
- setProgress(patch) {
3460
- this.progress = { ...this.progress, ...patch };
3461
- this.progressDirty = true;
3462
- this.scheduleProgress();
3463
- }
3464
3491
  scheduleProgress() {
3465
3492
  if (this.progressHandle !== null) return;
3466
3493
  this.progressHandle = this.setTimer(() => {
@@ -4303,10 +4330,17 @@ class MessageQueue2 {
4303
4330
  waiter = null;
4304
4331
  closed = false;
4305
4332
  onMessageHandler = null;
4333
+ /**
4334
+ * B-332: fired for every item the queue itself destroys before it was ever
4335
+ * handed to the runner. The runner wires this to a queue-cancel tombstone
4336
+ * so the web stops painting a silently-lost message as "queued".
4337
+ */
4338
+ onDiscardHandler = null;
4306
4339
  modeHasher;
4307
- constructor(modeHasher, onMessageHandler = null) {
4340
+ constructor(modeHasher, onMessageHandler = null, onDiscardHandler = null) {
4308
4341
  this.modeHasher = modeHasher;
4309
4342
  this.onMessageHandler = onMessageHandler;
4343
+ this.onDiscardHandler = onDiscardHandler;
4310
4344
  logger.debug(`[MessageQueue2] Initialized`);
4311
4345
  }
4312
4346
  /**
@@ -4315,6 +4349,19 @@ class MessageQueue2 {
4315
4349
  setOnMessage(handler) {
4316
4350
  this.onMessageHandler = handler;
4317
4351
  }
4352
+ /**
4353
+ * Set a handler that will be called when messages are discarded without ever
4354
+ * being processed (B-332).
4355
+ */
4356
+ setOnDiscard(handler) {
4357
+ this.onDiscardHandler = handler;
4358
+ }
4359
+ /** Report the current items as destroyed for `reason` (caller clears next). */
4360
+ notifyDiscard(reason) {
4361
+ if (this.queue.length === 0) return;
4362
+ const entries = this.queue.map((item) => ({ sourceId: item.sourceId, reason }));
4363
+ this.onDiscardHandler?.(entries);
4364
+ }
4318
4365
  /**
4319
4366
  * Push a message to the queue with a mode and an optional list of
4320
4367
  * attachments that travel with this message.
@@ -4382,6 +4429,7 @@ class MessageQueue2 {
4382
4429
  }
4383
4430
  const modeHash = this.modeHasher(mode);
4384
4431
  logger.debug(`[MessageQueue2] pushIsolateAndClear() called with mode hash: ${modeHash} - clearing ${this.queue.length} pending messages`);
4432
+ this.notifyDiscard("cleared");
4385
4433
  this.queue = [];
4386
4434
  this.queue.push({
4387
4435
  message,
@@ -4433,6 +4481,7 @@ class MessageQueue2 {
4433
4481
  */
4434
4482
  reset() {
4435
4483
  logger.debug(`[MessageQueue2] reset() called. Clearing ${this.queue.length} messages`);
4484
+ this.notifyDiscard("aborted");
4436
4485
  this.queue = [];
4437
4486
  this.closed = false;
4438
4487
  this.waiter = null;
@@ -9976,6 +10025,27 @@ async function openUserScopedSocket(token, options = {}) {
9976
10025
  };
9977
10026
  }
9978
10027
 
10028
+ const RPC_RATE_LIMIT_ERROR = "RPC rate limit reached";
10029
+ const RPC_ACCOUNT_RATE_LIMIT_ERROR = "RPC account rate limit reached";
10030
+ const RPC_RATE_RETRY_MIN_DELAY_MS = 2e3;
10031
+ const RPC_RATE_RETRY_MAX_DELAY_MS = 6e4;
10032
+ const RPC_RATE_MAX_RETRIES = 2;
10033
+ function isRpcRateLimitAck(ack) {
10034
+ if (!ack || typeof ack !== "object") return false;
10035
+ const a = ack;
10036
+ if (a.ok !== false) return false;
10037
+ if (a.code === "rpc_rate_limited" || a.code === "rpc_account_rate_limited") return true;
10038
+ return a.error === RPC_RATE_LIMIT_ERROR || a.error === RPC_ACCOUNT_RATE_LIMIT_ERROR;
10039
+ }
10040
+ function rpcRateRetryDelayMs(attempt, retryAfterMs, random = Math.random()) {
10041
+ if (typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs > 0) {
10042
+ return Math.min(RPC_RATE_RETRY_MAX_DELAY_MS, Math.round(retryAfterMs * (1 + random * 0.5)));
10043
+ }
10044
+ const exponent = Math.max(0, Math.min(attempt, 32) - 1);
10045
+ const ceiling = Math.min(RPC_RATE_RETRY_MIN_DELAY_MS * 2 ** exponent, RPC_RATE_RETRY_MAX_DELAY_MS);
10046
+ return Math.round(RPC_RATE_RETRY_MIN_DELAY_MS + random * (ceiling - RPC_RATE_RETRY_MIN_DELAY_MS));
10047
+ }
10048
+
9979
10049
  const PERMISSION_OPS_CLIENT = "session-ops";
9980
10050
  const PERMISSION_RPC_TIMEOUT_MS = 3e4;
9981
10051
  const SETTLE_TIMEOUT_MS = 5e3;
@@ -10007,6 +10077,9 @@ function interpretPermissionAck(ack, decryptResult) {
10007
10077
  if (/timed? ?out/i.test(message)) {
10008
10078
  return { status: "timeout", message };
10009
10079
  }
10080
+ if (isRpcRateLimitAck(ack)) {
10081
+ return { status: "rate-limited", message, retryAfterMs: rpcRateRetryDelayMs(1, ack.retryAfterMs, 0) };
10082
+ }
10010
10083
  return { status: "rejected", message };
10011
10084
  }
10012
10085
  let body = null;
@@ -10068,15 +10141,24 @@ async function resolvePermissionRequest(sessionId, requestId, verdict, deps = {}
10068
10141
  const params = encodeBase64(encrypt(key, persisted.encryptionVariant, payload));
10069
10142
  const transport = await (deps.openTransport ?? openUserScopedSocket)(token);
10070
10143
  const rpcTimeoutMs = deps.rpcTimeoutMs ?? PERMISSION_RPC_TIMEOUT_MS;
10144
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
10071
10145
  let ack;
10072
10146
  try {
10073
- ack = await withDeadline(
10074
- transport.rpcCall({ method: `${sessionId}:permission`, params }, rpcTimeoutMs),
10075
- rpcTimeoutMs + RPC_DEADLINE_GRACE_MS,
10076
- `permission RPC did not settle within ${rpcTimeoutMs}ms`
10077
- );
10078
- } catch (error) {
10079
- ack = { ok: false, error: error instanceof Error ? error.message : "RPC call failed" };
10147
+ for (let attempt = 1; ; attempt++) {
10148
+ try {
10149
+ ack = await withDeadline(
10150
+ transport.rpcCall({ method: `${sessionId}:permission`, params }, rpcTimeoutMs),
10151
+ rpcTimeoutMs + RPC_DEADLINE_GRACE_MS,
10152
+ `permission RPC did not settle within ${rpcTimeoutMs}ms`
10153
+ );
10154
+ } catch (error) {
10155
+ ack = { ok: false, error: error instanceof Error ? error.message : "RPC call failed" };
10156
+ }
10157
+ if (!isRpcRateLimitAck(ack) || attempt > RPC_RATE_MAX_RETRIES) break;
10158
+ const waitMs = rpcRateRetryDelayMs(attempt, ack.retryAfterMs, (deps.random ?? Math.random)());
10159
+ deps.onRateLimited?.({ attempt, waitMs, serverError: ack.error ?? "RPC rate limit reached" });
10160
+ await sleep(waitMs);
10161
+ }
10080
10162
  } finally {
10081
10163
  transport.close();
10082
10164
  }
@@ -10084,7 +10166,6 @@ async function resolvePermissionRequest(sessionId, requestId, verdict, deps = {}
10084
10166
  if (outcome.status !== "acknowledged") {
10085
10167
  return { sessionId, requestId, payload, outcome };
10086
10168
  }
10087
- const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
10088
10169
  const deadline = now() + (deps.settleTimeoutMs ?? SETTLE_TIMEOUT_MS);
10089
10170
  let settled = false;
10090
10171
  for (; ; ) {
@@ -11215,7 +11296,7 @@ function registerSideQuestionHandler(rpc, deps) {
11215
11296
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
11216
11297
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
11217
11298
  const run = deps.run ?? (async (input) => {
11218
- const { query } = await import('./index-DKj_0dqB.mjs');
11299
+ const { query } = await import('./index-CaIsj1q7.mjs');
11219
11300
  return runSideQuestion(query, input);
11220
11301
  });
11221
11302
  const slots = /* @__PURE__ */ new Map();
@@ -11897,6 +11978,7 @@ async function runClaude(credentials, options = {}) {
11897
11978
  if (reconnectSessionId) {
11898
11979
  session.suppressNextArchiveSignal();
11899
11980
  if (!reconnectSeeded) session.skipExistingMessages();
11981
+ void session.cancelUndeliveredQueuedInputs();
11900
11982
  session.updateMetadata((meta) => ({
11901
11983
  ...mergeReconnectMetadata(meta, metadata, Date.now()),
11902
11984
  queueCancellation: true,
@@ -12021,6 +12103,16 @@ async function runClaude(credentials, options = {}) {
12021
12103
  controlledByUser: options.startingMode !== "remote"
12022
12104
  }));
12023
12105
  const messageQueue = new MessageQueue2(claudeModeHash);
12106
+ messageQueue.setOnDiscard((entries) => {
12107
+ const byReason = /* @__PURE__ */ new Map();
12108
+ for (const entry of entries) {
12109
+ if (!entry.sourceId) continue;
12110
+ const list = byReason.get(entry.reason) ?? [];
12111
+ list.push(entry.sourceId);
12112
+ byReason.set(entry.reason, list);
12113
+ }
12114
+ for (const [reason, keys] of byReason) session.sendQueueCancelReason(keys, reason);
12115
+ });
12024
12116
  session.rpcHandlerManager.registerHandler("cancelQueuedMessage", async (params) => ({
12025
12117
  removed: messageQueue.cancelPending(
12026
12118
  typeof params?.localKey === "string" ? params.localKey : void 0,
@@ -15233,10 +15325,10 @@ function isCodexClearText(text) {
15233
15325
  }
15234
15326
  function enqueueCodexUserText(opts) {
15235
15327
  if (isCodexClearText(opts.text)) {
15236
- opts.queue.pushIsolateAndClear(opts.text, opts.mode);
15328
+ opts.queue.pushIsolateAndClear(opts.text, opts.mode, void 0, opts.sourceId);
15237
15329
  return "clear";
15238
15330
  }
15239
- opts.queue.push(opts.text, opts.mode);
15331
+ opts.queue.push(opts.text, opts.mode, void 0, opts.sourceId);
15240
15332
  return "queued";
15241
15333
  }
15242
15334
 
@@ -15421,6 +15513,7 @@ async function runCodex(opts) {
15421
15513
  if (reconnectSessionId) {
15422
15514
  session.suppressNextArchiveSignal();
15423
15515
  if (!reconnectSeeded) session.skipExistingMessages();
15516
+ void session.cancelUndeliveredQueuedInputs();
15424
15517
  session.updateMetadata((meta) => mergeReconnectMetadata(meta, metadata, Date.now()));
15425
15518
  }
15426
15519
  if (response) {
@@ -15443,6 +15536,10 @@ async function runCodex(opts) {
15443
15536
  }
15444
15537
  }
15445
15538
  const messageQueue = new MessageQueue2(hashCodexEnhancedMode);
15539
+ messageQueue.setOnDiscard((entries) => {
15540
+ const keys = entries.map((entry) => entry.sourceId).filter((key) => typeof key === "string");
15541
+ if (keys.length > 0) session.sendQueueCancelReason(keys, entries[0].reason);
15542
+ });
15446
15543
  let currentPermissionMode = initialPermissionMode;
15447
15544
  let currentModel = DEFAULT_CODEX_MODEL;
15448
15545
  let currentEffort = DEFAULT_CODEX_EFFORT;
@@ -15524,7 +15621,8 @@ async function runCodex(opts) {
15524
15621
  const enqueueResult = enqueueCodexUserText({
15525
15622
  text: message.content.text,
15526
15623
  mode: enhancedMode,
15527
- queue: messageQueue
15624
+ queue: messageQueue,
15625
+ sourceId: message.localKey
15528
15626
  });
15529
15627
  if (enqueueResult === "clear") {
15530
15628
  logger.debug("[Codex] /clear command pushed to isolated queue");
@@ -16149,7 +16247,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16149
16247
  process.exit(0);
16150
16248
  } else if (subcommand === "install-terminal-hooks") {
16151
16249
  try {
16152
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-B3EGNaJr.mjs');
16250
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-DFmKhCto.mjs');
16153
16251
  const command = parseTerminalHooksArgs(args.slice(1));
16154
16252
  if (command.action === "help") {
16155
16253
  console.log(TERMINAL_HOOKS_HELP);
@@ -16166,7 +16264,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16166
16264
  }
16167
16265
  } else if (subcommand === "spawn") {
16168
16266
  try {
16169
- const { handleSpawnCommand } = await import('./spawn-B6kP4YjA.mjs');
16267
+ const { handleSpawnCommand } = await import('./spawn-BRdfNDsu.mjs');
16170
16268
  await handleSpawnCommand(args.slice(1));
16171
16269
  } catch (error) {
16172
16270
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16178,7 +16276,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16178
16276
  return;
16179
16277
  } else if (subcommand === "sessions") {
16180
16278
  try {
16181
- const { handleSessionsCommand } = await import('./sessions-CKVfXHyi.mjs');
16279
+ const { handleSessionsCommand } = await import('./sessions-BuMgxuxd.mjs');
16182
16280
  await handleSessionsCommand(args.slice(1));
16183
16281
  } catch (error) {
16184
16282
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16190,7 +16288,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16190
16288
  return;
16191
16289
  } else if (subcommand === "send") {
16192
16290
  try {
16193
- const { handleSendCommand } = await import('./send-DFGH_-s_.mjs');
16291
+ const { handleSendCommand } = await import('./send-DRZppEr2.mjs');
16194
16292
  await handleSendCommand(args.slice(1));
16195
16293
  } catch (error) {
16196
16294
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16296,9 +16394,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16296
16394
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
16297
16395
  const projectId = args[3];
16298
16396
  try {
16299
- const { saveGoogleCloudProjectToConfig } = await import('./config-CGDxsTZg.mjs');
16300
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-Di0o0BdV.mjs').then(function (n) { return n.at; });
16301
- const { ApiClient: ApiClient2 } = await import('./types-Di0o0BdV.mjs').then(function (n) { return n.au; });
16397
+ const { saveGoogleCloudProjectToConfig } = await import('./config-BTEW041n.mjs');
16398
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-q7wtjfxb.mjs').then(function (n) { return n.au; });
16399
+ const { ApiClient: ApiClient2 } = await import('./types-q7wtjfxb.mjs').then(function (n) { return n.av; });
16302
16400
  let userEmail = void 0;
16303
16401
  try {
16304
16402
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -16329,7 +16427,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16329
16427
  }
16330
16428
  if (geminiSubcommand === "project" && args[2] === "get") {
16331
16429
  try {
16332
- const { readGeminiLocalConfig } = await import('./config-CGDxsTZg.mjs');
16430
+ const { readGeminiLocalConfig } = await import('./config-BTEW041n.mjs');
16333
16431
  const config = readGeminiLocalConfig();
16334
16432
  if (config.googleCloudProject) {
16335
16433
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -16369,7 +16467,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16369
16467
  process.exit(0);
16370
16468
  }
16371
16469
  try {
16372
- const { runGemini } = await import('./runGemini-C3BCKTMp.mjs');
16470
+ const { runGemini } = await import('./runGemini-BsfadDSa.mjs');
16373
16471
  let startedBy = void 0;
16374
16472
  for (let i = 1; i < args.length; i++) {
16375
16473
  if (args[i] === "--started-by") {
@@ -16399,7 +16497,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16399
16497
  }
16400
16498
  };
16401
16499
  try {
16402
- const { runAcp, resolveAcpAgentConfig } = await import('./index-D4_LW69g.mjs');
16500
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-BeHr5uMY.mjs');
16403
16501
  const parsed = parsePiRunnerArgs(args.slice(1));
16404
16502
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
16405
16503
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -16426,7 +16524,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16426
16524
  return;
16427
16525
  } else if (subcommand === "acp") {
16428
16526
  try {
16429
- const { runAcp, resolveAcpAgentConfig } = await import('./index-D4_LW69g.mjs');
16527
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-BeHr5uMY.mjs');
16430
16528
  let startedBy = void 0;
16431
16529
  let verbose = false;
16432
16530
  const acpArgs = [];
@@ -16466,7 +16564,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16466
16564
  return;
16467
16565
  } else if (subcommand === "openclaw") {
16468
16566
  try {
16469
- const { runOpenClaw } = await import('./runOpenClaw-DEYzcGSV.mjs');
16567
+ const { runOpenClaw } = await import('./runOpenClaw-xGtbJ7f8.mjs');
16470
16568
  let startedBy = void 0;
16471
16569
  let verbose = false;
16472
16570
  let gatewayUrl;
@@ -16517,7 +16615,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16517
16615
  return;
16518
16616
  } else if (subcommand === "mcp" && args.length === 1) {
16519
16617
  try {
16520
- const { handleMcpCommand } = await import('./mcp-CZ1Z6AI5.mjs');
16618
+ const { handleMcpCommand } = await import('./mcp-DnKBKXYa.mjs');
16521
16619
  await handleMcpCommand();
16522
16620
  } catch (error) {
16523
16621
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -16878,4 +16976,4 @@ ${chalk.bold("Examples:")}
16878
16976
  }
16879
16977
  }
16880
16978
 
16881
- export { ALLOWED_SPAWN_PERMISSION_MODES as A, registerKillSessionHandler as B, BasePermissionHandler as C, DEFAULT_RECENT_LIMIT as D, writeSessionModeFile as E, normalizeAcpPermissionMode as F, GEMINI_MODEL_ENV as G, removeSessionModeFile as H, GEMINI_API_KEY_ENV as I, GOOGLE_API_KEY_ENV as J, BaseReasoningProcessor as K, MessageBuffer as L, MAX_READ_LIMIT as M, CHANGE_TITLE_INSTRUCTION as N, SPAWN_AGENTS as S, TitleGenerator as T, sessionWebUrl as a, sanitizeSpawnPermissionMode as b, checkIfDaemonRunningAndCleanupStaleState as c, sendUserMessage as d, listSessions as e, stopSession as f, resolvePermissionRequest as g, archiveSession as h, isValidSpawnOrigin as i, DEFAULT_GEMINI_MODEL as j, setTerminalTitleViaDaemon as k, listAccountSessions as l, registerAssistantSessionTools as m, initialMachineMetadata as n, createSessionMetadata as o, pushClipboardViaDaemon as p, query as q, readSessionTranscript as r, spawnDaemonSession as s, tmuxSupportsSessionEnv as t, setupOfflineReconnection as u, notifyDaemonSessionStarted as v, waitForSessionKey as w, MessageQueue2 as x, hashObject as y, startHappyServer as z };
16979
+ export { ALLOWED_SPAWN_PERMISSION_MODES as A, startHappyServer as B, registerKillSessionHandler as C, DEFAULT_RECENT_LIMIT as D, BasePermissionHandler as E, writeSessionModeFile as F, GEMINI_MODEL_ENV as G, normalizeAcpPermissionMode as H, removeSessionModeFile as I, GEMINI_API_KEY_ENV as J, GOOGLE_API_KEY_ENV as K, BaseReasoningProcessor as L, MAX_READ_LIMIT as M, MessageBuffer as N, CHANGE_TITLE_INSTRUCTION as O, SPAWN_AGENTS as S, TitleGenerator as T, sessionWebUrl as a, sanitizeSpawnPermissionMode as b, checkIfDaemonRunningAndCleanupStaleState as c, sendUserMessage as d, listSessions as e, stopSession as f, resolvePermissionRequest as g, archiveSession as h, isValidSpawnOrigin as i, DEFAULT_GEMINI_MODEL as j, setTerminalTitleViaDaemon as k, listAccountSessions as l, registerAssistantSessionTools as m, initialMachineMetadata as n, createSessionMetadata as o, pushClipboardViaDaemon as p, query as q, readSessionTranscript as r, spawnDaemonSession as s, tmuxSupportsSessionEnv as t, setupOfflineReconnection as u, notifyDaemonSessionStarted as v, waitForSessionKey as w, StreamRelay as x, MessageQueue2 as y, hashObject as z };
@@ -1,10 +1,10 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-DiIJaJWG.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-DiIJaJWG.mjs';
3
- import { d as connectionState, A as ApiClient, e as readSettings, f as encodeBase64, l as logger, p as projectPath } from './types-Di0o0BdV.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-CZ0Az0GG.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-pgDnK76l.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-pgDnK76l.mjs';
3
+ import { d as connectionState, A as ApiClient, e as readSettings, f as encodeBase64, l as logger, p as projectPath } from './types-q7wtjfxb.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-Bq5Tiky1.mjs';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { join } from 'node:path';
7
- import { n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as MessageQueue2, y as hashObject, z as startHappyServer, T as TitleGenerator, B as registerKillSessionHandler, C as BasePermissionHandler, E as writeSessionModeFile, F as normalizeAcpPermissionMode, H as removeSessionModeFile } from './index-CUJ6faw1.mjs';
7
+ import { n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as StreamRelay, y as MessageQueue2, z as hashObject, B as startHappyServer, T as TitleGenerator, C as registerKillSessionHandler, E as BasePermissionHandler, F as writeSessionModeFile, H as normalizeAcpPermissionMode, I as removeSessionModeFile } from './index-BTnlYGaP.mjs';
8
8
  import 'node:child_process';
9
9
  import '@agentclientprotocol/sdk';
10
10
  import 'axios';
@@ -636,7 +636,11 @@ async function runAcp(opts) {
636
636
  }
637
637
  permissionHandler = new GenericAcpPermissionHandler(session, opts.agentName);
638
638
  permissionHandler.reset("Previous CLI process exited before responding");
639
- const sessionManager = new AcpSessionManager();
639
+ const streamingEnabled = process.env.HAPPY_SESSION_STREAM_DISABLED !== "1";
640
+ const streamRelay = new StreamRelay({
641
+ send: (frame) => session.sendStreamFrame(frame)
642
+ });
643
+ const sessionManager = new AcpSessionManager(streamingEnabled ? { stream: streamRelay } : {});
640
644
  const messageQueue = new MessageQueue2((mode) => hashObject(mode));
641
645
  let currentPermissionMode;
642
646
  let currentModel;
@@ -690,6 +694,13 @@ async function runAcp(opts) {
690
694
  verbose
691
695
  });
692
696
  let thinking = false;
697
+ const setThinking = (next) => {
698
+ if (thinking === next) {
699
+ return;
700
+ }
701
+ thinking = next;
702
+ session.keepAlive(thinking, "remote");
703
+ };
693
704
  let acpSessionId = null;
694
705
  let shouldExit = false;
695
706
  let abortController = new AbortController();
@@ -934,11 +945,6 @@ async function runAcp(opts) {
934
945
  const suffix = msg.detail ? `: ${msg.detail}` : "";
935
946
  const statusLine = `Status: ${msg.status}${suffix}`;
936
947
  logAcp("muted", statusLine);
937
- const nextThinking = msg.status === "running";
938
- if (thinking !== nextThinking) {
939
- thinking = nextThinking;
940
- session.keepAlive(thinking, "remote");
941
- }
942
948
  if (msg.status === "idle") {
943
949
  clearPendingTurn();
944
950
  }
@@ -1051,6 +1057,7 @@ async function runAcp(opts) {
1051
1057
  }
1052
1058
  logAcp("incoming", `Incoming prompt: ${formatUnknownForConsole(batch.message, ACP_EVENT_PREVIEW_CHARS)}`);
1053
1059
  sendEnvelopes(sessionManager.startTurn());
1060
+ setThinking(true);
1054
1061
  const turnEnded = waitForTurnEnd();
1055
1062
  try {
1056
1063
  if (typeof batch.mode.permissionMode === "string" && batch.mode.permissionMode.length > 0) {
@@ -1062,12 +1069,16 @@ async function runAcp(opts) {
1062
1069
  await backend.sendPrompt(acpSessionId, batch.message);
1063
1070
  await turnEnded;
1064
1071
  sendEnvelopes(sessionManager.endTurn("completed"));
1072
+ streamRelay.endTurn();
1073
+ setThinking(false);
1065
1074
  session.sendSessionEvent({ type: "ready" });
1066
1075
  if (verbose) {
1067
1076
  logAcp("muted", `Outgoing prompt completion from ${opts.agentName}`);
1068
1077
  }
1069
1078
  } catch (error) {
1070
1079
  sendEnvelopes(sessionManager.endTurn("failed"));
1080
+ streamRelay.endTurn();
1081
+ setThinking(false);
1071
1082
  session.sendSessionEvent({ type: "ready" });
1072
1083
  logAcp("error", `Prompt error from ${opts.agentName}: ${error instanceof Error ? error.message : String(error)}`);
1073
1084
  clearPendingTurn(error instanceof Error ? error : new Error(String(error)));
@@ -1075,7 +1086,10 @@ async function runAcp(opts) {
1075
1086
  }
1076
1087
  }
1077
1088
  } finally {
1089
+ setThinking(false);
1078
1090
  clearInterval(keepAliveInterval);
1091
+ streamRelay.endTurn();
1092
+ streamRelay.dispose();
1079
1093
  reconnectionHandle?.cancel();
1080
1094
  clearPendingTurn(new Error("ACP runner shutting down"));
1081
1095
  try {
@@ -1,9 +1,9 @@
1
- import { q as query } from './index-CUJ6faw1.mjs';
1
+ import { q as query } from './index-BTnlYGaP.mjs';
2
2
  export { AbortError } from '@anthropic-ai/claude-agent-sdk';
3
3
  import 'chalk';
4
4
  import 'node:os';
5
5
  import 'node:crypto';
6
- import './types-Di0o0BdV.mjs';
6
+ import './types-q7wtjfxb.mjs';
7
7
  import 'axios';
8
8
  import 'node:util';
9
9
  import 'node:fs';