very-happy-cli 0.2.116 → 0.2.118

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-Bk9b823B.mjs} +116 -12
  2. package/dist/{AcpBackend-CauAQpC1.cjs → AcpBackend-p_KNJkHc.cjs} +116 -12
  3. package/dist/{AcpSessionManager-CA6xeET_.cjs → AcpSessionManager-B9FTbpGQ.cjs} +15 -2
  4. package/dist/{AcpSessionManager-BQ6pBRW4.mjs → AcpSessionManager-CDvqVHKf.mjs} +15 -2
  5. package/dist/{config-BpR_CwZu.mjs → config-CIpgmGLR.mjs} +2 -2
  6. package/dist/{config-BoteFn-v.cjs → config-CkUn1dI4.cjs} +2 -2
  7. package/dist/{index-C45xN8s0.mjs → index-B6cpyG_G.mjs} +78 -8
  8. package/dist/{index-D7azxfcs.mjs → index-BUVQf9PI.mjs} +2 -2
  9. package/dist/{index-4jiUeiUz.cjs → index-BXOXRblP.cjs} +513 -47
  10. package/dist/{index-DWmhmji9.cjs → index-BzgtwTxC.cjs} +77 -7
  11. package/dist/{index-lX1UliOs.cjs → index-CxjX8DGz.cjs} +2 -2
  12. package/dist/{index-Dq-eFp89.mjs → index-DIXjhnV1.mjs} +502 -45
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-k3TrBHD0.mjs → installTerminalHooks-BAa6LaQQ.mjs} +2 -2
  16. package/dist/{installTerminalHooks-DnaFkrTF.cjs → installTerminalHooks-lXQa6bQU.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.d.cts +43 -1
  19. package/dist/lib.d.mts +43 -1
  20. package/dist/lib.mjs +1 -1
  21. package/dist/{mcp-DSpEpM_2.cjs → mcp-DMD7FxBI.cjs} +62 -16
  22. package/dist/{mcp-DRePTOap.mjs → mcp-Dwc299mU.mjs} +62 -17
  23. package/dist/{runGemini-_9-a7duU.mjs → runGemini-BaxGXE9w.mjs} +4 -4
  24. package/dist/{runGemini-IWHyBq0A.cjs → runGemini-Bj4Egq7j.cjs} +4 -4
  25. package/dist/{runOpenClaw-0myYIz1U.cjs → runOpenClaw-BJhSvXYy.cjs} +3 -3
  26. package/dist/{runOpenClaw-Boq6LQ9a.mjs → runOpenClaw-BwJhbIKU.mjs} +3 -3
  27. package/dist/{send-CwWvFfay.cjs → send-BWGUkWhi.cjs} +2 -2
  28. package/dist/{send-Cyo6mgMl.mjs → send-DX13svrS.mjs} +2 -2
  29. package/dist/sessions-CGhpBNqn.cjs +322 -0
  30. package/dist/sessions-L7O3ypda.mjs +317 -0
  31. package/dist/{spawn-DQ6FTUK9.cjs → spawn-CbY233Eg.cjs} +6 -7
  32. package/dist/{spawn-CU0DMauA.mjs → spawn-DPDWZdoW.mjs} +3 -4
  33. package/dist/{types-Ds0SiQ1D.mjs → types-BtuOM9lw.mjs} +52 -19
  34. package/dist/{types-C5kEpkcZ.cjs → types-ClvvHves.cjs} +54 -19
  35. package/package.json +7 -7
  36. package/dist/sessions-CbzqYiAy.cjs +0 -204
  37. package/dist/sessions-lvfTH_ql.mjs +0 -200
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-BXOXRblP.cjs');
5
+ require('./types-ClvvHves.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-DIXjhnV1.mjs';
3
+ import './types-BtuOM9lw.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-BtuOM9lw.mjs';
7
+ import { t as tmuxSupportsSessionEnv } from './index-DIXjhnV1.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-ClvvHves.cjs');
9
+ var index = require('./index-BXOXRblP.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-ClvvHves.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<{
@@ -335,6 +336,7 @@ declare const CliUpdateStateSchema: z$1.ZodObject<{
335
336
  available: "available";
336
337
  required: "required";
337
338
  }>;
339
+ autoUpdateVersion: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodString>>;
338
340
  checkedAt: z$1.ZodNumber;
339
341
  handoverHold: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodObject<{
340
342
  reason: z$1.ZodString;
@@ -429,6 +431,7 @@ declare const DaemonStateSchema: z$1.ZodObject<{
429
431
  available: "available";
430
432
  required: "required";
431
433
  }>;
434
+ autoUpdateVersion: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodString>>;
432
435
  checkedAt: z$1.ZodNumber;
433
436
  handoverHold: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodObject<{
434
437
  reason: z$1.ZodString;
@@ -1045,12 +1048,24 @@ declare class ApiSessionClient extends EventEmitter {
1045
1048
  private startSmartReconnect;
1046
1049
  }
1047
1050
 
1051
+ /**
1052
+ * The backends the daemon can spawn as a remote session.
1053
+ *
1054
+ * One list, four consumers: the `spawn --agent` CLI validator, the daemon's
1055
+ * `/spawn-session` zod enum, the machine RPC `SpawnSessionOptions` type and
1056
+ * the daemon's argv switch in `daemon/run.ts`. Before this module each site
1057
+ * carried its own copy and adding a backend meant finding all of them (B-306
1058
+ * found the CLI one missing). Import from here; do not re-list.
1059
+ */
1060
+ declare const SPAWN_AGENTS: readonly ["claude", "codex", "gemini", "openclaw", "pi"];
1061
+ type SpawnAgent = (typeof SPAWN_AGENTS)[number];
1062
+
1048
1063
  interface SpawnSessionOptions {
1049
1064
  machineId?: string;
1050
1065
  directory: string;
1051
1066
  sessionId?: string;
1052
1067
  approvedNewDirectoryCreation?: boolean;
1053
- agent?: 'claude' | 'codex' | 'gemini' | 'openclaw';
1068
+ agent?: SpawnAgent;
1054
1069
  environmentVariables?: Record<string, string>;
1055
1070
  token?: string;
1056
1071
  /**
@@ -1235,6 +1250,21 @@ interface TerminalListItem {
1235
1250
  paneRows?: number;
1236
1251
  }
1237
1252
 
1253
+ interface CLIAvailability {
1254
+ claude: boolean;
1255
+ codex: boolean;
1256
+ gemini: boolean;
1257
+ openclaw: boolean;
1258
+ /**
1259
+ * pi is spawnable only through the pi-acp adapter (`very-happy pi`), so this
1260
+ * is "both `pi` and `pi-acp` resolve on PATH". Older daemons never send the
1261
+ * field; the Web launcher treats absence as unavailable (unlike the other
1262
+ * agents), because such a daemon cannot spawn pi at all.
1263
+ */
1264
+ pi: boolean;
1265
+ detectedAt: number;
1266
+ }
1267
+
1238
1268
  /**
1239
1269
  * WebSocket client for machine/daemon communication with Happy server
1240
1270
  * Similar to ApiSessionClient but for machine-scoped connections
@@ -1281,6 +1311,18 @@ declare class ApiMachineClient {
1281
1311
  * waits until there are none. */
1282
1312
  hasLiveTerminals(): boolean;
1283
1313
  private webTerminal;
1314
+ /**
1315
+ * The CLI availability last advertised in machine metadata (re-probed by
1316
+ * the keep-alive). Null until the first keep-alive tick; the daemon falls
1317
+ * back to its startup probe then.
1318
+ */
1319
+ getCLIAvailability(): CLIAvailability | null;
1320
+ /**
1321
+ * Title a web terminal from inside it (`very-happy mcp` change_title →
1322
+ * daemon /terminal-title → here). Same tmux truthfulness as the
1323
+ * `set-terminal-title` RPC.
1324
+ */
1325
+ setTerminalTitle(terminalId: string, title: string, ifAbsent: boolean): boolean;
1284
1326
  /**
1285
1327
  * Push text to the clipboard of every web client the user has open
1286
1328
  * (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<{
@@ -335,6 +336,7 @@ declare const CliUpdateStateSchema: z$1.ZodObject<{
335
336
  available: "available";
336
337
  required: "required";
337
338
  }>;
339
+ autoUpdateVersion: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodString>>;
338
340
  checkedAt: z$1.ZodNumber;
339
341
  handoverHold: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodObject<{
340
342
  reason: z$1.ZodString;
@@ -429,6 +431,7 @@ declare const DaemonStateSchema: z$1.ZodObject<{
429
431
  available: "available";
430
432
  required: "required";
431
433
  }>;
434
+ autoUpdateVersion: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodString>>;
432
435
  checkedAt: z$1.ZodNumber;
433
436
  handoverHold: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodObject<{
434
437
  reason: z$1.ZodString;
@@ -1045,12 +1048,24 @@ declare class ApiSessionClient extends EventEmitter {
1045
1048
  private startSmartReconnect;
1046
1049
  }
1047
1050
 
1051
+ /**
1052
+ * The backends the daemon can spawn as a remote session.
1053
+ *
1054
+ * One list, four consumers: the `spawn --agent` CLI validator, the daemon's
1055
+ * `/spawn-session` zod enum, the machine RPC `SpawnSessionOptions` type and
1056
+ * the daemon's argv switch in `daemon/run.ts`. Before this module each site
1057
+ * carried its own copy and adding a backend meant finding all of them (B-306
1058
+ * found the CLI one missing). Import from here; do not re-list.
1059
+ */
1060
+ declare const SPAWN_AGENTS: readonly ["claude", "codex", "gemini", "openclaw", "pi"];
1061
+ type SpawnAgent = (typeof SPAWN_AGENTS)[number];
1062
+
1048
1063
  interface SpawnSessionOptions {
1049
1064
  machineId?: string;
1050
1065
  directory: string;
1051
1066
  sessionId?: string;
1052
1067
  approvedNewDirectoryCreation?: boolean;
1053
- agent?: 'claude' | 'codex' | 'gemini' | 'openclaw';
1068
+ agent?: SpawnAgent;
1054
1069
  environmentVariables?: Record<string, string>;
1055
1070
  token?: string;
1056
1071
  /**
@@ -1235,6 +1250,21 @@ interface TerminalListItem {
1235
1250
  paneRows?: number;
1236
1251
  }
1237
1252
 
1253
+ interface CLIAvailability {
1254
+ claude: boolean;
1255
+ codex: boolean;
1256
+ gemini: boolean;
1257
+ openclaw: boolean;
1258
+ /**
1259
+ * pi is spawnable only through the pi-acp adapter (`very-happy pi`), so this
1260
+ * is "both `pi` and `pi-acp` resolve on PATH". Older daemons never send the
1261
+ * field; the Web launcher treats absence as unavailable (unlike the other
1262
+ * agents), because such a daemon cannot spawn pi at all.
1263
+ */
1264
+ pi: boolean;
1265
+ detectedAt: number;
1266
+ }
1267
+
1238
1268
  /**
1239
1269
  * WebSocket client for machine/daemon communication with Happy server
1240
1270
  * Similar to ApiSessionClient but for machine-scoped connections
@@ -1281,6 +1311,18 @@ declare class ApiMachineClient {
1281
1311
  * waits until there are none. */
1282
1312
  hasLiveTerminals(): boolean;
1283
1313
  private webTerminal;
1314
+ /**
1315
+ * The CLI availability last advertised in machine metadata (re-probed by
1316
+ * the keep-alive). Null until the first keep-alive tick; the daemon falls
1317
+ * back to its startup probe then.
1318
+ */
1319
+ getCLIAvailability(): CLIAvailability | null;
1320
+ /**
1321
+ * Title a web terminal from inside it (`very-happy mcp` change_title →
1322
+ * daemon /terminal-title → here). Same tmux truthfulness as the
1323
+ * `set-terminal-title` RPC.
1324
+ */
1325
+ setTerminalTitle(terminalId: string, title: string, ifAbsent: boolean): boolean;
1284
1326
  /**
1285
1327
  * Push text to the clipboard of every web client the user has open
1286
1328
  * (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-BtuOM9lw.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'node:util';
@@ -3,30 +3,30 @@
3
3
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
4
4
  var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
5
5
  var z = require('zod');
6
- var index = require('./index-4jiUeiUz.cjs');
6
+ var index = require('./index-BXOXRblP.cjs');
7
7
  var limits = require('./limits-KdWKmwtH.cjs');
8
- var persistence = require('./types-C5kEpkcZ.cjs');
9
- require('chalk');
8
+ var persistence = require('./types-ClvvHves.cjs');
9
+ require('node:fs/promises');
10
10
  require('node:os');
11
11
  require('node:crypto');
12
- require('cross-spawn');
12
+ require('axios');
13
+ require('tweetnacl');
14
+ require('socket.io-client');
15
+ require('node:child_process');
16
+ require('node:fs');
13
17
  require('node:path');
18
+ require('chalk');
19
+ require('cross-spawn');
14
20
  require('node:readline');
15
- require('node:fs');
16
21
  require('./agentGuidance-BifkGXdE.cjs');
17
- require('node:fs/promises');
18
22
  require('fs/promises');
19
23
  require('ink');
20
24
  require('react');
21
25
  require('@anthropic-ai/claude-agent-sdk');
22
- require('axios');
23
26
  require('node:events');
24
- require('socket.io-client');
25
- require('tweetnacl');
26
27
  require('os');
27
28
  require('child_process');
28
29
  require('@paralleldrive/cuid2');
29
- require('node:child_process');
30
30
  require('fs');
31
31
  require('path');
32
32
  require('@anthropic-ai/sandbox-runtime');
@@ -49,11 +49,19 @@ require('node:util');
49
49
  require('url');
50
50
  require('node-pty');
51
51
 
52
- async function handleMcpCommand() {
53
- const server = new mcp_js.McpServer({
54
- name: "very-happy",
55
- version: "1.0.0"
56
- });
52
+ const TERMINAL_TITLE_TOOL_NAME = "change_title";
53
+ function resolveMcpTerminalId(env) {
54
+ if (env.HAPPY_MCP_URL) return null;
55
+ const id = env.VH_TERMINAL_ID;
56
+ return id && /^[a-zA-Z0-9_-]{1,64}$/.test(id) ? id : null;
57
+ }
58
+ function resolveMcpToolSurface(env) {
59
+ if (env.HAPPY_SESSION_VARIANT !== "assistant") return "clipboard";
60
+ if (env.HAPPY_MANAGED === "1") return "clipboard";
61
+ return "assistant";
62
+ }
63
+
64
+ function registerMcpTools(server, surface, terminalId = null) {
57
65
  server.registerTool(limits.CLIPBOARD_TOOL_NAME, {
58
66
  description: limits.CLIPBOARD_TOOL_DESCRIPTION,
59
67
  title: limits.CLIPBOARD_TOOL_TITLE,
@@ -81,9 +89,46 @@ async function handleMcpCommand() {
81
89
  isError: true
82
90
  };
83
91
  });
92
+ if (terminalId) {
93
+ server.registerTool(TERMINAL_TITLE_TOOL_NAME, {
94
+ description: "Change the title of the very-happy web terminal this agent is running in",
95
+ title: "Change Terminal Title",
96
+ inputSchema: {
97
+ title: z.z.string().min(1).max(200).describe("The new title for this terminal")
98
+ }
99
+ }, async (args) => {
100
+ persistence.logger.debug(`[MCP] change_title called for terminal ${terminalId}`);
101
+ const result = await index.setTerminalTitleViaDaemon(terminalId, args.title);
102
+ if (result.ok) {
103
+ return {
104
+ content: [{ type: "text", text: `Successfully changed terminal title to: "${args.title}"` }],
105
+ isError: false
106
+ };
107
+ }
108
+ return {
109
+ content: [{
110
+ type: "text",
111
+ text: `Failed to change terminal title: ${result.error || "unknown error"}. The very-happy daemon must be running on this machine (start it with \`very-happy daemon start\`).`
112
+ }],
113
+ isError: true
114
+ };
115
+ });
116
+ }
117
+ if (surface === "assistant") {
118
+ index.registerAssistantSessionTools(server);
119
+ }
120
+ }
121
+ async function handleMcpCommand() {
122
+ const server = new mcp_js.McpServer({
123
+ name: "very-happy",
124
+ version: "1.0.0"
125
+ });
126
+ const surface = resolveMcpToolSurface(process.env);
127
+ const terminalId = resolveMcpTerminalId(process.env);
128
+ registerMcpTools(server, surface, terminalId);
84
129
  const transport = new stdio_js.StdioServerTransport();
85
130
  await server.connect(transport);
86
- persistence.logger.debug("[MCP] very-happy stdio MCP server started");
131
+ persistence.logger.debug(`[MCP] very-happy stdio MCP server started (surface=${surface}, terminal=${terminalId ?? "none"})`);
87
132
  await new Promise((resolve) => {
88
133
  process.stdin.on("close", resolve);
89
134
  process.stdin.on("end", resolve);
@@ -91,3 +136,4 @@ async function handleMcpCommand() {
91
136
  }
92
137
 
93
138
  exports.handleMcpCommand = handleMcpCommand;
139
+ exports.registerMcpTools = registerMcpTools;
@@ -1,30 +1,30 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { z } from 'zod';
4
- import { p as pushClipboardViaDaemon } from './index-Dq-eFp89.mjs';
4
+ import { p as pushClipboardViaDaemon, k as setTerminalTitleViaDaemon, m as registerAssistantSessionTools } from './index-DIXjhnV1.mjs';
5
5
  import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, c as CLIPBOARD_MAX_BYTES } from './limits-CwFb5S2b.mjs';
6
- import { l as logger } from './types-Ds0SiQ1D.mjs';
7
- import 'chalk';
6
+ import { l as logger } from './types-BtuOM9lw.mjs';
7
+ import 'node:fs/promises';
8
8
  import 'node:os';
9
9
  import 'node:crypto';
10
- import 'cross-spawn';
10
+ import 'axios';
11
+ import 'tweetnacl';
12
+ import 'socket.io-client';
13
+ import 'node:child_process';
14
+ import 'node:fs';
11
15
  import 'node:path';
16
+ import 'chalk';
17
+ import 'cross-spawn';
12
18
  import 'node:readline';
13
- import 'node:fs';
14
19
  import './agentGuidance-C2_pkgRI.mjs';
15
- import 'node:fs/promises';
16
20
  import 'fs/promises';
17
21
  import 'ink';
18
22
  import 'react';
19
23
  import '@anthropic-ai/claude-agent-sdk';
20
- import 'axios';
21
24
  import 'node:events';
22
- import 'socket.io-client';
23
- import 'tweetnacl';
24
25
  import 'os';
25
26
  import 'child_process';
26
27
  import '@paralleldrive/cuid2';
27
- import 'node:child_process';
28
28
  import 'fs';
29
29
  import 'path';
30
30
  import '@anthropic-ai/sandbox-runtime';
@@ -47,11 +47,19 @@ import 'node:util';
47
47
  import 'url';
48
48
  import 'node-pty';
49
49
 
50
- async function handleMcpCommand() {
51
- const server = new McpServer({
52
- name: "very-happy",
53
- version: "1.0.0"
54
- });
50
+ const TERMINAL_TITLE_TOOL_NAME = "change_title";
51
+ function resolveMcpTerminalId(env) {
52
+ if (env.HAPPY_MCP_URL) return null;
53
+ const id = env.VH_TERMINAL_ID;
54
+ return id && /^[a-zA-Z0-9_-]{1,64}$/.test(id) ? id : null;
55
+ }
56
+ function resolveMcpToolSurface(env) {
57
+ if (env.HAPPY_SESSION_VARIANT !== "assistant") return "clipboard";
58
+ if (env.HAPPY_MANAGED === "1") return "clipboard";
59
+ return "assistant";
60
+ }
61
+
62
+ function registerMcpTools(server, surface, terminalId = null) {
55
63
  server.registerTool(CLIPBOARD_TOOL_NAME, {
56
64
  description: CLIPBOARD_TOOL_DESCRIPTION,
57
65
  title: CLIPBOARD_TOOL_TITLE,
@@ -79,13 +87,50 @@ async function handleMcpCommand() {
79
87
  isError: true
80
88
  };
81
89
  });
90
+ if (terminalId) {
91
+ server.registerTool(TERMINAL_TITLE_TOOL_NAME, {
92
+ description: "Change the title of the very-happy web terminal this agent is running in",
93
+ title: "Change Terminal Title",
94
+ inputSchema: {
95
+ title: z.string().min(1).max(200).describe("The new title for this terminal")
96
+ }
97
+ }, async (args) => {
98
+ logger.debug(`[MCP] change_title called for terminal ${terminalId}`);
99
+ const result = await setTerminalTitleViaDaemon(terminalId, args.title);
100
+ if (result.ok) {
101
+ return {
102
+ content: [{ type: "text", text: `Successfully changed terminal title to: "${args.title}"` }],
103
+ isError: false
104
+ };
105
+ }
106
+ return {
107
+ content: [{
108
+ type: "text",
109
+ text: `Failed to change terminal title: ${result.error || "unknown error"}. The very-happy daemon must be running on this machine (start it with \`very-happy daemon start\`).`
110
+ }],
111
+ isError: true
112
+ };
113
+ });
114
+ }
115
+ if (surface === "assistant") {
116
+ registerAssistantSessionTools(server);
117
+ }
118
+ }
119
+ async function handleMcpCommand() {
120
+ const server = new McpServer({
121
+ name: "very-happy",
122
+ version: "1.0.0"
123
+ });
124
+ const surface = resolveMcpToolSurface(process.env);
125
+ const terminalId = resolveMcpTerminalId(process.env);
126
+ registerMcpTools(server, surface, terminalId);
82
127
  const transport = new StdioServerTransport();
83
128
  await server.connect(transport);
84
- logger.debug("[MCP] very-happy stdio MCP server started");
129
+ logger.debug(`[MCP] very-happy stdio MCP server started (surface=${surface}, terminal=${terminalId ?? "none"})`);
85
130
  await new Promise((resolve) => {
86
131
  process.stdin.on("close", resolve);
87
132
  process.stdin.on("end", resolve);
88
133
  });
89
134
  }
90
135
 
91
- export { handleMcpCommand };
136
+ export { handleMcpCommand, registerMcpTools };
@@ -2,10 +2,10 @@ import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { join } from 'node:path';
5
- import { l as logger, h as connectionState, A as ApiClient, j as readSettings, e as errorLogMetadata, k as encodeBase64, p as projectPath, d as contentLogMetadata } from './types-Ds0SiQ1D.mjs';
6
- import { x as GEMINI_API_KEY_ENV, y as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, B as BasePermissionHandler, z as BaseReasoningProcessor, h as initialMachineMetadata, j as createSessionMetadata, k as setupOfflineReconnection, n as notifyDaemonSessionStarted, m as MessageQueue2, o as hashObject, C as MessageBuffer, E as CHANGE_TITLE_INSTRUCTION, v as registerKillSessionHandler, u as startHappyServer } from './index-Dq-eFp89.mjs';
7
- import { A as AcpBackend } from './AcpBackend-CfKPvoTU.mjs';
8
- import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-BpR_CwZu.mjs';
5
+ import { l as logger, d as connectionState, A as ApiClient, e as readSettings, h as errorLogMetadata, f as encodeBase64, p as projectPath, g as contentLogMetadata } from './types-BtuOM9lw.mjs';
6
+ import { I as GEMINI_API_KEY_ENV, J as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, C as BasePermissionHandler, K as BaseReasoningProcessor, n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as MessageQueue2, y as hashObject, L as MessageBuffer, N as CHANGE_TITLE_INSTRUCTION, B as registerKillSessionHandler, z as startHappyServer } from './index-DIXjhnV1.mjs';
7
+ import { A as AcpBackend } from './AcpBackend-Bk9b823B.mjs';
8
+ import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-CIpgmGLR.mjs';
9
9
  import 'axios';
10
10
  import 'chalk';
11
11
  import 'node:util';
@@ -4,10 +4,10 @@ var ink = require('ink');
4
4
  var React = require('react');
5
5
  var node_crypto = require('node:crypto');
6
6
  var path = require('node:path');
7
- var persistence = require('./types-C5kEpkcZ.cjs');
8
- var index = require('./index-4jiUeiUz.cjs');
9
- var AcpBackend = require('./AcpBackend-CauAQpC1.cjs');
10
- var config = require('./config-BoteFn-v.cjs');
7
+ var persistence = require('./types-ClvvHves.cjs');
8
+ var index = require('./index-BXOXRblP.cjs');
9
+ var AcpBackend = require('./AcpBackend-p_KNJkHc.cjs');
10
+ var config = require('./config-CkUn1dI4.cjs');
11
11
  require('axios');
12
12
  require('chalk');
13
13
  require('node:util');
@@ -5,9 +5,9 @@ var node_child_process = require('node:child_process');
5
5
  var os = require('node:os');
6
6
  var node_fs = require('node:fs');
7
7
  var path = require('node:path');
8
- var persistence = require('./types-C5kEpkcZ.cjs');
9
- var AcpSessionManager = require('./AcpSessionManager-CA6xeET_.cjs');
10
- var index = require('./index-4jiUeiUz.cjs');
8
+ var persistence = require('./types-ClvvHves.cjs');
9
+ var AcpSessionManager = require('./AcpSessionManager-B9FTbpGQ.cjs');
10
+ var index = require('./index-BXOXRblP.cjs');
11
11
  var WebSocket = require('ws');
12
12
  var ed = require('@noble/ed25519');
13
13
  var sha2_js = require('@noble/hashes/sha2.js');
@@ -3,9 +3,9 @@ import { execFileSync } from 'node:child_process';
3
3
  import os from 'node:os';
4
4
  import { existsSync, readFileSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
- import { m as ensurePrivateDirectorySync, n as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, j as readSettings, k as encodeBase64, l as logger, c as configuration } from './types-Ds0SiQ1D.mjs';
7
- import { A as AcpSessionManager } from './AcpSessionManager-BQ6pBRW4.mjs';
8
- import { h as initialMachineMetadata, j as createSessionMetadata, k as setupOfflineReconnection, n as notifyDaemonSessionStarted, m as MessageQueue2, v as registerKillSessionHandler } from './index-Dq-eFp89.mjs';
6
+ import { m as ensurePrivateDirectorySync, n as hardenPrivateFileSync, w as writePrivateFileSync, h as errorLogMetadata, g as contentLogMetadata, d as connectionState, A as ApiClient, e as readSettings, f as encodeBase64, l as logger, c as configuration } from './types-BtuOM9lw.mjs';
7
+ import { A as AcpSessionManager } from './AcpSessionManager-CDvqVHKf.mjs';
8
+ import { n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as MessageQueue2, B as registerKillSessionHandler } from './index-DIXjhnV1.mjs';
9
9
  import WebSocket from 'ws';
10
10
  import * as ed from '@noble/ed25519';
11
11
  import { sha512 } from '@noble/hashes/sha2.js';
@@ -3,8 +3,8 @@
3
3
  var chalk = require('chalk');
4
4
  var node_fs = require('node:fs');
5
5
  var path = require('node:path');
6
- var persistence = require('./types-C5kEpkcZ.cjs');
7
- var index = require('./index-4jiUeiUz.cjs');
6
+ var persistence = require('./types-ClvvHves.cjs');
7
+ var index = require('./index-BXOXRblP.cjs');
8
8
  require('axios');
9
9
  require('node:util');
10
10
  require('node:os');
@@ -1,8 +1,8 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
- import { r as readPersistedSessions, c as configuration } from './types-Ds0SiQ1D.mjs';
5
- import { d as sendUserMessage, a as sessionWebUrl } from './index-Dq-eFp89.mjs';
4
+ import { r as readPersistedSessions, c as configuration } from './types-BtuOM9lw.mjs';
5
+ import { d as sendUserMessage, a as sessionWebUrl } from './index-DIXjhnV1.mjs';
6
6
  import 'axios';
7
7
  import 'node:util';
8
8
  import 'node:os';