very-happy-cli 0.2.124 → 0.2.125

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 (49) hide show
  1. package/dist/{AcpBackend-CT9TOBEM.mjs → AcpBackend-DOQGItcS.mjs} +2 -1
  2. package/dist/{AcpBackend-au0veuIF.cjs → AcpBackend-ssQpTLks.cjs} +30 -29
  3. package/dist/{AcpSessionManager-p1tj7FxF.mjs → AcpSessionManager-D4WkNsRs.mjs} +1 -1
  4. package/dist/{AcpSessionManager-BCWeBKDd.cjs → AcpSessionManager-DHLc-D50.cjs} +9 -9
  5. package/dist/agentGuidance-Cg9ZG1pP.cjs +236 -0
  6. package/dist/agentGuidance-QpYwykb9.mjs +224 -0
  7. package/dist/codex/happyMcpStdioBridge.cjs +29 -6
  8. package/dist/codex/happyMcpStdioBridge.mjs +25 -2
  9. package/dist/{config-GzzqE5Ev.cjs → config-BxHOGtEK.cjs} +11 -11
  10. package/dist/{config-DwMic6AC.mjs → config-CZNIz3W0.mjs} +11 -11
  11. package/dist/{index-DgN9P02i.cjs → index-CBj99SO6.cjs} +53 -19
  12. package/dist/{index-SQKnWyz-.mjs → index-CrSifgJM.mjs} +2046 -1719
  13. package/dist/{index-B3-daYaX.mjs → index-D-BgvQi5.mjs} +8 -8
  14. package/dist/{index-C69uF6Up.cjs → index-D_Rm9dw9.cjs} +8 -8
  15. package/dist/{index-DI_VC3vJ.cjs → index-Do-kjc30.cjs} +2191 -1864
  16. package/dist/{index-Ba6Fu6MJ.mjs → index-Jrk6ud3g.mjs} +49 -15
  17. package/dist/index.cjs +5 -5
  18. package/dist/index.mjs +5 -5
  19. package/dist/{installTerminalHooks-CvMt8cBI.cjs → installTerminalHooks-CPsdeOM4.cjs} +9 -9
  20. package/dist/{installTerminalHooks-Df4hzzdM.mjs → installTerminalHooks-Z1Jb6yjb.mjs} +8 -8
  21. package/dist/lib.cjs +12 -12
  22. package/dist/lib.d.cts +3 -0
  23. package/dist/lib.d.mts +3 -0
  24. package/dist/lib.mjs +9 -9
  25. package/dist/limits-DViGHwSj.cjs +1198 -0
  26. package/dist/limits-FjbPJPsF.mjs +1129 -0
  27. package/dist/{mcp-DnyLwSPq.cjs → mcp-BbQ5FWcQ.cjs} +11 -10
  28. package/dist/{mcp-C0oW5u-b.mjs → mcp-zqPJ94Bk.mjs} +7 -6
  29. package/dist/resources-CVl7NdOB.cjs +83 -0
  30. package/dist/resources-D-mn7ptO.mjs +80 -0
  31. package/dist/{runGemini-_FHrxuHN.cjs → runGemini-BnHGYX-H.cjs} +30 -30
  32. package/dist/{runGemini-mqLjz-M7.mjs → runGemini-Bu49s_bN.mjs} +13 -13
  33. package/dist/{runOpenClaw-C5VZQ6Na.cjs → runOpenClaw-3Ld1eoWV.cjs} +24 -24
  34. package/dist/{runOpenClaw-Bs03yQHe.mjs → runOpenClaw-CznAYPCh.mjs} +9 -9
  35. package/dist/{send-BaLjQPZQ.mjs → send-BevgS1mf.mjs} +10 -10
  36. package/dist/{send-BD2gTVoP.cjs → send-DCzyBxLl.cjs} +10 -10
  37. package/dist/{sessions-ggBePfSY.mjs → sessions-Ba2oRTmw.mjs} +10 -10
  38. package/dist/{sessions-B108j_Xj.cjs → sessions-DSoaBHwA.cjs} +11 -11
  39. package/dist/{spawn-iI8w8sD7.cjs → spawn-CqMLY6dE.cjs} +21 -21
  40. package/dist/{spawn-CORFRb9D.mjs → spawn-j9KBfj4e.mjs} +21 -21
  41. package/dist/teams-C0ggJTJ-.mjs +164 -0
  42. package/dist/teams-CmU3K9vB.cjs +166 -0
  43. package/dist/{types-CZIwVSN2.cjs → types-BGHnQXl5.cjs} +462 -1571
  44. package/dist/{types-gDOmwOKw.mjs → types-DLDkfrTC.mjs} +76 -1148
  45. package/package.json +7 -7
  46. package/dist/agentGuidance-BifkGXdE.cjs +0 -46
  47. package/dist/agentGuidance-C2_pkgRI.mjs +0 -37
  48. package/dist/limits-CwFb5S2b.mjs +0 -22
  49. package/dist/limits-KdWKmwtH.cjs +0 -28
