very-happy-cli 0.2.128 → 0.2.130

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 (45) hide show
  1. package/dist/{AcpBackend-BYU6J2n0.cjs → AcpBackend-5slez7vM.cjs} +16 -4
  2. package/dist/{AcpBackend-D7987ody.mjs → AcpBackend-Co3atHW2.mjs} +16 -4
  3. package/dist/{AcpSessionManager-BwrjVZyn.mjs → AcpSessionManager-B0sD6Zi7.mjs} +1 -1
  4. package/dist/{AcpSessionManager-artXJgqb.cjs → AcpSessionManager-CxvVg8ec.cjs} +1 -1
  5. package/dist/{agentGuidance-BPGPaNlo.mjs → agentGuidance-7pJxf_3b.mjs} +1 -1
  6. package/dist/{agentGuidance-B1xYgHx6.cjs → agentGuidance-TjPzDO7d.cjs} +1 -1
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-Dmkzs3o4.cjs → config-B4Md4FvK.cjs} +4 -4
  10. package/dist/{config-DSBE0Az0.mjs → config-BWu8j7bO.mjs} +4 -4
  11. package/dist/{index-CfM6ewd8.mjs → index-BUhcXX8R.mjs} +187 -83
  12. package/dist/{index-35FahgEU.mjs → index-C13U8MkT.mjs} +4 -4
  13. package/dist/{index-D_YVlpsW.cjs → index-C7TA3haC.cjs} +194 -86
  14. package/dist/{index-m0Dnccc_.mjs → index-CdOVizh_.mjs} +115 -32
  15. package/dist/{index-vyTJsT_R.cjs → index-DksoP9b2.cjs} +114 -31
  16. package/dist/{index-CJmXLSQc.cjs → index-Ds0aHXiU.cjs} +4 -4
  17. package/dist/index.cjs +4 -4
  18. package/dist/index.mjs +4 -4
  19. package/dist/{installTerminalHooks-C1zwiJJ2.cjs → installTerminalHooks-cNXov61F.cjs} +4 -4
  20. package/dist/{installTerminalHooks-ClrFe2fA.mjs → installTerminalHooks-kUC6I6Rn.mjs} +4 -4
  21. package/dist/lib.cjs +2 -2
  22. package/dist/lib.d.cts +7 -0
  23. package/dist/lib.d.mts +7 -0
  24. package/dist/lib.mjs +2 -2
  25. package/dist/{limits-CrwtFbi8.cjs → limits-BhK6ovLz.cjs} +7 -7
  26. package/dist/{limits-BNTLh2lw.mjs → limits-CIcZA0Bn.mjs} +7 -7
  27. package/dist/{mcp-Bc_mmJt8.cjs → mcp-CNX1qErQ.cjs} +4 -4
  28. package/dist/{mcp-BsohER5A.mjs → mcp-D5uyhbfB.mjs} +4 -4
  29. package/dist/{runGemini-B9S85Vms.mjs → runGemini-BQd3qlVY.mjs} +6 -6
  30. package/dist/{runGemini-B2GesRWm.cjs → runGemini-DvV6X_GE.cjs} +6 -6
  31. package/dist/{runOpenClaw-Byhq-B1y.mjs → runOpenClaw-BgtE2jKF.mjs} +5 -5
  32. package/dist/{runOpenClaw-DCK-R8qe.cjs → runOpenClaw-jxvPz3bo.cjs} +5 -5
  33. package/dist/{send-DQS1zNWp.mjs → send-BdhUFGTm.mjs} +4 -4
  34. package/dist/{send-CPdT1nOg.cjs → send-Di8Q32Ds.cjs} +4 -4
  35. package/dist/{sessions-DIOdWnPe.mjs → sessions-Bo2THj4y.mjs} +4 -4
  36. package/dist/{sessions-DC0IKaD9.cjs → sessions-eaAew6IP.cjs} +4 -4
  37. package/dist/{spawn-DrnCW2QK.cjs → spawn-6GfCYB5i.cjs} +4 -4
  38. package/dist/{spawn-DV4pIAHZ.mjs → spawn-CJxg1JUO.mjs} +4 -4
  39. package/dist/{teams-DlTHOiCk.mjs → teams-CHGj90rR.mjs} +4 -4
  40. package/dist/{teams-CwXEJs9I.cjs → teams-WdwFtptw.cjs} +4 -4
  41. package/dist/{todo-DrME15xq.cjs → todo-C-wktj2K.cjs} +3 -3
  42. package/dist/{todo-BwXURAbQ.mjs → todo-C8hDSNBP.mjs} +3 -3
  43. package/dist/{types-CFs8FmiY.mjs → types-B-ySoSY9.mjs} +21 -5
  44. package/dist/{types-DfatCoQL.cjs → types-QB33ckXt.cjs} +21 -5
  45. package/package.json +7 -7
