very-happy-cli 0.2.123 → 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.
- package/dist/{AcpBackend-hM-qHLN3.mjs → AcpBackend-DOQGItcS.mjs} +2 -1
- package/dist/{AcpBackend-DzIvpTxZ.cjs → AcpBackend-ssQpTLks.cjs} +30 -29
- package/dist/{AcpSessionManager-CRPZPHPa.mjs → AcpSessionManager-D4WkNsRs.mjs} +1 -1
- package/dist/{AcpSessionManager-DyU3Ikgj.cjs → AcpSessionManager-DHLc-D50.cjs} +9 -9
- package/dist/agentGuidance-Cg9ZG1pP.cjs +236 -0
- package/dist/agentGuidance-QpYwykb9.mjs +224 -0
- package/dist/codex/happyMcpStdioBridge.cjs +29 -6
- package/dist/codex/happyMcpStdioBridge.mjs +25 -2
- package/dist/{config-4RaHrVSV.cjs → config-BxHOGtEK.cjs} +11 -11
- package/dist/{config-alG86PDN.mjs → config-CZNIz3W0.mjs} +11 -11
- package/dist/{index-Beh0UZf8.cjs → index-CBj99SO6.cjs} +53 -19
- package/dist/{index-36AkXNF2.mjs → index-CrSifgJM.mjs} +2046 -1719
- package/dist/{index-vyYHKKk6.mjs → index-D-BgvQi5.mjs} +8 -8
- package/dist/{index-DWBKJ5zy.cjs → index-D_Rm9dw9.cjs} +8 -8
- package/dist/{index-5b9YhV-1.cjs → index-Do-kjc30.cjs} +2191 -1864
- package/dist/{index-Cg3Vfc00.mjs → index-Jrk6ud3g.mjs} +49 -15
- package/dist/index.cjs +5 -5
- package/dist/index.mjs +5 -5
- package/dist/{installTerminalHooks-DWCa5j7u.cjs → installTerminalHooks-CPsdeOM4.cjs} +9 -9
- package/dist/{installTerminalHooks-DqUPkh0l.mjs → installTerminalHooks-Z1Jb6yjb.mjs} +8 -8
- package/dist/lib.cjs +12 -12
- package/dist/lib.d.cts +3 -0
- package/dist/lib.d.mts +3 -0
- package/dist/lib.mjs +9 -9
- package/dist/limits-DViGHwSj.cjs +1198 -0
- package/dist/limits-FjbPJPsF.mjs +1129 -0
- package/dist/{mcp-BBya5dsZ.cjs → mcp-BbQ5FWcQ.cjs} +11 -10
- package/dist/{mcp-DZIdcsJp.mjs → mcp-zqPJ94Bk.mjs} +7 -6
- package/dist/resources-CVl7NdOB.cjs +83 -0
- package/dist/resources-D-mn7ptO.mjs +80 -0
- package/dist/{runGemini-DmWxHIwS.cjs → runGemini-BnHGYX-H.cjs} +30 -30
- package/dist/{runGemini-BQa2eF3i.mjs → runGemini-Bu49s_bN.mjs} +13 -13
- package/dist/{runOpenClaw-boyJtPBq.cjs → runOpenClaw-3Ld1eoWV.cjs} +24 -24
- package/dist/{runOpenClaw-362eg04u.mjs → runOpenClaw-CznAYPCh.mjs} +9 -9
- package/dist/{send-CT22-8Tp.mjs → send-BevgS1mf.mjs} +10 -10
- package/dist/{send-BINA__Ke.cjs → send-DCzyBxLl.cjs} +10 -10
- package/dist/{sessions-DemgtCkr.mjs → sessions-Ba2oRTmw.mjs} +10 -10
- package/dist/{sessions-DXYxV6Np.cjs → sessions-DSoaBHwA.cjs} +11 -11
- package/dist/{spawn-BtV9dU1i.cjs → spawn-CqMLY6dE.cjs} +21 -21
- package/dist/{spawn-D2egvPgO.mjs → spawn-j9KBfj4e.mjs} +21 -21
- package/dist/teams-C0ggJTJ-.mjs +164 -0
- package/dist/teams-CmU3K9vB.cjs +166 -0
- package/dist/{types-Cd_tUN3U.cjs → types-BGHnQXl5.cjs} +585 -1577
- package/dist/{types-BqW7nEN_.mjs → types-DLDkfrTC.mjs} +198 -1153
- package/package.json +7 -7
- package/dist/agentGuidance-BifkGXdE.cjs +0 -46
- package/dist/agentGuidance-C2_pkgRI.mjs +0 -37
- package/dist/limits-CwFb5S2b.mjs +0 -22
- package/dist/limits-KdWKmwtH.cjs +0 -28
|
@@ -1,11 +1,11 @@
|
|
|
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 z = require('zod');
|
|
6
|
-
var index = require('./index-
|
|
7
|
-
var
|
|
8
|
-
var persistence = require('./types-Cd_tUN3U.cjs');
|
|
7
|
+
var index = require('./index-Do-kjc30.cjs');
|
|
8
|
+
var persistence = require('./limits-DViGHwSj.cjs');
|
|
9
9
|
require('node:fs/promises');
|
|
10
10
|
require('node:os');
|
|
11
11
|
require('node:crypto');
|
|
@@ -13,12 +13,12 @@ require('axios');
|
|
|
13
13
|
require('tweetnacl');
|
|
14
14
|
require('socket.io-client');
|
|
15
15
|
require('node:child_process');
|
|
16
|
+
require('./types-BGHnQXl5.cjs');
|
|
16
17
|
require('node:fs');
|
|
17
18
|
require('node:path');
|
|
18
19
|
require('chalk');
|
|
19
20
|
require('cross-spawn');
|
|
20
21
|
require('node:readline');
|
|
21
|
-
require('./agentGuidance-BifkGXdE.cjs');
|
|
22
22
|
require('fs/promises');
|
|
23
23
|
require('ink');
|
|
24
24
|
require('react');
|
|
@@ -38,6 +38,7 @@ require('open');
|
|
|
38
38
|
require('fastify');
|
|
39
39
|
require('fastify-type-provider-zod');
|
|
40
40
|
require('node:module');
|
|
41
|
+
require('node:util');
|
|
41
42
|
require('node:http');
|
|
42
43
|
require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
43
44
|
require('http');
|
|
@@ -45,7 +46,6 @@ require('util');
|
|
|
45
46
|
require('inquirer');
|
|
46
47
|
require('node:url');
|
|
47
48
|
require('node:readline/promises');
|
|
48
|
-
require('node:util');
|
|
49
49
|
require('url');
|
|
50
50
|
require('node-pty');
|
|
51
51
|
|
|
@@ -62,9 +62,9 @@ function resolveMcpToolSurface(env) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
function registerMcpTools(server, surface, terminalId = null) {
|
|
65
|
-
server.registerTool(
|
|
66
|
-
description:
|
|
67
|
-
title:
|
|
65
|
+
server.registerTool(persistence.CLIPBOARD_TOOL_NAME, {
|
|
66
|
+
description: persistence.CLIPBOARD_TOOL_DESCRIPTION,
|
|
67
|
+
title: persistence.CLIPBOARD_TOOL_TITLE,
|
|
68
68
|
inputSchema: {
|
|
69
69
|
text: z.z.string().describe("The text to copy to the user's clipboard")
|
|
70
70
|
}
|
|
@@ -72,7 +72,7 @@ function registerMcpTools(server, surface, terminalId = null) {
|
|
|
72
72
|
persistence.logger.debug(`[MCP] copy_to_clipboard called (${args.text.length} chars)`);
|
|
73
73
|
const result = await index.pushClipboardViaDaemon(args.text);
|
|
74
74
|
if (result.delivered) {
|
|
75
|
-
const note = result.truncated ? ` (truncated to ${
|
|
75
|
+
const note = result.truncated ? ` (truncated to ${persistence.CLIPBOARD_MAX_BYTES / 1024}KB \u2014 original was ${result.totalBytes} bytes)` : "";
|
|
76
76
|
return {
|
|
77
77
|
content: [{
|
|
78
78
|
type: "text",
|
|
@@ -114,7 +114,7 @@ function registerMcpTools(server, surface, terminalId = null) {
|
|
|
114
114
|
};
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
|
-
if (surface === "assistant") {
|
|
117
|
+
if (surface === "assistant" && !process.env.VH_TEAM_SCOPE_FILE) {
|
|
118
118
|
index.registerAssistantSessionTools(server);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -126,6 +126,7 @@ async function handleMcpCommand() {
|
|
|
126
126
|
const surface = resolveMcpToolSurface(process.env);
|
|
127
127
|
const terminalId = resolveMcpTerminalId(process.env);
|
|
128
128
|
registerMcpTools(server, surface, terminalId);
|
|
129
|
+
if (process.env.HAPPY_MANAGED !== "1" && !process.env.HAPPY_MCP_URL) agentGuidance.registerTeamsTools(server, process.env.HAPPY_SESSION_ID);
|
|
129
130
|
const transport = new stdio_js.StdioServerTransport();
|
|
130
131
|
await server.connect(transport);
|
|
131
132
|
persistence.logger.debug(`[MCP] very-happy stdio MCP server started (surface=${surface}, terminal=${terminalId ?? "none"})`);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { 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 { z } from 'zod';
|
|
4
|
-
import { p as pushClipboardViaDaemon, k as setTerminalTitleViaDaemon, m as registerAssistantSessionTools } from './index-
|
|
5
|
-
import { C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION,
|
|
6
|
-
import { l as logger } from './types-BqW7nEN_.mjs';
|
|
5
|
+
import { p as pushClipboardViaDaemon, k as setTerminalTitleViaDaemon, m as registerAssistantSessionTools } from './index-CrSifgJM.mjs';
|
|
6
|
+
import { l as logger, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, d as CLIPBOARD_MAX_BYTES } from './limits-FjbPJPsF.mjs';
|
|
7
7
|
import 'node:fs/promises';
|
|
8
8
|
import 'node:os';
|
|
9
9
|
import 'node:crypto';
|
|
@@ -11,12 +11,12 @@ import 'axios';
|
|
|
11
11
|
import 'tweetnacl';
|
|
12
12
|
import 'socket.io-client';
|
|
13
13
|
import 'node:child_process';
|
|
14
|
+
import './types-DLDkfrTC.mjs';
|
|
14
15
|
import 'node:fs';
|
|
15
16
|
import 'node:path';
|
|
16
17
|
import 'chalk';
|
|
17
18
|
import 'cross-spawn';
|
|
18
19
|
import 'node:readline';
|
|
19
|
-
import './agentGuidance-C2_pkgRI.mjs';
|
|
20
20
|
import 'fs/promises';
|
|
21
21
|
import 'ink';
|
|
22
22
|
import 'react';
|
|
@@ -36,6 +36,7 @@ import 'open';
|
|
|
36
36
|
import 'fastify';
|
|
37
37
|
import 'fastify-type-provider-zod';
|
|
38
38
|
import 'node:module';
|
|
39
|
+
import 'node:util';
|
|
39
40
|
import 'node:http';
|
|
40
41
|
import '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
41
42
|
import 'http';
|
|
@@ -43,7 +44,6 @@ import 'util';
|
|
|
43
44
|
import 'inquirer';
|
|
44
45
|
import 'node:url';
|
|
45
46
|
import 'node:readline/promises';
|
|
46
|
-
import 'node:util';
|
|
47
47
|
import 'url';
|
|
48
48
|
import 'node-pty';
|
|
49
49
|
|
|
@@ -112,7 +112,7 @@ function registerMcpTools(server, surface, terminalId = null) {
|
|
|
112
112
|
};
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
-
if (surface === "assistant") {
|
|
115
|
+
if (surface === "assistant" && !process.env.VH_TEAM_SCOPE_FILE) {
|
|
116
116
|
registerAssistantSessionTools(server);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -124,6 +124,7 @@ async function handleMcpCommand() {
|
|
|
124
124
|
const surface = resolveMcpToolSurface(process.env);
|
|
125
125
|
const terminalId = resolveMcpTerminalId(process.env);
|
|
126
126
|
registerMcpTools(server, surface, terminalId);
|
|
127
|
+
if (process.env.HAPPY_MANAGED !== "1" && !process.env.HAPPY_MCP_URL) registerTeamsTools(server, process.env.HAPPY_SESSION_ID);
|
|
127
128
|
const transport = new StdioServerTransport();
|
|
128
129
|
await server.connect(transport);
|
|
129
130
|
logger.debug(`[MCP] very-happy stdio MCP server started (surface=${surface}, terminal=${terminalId ?? "none"})`);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TEAM_SKILL = `---
|
|
4
|
+
name: very-happy-teams
|
|
5
|
+
description: Coordinate coding agents through Very Happy teams: delegate tasks, exchange messages, review results, and recover interrupted work. Use when asked to organize or participate in an agent team.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Very Happy Agent Teams
|
|
9
|
+
|
|
10
|
+
Use the team_* tools in a managed Very Happy session. If absent, explain that this host needs Very Happy Teams setup; reading this skill alone does not establish a connection.
|
|
11
|
+
|
|
12
|
+
Start with team_inspect if already assigned. Otherwise team_create or team_join connects the current session. A scoped worker cannot create another root identity or use account-wide tools to bypass its scope.
|
|
13
|
+
|
|
14
|
+
Work yourself when delegation would add needless overhead. Delegate independent work with a clear goal and acceptance criteria. When delegating within your assignment, pass its parentTaskId. Members may delegate further within their existing authority.
|
|
15
|
+
|
|
16
|
+
Name exactly one integrator for each team. Workers edit only their assigned worktrees; they must not merge main, publish releases, change global daemon or npm installations, or allocate shared backlog IDs. The integrator serializes these operations. Across teams, the owner coordinates shared resources: a worktree is not a global lock.
|
|
17
|
+
|
|
18
|
+
Use a unique requestId for each action and reuse it with identical arguments when the outcome is unknown. Inspect state before retrying; a timeout does not prove no action occurred. A delivered message is not proof its recipient processed it.
|
|
19
|
+
|
|
20
|
+
Inspect the current attemptId and goalVersion before team_submit. Include artifact locations and verification evidence. The owner reviews and accepts or returns the result. All children completing does not automatically satisfy the parent goal.
|
|
21
|
+
|
|
22
|
+
Acceptance and cleanup are separate. Never delete dirty worktrees, stop user-owned sessions, or bypass permissions to finish cleanup. Use team_cancel or team_handoff and inspect the resulting status. Keep summaries concise; do not repeatedly forward full descendant transcripts.
|
|
23
|
+
|
|
24
|
+
A root member may create a persistent schedule for itself with the schedule tools; team_inspect shows schedules. Timely delivery requires an online daemon. A scheduled delivery does not prove the model completed the work. Pause or cancel schedules explicitly when they are no longer needed.
|
|
25
|
+
|
|
26
|
+
On restart inspect authoritative state and messages before taking new actions. Do not invent completed work from a previous context. Scope credentials are runtime-owned; never read, print, or copy them into prompts.
|
|
27
|
+
`;
|
|
28
|
+
const PI_TEAMS_EXTENSION = `// Managed by Very Happy Agent Teams.
|
|
29
|
+
export default async function(pi) {
|
|
30
|
+
const endpoint = process.env.HAPPY_MCP_URL;
|
|
31
|
+
if (!endpoint) return;
|
|
32
|
+
const url = new URL(endpoint);
|
|
33
|
+
if (!['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.protocol !== 'http:') throw new Error('Very Happy MCP must be local');
|
|
34
|
+
const {readFile} = await import('node:fs/promises');
|
|
35
|
+
const {join} = await import('node:path');
|
|
36
|
+
const {homedir} = await import('node:os');
|
|
37
|
+
pi.on('tool_call', async (event, ctx) => {
|
|
38
|
+
let mode = process.env.HAPPY_PERMISSION_MODE || 'default';
|
|
39
|
+
const sid = process.env.HAPPY_SESSION_ID;
|
|
40
|
+
if (sid && /^[a-zA-Z0-9_-]+$/.test(sid)) {
|
|
41
|
+
try { mode = JSON.parse(await readFile(join(process.env.HAPPY_HOME_DIR || join(homedir(), '.happy'), 'session-modes', sid + '.json'), 'utf8')).permissionMode; }
|
|
42
|
+
catch (error) { if (error.code !== 'ENOENT') mode = 'default'; }
|
|
43
|
+
}
|
|
44
|
+
if (mode === 'yolo') mode = 'bypassPermissions';
|
|
45
|
+
if (!['default','plan','acceptEdits','bypassPermissions'].includes(mode)) mode = 'default';
|
|
46
|
+
if (mode === 'bypassPermissions') return;
|
|
47
|
+
if (['read','ls','find','grep','team_inspect','change_title','report_progress'].includes(event.toolName)) return;
|
|
48
|
+
if (mode === 'plan') return {block:true,reason:'Plan mode allows read-only tools'};
|
|
49
|
+
if (mode === 'acceptEdits' && ['write','edit'].includes(event.toolName)) return;
|
|
50
|
+
try {
|
|
51
|
+
const allowed = await ctx.ui.confirm('Allow ' + event.toolName + '?', JSON.stringify(event.input || {}).slice(0,4000));
|
|
52
|
+
if (allowed === true) return;
|
|
53
|
+
} catch {}
|
|
54
|
+
return {block:true,reason:'Tool requires explicit approval'};
|
|
55
|
+
});
|
|
56
|
+
let nextId = 0;
|
|
57
|
+
async function rpc(method, params) {
|
|
58
|
+
const requestId = ++nextId;
|
|
59
|
+
const response = await fetch(endpoint, { method: 'POST', redirect: 'error', headers: {'content-type':'application/json', accept:'application/json, text/event-stream'}, body: JSON.stringify({jsonrpc:'2.0',id:requestId,method,params}), signal: AbortSignal.timeout(30000) });
|
|
60
|
+
if (!response.ok) throw new Error('Very Happy tools unavailable (HTTP ' + response.status + ')');
|
|
61
|
+
const body = await response.text();
|
|
62
|
+
let data;
|
|
63
|
+
if ((response.headers.get('content-type') || '').includes('text/event-stream')) {
|
|
64
|
+
for (const event of body.split(/\\r?\\n\\r?\\n/)) {
|
|
65
|
+
const payload = event.split(/\\r?\\n/).filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\\n');
|
|
66
|
+
if (payload) { const candidate = JSON.parse(payload); if (candidate.id === requestId) data = candidate; }
|
|
67
|
+
}
|
|
68
|
+
} else data = JSON.parse(body);
|
|
69
|
+
if (!data || data.error) throw new Error('Very Happy tool request failed');
|
|
70
|
+
return data.result;
|
|
71
|
+
}
|
|
72
|
+
await rpc('initialize', {protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'very-happy-pi-teams',version:'1'}});
|
|
73
|
+
const available = await rpc('tools/list', {});
|
|
74
|
+
for (const tool of available.tools || []) {
|
|
75
|
+
pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
|
|
76
|
+
async execute(_id, args) { const result = await rpc('tools/call', {name:tool.name,arguments:args}); return {content:result.content,details:{isError:!!result.isError}}; }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
exports.PI_TEAMS_EXTENSION = PI_TEAMS_EXTENSION;
|
|
83
|
+
exports.TEAM_SKILL = TEAM_SKILL;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const TEAM_SKILL = `---
|
|
2
|
+
name: very-happy-teams
|
|
3
|
+
description: Coordinate coding agents through Very Happy teams: delegate tasks, exchange messages, review results, and recover interrupted work. Use when asked to organize or participate in an agent team.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Very Happy Agent Teams
|
|
7
|
+
|
|
8
|
+
Use the team_* tools in a managed Very Happy session. If absent, explain that this host needs Very Happy Teams setup; reading this skill alone does not establish a connection.
|
|
9
|
+
|
|
10
|
+
Start with team_inspect if already assigned. Otherwise team_create or team_join connects the current session. A scoped worker cannot create another root identity or use account-wide tools to bypass its scope.
|
|
11
|
+
|
|
12
|
+
Work yourself when delegation would add needless overhead. Delegate independent work with a clear goal and acceptance criteria. When delegating within your assignment, pass its parentTaskId. Members may delegate further within their existing authority.
|
|
13
|
+
|
|
14
|
+
Name exactly one integrator for each team. Workers edit only their assigned worktrees; they must not merge main, publish releases, change global daemon or npm installations, or allocate shared backlog IDs. The integrator serializes these operations. Across teams, the owner coordinates shared resources: a worktree is not a global lock.
|
|
15
|
+
|
|
16
|
+
Use a unique requestId for each action and reuse it with identical arguments when the outcome is unknown. Inspect state before retrying; a timeout does not prove no action occurred. A delivered message is not proof its recipient processed it.
|
|
17
|
+
|
|
18
|
+
Inspect the current attemptId and goalVersion before team_submit. Include artifact locations and verification evidence. The owner reviews and accepts or returns the result. All children completing does not automatically satisfy the parent goal.
|
|
19
|
+
|
|
20
|
+
Acceptance and cleanup are separate. Never delete dirty worktrees, stop user-owned sessions, or bypass permissions to finish cleanup. Use team_cancel or team_handoff and inspect the resulting status. Keep summaries concise; do not repeatedly forward full descendant transcripts.
|
|
21
|
+
|
|
22
|
+
A root member may create a persistent schedule for itself with the schedule tools; team_inspect shows schedules. Timely delivery requires an online daemon. A scheduled delivery does not prove the model completed the work. Pause or cancel schedules explicitly when they are no longer needed.
|
|
23
|
+
|
|
24
|
+
On restart inspect authoritative state and messages before taking new actions. Do not invent completed work from a previous context. Scope credentials are runtime-owned; never read, print, or copy them into prompts.
|
|
25
|
+
`;
|
|
26
|
+
const PI_TEAMS_EXTENSION = `// Managed by Very Happy Agent Teams.
|
|
27
|
+
export default async function(pi) {
|
|
28
|
+
const endpoint = process.env.HAPPY_MCP_URL;
|
|
29
|
+
if (!endpoint) return;
|
|
30
|
+
const url = new URL(endpoint);
|
|
31
|
+
if (!['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.protocol !== 'http:') throw new Error('Very Happy MCP must be local');
|
|
32
|
+
const {readFile} = await import('node:fs/promises');
|
|
33
|
+
const {join} = await import('node:path');
|
|
34
|
+
const {homedir} = await import('node:os');
|
|
35
|
+
pi.on('tool_call', async (event, ctx) => {
|
|
36
|
+
let mode = process.env.HAPPY_PERMISSION_MODE || 'default';
|
|
37
|
+
const sid = process.env.HAPPY_SESSION_ID;
|
|
38
|
+
if (sid && /^[a-zA-Z0-9_-]+$/.test(sid)) {
|
|
39
|
+
try { mode = JSON.parse(await readFile(join(process.env.HAPPY_HOME_DIR || join(homedir(), '.happy'), 'session-modes', sid + '.json'), 'utf8')).permissionMode; }
|
|
40
|
+
catch (error) { if (error.code !== 'ENOENT') mode = 'default'; }
|
|
41
|
+
}
|
|
42
|
+
if (mode === 'yolo') mode = 'bypassPermissions';
|
|
43
|
+
if (!['default','plan','acceptEdits','bypassPermissions'].includes(mode)) mode = 'default';
|
|
44
|
+
if (mode === 'bypassPermissions') return;
|
|
45
|
+
if (['read','ls','find','grep','team_inspect','change_title','report_progress'].includes(event.toolName)) return;
|
|
46
|
+
if (mode === 'plan') return {block:true,reason:'Plan mode allows read-only tools'};
|
|
47
|
+
if (mode === 'acceptEdits' && ['write','edit'].includes(event.toolName)) return;
|
|
48
|
+
try {
|
|
49
|
+
const allowed = await ctx.ui.confirm('Allow ' + event.toolName + '?', JSON.stringify(event.input || {}).slice(0,4000));
|
|
50
|
+
if (allowed === true) return;
|
|
51
|
+
} catch {}
|
|
52
|
+
return {block:true,reason:'Tool requires explicit approval'};
|
|
53
|
+
});
|
|
54
|
+
let nextId = 0;
|
|
55
|
+
async function rpc(method, params) {
|
|
56
|
+
const requestId = ++nextId;
|
|
57
|
+
const response = await fetch(endpoint, { method: 'POST', redirect: 'error', headers: {'content-type':'application/json', accept:'application/json, text/event-stream'}, body: JSON.stringify({jsonrpc:'2.0',id:requestId,method,params}), signal: AbortSignal.timeout(30000) });
|
|
58
|
+
if (!response.ok) throw new Error('Very Happy tools unavailable (HTTP ' + response.status + ')');
|
|
59
|
+
const body = await response.text();
|
|
60
|
+
let data;
|
|
61
|
+
if ((response.headers.get('content-type') || '').includes('text/event-stream')) {
|
|
62
|
+
for (const event of body.split(/\\r?\\n\\r?\\n/)) {
|
|
63
|
+
const payload = event.split(/\\r?\\n/).filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\\n');
|
|
64
|
+
if (payload) { const candidate = JSON.parse(payload); if (candidate.id === requestId) data = candidate; }
|
|
65
|
+
}
|
|
66
|
+
} else data = JSON.parse(body);
|
|
67
|
+
if (!data || data.error) throw new Error('Very Happy tool request failed');
|
|
68
|
+
return data.result;
|
|
69
|
+
}
|
|
70
|
+
await rpc('initialize', {protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'very-happy-pi-teams',version:'1'}});
|
|
71
|
+
const available = await rpc('tools/list', {});
|
|
72
|
+
for (const tool of available.tools || []) {
|
|
73
|
+
pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
|
|
74
|
+
async execute(_id, args) { const result = await rpc('tools/call', {name:tool.name,arguments:args}); return {content:result.content,details:{isError:!!result.isError}}; }
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
export { PI_TEAMS_EXTENSION as P, TEAM_SKILL as T };
|
|
@@ -4,22 +4,16 @@ 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
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
7
|
+
var api = require('./types-BGHnQXl5.cjs');
|
|
8
|
+
var persistence = require('./limits-DViGHwSj.cjs');
|
|
9
|
+
var index = require('./index-Do-kjc30.cjs');
|
|
10
|
+
var AcpBackend = require('./AcpBackend-ssQpTLks.cjs');
|
|
11
|
+
var config = require('./config-BxHOGtEK.cjs');
|
|
11
12
|
require('axios');
|
|
12
|
-
require('chalk');
|
|
13
|
-
require('node:util');
|
|
14
|
-
require('node:fs');
|
|
15
|
-
require('node:os');
|
|
16
|
-
require('node:fs/promises');
|
|
17
13
|
require('node:events');
|
|
18
14
|
require('socket.io-client');
|
|
19
15
|
require('zod');
|
|
20
16
|
require('@paralleldrive/cuid2');
|
|
21
|
-
require('tweetnacl');
|
|
22
|
-
require('./limits-KdWKmwtH.cjs');
|
|
23
17
|
require('child_process');
|
|
24
18
|
require('util');
|
|
25
19
|
require('fs/promises');
|
|
@@ -28,13 +22,19 @@ require('path');
|
|
|
28
22
|
require('cross-spawn');
|
|
29
23
|
require('url');
|
|
30
24
|
require('os');
|
|
25
|
+
require('node:fs/promises');
|
|
26
|
+
require('node:os');
|
|
31
27
|
require('node:child_process');
|
|
32
28
|
require('node-pty');
|
|
29
|
+
require('node:fs');
|
|
33
30
|
require('fs');
|
|
31
|
+
require('tweetnacl');
|
|
34
32
|
require('node:readline');
|
|
35
33
|
require('@anthropic-ai/sandbox-runtime');
|
|
36
34
|
require('expo-server-sdk');
|
|
37
|
-
require('
|
|
35
|
+
require('chalk');
|
|
36
|
+
require('node:util');
|
|
37
|
+
require('./agentGuidance-Cg9ZG1pP.cjs');
|
|
38
38
|
require('@anthropic-ai/claude-agent-sdk');
|
|
39
39
|
require('ps-list');
|
|
40
40
|
require('tmp');
|
|
@@ -860,8 +860,8 @@ ${formattedMessages}
|
|
|
860
860
|
|
|
861
861
|
async function runGemini(opts) {
|
|
862
862
|
const sessionTag = node_crypto.randomUUID();
|
|
863
|
-
|
|
864
|
-
const api = await
|
|
863
|
+
api.connectionState.setBackend("Gemini");
|
|
864
|
+
const api$1 = await api.ApiClient.create(opts.credentials);
|
|
865
865
|
const settings = await persistence.readSettings();
|
|
866
866
|
const machineId = settings?.machineId;
|
|
867
867
|
const sandboxConfig = settings?.sandboxConfig;
|
|
@@ -870,14 +870,14 @@ async function runGemini(opts) {
|
|
|
870
870
|
process.exit(1);
|
|
871
871
|
}
|
|
872
872
|
persistence.logger.debug(`Using machineId: ${machineId}`);
|
|
873
|
-
await api.getOrCreateMachine({
|
|
873
|
+
await api$1.getOrCreateMachine({
|
|
874
874
|
machineId,
|
|
875
875
|
metadata: index.initialMachineMetadata
|
|
876
876
|
});
|
|
877
877
|
let cloudToken = void 0;
|
|
878
878
|
let currentUserEmail = void 0;
|
|
879
879
|
try {
|
|
880
|
-
const vendorToken = await api.getVendorToken("gemini");
|
|
880
|
+
const vendorToken = await api$1.getVendorToken("gemini");
|
|
881
881
|
if (vendorToken?.oauth?.access_token) {
|
|
882
882
|
cloudToken = vendorToken.oauth.access_token;
|
|
883
883
|
persistence.logger.debug("[Gemini] Using OAuth token from Happy cloud");
|
|
@@ -897,7 +897,7 @@ async function runGemini(opts) {
|
|
|
897
897
|
}
|
|
898
898
|
}
|
|
899
899
|
} catch (error) {
|
|
900
|
-
persistence.logger.debug("[Gemini] Failed to fetch cloud token:",
|
|
900
|
+
persistence.logger.debug("[Gemini] Failed to fetch cloud token:", api.errorLogMetadata(error));
|
|
901
901
|
}
|
|
902
902
|
const { state, metadata } = index.createSessionMetadata({
|
|
903
903
|
flavor: "gemini",
|
|
@@ -905,7 +905,7 @@ async function runGemini(opts) {
|
|
|
905
905
|
startedBy: opts.startedBy,
|
|
906
906
|
sandbox: sandboxConfig
|
|
907
907
|
});
|
|
908
|
-
const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
908
|
+
const response = await api$1.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
909
909
|
let session;
|
|
910
910
|
let permissionHandler;
|
|
911
911
|
let isProcessingMessage = false;
|
|
@@ -921,7 +921,7 @@ async function runGemini(opts) {
|
|
|
921
921
|
}
|
|
922
922
|
};
|
|
923
923
|
const { session: initialSession, reconnectionHandle } = index.setupOfflineReconnection({
|
|
924
|
-
api,
|
|
924
|
+
api: api$1,
|
|
925
925
|
sessionTag,
|
|
926
926
|
metadata,
|
|
927
927
|
state,
|
|
@@ -1028,7 +1028,7 @@ async function runGemini(opts) {
|
|
|
1028
1028
|
const sendReady = () => {
|
|
1029
1029
|
session.sendSessionEvent({ type: "ready" });
|
|
1030
1030
|
try {
|
|
1031
|
-
api.push().sendSessionNotification({
|
|
1031
|
+
api$1.push().sendSessionNotification({
|
|
1032
1032
|
kind: "done",
|
|
1033
1033
|
metadata: session.getMetadata(),
|
|
1034
1034
|
data: {
|
|
@@ -1168,7 +1168,7 @@ async function runGemini(opts) {
|
|
|
1168
1168
|
process.stdin.setEncoding("utf8");
|
|
1169
1169
|
}
|
|
1170
1170
|
const happyServer = await index.startHappyServer(session);
|
|
1171
|
-
const bridgeCommand = path.join(
|
|
1171
|
+
const bridgeCommand = path.join(api.projectPath(), "bin", "very-happy-mcp.mjs");
|
|
1172
1172
|
const mcpServers = {
|
|
1173
1173
|
happy: {
|
|
1174
1174
|
command: bridgeCommand,
|
|
@@ -1211,10 +1211,10 @@ async function runGemini(opts) {
|
|
|
1211
1211
|
case "status":
|
|
1212
1212
|
const statusDetail = msg.detail ? typeof msg.detail === "object" ? JSON.stringify(msg.detail) : String(msg.detail) : "";
|
|
1213
1213
|
persistence.logger.debug(`[gemini] Status changed: ${msg.status}`, {
|
|
1214
|
-
detail:
|
|
1214
|
+
detail: api.contentLogMetadata(statusDetail)
|
|
1215
1215
|
});
|
|
1216
1216
|
if (msg.status === "error") {
|
|
1217
|
-
persistence.logger.debug("[gemini] Error status received:",
|
|
1217
|
+
persistence.logger.debug("[gemini] Error status received:", api.contentLogMetadata(statusDetail));
|
|
1218
1218
|
session.sendAgentMessage("gemini", {
|
|
1219
1219
|
type: "turn_aborted",
|
|
1220
1220
|
id: node_crypto.randomUUID()
|
|
@@ -1266,7 +1266,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1266
1266
|
const isInvestigationTool = msg.toolName === "codebase_investigator" || typeof msg.toolName === "string" && msg.toolName.includes("investigator");
|
|
1267
1267
|
persistence.logger.debug(`[gemini] \u{1F527} Tool call received: ${msg.toolName} (${msg.callId})${isInvestigationTool ? " [INVESTIGATION]" : ""}`);
|
|
1268
1268
|
if (isInvestigationTool && msg.args && typeof msg.args === "object" && "objective" in msg.args) {
|
|
1269
|
-
persistence.logger.debug("[gemini] Investigation objective received:",
|
|
1269
|
+
persistence.logger.debug("[gemini] Investigation objective received:", api.contentLogMetadata(msg.args.objective));
|
|
1270
1270
|
}
|
|
1271
1271
|
messageBuffer.addMessage(`Executing: ${msg.toolName}${toolArgs ? ` ${toolArgs}${toolArgs.length >= 100 ? "..." : ""}` : ""}`, "tool");
|
|
1272
1272
|
session.sendAgentMessage("gemini", {
|
|
@@ -1292,7 +1292,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1292
1292
|
}
|
|
1293
1293
|
if (isError) {
|
|
1294
1294
|
const errorMsg = msg.result.error || "Tool call failed";
|
|
1295
|
-
persistence.logger.debug("[gemini] Tool call error received:",
|
|
1295
|
+
persistence.logger.debug("[gemini] Tool call error received:", api.contentLogMetadata(errorMsg));
|
|
1296
1296
|
messageBuffer.addMessage(`Error: ${errorMsg}`, "status");
|
|
1297
1297
|
} else {
|
|
1298
1298
|
if (resultSize > 1e3) {
|
|
@@ -1410,7 +1410,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1410
1410
|
const thinkingText = thinkingPayload && typeof thinkingPayload === "object" && "text" in thinkingPayload ? String(thinkingPayload.text || "") : "";
|
|
1411
1411
|
if (thinkingText) {
|
|
1412
1412
|
reasoningProcessor.processChunk(thinkingText);
|
|
1413
|
-
persistence.logger.debug("[gemini] Thinking chunk received:",
|
|
1413
|
+
persistence.logger.debug("[gemini] Thinking chunk received:", api.contentLogMetadata(thinkingText));
|
|
1414
1414
|
if (!thinkingText.startsWith("**")) {
|
|
1415
1415
|
const thinkingPreview = thinkingText.substring(0, 100);
|
|
1416
1416
|
messageBuffer.updateLastMessage(`[Thinking] ${thinkingPreview}...`, "system");
|
|
@@ -1549,7 +1549,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1549
1549
|
promptToSend = historyContext + promptToSend;
|
|
1550
1550
|
persistence.logger.debug(`[gemini] Injected conversation history context (${historyContext.length} chars)`);
|
|
1551
1551
|
}
|
|
1552
|
-
persistence.logger.debug("[gemini] Sending prompt to Gemini:",
|
|
1552
|
+
persistence.logger.debug("[gemini] Sending prompt to Gemini:", api.contentLogMetadata(promptToSend));
|
|
1553
1553
|
const MAX_RETRIES = 3;
|
|
1554
1554
|
const RETRY_DELAY_MS = 2e3;
|
|
1555
1555
|
let lastError = null;
|
|
@@ -1584,7 +1584,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1584
1584
|
const isInternalError = errorCode === -32603;
|
|
1585
1585
|
const isRetryable = isEmptyResponseError || isInternalError;
|
|
1586
1586
|
if (isRetryable && attempt < MAX_RETRIES) {
|
|
1587
|
-
persistence.logger.debug(`[gemini] Retryable error on attempt ${attempt}/${MAX_RETRIES}:`,
|
|
1587
|
+
persistence.logger.debug(`[gemini] Retryable error on attempt ${attempt}/${MAX_RETRIES}:`, api.contentLogMetadata(errorDetails));
|
|
1588
1588
|
messageBuffer.addMessage(`Gemini returned empty response, retrying (${attempt}/${MAX_RETRIES})...`, "status");
|
|
1589
1589
|
await new Promise((resolve2) => setTimeout(resolve2, RETRY_DELAY_MS * attempt));
|
|
1590
1590
|
continue;
|
|
@@ -1599,7 +1599,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1599
1599
|
first = false;
|
|
1600
1600
|
}
|
|
1601
1601
|
} catch (error) {
|
|
1602
|
-
persistence.logger.debug("[gemini] Error in gemini session:",
|
|
1602
|
+
persistence.logger.debug("[gemini] Error in gemini session:", api.errorLogMetadata(error));
|
|
1603
1603
|
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1604
1604
|
if (isAbortError) {
|
|
1605
1605
|
messageBuffer.addMessage("Aborted by user", "status");
|
|
@@ -1667,7 +1667,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1667
1667
|
id: node_crypto.randomUUID(),
|
|
1668
1668
|
...options.length > 0 && { options }
|
|
1669
1669
|
};
|
|
1670
|
-
persistence.logger.debug("[gemini] Sending complete message to mobile:",
|
|
1670
|
+
persistence.logger.debug("[gemini] Sending complete message to mobile:", api.contentLogMetadata(finalMessageText));
|
|
1671
1671
|
session.sendAgentMessage("gemini", messagePayload);
|
|
1672
1672
|
accumulatedResponse = "";
|
|
1673
1673
|
isResponseInProgress = false;
|
|
@@ -2,22 +2,16 @@ 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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { f as connectionState, A as ApiClient, e as errorLogMetadata, p as projectPath, b as contentLogMetadata } from './types-DLDkfrTC.mjs';
|
|
6
|
+
import { l as logger, f as readSettings, g as encodeBase64 } from './limits-FjbPJPsF.mjs';
|
|
7
|
+
import { J as GEMINI_API_KEY_ENV, K as GOOGLE_API_KEY_ENV, G as GEMINI_MODEL_ENV, E as BasePermissionHandler, L as BaseReasoningProcessor, n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, x as MessageQueue2, B as hashObject, N as MessageBuffer, O as CHANGE_TITLE_INSTRUCTION, y as registerKillSessionHandler, C as startHappyServer } from './index-CrSifgJM.mjs';
|
|
8
|
+
import { A as AcpBackend } from './AcpBackend-DOQGItcS.mjs';
|
|
9
|
+
import { readGeminiLocalConfig, determineGeminiModel, getGeminiModelSource, getInitialGeminiModel, saveGeminiModelToConfig } from './config-CZNIz3W0.mjs';
|
|
9
10
|
import 'axios';
|
|
10
|
-
import 'chalk';
|
|
11
|
-
import 'node:util';
|
|
12
|
-
import 'node:fs';
|
|
13
|
-
import 'node:os';
|
|
14
|
-
import 'node:fs/promises';
|
|
15
11
|
import 'node:events';
|
|
16
12
|
import 'socket.io-client';
|
|
17
13
|
import 'zod';
|
|
18
14
|
import '@paralleldrive/cuid2';
|
|
19
|
-
import 'tweetnacl';
|
|
20
|
-
import './limits-CwFb5S2b.mjs';
|
|
21
15
|
import 'child_process';
|
|
22
16
|
import 'util';
|
|
23
17
|
import 'fs/promises';
|
|
@@ -26,13 +20,19 @@ import 'path';
|
|
|
26
20
|
import 'cross-spawn';
|
|
27
21
|
import 'url';
|
|
28
22
|
import 'os';
|
|
23
|
+
import 'node:fs/promises';
|
|
24
|
+
import 'node:os';
|
|
29
25
|
import 'node:child_process';
|
|
30
26
|
import 'node-pty';
|
|
27
|
+
import 'node:fs';
|
|
31
28
|
import 'fs';
|
|
29
|
+
import 'tweetnacl';
|
|
32
30
|
import 'node:readline';
|
|
33
31
|
import '@anthropic-ai/sandbox-runtime';
|
|
34
32
|
import 'expo-server-sdk';
|
|
35
|
-
import '
|
|
33
|
+
import 'chalk';
|
|
34
|
+
import 'node:util';
|
|
35
|
+
import './agentGuidance-QpYwykb9.mjs';
|
|
36
36
|
import '@anthropic-ai/claude-agent-sdk';
|
|
37
37
|
import 'ps-list';
|
|
38
38
|
import 'tmp';
|
|
@@ -1166,7 +1166,7 @@ async function runGemini(opts) {
|
|
|
1166
1166
|
process.stdin.setEncoding("utf8");
|
|
1167
1167
|
}
|
|
1168
1168
|
const happyServer = await startHappyServer(session);
|
|
1169
|
-
const bridgeCommand = join(projectPath(), "bin", "happy-mcp.mjs");
|
|
1169
|
+
const bridgeCommand = join(projectPath(), "bin", "very-happy-mcp.mjs");
|
|
1170
1170
|
const mcpServers = {
|
|
1171
1171
|
happy: {
|
|
1172
1172
|
command: bridgeCommand,
|