@@ -0,0 +1,224 @@
1
+ import { z } from 'zod';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { n as normalizeHttpEndpoint, c as configuration, f as readSettings, r as readPersistedSessions, i as readCredentialsForConfiguredRelay } from './limits-FjbPJPsF.mjs';
4
+ import { open, mkdir, rename, unlink } from 'node:fs/promises';
5
+ import { constants } from 'node:fs';
6
+ import { dirname, isAbsolute, join } from 'node:path';
7
+
8
+ const TeamScopeSchema = z.object({
9
+ serverUrl: z.string(),
10
+ scopeToken: z.string().min(1),
11
+ teamId: z.string().min(1),
12
+ botId: z.string().min(1),
13
+ taskId: z.string().optional(),
14
+ attemptId: z.string().optional()
15
+ });
16
+ function teamScopePath(sessionId) {
17
+ const supplied = process.env.VH_TEAM_SCOPE_FILE;
18
+ if (supplied) {
19
+ if (!isAbsolute(supplied)) throw new Error("VH_TEAM_SCOPE_FILE must be absolute");
20
+ return supplied;
21
+ }
22
+ if (!sessionId || !/^[a-zA-Z0-9_-]+$/.test(sessionId)) throw new Error("Teams requires a managed session or VH_TEAM_SCOPE_FILE");
23
+ return join(configuration.happyHomeDir, "teams", "scopes", `${sessionId}.json`);
24
+ }
25
+ async function readTeamScope(path) {
26
+ let file;
27
+ try {
28
+ file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
29
+ } catch (error) {
30
+ if (error.code === "ENOENT") return null;
31
+ throw new Error("Cannot open team scope");
32
+ }
33
+ try {
34
+ const stat = await file.stat();
35
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new Error("Team scope owner mismatch");
36
+ if (!stat.isFile() || process.platform !== "win32" && (stat.mode & 63) !== 0) throw new Error("Team scope must be a private 0600 file");
37
+ const scope = TeamScopeSchema.parse(JSON.parse(await file.readFile("utf8")));
38
+ scope.serverUrl = normalizeHttpEndpoint(scope.serverUrl, "team scope");
39
+ if (scope.serverUrl !== configuration.serverUrl) throw new Error("Team scope belongs to another server");
40
+ return scope;
41
+ } catch {
42
+ throw new Error("Invalid or unsafe team scope file");
43
+ } finally {
44
+ await file.close();
45
+ }
46
+ }
47
+ async function writeTeamScope(path, scope) {
48
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
49
+ const temporary = `${path}.${randomUUID()}.new`;
50
+ const file = await open(temporary, "wx", 384);
51
+ try {
52
+ await file.writeFile(JSON.stringify(TeamScopeSchema.parse(scope)));
53
+ await file.sync();
54
+ } finally {
55
+ await file.close();
56
+ }
57
+ try {
58
+ await rename(temporary, path);
59
+ } finally {
60
+ await unlink(temporary).catch(() => {
61
+ });
62
+ }
63
+ }
64
+
65
+ async function teamRequest(path, method, token, scoped, body) {
66
+ const response = await fetch(`${configuration.serverUrl}${path}`, {
67
+ method,
68
+ redirect: "error",
69
+ headers: { "content-type": "application/json", ...scoped ? { "x-happy-team-token": token } : { authorization: `Bearer ${token}` } },
70
+ body: body === void 0 ? void 0 : JSON.stringify(body),
71
+ signal: AbortSignal.timeout(25e3)
72
+ }).catch(() => {
73
+ throw new Error("Teams connection failed; outcome may be unknown. Reuse the request ID when retrying.");
74
+ });
75
+ if (!response.ok) throw new Error(`Teams request failed (HTTP ${response.status})`);
76
+ try {
77
+ return await response.json();
78
+ } catch {
79
+ throw new Error("Teams returned an invalid response");
80
+ }
81
+ }
82
+ function createTeamsClient(sessionId) {
83
+ const scopePath = () => teamScopePath(sessionId ?? process.env.HAPPY_SESSION_ID);
84
+ return {
85
+ async initialize(input) {
86
+ const path = scopePath();
87
+ if (await readTeamScope(path)) throw new Error("This session already has a team scope; use scoped team tools");
88
+ if (process.env.VH_TEAM_SCOPE_FILE) throw new Error("Assigned team scope is missing; ask the owner to restore it");
89
+ const sid = sessionId ?? process.env.HAPPY_SESSION_ID;
90
+ if (!sid) throw new Error("A managed session identity is required");
91
+ const machineId = (await readSettings()).machineId;
92
+ if (!machineId) throw new Error("Connect this machine to Very Happy before joining a team");
93
+ if (input.machineId && input.machineId !== machineId) throw new Error("Teams must use this session\u2019s local machine");
94
+ const persisted = readPersistedSessions()[sid];
95
+ if (!persisted || persisted.metadata.machineId !== machineId) throw new Error("Team identity must be a persisted session on this machine");
96
+ const credentials = await readCredentialsForConfiguredRelay();
97
+ if (!credentials) throw new Error("Sign in to Very Happy before creating or joining a team");
98
+ let teamId = input.teamId;
99
+ if (!teamId) {
100
+ const created = await teamRequest("/v1/teams", "POST", credentials.token, false, { name: input.name, machineId, requestId: input.requestId });
101
+ teamId = created.team.id;
102
+ } else {
103
+ const existing = await teamRequest(`/v1/teams/${encodeURIComponent(teamId)}`, "GET", credentials.token, false);
104
+ if (existing.team?.machineId !== machineId) throw new Error("Team belongs to another machine");
105
+ }
106
+ const result = await teamRequest(
107
+ `/v1/teams/${encodeURIComponent(teamId)}/actions`,
108
+ "POST",
109
+ credentials.token,
110
+ false,
111
+ { requestId: input.requestId, action: { type: "join", name: input.name, sessionId: sid, ...input.botId ? { botId: input.botId } : {} } }
112
+ );
113
+ if (!result.credential?.token || !result.credential?.botId) throw new Error("Team join did not issue a credential");
114
+ await writeTeamScope(path, { serverUrl: configuration.serverUrl, teamId, botId: result.credential.botId, scopeToken: result.credential.token });
115
+ return { team: result.team, botId: result.credential.botId };
116
+ },
117
+ async inspect() {
118
+ const scope = await readTeamScope(scopePath());
119
+ if (!scope) throw new Error("Join or create a team first");
120
+ const { credential: _credential, ...safe } = await teamRequest(`/v1/teams/${encodeURIComponent(scope.teamId)}/agent`, "GET", scope.scopeToken, true);
121
+ return safe;
122
+ },
123
+ async action(action, requestId = randomUUID()) {
124
+ const scope = await readTeamScope(scopePath());
125
+ if (!scope) throw new Error("Join or create a team first");
126
+ const result = await teamRequest(`/v1/teams/${encodeURIComponent(scope.teamId)}/agent`, "POST", scope.scopeToken, true, { requestId, action });
127
+ const { credential: _credential, ...safe } = result;
128
+ return safe;
129
+ }
130
+ };
131
+ }
132
+
133
+ const TEAM_TOOL_NAMES = ["team_create", "team_join", "team_inspect", "team_delegate", "team_message", "team_submit", "team_accept", "team_return", "team_cancel", "team_handoff", "team_schedule_create", "team_schedule_pause", "team_schedule_resume", "team_schedule_cancel"];
134
+ const id = z.string().min(1).max(128);
135
+ const text = z.string().min(1).max(32e3);
136
+ const requestId = id.describe("Unique operation ID. Reuse the same ID and arguments after an uncertain response.");
137
+ function registerTeamsTools(server, sessionId, client = createTeamsClient(sessionId)) {
138
+ const register = (name, description, inputSchema, execute) => {
139
+ server.registerTool(name, { description, inputSchema, annotations: { readOnlyHint: name === "team_inspect", idempotentHint: true, openWorldHint: false, destructiveHint: ["team_cancel", "team_handoff", "team_return", "team_schedule_cancel"].includes(name) } }, async (args) => {
140
+ try {
141
+ return { content: [{ type: "text", text: JSON.stringify(await execute(args)) }], isError: false };
142
+ } catch (error) {
143
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : "Teams operation failed" }], isError: true };
144
+ }
145
+ });
146
+ };
147
+ register(
148
+ "team_create",
149
+ "Create an official Very Happy team and join this session as its root bot. Does not grant access outside this account.",
150
+ { name: id, machineId: id.optional(), requestId },
151
+ (args) => client.initialize(args)
152
+ );
153
+ register(
154
+ "team_join",
155
+ "Join an existing team as a root bot using this session owner\u2019s account. Scoped workers cannot elevate their role.",
156
+ { teamId: id, name: id, botId: id.optional().describe("Existing root bot to reconnect; preserves its identity"), requestId },
157
+ (args) => client.initialize(args)
158
+ );
159
+ register("team_inspect", "Read your visible team tasks, attempts and messages. Inspect before decisions; submission is not acceptance.", {}, () => client.inspect());
160
+ const action = (name, description, fields) => register(
161
+ `team_${name}`,
162
+ description,
163
+ { requestId, ...fields },
164
+ ({ requestId: key, ...args }) => client.action({ type: name, ...args }, key)
165
+ );
166
+ action("delegate", "Delegate a goal with acceptance criteria. Use parentTaskId for subdelegation. Existing assigneeBotId or a managed worker may execute it.", {
167
+ goal: text,
168
+ acceptance: z.array(text).min(1).max(32),
169
+ parentTaskId: id.optional(),
170
+ assigneeBotId: id.optional(),
171
+ botName: id.optional(),
172
+ directory: z.string().min(1).optional(),
173
+ assistant: z.enum(["claude", "codex", "pi-acp"]).optional()
174
+ });
175
+ action("message", "Send a persistent task-related message. Delivery does not mean the model has read or acted on it.", { taskId: id, body: text, recipientBotId: id.optional() });
176
+ action("submit", "Submit evidence for your current attempt and goal version. The owner still has to accept it.", { taskId: id, attemptId: id, goalVersion: z.number().int().positive(), result: text });
177
+ const current = { attemptId: id, goalVersion: z.number().int().positive() };
178
+ action("accept", "Accept a task you own after checking its evidence. Resource cleanup is tracked separately.", { taskId: id, ...current });
179
+ action("return", "Return your task for further work with a concrete reason.", { taskId: id, reason: text, ...current });
180
+ action("cancel", "Cancel an owned task and its dependent work. Cleanup may remain pending.", { taskId: id, reason: text, ...current });
181
+ action("handoff", "Move an owned task to another bot; prior execution authority is superseded.", { taskId: id, assigneeBotId: id, ...current });
182
+ register("team_schedule_create", "Create a persistent one-time or fixed-interval reminder for your own root bot. Delivery wakes the bound session; it does not prove task completion. Use epoch milliseconds, and inspect before retrying.", { requestId, name: id, botId: id, body: text, runAt: z.number().int().nonnegative(), intervalMs: z.number().int().min(6e4).optional() }, ({ requestId: key, ...args }) => client.action({ type: "schedule-create", ...args }, key));
183
+ for (const operation of ["pause", "resume", "cancel"]) {
184
+ register(`team_schedule_${operation}`, `${operation} your root bot\u2019s schedule using its current version from team_inspect. An already in-flight message may still arrive.`, { requestId, scheduleId: id, version: z.number().int().positive() }, ({ requestId: key, ...args }) => client.action({ type: `schedule-${operation}`, ...args }, key));
185
+ }
186
+ }
187
+
188
+ function trimIdent(text) {
189
+ const lines = text.split("\n");
190
+ while (lines.length > 0 && lines[0].trim() === "") {
191
+ lines.shift();
192
+ }
193
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
194
+ lines.pop();
195
+ }
196
+ const minSpaces = lines.reduce((min, line) => {
197
+ if (line.trim() === "") {
198
+ return min;
199
+ }
200
+ const leadingSpaces = line.match(/^\s*/)[0].length;
201
+ return Math.min(min, leadingSpaces);
202
+ }, Infinity);
203
+ const trimmedLines = lines.map((line) => line.slice(minSpaces));
204
+ return trimmedLines.join("\n");
205
+ }
206
+
207
+ const AGENT_GUIDANCE = (() => trimIdent(`
208
+ You are running inside Happy: the user is reading this session in a web client (often a phone), NOT in the terminal where you run. Two consequences:
209
+
210
+ - Terminal output is not a good delivery channel. When the user asks for a piece of text ("copy that", "give me X", "\u590D\u5236\u7ED9\u6211", "\u53D1\u6211"), hand it over with mcp__happy__copy_to_clipboard instead of printing it for them to select by hand.
211
+ - When you produce or want to show a FILE (a document you wrote, a report, an image, a PDF, a diff), call mcp__happy__open_preview with its path instead of cat-ing it. Their client opens the rendered file.
212
+
213
+ Judgement, not reflex: use these when the user's goal is to RECEIVE something. If they are asking a question you can just answer, answer it. Do not preview a file you only read for your own reasoning, and do not preview the same file repeatedly within a turn.
214
+
215
+ If you are working on a task the user is tracking, call mcp__happy__report_progress at meaningful milestones (started / blocked / needs review / done) so their board reflects reality. Do not report every tool call.
216
+ `))();
217
+ const PREVIEW_TOOL_NAME = "open_preview";
218
+ const PREVIEW_TOOL_TITLE = "Open File Preview for the User";
219
+ const PREVIEW_TOOL_DESCRIPTION = 'Open a file in the preview panel of every web client the user currently has open. Use this whenever you produce or want to show the user a file: a document or report you just wrote, a generated image or diagram, a PDF, a data file, or a source file you want them to look at. Trigger phrases include: "write me a doc", "show me that file", "let me see it", "\u5199\u4E2A\u6587\u6863", "\u7ED9\u6211\u770B\u770B\u8FD9\u4E2A\u6587\u4EF6", "\u6253\u5F00\u770B\u4E0B". Pass an absolute path (or one starting with ~). The file is read on this machine and rendered in their browser \u2014 markdown is rendered, images and PDFs are displayed, other text is syntax-highlighted. Prefer this over printing file contents to the terminal, which the user is probably not looking at. NOTE: this only requests that their client open the file; it cannot confirm the user actually looked at it, so do not assume they have seen it.';
220
+ const REPORT_PROGRESS_TOOL_NAME = "report_progress";
221
+ const REPORT_PROGRESS_TOOL_TITLE = "Report Progress to the Task Board";
222
+ const REPORT_PROGRESS_TOOL_DESCRIPTION = `Report what you are doing to the user's task board, so they can see the state of this session at a glance without reading the transcript. Call it at meaningful milestones only: when you start substantive work, when you get blocked and need the user, when something is ready for their review, and when you are done. Do NOT call it after every tool call or every file edit \u2014 it is a status line, not a log. Set attention to "blocked" when you cannot proceed without the user, "review" when you want them to look at something, and "none" for ordinary progress. Keep progress to one short line describing the current state (the user reads it on a card).`;
223
+
224
+ export { AGENT_GUIDANCE as A, PREVIEW_TOOL_NAME as P, REPORT_PROGRESS_TOOL_NAME as R, TEAM_TOOL_NAMES as T, PREVIEW_TOOL_TITLE as a, PREVIEW_TOOL_DESCRIPTION as b, createTeamsClient as c, REPORT_PROGRESS_TOOL_TITLE as d, REPORT_PROGRESS_TOOL_DESCRIPTION as e, registerTeamsTools as r, trimIdent as t };
@@ -1,12 +1,20 @@
1
1
  'use strict';