@@ -1,13 +1,13 @@
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 { j as getProjectPath, h as BUILTIN_TODO_DISCOVERY, p as projectPath, k as initializeSandbox, w as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, f as delay, l as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, m as commandOnPath, n as deriveLocalCliUpdateSummary, o as isClaudeSessionId, q as getLocalHappyAgentCredentialPath, r as readLocalHappyAgentCredentials, t as parseExactVersion, u as compareExactVersions, v as extractCompleteLines, x as decideBackfill, y as parseTerminalHookPayload, z as tmuxArgs, C as scrubTmuxClientEnv, D as decideMirrorBinding, E as MIRROR_BACKFILL_LINES_DEFAULT, F as mirrorLineKey, G as mirrorLocalId, H as detectCLIAvailability, J as detectResumeSupport, K as summarizeSpawnSessionForLog, L as shellescape, A as ApiClient, N as fetchCliUpdateState, O as resolveCliUpdateCheckInterval, P as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, i as isValidSessionId, Q as listVhTerminals, T as isValidTerminalId, U as readVhTerminal, V as sendToVhTerminal, W as TITLE_PROMPT_PREFIX, X as BOARD_ANALYZER_PROMPT_PREFIX, g as connectionState, Y as startOfflineReconnection, c as createEnvelope, Z as CodexAppServerClient } from './types-CFs8FmiY.mjs';
5
- import { z as readDaemonState, l as logger, c as configuration, A as clearDaemonState, B as ensurePrivateDirectory, D as hardenPrivateFile, p as packageJson, g as readSettings, E as readCredentials, s as credentialRelayProblem, i as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, m as decodeBase64, I as writeCredentialsLegacy, J as writeCredentialsDataKey, d as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, K as decryptWithDataKey, L as decryptLegacy, M as persistSession, k as decrypt, f as ensurePrivateDirectorySync, N as acquireDaemonLock, O as deletePersistedSessions, P as writeDaemonState, Q as writeSettings, R as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES, S as clearCredentials, T as clearMachineId, U as SandboxConfigSchema, V as getLatestDaemonLog } from './limits-BNTLh2lw.mjs';
4
+ import { j as getProjectPath, g as BUILTIN_TODO_DISCOVERY, p as projectPath, k as initializeSandbox, w as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, h as delay, l as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, m as commandOnPath, n as deriveLocalCliUpdateSummary, o as isClaudeSessionId, q as getLocalHappyAgentCredentialPath, r as readLocalHappyAgentCredentials, t as parseExactVersion, u as compareExactVersions, v as extractCompleteLines, x as decideBackfill, y as parseTerminalHookPayload, z as tmuxArgs, C as scrubTmuxClientEnv, D as decideMirrorBinding, E as MIRROR_BACKFILL_LINES_DEFAULT, F as mirrorLineKey, G as mirrorLocalId, H as detectCLIAvailability, J as detectResumeSupport, K as summarizeSpawnSessionForLog, L as shellescape, A as ApiClient, N as fetchCliUpdateState, O as resolveCliUpdateCheckInterval, P as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, i as isValidSessionId, Q as listVhTerminals, T as isValidTerminalId, U as readVhTerminal, V as sendToVhTerminal, W as TITLE_PROMPT_PREFIX, X as BOARD_ANALYZER_PROMPT_PREFIX, f as connectionState, Y as startOfflineReconnection, c as createEnvelope, Z as CodexAppServerClient } from './types-B-ySoSY9.mjs';
5
+ import { z as readDaemonState, l as logger, c as configuration, A as clearDaemonState, B as ensurePrivateDirectory, D as hardenPrivateFile, p as packageJson, g as readSettings, E as readCredentials, s as credentialRelayProblem, i as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, m as decodeBase64, I as writeCredentialsLegacy, J as writeCredentialsDataKey, d as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, K as decryptWithDataKey, L as decryptLegacy, M as persistSession, k as decrypt, f as ensurePrivateDirectorySync, N as acquireDaemonLock, O as deletePersistedSessions, P as writeDaemonState, Q as writeSettings, R as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES, S as clearCredentials, T as clearMachineId, U as SandboxConfigSchema, V as getLatestDaemonLog } from './limits-CIcZA0Bn.mjs';
6
6
  import spawn$1, { spawn } from 'cross-spawn';
7
7
  import path, { join, resolve, basename, isAbsolute, dirname, sep, delimiter, normalize } from 'node:path';
8
8
  import { createInterface } from 'node:readline';
9
9
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, unlinkSync, renameSync, writeFileSync, accessSync, constants, openSync, fsyncSync, closeSync, rmSync as rmSync$1, chmodSync as chmodSync$1 } from 'node:fs';
10
- import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-BPGPaNlo.mjs';
10
+ import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-7pJxf_3b.mjs';
11
11
  import { readFile, writeFile, rename, unlink, mkdir, symlink, rm, readlink, lstat, stat, open as open$1, appendFile } from 'node:fs/promises';
12
12
  import fs, { watch, access } from 'fs/promises';
13
13
  import { useStdout, useInput, Box, Text, render } from 'ink';
@@ -2046,6 +2046,20 @@ async function claudeRemote(opts) {
2046
2046
  prompt: messages,
2047
2047
  options: sdkOptions
2048
2048
  });
