very-happy-cli 0.2.150 → 0.2.152
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-C-dhGJgd.mjs → AcpBackend-Bs3wwYbt.mjs} +2 -2
- package/dist/{AcpBackend-CXawaWWx.cjs → AcpBackend-CogiKCng.cjs} +2 -2
- package/dist/{AcpSessionManager-DKOtSqJc.mjs → AcpSessionManager-BT8PJMEm.mjs} +1 -1
- package/dist/{AcpSessionManager-TrwEZR7y.cjs → AcpSessionManager-BazkBuFo.cjs} +9 -9
- package/dist/agentGuidance-CqBn77AI.cjs +553 -0
- package/dist/agentGuidance-Daxn5Nvj.mjs +529 -0
- package/dist/{agentHome-Dm-Wjs38.mjs → agentHome-CCYfs8Ec.mjs} +3 -3
- package/dist/{agentHome-rylNIkix.cjs → agentHome-CpJjrQZc.cjs} +3 -3
- package/dist/auto-DV5emt1-.mjs +466 -0
- package/dist/auto-DncmTDvw.cjs +472 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -2
- package/dist/codex/happyMcpStdioBridge.mjs +4 -2
- package/dist/{config-Dhu6LU0h.mjs → config-DbKpQpdL.mjs} +5 -5
- package/dist/{config-CgPu2wtS.cjs → config-DeJgSMh-.cjs} +5 -5
- package/dist/{index-CpNYvMTJ.mjs → index-BauTv2jB.mjs} +900 -72
- package/dist/{index-BsYH1sjV.mjs → index-CN7OF2cU.mjs} +5 -5
- package/dist/{index-DvfCUuDK.cjs → index-CRaJ9k2p.cjs} +5 -5
- package/dist/{index-CzpooENj.mjs → index-DahaLbu4.mjs} +19 -19
- package/dist/{index-K6mUS0jF.cjs → index-ZIW1jrYm.cjs} +941 -104
- package/dist/{index-DEVkDRas.cjs → index-imt4mC41.cjs} +19 -19
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/install-C1K3nrxF.mjs +114 -0
- package/dist/install-CDR3qBhs.cjs +118 -0
- package/dist/{installTerminalHooks-NIbsQ1wi.cjs → installTerminalHooks-BoDxpaBO.cjs} +5 -5
- package/dist/{installTerminalHooks-BF_tmw9V.mjs → installTerminalHooks-VvmlO6Ue.mjs} +5 -5
- package/dist/lib.cjs +3 -3
- package/dist/lib.mjs +3 -3
- package/dist/limits-BNTWyUaZ.cjs +2175 -0
- package/dist/limits-CKR_HJnO.mjs +2080 -0
- package/dist/{mcp-_TVUSg8W.cjs → mcp-CCKCKL6l.cjs} +5 -5
- package/dist/{mcp-DzydgXpf.mjs → mcp-D4dAo5BQ.mjs} +5 -5
- package/dist/{piRuntime-CSAiJk0X.cjs → piRuntime-Bn5ypbjH.cjs} +2 -2
- package/dist/{piRuntime-ACOhil6t.mjs → piRuntime-CW-XA4rz.mjs} +2 -2
- package/dist/{piTerminal-DHxGB3Ic.cjs → piTerminal-B7K75aXB.cjs} +6 -6
- package/dist/{piTerminal-BlE4vbvZ.mjs → piTerminal-BLSXK1u2.mjs} +6 -6
- package/dist/{runGemini-BMYCU16c.cjs → runGemini-Bgg1BzDK.cjs} +7 -7
- package/dist/{runGemini-D5IV1MoF.mjs → runGemini-D5N11LxO.mjs} +7 -7
- package/dist/{runOpenClaw-Qs4eFtcS.cjs → runOpenClaw-B9qWHybc.cjs} +6 -6
- package/dist/{runOpenClaw-CWPXrKMJ.mjs → runOpenClaw-CCpyKbrn.mjs} +6 -6
- package/dist/{send-Dv0km8UI.mjs → send-C44kcXz5.mjs} +21 -7
- package/dist/{send-qXWGilm8.cjs → send-wl9ei8yG.cjs} +21 -7
- package/dist/{sessions-DoULfKui.mjs → sessions-DXJEcUg6.mjs} +57 -11
- package/dist/{sessions-CpIdDhH7.cjs → sessions-Tad8elmy.cjs} +57 -11
- package/dist/{spawn-DQuOmUP7.mjs → spawn-BOlb5y1_.mjs} +142 -28
- package/dist/{spawn-DqzG1bTH.cjs → spawn-DA8WAFmm.cjs} +142 -28
- package/dist/{teams-4mhNh5cj.mjs → teams-Bs8lI887.mjs} +15 -72
- package/dist/{teams-ByEgUzsH.cjs → teams-qVLrFcdd.cjs} +13 -70
- package/dist/{todo-D7SDXL0c.cjs → todo-mCMoGjJk.cjs} +4 -23
- package/dist/{todo-C9IAUf0K.mjs → todo-nzsqGZqp.mjs} +2 -21
- package/dist/{types-D2Gkpe0N.cjs → types-CN28qJl7.cjs} +38 -813
- package/dist/{types-BD542hb1.mjs → types-xKrxbCRv.mjs} +208 -978
- package/package.json +7 -7
- package/dist/agentGuidance-Dla-BXO8.cjs +0 -267
- package/dist/agentGuidance-DrfhtVP1.mjs +0 -251
- package/dist/limits-Dyma1REp.cjs +0 -1236
- package/dist/limits-FThmm7WR.mjs +0 -1165
|
@@ -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-CN28qJl7.cjs');
|
|
7
|
+
var persistence = require('./limits-BNTWyUaZ.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
12
|
var projectPath = require('./projectPath-ChLhdOyt.cjs');
|
|
13
|
-
var agentGuidance = require('./agentGuidance-
|
|
13
|
+
var agentGuidance = require('./agentGuidance-CqBn77AI.cjs');
|
|
14
14
|
var promises = require('node:fs/promises');
|
|
15
15
|
var fs = require('fs/promises');
|
|
16
16
|
var ink = require('ink');
|
|
@@ -478,17 +478,23 @@ function readClaudeSettings() {
|
|
|
478
478
|
}
|
|
479
479
|
function shouldIncludeCoAuthoredBy() {
|
|
480
480
|
const settings = readClaudeSettings();
|
|
481
|
-
if (!settings
|
|
481
|
+
if (!settings) {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
if (settings.attribution?.commit === "") {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
if (settings.includeCoAuthoredBy === void 0) {
|
|
482
488
|
return true;
|
|
483
489
|
}
|
|
484
490
|
return settings.includeCoAuthoredBy;
|
|
485
491
|
}
|
|
486
492
|
|
|
487
493
|
const BASE_SYSTEM_PROMPT = (() => agentGuidance.trimIdent(`
|
|
488
|
-
|
|
489
|
-
`))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" +
|
|
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" + persistence.BUILTIN_TODO_DISCOVERY;
|
|
490
496
|
const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
|
|
491
|
-
When
|
|
497
|
+
When you yourself create a git commit, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
492
498
|
|
|
493
499
|
<main commit message>
|
|
494
500
|
|
|
@@ -496,6 +502,8 @@ const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
|
|
|
496
502
|
via [Very Happy](https://github.com/Mereithhh/very-happy)
|
|
497
503
|
|
|
498
504
|
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
505
|
+
|
|
506
|
+
Do not add these lines to commit message text you only draft for the user.
|
|
499
507
|
`))();
|
|
500
508
|
const systemPrompt = (() => {
|
|
501
509
|
const includeCoAuthored = shouldIncludeCoAuthoredBy();
|
|
@@ -892,7 +900,7 @@ async function createSessionScanner(opts) {
|
|
|
892
900
|
let processedMessageKeys = /* @__PURE__ */ new Set();
|
|
893
901
|
let deadSessions = /* @__PURE__ */ new Set();
|
|
894
902
|
if (opts.sessionId) {
|
|
895
|
-
let messages = await readSessionLog(projectDir, opts.sessionId);
|
|
903
|
+
let messages = await readSessionLog$1(projectDir, opts.sessionId);
|
|
896
904
|
persistence.logger.debug(`[SESSION_SCANNER] Marking ${messages.length} existing messages as processed from session ${opts.sessionId}`);
|
|
897
905
|
for (let m of messages) {
|
|
898
906
|
processedMessageKeys.add(messageKey(m));
|
|
@@ -915,7 +923,7 @@ async function createSessionScanner(opts) {
|
|
|
915
923
|
}
|
|
916
924
|
}
|
|
917
925
|
for (let session of sessions) {
|
|
918
|
-
const sessionMessages = await readSessionLog(projectDir, session);
|
|
926
|
+
const sessionMessages = await readSessionLog$1(projectDir, session);
|
|
919
927
|
let skipped = 0;
|
|
920
928
|
let sent = 0;
|
|
921
929
|
for (let file of sessionMessages) {
|
|
@@ -992,7 +1000,7 @@ async function createSessionScanner(opts) {
|
|
|
992
1000
|
return;
|
|
993
1001
|
}
|
|
994
1002
|
if (options?.treatExistingAsProcessed) {
|
|
995
|
-
const existing = await readSessionLog(projectDir, sessionId);
|
|
1003
|
+
const existing = await readSessionLog$1(projectDir, sessionId);
|
|
996
1004
|
persistence.logger.debug(`[SESSION_SCANNER] Pre-marking ${existing.length} existing messages as processed for new session ${sessionId}`);
|
|
997
1005
|
for (const m of existing) {
|
|
998
1006
|
processedMessageKeys.add(messageKey(m));
|
|
@@ -1020,7 +1028,7 @@ function messageKey(message) {
|
|
|
1020
1028
|
throw Error();
|
|
1021
1029
|
}
|
|
1022
1030
|
}
|
|
1023
|
-
async function readSessionLog(projectDir, sessionId) {
|
|
1031
|
+
async function readSessionLog$1(projectDir, sessionId) {
|
|
1024
1032
|
const expectedSessionFile = path.join(projectDir, `${sessionId}.jsonl`);
|
|
1025
1033
|
persistence.logger.debug(`[SESSION_SCANNER] Reading session file: ${expectedSessionFile}`);
|
|
1026
1034
|
let file;
|
|
@@ -3499,7 +3507,7 @@ class StreamRelay {
|
|
|
3499
3507
|
}
|
|
3500
3508
|
this.pending.text += text;
|
|
3501
3509
|
this.pending.bytes += Buffer.byteLength(text, "utf8");
|
|
3502
|
-
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) {
|
|
3503
3511
|
this.flushPending();
|
|
3504
3512
|
return;
|
|
3505
3513
|
}
|
|
@@ -5394,8 +5402,8 @@ ${typeLabels[type] || type}:`));
|
|
|
5394
5402
|
}
|
|
5395
5403
|
console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
|
|
5396
5404
|
try {
|
|
5397
|
-
const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-
|
|
5398
|
-
const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-
|
|
5405
|
+
const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-CpJjrQZc.cjs'); });
|
|
5406
|
+
const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-CN28qJl7.cjs'); }).then(function (n) { return n.index; });
|
|
5399
5407
|
const homes = await resolveAgentHomesForCli();
|
|
5400
5408
|
console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
|
|
5401
5409
|
console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));
|
|
@@ -5506,7 +5514,7 @@ async function registryVersion(name) {
|
|
|
5506
5514
|
return typeof data.version === "string" ? stableVersion(data.version) : null;
|
|
5507
5515
|
}
|
|
5508
5516
|
async function detectAgentVersions(deps = { probe: probeAgentVersion, registryVersion }, now = Date.now()) {
|
|
5509
|
-
const agents = await Promise.all(
|
|
5517
|
+
const agents = await Promise.all(persistence.AGENT_VERSION_SOURCES.map(async (source) => {
|
|
5510
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 };
|
|
5511
5519
|
if (local.missing) return { id: source.id, installed: null, latest: null, status: "not-installed" };
|
|
5512
5520
|
const latest = await deps.registryVersion(source.package).catch(() => null);
|
|
@@ -6106,7 +6114,9 @@ async function sendUserMessage(sessionId, persisted, text, client, options = {})
|
|
|
6106
6114
|
},
|
|
6107
6115
|
meta: {
|
|
6108
6116
|
sentFrom: options.sentFrom ?? "cli",
|
|
6109
|
-
...options.model ? { model: options.model } : {}
|
|
6117
|
+
...options.model !== void 0 ? { model: options.model } : {},
|
|
6118
|
+
...options.permissionMode !== void 0 ? { permissionMode: options.permissionMode } : {},
|
|
6119
|
+
...options.effort !== void 0 ? { effort: options.effort } : {}
|
|
6110
6120
|
}
|
|
6111
6121
|
};
|
|
6112
6122
|
const encrypted = persistence.encodeBase64(persistence.encrypt(
|
|
@@ -6220,7 +6230,7 @@ const DENIED_BASENAME_PATTERNS = [
|
|
|
6220
6230
|
const DENIED_PATH_SUFFIXES = [
|
|
6221
6231
|
`${path.sep}.git${path.sep}config`
|
|
6222
6232
|
];
|
|
6223
|
-
function expandHome(input, home) {
|
|
6233
|
+
function expandHome$1(input, home) {
|
|
6224
6234
|
if (input === "~") return home;
|
|
6225
6235
|
if (input.startsWith(`~${path.sep}`) || input.startsWith("~/")) {
|
|
6226
6236
|
return path.resolve(home, input.slice(2));
|
|
@@ -6243,7 +6253,7 @@ function checkPreviewPath(input, opts) {
|
|
|
6243
6253
|
if (input.includes("\0")) {
|
|
6244
6254
|
return { resolved: "", deniedReason: "path contains a NUL byte" };
|
|
6245
6255
|
}
|
|
6246
|
-
const expanded = expandHome(input.trim(), home);
|
|
6256
|
+
const expanded = expandHome$1(input.trim(), home);
|
|
6247
6257
|
const resolved = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
|
|
6248
6258
|
for (const dir of DENIED_HOME_DIRS) {
|
|
6249
6259
|
if (isUnder(resolved, path.resolve(home, dir))) {
|
|
@@ -6417,13 +6427,22 @@ function startDaemonControlServer({
|
|
|
6417
6427
|
permissionMode: z.z.string().optional(),
|
|
6418
6428
|
// B-069: spawn-origin tag ('assistant' = session_spawn). Recorded on
|
|
6419
6429
|
// the TrackedSession; old clients never send it.
|
|
6420
|
-
spawnedBy: z.z.string().optional()
|
|
6430
|
+
spawnedBy: z.z.string().optional(),
|
|
6431
|
+
// B-492: `very-happy spawn --fork` — same fields the web's fork flow
|
|
6432
|
+
// sends over the machine RPC. Old CLIs never send them.
|
|
6433
|
+
resumeClaudeSessionId: z.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).optional(),
|
|
6434
|
+
resumeCodexThreadId: z.z.string().min(1).max(200).optional(),
|
|
6435
|
+
parentSessionId: z.z.string().min(1).max(200).optional()
|
|
6421
6436
|
}),
|
|
6422
6437
|
response: {
|
|
6423
6438
|
200: z.z.object({
|
|
6424
6439
|
success: z.z.boolean(),
|
|
6425
6440
|
sessionId: z.z.string().optional(),
|
|
6426
|
-
approvedNewDirectoryCreation: z.z.boolean().optional()
|
|
6441
|
+
approvedNewDirectoryCreation: z.z.boolean().optional(),
|
|
6442
|
+
// B-492: true when resumeClaudeSessionId / resumeCodexThreadId was
|
|
6443
|
+
// honoured. A caller that asked for a fork and does not get this
|
|
6444
|
+
// back is talking to an older daemon that spawned a fresh session.
|
|
6445
|
+
resumed: z.z.boolean().optional()
|
|
6427
6446
|
}),
|
|
6428
6447
|
409: z.z.object({
|
|
6429
6448
|
success: z.z.boolean(),
|
|
@@ -6438,13 +6457,14 @@ function startDaemonControlServer({
|
|
|
6438
6457
|
}
|
|
6439
6458
|
}
|
|
6440
6459
|
}, async (request, reply) => {
|
|
6441
|
-
const { directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy } = request.body;
|
|
6460
|
+
const { directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId } = request.body;
|
|
6442
6461
|
if (!directory && variant !== "assistant") {
|
|
6443
6462
|
reply.code(500);
|
|
6444
6463
|
return { success: false, error: "directory is required" };
|
|
6445
6464
|
}
|
|
6446
6465
|
persistence.logger.debug(`[CONTROL SERVER] Spawn session request: dir=${directory}, sessionId=${sessionId || "new"}, agent=${agent || "default"}, variant=${variant || "none"}, forceNew=${forceNew === true}, spawnedBy=${spawnedBy || "unset"}`);
|
|
6447
|
-
const result = await spawnSession({ directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy });
|
|
6466
|
+
const result = await spawnSession({ directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId });
|
|
6467
|
+
const resumed = Boolean(resumeClaudeSessionId || resumeCodexThreadId);
|
|
6448
6468
|
switch (result.type) {
|
|
6449
6469
|
case "success":
|
|
6450
6470
|
if (!result.sessionId) {
|
|
@@ -6457,7 +6477,8 @@ function startDaemonControlServer({
|
|
|
6457
6477
|
return {
|
|
6458
6478
|
success: true,
|
|
6459
6479
|
sessionId: result.sessionId,
|
|
6460
|
-
approvedNewDirectoryCreation: true
|
|
6480
|
+
approvedNewDirectoryCreation: true,
|
|
6481
|
+
...resumed ? { resumed: true } : {}
|
|
6461
6482
|
};
|
|
6462
6483
|
case "requestToApproveDirectoryCreation":
|
|
6463
6484
|
reply.code(409);
|
|
@@ -8123,7 +8144,7 @@ function parseJsonObject(text) {
|
|
|
8123
8144
|
return null;
|
|
8124
8145
|
}
|
|
8125
8146
|
}
|
|
8126
|
-
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-ZIW1jrYm.cjs', document.baseURI).href))).resolve(s)) {
|
|
8127
8148
|
const platformArch = `${platform}-${arch}`;
|
|
8128
8149
|
const candidates = [
|
|
8129
8150
|
`@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
|
|
@@ -9389,6 +9410,8 @@ ${goal}`;
|
|
|
9389
9410
|
|
|
9390
9411
|
const MAX_LINE_CHARS = 500;
|
|
9391
9412
|
const MAX_ARGS_CHARS = 160;
|
|
9413
|
+
const DEFAULT_LIMITS = { line: MAX_LINE_CHARS, args: MAX_ARGS_CHARS };
|
|
9414
|
+
const NO_LIMITS = { line: Number.POSITIVE_INFINITY, args: Number.POSITIVE_INFINITY };
|
|
9392
9415
|
function truncateText(text, max = MAX_LINE_CHARS) {
|
|
9393
9416
|
const chars = Array.from(text);
|
|
9394
9417
|
if (chars.length <= max) return text;
|
|
@@ -9404,12 +9427,12 @@ function safeJson(value, max) {
|
|
|
9404
9427
|
return "[unserializable]";
|
|
9405
9428
|
}
|
|
9406
9429
|
}
|
|
9407
|
-
function formatMessageBody(body) {
|
|
9430
|
+
function formatMessageBody(body, limits = DEFAULT_LIMITS) {
|
|
9408
9431
|
if (!body || typeof body !== "object") return null;
|
|
9409
9432
|
const b = body;
|
|
9410
9433
|
if (b.role === "user" && b.content?.type === "text" && typeof b.content.text === "string") {
|
|
9411
9434
|
const text = squash(b.content.text);
|
|
9412
|
-
return text.length > 0 ? `user: ${truncateText(text)}` : null;
|
|
9435
|
+
return text.length > 0 ? `user: ${truncateText(text, limits.line)}` : null;
|
|
9413
9436
|
}
|
|
9414
9437
|
if (b.role === "session" && b.content && typeof b.content === "object") {
|
|
9415
9438
|
const env = b.content;
|
|
@@ -9421,18 +9444,18 @@ function formatMessageBody(body) {
|
|
|
9421
9444
|
const text = squash(String(ev.text ?? ""));
|
|
9422
9445
|
if (text.length === 0) return null;
|
|
9423
9446
|
const role = env.role === "user" ? "user" : "assistant";
|
|
9424
|
-
return `${role}: ${truncateText(text)}`;
|
|
9447
|
+
return `${role}: ${truncateText(text, limits.line)}`;
|
|
9425
9448
|
}
|
|
9426
9449
|
case "tool-call-start": {
|
|
9427
9450
|
const name = String(ev.name ?? "tool");
|
|
9428
9451
|
const title = squash(String(ev.title ?? ""));
|
|
9429
|
-
const args = ev.args ? safeJson(ev.args,
|
|
9452
|
+
const args = ev.args ? safeJson(ev.args, limits.args) : "";
|
|
9430
9453
|
const detail = title || args;
|
|
9431
|
-
return `[tool] ${name}${detail ? `: ${truncateText(detail,
|
|
9454
|
+
return `[tool] ${name}${detail ? `: ${truncateText(detail, limits.args)}` : ""}`;
|
|
9432
9455
|
}
|
|
9433
9456
|
case "service": {
|
|
9434
9457
|
const text = squash(String(ev.text ?? ""));
|
|
9435
|
-
return text.length > 0 ? `[service] ${truncateText(text,
|
|
9458
|
+
return text.length > 0 ? `[service] ${truncateText(text, limits.args)}` : null;
|
|
9436
9459
|
}
|
|
9437
9460
|
case "turn-end": {
|
|
9438
9461
|
if (ev.status && ev.status !== "completed") return `[turn ${ev.status}]`;
|
|
@@ -9447,12 +9470,12 @@ function formatMessageBody(body) {
|
|
|
9447
9470
|
switch (d.type) {
|
|
9448
9471
|
case "message": {
|
|
9449
9472
|
const text = squash(String(d.message ?? ""));
|
|
9450
|
-
return text.length > 0 ? `assistant: ${truncateText(text)}` : null;
|
|
9473
|
+
return text.length > 0 ? `assistant: ${truncateText(text, limits.line)}` : null;
|
|
9451
9474
|
}
|
|
9452
9475
|
case "tool-call":
|
|
9453
|
-
return `[tool] ${String(d.name ?? "tool")}: ${safeJson(d.input,
|
|
9476
|
+
return `[tool] ${String(d.name ?? "tool")}: ${safeJson(d.input, limits.args)}`;
|
|
9454
9477
|
case "permission-request":
|
|
9455
|
-
return `[permission] ${String(d.toolName ?? "")}: ${truncateText(squash(String(d.description ?? "")),
|
|
9478
|
+
return `[permission] ${String(d.toolName ?? "")}: ${truncateText(squash(String(d.description ?? "")), limits.args)}`;
|
|
9456
9479
|
default:
|
|
9457
9480
|
return null;
|
|
9458
9481
|
}
|
|
@@ -9463,22 +9486,118 @@ function formatMessageBody(body) {
|
|
|
9463
9486
|
const texts = msg.content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => squash(c.text)).filter((t) => t.length > 0);
|
|
9464
9487
|
if (texts.length > 0) {
|
|
9465
9488
|
const role = msg.role === "user" ? "user" : "assistant";
|
|
9466
|
-
return `${role}: ${truncateText(texts.join(" "))}`;
|
|
9489
|
+
return `${role}: ${truncateText(texts.join(" "), limits.line)}`;
|
|
9467
9490
|
}
|
|
9468
9491
|
}
|
|
9469
9492
|
return null;
|
|
9470
9493
|
}
|
|
9471
9494
|
return null;
|
|
9472
9495
|
}
|
|
9473
|
-
function formatTranscript(bodies) {
|
|
9496
|
+
function formatTranscript(bodies, options = {}) {
|
|
9497
|
+
const limits = options.full ? NO_LIMITS : DEFAULT_LIMITS;
|
|
9474
9498
|
const lines = [];
|
|
9475
9499
|
for (const body of bodies) {
|
|
9476
|
-
const line = formatMessageBody(body);
|
|
9500
|
+
const line = formatMessageBody(body, limits);
|
|
9477
9501
|
if (line !== null) lines.push(line);
|
|
9478
9502
|
}
|
|
9479
9503
|
return lines.join("\n");
|
|
9480
9504
|
}
|
|
9481
9505
|
|
|
9506
|
+
function asRecord(value) {
|
|
9507
|
+
return value && typeof value === "object" ? value : null;
|
|
9508
|
+
}
|
|
9509
|
+
function sessionEnvelope(body) {
|
|
9510
|
+
const b = asRecord(body);
|
|
9511
|
+
if (!b || b.role !== "session") return null;
|
|
9512
|
+
const env = asRecord(b.content);
|
|
9513
|
+
if (!env) return null;
|
|
9514
|
+
const ev = asRecord(env.ev);
|
|
9515
|
+
return ev && typeof ev.t === "string" ? { role: env.role, ev } : null;
|
|
9516
|
+
}
|
|
9517
|
+
function isUserPrompt(body) {
|
|
9518
|
+
const b = asRecord(body);
|
|
9519
|
+
return !!b && b.role === "user" && b.content?.type === "text" && typeof b.content.text === "string";
|
|
9520
|
+
}
|
|
9521
|
+
function agentPiece(body) {
|
|
9522
|
+
const env = sessionEnvelope(body);
|
|
9523
|
+
if (env && env.role !== "user") {
|
|
9524
|
+
if (env.ev.t === "text" && !env.ev.thinking) {
|
|
9525
|
+
const text = String(env.ev.text ?? "");
|
|
9526
|
+
return text.trim().length > 0 ? { kind: "text", text } : null;
|
|
9527
|
+
}
|
|
9528
|
+
if (env.ev.t === "tool-call-start") return { kind: "tool" };
|
|
9529
|
+
return null;
|
|
9530
|
+
}
|
|
9531
|
+
const b = asRecord(body);
|
|
9532
|
+
if (b?.role === "agent" && b.content?.type === "acp") {
|
|
9533
|
+
const d = asRecord(b.content.data);
|
|
9534
|
+
if (d?.type === "message") {
|
|
9535
|
+
const text = String(d.message ?? "");
|
|
9536
|
+
return text.trim().length > 0 ? { kind: "text", text } : null;
|
|
9537
|
+
}
|
|
9538
|
+
if (d?.type === "tool-call") return { kind: "tool" };
|
|
9539
|
+
}
|
|
9540
|
+
return null;
|
|
9541
|
+
}
|
|
9542
|
+
function userPromptText(body) {
|
|
9543
|
+
return isUserPrompt(body) ? body.content.text : null;
|
|
9544
|
+
}
|
|
9545
|
+
function analyzeLatestTurn(entries, options = {}) {
|
|
9546
|
+
const lastSeq = entries.length > 0 ? entries[entries.length - 1].seq : 0;
|
|
9547
|
+
let userIndex = -1;
|
|
9548
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
9549
|
+
if (isUserPrompt(entries[i].body) && (!options.anchor || options.anchor(entries[i].body))) {
|
|
9550
|
+
userIndex = i;
|
|
9551
|
+
break;
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9554
|
+
let ended = false;
|
|
9555
|
+
let status = null;
|
|
9556
|
+
let error = null;
|
|
9557
|
+
let texts = [];
|
|
9558
|
+
let lastMarker = null;
|
|
9559
|
+
let startedAfterPrompt = false;
|
|
9560
|
+
const emitsStarts = entries.some((entry) => sessionEnvelope(entry.body)?.ev?.t === "turn-start");
|
|
9561
|
+
for (let i = userIndex + 1; i < entries.length; i++) {
|
|
9562
|
+
const body = entries[i].body;
|
|
9563
|
+
if (options.anchor && isUserPrompt(body)) {
|
|
9564
|
+
if (ended) break;
|
|
9565
|
+
continue;
|
|
9566
|
+
}
|
|
9567
|
+
const env = sessionEnvelope(body);
|
|
9568
|
+
if (env?.ev?.t === "turn-start") {
|
|
9569
|
+
lastMarker = "start";
|
|
9570
|
+
startedAfterPrompt = true;
|
|
9571
|
+
ended = false;
|
|
9572
|
+
status = null;
|
|
9573
|
+
error = null;
|
|
9574
|
+
texts = [];
|
|
9575
|
+
continue;
|
|
9576
|
+
}
|
|
9577
|
+
if (env?.ev?.t === "turn-end") {
|
|
9578
|
+
lastMarker = "end";
|
|
9579
|
+
if (userIndex >= 0 && emitsStarts && !startedAfterPrompt) continue;
|
|
9580
|
+
ended = true;
|
|
9581
|
+
status = typeof env.ev.status === "string" ? env.ev.status : null;
|
|
9582
|
+
error = typeof env.ev.error === "string" ? env.ev.error : null;
|
|
9583
|
+
continue;
|
|
9584
|
+
}
|
|
9585
|
+
const piece = agentPiece(body);
|
|
9586
|
+
if (!piece) continue;
|
|
9587
|
+
if (piece.kind === "tool") texts = [];
|
|
9588
|
+
else texts.push(piece.text);
|
|
9589
|
+
}
|
|
9590
|
+
if (userIndex < 0) ended = lastMarker === "end";
|
|
9591
|
+
return {
|
|
9592
|
+
userSeq: userIndex >= 0 ? entries[userIndex].seq : null,
|
|
9593
|
+
ended,
|
|
9594
|
+
status: ended ? status : null,
|
|
9595
|
+
error: ended ? error : null,
|
|
9596
|
+
answer: texts.join("\n\n").trim(),
|
|
9597
|
+
lastSeq
|
|
9598
|
+
};
|
|
9599
|
+
}
|
|
9600
|
+
|
|
9482
9601
|
const USER_SOCKET_CONNECT_TIMEOUT_MS = 15e3;
|
|
9483
9602
|
async function openUserScopedSocket(token, options = {}) {
|
|
9484
9603
|
const socket = socket_ioClient.io(persistence.configuration.serverUrl, {
|
|
@@ -9824,19 +9943,21 @@ async function bearerToken() {
|
|
|
9824
9943
|
if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
|
|
9825
9944
|
return credentials.token;
|
|
9826
9945
|
}
|
|
9827
|
-
|
|
9946
|
+
function requirePersisted(sessionId) {
|
|
9828
9947
|
const persisted = persistence.readPersistedSessions()[sessionId];
|
|
9829
9948
|
if (!persisted) {
|
|
9830
9949
|
throw new Error(
|
|
9831
9950
|
`No local key for session ${sessionId} \u2014 it was not spawned by this machine's daemon (or is older than 14 days).`
|
|
9832
9951
|
);
|
|
9833
9952
|
}
|
|
9834
|
-
|
|
9953
|
+
return persisted;
|
|
9954
|
+
}
|
|
9955
|
+
async function readSessionLog(sessionId, persisted, limit) {
|
|
9835
9956
|
const token = await bearerToken();
|
|
9836
9957
|
const response = await axios.get(
|
|
9837
9958
|
`${persistence.configuration.serverUrl}/v3/sessions/${encodeURIComponent(sessionId)}/messages`,
|
|
9838
9959
|
{
|
|
9839
|
-
params: { before_seq: 2147483647, limit
|
|
9960
|
+
params: { before_seq: 2147483647, limit },
|
|
9840
9961
|
headers: {
|
|
9841
9962
|
"Authorization": `Bearer ${token}`,
|
|
9842
9963
|
"X-Happy-Client": `${SESSION_OPS_CLIENT}/${persistence.configuration.currentCliVersion}`
|
|
@@ -9847,21 +9968,62 @@ async function readSessionTranscript(sessionId, limit) {
|
|
|
9847
9968
|
const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
|
|
9848
9969
|
messages.reverse();
|
|
9849
9970
|
const key = persistence.decodeBase64(persisted.encryptionKey);
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
if (message.content?.t !== "encrypted") return null;
|
|
9971
|
+
return messages.map((message) => {
|
|
9972
|
+
if (message.content?.t !== "encrypted") return { seq: message.seq, body: null };
|
|
9853
9973
|
try {
|
|
9854
|
-
return persistence.decrypt(key, persisted.encryptionVariant, persistence.decodeBase64(message.content.c));
|
|
9974
|
+
return { seq: message.seq, body: persistence.decrypt(key, persisted.encryptionVariant, persistence.decodeBase64(message.content.c)) };
|
|
9855
9975
|
} catch {
|
|
9856
|
-
return null;
|
|
9976
|
+
return { seq: message.seq, body: null };
|
|
9857
9977
|
}
|
|
9858
9978
|
});
|
|
9979
|
+
}
|
|
9980
|
+
const TURN_WINDOW = 500;
|
|
9981
|
+
async function readSessionTranscript(sessionId, limit, options = {}) {
|
|
9982
|
+
const persisted = requirePersisted(sessionId);
|
|
9983
|
+
const bounded = Math.max(1, Math.min(MAX_READ_LIMIT, Math.floor(limit)));
|
|
9984
|
+
const entries = await readSessionLog(sessionId, persisted, Math.max(bounded, TURN_WINDOW));
|
|
9985
|
+
const shown = entries.slice(-bounded);
|
|
9986
|
+
const live = await index.listDaemonSessions();
|
|
9859
9987
|
return {
|
|
9860
9988
|
summary: toSummary(sessionId, persisted, sessionLiveness(live, sessionId)),
|
|
9861
|
-
messageCount:
|
|
9862
|
-
transcript: formatTranscript(
|
|
9989
|
+
messageCount: shown.length,
|
|
9990
|
+
transcript: formatTranscript(shown.map((entry) => entry.body), { full: options.full }),
|
|
9991
|
+
turn: analyzeLatestTurn(entries)
|
|
9863
9992
|
};
|
|
9864
9993
|
}
|
|
9994
|
+
async function readSessionMetadata(sessionId, persisted) {
|
|
9995
|
+
const token = await bearerToken();
|
|
9996
|
+
const response = await axios.get(`${persistence.configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}`, {
|
|
9997
|
+
headers: {
|
|
9998
|
+
"Authorization": `Bearer ${token}`,
|
|
9999
|
+
"X-Happy-Client": `${SESSION_OPS_CLIENT}/${persistence.configuration.currentCliVersion}`
|
|
10000
|
+
},
|
|
10001
|
+
timeout: 15e3
|
|
10002
|
+
});
|
|
10003
|
+
const ciphertext = response.data?.session?.metadata;
|
|
10004
|
+
if (typeof ciphertext !== "string" || ciphertext.length === 0) return null;
|
|
10005
|
+
return persistence.decrypt(persistence.decodeBase64(persisted.encryptionKey), persisted.encryptionVariant, persistence.decodeBase64(ciphertext));
|
|
10006
|
+
}
|
|
10007
|
+
class TurnWaitTimeoutError extends Error {
|
|
10008
|
+
constructor(sessionId, timeoutMs, turn) {
|
|
10009
|
+
super(`Session ${sessionId}: the latest turn did not end within ${Math.round(timeoutMs / 1e3)}s`);
|
|
10010
|
+
this.sessionId = sessionId;
|
|
10011
|
+
this.timeoutMs = timeoutMs;
|
|
10012
|
+
this.turn = turn;
|
|
10013
|
+
}
|
|
10014
|
+
}
|
|
10015
|
+
async function waitForTurnEnd(sessionId, options) {
|
|
10016
|
+
const persisted = requirePersisted(sessionId);
|
|
10017
|
+
const pollMs = options.pollMs ?? 3e3;
|
|
10018
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
10019
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
10020
|
+
while (true) {
|
|
10021
|
+
const turn = analyzeLatestTurn(await readSessionLog(sessionId, persisted, TURN_WINDOW));
|
|
10022
|
+
if (turn.ended) return turn;
|
|
10023
|
+
if (Date.now() + pollMs > deadline) throw new TurnWaitTimeoutError(sessionId, options.timeoutMs, turn);
|
|
10024
|
+
await sleep(pollMs);
|
|
10025
|
+
}
|
|
10026
|
+
}
|
|
9865
10027
|
async function stopSession(sessionId) {
|
|
9866
10028
|
return await index.stopDaemonSession(sessionId);
|
|
9867
10029
|
}
|
|
@@ -9894,7 +10056,7 @@ function readReceipt(home, id) {
|
|
|
9894
10056
|
throw error;
|
|
9895
10057
|
}
|
|
9896
10058
|
}
|
|
9897
|
-
function writeReceipt(home, receipt) {
|
|
10059
|
+
function writeReceipt$1(home, receipt) {
|
|
9898
10060
|
persistence.ensurePrivateDirectorySync(path.join(home, "teams", "effects"));
|
|
9899
10061
|
const target = receiptPath(home, receipt.operationId);
|
|
9900
10062
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
@@ -9971,7 +10133,7 @@ function createTeamWorker(deps) {
|
|
|
9971
10133
|
const action = async (teamId, requestId, value) => (await http.post(`/v1/teams/${encodeURIComponent(teamId)}/actions`, { requestId, action: value })).data;
|
|
9972
10134
|
async function complete(op, receipt) {
|
|
9973
10135
|
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 } : {} });
|
|
9974
|
-
writeReceipt(home, { ...receipt, phase: "completed" });
|
|
10136
|
+
writeReceipt$1(home, { ...receipt, phase: "completed" });
|
|
9975
10137
|
}
|
|
9976
10138
|
async function execute(op) {
|
|
9977
10139
|
let receipt = readReceipt(home, op.id);
|
|
@@ -9988,7 +10150,7 @@ function createTeamWorker(deps) {
|
|
|
9988
10150
|
op = claimed.operation;
|
|
9989
10151
|
credential = claimed.credential;
|
|
9990
10152
|
receipt = { operationId: op.id, teamId: op.teamId, claimId: op.claimId, phase: "prepared" };
|
|
9991
|
-
writeReceipt(home, receipt);
|
|
10153
|
+
writeReceipt$1(home, receipt);
|
|
9992
10154
|
}
|
|
9993
10155
|
if (receipt.phase === "completed") return;
|
|
9994
10156
|
try {
|
|
@@ -10001,7 +10163,7 @@ function createTeamWorker(deps) {
|
|
|
10001
10163
|
if (!task) throw new Error("Task state unavailable; refusing to start");
|
|
10002
10164
|
if (task.currentAttemptId !== op.attemptId || ["done", "cancelled"].includes(task.status)) {
|
|
10003
10165
|
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" });
|
|
10004
|
-
writeReceipt(home, { ...receipt, phase: "completed" });
|
|
10166
|
+
writeReceipt$1(home, { ...receipt, phase: "completed" });
|
|
10005
10167
|
return;
|
|
10006
10168
|
}
|
|
10007
10169
|
if (!op.directory) throw new Error("No repository selected for worker");
|
|
@@ -10011,17 +10173,17 @@ function createTeamWorker(deps) {
|
|
|
10011
10173
|
const scopeFile = path.join(scopes, `${op.id}.json`);
|
|
10012
10174
|
persistence.writePrivateFileSync(scopeFile, JSON.stringify({ serverUrl: server, scopeToken: credential.token, teamId: op.teamId, botId: op.botId, taskId: op.taskId, attemptId: op.attemptId }));
|
|
10013
10175
|
receipt = { ...receipt, ...resource, scopeFile, phase: "spawning" };
|
|
10014
|
-
writeReceipt(home, receipt);
|
|
10176
|
+
writeReceipt$1(home, receipt);
|
|
10015
10177
|
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 } });
|
|
10016
10178
|
if (spawned.type !== "success") throw new Error(spawned.type === "error" ? spawned.errorMessage : "Directory approval required");
|
|
10017
10179
|
receipt = { ...receipt, sessionId: spawned.sessionId, phase: "spawned" };
|
|
10018
|
-
writeReceipt(home, receipt);
|
|
10180
|
+
writeReceipt$1(home, receipt);
|
|
10019
10181
|
}
|
|
10020
10182
|
if (receipt.phase === "spawning") {
|
|
10021
10183
|
const matches = Object.entries(persistence.readPersistedSessions()).filter(([, entry]) => entry.metadata?.teamOperationId === op.id);
|
|
10022
10184
|
if (matches.length !== 1) throw new Error("Spawn outcome unknown; reconcile the worker before retrying");
|
|
10023
10185
|
receipt = { ...receipt, sessionId: matches[0][0], phase: "spawned" };
|
|
10024
|
-
writeReceipt(home, receipt);
|
|
10186
|
+
writeReceipt$1(home, receipt);
|
|
10025
10187
|
}
|
|
10026
10188
|
if (receipt.phase === "spawned") {
|
|
10027
10189
|
const current = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
|
|
@@ -10036,14 +10198,14 @@ function createTeamWorker(deps) {
|
|
|
10036
10198
|
const key = await waitForSessionKey(receipt.sessionId, 1e4);
|
|
10037
10199
|
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 } : {} });
|
|
10038
10200
|
receipt = { ...receipt, phase: "delivered" };
|
|
10039
|
-
writeReceipt(home, receipt);
|
|
10201
|
+
writeReceipt$1(home, receipt);
|
|
10040
10202
|
}
|
|
10041
10203
|
await complete(op, receipt);
|
|
10042
10204
|
} else {
|
|
10043
10205
|
const team = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
|
|
10044
10206
|
const latest = team.operations.find((candidate) => candidate.id === op.id);
|
|
10045
10207
|
if (latest?.status === "completed") {
|
|
10046
|
-
writeReceipt(home, { ...receipt, phase: "completed" });
|
|
10208
|
+
writeReceipt$1(home, { ...receipt, phase: "completed" });
|
|
10047
10209
|
return;
|
|
10048
10210
|
}
|
|
10049
10211
|
const bot = team.bots.find((candidate) => candidate.id === op.botId);
|
|
@@ -10173,6 +10335,643 @@ Use the teams tools to inspect current task state before acting. This message is
|
|
|
10173
10335
|
} };
|
|
10174
10336
|
}
|
|
10175
10337
|
|
|
10338
|
+
function resolveSpawnPermissionMode(agent, explicit) {
|
|
10339
|
+
return explicit ?? persistence.getAgentCodeDefaults(agent ?? "claude").permissionMode;
|
|
10340
|
+
}
|
|
10341
|
+
function outboundPermissionMode(agent, mode) {
|
|
10342
|
+
const key = persistence.normalizeAgentKey(agent ?? "claude");
|
|
10343
|
+
if ((key === "claude" || key === "pi") && mode === "yolo") return "bypassPermissions";
|
|
10344
|
+
return mode;
|
|
10345
|
+
}
|
|
10346
|
+
function resolveFirstMessageMeta(input) {
|
|
10347
|
+
const meta = {
|
|
10348
|
+
permissionMode: outboundPermissionMode(input.agent, input.permissionMode)
|
|
10349
|
+
};
|
|
10350
|
+
if (input.explicitModel !== void 0) {
|
|
10351
|
+
meta.model = input.explicitModel === "default" ? null : input.explicitModel;
|
|
10352
|
+
}
|
|
10353
|
+
if (persistence.normalizeAgentKey(input.agent ?? "claude") !== "claude") return meta;
|
|
10354
|
+
if (meta.model === void 0) {
|
|
10355
|
+
const model = persistence.getAgentCodeDefaults("claude").modelMode;
|
|
10356
|
+
const runnable = !persistence.isClaudeOpus55Model(model) || persistence.supportsClaudeOpus55({ capabilities: input.capabilities ?? null });
|
|
10357
|
+
meta.model = runnable && model !== "default" ? model : null;
|
|
10358
|
+
}
|
|
10359
|
+
meta.effort = null;
|
|
10360
|
+
return meta;
|
|
10361
|
+
}
|
|
10362
|
+
|
|
10363
|
+
function payloadJson(payload) {
|
|
10364
|
+
if (!payload) return void 0;
|
|
10365
|
+
try {
|
|
10366
|
+
return JSON.parse(payload);
|
|
10367
|
+
} catch {
|
|
10368
|
+
return void 0;
|
|
10369
|
+
}
|
|
10370
|
+
}
|
|
10371
|
+
function lookupPath(root, path) {
|
|
10372
|
+
let current = root;
|
|
10373
|
+
for (const segment of path) {
|
|
10374
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
10375
|
+
current = current[segment];
|
|
10376
|
+
}
|
|
10377
|
+
return current;
|
|
10378
|
+
}
|
|
10379
|
+
function scalar(value) {
|
|
10380
|
+
if (value === void 0 || value === null) return "";
|
|
10381
|
+
if (typeof value === "string") return value;
|
|
10382
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
10383
|
+
try {
|
|
10384
|
+
return JSON.stringify(value);
|
|
10385
|
+
} catch {
|
|
10386
|
+
return "";
|
|
10387
|
+
}
|
|
10388
|
+
}
|
|
10389
|
+
function formatNow(now, tz) {
|
|
10390
|
+
if (!tz) return new Date(now).toISOString();
|
|
10391
|
+
try {
|
|
10392
|
+
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));
|
|
10393
|
+
const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
10394
|
+
const offset = get("timeZoneName").replace(/^GMT$/, "+00:00").replace(/^GMT/, "");
|
|
10395
|
+
return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}${offset || "Z"}`;
|
|
10396
|
+
} catch {
|
|
10397
|
+
return new Date(now).toISOString();
|
|
10398
|
+
}
|
|
10399
|
+
}
|
|
10400
|
+
function renderTemplate(text, context) {
|
|
10401
|
+
const json = payloadJson(context.payload);
|
|
10402
|
+
return text.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\s*\}\}/g, (whole, name) => {
|
|
10403
|
+
if (name === "payload") return context.payload ?? "";
|
|
10404
|
+
if (name.startsWith("payload.")) return scalar(lookupPath(json, name.slice("payload.".length).split(".")));
|
|
10405
|
+
if (name === "run.id") return context.runId;
|
|
10406
|
+
if (name === "automation.name") return context.automationName;
|
|
10407
|
+
if (name === "now") return formatNow(context.now ?? Date.now(), context.tz);
|
|
10408
|
+
return whole;
|
|
10409
|
+
});
|
|
10410
|
+
}
|
|
10411
|
+
function renderAction(action, context) {
|
|
10412
|
+
const r = (value) => renderTemplate(value, context);
|
|
10413
|
+
if (action.kind === "spawn") return { ...action, prompt: r(action.prompt) };
|
|
10414
|
+
if (action.kind === "send") return { ...action, prompt: r(action.prompt) };
|
|
10415
|
+
return { ...action, command: action.command.map(r), ...action.env ? { env: Object.fromEntries(Object.entries(action.env).map(([k, v]) => [k, r(v)])) } : {} };
|
|
10416
|
+
}
|
|
10417
|
+
function clipText(text, maxChars, keep = "head") {
|
|
10418
|
+
if (text.length <= maxChars) return text;
|
|
10419
|
+
return keep === "head" ? text.slice(0, maxChars) : text.slice(text.length - maxChars);
|
|
10420
|
+
}
|
|
10421
|
+
const DURATION_UNITS = { ms: 1, s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
10422
|
+
function parseDuration(raw) {
|
|
10423
|
+
const text = raw.trim();
|
|
10424
|
+
if (/^\d+$/.test(text)) return Number(text);
|
|
10425
|
+
const re = /(\d+(?:\.\d+)?)(ms|s|m|h|d)/gy;
|
|
10426
|
+
let total = 0;
|
|
10427
|
+
let matched = 0;
|
|
10428
|
+
let last = 0;
|
|
10429
|
+
let m;
|
|
10430
|
+
while ((m = re.exec(text)) !== null) {
|
|
10431
|
+
total += Number(m[1]) * DURATION_UNITS[m[2]];
|
|
10432
|
+
matched++;
|
|
10433
|
+
last = re.lastIndex;
|
|
10434
|
+
}
|
|
10435
|
+
if (matched === 0 || last !== text.length) throw new Error(`Invalid duration: ${raw} (use e.g. 90s, 5m, 1h30m, 2d)`);
|
|
10436
|
+
return Math.round(total);
|
|
10437
|
+
}
|
|
10438
|
+
function formatDuration(ms) {
|
|
10439
|
+
if (ms % 864e5 === 0) return `${ms / 864e5}d`;
|
|
10440
|
+
if (ms % 36e5 === 0) return `${ms / 36e5}h`;
|
|
10441
|
+
if (ms % 6e4 === 0) return `${ms / 6e4}m`;
|
|
10442
|
+
if (ms % 1e3 === 0) return `${ms / 1e3}s`;
|
|
10443
|
+
return `${ms}ms`;
|
|
10444
|
+
}
|
|
10445
|
+
function triggerFromFlags(flags, now = Date.now()) {
|
|
10446
|
+
const given = [flags.cron !== void 0, flags.every !== void 0, flags.at !== void 0, flags.manual === true].filter(Boolean).length;
|
|
10447
|
+
if (given === 0) {
|
|
10448
|
+
if (flags.tz !== void 0) throw new Error("--tz only applies with --cron");
|
|
10449
|
+
return void 0;
|
|
10450
|
+
}
|
|
10451
|
+
if (given > 1) throw new Error("Use exactly one of --cron, --every, --at, --manual");
|
|
10452
|
+
if (flags.cron !== void 0) {
|
|
10453
|
+
if (!flags.tz) throw new Error("--cron requires --tz <IANA zone> (e.g. Asia/Singapore)");
|
|
10454
|
+
return { kind: "cron", expr: flags.cron, tz: flags.tz };
|
|
10455
|
+
}
|
|
10456
|
+
if (flags.tz !== void 0) throw new Error("--tz only applies with --cron");
|
|
10457
|
+
if (flags.every !== void 0) return { kind: "interval", everyMs: parseDuration(flags.every), anchorAt: now };
|
|
10458
|
+
if (flags.at !== void 0) {
|
|
10459
|
+
const at = Date.parse(flags.at);
|
|
10460
|
+
if (!Number.isFinite(at)) throw new Error(`--at must be an ISO 8601 timestamp, got: ${flags.at}`);
|
|
10461
|
+
return { kind: "once", at };
|
|
10462
|
+
}
|
|
10463
|
+
return { kind: "manual" };
|
|
10464
|
+
}
|
|
10465
|
+
function describeTrigger(trigger) {
|
|
10466
|
+
switch (trigger.kind) {
|
|
10467
|
+
case "cron":
|
|
10468
|
+
return `cron ${trigger.expr} (${trigger.tz})`;
|
|
10469
|
+
case "interval":
|
|
10470
|
+
return `every ${formatDuration(trigger.everyMs)}`;
|
|
10471
|
+
case "once":
|
|
10472
|
+
return `once at ${new Date(trigger.at).toISOString()}`;
|
|
10473
|
+
default:
|
|
10474
|
+
return "manual";
|
|
10475
|
+
}
|
|
10476
|
+
}
|
|
10477
|
+
function describeAction(action) {
|
|
10478
|
+
if (action.kind === "spawn") return `spawn ${action.agent} in ${action.directory}${action.sticky ? ` (sticky ${action.sticky.key})` : ""}`;
|
|
10479
|
+
if (action.kind === "send") return `send to session ${action.sessionId}`;
|
|
10480
|
+
return `script ${action.command.join(" ")}`;
|
|
10481
|
+
}
|
|
10482
|
+
|
|
10483
|
+
const RECEIPT_RETENTION_MS = 7 * 864e5;
|
|
10484
|
+
const RECEIPT_MAX_ENTRIES = 2e3;
|
|
10485
|
+
function receiptsPath(home) {
|
|
10486
|
+
return path.join(home, "automation-receipts.json");
|
|
10487
|
+
}
|
|
10488
|
+
function readReceipts(home) {
|
|
10489
|
+
let raw;
|
|
10490
|
+
try {
|
|
10491
|
+
raw = node_fs.readFileSync(receiptsPath(home), "utf8");
|
|
10492
|
+
} catch (error) {
|
|
10493
|
+
if (error.code === "ENOENT") return {};
|
|
10494
|
+
throw error;
|
|
10495
|
+
}
|
|
10496
|
+
const parsed = JSON.parse(raw);
|
|
10497
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.receipts !== "object") throw new Error("Invalid automation receipts file");
|
|
10498
|
+
return parsed.receipts;
|
|
10499
|
+
}
|
|
10500
|
+
function writeReceipt(home, receipt, now = Date.now()) {
|
|
10501
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(receipt.runId)) throw new Error("Invalid automation run id");
|
|
10502
|
+
const all = readReceipts(home);
|
|
10503
|
+
const next = { ...receipt, updatedAt: now };
|
|
10504
|
+
all[receipt.runId] = next;
|
|
10505
|
+
const entries = Object.values(all).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
10506
|
+
const kept = {};
|
|
10507
|
+
let finished = 0;
|
|
10508
|
+
for (const entry of entries) {
|
|
10509
|
+
if (entry.phase === "finished") {
|
|
10510
|
+
if (now - entry.updatedAt > RECEIPT_RETENTION_MS || finished >= RECEIPT_MAX_ENTRIES) continue;
|
|
10511
|
+
finished++;
|
|
10512
|
+
}
|
|
10513
|
+
kept[entry.runId] = entry;
|
|
10514
|
+
}
|
|
10515
|
+
const target = receiptsPath(home);
|
|
10516
|
+
persistence.ensurePrivateDirectorySync(path.dirname(target));
|
|
10517
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
10518
|
+
persistence.writePrivateFileSync(tmp, JSON.stringify({ version: 1, receipts: kept }));
|
|
10519
|
+
const file = node_fs.openSync(tmp, "r");
|
|
10520
|
+
try {
|
|
10521
|
+
node_fs.fsyncSync(file);
|
|
10522
|
+
} finally {
|
|
10523
|
+
node_fs.closeSync(file);
|
|
10524
|
+
}
|
|
10525
|
+
node_fs.renameSync(tmp, target);
|
|
10526
|
+
if (process.platform !== "win32") {
|
|
10527
|
+
const directory = node_fs.openSync(path.dirname(target), "r");
|
|
10528
|
+
try {
|
|
10529
|
+
node_fs.fsyncSync(directory);
|
|
10530
|
+
} finally {
|
|
10531
|
+
node_fs.closeSync(directory);
|
|
10532
|
+
}
|
|
10533
|
+
}
|
|
10534
|
+
return next;
|
|
10535
|
+
}
|
|
10536
|
+
|
|
10537
|
+
const SCRIPT_KILL_GRACE_MS = 1e4;
|
|
10538
|
+
const SCRIPT_DRAIN_MS = 2e3;
|
|
10539
|
+
class TailBuffer {
|
|
10540
|
+
constructor(maxChars) {
|
|
10541
|
+
this.maxChars = maxChars;
|
|
10542
|
+
}
|
|
10543
|
+
text = "";
|
|
10544
|
+
push(chunk) {
|
|
10545
|
+
this.text += chunk;
|
|
10546
|
+
if (this.text.length > this.maxChars * 2) this.text = this.text.slice(this.text.length - this.maxChars);
|
|
10547
|
+
}
|
|
10548
|
+
value() {
|
|
10549
|
+
return this.text.length > this.maxChars ? this.text.slice(this.text.length - this.maxChars) : this.text;
|
|
10550
|
+
}
|
|
10551
|
+
}
|
|
10552
|
+
function killScriptTree(child, signal) {
|
|
10553
|
+
if (child.exitCode !== null || child.signalCode !== null || !child.pid) return;
|
|
10554
|
+
try {
|
|
10555
|
+
if (process.platform !== "win32") process.kill(-child.pid, signal);
|
|
10556
|
+
else child.kill(signal);
|
|
10557
|
+
} catch {
|
|
10558
|
+
try {
|
|
10559
|
+
child.kill(signal);
|
|
10560
|
+
} catch {
|
|
10561
|
+
}
|
|
10562
|
+
}
|
|
10563
|
+
}
|
|
10564
|
+
function terminateScriptTree(child) {
|
|
10565
|
+
killScriptTree(child, "SIGTERM");
|
|
10566
|
+
const killer = setTimeout(() => killScriptTree(child, "SIGKILL"), SCRIPT_KILL_GRACE_MS);
|
|
10567
|
+
killer.unref();
|
|
10568
|
+
child.once("exit", () => clearTimeout(killer));
|
|
10569
|
+
}
|
|
10570
|
+
function runScript(options) {
|
|
10571
|
+
return new Promise((resolve) => {
|
|
10572
|
+
const tail = new TailBuffer(persistence.AUTOMATION_SUMMARY_MAX_CHARS);
|
|
10573
|
+
let timedOut = false;
|
|
10574
|
+
let settled = false;
|
|
10575
|
+
let child;
|
|
10576
|
+
const timers = [];
|
|
10577
|
+
const finish = (result) => {
|
|
10578
|
+
if (settled) return;
|
|
10579
|
+
settled = true;
|
|
10580
|
+
for (const t of timers) clearTimeout(t);
|
|
10581
|
+
resolve(result);
|
|
10582
|
+
};
|
|
10583
|
+
const timer = setTimeout(() => {
|
|
10584
|
+
timedOut = true;
|
|
10585
|
+
terminateScriptTree(child);
|
|
10586
|
+
}, options.timeoutMs);
|
|
10587
|
+
timer.unref();
|
|
10588
|
+
timers.push(timer);
|
|
10589
|
+
try {
|
|
10590
|
+
child = node_child_process.spawn(options.command[0], options.command.slice(1), {
|
|
10591
|
+
cwd: options.cwd,
|
|
10592
|
+
shell: false,
|
|
10593
|
+
windowsHide: true,
|
|
10594
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
10595
|
+
detached: process.platform !== "win32",
|
|
10596
|
+
env: { ...options.baseEnv ?? process.env, ...options.env ?? {} }
|
|
10597
|
+
});
|
|
10598
|
+
} catch (error) {
|
|
10599
|
+
finish({ exitCode: null, signal: null, timedOut: false, output: "", error: error instanceof Error ? error.message : String(error) });
|
|
10600
|
+
return;
|
|
10601
|
+
}
|
|
10602
|
+
options.onStart?.(child);
|
|
10603
|
+
child.stdout?.setEncoding("utf8").on("data", (chunk) => tail.push(chunk));
|
|
10604
|
+
child.stderr?.setEncoding("utf8").on("data", (chunk) => tail.push(chunk));
|
|
10605
|
+
child.on("error", (error) => finish({ exitCode: null, signal: null, timedOut, output: tail.value(), error: error.message }));
|
|
10606
|
+
child.on("close", (code, signal) => finish({ exitCode: code, signal, timedOut, output: tail.value() }));
|
|
10607
|
+
child.on("exit", (code, signal) => {
|
|
10608
|
+
const drain = setTimeout(() => {
|
|
10609
|
+
killScriptTree(child, "SIGKILL");
|
|
10610
|
+
finish({ exitCode: code, signal, timedOut, output: tail.value() });
|
|
10611
|
+
}, SCRIPT_DRAIN_MS);
|
|
10612
|
+
drain.unref();
|
|
10613
|
+
timers.push(drain);
|
|
10614
|
+
});
|
|
10615
|
+
});
|
|
10616
|
+
}
|
|
10617
|
+
|
|
10618
|
+
const AUTOMATION_TICK_MS = 5e3;
|
|
10619
|
+
const AUTOMATION_LEASE_RENEW_MS = 3e4;
|
|
10620
|
+
const AUTOMATION_LOG_READ_MS = 3e4;
|
|
10621
|
+
const AUTOMATION_UNAVAILABLE_LOG_MS = 10 * 6e4;
|
|
10622
|
+
const AUTOMATION_MAX_CONCURRENT = persistence.AUTOMATION_CLAIM_MAX_RUNS;
|
|
10623
|
+
const AUTOMATION_SPAWNED_BY = "automation";
|
|
10624
|
+
function automationPromptHeader(name, runId) {
|
|
10625
|
+
return `[Very Happy automation "${name}" run ${runId}; finish with automation_report (runId ${runId}) or \`very-happy auto report --run ${runId}\`]`;
|
|
10626
|
+
}
|
|
10627
|
+
function automationRunAnchor(runId) {
|
|
10628
|
+
const marker = ` run ${runId};`;
|
|
10629
|
+
return (body) => userPromptText(body)?.startsWith("[Very Happy automation ") === true && userPromptText(body).includes(marker);
|
|
10630
|
+
}
|
|
10631
|
+
async function defaultReadTurn(sessionId, runId) {
|
|
10632
|
+
const persisted = persistence.readPersistedSessions()[sessionId];
|
|
10633
|
+
if (!persisted) throw new Error(`No local key for session ${sessionId}`);
|
|
10634
|
+
return analyzeLatestTurn(await readSessionLog(sessionId, persisted, TURN_WINDOW), { anchor: automationRunAnchor(runId) });
|
|
10635
|
+
}
|
|
10636
|
+
async function defaultSend(sessionId, text, meta) {
|
|
10637
|
+
const persisted = await waitForSessionKey(sessionId, 15e3);
|
|
10638
|
+
await sendUserMessage(sessionId, persisted, text, "automation", { ...meta, sentFrom: "automation" });
|
|
10639
|
+
}
|
|
10640
|
+
function expandHome(path$1) {
|
|
10641
|
+
if (path$1 === "~") return os.homedir();
|
|
10642
|
+
if (path$1.startsWith("~/")) return path.join(os.homedir(), path$1.slice(2));
|
|
10643
|
+
return path$1;
|
|
10644
|
+
}
|
|
10645
|
+
function isSettledByServer(error) {
|
|
10646
|
+
if (!(error instanceof agentGuidance.AutomationsRequestError)) return false;
|
|
10647
|
+
if (error.status === 409 && (error.code === "run_finished" || error.code === "claim_mismatch")) return true;
|
|
10648
|
+
return error.status === 404 && error.code === "run_not_found";
|
|
10649
|
+
}
|
|
10650
|
+
function scriptAlive(item) {
|
|
10651
|
+
return !!item.child && item.child.exitCode === null && item.child.signalCode === null;
|
|
10652
|
+
}
|
|
10653
|
+
function createAutomationRunner(deps) {
|
|
10654
|
+
const home = deps.home ?? persistence.configuration.happyHomeDir;
|
|
10655
|
+
const client = deps.client ?? agentGuidance.createAutomationsClient({ serverUrl: deps.serverUrl ?? persistence.configuration.serverUrl, token: deps.token, client: "daemon-auto" });
|
|
10656
|
+
const runScript$1 = deps.runScript ?? runScript;
|
|
10657
|
+
const readTurn = deps.readTurn ?? defaultReadTurn;
|
|
10658
|
+
const send = deps.send ?? defaultSend;
|
|
10659
|
+
const prepareWorktree = deps.prepareWorktree ?? prepareTeamWorktree;
|
|
10660
|
+
const now = deps.now ?? (() => Date.now());
|
|
10661
|
+
const tracked = /* @__PURE__ */ new Map();
|
|
10662
|
+
let running = false;
|
|
10663
|
+
let renewing = false;
|
|
10664
|
+
let closed = false;
|
|
10665
|
+
let disabledUntil = 0;
|
|
10666
|
+
let lastUnavailableLogAt = -Infinity;
|
|
10667
|
+
let recovered = false;
|
|
10668
|
+
function persist(receipt) {
|
|
10669
|
+
writeReceipt(home, receipt, now());
|
|
10670
|
+
}
|
|
10671
|
+
async function finish(item, report2) {
|
|
10672
|
+
try {
|
|
10673
|
+
await client.report(item.runId, report2);
|
|
10674
|
+
} catch (error) {
|
|
10675
|
+
if (!isSettledByServer(error)) {
|
|
10676
|
+
item.pendingFinal = report2;
|
|
10677
|
+
deps.log(`Automation run ${item.runId} final report deferred`);
|
|
10678
|
+
return;
|
|
10679
|
+
}
|
|
10680
|
+
}
|
|
10681
|
+
persist({ runId: item.runId, automationId: item.automationId, kind: item.kind, phase: "finished", claimId: item.claimId, sessionId: item.sessionId });
|
|
10682
|
+
tracked.delete(item.runId);
|
|
10683
|
+
}
|
|
10684
|
+
function track(claimed, kind, sessionId, child) {
|
|
10685
|
+
const at = now();
|
|
10686
|
+
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 };
|
|
10687
|
+
tracked.set(item.runId, item);
|
|
10688
|
+
return item;
|
|
10689
|
+
}
|
|
10690
|
+
async function failNow(claimed, kind, error, attentionReason, sessionId) {
|
|
10691
|
+
const item = track(claimed, kind, sessionId);
|
|
10692
|
+
await finish(item, { claimId: item.claimId, status: "failed", error: clipText(error, persistence.AUTOMATION_SUMMARY_MAX_CHARS), ...sessionId ? { sessionId } : {}, ...attentionReason ? { needsAttention: true, attentionReason } : {} });
|
|
10693
|
+
}
|
|
10694
|
+
async function reportRunning(item, sessionId, stickyKey) {
|
|
10695
|
+
const body = { claimId: item.claimId, status: "running", ...sessionId ? { sessionId } : {}, ...stickyKey ? { stickyKey } : {} };
|
|
10696
|
+
try {
|
|
10697
|
+
await client.report(item.runId, body);
|
|
10698
|
+
} catch (error) {
|
|
10699
|
+
if (isSettledByServer(error)) {
|
|
10700
|
+
settleExternally(item);
|
|
10701
|
+
return;
|
|
10702
|
+
}
|
|
10703
|
+
if (sessionId) item.pendingRunning = { sessionId, ...stickyKey ? { stickyKey } : {} };
|
|
10704
|
+
deps.log(`Automation run ${item.runId} running report deferred`);
|
|
10705
|
+
}
|
|
10706
|
+
}
|
|
10707
|
+
async function execute(claimed) {
|
|
10708
|
+
const { run, automation } = claimed;
|
|
10709
|
+
const existing = readReceipts(home)[run.id];
|
|
10710
|
+
if (existing) {
|
|
10711
|
+
if (existing.phase === "finished" || tracked.has(run.id)) return;
|
|
10712
|
+
if (existing.phase === "starting") {
|
|
10713
|
+
await failNow(claimed, existing.kind, "Local execution outcome unknown after restart; verify before re-running", "unknown_outcome");
|
|
10714
|
+
return;
|
|
10715
|
+
}
|
|
10716
|
+
if (existing.kind === "script") {
|
|
10717
|
+
await failNow(claimed, "script", "Daemon restarted while the script was running", "daemon_restarted");
|
|
10718
|
+
return;
|
|
10719
|
+
}
|
|
10720
|
+
track(claimed, existing.kind, existing.sessionId).sawTurn = true;
|
|
10721
|
+
return;
|
|
10722
|
+
}
|
|
10723
|
+
const context = { payload: run.payload, runId: run.id, automationName: automation.name, ...automation.trigger.kind === "cron" ? { tz: automation.trigger.tz } : {}, now: now() };
|
|
10724
|
+
const action = renderAction(automation.action, context);
|
|
10725
|
+
const env = { VH_AUTOMATION_RUN_ID: run.id, VH_AUTOMATION_NAME: automation.name };
|
|
10726
|
+
const header = automationPromptHeader(automation.name, run.id);
|
|
10727
|
+
if (action.kind === "script") {
|
|
10728
|
+
const timeoutMs = action.timeoutMs ?? automation.maxRuntimeMs;
|
|
10729
|
+
persist({ runId: run.id, automationId: automation.id, kind: "script", phase: "starting", claimId: run.claimId });
|
|
10730
|
+
const item2 = track(claimed, "script");
|
|
10731
|
+
const finished = runScript$1({
|
|
10732
|
+
command: action.command,
|
|
10733
|
+
cwd: action.cwd ? expandHome(action.cwd) : void 0,
|
|
10734
|
+
timeoutMs,
|
|
10735
|
+
env: { ...action.env ?? {}, ...env, ...run.payload ? { VH_AUTOMATION_PAYLOAD: run.payload } : {} },
|
|
10736
|
+
onStart: (child) => {
|
|
10737
|
+
item2.child = child;
|
|
10738
|
+
}
|
|
10739
|
+
});
|
|
10740
|
+
persist({ runId: run.id, automationId: automation.id, kind: "script", phase: "running", claimId: run.claimId });
|
|
10741
|
+
await reportRunning(item2);
|
|
10742
|
+
void finished.then(async (result) => {
|
|
10743
|
+
if (!tracked.has(run.id)) return;
|
|
10744
|
+
const ok = result.exitCode === 0 && !result.timedOut && !result.error;
|
|
10745
|
+
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;
|
|
10746
|
+
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 } : {} });
|
|
10747
|
+
});
|
|
10748
|
+
return;
|
|
10749
|
+
}
|
|
10750
|
+
if (action.kind === "send") {
|
|
10751
|
+
if (!deps.live(action.sessionId)) {
|
|
10752
|
+
await failNow(claimed, "send", `Session ${action.sessionId} is not running on this machine`, "session_not_live", action.sessionId);
|
|
10753
|
+
return;
|
|
10754
|
+
}
|
|
10755
|
+
persist({ runId: run.id, automationId: automation.id, kind: "send", phase: "running", claimId: run.claimId, sessionId: action.sessionId });
|
|
10756
|
+
try {
|
|
10757
|
+
await send(action.sessionId, `${header}
|
|
10758
|
+
${action.prompt}`, { localId: `automation-${run.id}` });
|
|
10759
|
+
} catch (error) {
|
|
10760
|
+
await failNow(claimed, "send", `Message delivery failed: ${error instanceof Error ? error.message : String(error)}`, "send_failed", action.sessionId);
|
|
10761
|
+
return;
|
|
10762
|
+
}
|
|
10763
|
+
await reportRunning(track(claimed, "send", action.sessionId), action.sessionId);
|
|
10764
|
+
return;
|
|
10765
|
+
}
|
|
10766
|
+
const agent = action.agent;
|
|
10767
|
+
if (!isSpawnAgent(agent)) {
|
|
10768
|
+
await failNow(claimed, "spawn", `Unknown agent "${agent}"`, "invalid_action");
|
|
10769
|
+
return;
|
|
10770
|
+
}
|
|
10771
|
+
if (action.permissionMode && sanitizeSpawnPermissionMode(action.permissionMode) === null) {
|
|
10772
|
+
await failNow(claimed, "spawn", `Invalid permission mode "${action.permissionMode}"`, "invalid_action");
|
|
10773
|
+
return;
|
|
10774
|
+
}
|
|
10775
|
+
const permissionMode = resolveSpawnPermissionMode(agent, action.permissionMode ?? void 0);
|
|
10776
|
+
const stickyKey = action.sticky ? renderTemplate(action.sticky.key, context) : void 0;
|
|
10777
|
+
if (stickyKey) {
|
|
10778
|
+
const bound = claimed.stickies.find((sticky) => sticky.key === stickyKey);
|
|
10779
|
+
if (bound && deps.live(bound.sessionId)) {
|
|
10780
|
+
persist({ runId: run.id, automationId: automation.id, kind: "send", phase: "running", claimId: run.claimId, sessionId: bound.sessionId, stickyKey });
|
|
10781
|
+
try {
|
|
10782
|
+
await send(bound.sessionId, `${header}
|
|
10783
|
+
${action.prompt}`, { localId: `automation-${run.id}` });
|
|
10784
|
+
} catch (error) {
|
|
10785
|
+
await failNow(claimed, "send", `Sticky session delivery failed: ${error instanceof Error ? error.message : String(error)}`, "send_failed", bound.sessionId);
|
|
10786
|
+
return;
|
|
10787
|
+
}
|
|
10788
|
+
await reportRunning(track(claimed, "send", bound.sessionId), bound.sessionId, stickyKey);
|
|
10789
|
+
return;
|
|
10790
|
+
}
|
|
10791
|
+
if (bound) await client.deleteSticky(automation.id, stickyKey).catch(() => deps.log(`Automation ${automation.name}: stale sticky ${stickyKey} not cleared`));
|
|
10792
|
+
}
|
|
10793
|
+
let directory = expandHome(action.directory);
|
|
10794
|
+
if (!path.isAbsolute(directory)) {
|
|
10795
|
+
await failNow(claimed, "spawn", `Directory must be absolute: ${action.directory}`, "invalid_action");
|
|
10796
|
+
return;
|
|
10797
|
+
}
|
|
10798
|
+
if (action.worktree) {
|
|
10799
|
+
try {
|
|
10800
|
+
directory = (await prepareWorktree(home, directory, `auto-${run.id}`)).directory;
|
|
10801
|
+
} catch (error) {
|
|
10802
|
+
await failNow(claimed, "spawn", `Worktree preparation failed: ${error instanceof Error ? error.message : String(error)}`, "worktree_failed");
|
|
10803
|
+
return;
|
|
10804
|
+
}
|
|
10805
|
+
}
|
|
10806
|
+
persist({ runId: run.id, automationId: automation.id, kind: "spawn", phase: "starting", claimId: run.claimId, stickyKey, ...action.worktree ? { directory } : {} });
|
|
10807
|
+
const spawned = await deps.spawn({ directory, agent, permissionMode, spawnedBy: AUTOMATION_SPAWNED_BY, environmentVariables: env });
|
|
10808
|
+
if (spawned.type !== "success") {
|
|
10809
|
+
await failNow(claimed, "spawn", spawned.type === "error" ? spawned.errorMessage : "Directory approval required", "spawn_failed");
|
|
10810
|
+
return;
|
|
10811
|
+
}
|
|
10812
|
+
persist({ runId: run.id, automationId: automation.id, kind: "spawn", phase: "running", claimId: run.claimId, sessionId: spawned.sessionId, stickyKey, ...action.worktree ? { directory } : {} });
|
|
10813
|
+
const item = track(claimed, "spawn", spawned.sessionId);
|
|
10814
|
+
try {
|
|
10815
|
+
const capabilities = persistence.readPersistedSessions()[spawned.sessionId]?.metadata?.capabilities ?? null;
|
|
10816
|
+
const meta = resolveFirstMessageMeta({ agent, permissionMode, explicitModel: action.model ?? void 0, capabilities });
|
|
10817
|
+
await send(spawned.sessionId, `${header}
|
|
10818
|
+
${action.prompt}`, { localId: `automation-${run.id}`, ...meta });
|
|
10819
|
+
} catch (error) {
|
|
10820
|
+
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" });
|
|
10821
|
+
return;
|
|
10822
|
+
}
|
|
10823
|
+
await reportRunning(item, spawned.sessionId, stickyKey);
|
|
10824
|
+
}
|
|
10825
|
+
async function monitor() {
|
|
10826
|
+
for (const item of [...tracked.values()]) {
|
|
10827
|
+
if (closed) return;
|
|
10828
|
+
if (item.pendingFinal) {
|
|
10829
|
+
await finish(item, item.pendingFinal);
|
|
10830
|
+
continue;
|
|
10831
|
+
}
|
|
10832
|
+
if (item.kind === "script") continue;
|
|
10833
|
+
const sessionId = item.sessionId;
|
|
10834
|
+
const active = deps.turnActive(sessionId);
|
|
10835
|
+
if (active) item.sawTurn = true;
|
|
10836
|
+
const alive = deps.live(sessionId) && !item.exited;
|
|
10837
|
+
if (!alive) {
|
|
10838
|
+
const turn2 = await readTurn(sessionId, item.runId).catch(() => null);
|
|
10839
|
+
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) });
|
|
10840
|
+
else await finish(item, { claimId: item.claimId, status: "failed", sessionId, error: turn2?.error ?? "Session exited before the turn ended", needsAttention: true, attentionReason: "session_exited" });
|
|
10841
|
+
continue;
|
|
10842
|
+
}
|
|
10843
|
+
const edgeFell = item.sawTurn && !active;
|
|
10844
|
+
if (!edgeFell && now() < item.nextLogReadAt) continue;
|
|
10845
|
+
item.nextLogReadAt = now() + AUTOMATION_LOG_READ_MS;
|
|
10846
|
+
if (edgeFell) item.sawTurn = false;
|
|
10847
|
+
const turn = await readTurn(sessionId, item.runId).catch(() => null);
|
|
10848
|
+
if (!turn?.ended) continue;
|
|
10849
|
+
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}` });
|
|
10850
|
+
else await finish(item, { claimId: item.claimId, status: "done", sessionId, summary: clipText(turn.answer, persistence.AUTOMATION_SUMMARY_MAX_CHARS) });
|
|
10851
|
+
}
|
|
10852
|
+
}
|
|
10853
|
+
async function renewAll() {
|
|
10854
|
+
if (renewing || closed) return;
|
|
10855
|
+
renewing = true;
|
|
10856
|
+
try {
|
|
10857
|
+
const at = now();
|
|
10858
|
+
for (const item of [...tracked.values()]) {
|
|
10859
|
+
if (closed) return;
|
|
10860
|
+
if (at - item.lastLeaseAt < AUTOMATION_LEASE_RENEW_MS) continue;
|
|
10861
|
+
item.lastLeaseAt = at;
|
|
10862
|
+
const body = { claimId: item.claimId, leaseMs: persistence.AUTOMATION_DEFAULT_LEASE_MS, ...item.pendingRunning ? { status: "running", ...item.pendingRunning } : {} };
|
|
10863
|
+
try {
|
|
10864
|
+
const run = await client.report(item.runId, body);
|
|
10865
|
+
item.pendingRunning = void 0;
|
|
10866
|
+
if (persistence.isAutomationRunTerminal(run.status)) settleExternally(item);
|
|
10867
|
+
} catch (error) {
|
|
10868
|
+
if (isSettledByServer(error)) settleExternally(item);
|
|
10869
|
+
else deps.log(`Automation run ${item.runId} lease renewal failed`);
|
|
10870
|
+
}
|
|
10871
|
+
}
|
|
10872
|
+
} finally {
|
|
10873
|
+
renewing = false;
|
|
10874
|
+
}
|
|
10875
|
+
}
|
|
10876
|
+
function settleExternally(item) {
|
|
10877
|
+
if (item.child && scriptAlive(item)) terminateScriptTree(item.child);
|
|
10878
|
+
persist({ runId: item.runId, automationId: item.automationId, kind: item.kind, phase: "finished", claimId: item.claimId, sessionId: item.sessionId });
|
|
10879
|
+
tracked.delete(item.runId);
|
|
10880
|
+
}
|
|
10881
|
+
async function recover() {
|
|
10882
|
+
recovered = true;
|
|
10883
|
+
let receipts;
|
|
10884
|
+
try {
|
|
10885
|
+
receipts = readReceipts(home);
|
|
10886
|
+
} catch {
|
|
10887
|
+
deps.log("Automation receipts unreadable; refusing to execute until repaired");
|
|
10888
|
+
closed = true;
|
|
10889
|
+
return;
|
|
10890
|
+
}
|
|
10891
|
+
for (const receipt of Object.values(receipts)) {
|
|
10892
|
+
if (receipt.phase === "finished") continue;
|
|
10893
|
+
const item = track({ run: { id: receipt.runId, claimId: receipt.claimId }, automation: { id: receipt.automationId } }, receipt.kind, receipt.sessionId);
|
|
10894
|
+
item.sawTurn = true;
|
|
10895
|
+
item.lastLeaseAt = 0;
|
|
10896
|
+
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" });
|
|
10897
|
+
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" });
|
|
10898
|
+
}
|
|
10899
|
+
}
|
|
10900
|
+
async function tick() {
|
|
10901
|
+
if (running || closed) return;
|
|
10902
|
+
running = true;
|
|
10903
|
+
try {
|
|
10904
|
+
if (!recovered) await recover();
|
|
10905
|
+
if (closed) return;
|
|
10906
|
+
await monitor();
|
|
10907
|
+
const capacity = AUTOMATION_MAX_CONCURRENT - tracked.size;
|
|
10908
|
+
if (now() >= disabledUntil && capacity > 0) {
|
|
10909
|
+
try {
|
|
10910
|
+
const claimed = await client.claim({ machineId: deps.machineId, limit: capacity, leaseMs: persistence.AUTOMATION_DEFAULT_LEASE_MS });
|
|
10911
|
+
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)}`))));
|
|
10912
|
+
} catch (error) {
|
|
10913
|
+
if (agentGuidance.isAutomationsUnavailable(error)) {
|
|
10914
|
+
disabledUntil = now() + 6e4;
|
|
10915
|
+
if (now() - lastUnavailableLogAt >= AUTOMATION_UNAVAILABLE_LOG_MS) {
|
|
10916
|
+
lastUnavailableLogAt = now();
|
|
10917
|
+
deps.log("Automations unavailable on this server (404); claim loop idle");
|
|
10918
|
+
}
|
|
10919
|
+
} else {
|
|
10920
|
+
disabledUntil = now() + 1e4;
|
|
10921
|
+
deps.log(`Automation claim failed${error instanceof agentGuidance.AutomationsRequestError ? ` (${error.status})` : ""}`);
|
|
10922
|
+
}
|
|
10923
|
+
}
|
|
10924
|
+
}
|
|
10925
|
+
} finally {
|
|
10926
|
+
running = false;
|
|
10927
|
+
}
|
|
10928
|
+
}
|
|
10929
|
+
function report(sessionId, event) {
|
|
10930
|
+
for (const item of tracked.values()) {
|
|
10931
|
+
if (item.sessionId !== sessionId) continue;
|
|
10932
|
+
if (event === "exited") item.exited = true;
|
|
10933
|
+
if (event === "blocked" && !item.attentionReported) {
|
|
10934
|
+
item.attentionReported = true;
|
|
10935
|
+
void client.report(item.runId, { claimId: item.claimId, needsAttention: true, attentionReason: "needs_input" }).catch(() => deps.log(`Automation run ${item.runId} attention report failed`));
|
|
10936
|
+
}
|
|
10937
|
+
}
|
|
10938
|
+
}
|
|
10939
|
+
const tickMs = deps.tickMs ?? AUTOMATION_TICK_MS;
|
|
10940
|
+
const timer = setInterval(() => {
|
|
10941
|
+
void tick();
|
|
10942
|
+
}, tickMs);
|
|
10943
|
+
timer.unref();
|
|
10944
|
+
const leaseTimer = setInterval(() => {
|
|
10945
|
+
void renewAll();
|
|
10946
|
+
}, tickMs);
|
|
10947
|
+
leaseTimer.unref();
|
|
10948
|
+
void tick();
|
|
10949
|
+
return {
|
|
10950
|
+
tick,
|
|
10951
|
+
renewAll,
|
|
10952
|
+
report,
|
|
10953
|
+
hasSession: (id) => [...tracked.values()].some((item) => item.sessionId === id),
|
|
10954
|
+
trackedRunIds: () => [...tracked.keys()],
|
|
10955
|
+
/** Executing a claim, or a script child is still alive — handover and auto-update wait for this. */
|
|
10956
|
+
get busy() {
|
|
10957
|
+
return running || [...tracked.values()].some(scriptAlive);
|
|
10958
|
+
},
|
|
10959
|
+
/**
|
|
10960
|
+
* Stop the loops. `killScripts` (real shutdown) terminates live script
|
|
10961
|
+
* trees; the next daemon's recovery reports them failed + attention from
|
|
10962
|
+
* the receipt, never re-running them. A handover waits on `busy` instead.
|
|
10963
|
+
*/
|
|
10964
|
+
stop: (options = {}) => {
|
|
10965
|
+
closed = true;
|
|
10966
|
+
clearInterval(timer);
|
|
10967
|
+
clearInterval(leaseTimer);
|
|
10968
|
+
if (options.killScripts) {
|
|
10969
|
+
for (const item of tracked.values()) if (item.child && scriptAlive(item)) terminateScriptTree(item.child);
|
|
10970
|
+
}
|
|
10971
|
+
}
|
|
10972
|
+
};
|
|
10973
|
+
}
|
|
10974
|
+
|
|
10176
10975
|
function daemonExitCode(source) {
|
|
10177
10976
|
return source === "exception" ? 1 : 0;
|
|
10178
10977
|
}
|
|
@@ -10333,6 +11132,7 @@ async function startDaemon() {
|
|
|
10333
11132
|
const turnActivity = new index.TurnActivityTracker();
|
|
10334
11133
|
const sessionIdToFinishedSession = /* @__PURE__ */ new Map();
|
|
10335
11134
|
let teamWorker;
|
|
11135
|
+
let automationRunner;
|
|
10336
11136
|
const persisted = persistence.readPersistedSessions();
|
|
10337
11137
|
const liveHappyProcesses = await findAllHappyProcesses();
|
|
10338
11138
|
for (const [id, s] of Object.entries(persisted)) {
|
|
@@ -11187,6 +11987,7 @@ async function startDaemon() {
|
|
|
11187
11987
|
const onChildExited = (pid) => {
|
|
11188
11988
|
const session = pidToTrackedSession.get(pid);
|
|
11189
11989
|
if (session?.happySessionId && (session.spawnedBy === "teams" || teamWorker?.hasSession(session.happySessionId))) teamWorker?.report(session.happySessionId, "exited");
|
|
11990
|
+
if (session?.happySessionId && automationRunner?.hasSession(session.happySessionId)) automationRunner.report(session.happySessionId, "exited");
|
|
11190
11991
|
if (session?.happySessionId) turnActivity.forget(session.happySessionId);
|
|
11191
11992
|
if (session?.happySessionId && session.encryption) {
|
|
11192
11993
|
sessionIdToFinishedSession.set(session.happySessionId, session);
|
|
@@ -11206,6 +12007,7 @@ async function startDaemon() {
|
|
|
11206
12007
|
}
|
|
11207
12008
|
};
|
|
11208
12009
|
const onSessionStateEvent = (sessionId, event, spawnedByFromSession) => {
|
|
12010
|
+
if (automationRunner?.hasSession(sessionId)) automationRunner.report(sessionId, event === "completed" ? "idle" : "blocked");
|
|
11209
12011
|
if (spawnedByFromSession === "teams" || teamWorker?.hasSession(sessionId) || [...pidToTrackedSession.values()].some((s) => s.happySessionId === sessionId && s.spawnedBy === "teams")) {
|
|
11210
12012
|
teamWorker?.report(sessionId, event === "completed" ? "idle" : "blocked");
|
|
11211
12013
|
return;
|
|
@@ -11444,7 +12246,7 @@ async function startDaemon() {
|
|
|
11444
12246
|
} catch {
|
|
11445
12247
|
}
|
|
11446
12248
|
}
|
|
11447
|
-
if (bundleReplaced && !teamWorker?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
|
|
12249
|
+
if (bundleReplaced && !teamWorker?.busy && !automationRunner?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
|
|
11448
12250
|
const preflight = await preflightNewBundle(bundlePath);
|
|
11449
12251
|
if (preflight.action === "hold") {
|
|
11450
12252
|
if (lastHandoverHold !== preflight.reason) {
|
|
@@ -11454,10 +12256,11 @@ async function startDaemon() {
|
|
|
11454
12256
|
updateController.holdHandover(preflight.reason);
|
|
11455
12257
|
return;
|
|
11456
12258
|
}
|
|
11457
|
-
if (teamWorker?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
|
|
12259
|
+
if (teamWorker?.busy || automationRunner?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
|
|
11458
12260
|
lastHandoverHold = null;
|
|
11459
12261
|
persistence.logger.debug("[DAEMON RUN] Daemon bundle replaced on disk and verified, handing off to new daemon");
|
|
11460
12262
|
teamWorker?.stop();
|
|
12263
|
+
automationRunner?.stop();
|
|
11461
12264
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
11462
12265
|
clearInterval(cliUpdateInterval);
|
|
11463
12266
|
stopAgentVersionChecks();
|
|
@@ -11509,6 +12312,14 @@ async function startDaemon() {
|
|
|
11509
12312
|
live: (id) => [...pidToTrackedSession.values()].some((s) => s.happySessionId === id && isPidAlive(s.pid)),
|
|
11510
12313
|
log: (message) => persistence.logger.debug(`[DAEMON RUN] ${message}`)
|
|
11511
12314
|
});
|
|
12315
|
+
automationRunner = createAutomationRunner({
|
|
12316
|
+
machineId,
|
|
12317
|
+
token: credentials.token,
|
|
12318
|
+
spawn: spawnSession,
|
|
12319
|
+
live: (id) => [...pidToTrackedSession.values()].some((s) => s.happySessionId === id && isPidAlive(s.pid)),
|
|
12320
|
+
turnActive: (id) => turnActivity.activeSessions().includes(id),
|
|
12321
|
+
log: (message) => persistence.logger.debug(`[DAEMON RUN] ${message}`)
|
|
12322
|
+
});
|
|
11512
12323
|
void apiMachine.updateMachineMetadata((current) => ({
|
|
11513
12324
|
...current ?? initialMachineMetadata,
|
|
11514
12325
|
teamsVersion: 1,
|
|
@@ -11517,6 +12328,7 @@ async function startDaemon() {
|
|
|
11517
12328
|
const cleanupAndShutdown = async (source, errorMessage) => {
|
|
11518
12329
|
persistence.logger.debug(`[DAEMON RUN] Starting proper cleanup (source: ${source}, errorMessage: ${errorMessage})...`);
|
|
11519
12330
|
teamWorker?.stop();
|
|
12331
|
+
automationRunner?.stop({ killScripts: true });
|
|
11520
12332
|
if (restartOnStaleVersionAndHeartbeat) {
|
|
11521
12333
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
11522
12334
|
persistence.logger.debug("[DAEMON RUN] Health check interval cleared");
|
|
@@ -12021,6 +12833,7 @@ function createMcpServer(handlers, options, sessionId) {
|
|
|
12021
12833
|
version: "1.0.0"
|
|
12022
12834
|
});
|
|
12023
12835
|
agentGuidance.registerTeamsTools(mcp, sessionId);
|
|
12836
|
+
agentGuidance.registerAutomationTools(mcp);
|
|
12024
12837
|
mcp.registerTool("change_title", {
|
|
12025
12838
|
description: "Change the title of the current chat session",
|
|
12026
12839
|
title: "Change Chat Title",
|
|
@@ -12215,7 +13028,7 @@ async function startHappyServer(client, options) {
|
|
|
12215
13028
|
return;
|
|
12216
13029
|
}
|
|
12217
13030
|
}
|
|
12218
|
-
const usage =
|
|
13031
|
+
const usage = persistence.ContextUsageSchema.parse({ ...JSON.parse(body), updatedAt: Date.now() });
|
|
12219
13032
|
options.onContextUsage(usage);
|
|
12220
13033
|
res.writeHead(204).end();
|
|
12221
13034
|
} catch {
|
|
@@ -12263,6 +13076,7 @@ async function startHappyServer(client, options) {
|
|
|
12263
13076
|
agentGuidance.PREVIEW_TOOL_NAME,
|
|
12264
13077
|
agentGuidance.REPORT_PROGRESS_TOOL_NAME,
|
|
12265
13078
|
...agentGuidance.TEAM_TOOL_NAMES,
|
|
13079
|
+
...agentGuidance.AUTOMATION_TOOL_NAMES,
|
|
12266
13080
|
...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? ASSISTANT_TOOL_NAMES : []
|
|
12267
13081
|
],
|
|
12268
13082
|
stop: () => {
|
|
@@ -12599,7 +13413,7 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12599
13413
|
const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
|
|
12600
13414
|
const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
|
|
12601
13415
|
const fork = deps.run ?? (async (input) => {
|
|
12602
|
-
const { query } = await Promise.resolve().then(function () { return require('./index-
|
|
13416
|
+
const { query } = await Promise.resolve().then(function () { return require('./index-CRaJ9k2p.cjs'); });
|
|
12603
13417
|
return runSideQuestion(query, input);
|
|
12604
13418
|
});
|
|
12605
13419
|
const run = async (input, live) => {
|
|
@@ -12718,7 +13532,7 @@ const MAX_TITLE_CHARS = 60;
|
|
|
12718
13532
|
const GENERATION_TIMEOUT_MS = 3e4;
|
|
12719
13533
|
function resolveClaudeBinary() {
|
|
12720
13534
|
try {
|
|
12721
|
-
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-
|
|
13535
|
+
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-ZIW1jrYm.cjs', document.baseURI).href)));
|
|
12722
13536
|
const utilsPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_version_utils.cjs"));
|
|
12723
13537
|
const { getClaudeCliPath } = require$1(utilsPath);
|
|
12724
13538
|
const path$1 = getClaudeCliPath();
|
|
@@ -14947,9 +15761,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
|
|
|
14947
15761
|
};
|
|
14948
15762
|
}
|
|
14949
15763
|
|
|
14950
|
-
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-
|
|
15764
|
+
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-ZIW1jrYm.cjs', document.baseURI).href)));
|
|
14951
15765
|
const __dirname$1 = path.dirname(__filename$1);
|
|
14952
|
-
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
15766
|
+
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-ZIW1jrYm.cjs', document.baseURI).href)));
|
|
14953
15767
|
const PRISMA_QUERY_ENGINE_FILES = {
|
|
14954
15768
|
"arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
|
|
14955
15769
|
"x64-darwin": "libquery_engine-darwin.dylib.node",
|
|
@@ -15384,14 +16198,14 @@ function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagent
|
|
|
15384
16198
|
if (!subagent || startedSubagents.has(subagent)) {
|
|
15385
16199
|
return;
|
|
15386
16200
|
}
|
|
15387
|
-
envelopes.push(
|
|
16201
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
|
|
15388
16202
|
startedSubagents.add(subagent);
|
|
15389
16203
|
activeSubagents.add(subagent);
|
|
15390
16204
|
}
|
|
15391
16205
|
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15392
16206
|
const envelopes = [];
|
|
15393
16207
|
for (const subagent of activeSubagents) {
|
|
15394
|
-
envelopes.push(
|
|
16208
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
15395
16209
|
}
|
|
15396
16210
|
activeSubagents.clear();
|
|
15397
16211
|
startedSubagents.clear();
|
|
@@ -15487,7 +16301,7 @@ function turnStatus(turn) {
|
|
|
15487
16301
|
function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
|
|
15488
16302
|
const time = turnTimestampMs(turn);
|
|
15489
16303
|
const opts = { turn: turn.id, time, codexItemId: item.id };
|
|
15490
|
-
envelopes.push(
|
|
16304
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15491
16305
|
t: "tool-call-start",
|
|
15492
16306
|
call: item.id,
|
|
15493
16307
|
name,
|
|
@@ -15499,7 +16313,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
|
|
|
15499
16313
|
id: `${item.id}:start`
|
|
15500
16314
|
}));
|
|
15501
16315
|
if (output && output.trim().length > 0) {
|
|
15502
|
-
envelopes.push(
|
|
16316
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15503
16317
|
t: "text",
|
|
15504
16318
|
text: output,
|
|
15505
16319
|
thinking: true
|
|
@@ -15509,7 +16323,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
|
|
|
15509
16323
|
}));
|
|
15510
16324
|
}
|
|
15511
16325
|
if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
|
|
15512
|
-
envelopes.push(
|
|
16326
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15513
16327
|
t: "tool-call-end",
|
|
15514
16328
|
call: item.id
|
|
15515
16329
|
}, {
|
|
@@ -15531,7 +16345,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
|
15531
16345
|
agentsStates: item.agentsStates ?? {}
|
|
15532
16346
|
};
|
|
15533
16347
|
const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
|
|
15534
|
-
const envelopes = [
|
|
16348
|
+
const envelopes = [persistence.createEnvelope("agent", {
|
|
15535
16349
|
t: "tool-call-start",
|
|
15536
16350
|
call: item.id,
|
|
15537
16351
|
name: "CodexCollaboration",
|
|
@@ -15540,7 +16354,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
|
15540
16354
|
args
|
|
15541
16355
|
}, { ...opts, id: startId })];
|
|
15542
16356
|
if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
|
|
15543
|
-
envelopes.push(
|
|
16357
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15544
16358
|
t: "tool-call-end",
|
|
15545
16359
|
call: item.id,
|
|
15546
16360
|
result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
|
|
@@ -15553,7 +16367,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15553
16367
|
for (const turn of thread.turns ?? []) {
|
|
15554
16368
|
const startedAt = turnTimestampMs(turn);
|
|
15555
16369
|
const completedAt = completedTimestampMs(turn);
|
|
15556
|
-
envelopes.push(
|
|
16370
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "turn-start" }, {
|
|
15557
16371
|
id: `${turn.id}:start`,
|
|
15558
16372
|
turn: turn.id,
|
|
15559
16373
|
time: startedAt
|
|
@@ -15568,7 +16382,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15568
16382
|
case "userMessage": {
|
|
15569
16383
|
const text = textFromInputItems(item.content);
|
|
15570
16384
|
if (text) {
|
|
15571
|
-
envelopes.push(
|
|
16385
|
+
envelopes.push(persistence.createEnvelope("user", { t: "text", text }, {
|
|
15572
16386
|
id: item.id,
|
|
15573
16387
|
time: startedAt,
|
|
15574
16388
|
codexItemId: item.id
|
|
@@ -15579,7 +16393,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15579
16393
|
case "agentMessage": {
|
|
15580
16394
|
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15581
16395
|
if (text.length > 0) {
|
|
15582
|
-
envelopes.push(
|
|
16396
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text }, {
|
|
15583
16397
|
id: item.id,
|
|
15584
16398
|
turn: turn.id,
|
|
15585
16399
|
time: completedAt,
|
|
@@ -15591,7 +16405,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15591
16405
|
case "reasoning": {
|
|
15592
16406
|
const text = reasoningText(item);
|
|
15593
16407
|
if (text) {
|
|
15594
|
-
envelopes.push(
|
|
16408
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15595
16409
|
id: item.id,
|
|
15596
16410
|
turn: turn.id,
|
|
15597
16411
|
time: startedAt,
|
|
@@ -15646,7 +16460,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15646
16460
|
}
|
|
15647
16461
|
}
|
|
15648
16462
|
}
|
|
15649
|
-
envelopes.push(
|
|
16463
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15650
16464
|
id: `${turn.id}:end`,
|
|
15651
16465
|
turn: turn.id,
|
|
15652
16466
|
time: completedAt
|
|
@@ -15701,7 +16515,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15701
16515
|
}
|
|
15702
16516
|
if (type === "task_started") {
|
|
15703
16517
|
const turnId = cuid2.createId();
|
|
15704
|
-
const turnStart =
|
|
16518
|
+
const turnStart = persistence.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15705
16519
|
startedSubagents.clear();
|
|
15706
16520
|
activeSubagents.clear();
|
|
15707
16521
|
providerSubagentToSessionSubagent.clear();
|
|
@@ -15732,7 +16546,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15732
16546
|
providerSubagentToSessionSubagent,
|
|
15733
16547
|
envelopes: [
|
|
15734
16548
|
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
15735
|
-
|
|
16549
|
+
persistence.createEnvelope("agent", {
|
|
15736
16550
|
t: "turn-end",
|
|
15737
16551
|
status: pickTurnEndStatus(message, type)
|
|
15738
16552
|
}, lifecycleOpts)
|
|
@@ -15762,7 +16576,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15762
16576
|
}
|
|
15763
16577
|
const envelopes = [];
|
|
15764
16578
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15765
|
-
envelopes.push(
|
|
16579
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text: message.message }, opts));
|
|
15766
16580
|
return {
|
|
15767
16581
|
currentTurnId: state.currentTurnId,
|
|
15768
16582
|
startedSubagents,
|
|
@@ -15784,7 +16598,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15784
16598
|
}
|
|
15785
16599
|
const envelopes = [];
|
|
15786
16600
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15787
|
-
envelopes.push(
|
|
16601
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
15788
16602
|
return {
|
|
15789
16603
|
currentTurnId: state.currentTurnId,
|
|
15790
16604
|
startedSubagents,
|
|
@@ -15801,7 +16615,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15801
16615
|
const envelopes = [];
|
|
15802
16616
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15803
16617
|
envelopes.push(
|
|
15804
|
-
|
|
16618
|
+
persistence.createEnvelope("agent", {
|
|
15805
16619
|
t: "tool-call-start",
|
|
15806
16620
|
call,
|
|
15807
16621
|
name: "CodexBash",
|
|
@@ -15822,7 +16636,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15822
16636
|
const call = pickCallId(message);
|
|
15823
16637
|
const envelopes = [];
|
|
15824
16638
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15825
|
-
envelopes.push(
|
|
16639
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15826
16640
|
return {
|
|
15827
16641
|
currentTurnId: state.currentTurnId,
|
|
15828
16642
|
startedSubagents,
|
|
@@ -15838,7 +16652,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15838
16652
|
const envelopes = [];
|
|
15839
16653
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15840
16654
|
envelopes.push(
|
|
15841
|
-
|
|
16655
|
+
persistence.createEnvelope("agent", {
|
|
15842
16656
|
t: "tool-call-start",
|
|
15843
16657
|
call,
|
|
15844
16658
|
name: "CodexPatch",
|
|
@@ -15862,7 +16676,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15862
16676
|
const call = pickCallId(message);
|
|
15863
16677
|
const envelopes = [];
|
|
15864
16678
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15865
|
-
envelopes.push(
|
|
16679
|
+
envelopes.push(persistence.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15866
16680
|
return {
|
|
15867
16681
|
currentTurnId: state.currentTurnId,
|
|
15868
16682
|
startedSubagents,
|
|
@@ -15883,7 +16697,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
15883
16697
|
const toolLikeMessage = message;
|
|
15884
16698
|
const opts = buildEnvelopeOptions(state.currentTurnId);
|
|
15885
16699
|
if (message.type === "reasoning") {
|
|
15886
|
-
return [
|
|
16700
|
+
return [persistence.createEnvelope("agent", {
|
|
15887
16701
|
t: "text",
|
|
15888
16702
|
text: message.message,
|
|
15889
16703
|
thinking: true
|
|
@@ -15891,7 +16705,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
15891
16705
|
}
|
|
15892
16706
|
if (message.type === "tool-call") {
|
|
15893
16707
|
const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
|
|
15894
|
-
return [
|
|
16708
|
+
return [persistence.createEnvelope("agent", {
|
|
15895
16709
|
t: "tool-call-start",
|
|
15896
16710
|
call: toolLikeMessage.callId,
|
|
15897
16711
|
name: toolLikeMessage.name || "unknown",
|
|
@@ -15904,13 +16718,13 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
15904
16718
|
const envelopes = [];
|
|
15905
16719
|
const content = toolLikeMessage.output?.content;
|
|
15906
16720
|
if (typeof content === "string" && content.trim().length > 0) {
|
|
15907
|
-
envelopes.push(
|
|
16721
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15908
16722
|
t: "text",
|
|
15909
16723
|
text: content,
|
|
15910
16724
|
thinking: true
|
|
15911
16725
|
}, opts));
|
|
15912
16726
|
}
|
|
15913
|
-
envelopes.push(
|
|
16727
|
+
envelopes.push(persistence.createEnvelope("agent", {
|
|
15914
16728
|
t: "tool-call-end",
|
|
15915
16729
|
call: toolLikeMessage.callId
|
|
15916
16730
|
}, opts));
|
|
@@ -16906,7 +17720,7 @@ function buildCodexTurnPrompt(opts) {
|
|
|
16906
17720
|
parts.push(opts.message);
|
|
16907
17721
|
if (opts.includeTitleInstruction) {
|
|
16908
17722
|
parts.push(CHANGE_TITLE_INSTRUCTION);
|
|
16909
|
-
parts.push(
|
|
17723
|
+
parts.push(persistence.BUILTIN_TODO_DISCOVERY);
|
|
16910
17724
|
}
|
|
16911
17725
|
return parts.join("\n\n");
|
|
16912
17726
|
}
|
|
@@ -17887,7 +18701,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17887
18701
|
return;
|
|
17888
18702
|
} else if (subcommand === "install-terminal-hooks") {
|
|
17889
18703
|
try {
|
|
17890
|
-
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-
|
|
18704
|
+
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-BoDxpaBO.cjs'); });
|
|
17891
18705
|
const command = parseTerminalHooksArgs(args.slice(1));
|
|
17892
18706
|
if (command.action === "help") {
|
|
17893
18707
|
console.log(TERMINAL_HOOKS_HELP);
|
|
@@ -17904,7 +18718,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17904
18718
|
}
|
|
17905
18719
|
} else if (subcommand === "agent-home") {
|
|
17906
18720
|
try {
|
|
17907
|
-
const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-
|
|
18721
|
+
const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-CpJjrQZc.cjs'); });
|
|
17908
18722
|
await runAgentHomeCommand(parseAgentHomeArgs(args.slice(1)));
|
|
17909
18723
|
process.exit(0);
|
|
17910
18724
|
} catch (error) {
|
|
@@ -17913,7 +18727,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17913
18727
|
}
|
|
17914
18728
|
} else if (subcommand === "spawn") {
|
|
17915
18729
|
try {
|
|
17916
|
-
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-
|
|
18730
|
+
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DA8WAFmm.cjs'); });
|
|
17917
18731
|
await handleSpawnCommand(args.slice(1));
|
|
17918
18732
|
} catch (error) {
|
|
17919
18733
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17925,7 +18739,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17925
18739
|
return;
|
|
17926
18740
|
} else if (subcommand === "sessions") {
|
|
17927
18741
|
try {
|
|
17928
|
-
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-
|
|
18742
|
+
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-Tad8elmy.cjs'); });
|
|
17929
18743
|
await handleSessionsCommand(args.slice(1));
|
|
17930
18744
|
} catch (error) {
|
|
17931
18745
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17937,7 +18751,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17937
18751
|
return;
|
|
17938
18752
|
} else if (subcommand === "send") {
|
|
17939
18753
|
try {
|
|
17940
|
-
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-
|
|
18754
|
+
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-wl9ei8yG.cjs'); });
|
|
17941
18755
|
await handleSendCommand(args.slice(1));
|
|
17942
18756
|
} catch (error) {
|
|
17943
18757
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -18043,9 +18857,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18043
18857
|
if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
|
|
18044
18858
|
const projectId = args[3];
|
|
18045
18859
|
try {
|
|
18046
|
-
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
18047
|
-
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-
|
|
18048
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-
|
|
18860
|
+
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-DeJgSMh-.cjs'); });
|
|
18861
|
+
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-BNTWyUaZ.cjs'); }).then(function (n) { return n.persistence; });
|
|
18862
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CN28qJl7.cjs'); }).then(function (n) { return n.api; });
|
|
18049
18863
|
let userEmail = void 0;
|
|
18050
18864
|
try {
|
|
18051
18865
|
const credentials = await readCredentialsForConfiguredRelay2();
|
|
@@ -18076,7 +18890,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18076
18890
|
}
|
|
18077
18891
|
if (geminiSubcommand === "project" && args[2] === "get") {
|
|
18078
18892
|
try {
|
|
18079
|
-
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
18893
|
+
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-DeJgSMh-.cjs'); });
|
|
18080
18894
|
const config = readGeminiLocalConfig();
|
|
18081
18895
|
if (config.googleCloudProject) {
|
|
18082
18896
|
console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
|
|
@@ -18116,7 +18930,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18116
18930
|
process.exit(0);
|
|
18117
18931
|
}
|
|
18118
18932
|
try {
|
|
18119
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
18933
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-Bgg1BzDK.cjs'); });
|
|
18120
18934
|
let startedBy = void 0;
|
|
18121
18935
|
for (let i = 1; i < args.length; i++) {
|
|
18122
18936
|
if (args[i] === "--started-by") {
|
|
@@ -18139,7 +18953,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18139
18953
|
} else if (subcommand === "pi") {
|
|
18140
18954
|
if (args[1] === "--terminal") {
|
|
18141
18955
|
try {
|
|
18142
|
-
const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-
|
|
18956
|
+
const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-B7K75aXB.cjs'); });
|
|
18143
18957
|
process.exitCode = await runPiTerminal(args.slice(2));
|
|
18144
18958
|
} catch (error) {
|
|
18145
18959
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -18156,7 +18970,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18156
18970
|
}
|
|
18157
18971
|
};
|
|
18158
18972
|
try {
|
|
18159
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
18973
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-imt4mC41.cjs'); });
|
|
18160
18974
|
const parsed = parsePiRunnerArgs(args.slice(1));
|
|
18161
18975
|
const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
|
|
18162
18976
|
const { credentials } = await authAndSetupMachineIfNeeded();
|
|
@@ -18184,7 +18998,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18184
18998
|
return;
|
|
18185
18999
|
} else if (subcommand === "acp") {
|
|
18186
19000
|
try {
|
|
18187
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
19001
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-imt4mC41.cjs'); });
|
|
18188
19002
|
let startedBy = void 0;
|
|
18189
19003
|
let verbose = false;
|
|
18190
19004
|
const acpArgs = [];
|
|
@@ -18224,7 +19038,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18224
19038
|
return;
|
|
18225
19039
|
} else if (subcommand === "openclaw") {
|
|
18226
19040
|
try {
|
|
18227
|
-
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-
|
|
19041
|
+
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-B9qWHybc.cjs'); });
|
|
18228
19042
|
let startedBy = void 0;
|
|
18229
19043
|
let verbose = false;
|
|
18230
19044
|
let gatewayUrl;
|
|
@@ -18275,7 +19089,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18275
19089
|
return;
|
|
18276
19090
|
} else if (subcommand === "todo") {
|
|
18277
19091
|
try {
|
|
18278
|
-
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-
|
|
19092
|
+
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-mCMoGjJk.cjs'); });
|
|
18279
19093
|
await handleTodoCommand(args.slice(1));
|
|
18280
19094
|
} catch (error) {
|
|
18281
19095
|
console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
|
|
@@ -18284,16 +19098,28 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18284
19098
|
return;
|
|
18285
19099
|
} else if (subcommand === "teams") {
|
|
18286
19100
|
try {
|
|
18287
|
-
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-
|
|
19101
|
+
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-qVLrFcdd.cjs'); });
|
|
18288
19102
|
await handleTeamsCommand(args.slice(1));
|
|
18289
19103
|
} catch (error) {
|
|
18290
19104
|
console.error(error instanceof Error ? error.message : "Teams command failed");
|
|
18291
19105
|
process.exitCode = 1;
|
|
18292
19106
|
}
|
|
18293
19107
|
return;
|
|
19108
|
+
} else if (subcommand === "auto") {
|
|
19109
|
+
try {
|
|
19110
|
+
const { handleAutoCommand } = await Promise.resolve().then(function () { return require('./auto-DncmTDvw.cjs'); });
|
|
19111
|
+
await handleAutoCommand(args.slice(1));
|
|
19112
|
+
} catch (error) {
|
|
19113
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Automation command failed");
|
|
19114
|
+
if (process.env.DEBUG) {
|
|
19115
|
+
console.error(error);
|
|
19116
|
+
}
|
|
19117
|
+
process.exitCode = 1;
|
|
19118
|
+
}
|
|
19119
|
+
return;
|
|
18294
19120
|
} else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
|
|
18295
19121
|
try {
|
|
18296
|
-
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-
|
|
19122
|
+
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-CCKCKL6l.cjs'); });
|
|
18297
19123
|
await handleMcpCommand(args[1] === "--terminal-tools");
|
|
18298
19124
|
} catch (error) {
|
|
18299
19125
|
process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -18527,6 +19353,8 @@ ${chalk.bold("Usage:")}
|
|
|
18527
19353
|
conversations on this machine (agent-home --help)
|
|
18528
19354
|
very-happy connect Connect AI vendor API keys
|
|
18529
19355
|
very-happy todo Built-in Todos and official agent skill (todo --help)
|
|
19356
|
+
very-happy auto Scheduled / triggered automations: spawn, sticky
|
|
19357
|
+
sessions or scripts with tracked runs (auto --help)
|
|
18530
19358
|
very-happy sandbox Configure and manage OS-level sandboxing
|
|
18531
19359
|
very-happy notify Send push notification
|
|
18532
19360
|
very-happy mcp Stdio MCP server exposing copy_to_clipboard for a
|
|
@@ -18679,23 +19507,30 @@ exports.SPAWN_AGENTS = SPAWN_AGENTS;
|
|
|
18679
19507
|
exports.StreamRelay = StreamRelay;
|
|
18680
19508
|
exports.TEAM_SKILL = TEAM_SKILL;
|
|
18681
19509
|
exports.TitleGenerator = TitleGenerator;
|
|
19510
|
+
exports.TurnWaitTimeoutError = TurnWaitTimeoutError;
|
|
18682
19511
|
exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
|
|
18683
19512
|
exports.archiveSession = archiveSession;
|
|
18684
19513
|
exports.checkPreviewPath = checkPreviewPath;
|
|
18685
19514
|
exports.createSessionMetadata = createSessionMetadata;
|
|
19515
|
+
exports.describeAction = describeAction;
|
|
19516
|
+
exports.describeTrigger = describeTrigger;
|
|
18686
19517
|
exports.hashObject = hashObject;
|
|
18687
19518
|
exports.initialMachineMetadata = initialMachineMetadata;
|
|
18688
19519
|
exports.isValidSpawnOrigin = isValidSpawnOrigin;
|
|
18689
19520
|
exports.listAccountSessions = listAccountSessions;
|
|
18690
19521
|
exports.listSessions = listSessions;
|
|
18691
19522
|
exports.normalizeAcpPermissionMode = normalizeAcpPermissionMode;
|
|
19523
|
+
exports.parseDuration = parseDuration;
|
|
18692
19524
|
exports.query = query;
|
|
19525
|
+
exports.readSessionMetadata = readSessionMetadata;
|
|
18693
19526
|
exports.readSessionTranscript = readSessionTranscript;
|
|
18694
19527
|
exports.registerAgentAttachmentDownloads = registerAgentAttachmentDownloads;
|
|
18695
19528
|
exports.registerAssistantSessionTools = registerAssistantSessionTools;
|
|
18696
19529
|
exports.registerKillSessionHandler = registerKillSessionHandler;
|
|
18697
19530
|
exports.removeSessionModeFile = removeSessionModeFile;
|
|
19531
|
+
exports.resolveFirstMessageMeta = resolveFirstMessageMeta;
|
|
18698
19532
|
exports.resolvePermissionRequest = resolvePermissionRequest;
|
|
19533
|
+
exports.resolveSpawnPermissionMode = resolveSpawnPermissionMode;
|
|
18699
19534
|
exports.sanitizeSpawnPermissionMode = sanitizeSpawnPermissionMode;
|
|
18700
19535
|
exports.sendUserMessage = sendUserMessage;
|
|
18701
19536
|
exports.sessionWebUrl = sessionWebUrl;
|
|
@@ -18704,5 +19539,7 @@ exports.stageClaudeAttachments = stageClaudeAttachments;
|
|
|
18704
19539
|
exports.startHappyServer = startHappyServer;
|
|
18705
19540
|
exports.stopSession = stopSession;
|
|
18706
19541
|
exports.tmuxSupportsSessionEnv = tmuxSupportsSessionEnv;
|
|
19542
|
+
exports.triggerFromFlags = triggerFromFlags;
|
|
18707
19543
|
exports.waitForSessionKey = waitForSessionKey;
|
|
19544
|
+
exports.waitForTurnEnd = waitForTurnEnd;
|
|
18708
19545
|
exports.writeSessionModeFile = writeSessionModeFile;
|