2
2
 
3
+ var agentGuidance = require('../agentGuidance-Cg9ZG1pP.cjs');
3
4
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
4
5
  var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
5
6
  var index_js = require('@modelcontextprotocol/sdk/client/index.js');
6
7
  var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
7
8
  var z = require('zod');
8
- var limits = require('../limits-KdWKmwtH.cjs');
9
- var agentGuidance = require('../agentGuidance-BifkGXdE.cjs');
9
+ var persistence = require('../limits-DViGHwSj.cjs');
10
+ require('node:crypto');
11
+ require('node:fs/promises');
12
+ require('node:fs');
13
+ require('node:path');
14
+ require('node:os');
15
+ require('tweetnacl');
16
+ require('chalk');
17
+ require('node:util');
10
18
 
11
19
  function parseArgs(argv) {
12
20
  let url = null;
@@ -94,10 +102,10 @@ async function main() {
94
102
  }
95
103
  );
96
104
  server.registerTool(
97
- limits.CLIPBOARD_TOOL_NAME,
105
+ persistence.CLIPBOARD_TOOL_NAME,
98
106
  {
99
- description: limits.CLIPBOARD_TOOL_DESCRIPTION,
100
- title: limits.CLIPBOARD_TOOL_TITLE,
107
+ description: persistence.CLIPBOARD_TOOL_DESCRIPTION,
108
+ title: persistence.CLIPBOARD_TOOL_TITLE,
101
109
  inputSchema: {
102
110
  text: z.z.string().describe("The text to copy to the user's clipboard")
103
111
  }
@@ -105,7 +113,7 @@ async function main() {
105
113
  async (args) => {
106
114
  try {
107
115
  const client = await ensureHttpClient();
108
- const response = await client.callTool({ name: limits.CLIPBOARD_TOOL_NAME, arguments: args });
116
+ const response = await client.callTool({ name: persistence.CLIPBOARD_TOOL_NAME, arguments: args });
109
117
  return response;
110
118
  } catch (error) {
111
119
  return {
@@ -117,6 +125,21 @@ async function main() {
117
125
  }
118
126
  }
119
127
  );
128
+ const forwardTeam = async (name, args) => {
129
+ const response = await (await ensureHttpClient()).callTool({ name, arguments: args });
130
+ const content = response.content;
131
+ const text = content.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n");
132
+ if (response.isError) throw new Error(text || "Teams operation failed");
133
+ return JSON.parse(text);
134
+ };
135
+ agentGuidance.registerTeamsTools(server, void 0, {
136
+ initialize: (input) => forwardTeam(input.teamId ? "team_join" : "team_create", input),
137
+ inspect: () => forwardTeam("team_inspect", {}),
138
+ action: (action, requestId) => {
139
+ const { type, ...args } = action;
140
+ return forwardTeam(`team_${String(type).replaceAll("-", "_")}`, { ...args, requestId });
141
+ }
142
+ });
120
143
  const stdio = new stdio_js.StdioServerTransport();
121
144
  await server.connect(stdio);
122
145
  }
@@ -1,10 +1,18 @@
1
+ import { P as PREVIEW_TOOL_NAME, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, r as registerTeamsTools } from '../agentGuidance-QpYwykb9.mjs';
1
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
4
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
4
5
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5
6
  import { z } from 'zod';
6
- import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION } from '../limits-CwFb5S2b.mjs';
7
- import { P as PREVIEW_TOOL_NAME, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION } from '../agentGuidance-C2_pkgRI.mjs';
7
+ import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION } from '../limits-FjbPJPsF.mjs';
8
+ import 'node:crypto';
9
+ import 'node:fs/promises';
10
+ import 'node:fs';
11
+ import 'node:path';
12
+ import 'node:os';
13
+ import 'tweetnacl';
14
+ import 'chalk';
15
+ import 'node:util';
8
16
 
9
17
  function parseArgs(argv) {
10
18
  let url = null;
@@ -115,6 +123,21 @@ async function main() {
115
123
  }
116
124
  }
117
125
  );