2049
+ let models;
2050
+ if (typeof response.supportedModels === "function") {
2051
+ try {
2052
+ models = (await response.supportedModels()).map((model) => ({
2053
+ code: model.value,
2054
+ value: model.displayName,
2055
+ description: model.description,
2056
+ resolvedModel: model.resolvedModel,
2057
+ ...model.supportsEffort === false ? { reasoningEfforts: [] } : model.supportedEffortLevels ? { reasoningEfforts: model.supportedEffortLevels } : {}
2058
+ }));
2059
+ } catch (error) {
2060
+ logger.debug("[claudeRemote] Model capability discovery unavailable");
2061
+ }
2062
+ }
2049
2063
  if (opts.onQueryReady) {
2050
2064
  opts.onQueryReady({
2051
2065
  setPermissionMode: (mode2) => response.setPermissionMode(mode2),
@@ -2097,6 +2111,7 @@ async function claudeRemote(opts) {
2097
2111
  const systemInit = message;
2098
2112
  if (opts.onSDKMetadata) {
2099
2113
  opts.onSDKMetadata({
2114
+ models,
2100
2115
  tools: systemInit.tools,
2101
2116
  slashCommands: systemInit.slash_commands,
2102
2117
  mcpServers: systemInit.mcp_servers?.map((s) => ({ name: s.name, status: s.status })),
@@ -3380,6 +3395,16 @@ class StreamRelay {
3380
3395
  this.flushPending();
3381
3396
  this.closeOpenBlocks();
3382
3397
  this.currentMid = typeof mid === "string" && mid.length > 0 ? mid : null;
3398
+ const usage = e.message?.usage;
3399
+ if (usage || ["inputTokens", "cacheTokens", "outputTokens", "thinkingTokens"].some((key) => key in this.progress)) {
3400
+ const count = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
3401
+ this.setProgress({
3402
+ inputTokens: count(usage?.input_tokens),
3403
+ cacheTokens: count(usage?.cache_creation_input_tokens) + count(usage?.cache_read_input_tokens),
3404
+ outputTokens: count(usage?.output_tokens),
3405
+ thinkingTokens: 0
3406
+ });
3407
+ }
3383
3408
  return;
3384
3409
  }
3385
3410
  if (e.type === "content_block_start") {
@@ -3670,6 +3695,7 @@ function stripAttachmentManifest(text) {
3670
3695
  function applyClaudeSdkMetadata(current, update) {
3671
3696
  return {
3672
3697
  ...current,
3698
+ ...update.models ? { models: update.models } : {},
3673
3699
  tools: update.tools,
3674
3700
  slashCommands: update.slashCommands,
3675
3701
  mcpServers: update.mcpServers,
@@ -5271,6 +5297,11 @@ ${typeLabels[type] || type}:`));
5271
5297
  console.log(chalk.green("\n\u2705 Doctor diagnosis complete!\n"));
5272
5298
  }
5273
5299
 
5300
+ function sanitizeSpawnModel(value) {
5301
+ if (typeof value !== "string") return null;
5302
+ return /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}(\[1m\])?$/i.test(value) ? value : null;
5303
+ }
5304
+
5274
5305
  const SPAWN_AGENTS = ["claude", "codex", "gemini", "openclaw", "pi"];
5275
5306
  function isSpawnAgent(value) {
5276
5307
  return typeof value === "string" && SPAWN_AGENTS.includes(value);
@@ -7033,6 +7064,12 @@ function parsePiRunnerArgs(args) {
7033
7064
  parsed.verbose = true;
7034
7065
  continue;
7035
7066
  }
7067
+ if (arg === "--model") {
7068
+ const model = sanitizeSpawnModel(args[++i]);
7069
+ if (!model) throw new Error("Invalid --model identifier");
7070
+ parsed.model = model;
7071
+ continue;
7072
+ }
7036
7073
  if (arg === "--permission-mode") {
7037
7074
  const mode = normalizeAcpPermissionMode(args[++i]);
7038
7075
  if (mode) parsed.permissionMode = mode;
@@ -9923,6 +9960,7 @@ async function startDaemon() {
9923
9960
  const currentCliAvailability = () => apiMachineRef?.getCLIAvailability() ?? startupCliAvailability;
9924
9961
  const spawnSessionImpl = async (options) => {
9925
9962
  logger.debugLargeJson("[DAEMON RUN] Spawning session", summarizeSpawnSessionForLog(options));
9963
+ const spawnModel = options.agent === "pi" ? sanitizeSpawnModel(options.model) : null;
9926
9964
  const spawnPermissionMode = sanitizeSpawnPermissionMode(options.permissionMode);
9927
9965
  if (options.permissionMode !== void 0 && spawnPermissionMode === null) {
9928
9966
  logger.warn("[DAEMON RUN] Ignoring invalid permissionMode in spawn request");
@@ -10147,7 +10185,8 @@ async function startDaemon() {
10147
10185
  const resumeId = agent === "claude" ? options.resumeClaudeSessionId : agent === "codex" ? options.resumeCodexThreadId : void 0;
10148
10186
  const resumeFragment = resumeId ? ` --resume ${shellescape(resumeId)}` : "";
10149
10187
  const permissionModeFragment = spawnPermissionMode ? ` --permission-mode ${shellescape(spawnPermissionMode)}` : "";
10150
- const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${agent} --happy-starting-mode remote --started-by daemon${resumeFragment}${permissionModeFragment}`;
10188
+ const modelFragment = spawnModel ? ` --model ${shellescape(spawnModel)}` : "";
10189
+ const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${agent} --happy-starting-mode remote --started-by daemon${resumeFragment}${permissionModeFragment}${modelFragment}`;
10151
10190
  const windowName = `happy-${Date.now()}-${agent}`;
10152
10191
  const tmuxEnv = {};
10153
10192
  for (const [key, value] of Object.entries(process.env)) {
@@ -10240,6 +10279,7 @@ async function startDaemon() {
10240
10279
  if (options.resumeCodexThreadId && agentCommand === "codex") {
10241
10280
  args.push("--resume", options.resumeCodexThreadId);
10242
10281
  }
10282
+ if (spawnModel) args.push("--model", spawnModel);
10243
10283
  if (spawnPermissionMode) {
10244
10284
  args.push("--permission-mode", spawnPermissionMode);
10245
10285
  }
@@ -12023,7 +12063,7 @@ function registerSideQuestionHandler(rpc, deps) {
12023
12063
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12024
12064
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12025
12065
  const run = deps.run ?? (async (input) => {
12026
- const { query } = await import('./index-35FahgEU.mjs');
12066
+ const { query } = await import('./index-C13U8MkT.mjs');
12027
12067
  return runSideQuestion(query, input);
12028
12068
  });
12029
12069
  const slots = /* @__PURE__ */ new Map();
@@ -14767,6 +14807,23 @@ async function ensureDaemonRunning() {
14767
14807
  logger.debug(`Happy background service did not become ready within ${DAEMON_READY_TIMEOUT_MS}ms; continuing anyway`);
14768
14808
  }
14769
14809
 
14810
+ function registerAgentAttachmentDownloads(session) {
14811
+ session.onFileEvent((fileEvent) => {
14812
+ const attachment = fileEvent.content.data.ev;
14813
+ const download = (async () => {
14814
+ try {
14815
+ const data = await session.downloadAndDecryptAttachment(attachment.ref);
14816
+ if (!data) throw new Error("Attachment decryption failed");
14817
+ return { data, mimeType: attachment.mimeType ?? "image/jpeg", name: attachment.name };
14818
+ } catch {
14819
+ session.sendSessionEvent({ type: "message", message: `Could not download attachment: ${attachment.name}. Please attach it again.` });
14820
+ return null;
14821
+ }
14822
+ })();
14823
+ session.trackAttachmentDownload(download);
14824
+ });
14825
+ }
14826
+
14770
14827
  class BasePermissionHandler {
14771
14828
  pendingRequests = /* @__PURE__ */ new Map();
14772
14829
  session;
@@ -15406,6 +15463,11 @@ function createOfflineSessionStub(sessionTag) {
15406
15463
  },
15407
15464
  onUserMessage: () => {
15408
15465
  },
15466
+ onFileEvent: () => {
15467
+ },
15468
+ trackAttachmentDownload: () => {
15469
+ },
15470
+ drainAttachmentsForUserMessage: async () => [],
15409
15471
  rpcHandlerManager: {
15410
15472
  registerHandler: () => {
15411
15473
  }
@@ -15456,8 +15518,8 @@ function resolveCodexExecutionPolicy(permissionMode, sandboxManagedByHappy) {
15456
15518
  return "never";
15457
15519
  // Never ask, read-only enforced by sandbox
15458
15520
  case "safe-yolo":
15459
- return "on-failure";
15460
- // Auto-run, ask only on failure
15521
+ return "on-request";
15522
+ // Workspace auto-run; request approval for escalation (0.153 removed on-failure)
15461
15523
  case "yolo":
15462
15524
  return "never";
15463
15525
  // Full YOLO: never interrupt for approvals
@@ -16053,10 +16115,10 @@ function isCodexClearText(text) {
16053
16115
  }
16054
16116
  function enqueueCodexUserText(opts) {
16055
16117
  if (isCodexClearText(opts.text)) {
16056
- opts.queue.pushIsolateAndClear(opts.text, opts.mode, void 0, opts.sourceId);
16118
+ opts.queue.pushIsolateAndClear(opts.text, opts.mode, opts.attachments, opts.sourceId);
16057
16119
  return "clear";
16058
16120
  }
16059
- opts.queue.push(opts.text, opts.mode, void 0, opts.sourceId);
16121
+ opts.queue.push(opts.text, opts.mode, opts.attachments, opts.sourceId);
16060
16122
  return "queued";
16061
16123
  }
16062
16124
 
@@ -16180,6 +16242,7 @@ async function runCodex(opts) {
16180
16242
  ...forkedFromSessionId ? { parentSessionId: forkedFromSessionId } : {},
16181
16243
  ...forkedFromMessageId ? { forkedFromMessageId } : {}
16182
16244
  });
16245
+ metadata.attachmentKinds = [...CLAUDE_ATTACHMENT_KINDS];
16183
16246
  const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
16184
16247
  const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
16185
16248
  const reconnectVariant = process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT;
@@ -16287,76 +16350,88 @@ async function runCodex(opts) {
16287
16350
  "safe-yolo",
16288
16351
  "yolo"
16289
16352
  ];
16353
+ let discoveredModels = [];
16290
16354
  const VALID_REMOTE_EFFORTS = [
16291
16355
  "none",
16292
16356
  "minimal",
16293
16357
  "low",
16294
16358
  "medium",
16295
16359
  "high",
16296
- "xhigh"
16360
+ "xhigh",
16361
+ "max"
16297
16362
  ];
16363
+ registerAgentAttachmentDownloads(session);
16364
+ let userMessageDelivery = Promise.resolve();
16298
16365
  session.onUserMessage((message) => {
16299
- let messagePermissionMode = currentPermissionMode;
16300
- if (message.meta?.permissionMode) {
16301
- const incoming = message.meta.permissionMode;
16302
- if (VALID_REMOTE_PERMISSION_MODES.includes(incoming)) {
16303
- messagePermissionMode = incoming;
16304
- currentPermissionMode = messagePermissionMode;
16305
- logger.debug("[Codex] Valid permission mode override applied");
16366
+ const downloads = session.drainAttachmentsForUserMessage();
16367
+ userMessageDelivery = userMessageDelivery.then(async () => {
16368
+ const attachments = await downloads;
16369
+ let messagePermissionMode = currentPermissionMode;
16370
+ if (message.meta?.permissionMode) {
16371
+ const incoming = message.meta.permissionMode;
16372
+ if (VALID_REMOTE_PERMISSION_MODES.includes(incoming)) {
16373
+ messagePermissionMode = incoming;
16374
+ currentPermissionMode = messagePermissionMode;
16375
+ logger.debug("[Codex] Valid permission mode override applied");
16376
+ } else {
16377
+ logger.debug("[Codex] Ignoring invalid permission mode override", logValueMetadata(message.meta.permissionMode));
16378
+ }
16306
16379
  } else {
16307
- logger.debug("[Codex] Ignoring invalid permission mode override", logValueMetadata(message.meta.permissionMode));
16380
+ logger.debug("[Codex] User message received without a permission mode override");
16308
16381
  }
16309
- } else {
16310
- logger.debug("[Codex] User message received without a permission mode override");
16311
- }
16312
- let messageModel = currentModel;
16313
- if (message.meta?.hasOwnProperty("model")) {
16314
- messageModel = message.meta.model || void 0;
16315
- currentModel = messageModel;
16316
- logger.debug("[Codex] Model override updated", logValueMetadata(message.meta.model));
16317
- } else {
16318
- logger.debug("[Codex] User message received without a model override");
16319
- }
16320
- let messageEffort = currentEffort;
16321
- if (message.meta?.hasOwnProperty("effort")) {
16322
- const incoming = message.meta.effort;
16323
- if (incoming === null || incoming === void 0) {
16324
- messageEffort = void 0;
16325
- currentEffort = void 0;
16326
- logger.debug(`[Codex] Effort reset to default`);
16327
- } else if (typeof incoming === "string" && VALID_REMOTE_EFFORTS.includes(incoming)) {
16328
- messageEffort = incoming;
16329
- currentEffort = messageEffort;
16330
- logger.debug("[Codex] Valid effort override applied");
16382
+ let messageModel = currentModel;
16383
+ if (message.meta?.hasOwnProperty("model")) {
16384
+ messageModel = message.meta.model || void 0;
16385
+ currentModel = messageModel;
16386
+ logger.debug("[Codex] Model override updated", logValueMetadata(message.meta.model));
16387
+ } else {
16388
+ logger.debug("[Codex] User message received without a model override");
16389
+ }
16390
+ let messageEffort = currentEffort;
16391
+ if (message.meta?.hasOwnProperty("effort")) {
16392
+ const incoming = message.meta.effort;
16393
+ if (incoming === null || incoming === void 0) {
16394
+ messageEffort = void 0;
16395
+ currentEffort = void 0;
16396
+ logger.debug(`[Codex] Effort reset to default`);
16397
+ } else if (typeof incoming === "string") {
16398
+ messageEffort = incoming;
16399
+ currentEffort = messageEffort;
16400
+ logger.debug("[Codex] Valid effort override applied");
16401
+ } else {
16402
+ logger.debug("[Codex] Ignoring invalid effort override", logValueMetadata(incoming));
16403
+ }
16331
16404
  } else {
16332
- logger.debug("[Codex] Ignoring invalid effort override", logValueMetadata(incoming));
16405
+ logger.debug("[Codex] User message received without an effort override");
16333
16406
  }
16334
- } else {
16335
- logger.debug("[Codex] User message received without an effort override");
16336
- }
16337
- let messageAppendSystemPrompt = currentAppendSystemPrompt;
16338
- if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
16339
- messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
16340
- currentAppendSystemPrompt = messageAppendSystemPrompt;
16341
- logger.debug("[Codex] Append system prompt override updated", logValueMetadata(message.meta.appendSystemPrompt));
16342
- } else {
16343
- logger.debug("[Codex] User message received without an append system prompt override");
16344
- }
16345
- const enhancedMode = {
16346
- permissionMode: messagePermissionMode || "default",
16347
- model: messageModel,
16348
- appendSystemPrompt: messageAppendSystemPrompt,
16349
- effort: messageEffort
16350
- };
16351
- const enqueueResult = enqueueCodexUserText({
16352
- text: message.content.text,
16353
- mode: enhancedMode,
16354
- queue: messageQueue,
16355
- sourceId: message.localKey
16407
+ let messageAppendSystemPrompt = currentAppendSystemPrompt;
16408
+ if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
16409
+ messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
16410
+ currentAppendSystemPrompt = messageAppendSystemPrompt;
16411
+ logger.debug("[Codex] Append system prompt override updated", logValueMetadata(message.meta.appendSystemPrompt));
16412
+ } else {
16413
+ logger.debug("[Codex] User message received without an append system prompt override");
16414
+ }
16415
+ const enhancedMode = {
16416
+ permissionMode: messagePermissionMode || "default",
16417
+ model: messageModel,
16418
+ appendSystemPrompt: messageAppendSystemPrompt,
16419
+ effort: messageEffort
16420
+ };
16421
+ const enqueueResult = enqueueCodexUserText({
16422
+ text: message.content.text,
16423
+ mode: enhancedMode,
16424
+ queue: messageQueue,
16425
+ attachments,
16426
+ sourceId: message.localKey
16427
+ });
16428
+ if (enqueueResult === "clear") {
16429
+ logger.debug("[Codex] /clear command pushed to isolated queue");
16430
+ }
16431
+ }).catch((error) => {
16432
+ logger.warn("[Codex] Failed to prepare user attachments", safeCodexErrorMetadata(error));
16433
+ session.sendSessionEvent({ type: "message", message: "Could not prepare this message. Please send it again." });
16356
16434
  });
16357
- if (enqueueResult === "clear") {
16358
- logger.debug("[Codex] /clear command pushed to isolated queue");
16359
- }
16360
16435
  });
16361
16436
  let thinking = false;
16362
16437
  let currentTurnId = null;
@@ -16632,6 +16707,24 @@ async function runCodex(opts) {
16632
16707
  logger.debug("[codex]: client.connect begin");
16633
16708
  await client.connect();
16634
16709
  logger.debug("[codex]: client.connect done");
16710
+ try {
16711
+ discoveredModels = await client.listModels();
16712
+ if (discoveredModels.length > 0) {
16713
+ session.updateMetadata((metadata2) => ({
16714
+ ...metadata2,
16715
+ models: discoveredModels.map((model) => ({
16716
+ code: model.model,
16717
+ value: model.displayName,
16718
+ description: model.description,
16719
+ reasoningEfforts: model.supportedReasoningEfforts.map((option) => option.reasoningEffort),
16720
+ defaultReasoningEffort: model.defaultReasoningEffort
16721
+ })),
16722
+ defaultModelCode: discoveredModels.find((model) => model.isDefault)?.model
16723
+ }));
16724
+ }
16725
+ } catch (error) {
16726
+ logger.debug("[Codex] Model discovery unavailable; using compatibility fallback", safeCodexErrorMetadata(error));
16727
+ }
16635
16728
  if (opts.resumeThreadId) {
16636
16729
  await resumeExistingThread({
16637
16730
  client,
@@ -16720,6 +16813,11 @@ async function runCodex(opts) {
16720
16813
  message.mode.permissionMode,
16721
16814
  sandboxManagedByHappy
16722
16815
  );
16816
+ const selectedModel = discoveredModels.find((model) => message.mode.model ? model.model === message.mode.model : model.isDefault);
16817
+ const supportedEfforts = selectedModel?.supportedReasoningEfforts.map((option) => option.reasoningEffort) ?? VALID_REMOTE_EFFORTS;
16818
+ if (message.mode.effort && !supportedEfforts.includes(message.mode.effort)) {
16819
+ throw new Error(`Reasoning effort "${message.mode.effort}" is not supported by ${message.mode.model ?? "the default model"}. Choose a supported level.`);
16820
+ }
16723
16821
  if (!client.hasActiveThread()) {
16724
16822
  const startedThread = await client.startThread({
16725
16823
  model: message.mode.model,
@@ -16742,7 +16840,12 @@ async function runCodex(opts) {
16742
16840
  includeAppendSystemPrompt,
16743
16841
  includeTitleInstruction: first
16744
16842
  });
16745
- const result = await client.sendTurnAndWait(turnPrompt, {
16843
+ const staged = message.attachments?.length ? await stageClaudeAttachments(message.attachments, {
16844
+ happyHomeDir: configuration.happyHomeDir,
16845
+ sessionId: session.sessionId
16846
+ }) : [];
16847
+ const result = await client.sendTurnAndWait(appendStagedAttachmentsToPrompt(turnPrompt, staged), {
16848
+ images: staged.filter((file) => ["image/jpeg", "image/png", "image/gif", "image/webp"].includes(file.mimeType)).map((file) => file.path),
16746
16849
  model: message.mode.model,
16747
16850
  approvalPolicy: executionPolicy.approvalPolicy,
16748
16851
  sandbox: executionPolicy.sandbox,
@@ -16977,7 +17080,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16977
17080
  process.exit(0);
16978
17081
  } else if (subcommand === "install-terminal-hooks") {
16979
17082
  try {
16980
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-ClrFe2fA.mjs');
17083
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-kUC6I6Rn.mjs');
16981
17084
  const command = parseTerminalHooksArgs(args.slice(1));
16982
17085
  if (command.action === "help") {
16983
17086
  console.log(TERMINAL_HOOKS_HELP);
@@ -16994,7 +17097,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16994
17097
  }
16995
17098
  } else if (subcommand === "spawn") {
16996
17099
  try {
16997
- const { handleSpawnCommand } = await import('./spawn-DV4pIAHZ.mjs');
17100
+ const { handleSpawnCommand } = await import('./spawn-CJxg1JUO.mjs');
16998
17101
  await handleSpawnCommand(args.slice(1));
16999
17102
  } catch (error) {
17000
17103
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17006,7 +17109,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17006
17109
  return;
17007
17110
  } else if (subcommand === "sessions") {
17008
17111
  try {
17009
- const { handleSessionsCommand } = await import('./sessions-DIOdWnPe.mjs');
17112
+ const { handleSessionsCommand } = await import('./sessions-Bo2THj4y.mjs');
17010
17113
  await handleSessionsCommand(args.slice(1));
17011
17114
  } catch (error) {
17012
17115
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17018,7 +17121,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17018
17121
  return;
17019
17122
  } else if (subcommand === "send") {
17020
17123
  try {
17021
- const { handleSendCommand } = await import('./send-DQS1zNWp.mjs');
17124
+ const { handleSendCommand } = await import('./send-BdhUFGTm.mjs');
17022
17125
  await handleSendCommand(args.slice(1));
17023
17126
  } catch (error) {
17024
17127
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17124,9 +17227,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17124
17227
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
17125
17228
  const projectId = args[3];
17126
17229
  try {
17127
- const { saveGoogleCloudProjectToConfig } = await import('./config-DSBE0Az0.mjs');
17128
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-BNTLh2lw.mjs').then(function (n) { return n.W; });
17129
- const { ApiClient: ApiClient2 } = await import('./types-CFs8FmiY.mjs').then(function (n) { return n._; });
17230
+ const { saveGoogleCloudProjectToConfig } = await import('./config-BWu8j7bO.mjs');
17231
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-CIcZA0Bn.mjs').then(function (n) { return n.W; });
17232
+ const { ApiClient: ApiClient2 } = await import('./types-B-ySoSY9.mjs').then(function (n) { return n._; });
17130
17233
  let userEmail = void 0;
17131
17234
  try {
17132
17235
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -17157,7 +17260,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17157
17260
  }
17158
17261
  if (geminiSubcommand === "project" && args[2] === "get") {
17159
17262
  try {
17160
- const { readGeminiLocalConfig } = await import('./config-DSBE0Az0.mjs');
17263
+ const { readGeminiLocalConfig } = await import('./config-BWu8j7bO.mjs');
17161
17264
  const config = readGeminiLocalConfig();
17162
17265
  if (config.googleCloudProject) {
17163
17266
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -17197,7 +17300,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17197
17300
  process.exit(0);
17198
17301
  }
17199
17302
  try {
17200
- const { runGemini } = await import('./runGemini-B9S85Vms.mjs');
17303
+ const { runGemini } = await import('./runGemini-BQd3qlVY.mjs');
17201
17304
  let startedBy = void 0;
17202
17305
  for (let i = 1; i < args.length; i++) {
17203
17306
  if (args[i] === "--started-by") {
@@ -17227,7 +17330,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17227
17330
  }
17228
17331
  };
17229
17332
  try {
17230
- const { runAcp, resolveAcpAgentConfig } = await import('./index-m0Dnccc_.mjs');
17333
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-CdOVizh_.mjs');
17231
17334
  const parsed = parsePiRunnerArgs(args.slice(1));
17232
17335
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17233
17336
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17237,6 +17340,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17237
17340
  startedBy: parsed.startedBy,
17238
17341
  verbose: parsed.verbose,
17239
17342
  permissionMode: parsed.permissionMode,
17343
+ model: parsed.model,
17240
17344
  agentName: resolved.agentName,
17241
17345
  command: resolved.command,
17242
17346
  args: resolved.args,
@@ -17254,7 +17358,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17254
17358
  return;
17255
17359
  } else if (subcommand === "acp") {
17256
17360
  try {
17257
- const { runAcp, resolveAcpAgentConfig } = await import('./index-m0Dnccc_.mjs');
17361
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-CdOVizh_.mjs');
17258
17362
  let startedBy = void 0;
17259
17363
  let verbose = false;
17260
17364
  const acpArgs = [];
@@ -17294,7 +17398,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17294
17398
  return;
17295
17399
  } else if (subcommand === "openclaw") {
17296
17400
  try {
17297
- const { runOpenClaw } = await import('./runOpenClaw-Byhq-B1y.mjs');
17401
+ const { runOpenClaw } = await import('./runOpenClaw-BgtE2jKF.mjs');
17298
17402
  let startedBy = void 0;
17299
17403
  let verbose = false;
17300
17404
  let gatewayUrl;
@@ -17345,7 +17449,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17345
17449
  return;
17346
17450
  } else if (subcommand === "todo") {
17347
17451
  try {
17348
- const { handleTodoCommand } = await import('./todo-BwXURAbQ.mjs');
17452
+ const { handleTodoCommand } = await import('./todo-C8hDSNBP.mjs');
17349
17453
  await handleTodoCommand(args.slice(1));
17350
17454
  } catch (error) {
17351
17455
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -17354,7 +17458,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17354
17458
  return;
17355
17459
  } else if (subcommand === "teams") {
17356
17460
  try {
17357
- const { handleTeamsCommand } = await import('./teams-DlTHOiCk.mjs');
17461
+ const { handleTeamsCommand } = await import('./teams-CHGj90rR.mjs');
17358
17462
  await handleTeamsCommand(args.slice(1));
17359
17463
  } catch (error) {
17360
17464
  console.error(error instanceof Error ? error.message : "Teams command failed");
@@ -17363,7 +17467,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17363
17467
  return;
17364
17468
  } else if (subcommand === "mcp" && args.length === 1) {
17365
17469
  try {
17366
- const { handleMcpCommand } = await import('./mcp-BsohER5A.mjs');
17470
+ const { handleMcpCommand } = await import('./mcp-D5uyhbfB.mjs');
17367
17471
  await handleMcpCommand();
17368
17472
  } catch (error) {
17369
17473
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -17725,4 +17829,4 @@ ${chalk.bold("Examples:")}
17725
17829
  }
17726
17830
  }
17727
17831
 
17728
- export { ALLOWED_SPAWN_PERMISSION_MODES as A, hashObject as B, startHappyServer as C, DEFAULT_RECENT_LIMIT as D, TitleGenerator as E, BasePermissionHandler as F, GEMINI_MODEL_ENV as G, writeSessionModeFile as H, normalizeAcpPermissionMode as I, removeSessionModeFile as J, GEMINI_API_KEY_ENV as K, GOOGLE_API_KEY_ENV as L, MAX_READ_LIMIT as M, BaseReasoningProcessor as N, MessageBuffer as O, PI_TEAMS_EXTENSION as P, CHANGE_TITLE_INSTRUCTION as Q, SPAWN_AGENTS as S, TEAM_SKILL 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, registerKillSessionHandler as y, StreamRelay as z };
17832
+ export { ALLOWED_SPAWN_PERMISSION_MODES as A, hashObject as B, startHappyServer as C, DEFAULT_RECENT_LIMIT as D, registerAgentAttachmentDownloads as E, TitleGenerator as F, GEMINI_MODEL_ENV as G, CLAUDE_ATTACHMENT_KINDS as H, stageClaudeAttachments as I, appendStagedAttachmentsToPrompt as J, BasePermissionHandler as K, writeSessionModeFile as L, MAX_READ_LIMIT as M, normalizeAcpPermissionMode as N, removeSessionModeFile as O, PI_TEAMS_EXTENSION as P, GEMINI_API_KEY_ENV as Q, GOOGLE_API_KEY_ENV as R, SPAWN_AGENTS as S, TEAM_SKILL as T, BaseReasoningProcessor as U, MessageBuffer as V, CHANGE_TITLE_INSTRUCTION as W, 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, registerKillSessionHandler as y, StreamRelay as z };
@@ -1,11 +1,11 @@
1
- import { q as query } from './index-CfM6ewd8.mjs';
1
+ import { q as query } from './index-BUhcXX8R.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-CFs8FmiY.mjs';
6
+ import './types-B-ySoSY9.mjs';
7
7
  import 'axios';
8
- import './limits-BNTLh2lw.mjs';
8
+ import './limits-CIcZA0Bn.mjs';
9
9
  import 'node:fs/promises';
10
10
  import 'node:fs';
11
11
  import 'node:path';
@@ -29,7 +29,7 @@ import 'fs';
29
29
  import 'node:readline';
30
30
  import '@anthropic-ai/sandbox-runtime';
31
31
  import 'expo-server-sdk';
32
- import './agentGuidance-BPGPaNlo.mjs';
32
+ import './agentGuidance-7pJxf_3b.mjs';
33
33
  import 'ink';
34
34
  import 'react';
35
35
  import 'ps-list';