very-happy-cli 0.2.116 → 0.2.117

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 (37) hide show
  1. package/dist/{AcpBackend-CfKPvoTU.mjs → AcpBackend-C4RMHt0I.mjs} +1 -1
  2. package/dist/{AcpBackend-CauAQpC1.cjs → AcpBackend-CrbXvcgQ.cjs} +1 -1
  3. package/dist/{AcpSessionManager-CA6xeET_.cjs → AcpSessionManager-CjAVO4RX.cjs} +1 -1
  4. package/dist/{AcpSessionManager-BQ6pBRW4.mjs → AcpSessionManager-Cnc8Jatl.mjs} +1 -1
  5. package/dist/{config-BpR_CwZu.mjs → config-CnA3JIUn.mjs} +2 -2
  6. package/dist/{config-BoteFn-v.cjs → config-O9quMfxg.cjs} +2 -2
  7. package/dist/{index-D7azxfcs.mjs → index-B0o3H6_x.mjs} +2 -2
  8. package/dist/{index-Dq-eFp89.mjs → index-BYZKuQoy.mjs} +499 -42
  9. package/dist/{index-4jiUeiUz.cjs → index-DcESl4cI.cjs} +510 -44
  10. package/dist/{index-C45xN8s0.mjs → index-DiRN0Rv8.mjs} +78 -8
  11. package/dist/{index-lX1UliOs.cjs → index-DqmVH6R5.cjs} +2 -2
  12. package/dist/{index-DWmhmji9.cjs → index-W-O4xR_O.cjs} +77 -7
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-k3TrBHD0.mjs → installTerminalHooks-DeYqyjCB.mjs} +2 -2
  16. package/dist/{installTerminalHooks-DnaFkrTF.cjs → installTerminalHooks-zfsWSotO.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.d.cts +41 -1
  19. package/dist/lib.d.mts +41 -1
  20. package/dist/lib.mjs +1 -1
  21. package/dist/{mcp-DRePTOap.mjs → mcp-D_5aOK0I.mjs} +62 -17
  22. package/dist/{mcp-DSpEpM_2.cjs → mcp-nYqC4FYm.cjs} +62 -16
  23. package/dist/{runGemini-_9-a7duU.mjs → runGemini-CEURRmM4.mjs} +4 -4
  24. package/dist/{runGemini-IWHyBq0A.cjs → runGemini-DYf3ntib.cjs} +4 -4
  25. package/dist/{runOpenClaw-Boq6LQ9a.mjs → runOpenClaw-BozfKWng.mjs} +3 -3
  26. package/dist/{runOpenClaw-0myYIz1U.cjs → runOpenClaw-CNKjAw-H.cjs} +3 -3
  27. package/dist/{send-CwWvFfay.cjs → send-BZLhRZb-.cjs} +2 -2
  28. package/dist/{send-Cyo6mgMl.mjs → send-CYznZGrp.mjs} +2 -2
  29. package/dist/sessions-DYPB3Peg.mjs +317 -0
  30. package/dist/sessions-YWt9YEN8.cjs +322 -0
  31. package/dist/{spawn-CU0DMauA.mjs → spawn-CsrHZd3y.mjs} +3 -4
  32. package/dist/{spawn-DQ6FTUK9.cjs → spawn-tSvo3stN.cjs} +6 -7
  33. package/dist/{types-C5kEpkcZ.cjs → types-DD3AK6Nb.cjs} +47 -19
  34. package/dist/{types-Ds0SiQ1D.mjs → types-jSfU_0o1.mjs} +45 -19
  35. package/package.json +7 -7
  36. package/dist/sessions-CbzqYiAy.cjs +0 -204
  37. package/dist/sessions-lvfTH_ql.mjs +0 -200
@@ -1,10 +1,10 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-CfKPvoTU.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-CfKPvoTU.mjs';
3
- import { h as connectionState, A as ApiClient, j as readSettings, k as encodeBase64, l as logger, p as projectPath } from './types-Ds0SiQ1D.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-BQ6pBRW4.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-C4RMHt0I.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-C4RMHt0I.mjs';
3
+ import { h as connectionState, A as ApiClient, j as readSettings, k as encodeBase64, l as logger, p as projectPath } from './types-jSfU_0o1.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-Cnc8Jatl.mjs';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { join } from 'node:path';
7
- import { h as initialMachineMetadata, j as createSessionMetadata, k as setupOfflineReconnection, n as notifyDaemonSessionStarted, m as MessageQueue2, o as hashObject, u as startHappyServer, v as registerKillSessionHandler, B as BasePermissionHandler } from './index-Dq-eFp89.mjs';
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-BYZKuQoy.mjs';
8
8
  import 'node:child_process';
9
9
  import '@agentclientprotocol/sdk';
10
10
  import 'axios';
@@ -483,12 +483,16 @@ function extractConfigSelector(configOptions, category) {
483
483
  if (option.category === category) {
484
484
  return true;
485
485
  }
486
+ if (option.category) {
487
+ return false;
488
+ }
486
489
  const id = normalizeComparable(option.id);
487
490
  const name = normalizeComparable(option.name);
488
491
  if (category === "model") {
489
492
  return id.includes("model") || name.includes("model");
490
493
  }
491
- return id.includes("mode") || id.includes("permission") || name.includes("mode") || name.includes("permission");
494
+ const mentionsMode = (value) => value.includes("permission") || value.includes("mode") && !value.includes("model");
495
+ return mentionsMode(id) || mentionsMode(name);
492
496
  };
493
497
  for (const option of configOptions) {
494
498
  if (option.type !== "select" || !optionMatchesCategory(option)) {
@@ -643,7 +647,27 @@ async function runAcp(opts) {
643
647
  let sawSlashCommands = false;
644
648
  let sawModes = false;
645
649
  let sawModels = false;
646
- const happyServer = await startHappyServer(session);
650
+ const initialPermissionMode = opts.permissionMode ?? "default";
651
+ let fileBackedModeActive = false;
652
+ const permissionModeIsFileBacked = () => opts.agentName === "pi" || !modeSelector && !legacyModes;
653
+ let fileBackedPermissionMode = null;
654
+ const publishFileBackedPermissionMode = (mode) => {
655
+ if (fileBackedPermissionMode === mode) {
656
+ return true;
657
+ }
658
+ try {
659
+ writeSessionModeFile(session.sessionId, mode);
660
+ } catch (error) {
661
+ logger.debug(`[${opts.agentName}] Failed to write session mode file:`, error);
662
+ return false;
663
+ }
664
+ fileBackedPermissionMode = mode;
665
+ session.updateMetadata((currentMetadata) => ({ ...currentMetadata, permissionMode: mode }));
666
+ logger.debug(`[${opts.agentName}] Published file-backed permission mode: ${mode}`);
667
+ return true;
668
+ };
669
+ const isAssistantVariant = process.env.HAPPY_SESSION_VARIANT === "assistant";
670
+ const happyServer = await startHappyServer(session, { assistant: isAssistantVariant });
647
671
  const mcpServers = {
648
672
  happy: {
649
673
  command: join(projectPath(), "bin", "happy-mcp.mjs"),
@@ -655,6 +679,11 @@ async function runAcp(opts) {
655
679
  cwd: process.cwd(),
656
680
  command: opts.command,
657
681
  args: opts.args,
682
+ env: {
683
+ HAPPY_MCP_URL: happyServer.url,
684
+ HAPPY_SESSION_ID: session.sessionId,
685
+ HAPPY_PERMISSION_MODE: initialPermissionMode
686
+ },
658
687
  mcpServers,
659
688
  permissionHandler,
660
689
  transportHandler: new DefaultTransport(opts.agentName),
@@ -708,6 +737,15 @@ async function runAcp(opts) {
708
737
  if (!requestedMode) {
709
738
  return;
710
739
  }
740
+ if (permissionModeIsFileBacked()) {
741
+ const mode = normalizeAcpPermissionMode(requestedMode);
742
+ if (!mode) {
743
+ logger.debug(`[${opts.agentName}] Ignoring unknown file-backed permission mode request: ${requestedMode}`);
744
+ return;
745
+ }
746
+ publishFileBackedPermissionMode(mode);
747
+ return;
748
+ }
711
749
  if (modeSelector) {
712
750
  const resolved = resolveRequestedCode(modeSelector.options, requestedMode);
713
751
  if (!resolved) {
@@ -905,6 +943,7 @@ async function runAcp(opts) {
905
943
  clearPendingTurn();
906
944
  }
907
945
  if (msg.status === "error" || msg.status === "stopped") {
946
+ if (msg.status === "error") opts.onBackendError?.(msg.detail);
908
947
  stopRunnerFromBackendStatus(msg.status, msg.detail);
909
948
  }
910
949
  }
@@ -918,10 +957,12 @@ async function runAcp(opts) {
918
957
  sendEnvelopes(sessionManager.mapMessage(msg));
919
958
  };
920
959
  backend.onMessage(onBackendMessage);
960
+ const titleGenerator = new TitleGenerator(session);
921
961
  session.onUserMessage((message) => {
922
962
  if (!message.content.text) {
923
963
  return;
924
964
  }
965
+ titleGenerator.maybeGenerate(message.content.text);
925
966
  if (typeof message.meta?.permissionMode === "string") {
926
967
  currentPermissionMode = message.meta.permissionMode;
927
968
  logger.debug(`[${opts.agentName}] Requested ACP permission mode: ${currentPermissionMode}`);
@@ -953,6 +994,22 @@ async function runAcp(opts) {
953
994
  }
954
995
  }
955
996
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
997
+ session.rpcHandlerManager.registerHandler(
998
+ "set-permission-mode",
999
+ async (request) => {
1000
+ if (!fileBackedModeActive) {
1001
+ throw new Error(`${opts.agentName} does not support live permission mode changes`);
1002
+ }
1003
+ const mode = typeof request?.mode === "string" ? normalizeAcpPermissionMode(request.mode) : null;
1004
+ if (!mode) {
1005
+ throw new Error("Invalid permission mode");
1006
+ }
1007
+ if (!publishFileBackedPermissionMode(mode)) {
1008
+ throw new Error("Failed to write session mode file");
1009
+ }
1010
+ return { mode };
1011
+ }
1012
+ );
956
1013
  registerKillSessionHandler(session.rpcHandlerManager, async () => {
957
1014
  shouldExit = true;
958
1015
  messageQueue.close();
@@ -962,6 +1019,10 @@ async function runAcp(opts) {
962
1019
  try {
963
1020
  const started = await backend.startSession();
964
1021
  acpSessionId = started.sessionId;
1022
+ if (permissionModeIsFileBacked()) {
1023
+ fileBackedModeActive = true;
1024
+ publishFileBackedPermissionMode(initialPermissionMode);
1025
+ }
965
1026
  if (verbose) {
966
1027
  if (!sawSlashCommands) {
967
1028
  logAcp("muted", `Outgoing slash commands from ${opts.agentName}: not reported yet`);
@@ -1024,6 +1085,13 @@ async function runAcp(opts) {
1024
1085
  }
1025
1086
  backend.offMessage?.(onBackendMessage);
1026
1087
  await backend.dispose();
1088
+ if (fileBackedPermissionMode) {
1089
+ try {
1090
+ removeSessionModeFile(session.sessionId);
1091
+ } catch (error) {
1092
+ logger.debug(`[${opts.agentName}] Failed to remove session mode file:`, error);
1093
+ }
1094
+ }
1027
1095
  try {
1028
1096
  happyServer.stop();
1029
1097
  } catch (error) {
@@ -1046,9 +1114,11 @@ async function runAcp(opts) {
1046
1114
  }
1047
1115
  }
1048
1116
 
1117
+ const PI_ACP_COMMAND = "pi-acp";
1049
1118
  const KNOWN_ACP_AGENTS = {
1050
1119
  gemini: { command: "gemini", args: ["--experimental-acp"] },
1051
- opencode: { command: "opencode", args: ["acp"] }
1120
+ opencode: { command: "opencode", args: ["acp"] },
1121
+ pi: { command: PI_ACP_COMMAND, args: [] }
1052
1122
  };
1053
1123
  function resolveAcpAgentConfig(cliArgs) {
1054
1124
  if (cliArgs.length === 0) {
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-4jiUeiUz.cjs');
3
+ var index = require('./index-DcESl4cI.cjs');
4
4
  var claudeAgentSdk = require('@anthropic-ai/claude-agent-sdk');
5
5
  require('chalk');
6
6
  require('node:os');
7
7
  require('node:crypto');
8
- require('./types-C5kEpkcZ.cjs');
8
+ require('./types-DD3AK6Nb.cjs');
9
9
  require('axios');
10
10
  require('node:util');
11
11
  require('node:fs');
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-CauAQpC1.cjs');
4
- var persistence = require('./types-C5kEpkcZ.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-CA6xeET_.cjs');
3
+ var AcpBackend = require('./AcpBackend-CrbXvcgQ.cjs');
4
+ var persistence = require('./types-DD3AK6Nb.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-CjAVO4RX.cjs');
6
6
  var node_crypto = require('node:crypto');
7
7
  var path = require('node:path');
8
- var index = require('./index-4jiUeiUz.cjs');
8
+ var index = require('./index-DcESl4cI.cjs');
9
9
  require('node:child_process');
10
10
  require('@agentclientprotocol/sdk');
11
11
  require('axios');
@@ -484,12 +484,16 @@ function extractConfigSelector(configOptions, category) {
484
484
  if (option.category === category) {
485
485
  return true;
486
486
  }
487
+ if (option.category) {
488
+ return false;
489
+ }
487
490
  const id = normalizeComparable(option.id);
488
491
  const name = normalizeComparable(option.name);
489
492
  if (category === "model") {
490
493
  return id.includes("model") || name.includes("model");
491
494
  }
492
- return id.includes("mode") || id.includes("permission") || name.includes("mode") || name.includes("permission");
495
+ const mentionsMode = (value) => value.includes("permission") || value.includes("mode") && !value.includes("model");
496
+ return mentionsMode(id) || mentionsMode(name);
493
497
  };
494
498
  for (const option of configOptions) {
495
499
  if (option.type !== "select" || !optionMatchesCategory(option)) {
@@ -644,7 +648,27 @@ async function runAcp(opts) {
644
648
  let sawSlashCommands = false;
645
649
  let sawModes = false;
646
650
  let sawModels = false;
647
- const happyServer = await index.startHappyServer(session);
651
+ const initialPermissionMode = opts.permissionMode ?? "default";
652
+ let fileBackedModeActive = false;
653
+ const permissionModeIsFileBacked = () => opts.agentName === "pi" || !modeSelector && !legacyModes;
654
+ let fileBackedPermissionMode = null;
655
+ const publishFileBackedPermissionMode = (mode) => {
656
+ if (fileBackedPermissionMode === mode) {
657
+ return true;
658
+ }
659
+ try {
660
+ index.writeSessionModeFile(session.sessionId, mode);
661
+ } catch (error) {
662
+ persistence.logger.debug(`[${opts.agentName}] Failed to write session mode file:`, error);
663
+ return false;
664
+ }
665
+ fileBackedPermissionMode = mode;
666
+ session.updateMetadata((currentMetadata) => ({ ...currentMetadata, permissionMode: mode }));
667
+ persistence.logger.debug(`[${opts.agentName}] Published file-backed permission mode: ${mode}`);
668
+ return true;
669
+ };
670
+ const isAssistantVariant = process.env.HAPPY_SESSION_VARIANT === "assistant";
671
+ const happyServer = await index.startHappyServer(session, { assistant: isAssistantVariant });
648
672
  const mcpServers = {
649
673
  happy: {
650
674
  command: path.join(persistence.projectPath(), "bin", "happy-mcp.mjs"),
@@ -656,6 +680,11 @@ async function runAcp(opts) {
656
680
  cwd: process.cwd(),
657
681
  command: opts.command,
658
682
  args: opts.args,
683
+ env: {
684
+ HAPPY_MCP_URL: happyServer.url,
685
+ HAPPY_SESSION_ID: session.sessionId,
686
+ HAPPY_PERMISSION_MODE: initialPermissionMode
687
+ },
659
688
  mcpServers,
660
689
  permissionHandler,
661
690
  transportHandler: new AcpBackend.DefaultTransport(opts.agentName),
@@ -709,6 +738,15 @@ async function runAcp(opts) {
709
738
  if (!requestedMode) {
710
739
  return;
711
740
  }
741
+ if (permissionModeIsFileBacked()) {
742
+ const mode = index.normalizeAcpPermissionMode(requestedMode);
743
+ if (!mode) {
744
+ persistence.logger.debug(`[${opts.agentName}] Ignoring unknown file-backed permission mode request: ${requestedMode}`);
745
+ return;
746
+ }
747
+ publishFileBackedPermissionMode(mode);
748
+ return;
749
+ }
712
750
  if (modeSelector) {
713
751
  const resolved = resolveRequestedCode(modeSelector.options, requestedMode);
714
752
  if (!resolved) {
@@ -906,6 +944,7 @@ async function runAcp(opts) {
906
944
  clearPendingTurn();
907
945
  }
908
946
  if (msg.status === "error" || msg.status === "stopped") {
947
+ if (msg.status === "error") opts.onBackendError?.(msg.detail);
909
948
  stopRunnerFromBackendStatus(msg.status, msg.detail);
910
949
  }
911
950
  }
@@ -919,10 +958,12 @@ async function runAcp(opts) {
919
958
  sendEnvelopes(sessionManager.mapMessage(msg));
920
959
  };
921
960
  backend.onMessage(onBackendMessage);
961
+ const titleGenerator = new index.TitleGenerator(session);
922
962
  session.onUserMessage((message) => {
923
963
  if (!message.content.text) {
924
964
  return;
925
965
  }
966
+ titleGenerator.maybeGenerate(message.content.text);
926
967
  if (typeof message.meta?.permissionMode === "string") {
927
968
  currentPermissionMode = message.meta.permissionMode;
928
969
  persistence.logger.debug(`[${opts.agentName}] Requested ACP permission mode: ${currentPermissionMode}`);
@@ -954,6 +995,22 @@ async function runAcp(opts) {
954
995
  }
955
996
  }
956
997
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
998
+ session.rpcHandlerManager.registerHandler(
999
+ "set-permission-mode",
1000
+ async (request) => {
1001
+ if (!fileBackedModeActive) {
1002
+ throw new Error(`${opts.agentName} does not support live permission mode changes`);
1003
+ }
1004
+ const mode = typeof request?.mode === "string" ? index.normalizeAcpPermissionMode(request.mode) : null;
1005
+ if (!mode) {
1006
+ throw new Error("Invalid permission mode");
1007
+ }
1008
+ if (!publishFileBackedPermissionMode(mode)) {
1009
+ throw new Error("Failed to write session mode file");
1010
+ }
1011
+ return { mode };
1012
+ }
1013
+ );
957
1014
  index.registerKillSessionHandler(session.rpcHandlerManager, async () => {
958
1015
  shouldExit = true;
959
1016
  messageQueue.close();
@@ -963,6 +1020,10 @@ async function runAcp(opts) {
963
1020
  try {
964
1021
  const started = await backend.startSession();
965
1022
  acpSessionId = started.sessionId;
1023
+ if (permissionModeIsFileBacked()) {
1024
+ fileBackedModeActive = true;
1025
+ publishFileBackedPermissionMode(initialPermissionMode);
1026
+ }
966
1027
  if (verbose) {
967
1028
  if (!sawSlashCommands) {
968
1029
  logAcp("muted", `Outgoing slash commands from ${opts.agentName}: not reported yet`);
@@ -1025,6 +1086,13 @@ async function runAcp(opts) {
1025
1086
  }
1026
1087
  backend.offMessage?.(onBackendMessage);
1027
1088
  await backend.dispose();
1089
+ if (fileBackedPermissionMode) {
1090
+ try {
1091
+ index.removeSessionModeFile(session.sessionId);
1092
+ } catch (error) {
1093
+ persistence.logger.debug(`[${opts.agentName}] Failed to remove session mode file:`, error);
1094
+ }
1095
+ }
1028
1096
  try {
1029
1097
  happyServer.stop();
1030
1098
  } catch (error) {
@@ -1047,9 +1115,11 @@ async function runAcp(opts) {
1047
1115
  }
1048
1116
  }
1049
1117
 
1118
+ const PI_ACP_COMMAND = "pi-acp";
1050
1119
  const KNOWN_ACP_AGENTS = {
1051
1120
  gemini: { command: "gemini", args: ["--experimental-acp"] },
1052
- opencode: { command: "opencode", args: ["acp"] }
1121
+ opencode: { command: "opencode", args: ["acp"] },
1122
+ pi: { command: PI_ACP_COMMAND, args: [] }
1053
1123
  };
1054
1124
  function resolveAcpAgentConfig(cliArgs) {
1055
1125
  if (cliArgs.length === 0) {
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  require('chalk');
4
- require('./index-4jiUeiUz.cjs');
5
- require('./types-C5kEpkcZ.cjs');
4
+ require('./index-DcESl4cI.cjs');
5
+ require('./types-DD3AK6Nb.cjs');
6
6
  require('zod');
7
7
  require('node:child_process');
8
8
  require('node:os');
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'chalk';
2
- import './index-Dq-eFp89.mjs';
3
- import './types-Ds0SiQ1D.mjs';
2
+ import './index-BYZKuQoy.mjs';
3
+ import './types-jSfU_0o1.mjs';
4
4
  import 'zod';
5
5
  import 'node:child_process';
6
6
  import 'node:os';
@@ -3,8 +3,8 @@ import { join, resolve } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import chalk from 'chalk';
6
- import { p as projectPath } from './types-Ds0SiQ1D.mjs';
7
- import { t as tmuxSupportsSessionEnv } from './index-Dq-eFp89.mjs';
6
+ import { p as projectPath } from './types-jSfU_0o1.mjs';
7
+ import { t as tmuxSupportsSessionEnv } from './index-BYZKuQoy.mjs';
8
8
  import 'axios';
9
9
  import 'node:util';
10
10
  import 'node:fs/promises';
@@ -5,8 +5,8 @@ var path = require('node:path');
5
5
  var os = require('node:os');
6
6
  var node_child_process = require('node:child_process');
7
7
  var chalk = require('chalk');
8
- var persistence = require('./types-C5kEpkcZ.cjs');
9
- var index = require('./index-4jiUeiUz.cjs');
8
+ var persistence = require('./types-DD3AK6Nb.cjs');
9
+ var index = require('./index-DcESl4cI.cjs');
10
10
  require('axios');
11
11
  require('node:util');
12
12
  require('node:fs/promises');
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var persistence = require('./types-C5kEpkcZ.cjs');
3
+ var persistence = require('./types-DD3AK6Nb.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('node:util');
package/dist/lib.d.cts CHANGED
@@ -315,6 +315,7 @@ declare const MachineMetadataSchema: z$1.ZodObject<{
315
315
  codex: z$1.ZodBoolean;
316
316
  gemini: z$1.ZodBoolean;
317
317
  openclaw: z$1.ZodBoolean;
318
+ pi: z$1.ZodOptional<z$1.ZodBoolean>;
318
319
  detectedAt: z$1.ZodNumber;
319
320
  }, z$1.core.$strip>>;
320
321
  resumeSupport: z$1.ZodOptional<z$1.ZodObject<{
@@ -1045,12 +1046,24 @@ declare class ApiSessionClient extends EventEmitter {
1045
1046
  private startSmartReconnect;
1046
1047
  }
1047
1048
 
1049
+ /**
1050
+ * The backends the daemon can spawn as a remote session.
1051
+ *
1052
+ * One list, four consumers: the `spawn --agent` CLI validator, the daemon's
1053
+ * `/spawn-session` zod enum, the machine RPC `SpawnSessionOptions` type and
1054
+ * the daemon's argv switch in `daemon/run.ts`. Before this module each site
1055
+ * carried its own copy and adding a backend meant finding all of them (B-306
1056
+ * found the CLI one missing). Import from here; do not re-list.
1057
+ */
1058
+ declare const SPAWN_AGENTS: readonly ["claude", "codex", "gemini", "openclaw", "pi"];
1059
+ type SpawnAgent = (typeof SPAWN_AGENTS)[number];
1060
+
1048
1061
  interface SpawnSessionOptions {
1049
1062
  machineId?: string;
1050
1063
  directory: string;
1051
1064
  sessionId?: string;
1052
1065
  approvedNewDirectoryCreation?: boolean;
1053
- agent?: 'claude' | 'codex' | 'gemini' | 'openclaw';
1066
+ agent?: SpawnAgent;
1054
1067
  environmentVariables?: Record<string, string>;
1055
1068
  token?: string;
1056
1069
  /**
@@ -1235,6 +1248,21 @@ interface TerminalListItem {
1235
1248
  paneRows?: number;
1236
1249
  }
1237
1250
 
1251
+ interface CLIAvailability {
1252
+ claude: boolean;
1253
+ codex: boolean;
1254
+ gemini: boolean;
1255
+ openclaw: boolean;
1256
+ /**
1257
+ * pi is spawnable only through the pi-acp adapter (`very-happy pi`), so this
1258
+ * is "both `pi` and `pi-acp` resolve on PATH". Older daemons never send the
1259
+ * field; the Web launcher treats absence as unavailable (unlike the other
1260
+ * agents), because such a daemon cannot spawn pi at all.
1261
+ */
1262
+ pi: boolean;
1263
+ detectedAt: number;
1264
+ }
1265
+
1238
1266
  /**
1239
1267
  * WebSocket client for machine/daemon communication with Happy server
1240
1268
  * Similar to ApiSessionClient but for machine-scoped connections
@@ -1281,6 +1309,18 @@ declare class ApiMachineClient {
1281
1309
  * waits until there are none. */
1282
1310
  hasLiveTerminals(): boolean;
1283
1311
  private webTerminal;
1312
+ /**
1313
+ * The CLI availability last advertised in machine metadata (re-probed by
1314
+ * the keep-alive). Null until the first keep-alive tick; the daemon falls
1315
+ * back to its startup probe then.
1316
+ */
1317
+ getCLIAvailability(): CLIAvailability | null;
1318
+ /**
1319
+ * Title a web terminal from inside it (`very-happy mcp` change_title →
1320
+ * daemon /terminal-title → here). Same tmux truthfulness as the
1321
+ * `set-terminal-title` RPC.
1322
+ */
1323
+ setTerminalTitle(terminalId: string, title: string, ifAbsent: boolean): boolean;
1284
1324
  /**
1285
1325
  * Push text to the clipboard of every web client the user has open
1286
1326
  * (terminal-path claude → `very-happy mcp` → daemon /clipboard → here).
package/dist/lib.d.mts CHANGED
@@ -315,6 +315,7 @@ declare const MachineMetadataSchema: z$1.ZodObject<{
315
315
  codex: z$1.ZodBoolean;
316
316
  gemini: z$1.ZodBoolean;
317
317
  openclaw: z$1.ZodBoolean;
318
+ pi: z$1.ZodOptional<z$1.ZodBoolean>;
318
319
  detectedAt: z$1.ZodNumber;
319
320
  }, z$1.core.$strip>>;
320
321
  resumeSupport: z$1.ZodOptional<z$1.ZodObject<{
@@ -1045,12 +1046,24 @@ declare class ApiSessionClient extends EventEmitter {
1045
1046
  private startSmartReconnect;
1046
1047
  }
1047
1048
 
1049
+ /**
1050
+ * The backends the daemon can spawn as a remote session.
1051
+ *
1052
+ * One list, four consumers: the `spawn --agent` CLI validator, the daemon's
1053
+ * `/spawn-session` zod enum, the machine RPC `SpawnSessionOptions` type and
1054
+ * the daemon's argv switch in `daemon/run.ts`. Before this module each site
1055
+ * carried its own copy and adding a backend meant finding all of them (B-306
1056
+ * found the CLI one missing). Import from here; do not re-list.
1057
+ */
1058
+ declare const SPAWN_AGENTS: readonly ["claude", "codex", "gemini", "openclaw", "pi"];
1059
+ type SpawnAgent = (typeof SPAWN_AGENTS)[number];
1060
+
1048
1061
  interface SpawnSessionOptions {
1049
1062
  machineId?: string;
1050
1063
  directory: string;
1051
1064
  sessionId?: string;
1052
1065
  approvedNewDirectoryCreation?: boolean;
1053
- agent?: 'claude' | 'codex' | 'gemini' | 'openclaw';
1066
+ agent?: SpawnAgent;
1054
1067
  environmentVariables?: Record<string, string>;
1055
1068
  token?: string;
1056
1069
  /**
@@ -1235,6 +1248,21 @@ interface TerminalListItem {
1235
1248
  paneRows?: number;
1236
1249
  }
1237
1250
 
1251
+ interface CLIAvailability {
1252
+ claude: boolean;
1253
+ codex: boolean;
1254
+ gemini: boolean;
1255
+ openclaw: boolean;
1256
+ /**
1257
+ * pi is spawnable only through the pi-acp adapter (`very-happy pi`), so this
1258
+ * is "both `pi` and `pi-acp` resolve on PATH". Older daemons never send the
1259
+ * field; the Web launcher treats absence as unavailable (unlike the other
1260
+ * agents), because such a daemon cannot spawn pi at all.
1261
+ */
1262
+ pi: boolean;
1263
+ detectedAt: number;
1264
+ }
1265
+
1238
1266
  /**
1239
1267
  * WebSocket client for machine/daemon communication with Happy server
1240
1268
  * Similar to ApiSessionClient but for machine-scoped connections
@@ -1281,6 +1309,18 @@ declare class ApiMachineClient {
1281
1309
  * waits until there are none. */
1282
1310
  hasLiveTerminals(): boolean;
1283
1311
  private webTerminal;
1312
+ /**
1313
+ * The CLI availability last advertised in machine metadata (re-probed by
1314
+ * the keep-alive). Null until the first keep-alive tick; the daemon falls
1315
+ * back to its startup probe then.
1316
+ */
1317
+ getCLIAvailability(): CLIAvailability | null;
1318
+ /**
1319
+ * Title a web terminal from inside it (`very-happy mcp` change_title →
1320
+ * daemon /terminal-title → here). Same tmux truthfulness as the
1321
+ * `set-terminal-title` RPC.
1322
+ */
1323
+ setTerminalTitle(terminalId: string, title: string, ifAbsent: boolean): boolean;
1284
1324
  /**
1285
1325
  * Push text to the clipboard of every web client the user has open
1286
1326
  * (terminal-path claude → `very-happy mcp` → daemon /clipboard → here).
package/dist/lib.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-Ds0SiQ1D.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-jSfU_0o1.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'node:util';