126
+ const forwardTeam = async (name, args) => {
127
+ const response = await (await ensureHttpClient()).callTool({ name, arguments: args });
128
+ const content = response.content;
129
+ const text = content.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n");
130
+ if (response.isError) throw new Error(text || "Teams operation failed");
131
+ return JSON.parse(text);
132
+ };
133
+ registerTeamsTools(server, void 0, {
134
+ initialize: (input) => forwardTeam(input.teamId ? "team_join" : "team_create", input),
135
+ inspect: () => forwardTeam("team_inspect", {}),
136
+ action: (action, requestId) => {
137
+ const { type, ...args } = action;
138
+ return forwardTeam(`team_${String(type).replaceAll("-", "_")}`, { ...args, requestId });
139
+ }
140
+ });
118
141
  const stdio = new StdioServerTransport();
119
142
  await server.connect(stdio);
120
143
  }
@@ -4,22 +4,22 @@ var fs = require('fs');
4
4
  var path = require('path');
5
5
  var os = require('os');
6
6
  var child_process = require('child_process');
7
- var persistence = require('./types-CZIwVSN2.cjs');
8
- var index = require('./index-DI_VC3vJ.cjs');
9
- require('axios');
10
- require('chalk');
11
- require('node:util');
7
+ var persistence = require('./limits-DViGHwSj.cjs');
8
+ var index = require('./index-Do-kjc30.cjs');
9
+ require('node:fs/promises');
12
10
  require('node:fs');
