very-happy-cli 0.2.151 → 0.2.153
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-blE9IEod.cjs → AcpBackend-DSt8nTYm.cjs} +2 -2
- package/dist/{AcpBackend-BjTA5gUB.mjs → AcpBackend-YDGv4E-g.mjs} +2 -2
- package/dist/{AcpSessionManager-D-9mrp40.mjs → AcpSessionManager-Cxf5HQX1.mjs} +1 -1
- package/dist/{AcpSessionManager-bF8PYu_B.cjs → AcpSessionManager-uov6I9C8.cjs} +9 -9
- package/dist/agentGuidance-BM4kMNyr.cjs +553 -0
- package/dist/agentGuidance-C8unGWp7.mjs +529 -0
- package/dist/{agentHome-D6MSfwnp.cjs → agentHome-BJ7bde35.cjs} +4 -4
- package/dist/{agentHome-CeFDNzvq.mjs → agentHome-D68Xh2ae.mjs} +4 -4
- package/dist/auto-C-90ojIH.cjs +472 -0
- package/dist/auto-CcXxfqkC.mjs +466 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -2
- package/dist/codex/happyMcpStdioBridge.mjs +4 -2
- package/dist/{config-C-A1XF62.mjs → config-CHJ189UJ.mjs} +6 -6
- package/dist/{config-Cadw1sAk.cjs → config-D23kF0_u.cjs} +6 -6
- package/dist/{index-DYQgcgL2.mjs → index-BXA9fxbK.mjs} +6 -6
- package/dist/{index-CIyjJy8D.mjs → index-BacwoXzs.mjs} +748 -55
- package/dist/{index-DEp6CmmS.mjs → index-BgYZGODP.mjs} +20 -20
- package/dist/{index-vyRqLjDv.cjs → index-D49WvbDT.cjs} +6 -6
- package/dist/{index-BKGspSWk.cjs → index-DEOVn46R.cjs} +21 -21
- package/dist/{index-CqtfRzug.cjs → index-DHrwHxHf.cjs} +805 -105
- package/dist/index.cjs +5 -5
- package/dist/index.mjs +5 -5
- package/dist/install-BiOXBnnS.cjs +118 -0
- package/dist/install-Ca7_Z3bV.mjs +114 -0
- package/dist/{installPiTools-C-Lxv4Bt.mjs → installPiTools-Cj7yU9Fc.mjs} +2 -1
- package/dist/{installPiTools-BKnXjrVu.cjs → installPiTools-DuZ6jZvX.cjs} +3 -2
- package/dist/{installTerminalHooks-Bf_adbl3.cjs → installTerminalHooks-BF0kL0ae.cjs} +8 -8
- package/dist/{installTerminalHooks-Ch_yTtEA.mjs → installTerminalHooks-Bpl32K75.mjs} +7 -7
- package/dist/lib.cjs +4 -4
- package/dist/lib.mjs +4 -4
- package/dist/limits-BtAuKRl2.mjs +2080 -0
- package/dist/limits-DY_sFA3J.cjs +2175 -0
- package/dist/{mcp-zu8n8TkR.mjs → mcp-DQkmk4VV.mjs} +15 -8
- package/dist/{mcp-CMxEZ6-0.cjs → mcp-JpqI2POW.cjs} +15 -7
- package/dist/piExtension.d.mts +4 -1
- package/dist/piExtension.mjs +24 -1
- package/dist/{piRuntime-Cu6cXZ6N.mjs → piRuntime-DBpe12NN.mjs} +2 -2
- package/dist/{piRuntime-D3ny0mVg.cjs → piRuntime-DRbbeQ72.cjs} +2 -2
- package/dist/{piTerminal-M677UP7y.mjs → piTerminal-BoHvPEZ_.mjs} +8 -7
- package/dist/{piTerminal-kDSd_r9X.cjs → piTerminal-qdJ9KZVL.cjs} +8 -7
- package/dist/{runGemini-DuwdVV1O.cjs → runGemini-CtOEFvhf.cjs} +9 -9
- package/dist/{runGemini-DvJwQVGp.mjs → runGemini-YqH7a-7O.mjs} +8 -8
- package/dist/{runOpenClaw-BG82bF18.mjs → runOpenClaw-DSSyFQBb.mjs} +7 -7
- package/dist/{runOpenClaw-BsvtCE_H.cjs → runOpenClaw-DoqZXBvL.cjs} +7 -7
- package/dist/{send-aUkjhey7.cjs → send-9075kHZP.cjs} +6 -6
- package/dist/{send-o6WM-1G8.mjs → send-Cs-RwqNf.mjs} +6 -6
- package/dist/{sessions-DlU3foY_.mjs → sessions-iBnYzVUK.mjs} +6 -6
- package/dist/{sessions-B3vP18kz.cjs → sessions-qULzUzlI.cjs} +6 -6
- package/dist/{spawn-DxHIIqhi.mjs → spawn-C6fRkedx.mjs} +7 -32
- package/dist/{spawn-DH6_p4-f.cjs → spawn-od9V23cc.cjs} +8 -33
- package/dist/{teams-DoAA_CcN.mjs → teams-CkrWQ4uM.mjs} +16 -73
- package/dist/{teams-BW8D1n3o.cjs → teams-CrCzU-AV.cjs} +14 -71
- package/dist/terminalTitleSuggest-DKgX3osW.mjs +38 -0
- package/dist/terminalTitleSuggest-Dji8I0Jy.cjs +45 -0
- package/dist/{todo-Bcc2XxGh.cjs → todo-BCT_ZPuV.cjs} +4 -23
- package/dist/{todo-BhJbG5eq.mjs → todo-aCZmSt27.mjs} +2 -21
- package/dist/{types-BN3eiipi.mjs → types-BvqOVUOW.mjs} +214 -1022
- package/dist/{types-CPubg05Z.cjs → types-CUCU7Vw8.cjs} +43 -863
- package/package.json +7 -7
- package/dist/agentGuidance-BXa9ikrZ.cjs +0 -267
- package/dist/agentGuidance-Dvo3cZMW.mjs +0 -251
- package/dist/limits-DS-bnYYp.mjs +0 -1165
- package/dist/limits-DwcY3kyi.cjs +0 -1236
- package/dist/projectPath-CSpqWdxp.mjs +0 -10
- package/dist/projectPath-ChLhdOyt.cjs +0 -13
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var os = require('node:os');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
|
-
var index = require('./types-
|
|
7
|
-
var persistence = require('./limits-
|
|
6
|
+
var index = require('./types-CUCU7Vw8.cjs');
|
|
7
|
+
var persistence = require('./limits-DY_sFA3J.cjs');
|
|
8
8
|
var spawn = require('cross-spawn');
|
|
9
9
|
var path = require('node:path');
|
|
10
10
|
var node_readline = require('node:readline');
|
|
11
11
|
var node_fs = require('node:fs');
|
|
12
|
-
var
|
|
13
|
-
var agentGuidance = require('./agentGuidance-
|
|
12
|
+
var terminalTitleSuggest = require('./terminalTitleSuggest-Dji8I0Jy.cjs');
|
|
13
|
+
var agentGuidance = require('./agentGuidance-BM4kMNyr.cjs');
|
|
14
14
|
var promises = require('node:fs/promises');
|
|
15
15
|
var fs = require('fs/promises');
|
|
16
16
|
var ink = require('ink');
|
|
@@ -492,7 +492,7 @@ function shouldIncludeCoAuthoredBy() {
|
|
|
492
492
|
|
|
493
493
|
const BASE_SYSTEM_PROMPT = (() => agentGuidance.trimIdent(`
|
|
494
494
|
This chat is titled automatically from the user's first message. Call "mcp__happy__change_title" only when the topic has clearly moved on or the current title is too generic to find this chat later. Do not call it as a ritual at the start of a chat, and not for a quick question you can just answer.
|
|
495
|
-
`))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" +
|
|
495
|
+
`))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" + persistence.BUILTIN_TODO_DISCOVERY;
|
|
496
496
|
const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
|
|
497
497
|
When you yourself create a git commit, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
498
498
|
|
|
@@ -522,7 +522,7 @@ class ExitCodeError extends Error {
|
|
|
522
522
|
this.exitCode = exitCode;
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
|
-
const claudeCliPath = path.resolve(path.join(
|
|
525
|
+
const claudeCliPath = path.resolve(path.join(terminalTitleSuggest.projectPath(), "scripts", "claude_local_launcher.cjs"));
|
|
526
526
|
function quoteShellArg(value) {
|
|
527
527
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
528
528
|
}
|
|
@@ -3507,7 +3507,7 @@ class StreamRelay {
|
|
|
3507
3507
|
}
|
|
3508
3508
|
this.pending.text += text;
|
|
3509
3509
|
this.pending.bytes += Buffer.byteLength(text, "utf8");
|
|
3510
|
-
if (this.pending.bytes >= STREAM_FLUSH_MAX_BYTES || this.pending.text.length >=
|
|
3510
|
+
if (this.pending.bytes >= STREAM_FLUSH_MAX_BYTES || this.pending.text.length >= persistence.STREAM_DELTA_MAX_CHARS) {
|
|
3511
3511
|
this.flushPending();
|
|
3512
3512
|
return;
|
|
3513
3513
|
}
|
|
@@ -4878,8 +4878,8 @@ function createSessionMetadata(opts) {
|
|
|
4878
4878
|
...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
|
|
4879
4879
|
homeDir: os.homedir(),
|
|
4880
4880
|
happyHomeDir: persistence.configuration.happyHomeDir,
|
|
4881
|
-
happyLibDir:
|
|
4882
|
-
happyToolsDir: path.resolve(
|
|
4881
|
+
happyLibDir: terminalTitleSuggest.projectPath(),
|
|
4882
|
+
happyToolsDir: path.resolve(terminalTitleSuggest.projectPath(), "tools", "unpacked"),
|
|
4883
4883
|
startedFromDaemon: opts.startedBy === "daemon",
|
|
4884
4884
|
hostPid: process.pid,
|
|
4885
4885
|
startedBy: opts.startedBy || "terminal",
|
|
@@ -5206,7 +5206,7 @@ function printCliUpdateStatus(state) {
|
|
|
5206
5206
|
console.log(chalk.yellow(" \u25B3 A newer CLI is available."));
|
|
5207
5207
|
}
|
|
5208
5208
|
if (summary.installCommand) console.log(` Update: ${summary.installCommand}`);
|
|
5209
|
-
const facts = gatherInstallLocationFacts(
|
|
5209
|
+
const facts = gatherInstallLocationFacts(terminalTitleSuggest.projectPath());
|
|
5210
5210
|
for (const warning of facts ? installLocationWarnings(facts, state?.cliUpdate?.recommendedVersion ?? void 0) : []) {
|
|
5211
5211
|
console.log(chalk.yellow(` \u26A0 ${warning}`));
|
|
5212
5212
|
}
|
|
@@ -5338,7 +5338,7 @@ ${typeLabels[type] || type}:`));
|
|
|
5338
5338
|
console.log(chalk.yellow("No log files found"));
|
|
5339
5339
|
}
|
|
5340
5340
|
console.log(chalk.bold("\n\u{1F527} Daemon Spawn Diagnostics"));
|
|
5341
|
-
const projectRoot =
|
|
5341
|
+
const projectRoot = terminalTitleSuggest.projectPath();
|
|
5342
5342
|
const wrapperPath = path.join(projectRoot, "bin", "very-happy.mjs");
|
|
5343
5343
|
const cliEntrypoint = path.join(projectRoot, "dist", "index.mjs");
|
|
5344
5344
|
console.log(`Project Root: ${chalk.blue(projectRoot)}`);
|
|
@@ -5402,8 +5402,8 @@ ${typeLabels[type] || type}:`));
|
|
|
5402
5402
|
}
|
|
5403
5403
|
console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
|
|
5404
5404
|
try {
|
|
5405
|
-
const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-
|
|
5406
|
-
const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-
|
|
5405
|
+
const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-BJ7bde35.cjs'); });
|
|
5406
|
+
const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-CUCU7Vw8.cjs'); }).then(function (n) { return n.index; });
|
|
5407
5407
|
const homes = await resolveAgentHomesForCli();
|
|
5408
5408
|
console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
|
|
5409
5409
|
console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));
|
|
@@ -5514,7 +5514,7 @@ async function registryVersion(name) {
|
|
|
5514
5514
|
return typeof data.version === "string" ? stableVersion(data.version) : null;
|
|
5515
5515
|
}
|
|
5516
5516
|
async function detectAgentVersions(deps = { probe: probeAgentVersion, registryVersion }, now = Date.now()) {
|
|
5517
|
-
const agents = await Promise.all(
|
|
5517
|
+
const agents = await Promise.all(persistence.AGENT_VERSION_SOURCES.map(async (source) => {
|
|
5518
5518
|
const local = source.command ? await deps.probe(source.command).catch(() => ({ version: null, missing: false })) : { version: stableVersion(persistence.packageJson.dependencies["@anthropic-ai/claude-agent-sdk"]), missing: false };
|
|
5519
5519
|
if (local.missing) return { id: source.id, installed: null, latest: null, status: "not-installed" };
|
|
5520
5520
|
const latest = await deps.registryVersion(source.package).catch(() => null);
|
|
@@ -5953,7 +5953,7 @@ function getRuntime() {
|
|
|
5953
5953
|
const isBun = () => getRuntime() === "bun";
|
|
5954
5954
|
|
|
5955
5955
|
function spawnHappyCLI(args, options = {}) {
|
|
5956
|
-
const projectRoot =
|
|
5956
|
+
const projectRoot = terminalTitleSuggest.projectPath();
|
|
5957
5957
|
const entrypoint = path.join(projectRoot, "dist", "index.mjs");
|
|
5958
5958
|
let directory;
|
|
5959
5959
|
if ("cwd" in options) {
|
|
@@ -6230,7 +6230,7 @@ const DENIED_BASENAME_PATTERNS = [
|
|
|
6230
6230
|
const DENIED_PATH_SUFFIXES = [
|
|
6231
6231
|
`${path.sep}.git${path.sep}config`
|
|
6232
6232
|
];
|
|
6233
|
-
function expandHome(input, home) {
|
|
6233
|
+
function expandHome$1(input, home) {
|
|
6234
6234
|
if (input === "~") return home;
|
|
6235
6235
|
if (input.startsWith(`~${path.sep}`) || input.startsWith("~/")) {
|
|
6236
6236
|
return path.resolve(home, input.slice(2));
|
|
@@ -6253,7 +6253,7 @@ function checkPreviewPath(input, opts) {
|
|
|
6253
6253
|
if (input.includes("\0")) {
|
|
6254
6254
|
return { resolved: "", deniedReason: "path contains a NUL byte" };
|
|
6255
6255
|
}
|
|
6256
|
-
const expanded = expandHome(input.trim(), home);
|
|
6256
|
+
const expanded = expandHome$1(input.trim(), home);
|
|
6257
6257
|
const resolved = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
|
|
6258
6258
|
for (const dir of DENIED_HOME_DIRS) {
|
|
6259
6259
|
if (isUnder(resolved, path.resolve(home, dir))) {
|
|
@@ -7784,7 +7784,7 @@ const defaults = {
|
|
|
7784
7784
|
npm: runNpm,
|
|
7785
7785
|
fs: { lstat: promises.lstat, readlink: promises.readlink, unlink: promises.unlink, rm: promises.rm, readFile: promises.readFile, symlink: promises.symlink, access: promises.access },
|
|
7786
7786
|
platform: process.platform,
|
|
7787
|
-
runningPackageDir:
|
|
7787
|
+
runningPackageDir: terminalTitleSuggest.projectPath()
|
|
7788
7788
|
};
|
|
7789
7789
|
async function runningVersion(deps) {
|
|
7790
7790
|
if (!deps.runningPackageDir) return null;
|
|
@@ -8144,7 +8144,7 @@ function parseJsonObject(text) {
|
|
|
8144
8144
|
return null;
|
|
8145
8145
|
}
|
|
8146
8146
|
}
|
|
8147
|
-
function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
8147
|
+
function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href))).resolve(s)) {
|
|
8148
8148
|
const platformArch = `${platform}-${arch}`;
|
|
8149
8149
|
const candidates = [
|
|
8150
8150
|
`@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
|
|
@@ -8857,8 +8857,8 @@ function createMirrorManager(deps) {
|
|
|
8857
8857
|
machineId: deps.machineId,
|
|
8858
8858
|
homeDir: os.homedir(),
|
|
8859
8859
|
happyHomeDir: persistence.configuration.happyHomeDir,
|
|
8860
|
-
happyLibDir:
|
|
8861
|
-
happyToolsDir: path.resolve(
|
|
8860
|
+
happyLibDir: terminalTitleSuggest.projectPath(),
|
|
8861
|
+
happyToolsDir: path.resolve(terminalTitleSuggest.projectPath(), "tools", "unpacked"),
|
|
8862
8862
|
startedBy: "terminal",
|
|
8863
8863
|
lifecycleState: "running",
|
|
8864
8864
|
lifecycleStateSince: Date.now(),
|
|
@@ -9380,6 +9380,20 @@ export default async function(pi) {
|
|
|
9380
9380
|
return data.result;
|
|
9381
9381
|
}
|
|
9382
9382
|
await rpc('initialize', {protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'very-happy-pi-teams',version:'1'}});
|
|
9383
|
+
if (!managed && pi.setSessionName) {
|
|
9384
|
+
// B-500: name the pi session from its first prompt (managed sessions are titled by TitleGenerator).
|
|
9385
|
+
let titleArmed = false, generation = 0;
|
|
9386
|
+
pi.on('session_start', async () => { generation += 1; titleArmed = !pi.getSessionName?.(); });
|
|
9387
|
+
pi.on('before_agent_start', async (event) => {
|
|
9388
|
+
const prompt = event && typeof event.prompt === 'string' ? event.prompt : '';
|
|
9389
|
+
if (!titleArmed || !prompt.trim()) return;
|
|
9390
|
+
titleArmed = false;
|
|
9391
|
+
const expected = generation;
|
|
9392
|
+
rpc('very-happy/terminal-title-suggest', {prompt}).then(result => {
|
|
9393
|
+
if (result && result.title && expected === generation && !pi.getSessionName?.()) pi.setSessionName(result.title);
|
|
9394
|
+
}).catch(() => {});
|
|
9395
|
+
});
|
|
9396
|
+
}
|
|
9383
9397
|
const available = await rpc('tools/list', {});
|
|
9384
9398
|
for (const tool of available.tools || []) {
|
|
9385
9399
|
pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
|
|
@@ -9539,11 +9553,14 @@ function agentPiece(body) {
|
|
|
9539
9553
|
}
|
|
9540
9554
|
return null;
|
|
9541
9555
|
}
|
|
9542
|
-
function
|
|
9556
|
+
function userPromptText(body) {
|
|
9557
|
+
return isUserPrompt(body) ? body.content.text : null;
|
|
9558
|
+
}
|
|
9559
|
+
function analyzeLatestTurn(entries, options = {}) {
|
|
9543
9560
|
const lastSeq = entries.length > 0 ? entries[entries.length - 1].seq : 0;
|
|
9544
9561
|
let userIndex = -1;
|
|
9545
9562
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
9546
|
-
if (isUserPrompt(entries[i].body)) {
|
|
9563
|
+
if (isUserPrompt(entries[i].body) && (!options.anchor || options.anchor(entries[i].body))) {
|
|
9547
9564
|
userIndex = i;
|
|
9548
9565
|
break;
|
|
9549
9566
|
}
|
|
@@ -9557,6 +9574,10 @@ function analyzeLatestTurn(entries) {
|
|
|
9557
9574
|
const emitsStarts = entries.some((entry) => sessionEnvelope(entry.body)?.ev?.t === "turn-start");
|
|
9558
9575
|
for (let i = userIndex + 1; i < entries.length; i++) {
|
|
9559
9576
|
const body = entries[i].body;
|
|
9577
|
+
if (options.anchor && isUserPrompt(body)) {
|
|
9578
|
+
if (ended) break;
|
|
9579
|
+
continue;
|
|
9580
|
+
}
|
|
9560
9581
|
const env = sessionEnvelope(body);
|
|
9561
9582
|
if (env?.ev?.t === "turn-start") {
|
|
9562
9583
|
lastMarker = "start";
|
|
@@ -10049,7 +10070,7 @@ function readReceipt(home, id) {
|
|
|
10049
10070
|
throw error;
|
|
10050
10071
|
}
|
|
10051
10072
|
}
|
|
10052
|
-
function writeReceipt(home, receipt) {
|
|
10073
|
+
function writeReceipt$1(home, receipt) {
|
|
10053
10074
|
persistence.ensurePrivateDirectorySync(path.join(home, "teams", "effects"));
|
|
10054
10075
|
const target = receiptPath(home, receipt.operationId);
|
|
10055
10076
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
@@ -10126,7 +10147,7 @@ function createTeamWorker(deps) {
|
|
|
10126
10147
|
const action = async (teamId, requestId, value) => (await http.post(`/v1/teams/${encodeURIComponent(teamId)}/actions`, { requestId, action: value })).data;
|
|
10127
10148
|
async function complete(op, receipt) {
|
|
10128
10149
|
await action(op.teamId, `complete-${op.id}`, { type: "complete-operation", operationId: op.id, machineId: deps.machineId, claimId: receipt.claimId, ...receipt.sessionId ? { sessionId: receipt.sessionId } : {}, ...op.type === "spawn" && receipt.directory ? { workingDirectory: receipt.directory } : {} });
|
|
10129
|
-
writeReceipt(home, { ...receipt, phase: "completed" });
|
|
10150
|
+
writeReceipt$1(home, { ...receipt, phase: "completed" });
|
|
10130
10151
|
}
|
|
10131
10152
|
async function execute(op) {
|
|
10132
10153
|
let receipt = readReceipt(home, op.id);
|
|
@@ -10143,7 +10164,7 @@ function createTeamWorker(deps) {
|
|
|
10143
10164
|
op = claimed.operation;
|
|
10144
10165
|
credential = claimed.credential;
|
|
10145
10166
|
receipt = { operationId: op.id, teamId: op.teamId, claimId: op.claimId, phase: "prepared" };
|
|
10146
|
-
writeReceipt(home, receipt);
|
|
10167
|
+
writeReceipt$1(home, receipt);
|
|
10147
10168
|
}
|
|
10148
10169
|
if (receipt.phase === "completed") return;
|
|
10149
10170
|
try {
|
|
@@ -10156,7 +10177,7 @@ function createTeamWorker(deps) {
|
|
|
10156
10177
|
if (!task) throw new Error("Task state unavailable; refusing to start");
|
|
10157
10178
|
if (task.currentAttemptId !== op.attemptId || ["done", "cancelled"].includes(task.status)) {
|
|
10158
10179
|
await action(op.teamId, `cancelled-before-spawn-${op.id}`, { type: "fail-operation", operationId: op.id, machineId: deps.machineId, claimId: receipt.claimId, error: "task_closed_before_spawn" });
|
|
10159
|
-
writeReceipt(home, { ...receipt, phase: "completed" });
|
|
10180
|
+
writeReceipt$1(home, { ...receipt, phase: "completed" });
|
|
10160
10181
|
return;
|
|
10161
10182
|
}
|
|
10162
10183
|
if (!op.directory) throw new Error("No repository selected for worker");
|
|
@@ -10166,17 +10187,17 @@ function createTeamWorker(deps) {
|
|
|
10166
10187
|
const scopeFile = path.join(scopes, `${op.id}.json`);
|
|
10167
10188
|
persistence.writePrivateFileSync(scopeFile, JSON.stringify({ serverUrl: server, scopeToken: credential.token, teamId: op.teamId, botId: op.botId, taskId: op.taskId, attemptId: op.attemptId }));
|
|
10168
10189
|
receipt = { ...receipt, ...resource, scopeFile, phase: "spawning" };
|
|
10169
|
-
writeReceipt(home, receipt);
|
|
10190
|
+
writeReceipt$1(home, receipt);
|
|
10170
10191
|
const spawned = await deps.spawn({ directory: resource.directory, agent: op.assistant === "pi-acp" ? "pi" : op.assistant, permissionMode: op.permissionMode === "bypassPermissions" ? "bypassPermissions" : "default", spawnedBy: "teams", environmentVariables: { VH_TEAM_SCOPE_FILE: scopeFile, VH_TEAM_OPERATION_ID: op.id } });
|
|
10171
10192
|
if (spawned.type !== "success") throw new Error(spawned.type === "error" ? spawned.errorMessage : "Directory approval required");
|
|
10172
10193
|
receipt = { ...receipt, sessionId: spawned.sessionId, phase: "spawned" };
|
|
10173
|
-
writeReceipt(home, receipt);
|
|
10194
|
+
writeReceipt$1(home, receipt);
|
|
10174
10195
|
}
|
|
10175
10196
|
if (receipt.phase === "spawning") {
|
|
10176
10197
|
const matches = Object.entries(persistence.readPersistedSessions()).filter(([, entry]) => entry.metadata?.teamOperationId === op.id);
|
|
10177
10198
|
if (matches.length !== 1) throw new Error("Spawn outcome unknown; reconcile the worker before retrying");
|
|
10178
10199
|
receipt = { ...receipt, sessionId: matches[0][0], phase: "spawned" };
|
|
10179
|
-
writeReceipt(home, receipt);
|
|
10200
|
+
writeReceipt$1(home, receipt);
|
|
10180
10201
|
}
|
|
10181
10202
|
if (receipt.phase === "spawned") {
|
|
10182
10203
|
const current = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
|
|
@@ -10191,14 +10212,14 @@ function createTeamWorker(deps) {
|
|
|
10191
10212
|
const key = await waitForSessionKey(receipt.sessionId, 1e4);
|
|
10192
10213
|
await sendUserMessage(receipt.sessionId, key, teamInitialPrompt(op.prompt, bot?.root === true, receipt.directory), "teams", { localId: `teams-initial-${op.id}`, sentFrom: "team", ...op.model ? { model: op.model } : {} });
|
|
10193
10214
|
receipt = { ...receipt, phase: "delivered" };
|
|
10194
|
-
writeReceipt(home, receipt);
|
|
10215
|
+
writeReceipt$1(home, receipt);
|
|
10195
10216
|
}
|
|
10196
10217
|
await complete(op, receipt);
|
|
10197
10218
|
} else {
|
|
10198
10219
|
const team = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
|
|
10199
10220
|
const latest = team.operations.find((candidate) => candidate.id === op.id);
|
|
10200
10221
|
if (latest?.status === "completed") {
|
|
10201
|
-
writeReceipt(home, { ...receipt, phase: "completed" });
|
|
10222
|
+
writeReceipt$1(home, { ...receipt, phase: "completed" });
|
|
10202
10223
|
return;
|
|
10203
10224
|
}
|
|
10204
10225
|
const bot = team.bots.find((candidate) => candidate.id === op.botId);
|
|
@@ -10328,6 +10349,643 @@ Use the teams tools to inspect current task state before acting. This message is
|
|
|
10328
10349
|
} };
|
|
10329
10350
|
}
|
|
10330
10351
|
|
|
10352
|
+
function resolveSpawnPermissionMode(agent, explicit) {
|
|
10353
|
+
return explicit ?? persistence.getAgentCodeDefaults(agent ?? "claude").permissionMode;
|
|
10354
|
+
}
|
|
10355
|
+
function outboundPermissionMode(agent, mode) {
|
|
10356
|
+
const key = persistence.normalizeAgentKey(agent ?? "claude");
|
|
10357
|
+
if ((key === "claude" || key === "pi") && mode === "yolo") return "bypassPermissions";
|
|
10358
|
+
return mode;
|
|
10359
|
+
}
|
|
10360
|
+
function resolveFirstMessageMeta(input) {
|
|
10361
|
+
const meta = {
|
|
10362
|
+
permissionMode: outboundPermissionMode(input.agent, input.permissionMode)
|
|
10363
|
+
};
|
|
10364
|
+
if (input.explicitModel !== void 0) {
|
|
10365
|
+
meta.model = input.explicitModel === "default" ? null : input.explicitModel;
|
|
10366
|
+
}
|
|
10367
|
+
if (persistence.normalizeAgentKey(input.agent ?? "claude") !== "claude") return meta;
|
|
10368
|
+
if (meta.model === void 0) {
|
|
10369
|
+
const model = persistence.getAgentCodeDefaults("claude").modelMode;
|
|
10370
|
+
const runnable = !persistence.isClaudeOpus55Model(model) || persistence.supportsClaudeOpus55({ capabilities: input.capabilities ?? null });
|
|
10371
|
+
meta.model = runnable && model !== "default" ? model : null;
|
|
10372
|
+
}
|
|
10373
|
+
meta.effort = null;
|
|
10374
|
+
return meta;
|
|
10375
|
+
}
|
|
10376
|
+
|
|
10377
|
+
function payloadJson(payload) {
|
|
10378
|
+
if (!payload) return void 0;
|
|
10379
|
+
try {
|
|
10380
|
+
return JSON.parse(payload);
|
|
10381
|
+
} catch {
|
|
10382
|
+
return void 0;
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
function lookupPath(root, path) {
|
|
10386
|
+
let current = root;
|
|
10387
|
+
for (const segment of path) {
|
|
10388
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
10389
|
+
current = current[segment];
|
|
10390
|
+
}
|
|
10391
|
+
return current;
|
|
10392
|
+
}
|
|
10393
|
+
function scalar(value) {
|
|
10394
|
+
if (value === void 0 || value === null) return "";
|
|
10395
|
+
if (typeof value === "string") return value;
|
|
10396
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
10397
|
+
try {
|
|
10398
|
+
return JSON.stringify(value);
|
|
10399
|
+
} catch {
|
|
10400
|
+
return "";
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
function formatNow(now, tz) {
|
|
10404
|
+
if (!tz) return new Date(now).toISOString();
|
|
10405
|
+
try {
|
|
10406
|
+
const parts = new Intl.DateTimeFormat("en-CA", { timeZone: tz, hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: "longOffset" }).formatToParts(new Date(now));
|
|
10407
|
+
const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
10408
|
+
const offset = get("timeZoneName").replace(/^GMT$/, "+00:00").replace(/^GMT/, "");
|
|
10409
|
+
return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}${offset || "Z"}`;
|
|
10410
|
+
} catch {
|
|
10411
|
+
return new Date(now).toISOString();
|
|
10412
|
+
}
|
|
10413
|
+
}
|
|
10414
|
+
function renderTemplate(text, context) {
|
|
10415
|
+
const json = payloadJson(context.payload);
|
|
10416
|
+
return text.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\s*\}\}/g, (whole, name) => {
|
|
10417
|
+
if (name === "payload") return context.payload ?? "";
|
|
10418
|
+
if (name.startsWith("payload.")) return scalar(lookupPath(json, name.slice("payload.".length).split(".")));
|
|
10419
|
+
if (name === "run.id") return context.runId;
|
|
10420
|
+
if (name === "automation.name") return context.automationName;
|
|
10421
|
+
if (name === "now") return formatNow(context.now ?? Date.now(), context.tz);
|
|
10422
|
+
return whole;
|
|
10423
|
+
});
|
|
10424
|
+
}
|
|
10425
|
+
function renderAction(action, context) {
|
|
10426
|
+
const r = (value) => renderTemplate(value, context);
|
|
10427
|
+
if (action.kind === "spawn") return { ...action, prompt: r(action.prompt) };
|
|
10428
|
+
if (action.kind === "send") return { ...action, prompt: r(action.prompt) };
|
|
10429
|
+
return { ...action, command: action.command.map(r), ...action.env ? { env: Object.fromEntries(Object.entries(action.env).map(([k, v]) => [k, r(v)])) } : {} };
|
|
10430
|
+
}
|
|
10431
|
+
function clipText(text, maxChars, keep = "head") {
|
|
10432
|
+
if (text.length <= maxChars) return text;
|
|
10433
|
+
return keep === "head" ? text.slice(0, maxChars) : text.slice(text.length - maxChars);
|
|
10434
|
+
}
|
|
10435
|
+
const DURATION_UNITS = { ms: 1, s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
10436
|
+
function parseDuration(raw) {
|
|
10437
|
+
const text = raw.trim();
|
|
10438
|
+
if (/^\d+$/.test(text)) return Number(text);
|
|
10439
|
+
const re = /(\d+(?:\.\d+)?)(ms|s|m|h|d)/gy;
|
|
10440
|
+
let total = 0;
|
|
10441
|
+
let matched = 0;
|
|
10442
|
+
let last = 0;
|
|
10443
|
+
let m;
|
|
10444
|
+
while ((m = re.exec(text)) !== null) {
|
|
10445
|
+
total += Number(m[1]) * DURATION_UNITS[m[2]];
|
|
10446
|
+
matched++;
|
|
10447
|
+
last = re.lastIndex;
|
|
10448
|
+
}
|
|
10449
|
+
if (matched === 0 || last !== text.length) throw new Error(`Invalid duration: ${raw} (use e.g. 90s, 5m, 1h30m, 2d)`);
|
|
10450
|
+
return Math.round(total);
|
|
10451
|
+
}
|
|
10452
|
+
function formatDuration(ms) {
|
|
10453
|
+
if (ms % 864e5 === 0) return `${ms / 864e5}d`;
|
|
10454
|
+
if (ms % 36e5 === 0) return `${ms / 36e5}h`;
|
|
10455
|
+
if (ms % 6e4 === 0) return `${ms / 6e4}m`;
|
|
10456
|
+
if (ms % 1e3 === 0) return `${ms / 1e3}s`;
|
|
10457
|
+
return `${ms}ms`;
|
|
10458
|
+
}
|
|
10459
|
+
function triggerFromFlags(flags, now = Date.now()) {
|
|
10460
|
+
const given = [flags.cron !== void 0, flags.every !== void 0, flags.at !== void 0, flags.manual === true].filter(Boolean).length;
|
|
10461
|
+
if (given === 0) {
|
|
10462
|
+
if (flags.tz !== void 0) throw new Error("--tz only applies with --cron");
|
|
10463
|
+
return void 0;
|
|
10464
|
+
}
|
|
10465
|
+
if (given > 1) throw new Error("Use exactly one of --cron, --every, --at, --manual");
|
|
10466
|
+
if (flags.cron !== void 0) {
|
|
10467
|
+
if (!flags.tz) throw new Error("--cron requires --tz <IANA zone> (e.g. Asia/Singapore)");
|
|
10468
|
+
return { kind: "cron", expr: flags.cron, tz: flags.tz };
|
|
10469
|
+
}
|
|
10470
|
+
if (flags.tz !== void 0) throw new Error("--tz only applies with --cron");
|
|
10471
|
+
if (flags.every !== void 0) return { kind: "interval", everyMs: parseDuration(flags.every), anchorAt: now };
|
|
10472
|
+
if (flags.at !== void 0) {
|
|
10473
|
+
const at = Date.parse(flags.at);
|
|
10474
|
+
if (!Number.isFinite(at)) throw new Error(`--at must be an ISO 8601 timestamp, got: ${flags.at}`);
|
|
10475
|
+
return { kind: "once", at };
|
|
10476
|
+
}
|
|
10477
|
+
return { kind: "manual" };
|
|
10478
|
+
}
|
|
10479
|
+
function describeTrigger(trigger) {
|
|
10480
|
+
switch (trigger.kind) {
|
|
10481
|
+
case "cron":
|
|
10482
|
+
return `cron ${trigger.expr} (${trigger.tz})`;
|
|
10483
|
+
case "interval":
|
|
10484
|
+
return `every ${formatDuration(trigger.everyMs)}`;
|
|
10485
|
+
case "once":
|
|
10486
|
+
return `once at ${new Date(trigger.at).toISOString()}`;
|
|
10487
|
+
default:
|
|
10488
|
+
return "manual";
|
|
10489
|
+
}
|
|
10490
|
+
}
|
|
10491
|
+
function describeAction(action) {
|
|
10492
|
+
if (action.kind === "spawn") return `spawn ${action.agent} in ${action.directory}${action.sticky ? ` (sticky ${action.sticky.key})` : ""}`;
|
|
10493
|
+
if (action.kind === "send") return `send to session ${action.sessionId}`;
|
|
10494
|
+
return `script ${action.command.join(" ")}`;
|
|
10495
|
+
}
|
|
10496
|
+
|
|
10497
|
+
const RECEIPT_RETENTION_MS = 7 * 864e5;
|
|
10498
|
+
const RECEIPT_MAX_ENTRIES = 2e3;
|
|
10499
|
+
function receiptsPath(home) {
|
|
10500
|
+
return path.join(home, "automation-receipts.json");
|
|
10501
|
+
}
|
|
10502
|
+
function readReceipts(home) {
|
|
10503
|
+
let raw;
|
|
10504
|
+
try {
|
|
10505
|
+
raw = node_fs.readFileSync(receiptsPath(home), "utf8");
|
|
10506
|
+
} catch (error) {
|
|
10507
|
+
if (error.code === "ENOENT") return {};
|
|
10508
|
+
throw error;
|
|
10509
|
+
}
|
|
10510
|
+
const parsed = JSON.parse(raw);
|
|
10511
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.receipts !== "object") throw new Error("Invalid automation receipts file");
|
|
10512
|
+
return parsed.receipts;
|
|
10513
|
+
}
|
|
10514
|
+
function writeReceipt(home, receipt, now = Date.now()) {
|
|
10515
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(receipt.runId)) throw new Error("Invalid automation run id");
|
|
10516
|
+
const all = readReceipts(home);
|
|
10517
|
+
const next = { ...receipt, updatedAt: now };
|
|
10518
|
+
all[receipt.runId] = next;
|
|
10519
|
+
const entries = Object.values(all).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
10520
|
+
const kept = {};
|
|
10521
|
+
let finished = 0;
|
|
10522
|
+
for (const entry of entries) {
|
|
10523
|
+
if (entry.phase === "finished") {
|
|
10524
|
+
if (now - entry.updatedAt > RECEIPT_RETENTION_MS || finished >= RECEIPT_MAX_ENTRIES) continue;
|
|
10525
|
+
finished++;
|
|
10526
|
+
}
|
|
10527
|
+
kept[entry.runId] = entry;
|
|
10528
|
+
}
|
|
10529
|
+
const target = receiptsPath(home);
|
|
10530
|
+
persistence.ensurePrivateDirectorySync(path.dirname(target));
|
|
10531
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
10532
|
+
persistence.writePrivateFileSync(tmp, JSON.stringify({ version: 1, receipts: kept }));
|
|
10533
|
+
const file = node_fs.openSync(tmp, "r");
|
|
10534
|
+
try {
|
|
10535
|
+
node_fs.fsyncSync(file);
|
|
10536
|
+
} finally {
|
|
10537
|
+
node_fs.closeSync(file);
|
|
10538
|
+
}
|
|
10539
|
+
node_fs.renameSync(tmp, target);
|
|
10540
|
+
if (process.platform !== "win32") {
|
|
10541
|
+
const directory = node_fs.openSync(path.dirname(target), "r");
|
|
10542
|
+
try {
|
|
10543
|
+
node_fs.fsyncSync(directory);
|
|
10544
|
+
} finally {
|
|
10545
|
+
node_fs.closeSync(directory);
|
|
10546
|
+
}
|
|
10547
|
+
}
|
|
10548
|
+
return next;
|
|
10549
|
+
}
|
|
10550
|
+
|
|
10551
|
+
const SCRIPT_KILL_GRACE_MS = 1e4;
|
|
10552
|
+
const SCRIPT_DRAIN_MS = 2e3;
|
|
10553
|
+
class TailBuffer {
|
|
10554
|
+
constructor(maxChars) {
|
|
10555
|
+
this.maxChars = maxChars;
|
|
10556
|
+
}
|
|
10557
|
+
text = "";
|
|
10558
|
+
push(chunk) {
|
|
10559
|
+
this.text += chunk;
|
|
10560
|
+
if (this.text.length > this.maxChars * 2) this.text = this.text.slice(this.text.length - this.maxChars);
|
|
10561
|
+
}
|
|
10562
|
+
value() {
|
|
10563
|
+
return this.text.length > this.maxChars ? this.text.slice(this.text.length - this.maxChars) : this.text;
|
|
10564
|
+
}
|
|
10565
|
+
}
|
|
10566
|
+
function killScriptTree(child, signal) {
|
|
10567
|
+
if (child.exitCode !== null || child.signalCode !== null || !child.pid) return;
|
|
10568
|
+
try {
|
|
10569
|
+
if (process.platform !== "win32") process.kill(-child.pid, signal);
|
|
10570
|
+
else child.kill(signal);
|
|
10571
|
+
} catch {
|
|
10572
|
+
try {
|
|
10573
|
+
child.kill(signal);
|
|
10574
|
+
} catch {
|
|
10575
|
+
}
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
function terminateScriptTree(child) {
|
|
10579
|
+
killScriptTree(child, "SIGTERM");
|
|
10580
|
+
const killer = setTimeout(() => killScriptTree(child, "SIGKILL"), SCRIPT_KILL_GRACE_MS);
|
|
10581
|
+
killer.unref();
|
|
10582
|
+
child.once("exit", () => clearTimeout(killer));
|
|
10583
|
+
}
|
|
10584
|
+
function runScript(options) {
|
|
10585
|
+
return new Promise((resolve) => {
|
|
10586
|
+
const tail = new TailBuffer(persistence.AUTOMATION_SUMMARY_MAX_CHARS);
|
|
10587
|
+
let timedOut = false;
|
|
10588
|
+
let settled = false;
|
|
10589
|
+
let child;
|
|
10590
|
+
const timers = [];
|
|
10591
|
+
const finish = (result) => {
|
|
10592
|
+
if (settled) return;
|
|
10593
|
+
settled = true;
|
|
10594
|
+
for (const t of timers) clearTimeout(t);
|
|
10595
|
+
resolve(result);
|
|
10596
|
+
};
|
|
10597
|
+
const timer = setTimeout(() => {
|
|
10598
|
+
timedOut = true;
|
|
10599
|
+
terminateScriptTree(child);
|
|
10600
|
+
}, options.timeoutMs);
|
|
10601
|
+
timer.unref();
|
|
10602
|
+
timers.push(timer);
|
|
10603
|
+
try {
|
|
10604
|
+
child = node_child_process.spawn(options.command[0], options.command.slice(1), {
|
|
10605
|
+
cwd: options.cwd,
|
|
10606
|
+
shell: false,
|
|
10607
|
+
windowsHide: true,
|
|
10608
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
10609
|
+
detached: process.platform !== "win32",
|
|
10610
|
+
env: { ...options.baseEnv ?? process.env, ...options.env ?? {} }
|
|
10611
|
+
});
|
|
10612
|
+
} catch (error) {
|
|
10613
|
+
finish({ exitCode: null, signal: null, timedOut: false, output: "", error: error instanceof Error ? error.message : String(error) });
|
|
10614
|
+
return;
|
|
10615
|
+
}
|
|
10616
|
+
options.onStart?.(child);
|
|
10617
|
+
child.stdout?.setEncoding("utf8").on("data", (chunk) => tail.push(chunk));
|
|
10618
|
+
child.stderr?.setEncoding("utf8").on("data", (chunk) => tail.push(chunk));
|
|
10619
|
+
child.on("error", (error) => finish({ exitCode: null, signal: null, timedOut, output: tail.value(), error: error.message }));
|
|
10620
|
+
child.on("close", (code, signal) => finish({ exitCode: code, signal, timedOut, output: tail.value() }));
|
|
10621
|
+
child.on("exit", (code, signal) => {
|
|
10622
|
+
const drain = setTimeout(() => {
|
|
10623
|
+
killScriptTree(child, "SIGKILL");
|
|
10624
|
+
finish({ exitCode: code, signal, timedOut, output: tail.value() });
|
|
10625
|
+
}, SCRIPT_DRAIN_MS);
|
|
10626
|
+
drain.unref();
|
|
10627
|
+
timers.push(drain);
|
|
10628
|
+
});
|
|
10629
|
+
});
|
|
10630
|
+
}
|
|
10631
|
+
|
|
10632
|
+
const AUTOMATION_TICK_MS = 5e3;
|
|
10633
|
+
const AUTOMATION_LEASE_RENEW_MS = 3e4;
|
|
10634
|
+
const AUTOMATION_LOG_READ_MS = 3e4;
|
|
10635
|
+
const AUTOMATION_UNAVAILABLE_LOG_MS = 10 * 6e4;
|
|
10636
|
+
const AUTOMATION_MAX_CONCURRENT = persistence.AUTOMATION_CLAIM_MAX_RUNS;
|
|
10637
|
+
const AUTOMATION_SPAWNED_BY = "automation";
|
|
10638
|
+
function automationPromptHeader(name, runId) {
|
|
10639
|
+
return `[Very Happy automation "${name}" run ${runId}; finish with automation_report (runId ${runId}) or \`very-happy auto report --run ${runId}\`]`;
|
|
10640
|
+
}
|
|
10641
|
+
function automationRunAnchor(runId) {
|
|
10642
|
+
const marker = ` run ${runId};`;
|
|
10643
|
+
return (body) => userPromptText(body)?.startsWith("[Very Happy automation ") === true && userPromptText(body).includes(marker);
|
|
10644
|
+
}
|
|
10645
|
+
async function defaultReadTurn(sessionId, runId) {
|
|
10646
|
+
const persisted = persistence.readPersistedSessions()[sessionId];
|
|
10647
|
+
if (!persisted) throw new Error(`No local key for session ${sessionId}`);
|
|
10648
|
+
return analyzeLatestTurn(await readSessionLog(sessionId, persisted, TURN_WINDOW), { anchor: automationRunAnchor(runId) });
|
|
10649
|
+
}
|
|
10650
|
+
async function defaultSend(sessionId, text, meta) {
|
|
10651
|
+
const persisted = await waitForSessionKey(sessionId, 15e3);
|
|
10652
|
+
await sendUserMessage(sessionId, persisted, text, "automation", { ...meta, sentFrom: "automation" });
|
|
10653
|
+
}
|
|
10654
|
+
function expandHome(path$1) {
|
|
10655
|
+
if (path$1 === "~") return os.homedir();
|
|
10656
|
+
if (path$1.startsWith("~/")) return path.join(os.homedir(), path$1.slice(2));
|
|
10657
|
+
return path$1;
|
|
10658
|
+
}
|
|
10659
|
+
function isSettledByServer(error) {
|
|
10660
|
+
if (!(error instanceof agentGuidance.AutomationsRequestError)) return false;
|
|
10661
|
+
if (error.status === 409 && (error.code === "run_finished" || error.code === "claim_mismatch")) return true;
|
|
10662
|
+
return error.status === 404 && error.code === "run_not_found";
|
|
10663
|
+
}
|
|
10664
|
+
function scriptAlive(item) {
|
|
10665
|
+
return !!item.child && item.child.exitCode === null && item.child.signalCode === null;
|
|
10666
|
+
}
|
|
10667
|
+
function createAutomationRunner(deps) {
|
|
10668
|
+
const home = deps.home ?? persistence.configuration.happyHomeDir;
|
|
10669
|
+
const client = deps.client ?? agentGuidance.createAutomationsClient({ serverUrl: deps.serverUrl ?? persistence.configuration.serverUrl, token: deps.token, client: "daemon-auto" });
|
|
10670
|
+
const runScript$1 = deps.runScript ?? runScript;
|
|
10671
|
+
const readTurn = deps.readTurn ?? defaultReadTurn;
|
|
10672
|
+
const send = deps.send ?? defaultSend;
|
|
10673
|
+
const prepareWorktree = deps.prepareWorktree ?? prepareTeamWorktree;
|
|
10674
|
+
const now = deps.now ?? (() => Date.now());
|
|
10675
|
+
const tracked = /* @__PURE__ */ new Map();
|
|
10676
|
+
let running = false;
|
|
10677
|
+
let renewing = false;
|
|
10678
|
+
let closed = false;
|
|
10679
|
+
let disabledUntil = 0;
|
|
10680
|
+
let lastUnavailableLogAt = -Infinity;
|
|
10681
|
+
let recovered = false;
|
|
10682
|
+
function persist(receipt) {
|
|
10683
|
+
writeReceipt(home, receipt, now());
|
|
10684
|
+
}
|
|
10685
|
+
async function finish(item, report2) {
|
|
10686
|
+
try {
|
|
10687
|
+
await client.report(item.runId, report2);
|
|
10688
|
+
} catch (error) {
|
|
10689
|
+
if (!isSettledByServer(error)) {
|
|
10690
|
+
item.pendingFinal = report2;
|
|
10691
|
+
deps.log(`Automation run ${item.runId} final report deferred`);
|
|
10692
|
+
return;
|
|
10693
|
+
}
|
|
10694
|
+
}
|
|
10695
|
+
persist({ runId: item.runId, automationId: item.automationId, kind: item.kind, phase: "finished", claimId: item.claimId, sessionId: item.sessionId });
|
|
10696
|
+
tracked.delete(item.runId);
|
|
10697
|
+
}
|
|
10698
|
+
function track(claimed, kind, sessionId, child) {
|
|
10699
|
+
const at = now();
|
|
10700
|
+
const item = { runId: claimed.run.id, automationId: claimed.automation.id, claimId: claimed.run.claimId, kind, sessionId, child, sawTurn: false, startedAt: at, lastLeaseAt: at, nextLogReadAt: at + AUTOMATION_LOG_READ_MS, attentionReported: false, exited: false };
|
|
10701
|
+
tracked.set(item.runId, item);
|
|
10702
|
+
return item;
|
|
10703
|
+
}
|
|
10704
|
+
async function failNow(claimed, kind, error, attentionReason, sessionId) {
|
|
10705
|
+
const item = track(claimed, kind, sessionId);
|
|
10706
|
+
await finish(item, { claimId: item.claimId, status: "failed", error: clipText(error, persistence.AUTOMATION_SUMMARY_MAX_CHARS), ...sessionId ? { sessionId } : {}, ...attentionReason ? { needsAttention: true, attentionReason } : {} });
|
|
10707
|
+
}
|
|
10708
|
+
async function reportRunning(item, sessionId, stickyKey) {
|
|
10709
|
+
const body = { claimId: item.claimId, status: "running", ...sessionId ? { sessionId } : {}, ...stickyKey ? { stickyKey } : {} };
|
|
10710
|
+
try {
|
|
10711
|
+
await client.report(item.runId, body);
|
|
10712
|
+
} catch (error) {
|
|
10713
|
+
if (isSettledByServer(error)) {
|
|
10714
|
+
settleExternally(item);
|
|
10715
|
+
return;
|
|
10716
|
+
}
|
|
10717
|
+
if (sessionId) item.pendingRunning = { sessionId, ...stickyKey ? { stickyKey } : {} };
|
|
10718
|
+
deps.log(`Automation run ${item.runId} running report deferred`);
|
|
10719
|
+
}
|
|
10720
|
+
}
|
|
10721
|
+
async function execute(claimed) {
|
|
10722
|
+
const { run, automation } = claimed;
|
|
10723
|
+
const existing = readReceipts(home)[run.id];
|
|
10724
|
+
if (existing) {
|
|
10725
|
+
if (existing.phase === "finished" || tracked.has(run.id)) return;
|
|
10726
|
+
if (existing.phase === "starting") {
|
|
10727
|
+
await failNow(claimed, existing.kind, "Local execution outcome unknown after restart; verify before re-running", "unknown_outcome");
|
|
10728
|
+
return;
|
|
10729
|
+
}
|
|
10730
|
+
if (existing.kind === "script") {
|
|
10731
|
+
await failNow(claimed, "script", "Daemon restarted while the script was running", "daemon_restarted");
|
|
10732
|
+
return;
|
|
10733
|
+
}
|
|
10734
|
+
track(claimed, existing.kind, existing.sessionId).sawTurn = true;
|
|
10735
|
+
return;
|
|
10736
|
+
}
|
|
10737
|
+
const context = { payload: run.payload, runId: run.id, automationName: automation.name, ...automation.trigger.kind === "cron" ? { tz: automation.trigger.tz } : {}, now: now() };
|
|
10738
|
+
const action = renderAction(automation.action, context);
|
|
10739
|
+
const env = { VH_AUTOMATION_RUN_ID: run.id, VH_AUTOMATION_NAME: automation.name };
|
|
10740
|
+
const header = automationPromptHeader(automation.name, run.id);
|
|
10741
|
+
if (action.kind === "script") {
|
|
10742
|
+
const timeoutMs = action.timeoutMs ?? automation.maxRuntimeMs;
|
|
10743
|
+
persist({ runId: run.id, automationId: automation.id, kind: "script", phase: "starting", claimId: run.claimId });
|
|
10744
|
+
const item2 = track(claimed, "script");
|
|
10745
|
+
const finished = runScript$1({
|
|
10746
|
+
command: action.command,
|
|
10747
|
+
cwd: action.cwd ? expandHome(action.cwd) : void 0,
|
|
10748
|
+
timeoutMs,
|
|
10749
|
+
env: { ...action.env ?? {}, ...env, ...run.payload ? { VH_AUTOMATION_PAYLOAD: run.payload } : {} },
|
|
10750
|
+
onStart: (child) => {
|
|
10751
|
+
item2.child = child;
|
|
10752
|
+
}
|
|
10753
|
+
});
|
|
10754
|
+
persist({ runId: run.id, automationId: automation.id, kind: "script", phase: "running", claimId: run.claimId });
|
|
10755
|
+
await reportRunning(item2);
|
|
10756
|
+
void finished.then(async (result) => {
|
|
10757
|
+
if (!tracked.has(run.id)) return;
|
|
10758
|
+
const ok = result.exitCode === 0 && !result.timedOut && !result.error;
|
|
10759
|
+
const error = result.error ? `Launch failed: ${result.error}` : result.timedOut ? `Timed out after ${timeoutMs}ms` : result.exitCode !== 0 ? `Exit ${result.exitCode ?? `signal ${result.signal}`}` : void 0;
|
|
10760
|
+
await finish(item2, { claimId: run.claimId, status: ok ? "done" : "failed", summary: clipText(result.output, persistence.AUTOMATION_SUMMARY_MAX_CHARS, "tail"), exitCode: result.exitCode, ...error ? { error } : {} });
|
|
10761
|
+
});
|
|
10762
|
+
return;
|
|
10763
|
+
}
|
|
10764
|
+
if (action.kind === "send") {
|
|
10765
|
+
if (!deps.live(action.sessionId)) {
|
|
10766
|
+
await failNow(claimed, "send", `Session ${action.sessionId} is not running on this machine`, "session_not_live", action.sessionId);
|
|
10767
|
+
return;
|
|
10768
|
+
}
|
|
10769
|
+
persist({ runId: run.id, automationId: automation.id, kind: "send", phase: "running", claimId: run.claimId, sessionId: action.sessionId });
|
|
10770
|
+
try {
|
|
10771
|
+
await send(action.sessionId, `${header}
|
|
10772
|
+
${action.prompt}`, { localId: `automation-${run.id}` });
|
|
10773
|
+
} catch (error) {
|
|
10774
|
+
await failNow(claimed, "send", `Message delivery failed: ${error instanceof Error ? error.message : String(error)}`, "send_failed", action.sessionId);
|
|
10775
|
+
return;
|
|
10776
|
+
}
|
|
10777
|
+
await reportRunning(track(claimed, "send", action.sessionId), action.sessionId);
|
|
10778
|
+
return;
|
|
10779
|
+
}
|
|
10780
|
+
const agent = action.agent;
|
|
10781
|
+
if (!isSpawnAgent(agent)) {
|
|
10782
|
+
await failNow(claimed, "spawn", `Unknown agent "${agent}"`, "invalid_action");
|
|
10783
|
+
return;
|
|
10784
|
+
}
|
|
10785
|
+
if (action.permissionMode && sanitizeSpawnPermissionMode(action.permissionMode) === null) {
|
|
10786
|
+
await failNow(claimed, "spawn", `Invalid permission mode "${action.permissionMode}"`, "invalid_action");
|
|
10787
|
+
return;
|
|
10788
|
+
}
|
|
10789
|
+
const permissionMode = resolveSpawnPermissionMode(agent, action.permissionMode ?? void 0);
|
|
10790
|
+
const stickyKey = action.sticky ? renderTemplate(action.sticky.key, context) : void 0;
|
|
10791
|
+
if (stickyKey) {
|
|
10792
|
+
const bound = claimed.stickies.find((sticky) => sticky.key === stickyKey);
|
|
10793
|
+
if (bound && deps.live(bound.sessionId)) {
|
|
10794
|
+
persist({ runId: run.id, automationId: automation.id, kind: "send", phase: "running", claimId: run.claimId, sessionId: bound.sessionId, stickyKey });
|
|
10795
|
+
try {
|
|
10796
|
+
await send(bound.sessionId, `${header}
|
|
10797
|
+
${action.prompt}`, { localId: `automation-${run.id}` });
|
|
10798
|
+
} catch (error) {
|
|
10799
|
+
await failNow(claimed, "send", `Sticky session delivery failed: ${error instanceof Error ? error.message : String(error)}`, "send_failed", bound.sessionId);
|
|
10800
|
+
return;
|
|
10801
|
+
}
|
|
10802
|
+
await reportRunning(track(claimed, "send", bound.sessionId), bound.sessionId, stickyKey);
|
|
10803
|
+
return;
|
|
10804
|
+
}
|
|
10805
|
+
if (bound) await client.deleteSticky(automation.id, stickyKey).catch(() => deps.log(`Automation ${automation.name}: stale sticky ${stickyKey} not cleared`));
|
|
10806
|
+
}
|
|
10807
|
+
let directory = expandHome(action.directory);
|
|
10808
|
+
if (!path.isAbsolute(directory)) {
|
|
10809
|
+
await failNow(claimed, "spawn", `Directory must be absolute: ${action.directory}`, "invalid_action");
|
|
10810
|
+
return;
|
|
10811
|
+
}
|
|
10812
|
+
if (action.worktree) {
|
|
10813
|
+
try {
|
|
10814
|
+
directory = (await prepareWorktree(home, directory, `auto-${run.id}`)).directory;
|
|
10815
|
+
} catch (error) {
|
|
10816
|
+
await failNow(claimed, "spawn", `Worktree preparation failed: ${error instanceof Error ? error.message : String(error)}`, "worktree_failed");
|
|
10817
|
+
return;
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
persist({ runId: run.id, automationId: automation.id, kind: "spawn", phase: "starting", claimId: run.claimId, stickyKey, ...action.worktree ? { directory } : {} });
|
|
10821
|
+
const spawned = await deps.spawn({ directory, agent, permissionMode, spawnedBy: AUTOMATION_SPAWNED_BY, environmentVariables: env });
|
|
10822
|
+
if (spawned.type !== "success") {
|
|
10823
|
+
await failNow(claimed, "spawn", spawned.type === "error" ? spawned.errorMessage : "Directory approval required", "spawn_failed");
|
|
10824
|
+
return;
|
|
10825
|
+
}
|
|
10826
|
+
persist({ runId: run.id, automationId: automation.id, kind: "spawn", phase: "running", claimId: run.claimId, sessionId: spawned.sessionId, stickyKey, ...action.worktree ? { directory } : {} });
|
|
10827
|
+
const item = track(claimed, "spawn", spawned.sessionId);
|
|
10828
|
+
try {
|
|
10829
|
+
const capabilities = persistence.readPersistedSessions()[spawned.sessionId]?.metadata?.capabilities ?? null;
|
|
10830
|
+
const meta = resolveFirstMessageMeta({ agent, permissionMode, explicitModel: action.model ?? void 0, capabilities });
|
|
10831
|
+
await send(spawned.sessionId, `${header}
|
|
10832
|
+
${action.prompt}`, { localId: `automation-${run.id}`, ...meta });
|
|
10833
|
+
} catch (error) {
|
|
10834
|
+
await finish(item, { claimId: run.claimId, status: "failed", sessionId: spawned.sessionId, error: `First message failed: ${error instanceof Error ? error.message : String(error)}`, needsAttention: true, attentionReason: "first_message_failed" });
|
|
10835
|
+
return;
|
|
10836
|
+
}
|
|
10837
|
+
await reportRunning(item, spawned.sessionId, stickyKey);
|
|
10838
|
+
}
|
|
10839
|
+
async function monitor() {
|
|
10840
|
+
for (const item of [...tracked.values()]) {
|
|
10841
|
+
if (closed) return;
|
|
10842
|
+
if (item.pendingFinal) {
|
|
10843
|
+
await finish(item, item.pendingFinal);
|
|
10844
|
+
continue;
|
|
10845
|
+
}
|
|
10846
|
+
if (item.kind === "script") continue;
|
|
10847
|
+
const sessionId = item.sessionId;
|
|
10848
|
+
const active = deps.turnActive(sessionId);
|
|
10849
|
+
if (active) item.sawTurn = true;
|
|
10850
|
+
const alive = deps.live(sessionId) && !item.exited;
|
|
10851
|
+
if (!alive) {
|
|
10852
|
+
const turn2 = await readTurn(sessionId, item.runId).catch(() => null);
|
|
10853
|
+
if (turn2?.ended && turn2.status !== "failed" && turn2.status !== "cancelled") await finish(item, { claimId: item.claimId, status: "done", sessionId, summary: clipText(turn2.answer, persistence.AUTOMATION_SUMMARY_MAX_CHARS) });
|
|
10854
|
+
else await finish(item, { claimId: item.claimId, status: "failed", sessionId, error: turn2?.error ?? "Session exited before the turn ended", needsAttention: true, attentionReason: "session_exited" });
|
|
10855
|
+
continue;
|
|
10856
|
+
}
|
|
10857
|
+
const edgeFell = item.sawTurn && !active;
|
|
10858
|
+
if (!edgeFell && now() < item.nextLogReadAt) continue;
|
|
10859
|
+
item.nextLogReadAt = now() + AUTOMATION_LOG_READ_MS;
|
|
10860
|
+
if (edgeFell) item.sawTurn = false;
|
|
10861
|
+
const turn = await readTurn(sessionId, item.runId).catch(() => null);
|
|
10862
|
+
if (!turn?.ended) continue;
|
|
10863
|
+
if (turn.status === "failed" || turn.status === "cancelled") await finish(item, { claimId: item.claimId, status: "failed", sessionId, error: turn.error ?? `Turn ${turn.status}`, needsAttention: true, attentionReason: `turn_${turn.status}` });
|
|
10864
|
+
else await finish(item, { claimId: item.claimId, status: "done", sessionId, summary: clipText(turn.answer, persistence.AUTOMATION_SUMMARY_MAX_CHARS) });
|
|
10865
|
+
}
|
|
10866
|
+
}
|
|
10867
|
+
async function renewAll() {
|
|
10868
|
+
if (renewing || closed) return;
|
|
10869
|
+
renewing = true;
|
|
10870
|
+
try {
|
|
10871
|
+
const at = now();
|
|
10872
|
+
for (const item of [...tracked.values()]) {
|
|
10873
|
+
if (closed) return;
|
|
10874
|
+
if (at - item.lastLeaseAt < AUTOMATION_LEASE_RENEW_MS) continue;
|
|
10875
|
+
item.lastLeaseAt = at;
|
|
10876
|
+
const body = { claimId: item.claimId, leaseMs: persistence.AUTOMATION_DEFAULT_LEASE_MS, ...item.pendingRunning ? { status: "running", ...item.pendingRunning } : {} };
|
|
10877
|
+
try {
|
|
10878
|
+
const run = await client.report(item.runId, body);
|
|
10879
|
+
item.pendingRunning = void 0;
|
|
10880
|
+
if (persistence.isAutomationRunTerminal(run.status)) settleExternally(item);
|
|
10881
|
+
} catch (error) {
|
|
10882
|
+
if (isSettledByServer(error)) settleExternally(item);
|
|
10883
|
+
else deps.log(`Automation run ${item.runId} lease renewal failed`);
|
|
10884
|
+
}
|
|
10885
|
+
}
|
|
10886
|
+
} finally {
|
|
10887
|
+
renewing = false;
|
|
10888
|
+
}
|
|
10889
|
+
}
|
|
10890
|
+
function settleExternally(item) {
|
|
10891
|
+
if (item.child && scriptAlive(item)) terminateScriptTree(item.child);
|
|
10892
|
+
persist({ runId: item.runId, automationId: item.automationId, kind: item.kind, phase: "finished", claimId: item.claimId, sessionId: item.sessionId });
|
|
10893
|
+
tracked.delete(item.runId);
|
|
10894
|
+
}
|
|
10895
|
+
async function recover() {
|
|
10896
|
+
recovered = true;
|
|
10897
|
+
let receipts;
|
|
10898
|
+
try {
|
|
10899
|
+
receipts = readReceipts(home);
|
|
10900
|
+
} catch {
|
|
10901
|
+
deps.log("Automation receipts unreadable; refusing to execute until repaired");
|
|
10902
|
+
closed = true;
|
|
10903
|
+
return;
|
|
10904
|
+
}
|
|
10905
|
+
for (const receipt of Object.values(receipts)) {
|
|
10906
|
+
if (receipt.phase === "finished") continue;
|
|
10907
|
+
const item = track({ run: { id: receipt.runId, claimId: receipt.claimId }, automation: { id: receipt.automationId } }, receipt.kind, receipt.sessionId);
|
|
10908
|
+
item.sawTurn = true;
|
|
10909
|
+
item.lastLeaseAt = 0;
|
|
10910
|
+
if (receipt.phase === "starting") await finish(item, { claimId: receipt.claimId, status: "failed", error: "Daemon restarted before the launch outcome was known", needsAttention: true, attentionReason: "unknown_outcome" });
|
|
10911
|
+
else if (receipt.kind === "script") await finish(item, { claimId: receipt.claimId, status: "failed", error: "Daemon restarted while the script was running", needsAttention: true, attentionReason: "daemon_restarted" });
|
|
10912
|
+
}
|
|
10913
|
+
}
|
|
10914
|
+
async function tick() {
|
|
10915
|
+
if (running || closed) return;
|
|
10916
|
+
running = true;
|
|
10917
|
+
try {
|
|
10918
|
+
if (!recovered) await recover();
|
|
10919
|
+
if (closed) return;
|
|
10920
|
+
await monitor();
|
|
10921
|
+
const capacity = AUTOMATION_MAX_CONCURRENT - tracked.size;
|
|
10922
|
+
if (now() >= disabledUntil && capacity > 0) {
|
|
10923
|
+
try {
|
|
10924
|
+
const claimed = await client.claim({ machineId: deps.machineId, limit: capacity, leaseMs: persistence.AUTOMATION_DEFAULT_LEASE_MS });
|
|
10925
|
+
await Promise.all(claimed.runs.map((entry) => closed ? Promise.resolve() : execute(entry).catch((error) => deps.log(`Automation run ${entry.run.id} failed to start: ${error instanceof Error ? error.message : String(error)}`))));
|
|
10926
|
+
} catch (error) {
|
|
10927
|
+
if (agentGuidance.isAutomationsUnavailable(error)) {
|
|
10928
|
+
disabledUntil = now() + 6e4;
|
|
10929
|
+
if (now() - lastUnavailableLogAt >= AUTOMATION_UNAVAILABLE_LOG_MS) {
|
|
10930
|
+
lastUnavailableLogAt = now();
|
|
10931
|
+
deps.log("Automations unavailable on this server (404); claim loop idle");
|
|
10932
|
+
}
|
|
10933
|
+
} else {
|
|
10934
|
+
disabledUntil = now() + 1e4;
|
|
10935
|
+
deps.log(`Automation claim failed${error instanceof agentGuidance.AutomationsRequestError ? ` (${error.status})` : ""}`);
|
|
10936
|
+
}
|
|
10937
|
+
}
|
|
10938
|
+
}
|
|
10939
|
+
} finally {
|
|
10940
|
+
running = false;
|
|
10941
|
+
}
|
|
10942
|
+
}
|
|
10943
|
+
function report(sessionId, event) {
|
|
10944
|
+
for (const item of tracked.values()) {
|
|
10945
|
+
if (item.sessionId !== sessionId) continue;
|
|
10946
|
+
if (event === "exited") item.exited = true;
|
|
10947
|
+
if (event === "blocked" && !item.attentionReported) {
|
|
10948
|
+
item.attentionReported = true;
|
|
10949
|
+
void client.report(item.runId, { claimId: item.claimId, needsAttention: true, attentionReason: "needs_input" }).catch(() => deps.log(`Automation run ${item.runId} attention report failed`));
|
|
10950
|
+
}
|
|
10951
|
+
}
|
|
10952
|
+
}
|
|
10953
|
+
const tickMs = deps.tickMs ?? AUTOMATION_TICK_MS;
|
|
10954
|
+
const timer = setInterval(() => {
|
|
10955
|
+
void tick();
|
|
10956
|
+
}, tickMs);
|
|
10957
|
+
timer.unref();
|
|
10958
|
+
const leaseTimer = setInterval(() => {
|
|
10959
|
+
void renewAll();
|
|
10960
|
+
}, tickMs);
|
|
10961
|
+
leaseTimer.unref();
|
|
10962
|
+
void tick();
|
|
10963
|
+
return {
|
|
10964
|
+
tick,
|
|
10965
|
+
renewAll,
|
|
10966
|
+
report,
|
|
10967
|
+
hasSession: (id) => [...tracked.values()].some((item) => item.sessionId === id),
|
|
10968
|
+
trackedRunIds: () => [...tracked.keys()],
|
|
10969
|
+
/** Executing a claim, or a script child is still alive — handover and auto-update wait for this. */
|
|
10970
|
+
get busy() {
|
|
10971
|
+
return running || [...tracked.values()].some(scriptAlive);
|
|
10972
|
+
},
|
|
10973
|
+
/**
|
|
10974
|
+
* Stop the loops. `killScripts` (real shutdown) terminates live script
|
|
10975
|
+
* trees; the next daemon's recovery reports them failed + attention from
|
|
10976
|
+
* the receipt, never re-running them. A handover waits on `busy` instead.
|
|
10977
|
+
*/
|
|
10978
|
+
stop: (options = {}) => {
|
|
10979
|
+
closed = true;
|
|
10980
|
+
clearInterval(timer);
|
|
10981
|
+
clearInterval(leaseTimer);
|
|
10982
|
+
if (options.killScripts) {
|
|
10983
|
+
for (const item of tracked.values()) if (item.child && scriptAlive(item)) terminateScriptTree(item.child);
|
|
10984
|
+
}
|
|
10985
|
+
}
|
|
10986
|
+
};
|
|
10987
|
+
}
|
|
10988
|
+
|
|
10331
10989
|
function daemonExitCode(source) {
|
|
10332
10990
|
return source === "exception" ? 1 : 0;
|
|
10333
10991
|
}
|
|
@@ -10349,7 +11007,7 @@ const initialMachineMetadata = {
|
|
|
10349
11007
|
happyCliVersion: persistence.packageJson.version,
|
|
10350
11008
|
homeDir: os$1.homedir(),
|
|
10351
11009
|
happyHomeDir: persistence.configuration.happyHomeDir,
|
|
10352
|
-
happyLibDir:
|
|
11010
|
+
happyLibDir: terminalTitleSuggest.projectPath(),
|
|
10353
11011
|
cliAvailability: startupCliAvailability,
|
|
10354
11012
|
resumeSupport: { ...index.detectResumeSupport(), rpcAvailable: true }
|
|
10355
11013
|
};
|
|
@@ -10488,6 +11146,7 @@ async function startDaemon() {
|
|
|
10488
11146
|
const turnActivity = new index.TurnActivityTracker();
|
|
10489
11147
|
const sessionIdToFinishedSession = /* @__PURE__ */ new Map();
|
|
10490
11148
|
let teamWorker;
|
|
11149
|
+
let automationRunner;
|
|
10491
11150
|
const persisted = persistence.readPersistedSessions();
|
|
10492
11151
|
const liveHappyProcesses = await findAllHappyProcesses();
|
|
10493
11152
|
for (const [id, s] of Object.entries(persisted)) {
|
|
@@ -10806,7 +11465,7 @@ async function startDaemon() {
|
|
|
10806
11465
|
const sessionDesc = tmuxSessionName || "current/most recent session";
|
|
10807
11466
|
persistence.logger.debug(`[DAEMON RUN] Attempting to spawn session in tmux: ${sessionDesc}`);
|
|
10808
11467
|
const tmux = getTmuxUtilities(tmuxSessionName);
|
|
10809
|
-
const cliPath = path$1.join(
|
|
11468
|
+
const cliPath = path$1.join(terminalTitleSuggest.projectPath(), "dist", "index.mjs");
|
|
10810
11469
|
const agent = options.agent ?? "claude";
|
|
10811
11470
|
if (!isSpawnAgent(agent)) {
|
|
10812
11471
|
return {
|
|
@@ -11342,6 +12001,7 @@ async function startDaemon() {
|
|
|
11342
12001
|
const onChildExited = (pid) => {
|
|
11343
12002
|
const session = pidToTrackedSession.get(pid);
|
|
11344
12003
|
if (session?.happySessionId && (session.spawnedBy === "teams" || teamWorker?.hasSession(session.happySessionId))) teamWorker?.report(session.happySessionId, "exited");
|
|
12004
|
+
if (session?.happySessionId && automationRunner?.hasSession(session.happySessionId)) automationRunner.report(session.happySessionId, "exited");
|
|
11345
12005
|
if (session?.happySessionId) turnActivity.forget(session.happySessionId);
|
|
11346
12006
|
if (session?.happySessionId && session.encryption) {
|
|
11347
12007
|
sessionIdToFinishedSession.set(session.happySessionId, session);
|
|
@@ -11361,6 +12021,7 @@ async function startDaemon() {
|
|
|
11361
12021
|
}
|
|
11362
12022
|
};
|
|
11363
12023
|
const onSessionStateEvent = (sessionId, event, spawnedByFromSession) => {
|
|
12024
|
+
if (automationRunner?.hasSession(sessionId)) automationRunner.report(sessionId, event === "completed" ? "idle" : "blocked");
|
|
11364
12025
|
if (spawnedByFromSession === "teams" || teamWorker?.hasSession(sessionId) || [...pidToTrackedSession.values()].some((s) => s.happySessionId === sessionId && s.spawnedBy === "teams")) {
|
|
11365
12026
|
teamWorker?.report(sessionId, event === "completed" ? "idle" : "blocked");
|
|
11366
12027
|
return;
|
|
@@ -11457,7 +12118,7 @@ async function startDaemon() {
|
|
|
11457
12118
|
};
|
|
11458
12119
|
persistence.writeDaemonState(fileState);
|
|
11459
12120
|
persistence.logger.debug("[DAEMON RUN] Daemon state written");
|
|
11460
|
-
const bundlePath = path$1.join(
|
|
12121
|
+
const bundlePath = path$1.join(terminalTitleSuggest.projectPath(), "dist", "index.mjs");
|
|
11461
12122
|
let initialBundleMtimeMs = 0;
|
|
11462
12123
|
try {
|
|
11463
12124
|
initialBundleMtimeMs = fs$1.statSync(bundlePath).mtimeMs;
|
|
@@ -11513,7 +12174,7 @@ async function startDaemon() {
|
|
|
11513
12174
|
});
|
|
11514
12175
|
const claudeAuthService = new ClaudeAuthService({
|
|
11515
12176
|
happyHomeDir: persistence.configuration.happyHomeDir,
|
|
11516
|
-
happyLibDir:
|
|
12177
|
+
happyLibDir: terminalTitleSuggest.projectPath(),
|
|
11517
12178
|
credentialSource: daemonClaudeCredentials.source,
|
|
11518
12179
|
getCredentialStore: () => claudeCredentialStoreSetting,
|
|
11519
12180
|
setCredentialStore: async (store) => {
|
|
@@ -11599,7 +12260,7 @@ async function startDaemon() {
|
|
|
11599
12260
|
} catch {
|
|
11600
12261
|
}
|
|
11601
12262
|
}
|
|
11602
|
-
if (bundleReplaced && !teamWorker?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
|
|
12263
|
+
if (bundleReplaced && !teamWorker?.busy && !automationRunner?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
|
|
11603
12264
|
const preflight = await preflightNewBundle(bundlePath);
|
|
11604
12265
|
if (preflight.action === "hold") {
|
|
11605
12266
|
if (lastHandoverHold !== preflight.reason) {
|
|
@@ -11609,10 +12270,11 @@ async function startDaemon() {
|
|
|
11609
12270
|
updateController.holdHandover(preflight.reason);
|
|
11610
12271
|
return;
|
|
11611
12272
|
}
|
|
11612
|
-
if (teamWorker?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
|
|
12273
|
+
if (teamWorker?.busy || automationRunner?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
|
|
11613
12274
|
lastHandoverHold = null;
|
|
11614
12275
|
persistence.logger.debug("[DAEMON RUN] Daemon bundle replaced on disk and verified, handing off to new daemon");
|
|
11615
12276
|
teamWorker?.stop();
|
|
12277
|
+
automationRunner?.stop();
|
|
11616
12278
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
11617
12279
|
clearInterval(cliUpdateInterval);
|
|
11618
12280
|
stopAgentVersionChecks();
|
|
@@ -11664,6 +12326,14 @@ async function startDaemon() {
|
|
|
11664
12326
|
live: (id) => [...pidToTrackedSession.values()].some((s) => s.happySessionId === id && isPidAlive(s.pid)),
|
|
11665
12327
|
log: (message) => persistence.logger.debug(`[DAEMON RUN] ${message}`)
|
|
11666
12328
|
});
|
|
12329
|
+
automationRunner = createAutomationRunner({
|
|
12330
|
+
machineId,
|
|
12331
|
+
token: credentials.token,
|
|
12332
|
+
spawn: spawnSession,
|
|
12333
|
+
live: (id) => [...pidToTrackedSession.values()].some((s) => s.happySessionId === id && isPidAlive(s.pid)),
|
|
12334
|
+
turnActive: (id) => turnActivity.activeSessions().includes(id),
|
|
12335
|
+
log: (message) => persistence.logger.debug(`[DAEMON RUN] ${message}`)
|
|
12336
|
+
});
|
|
11667
12337
|
void apiMachine.updateMachineMetadata((current) => ({
|
|
11668
12338
|
...current ?? initialMachineMetadata,
|
|
11669
12339
|
teamsVersion: 1,
|
|
@@ -11672,6 +12342,7 @@ async function startDaemon() {
|
|
|
11672
12342
|
const cleanupAndShutdown = async (source, errorMessage) => {
|
|
11673
12343
|
persistence.logger.debug(`[DAEMON RUN] Starting proper cleanup (source: ${source}, errorMessage: ${errorMessage})...`);
|
|
11674
12344
|
teamWorker?.stop();
|
|
12345
|
+
automationRunner?.stop({ killScripts: true });
|
|
11675
12346
|
if (restartOnStaleVersionAndHeartbeat) {
|
|
11676
12347
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
11677
12348
|
persistence.logger.debug("[DAEMON RUN] Health check interval cleared");
|
|
@@ -12176,6 +12847,7 @@ function createMcpServer(handlers, options, sessionId) {
|
|
|
12176
12847
|
version: "1.0.0"
|
|
12177
12848
|
});
|
|
12178
12849
|
agentGuidance.registerTeamsTools(mcp, sessionId);
|
|
12850
|
+
agentGuidance.registerAutomationTools(mcp);
|
|
12179
12851
|
mcp.registerTool("change_title", {
|
|
12180
12852
|
description: "Change the title of the current chat session",
|
|
12181
12853
|
title: "Change Chat Title",
|
|
@@ -12370,7 +13042,7 @@ async function startHappyServer(client, options) {
|
|
|
12370
13042
|
return;
|
|
12371
13043
|
}
|
|
12372
13044
|
}
|
|
12373
|
-
const usage =
|
|
13045
|
+
const usage = persistence.ContextUsageSchema.parse({ ...JSON.parse(body), updatedAt: Date.now() });
|
|
12374
13046
|
options.onContextUsage(usage);
|
|
12375
13047
|
res.writeHead(204).end();
|
|
12376
13048
|
} catch {
|
|
@@ -12418,6 +13090,7 @@ async function startHappyServer(client, options) {
|
|
|
12418
13090
|
agentGuidance.PREVIEW_TOOL_NAME,
|
|
12419
13091
|
agentGuidance.REPORT_PROGRESS_TOOL_NAME,
|
|
12420
13092
|
...agentGuidance.TEAM_TOOL_NAMES,
|
|
13093
|
+
...agentGuidance.AUTOMATION_TOOL_NAMES,
|
|
12421
13094
|
...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? ASSISTANT_TOOL_NAMES : []
|
|
12422
13095
|
],
|
|
12423
13096
|
stop: () => {
|
|
@@ -12517,7 +13190,7 @@ function generateHookSettingsFile(port) {
|
|
|
12517
13190
|
node_fs.mkdirSync(hooksDir, { recursive: true });
|
|
12518
13191
|
const filename = `session-hook-${process.pid}.json`;
|
|
12519
13192
|
const filepath = path.join(hooksDir, filename);
|
|
12520
|
-
const forwarderScript = path.resolve(
|
|
13193
|
+
const forwarderScript = path.resolve(terminalTitleSuggest.projectPath(), "scripts", "session_hook_forwarder.cjs");
|
|
12521
13194
|
const hookCommand = `node "${forwarderScript}" ${port}`;
|
|
12522
13195
|
const settings = {
|
|
12523
13196
|
hooks: {
|
|
@@ -12754,7 +13427,7 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12754
13427
|
const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
|
|
12755
13428
|
const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
|
|
12756
13429
|
const fork = deps.run ?? (async (input) => {
|
|
12757
|
-
const { query } = await Promise.resolve().then(function () { return require('./index-
|
|
13430
|
+
const { query } = await Promise.resolve().then(function () { return require('./index-D49WvbDT.cjs'); });
|
|
12758
13431
|
return runSideQuestion(query, input);
|
|
12759
13432
|
});
|
|
12760
13433
|
const run = async (input, live) => {
|
|
@@ -12873,8 +13546,8 @@ const MAX_TITLE_CHARS = 60;
|
|
|
12873
13546
|
const GENERATION_TIMEOUT_MS = 3e4;
|
|
12874
13547
|
function resolveClaudeBinary() {
|
|
12875
13548
|
try {
|
|
12876
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
12877
|
-
const utilsPath = path.resolve(path.join(
|
|
13549
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href)));
|
|
13550
|
+
const utilsPath = path.resolve(path.join(terminalTitleSuggest.projectPath(), "scripts", "claude_version_utils.cjs"));
|
|
12878
13551
|
const { getClaudeCliPath } = require$1(utilsPath);
|
|
12879
13552
|
const path$1 = getClaudeCliPath();
|
|
12880
13553
|
if (typeof path$1 === "string" && path$1.length > 0) return path$1;
|
|
@@ -12931,7 +13604,12 @@ function runClaudeOneShot(binary, prompt) {
|
|
|
12931
13604
|
try {
|
|
12932
13605
|
child = node_child_process.spawn(binary, ["-p", "--model", "haiku", prompt], {
|
|
12933
13606
|
stdio: ["ignore", "pipe", "ignore"],
|
|
12934
|
-
|
|
13607
|
+
// HAPPY_MANAGED=1: this claude is run BY happy. The user-wide
|
|
13608
|
+
// terminal-mirror SessionStart hook exits on it, so a one-shot
|
|
13609
|
+
// spawned from inside a vh web terminal (VH_TERMINAL_ID
|
|
13610
|
+
// inherited — B-500's pi terminal bridge, or `very-happy` typed
|
|
13611
|
+
// in a terminal) never binds a mirror session to itself.
|
|
13612
|
+
env: { ...process.env, HAPPY_MANAGED: "1" }
|
|
12935
13613
|
});
|
|
12936
13614
|
} catch (error) {
|
|
12937
13615
|
persistence.logger.debug("[titleGenerator] spawn failed", { error: String(error) });
|
|
@@ -12966,6 +13644,18 @@ function runClaudeOneShot(binary, prompt) {
|
|
|
12966
13644
|
});
|
|
12967
13645
|
});
|
|
12968
13646
|
}
|
|
13647
|
+
async function suggestTitleForPrompt(prompt) {
|
|
13648
|
+
const binary = resolveClaudeBinary();
|
|
13649
|
+
if (!binary) {
|
|
13650
|
+
persistence.logger.debug("[titleGenerator] no claude binary; skipping auto-title");
|
|
13651
|
+
return null;
|
|
13652
|
+
}
|
|
13653
|
+
const stdout = await runClaudeOneShot(binary, buildPrompt(prompt));
|
|
13654
|
+
if (stdout == null) return null;
|
|
13655
|
+
const title = sanitizeTitle(stdout);
|
|
13656
|
+
if (!title) persistence.logger.debug("[titleGenerator] empty/unusable title output; skipping");
|
|
13657
|
+
return title;
|
|
13658
|
+
}
|
|
12969
13659
|
class TitleGenerator {
|
|
12970
13660
|
constructor(session) {
|
|
12971
13661
|
this.session = session;
|
|
@@ -12989,23 +13679,12 @@ class TitleGenerator {
|
|
|
12989
13679
|
});
|
|
12990
13680
|
}
|
|
12991
13681
|
async generate(firstUserMessage) {
|
|
12992
|
-
const binary = resolveClaudeBinary();
|
|
12993
|
-
if (!binary) {
|
|
12994
|
-
persistence.logger.debug("[titleGenerator] no claude binary; skipping auto-title");
|
|
12995
|
-
return;
|
|
12996
|
-
}
|
|
12997
13682
|
if (this.session.getMetadata()?.summary?.text) {
|
|
12998
13683
|
persistence.logger.debug("[titleGenerator] summary appeared before generation; skipping");
|
|
12999
13684
|
return;
|
|
13000
13685
|
}
|
|
13001
|
-
const
|
|
13002
|
-
|
|
13003
|
-
if (stdout == null) return;
|
|
13004
|
-
const title = sanitizeTitle(stdout);
|
|
13005
|
-
if (!title) {
|
|
13006
|
-
persistence.logger.debug("[titleGenerator] empty/unusable title output; skipping");
|
|
13007
|
-
return;
|
|
13008
|
-
}
|
|
13686
|
+
const title = await suggestTitleForPrompt(firstUserMessage);
|
|
13687
|
+
if (!title) return;
|
|
13009
13688
|
if (this.session.getMetadata()?.summary?.text) {
|
|
13010
13689
|
persistence.logger.debug("[titleGenerator] summary set during generation; not overwriting");
|
|
13011
13690
|
return;
|
|
@@ -13320,8 +13999,8 @@ async function runClaude(credentials, options = {}) {
|
|
|
13320
13999
|
machineId,
|
|
13321
14000
|
homeDir: os.homedir(),
|
|
13322
14001
|
happyHomeDir: persistence.configuration.happyHomeDir,
|
|
13323
|
-
happyLibDir:
|
|
13324
|
-
happyToolsDir: path.resolve(
|
|
14002
|
+
happyLibDir: terminalTitleSuggest.projectPath(),
|
|
14003
|
+
happyToolsDir: path.resolve(terminalTitleSuggest.projectPath(), "tools", "unpacked"),
|
|
13325
14004
|
startedFromDaemon: options.startedBy === "daemon",
|
|
13326
14005
|
hostPid: process.pid,
|
|
13327
14006
|
startedBy: options.startedBy || "terminal",
|
|
@@ -15102,9 +15781,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
|
|
|
15102
15781
|
};
|
|
15103
15782
|
}
|
|
15104
15783
|
|
|
15105
|
-
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
15784
|
+
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href)));
|
|
15106
15785
|
const __dirname$1 = path.dirname(__filename$1);
|
|
15107
|
-
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
15786
|
+
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href)));
|
|
15108
15787
|
const PRISMA_QUERY_ENGINE_FILES = {
|
|
15109
15788
|
"arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
|
|
15110
15789
|
"x64-darwin": "libquery_engine-darwin.dylib.node",
|
|
@@ -15539,14 +16218,14 @@ function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagent
|
|
|
15539
16218
|
if (!subagent || startedSubagents.has(subagent)) {
|
|
15540
16219
|
return;
|
|
15541
16220
|
}
|
|
15542
|
-
envelopes.push(
|
|
16221
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
|
|
15543
16222
|
startedSubagents.add(subagent);
|
|
15544
16223
|
activeSubagents.add(subagent);
|
|
15545
16224
|
}
|
|
15546
16225
|
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15547
16226
|
const envelopes = [];
|
|
15548
16227
|
for (const subagent of activeSubagents) {
|
|
15549
|
-
envelopes.push(
|
|
16228
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
15550
16229
|
}
|
|
15551
16230
|
activeSubagents.clear();
|
|
15552
16231
|
startedSubagents.clear();
|
|
@@ -15642,7 +16321,7 @@ function turnStatus(turn) {
|
|
|
15642
16321
|
function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
|
|
15643
16322
|
const time = turnTimestampMs(turn);
|
|
15644
16323
|
const opts = { turn: turn.id, time, codexItemId: item.id };
|
|
15645
|
-
envelopes.push(
|
|
16324
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15646
16325
|
t: "tool-call-start",
|
|
15647
16326
|
call: item.id,
|
|
15648
16327
|
name,
|
|
@@ -15654,7 +16333,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
|
|
|
15654
16333
|
id: `${item.id}:start`
|
|
15655
16334
|
}));
|
|
15656
16335
|
if (output && output.trim().length > 0) {
|
|
15657
|
-
envelopes.push(
|
|
16336
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15658
16337
|
t: "text",
|
|
15659
16338
|
text: output,
|
|
15660
16339
|
thinking: true
|
|
@@ -15664,7 +16343,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
|
|
|
15664
16343
|
}));
|
|
15665
16344
|
}
|
|
15666
16345
|
if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
|
|
15667
|
-
envelopes.push(
|
|
16346
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15668
16347
|
t: "tool-call-end",
|
|
15669
16348
|
call: item.id
|
|
15670
16349
|
}, {
|
|
@@ -15686,7 +16365,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
|
15686
16365
|
agentsStates: item.agentsStates ?? {}
|
|
15687
16366
|
};
|
|
15688
16367
|
const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
|
|
15689
|
-
const envelopes = [
|
|
16368
|
+
const envelopes = [persistence.createEnvelope("agent", {
|
|
15690
16369
|
t: "tool-call-start",
|
|
15691
16370
|
call: item.id,
|
|
15692
16371
|
name: "CodexCollaboration",
|
|
@@ -15695,7 +16374,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
|
15695
16374
|
args
|
|
15696
16375
|
}, { ...opts, id: startId })];
|
|
15697
16376
|
if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
|
|
15698
|
-
envelopes.push(
|
|
16377
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15699
16378
|
t: "tool-call-end",
|
|
15700
16379
|
call: item.id,
|
|
15701
16380
|
result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
|
|
@@ -15708,7 +16387,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15708
16387
|
for (const turn of thread.turns ?? []) {
|
|
15709
16388
|
const startedAt = turnTimestampMs(turn);
|
|
15710
16389
|
const completedAt = completedTimestampMs(turn);
|
|
15711
|
-
envelopes.push(
|
|
16390
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "turn-start" }, {
|
|
15712
16391
|
id: `${turn.id}:start`,
|
|
15713
16392
|
turn: turn.id,
|
|
15714
16393
|
time: startedAt
|
|
@@ -15723,7 +16402,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15723
16402
|
case "userMessage": {
|
|
15724
16403
|
const text = textFromInputItems(item.content);
|
|
15725
16404
|
if (text) {
|
|
15726
|
-
envelopes.push(
|
|
16405
|
+
envelopes.push(persistence.createEnvelope("user", { t: "text", text }, {
|
|
15727
16406
|
id: item.id,
|
|
15728
16407
|
time: startedAt,
|
|
15729
16408
|
codexItemId: item.id
|
|
@@ -15734,7 +16413,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15734
16413
|
case "agentMessage": {
|
|
15735
16414
|
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15736
16415
|
if (text.length > 0) {
|
|
15737
|
-
envelopes.push(
|
|
16416
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text }, {
|
|
15738
16417
|
id: item.id,
|
|
15739
16418
|
turn: turn.id,
|
|
15740
16419
|
time: completedAt,
|
|
@@ -15746,7 +16425,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15746
16425
|
case "reasoning": {
|
|
15747
16426
|
const text = reasoningText(item);
|
|
15748
16427
|
if (text) {
|
|
15749
|
-
envelopes.push(
|
|
16428
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15750
16429
|
id: item.id,
|
|
15751
16430
|
turn: turn.id,
|
|
15752
16431
|
time: startedAt,
|
|
@@ -15801,7 +16480,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15801
16480
|
}
|
|
15802
16481
|
}
|
|
15803
16482
|
}
|
|
15804
|
-
envelopes.push(
|
|
16483
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15805
16484
|
id: `${turn.id}:end`,
|
|
15806
16485
|
turn: turn.id,
|
|
15807
16486
|
time: completedAt
|
|
@@ -15856,7 +16535,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15856
16535
|
}
|
|
15857
16536
|
if (type === "task_started") {
|
|
15858
16537
|
const turnId = cuid2.createId();
|
|
15859
|
-
const turnStart =
|
|
16538
|
+
const turnStart = persistence.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15860
16539
|
startedSubagents.clear();
|
|
15861
16540
|
activeSubagents.clear();
|
|
15862
16541
|
providerSubagentToSessionSubagent.clear();
|
|
@@ -15887,7 +16566,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15887
16566
|
providerSubagentToSessionSubagent,
|
|
15888
16567
|
envelopes: [
|
|
15889
16568
|
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
15890
|
-
|
|
16569
|
+
persistence.createEnvelope("agent", {
|
|
15891
16570
|
t: "turn-end",
|
|
15892
16571
|
status: pickTurnEndStatus(message, type)
|
|
15893
16572
|
}, lifecycleOpts)
|
|
@@ -15917,7 +16596,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15917
16596
|
}
|
|
15918
16597
|
const envelopes = [];
|
|
15919
16598
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15920
|
-
envelopes.push(
|
|
16599
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text: message.message }, opts));
|
|
15921
16600
|
return {
|
|
15922
16601
|
currentTurnId: state.currentTurnId,
|
|
15923
16602
|
startedSubagents,
|
|
@@ -15939,7 +16618,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15939
16618
|
}
|
|
15940
16619
|
const envelopes = [];
|
|
15941
16620
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15942
|
-
envelopes.push(
|
|
16621
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
15943
16622
|
return {
|
|
15944
16623
|
currentTurnId: state.currentTurnId,
|
|
15945
16624
|
startedSubagents,
|
|
@@ -15956,7 +16635,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15956
16635
|
const envelopes = [];
|
|
15957
16636
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15958
16637
|
envelopes.push(
|
|
15959
|
-
|
|
16638
|
+
persistence.createEnvelope("agent", {
|
|
15960
16639
|
t: "tool-call-start",
|
|
15961
16640
|
call,
|
|
15962
16641
|
name: "CodexBash",
|
|
@@ -15977,7 +16656,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15977
16656
|
const call = pickCallId(message);
|
|
15978
16657
|
const envelopes = [];
|
|
15979
16658
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15980
|
-
envelopes.push(
|
|
16659
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15981
16660
|
return {
|
|
15982
16661
|
currentTurnId: state.currentTurnId,
|
|
15983
16662
|
startedSubagents,
|
|
@@ -15993,7 +16672,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15993
16672
|
const envelopes = [];
|
|
15994
16673
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15995
16674
|
envelopes.push(
|
|
15996
|
-
|
|
16675
|
+
persistence.createEnvelope("agent", {
|
|
15997
16676
|
t: "tool-call-start",
|
|
15998
16677
|
call,
|
|
15999
16678
|
name: "CodexPatch",
|
|
@@ -16017,7 +16696,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
16017
16696
|
const call = pickCallId(message);
|
|
16018
16697
|
const envelopes = [];
|
|
16019
16698
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
16020
|
-
envelopes.push(
|
|
16699
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
16021
16700
|
return {
|
|
16022
16701
|
currentTurnId: state.currentTurnId,
|
|
16023
16702
|
startedSubagents,
|
|
@@ -16038,7 +16717,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
16038
16717
|
const toolLikeMessage = message;
|
|
16039
16718
|
const opts = buildEnvelopeOptions(state.currentTurnId);
|
|
16040
16719
|
if (message.type === "reasoning") {
|
|
16041
|
-
return [
|
|
16720
|
+
return [persistence.createEnvelope("agent", {
|
|
16042
16721
|
t: "text",
|
|
16043
16722
|
text: message.message,
|
|
16044
16723
|
thinking: true
|
|
@@ -16046,7 +16725,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
16046
16725
|
}
|
|
16047
16726
|
if (message.type === "tool-call") {
|
|
16048
16727
|
const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
|
|
16049
|
-
return [
|
|
16728
|
+
return [persistence.createEnvelope("agent", {
|
|
16050
16729
|
t: "tool-call-start",
|
|
16051
16730
|
call: toolLikeMessage.callId,
|
|
16052
16731
|
name: toolLikeMessage.name || "unknown",
|
|
@@ -16059,13 +16738,13 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
16059
16738
|
const envelopes = [];
|
|
16060
16739
|
const content = toolLikeMessage.output?.content;
|
|
16061
16740
|
if (typeof content === "string" && content.trim().length > 0) {
|
|
16062
|
-
envelopes.push(
|
|
16741
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
16063
16742
|
t: "text",
|
|
16064
16743
|
text: content,
|
|
16065
16744
|
thinking: true
|
|
16066
16745
|
}, opts));
|
|
16067
16746
|
}
|
|
16068
|
-
envelopes.push(
|
|
16747
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
16069
16748
|
t: "tool-call-end",
|
|
16070
16749
|
call: toolLikeMessage.callId
|
|
16071
16750
|
}, opts));
|
|
@@ -17061,7 +17740,7 @@ function buildCodexTurnPrompt(opts) {
|
|
|
17061
17740
|
parts.push(opts.message);
|
|
17062
17741
|
if (opts.includeTitleInstruction) {
|
|
17063
17742
|
parts.push(CHANGE_TITLE_INSTRUCTION);
|
|
17064
|
-
parts.push(
|
|
17743
|
+
parts.push(persistence.BUILTIN_TODO_DISCOVERY);
|
|
17065
17744
|
}
|
|
17066
17745
|
return parts.join("\n\n");
|
|
17067
17746
|
}
|
|
@@ -17620,7 +18299,7 @@ async function runCodex(opts) {
|
|
|
17620
18299
|
}
|
|
17621
18300
|
});
|
|
17622
18301
|
const happyServer = await startHappyServer(session);
|
|
17623
|
-
const bridgeEntrypoint = path.join(
|
|
18302
|
+
const bridgeEntrypoint = path.join(terminalTitleSuggest.projectPath(), "bin", "very-happy-mcp.mjs");
|
|
17624
18303
|
const mcpServers = {
|
|
17625
18304
|
happy: {
|
|
17626
18305
|
command: process.execPath,
|
|
@@ -18033,7 +18712,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18033
18712
|
process.exit(0);
|
|
18034
18713
|
} else if (subcommand === "install-pi-tools") {
|
|
18035
18714
|
try {
|
|
18036
|
-
const { handleInstallPiTools } = await Promise.resolve().then(function () { return require('./installPiTools-
|
|
18715
|
+
const { handleInstallPiTools } = await Promise.resolve().then(function () { return require('./installPiTools-DuZ6jZvX.cjs'); });
|
|
18037
18716
|
await handleInstallPiTools(args.slice(1));
|
|
18038
18717
|
} catch (error) {
|
|
18039
18718
|
console.error(error instanceof Error ? error.message : "Pi tools installation failed");
|
|
@@ -18042,7 +18721,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18042
18721
|
return;
|
|
18043
18722
|
} else if (subcommand === "install-terminal-hooks") {
|
|
18044
18723
|
try {
|
|
18045
|
-
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-
|
|
18724
|
+
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-BF0kL0ae.cjs'); });
|
|
18046
18725
|
const command = parseTerminalHooksArgs(args.slice(1));
|
|
18047
18726
|
if (command.action === "help") {
|
|
18048
18727
|
console.log(TERMINAL_HOOKS_HELP);
|
|
@@ -18059,7 +18738,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18059
18738
|
}
|
|
18060
18739
|
} else if (subcommand === "agent-home") {
|
|
18061
18740
|
try {
|
|
18062
|
-
const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-
|
|
18741
|
+
const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-BJ7bde35.cjs'); });
|
|
18063
18742
|
await runAgentHomeCommand(parseAgentHomeArgs(args.slice(1)));
|
|
18064
18743
|
process.exit(0);
|
|
18065
18744
|
} catch (error) {
|
|
@@ -18068,7 +18747,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18068
18747
|
}
|
|
18069
18748
|
} else if (subcommand === "spawn") {
|
|
18070
18749
|
try {
|
|
18071
|
-
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-
|
|
18750
|
+
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-od9V23cc.cjs'); });
|
|
18072
18751
|
await handleSpawnCommand(args.slice(1));
|
|
18073
18752
|
} catch (error) {
|
|
18074
18753
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -18080,7 +18759,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18080
18759
|
return;
|
|
18081
18760
|
} else if (subcommand === "sessions") {
|
|
18082
18761
|
try {
|
|
18083
|
-
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-
|
|
18762
|
+
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-qULzUzlI.cjs'); });
|
|
18084
18763
|
await handleSessionsCommand(args.slice(1));
|
|
18085
18764
|
} catch (error) {
|
|
18086
18765
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -18092,7 +18771,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18092
18771
|
return;
|
|
18093
18772
|
} else if (subcommand === "send") {
|
|
18094
18773
|
try {
|
|
18095
|
-
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-
|
|
18774
|
+
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-9075kHZP.cjs'); });
|
|
18096
18775
|
await handleSendCommand(args.slice(1));
|
|
18097
18776
|
} catch (error) {
|
|
18098
18777
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -18198,9 +18877,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18198
18877
|
if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
|
|
18199
18878
|
const projectId = args[3];
|
|
18200
18879
|
try {
|
|
18201
|
-
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
18202
|
-
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-
|
|
18203
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-
|
|
18880
|
+
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-D23kF0_u.cjs'); });
|
|
18881
|
+
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DY_sFA3J.cjs'); }).then(function (n) { return n.persistence; });
|
|
18882
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CUCU7Vw8.cjs'); }).then(function (n) { return n.api; });
|
|
18204
18883
|
let userEmail = void 0;
|
|
18205
18884
|
try {
|
|
18206
18885
|
const credentials = await readCredentialsForConfiguredRelay2();
|
|
@@ -18231,7 +18910,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18231
18910
|
}
|
|
18232
18911
|
if (geminiSubcommand === "project" && args[2] === "get") {
|
|
18233
18912
|
try {
|
|
18234
|
-
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
18913
|
+
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-D23kF0_u.cjs'); });
|
|
18235
18914
|
const config = readGeminiLocalConfig();
|
|
18236
18915
|
if (config.googleCloudProject) {
|
|
18237
18916
|
console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
|
|
@@ -18271,7 +18950,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18271
18950
|
process.exit(0);
|
|
18272
18951
|
}
|
|
18273
18952
|
try {
|
|
18274
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
18953
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-CtOEFvhf.cjs'); });
|
|
18275
18954
|
let startedBy = void 0;
|
|
18276
18955
|
for (let i = 1; i < args.length; i++) {
|
|
18277
18956
|
if (args[i] === "--started-by") {
|
|
@@ -18294,7 +18973,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18294
18973
|
} else if (subcommand === "pi") {
|
|
18295
18974
|
if (args[1] === "--terminal") {
|
|
18296
18975
|
try {
|
|
18297
|
-
const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-
|
|
18976
|
+
const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-qdJ9KZVL.cjs'); });
|
|
18298
18977
|
process.exitCode = await runPiTerminal(args.slice(2));
|
|
18299
18978
|
} catch (error) {
|
|
18300
18979
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -18311,7 +18990,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18311
18990
|
}
|
|
18312
18991
|
};
|
|
18313
18992
|
try {
|
|
18314
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
18993
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DEOVn46R.cjs'); });
|
|
18315
18994
|
const parsed = parsePiRunnerArgs(args.slice(1));
|
|
18316
18995
|
const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
|
|
18317
18996
|
const { credentials } = await authAndSetupMachineIfNeeded();
|
|
@@ -18339,7 +19018,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18339
19018
|
return;
|
|
18340
19019
|
} else if (subcommand === "acp") {
|
|
18341
19020
|
try {
|
|
18342
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
19021
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DEOVn46R.cjs'); });
|
|
18343
19022
|
let startedBy = void 0;
|
|
18344
19023
|
let verbose = false;
|
|
18345
19024
|
const acpArgs = [];
|
|
@@ -18379,7 +19058,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18379
19058
|
return;
|
|
18380
19059
|
} else if (subcommand === "openclaw") {
|
|
18381
19060
|
try {
|
|
18382
|
-
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-
|
|
19061
|
+
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-DoqZXBvL.cjs'); });
|
|
18383
19062
|
let startedBy = void 0;
|
|
18384
19063
|
let verbose = false;
|
|
18385
19064
|
let gatewayUrl;
|
|
@@ -18430,7 +19109,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18430
19109
|
return;
|
|
18431
19110
|
} else if (subcommand === "todo") {
|
|
18432
19111
|
try {
|
|
18433
|
-
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-
|
|
19112
|
+
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-BCT_ZPuV.cjs'); });
|
|
18434
19113
|
await handleTodoCommand(args.slice(1));
|
|
18435
19114
|
} catch (error) {
|
|
18436
19115
|
console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
|
|
@@ -18439,16 +19118,28 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18439
19118
|
return;
|
|
18440
19119
|
} else if (subcommand === "teams") {
|
|
18441
19120
|
try {
|
|
18442
|
-
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-
|
|
19121
|
+
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-CrCzU-AV.cjs'); });
|
|
18443
19122
|
await handleTeamsCommand(args.slice(1));
|
|
18444
19123
|
} catch (error) {
|
|
18445
19124
|
console.error(error instanceof Error ? error.message : "Teams command failed");
|
|
18446
19125
|
process.exitCode = 1;
|
|
18447
19126
|
}
|
|
18448
19127
|
return;
|
|
19128
|
+
} else if (subcommand === "auto") {
|
|
19129
|
+
try {
|
|
19130
|
+
const { handleAutoCommand } = await Promise.resolve().then(function () { return require('./auto-C-90ojIH.cjs'); });
|
|
19131
|
+
await handleAutoCommand(args.slice(1));
|
|
19132
|
+
} catch (error) {
|
|
19133
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Automation command failed");
|
|
19134
|
+
if (process.env.DEBUG) {
|
|
19135
|
+
console.error(error);
|
|
19136
|
+
}
|
|
19137
|
+
process.exitCode = 1;
|
|
19138
|
+
}
|
|
19139
|
+
return;
|
|
18449
19140
|
} else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
|
|
18450
19141
|
try {
|
|
18451
|
-
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-
|
|
19142
|
+
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-JpqI2POW.cjs'); });
|
|
18452
19143
|
await handleMcpCommand(args[1] === "--terminal-tools");
|
|
18453
19144
|
} catch (error) {
|
|
18454
19145
|
process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -18682,6 +19373,8 @@ ${chalk.bold("Usage:")}
|
|
|
18682
19373
|
conversations on this machine (agent-home --help)
|
|
18683
19374
|
very-happy connect Connect AI vendor API keys
|
|
18684
19375
|
very-happy todo Built-in Todos and official agent skill (todo --help)
|
|
19376
|
+
very-happy auto Scheduled / triggered automations: spawn, sticky
|
|
19377
|
+
sessions or scripts with tracked runs (auto --help)
|
|
18685
19378
|
very-happy sandbox Configure and manage OS-level sandboxing
|
|
18686
19379
|
very-happy notify Send push notification
|
|
18687
19380
|
very-happy mcp Stdio MCP server exposing copy_to_clipboard for a
|
|
@@ -18839,12 +19532,15 @@ exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
|
|
|
18839
19532
|
exports.archiveSession = archiveSession;
|
|
18840
19533
|
exports.checkPreviewPath = checkPreviewPath;
|
|
18841
19534
|
exports.createSessionMetadata = createSessionMetadata;
|
|
19535
|
+
exports.describeAction = describeAction;
|
|
19536
|
+
exports.describeTrigger = describeTrigger;
|
|
18842
19537
|
exports.hashObject = hashObject;
|
|
18843
19538
|
exports.initialMachineMetadata = initialMachineMetadata;
|
|
18844
19539
|
exports.isValidSpawnOrigin = isValidSpawnOrigin;
|
|
18845
19540
|
exports.listAccountSessions = listAccountSessions;
|
|
18846
19541
|
exports.listSessions = listSessions;
|
|
18847
19542
|
exports.normalizeAcpPermissionMode = normalizeAcpPermissionMode;
|
|
19543
|
+
exports.parseDuration = parseDuration;
|
|
18848
19544
|
exports.query = query;
|
|
18849
19545
|
exports.readSessionMetadata = readSessionMetadata;
|
|
18850
19546
|
exports.readSessionTranscript = readSessionTranscript;
|
|
@@ -18852,7 +19548,9 @@ exports.registerAgentAttachmentDownloads = registerAgentAttachmentDownloads;
|
|
|
18852
19548
|
exports.registerAssistantSessionTools = registerAssistantSessionTools;
|
|
18853
19549
|
exports.registerKillSessionHandler = registerKillSessionHandler;
|
|
18854
19550
|
exports.removeSessionModeFile = removeSessionModeFile;
|
|
19551
|
+
exports.resolveFirstMessageMeta = resolveFirstMessageMeta;
|
|
18855
19552
|
exports.resolvePermissionRequest = resolvePermissionRequest;
|
|
19553
|
+
exports.resolveSpawnPermissionMode = resolveSpawnPermissionMode;
|
|
18856
19554
|
exports.sanitizeSpawnPermissionMode = sanitizeSpawnPermissionMode;
|
|
18857
19555
|
exports.sendUserMessage = sendUserMessage;
|
|
18858
19556
|
exports.sessionWebUrl = sessionWebUrl;
|
|
@@ -18860,7 +19558,9 @@ exports.setupOfflineReconnection = setupOfflineReconnection;
|
|
|
18860
19558
|
exports.stageClaudeAttachments = stageClaudeAttachments;
|
|
18861
19559
|
exports.startHappyServer = startHappyServer;
|
|
18862
19560
|
exports.stopSession = stopSession;
|
|
19561
|
+
exports.suggestTitleForPrompt = suggestTitleForPrompt;
|
|
18863
19562
|
exports.tmuxSupportsSessionEnv = tmuxSupportsSessionEnv;
|
|
19563
|
+
exports.triggerFromFlags = triggerFromFlags;
|
|
18864
19564
|
exports.waitForSessionKey = waitForSessionKey;
|
|
18865
19565
|
exports.waitForTurnEnd = waitForTurnEnd;
|
|
18866
19566
|
exports.writeSessionModeFile = writeSessionModeFile;
|