13
11
  require('node:os');
14
12
  require('node:path');
15
- require('node:fs/promises');
16
- require('node:events');
17
- require('socket.io-client');
18
13
  require('zod');
19
- require('@paralleldrive/cuid2');
20
14
  require('node:crypto');
21
15
  require('tweetnacl');
22
- require('./limits-KdWKmwtH.cjs');
16
+ require('chalk');
17
+ require('node:util');
18
+ require('./types-BGHnQXl5.cjs');
19
+ require('axios');
20
+ require('node:events');
21
+ require('socket.io-client');
22
+ require('@paralleldrive/cuid2');
23
23
  require('util');
24
24
  require('fs/promises');
25
25
  require('crypto');
@@ -30,7 +30,7 @@ require('node-pty');
30
30
  require('node:readline');
31
31
  require('@anthropic-ai/sandbox-runtime');
32
32
  require('expo-server-sdk');
33
- require('./agentGuidance-BifkGXdE.cjs');
33
+ require('./agentGuidance-Cg9ZG1pP.cjs');
34
34
  require('ink');
35
35
  require('react');
36
36
  require('@anthropic-ai/claude-agent-sdk');
@@ -2,22 +2,22 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { execSync } from 'child_process';
5
- import { l as logger } from './types-gDOmwOKw.mjs';
6
- import { j as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-SQKnWyz-.mjs';
7
- import 'axios';
8
- import 'chalk';
9
- import 'node:util';
5
+ import { l as logger } from './limits-FjbPJPsF.mjs';
6
+ import { j as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-CrSifgJM.mjs';
7
+ import 'node:fs/promises';
10
8
  import 'node:fs';
11
9
  import 'node:os';
12
10
  import 'node:path';
13
- import 'node:fs/promises';
14
- import 'node:events';
15
- import 'socket.io-client';
16
11
  import 'zod';
17
- import '@paralleldrive/cuid2';
18
12
  import 'node:crypto';
19
13
  import 'tweetnacl';
20
- import './limits-CwFb5S2b.mjs';
14
+ import 'chalk';
15
+ import 'node:util';
16
+ import './types-DLDkfrTC.mjs';
17
+ import 'axios';
18
+ import 'node:events';
19
+ import 'socket.io-client';
20
+ import '@paralleldrive/cuid2';
21
21
  import 'util';
22
22
  import 'fs/promises';
23
23
  import 'crypto';
@@ -28,7 +28,7 @@ import 'node-pty';
28
28
  import 'node:readline';
29
29
  import '@anthropic-ai/sandbox-runtime';
30
30
  import 'expo-server-sdk';
31
- import './agentGuidance-C2_pkgRI.mjs';
31
+ import './agentGuidance-QpYwykb9.mjs';
32
32
  import 'ink';
33
33
  import 'react';
34
34
  import '@anthropic-ai/claude-agent-sdk';
@@ -1,25 +1,26 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-au0veuIF.cjs');
4
- var persistence = require('./types-CZIwVSN2.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-BCWeBKDd.cjs');
3
+ var AcpBackend = require('./AcpBackend-ssQpTLks.cjs');
4
+ var persistence = require('./limits-DViGHwSj.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-DHLc-D50.cjs');
6
+ var promises = require('node:fs/promises');
6
7
  var node_crypto = require('node:crypto');
7
8
  var path = require('node:path');
8
- var index = require('./index-DI_VC3vJ.cjs');
9
+ var resources = require('./resources-CVl7NdOB.cjs');
10
+ var api = require('./types-BGHnQXl5.cjs');
11
+ var index = require('./index-Do-kjc30.cjs');
9
12
  require('node:child_process');
10
13
  require('@agentclientprotocol/sdk');
11
- require('axios');
12
- require('chalk');
13
- require('node:util');
14
14
  require('node:fs');
15
15
  require('node:os');
16
- require('node:fs/promises');
17
- require('node:events');
18
- require('socket.io-client');
19
16
  require('zod');
20
- require('@paralleldrive/cuid2');
21
17
  require('tweetnacl');
22
- require('./limits-KdWKmwtH.cjs');
18
+ require('chalk');
19
+ require('node:util');
20
+ require('@paralleldrive/cuid2');
21
+ require('axios');
22
+ require('node:events');
23
+ require('socket.io-client');
23
24
  require('child_process');
24
25
  require('util');
25
26
  require('fs/promises');
@@ -33,7 +34,7 @@ require('fs');
33
34
  require('node:readline');
34
35
  require('@anthropic-ai/sandbox-runtime');
35
36
  require('expo-server-sdk');
36
- require('./agentGuidance-BifkGXdE.cjs');
37
+ require('./agentGuidance-Cg9ZG1pP.cjs');
37
38
  require('ink');
38
39
  require('react');
39
40
  require('@anthropic-ai/claude-agent-sdk');
@@ -65,6 +66,36 @@ function createAcpBackend(options) {
65
66
  return new AcpBackend.AcpBackend(backendOptions);
66
67
  }
67
68
 
69
+ async function publish(path, content, mode) {
70
+ const temporary = `${path}.${node_crypto.randomUUID()}.new`;
71
+ await promises.writeFile(temporary, content, { mode, flag: "wx" });
72
+ await promises.rename(temporary, path);
73
+ }
74
+ async function preparePiTeamsRuntime() {
75
+ const directory = path.join(persistence.configuration.happyHomeDir, "teams", "pi-runtime");
76
+ await promises.mkdir(directory, { recursive: true, mode: 448 });
77
+ const extension = path.join(directory, "very-happy-teams.mjs");
78
+ await publish(extension, resources.PI_TEAMS_EXTENSION, 384);
79
+ const launcher = path.join(directory, "pi-teams.mjs");
80
+ const script = `#!${process.execPath}
81
+ import { spawn } from 'node:child_process';
82
+ const child = spawn(process.env.VH_TEAM_PI_COMMAND || 'pi', ['--extension', ${JSON.stringify(extension)}, ...process.argv.slice(2)], {stdio:'inherit',env:process.env,shell:process.platform==='win32'});
83
+ child.on('error',()=>{process.stderr.write('Very Happy could not launch pi\\n');process.exitCode=1});
84
+ child.on('exit',(code)=>{process.exitCode=code ?? 1});
85
+ for(const signal of ['SIGINT','SIGTERM'])process.on(signal,()=>child.kill(signal));
86
+ `;
87
+ await publish(launcher, script, 448);
88
+ await promises.chmod(launcher, 448);
89
+ let command = launcher;
90
+ if (process.platform === "win32") {
91
+ command = path.join(directory, "pi-teams.cmd");
92
+ await publish(command, `@echo off\r
93
+ "${process.execPath}" "${launcher}" %*\r
94
+ `, 448);
95
+ }
96
+ return { PI_ACP_PI_COMMAND: command, VH_TEAM_PI_COMMAND: (process.env.PI_ACP_PI_COMMAND === command ? process.env.VH_TEAM_PI_COMMAND : process.env.PI_ACP_PI_COMMAND) || "pi" };
97
+ }
98
+
68
99
  const SUPPORTED_CATEGORIES = /* @__PURE__ */ new Set(["mode", "model", "thought_level"]);
69
100
  function isRecord$1(value) {
70
101
  return !!value && typeof value === "object";
@@ -586,30 +617,31 @@ function resolveSessionFlavor(agentName) {
586
617
  async function runAcp(opts) {
587
618
  const verbose = opts.verbose === true;
588
619
  const sessionTag = node_crypto.randomUUID();
589
- persistence.connectionState.setBackend(opts.agentName);
590
- const api = await persistence.ApiClient.create(opts.credentials);
620
+ api.connectionState.setBackend(opts.agentName);
621
+ const api$1 = await api.ApiClient.create(opts.credentials);
591
622
  const settings = await persistence.readSettings();
592
623
  if (!settings?.machineId) {
593
624
  throw new Error("No machine ID found in settings");
594
625
  }
595
- await api.getOrCreateMachine({
626
+ await api$1.getOrCreateMachine({
596
627
  machineId: settings.machineId,
597
628
  metadata: index.initialMachineMetadata
598
629
  });
599
630
  const { state, metadata } = index.createSessionMetadata({
600
631
  flavor: resolveSessionFlavor(opts.agentName),
632
+ ...process.env.VH_TEAM_OPERATION_ID ? { teamOperationId: process.env.VH_TEAM_OPERATION_ID } : {},
601
633
  machineId: settings.machineId,
602
634
  startedBy: opts.startedBy,
603
635
  sandbox: settings.sandboxConfig
604
636
  });
605
- const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
637
+ const response = await api$1.getOrCreateSession({ tag: sessionTag, metadata, state });
606
638
  if (response) {
607
639
  logAcp("muted", `Happy Session ID: ${response.id}`);
608
640
  }
609
641
  let session;
610
642
  let permissionHandler;
611
643
  const { session: initialSession, reconnectionHandle } = index.setupOfflineReconnection({
612
- api,
644
+ api: api$1,
613
645
  sessionTag,
614
646
  metadata,
615
647
  state,
@@ -675,16 +707,18 @@ async function runAcp(opts) {
675
707
  const happyServer = await index.startHappyServer(session, { assistant: isAssistantVariant });
676
708
  const mcpServers = {
677
709
  happy: {
678
- command: path.join(persistence.projectPath(), "bin", "happy-mcp.mjs"),
710
+ command: path.join(api.projectPath(), "bin", "very-happy-mcp.mjs"),
679
711
  args: ["--url", happyServer.url]
680
712
  }
681
713
  };
714
+ const teamsPiEnv = opts.agentName === "pi" ? await preparePiTeamsRuntime() : {};
682
715
  const backend = new AcpBackend.AcpBackend({
683
716
  agentName: opts.agentName,
684
717
  cwd: process.cwd(),
685
718
  command: opts.command,
686
719
  args: opts.args,
687
720
  env: {
721
+ ...teamsPiEnv,
688
722
  HAPPY_MCP_URL: happyServer.url,
689
723
  HAPPY_SESSION_ID: session.sessionId,
690
724
  HAPPY_PERMISSION_MODE: initialPermissionMode