very-happy-cli 0.2.143 → 0.2.145
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-LEgn9vol.mjs → AcpBackend-BITeBnbV.mjs} +2 -2
- package/dist/{AcpBackend-B8oehrfU.cjs → AcpBackend-FPv5uXFa.cjs} +30 -30
- package/dist/{AcpSessionManager-BomdfyhZ.cjs → AcpSessionManager-vqLzM219.cjs} +9 -9
- package/dist/{AcpSessionManager-Yfbs5bi2.mjs → AcpSessionManager-w2srpWQJ.mjs} +1 -1
- package/dist/{agentGuidance-CJSl54ua.cjs → agentGuidance-Bd6GEiYp.cjs} +1 -1
- package/dist/{agentGuidance-BU4hxm8M.mjs → agentGuidance-HFHrz3C1.mjs} +1 -1
- package/dist/agentHome-CGl7FF7a.cjs +113 -0
- package/dist/agentHome-DBqr8rpv.mjs +107 -0
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-euZ_K5Lo.mjs → config-B9zAvnGT.mjs} +4 -4
- package/dist/{config-CfqWoHmO.cjs → config-CPfFuHP6.cjs} +4 -4
- package/dist/{index-ZOMC8Gzn.mjs → index-BzLZu_aF.mjs} +8 -8
- package/dist/{index-PM_SsRpw.mjs → index-CxK0l_t0.mjs} +4 -4
- package/dist/{index-BREtikhm.cjs → index-D4Smqppj.cjs} +4 -4
- package/dist/{index-C510dSUn.cjs → index-DhwtFJEe.cjs} +309 -212
- package/dist/{index-Bj68y6ee.cjs → index-avfJgX4p.cjs} +31 -31
- package/dist/{index-DoiG88oX.mjs → index-eBrE-0R1.mjs} +136 -39
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{installTerminalHooks-BGVQqbgq.mjs → installTerminalHooks-Dq1MFFDQ.mjs} +4 -4
- package/dist/{installTerminalHooks-C_jraM1j.cjs → installTerminalHooks-DspFGxBI.cjs} +4 -4
- package/dist/lib.cjs +5 -5
- package/dist/lib.mjs +2 -2
- package/dist/{limits-uABU-yq3.mjs → limits-CzWD9hWU.mjs} +8 -8
- package/dist/{limits-B3oVgqBJ.cjs → limits-DGZB-OVl.cjs} +7 -7
- package/dist/{mcp-G-3lL_KH.cjs → mcp-BioGrTC_.cjs} +9 -9
- package/dist/{mcp-CfbTQXyh.mjs → mcp-L1Yq25LB.mjs} +4 -4
- package/dist/{piRuntime-CWE-1W-y.cjs → piRuntime-DodWS0UI.cjs} +2 -2
- package/dist/{piRuntime-BE6iYQwU.mjs → piRuntime-UdVG4eJ7.mjs} +2 -2
- package/dist/{piTerminal-Dyc_9pGP.cjs → piTerminal-6P00O_w8.cjs} +6 -6
- package/dist/{piTerminal-rqKn1_Zy.mjs → piTerminal-BIRroC_N.mjs} +6 -6
- package/dist/{runGemini-BMlXIaTL.mjs → runGemini-B-It6K8t.mjs} +6 -6
- package/dist/{runGemini-C6qMFzcn.cjs → runGemini-ChCHjnIh.cjs} +24 -24
- package/dist/{runOpenClaw-C2_aCP7v.cjs → runOpenClaw-Cc4_zmHz.cjs} +26 -26
- package/dist/{runOpenClaw-CoO5Vp3S.mjs → runOpenClaw-DET6LY2v.mjs} +5 -5
- package/dist/{send-CBrJylJc.mjs → send-CPayvXBm.mjs} +4 -4
- package/dist/{send-DGVwmB-p.cjs → send-LBjLfAos.cjs} +4 -4
- package/dist/{sessions-DbLubh2N.mjs → sessions-CJUEp43_.mjs} +4 -4
- package/dist/{sessions-DyQXMN-8.cjs → sessions-WBowxNO-.cjs} +5 -5
- package/dist/{spawn-DIyFOn3W.cjs → spawn-BqgZc7YE.cjs} +6 -6
- package/dist/{spawn-wkkxEGdF.mjs → spawn-Dcetf5Jt.mjs} +4 -4
- package/dist/{teams-BTyVFK3P.mjs → teams-CW_Zb2Q1.mjs} +4 -4
- package/dist/{teams-QOkGFjkJ.cjs → teams-eIUxGmqg.cjs} +4 -4
- package/dist/{todo-DoLkqdBe.mjs → todo-CV5fwHg_.mjs} +3 -3
- package/dist/{todo-Du6pjwx_.cjs → todo-DeouLiGa.cjs} +5 -5
- package/dist/{types-BG4gFjLL.mjs → types-BbvxVQF3.mjs} +321 -11
- package/dist/{types-6__wqmmP.cjs → types-Bqo68u61.cjs} +328 -9
- package/package.json +7 -7
|
@@ -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
|
|
7
|
-
var persistence = require('./limits-
|
|
6
|
+
var index = require('./types-Bqo68u61.cjs');
|
|
7
|
+
var persistence = require('./limits-DGZB-OVl.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-Bd6GEiYp.cjs');
|
|
14
14
|
var promises = require('node:fs/promises');
|
|
15
15
|
var fs = require('fs/promises');
|
|
16
16
|
var ink = require('ink');
|
|
@@ -288,7 +288,7 @@ class Session {
|
|
|
288
288
|
reportEventToDaemon = (event) => {
|
|
289
289
|
const spawnedBy = process.env.HAPPY_SPAWNED_BY;
|
|
290
290
|
if (!spawnedBy) return;
|
|
291
|
-
void
|
|
291
|
+
void index.notifyDaemonSessionEvent(this.client.sessionId, event, spawnedBy).catch((error) => persistence.logger.debug("[Session] Failed to report session event to daemon:", error));
|
|
292
292
|
};
|
|
293
293
|
/** Produce an `error` notification for an unexpected session failure. */
|
|
294
294
|
onSessionError = (message) => {
|
|
@@ -398,7 +398,7 @@ function pinSmallFastModel(env) {
|
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
function claudeCheckSession(sessionId, path$1) {
|
|
401
|
-
const projectDir =
|
|
401
|
+
const projectDir = index.getProjectPath(path$1);
|
|
402
402
|
const sessionFile = path.join(projectDir, `${sessionId}.jsonl`);
|
|
403
403
|
const sessionExists = node_fs.existsSync(sessionFile);
|
|
404
404
|
if (!sessionExists) {
|
|
@@ -424,7 +424,7 @@ function claudeCheckSession(sessionId, path$1) {
|
|
|
424
424
|
|
|
425
425
|
function claudeFindLastSession(workingDirectory) {
|
|
426
426
|
try {
|
|
427
|
-
const projectDir =
|
|
427
|
+
const projectDir = index.getProjectPath(workingDirectory);
|
|
428
428
|
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
429
429
|
const files = node_fs.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
430
430
|
const sessionId = f.replace(".jsonl", "");
|
|
@@ -478,7 +478,7 @@ function shouldIncludeCoAuthoredBy() {
|
|
|
478
478
|
|
|
479
479
|
const BASE_SYSTEM_PROMPT = (() => agentGuidance.trimIdent(`
|
|
480
480
|
ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
|
|
481
|
-
`))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" +
|
|
481
|
+
`))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" + index.BUILTIN_TODO_DISCOVERY;
|
|
482
482
|
const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
|
|
483
483
|
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
484
484
|
|
|
@@ -511,7 +511,7 @@ function quoteShellArg(value) {
|
|
|
511
511
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
512
512
|
}
|
|
513
513
|
async function claudeLocal(opts) {
|
|
514
|
-
const projectDir =
|
|
514
|
+
const projectDir = index.getProjectPath(opts.path);
|
|
515
515
|
node_fs.mkdirSync(projectDir, { recursive: true });
|
|
516
516
|
const hasContinueFlag = opts.claudeArgs?.includes("--continue");
|
|
517
517
|
const hasResumeFlag = opts.claudeArgs?.includes("--resume");
|
|
@@ -667,7 +667,7 @@ async function claudeLocal(opts) {
|
|
|
667
667
|
persistence.logger.warn("[ClaudeLocal] Sandbox is not supported on Windows; continuing without sandbox.");
|
|
668
668
|
} else {
|
|
669
669
|
try {
|
|
670
|
-
cleanupSandbox = await
|
|
670
|
+
cleanupSandbox = await index.initializeSandbox(opts.sandboxConfig, opts.path);
|
|
671
671
|
if (!spawnArgs.includes("--dangerously-skip-permissions")) {
|
|
672
672
|
spawnArgs = [...spawnArgs, "--dangerously-skip-permissions"];
|
|
673
673
|
}
|
|
@@ -675,7 +675,7 @@ async function claudeLocal(opts) {
|
|
|
675
675
|
"node",
|
|
676
676
|
...spawnArgs.map((arg) => quoteShellArg(arg))
|
|
677
677
|
].join(" ");
|
|
678
|
-
spawnCommand = await
|
|
678
|
+
spawnCommand = await index.wrapCommand(fullCommand);
|
|
679
679
|
spawnWithShell = true;
|
|
680
680
|
persistence.logger.info(
|
|
681
681
|
`[ClaudeLocal] Sandbox enabled: workspace=${opts.sandboxConfig.workspaceRoot ?? opts.path}, network=${opts.sandboxConfig.networkMode}`
|
|
@@ -738,7 +738,7 @@ async function claudeLocal(opts) {
|
|
|
738
738
|
persistence.logger.debug(`[ClaudeLocal] Unknown message type: ${message.type}`);
|
|
739
739
|
}
|
|
740
740
|
} catch (e) {
|
|
741
|
-
persistence.logger.debug("[ClaudeLocal] Non-JSON line from fd3:",
|
|
741
|
+
persistence.logger.debug("[ClaudeLocal] Non-JSON line from fd3:", index.contentLogMetadata(line));
|
|
742
742
|
}
|
|
743
743
|
});
|
|
744
744
|
rl.on("error", (err) => {
|
|
@@ -876,7 +876,7 @@ const INTERNAL_CLAUDE_EVENT_TYPES$1 = /* @__PURE__ */ new Set([
|
|
|
876
876
|
"queue-operation"
|
|
877
877
|
]);
|
|
878
878
|
async function createSessionScanner(opts) {
|
|
879
|
-
const projectDir =
|
|
879
|
+
const projectDir = index.getProjectPath(opts.workingDirectory);
|
|
880
880
|
let finishedSessions = /* @__PURE__ */ new Set();
|
|
881
881
|
let pendingSessions = /* @__PURE__ */ new Set();
|
|
882
882
|
let currentSessionId = null;
|
|
@@ -891,7 +891,7 @@ async function createSessionScanner(opts) {
|
|
|
891
891
|
}
|
|
892
892
|
currentSessionId = opts.sessionId;
|
|
893
893
|
}
|
|
894
|
-
const sync = new
|
|
894
|
+
const sync = new index.InvalidateSync(async () => {
|
|
895
895
|
let sessions = [];
|
|
896
896
|
for (let p of pendingSessions) {
|
|
897
897
|
if (!deadSessions.has(p)) {
|
|
@@ -1033,7 +1033,7 @@ async function readSessionLog(projectDir, sessionId) {
|
|
|
1033
1033
|
if (message.type && INTERNAL_CLAUDE_EVENT_TYPES$1.has(message.type)) {
|
|
1034
1034
|
continue;
|
|
1035
1035
|
}
|
|
1036
|
-
let parsed =
|
|
1036
|
+
let parsed = index.RawJSONLinesSchema.safeParse(message);
|
|
1037
1037
|
if (!parsed.success) {
|
|
1038
1038
|
continue;
|
|
1039
1039
|
}
|
|
@@ -1912,7 +1912,7 @@ async function awaitFileExist(file, timeout = 1e4) {
|
|
|
1912
1912
|
await fs.access(file);
|
|
1913
1913
|
return true;
|
|
1914
1914
|
} catch (e) {
|
|
1915
|
-
await
|
|
1915
|
+
await index.delay(1e3);
|
|
1916
1916
|
}
|
|
1917
1917
|
}
|
|
1918
1918
|
return false;
|
|
@@ -2107,7 +2107,7 @@ async function claudeRemote(opts) {
|
|
|
2107
2107
|
}
|
|
2108
2108
|
continue;
|
|
2109
2109
|
}
|
|
2110
|
-
persistence.logger.debug(`[claudeRemote] Message ${message.type}`,
|
|
2110
|
+
persistence.logger.debug(`[claudeRemote] Message ${message.type}`, index.contentLogMetadata(message));
|
|
2111
2111
|
if (message.type === "assistant" && message.error) {
|
|
2112
2112
|
lastAssistantError = message.error;
|
|
2113
2113
|
}
|
|
@@ -2147,7 +2147,7 @@ async function claudeRemote(opts) {
|
|
|
2147
2147
|
}
|
|
2148
2148
|
if (systemInit.session_id) {
|
|
2149
2149
|
persistence.logger.debug(`[claudeRemote] Waiting for session file to be written to disk: ${systemInit.session_id}`);
|
|
2150
|
-
const projectDir =
|
|
2150
|
+
const projectDir = index.getProjectPath(opts.path);
|
|
2151
2151
|
const found = await awaitFileExist(path.join(projectDir, `${systemInit.session_id}.jsonl`), 3e4);
|
|
2152
2152
|
persistence.logger.debug(`[claudeRemote] Session file found: ${systemInit.session_id} ${found}`);
|
|
2153
2153
|
if (!found) {
|
|
@@ -2378,9 +2378,9 @@ class PermissionHandler {
|
|
|
2378
2378
|
persistence.logger.debug("Plan mode result received", {
|
|
2379
2379
|
approved: response.approved,
|
|
2380
2380
|
mode: response.mode,
|
|
2381
|
-
reason:
|
|
2381
|
+
reason: index.contentLogMetadata(response.reason),
|
|
2382
2382
|
allowToolCount: response.allowTools?.length ?? 0,
|
|
2383
|
-
updatedInput:
|
|
2383
|
+
updatedInput: index.contentLogMetadata(response.updatedInput)
|
|
2384
2384
|
});
|
|
2385
2385
|
if (response.approved) {
|
|
2386
2386
|
const newMode = response.mode && ["default", "acceptEdits", "bypassPermissions"].includes(response.mode) ? response.mode : "bypassPermissions";
|
|
@@ -2704,9 +2704,9 @@ class PermissionHandler {
|
|
|
2704
2704
|
id: message.id,
|
|
2705
2705
|
approved: message.approved,
|
|
2706
2706
|
mode: message.mode,
|
|
2707
|
-
reason:
|
|
2707
|
+
reason: index.contentLogMetadata(message.reason),
|
|
2708
2708
|
allowToolCount: message.allowTools?.length ?? 0,
|
|
2709
|
-
updatedInput:
|
|
2709
|
+
updatedInput: index.contentLogMetadata(message.updatedInput)
|
|
2710
2710
|
});
|
|
2711
2711
|
const id = message.id;
|
|
2712
2712
|
const pending = this.pendingRequests.get(id);
|
|
@@ -3191,7 +3191,7 @@ class OutgoingMessageQueue {
|
|
|
3191
3191
|
}
|
|
3192
3192
|
queue = [];
|
|
3193
3193
|
nextId = 1;
|
|
3194
|
-
lock = new
|
|
3194
|
+
lock = new index.AsyncLock();
|
|
3195
3195
|
processTimer;
|
|
3196
3196
|
delayTimers = /* @__PURE__ */ new Map();
|
|
3197
3197
|
/**
|
|
@@ -3483,7 +3483,7 @@ class StreamRelay {
|
|
|
3483
3483
|
}
|
|
3484
3484
|
this.pending.text += text;
|
|
3485
3485
|
this.pending.bytes += Buffer.byteLength(text, "utf8");
|
|
3486
|
-
if (this.pending.bytes >= STREAM_FLUSH_MAX_BYTES || this.pending.text.length >=
|
|
3486
|
+
if (this.pending.bytes >= STREAM_FLUSH_MAX_BYTES || this.pending.text.length >= index.STREAM_DELTA_MAX_CHARS) {
|
|
3487
3487
|
this.flushPending();
|
|
3488
3488
|
return;
|
|
3489
3489
|
}
|
|
@@ -3663,8 +3663,8 @@ async function stageClaudeAttachments(attachments, options) {
|
|
|
3663
3663
|
await persistence.ensurePrivateDirectory(sessionDir);
|
|
3664
3664
|
const staged = [];
|
|
3665
3665
|
for (const attachment of attachments) {
|
|
3666
|
-
if (attachment.data.length >
|
|
3667
|
-
throw new Error(`Attachment exceeds the ${
|
|
3666
|
+
if (attachment.data.length > index.MAX_CHAT_ATTACHMENT_SOURCE_BYTES) {
|
|
3667
|
+
throw new Error(`Attachment exceeds the ${index.MAX_CHAT_ATTACHMENT_SOURCE_BYTES / 1024 / 1024} MB limit`);
|
|
3668
3668
|
}
|
|
3669
3669
|
const name = safeFileName(attachment.name || "file");
|
|
3670
3670
|
const targetPath = path.join(sessionDir, uniqueFileName(name));
|
|
@@ -4265,12 +4265,12 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4265
4265
|
claudeArgs: session.claudeArgs,
|
|
4266
4266
|
onMessage,
|
|
4267
4267
|
onCompletionEvent: (message) => {
|
|
4268
|
-
persistence.logger.debug("[remote]: Completion event received:",
|
|
4268
|
+
persistence.logger.debug("[remote]: Completion event received:", index.contentLogMetadata(message));
|
|
4269
4269
|
session.client.sendSessionEvent({ type: "message", message });
|
|
4270
4270
|
},
|
|
4271
4271
|
onAuthFailure: (reason) => {
|
|
4272
4272
|
session.client.sendSessionEvent({ type: "message", message: `Claude Code auth: ${reason}`, kind: "claude-auth-failed" });
|
|
4273
|
-
void
|
|
4273
|
+
void index.notifyDaemonClaudeAuthFailed(session.client.sessionId).catch((error) => persistence.logger.debug("[remote]: auth_failed report to daemon failed:", error));
|
|
4274
4274
|
},
|
|
4275
4275
|
onSessionReset: () => {
|
|
4276
4276
|
persistence.logger.debug("[remote]: Session reset");
|
|
@@ -5062,7 +5062,7 @@ function daemonReadiness(authenticated, running, hasState) {
|
|
|
5062
5062
|
message: "\u26A0\uFE0F Daemon is not running \u2014 run `very-happy daemon start`"
|
|
5063
5063
|
};
|
|
5064
5064
|
}
|
|
5065
|
-
function collectRuntimeReadiness(run = node_child_process.spawnSync, currentNodeVersion = process.version, onPath =
|
|
5065
|
+
function collectRuntimeReadiness(run = node_child_process.spawnSync, currentNodeVersion = process.version, onPath = index.commandOnPath) {
|
|
5066
5066
|
const tmux = probe("tmux", run);
|
|
5067
5067
|
return {
|
|
5068
5068
|
node: {
|
|
@@ -5111,7 +5111,7 @@ function shareSafeEnvironmentInfo(env = process.env, runtime = process) {
|
|
|
5111
5111
|
function printCliUpdateStatus(state) {
|
|
5112
5112
|
const installed = persistence.configuration.currentCliVersion;
|
|
5113
5113
|
const running = state?.startedWithCliVersion ?? null;
|
|
5114
|
-
const summary =
|
|
5114
|
+
const summary = index.deriveLocalCliUpdateSummary(installed, running, state?.cliUpdate);
|
|
5115
5115
|
console.log(` Installed CLI: ${installed}`);
|
|
5116
5116
|
console.log(` Running daemon: ${running ?? "not running"}`);
|
|
5117
5117
|
if (state?.cliUpdate?.recommendedVersion) console.log(` Recommended: ${state.cliUpdate.recommendedVersion}`);
|
|
@@ -5146,7 +5146,7 @@ function getLogFiles(logDir) {
|
|
|
5146
5146
|
async function runDoctorDaemon() {
|
|
5147
5147
|
console.log(chalk.bold("\n\u{1F916} Daemon Status"));
|
|
5148
5148
|
try {
|
|
5149
|
-
const isRunning = await
|
|
5149
|
+
const isRunning = await index.checkIfDaemonRunningAndCleanupStaleState();
|
|
5150
5150
|
const state = await persistence.readDaemonState();
|
|
5151
5151
|
if (isRunning && state) {
|
|
5152
5152
|
console.log(chalk.green("\u2713 Daemon is running"));
|
|
@@ -5316,6 +5316,16 @@ ${typeLabels[type] || type}:`));
|
|
|
5316
5316
|
console.log(chalk.gray(" OS-keychain credentials cannot be verified here; see /docs/configuration#claude-credentials."));
|
|
5317
5317
|
}
|
|
5318
5318
|
console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
|
|
5319
|
+
try {
|
|
5320
|
+
const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-CGl7FF7a.cjs'); });
|
|
5321
|
+
const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-Bqo68u61.cjs'); }).then(function (n) { return n.index; });
|
|
5322
|
+
const homes = await resolveAgentHomesForCli();
|
|
5323
|
+
console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
|
|
5324
|
+
console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));
|
|
5325
|
+
console.log(chalk.gray(" Resolved from settings.json, then your login shell, then the daemon environment; pin with `very-happy agent-home`."));
|
|
5326
|
+
} catch {
|
|
5327
|
+
console.log(chalk.yellow("\u25CB Could not resolve the Claude/Codex home directories"));
|
|
5328
|
+
}
|
|
5319
5329
|
if (availableAgents.length === 0) {
|
|
5320
5330
|
console.log(chalk.yellow("\u25CB No external agent command found on this daemon PATH"));
|
|
5321
5331
|
console.log(chalk.gray(" Codex, Gemini, OpenCode, OpenClaw, and native Claude terminal paths need their local command or gateway."));
|
|
@@ -5349,7 +5359,7 @@ ${typeLabels[type] || type}:`));
|
|
|
5349
5359
|
}
|
|
5350
5360
|
console.log(chalk.bold("\n\u{1F916} Daemon Status"));
|
|
5351
5361
|
try {
|
|
5352
|
-
const isRunning = await
|
|
5362
|
+
const isRunning = await index.checkIfDaemonRunningAndCleanupStaleState();
|
|
5353
5363
|
const state = await persistence.readDaemonState();
|
|
5354
5364
|
const summary = daemonReadiness(authenticated, isRunning, Boolean(state));
|
|
5355
5365
|
if (summary.level === "ready" && state) {
|
|
@@ -5419,7 +5429,7 @@ async function registryVersion(name) {
|
|
|
5419
5429
|
return typeof data.version === "string" ? stableVersion(data.version) : null;
|
|
5420
5430
|
}
|
|
5421
5431
|
async function detectAgentVersions(deps = { probe: probeAgentVersion, registryVersion }, now = Date.now()) {
|
|
5422
|
-
const agents = await Promise.all(
|
|
5432
|
+
const agents = await Promise.all(index.AGENT_VERSION_SOURCES.map(async (source) => {
|
|
5423
5433
|
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 };
|
|
5424
5434
|
if (local.missing) return { id: source.id, installed: null, latest: null, status: "not-installed" };
|
|
5425
5435
|
const latest = await deps.registryVersion(source.package).catch(() => null);
|
|
@@ -5678,7 +5688,7 @@ async function waitForAuthentication(keypair, claimSecret) {
|
|
|
5678
5688
|
}
|
|
5679
5689
|
process.stdout.write("\rWaiting for authentication" + ".".repeat(dots % 3 + 1) + " ");
|
|
5680
5690
|
dots++;
|
|
5681
|
-
await
|
|
5691
|
+
await index.delay(1e3);
|
|
5682
5692
|
}
|
|
5683
5693
|
} finally {
|
|
5684
5694
|
process.off("SIGINT", handleInterrupt);
|
|
@@ -6003,7 +6013,7 @@ async function waitForSessionKey(sessionId, timeoutMs) {
|
|
|
6003
6013
|
`Session ${sessionId} has no encryption key in ${persistence.configuration.sessionsFile}. Either it was not spawned by this machine's daemon, or the daemon is too old to persist session keys.`
|
|
6004
6014
|
);
|
|
6005
6015
|
}
|
|
6006
|
-
await
|
|
6016
|
+
await index.delay(200);
|
|
6007
6017
|
}
|
|
6008
6018
|
}
|
|
6009
6019
|
async function sendUserMessage(sessionId, persisted, text, client, options = {}) {
|
|
@@ -6065,7 +6075,7 @@ function resumePrecheck(metadata, fs) {
|
|
|
6065
6075
|
if (!metadata.codexThreadId) return { ok: false, reason: "no-backend-id", detail: "session has no Codex thread id" };
|
|
6066
6076
|
return { ok: true };
|
|
6067
6077
|
}
|
|
6068
|
-
if (!
|
|
6078
|
+
if (!index.isClaudeSessionId(metadata.claudeSessionId)) {
|
|
6069
6079
|
return { ok: false, reason: "no-backend-id", detail: "session has no Claude session id" };
|
|
6070
6080
|
}
|
|
6071
6081
|
if (!fs.conversationExists(metadata.path, metadata.claudeSessionId)) {
|
|
@@ -7377,8 +7387,8 @@ function decryptBoxBundle(bundle, recipientSecretKey) {
|
|
|
7377
7387
|
return decrypted ? new Uint8Array(decrypted) : null;
|
|
7378
7388
|
}
|
|
7379
7389
|
function readAgentCredentials() {
|
|
7380
|
-
const credentialPath =
|
|
7381
|
-
const credentials =
|
|
7390
|
+
const credentialPath = index.getLocalHappyAgentCredentialPath();
|
|
7391
|
+
const credentials = index.readLocalHappyAgentCredentials();
|
|
7382
7392
|
if (!credentials) {
|
|
7383
7393
|
throw new Error(
|
|
7384
7394
|
`Cannot use the legacy CLI resume path without ${credentialPath}. This credential is produced only by the source-only internal control-plane prototype; do not install the unrelated npm package named happy-agent. Use the Web session controls instead.`
|
|
@@ -7678,7 +7688,7 @@ function ownedCliLink(link, target, packageDir) {
|
|
|
7678
7688
|
return resolved.startsWith(path.join(packageDir, "bin") + path.sep);
|
|
7679
7689
|
}
|
|
7680
7690
|
async function installCliSafely(version, deps = defaults) {
|
|
7681
|
-
const parsed =
|
|
7691
|
+
const parsed = index.parseExactVersion(version);
|
|
7682
7692
|
if (!parsed) throw new Error("Invalid exact CLI version");
|
|
7683
7693
|
const args = autoUpdateInstallArgs(parsed.exact);
|
|
7684
7694
|
if (deps.platform === "win32") {
|
|
@@ -7769,7 +7779,7 @@ function createUpdateController(deps) {
|
|
|
7769
7779
|
let lastPublished = "";
|
|
7770
7780
|
const now = deps.now ?? Date.now;
|
|
7771
7781
|
function validManualTarget(next, version) {
|
|
7772
|
-
return version === next.recommendedVersion && Number.isFinite(next.checkedAt) && now() - next.checkedAt <= 65 * 6e4 && next.checkedAt <= now() + 6e4 &&
|
|
7782
|
+
return version === next.recommendedVersion && Number.isFinite(next.checkedAt) && now() - next.checkedAt <= 65 * 6e4 && next.checkedAt <= now() + 6e4 && index.compareExactVersions(next.currentVersion, version) === -1 && (!next.minimumVersion || [0, 1].includes(index.compareExactVersions(version, next.minimumVersion) ?? -1));
|
|
7773
7783
|
}
|
|
7774
7784
|
function publish(state, version, detail) {
|
|
7775
7785
|
if (!policy) return;
|
|
@@ -7807,7 +7817,7 @@ function createUpdateController(deps) {
|
|
|
7807
7817
|
publish("unapproved", null);
|
|
7808
7818
|
return;
|
|
7809
7819
|
}
|
|
7810
|
-
const order =
|
|
7820
|
+
const order = index.compareExactVersions(policy.currentVersion, target);
|
|
7811
7821
|
if (order !== -1) {
|
|
7812
7822
|
publish(order === null ? "policy_stale" : "current", target);
|
|
7813
7823
|
return;
|
|
@@ -7988,7 +7998,7 @@ function parseJsonObject(text) {
|
|
|
7988
7998
|
return null;
|
|
7989
7999
|
}
|
|
7990
8000
|
}
|
|
7991
|
-
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-
|
|
8001
|
+
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-DhwtFJEe.cjs', document.baseURI).href))).resolve(s)) {
|
|
7992
8002
|
const platformArch = `${platform}-${arch}`;
|
|
7993
8003
|
const candidates = [
|
|
7994
8004
|
`@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
|
|
@@ -8378,7 +8388,7 @@ function parseLines(lines) {
|
|
|
8378
8388
|
try {
|
|
8379
8389
|
const parsed = JSON.parse(line);
|
|
8380
8390
|
if (parsed?.type && INTERNAL_CLAUDE_EVENT_TYPES.has(parsed.type)) continue;
|
|
8381
|
-
const result =
|
|
8391
|
+
const result = index.RawJSONLinesSchema.safeParse(parsed);
|
|
8382
8392
|
if (!result.success) continue;
|
|
8383
8393
|
out.push(result.data);
|
|
8384
8394
|
} catch {
|
|
@@ -8422,19 +8432,19 @@ function createMirrorScanner(opts) {
|
|
|
8422
8432
|
} finally {
|
|
8423
8433
|
await handle.close();
|
|
8424
8434
|
}
|
|
8425
|
-
const { lines, consumedBytes } =
|
|
8435
|
+
const { lines, consumedBytes } = index.extractCompleteLines(buf);
|
|
8426
8436
|
tail.offset += consumedBytes;
|
|
8427
8437
|
if (lines.length === 0) return;
|
|
8428
8438
|
let messages = parseLines(lines);
|
|
8429
8439
|
if (isFirstBackfillRead) {
|
|
8430
|
-
const decision =
|
|
8440
|
+
const decision = index.decideBackfill(messages, opts.backfillLines);
|
|
8431
8441
|
messages = decision.replay;
|
|
8432
8442
|
if (decision.truncated) opts.events.onBackfillTruncated?.();
|
|
8433
8443
|
tail.mode = "from-eof";
|
|
8434
8444
|
}
|
|
8435
8445
|
if (messages.length > 0) opts.events.onMessages(messages);
|
|
8436
8446
|
};
|
|
8437
|
-
const sync = new
|
|
8447
|
+
const sync = new index.InvalidateSync(async () => {
|
|
8438
8448
|
for (const tail of files.values()) {
|
|
8439
8449
|
try {
|
|
8440
8450
|
await readTail(tail);
|
|
@@ -8518,7 +8528,7 @@ function pendingCreateSupersededBy(pending, event) {
|
|
|
8518
8528
|
const TERMINAL_MIRROR_FLAVOR = "terminal-mirror";
|
|
8519
8529
|
const RECONCILE_HYSTERESIS_MS = 2e4;
|
|
8520
8530
|
function createMirrorManager(deps) {
|
|
8521
|
-
const backfillLines = deps.backfillLines ??
|
|
8531
|
+
const backfillLines = deps.backfillLines ?? index.MIRROR_BACKFILL_LINES_DEFAULT;
|
|
8522
8532
|
const bindings = /* @__PURE__ */ new Map();
|
|
8523
8533
|
const lastEndedAt = /* @__PURE__ */ new Map();
|
|
8524
8534
|
const pendingCreates = /* @__PURE__ */ new Map();
|
|
@@ -8533,7 +8543,7 @@ function createMirrorManager(deps) {
|
|
|
8533
8543
|
};
|
|
8534
8544
|
const isTerminalAlive = deps.isTerminalAlive ?? ((terminalId) => {
|
|
8535
8545
|
try {
|
|
8536
|
-
return node_child_process.spawnSync("tmux",
|
|
8546
|
+
return node_child_process.spawnSync("tmux", index.tmuxArgs(["has-session", "-t", `=vh-${terminalId}:`]), { stdio: "ignore", timeout: 3e3, env: index.scrubTmuxClientEnv({ ...process.env }) }).status === 0;
|
|
8537
8547
|
} catch {
|
|
8538
8548
|
return false;
|
|
8539
8549
|
}
|
|
@@ -8541,7 +8551,7 @@ function createMirrorManager(deps) {
|
|
|
8541
8551
|
const transcriptPathFor = (event) => {
|
|
8542
8552
|
if (event.transcriptPath) return event.transcriptPath;
|
|
8543
8553
|
if (!event.cwd) return null;
|
|
8544
|
-
return path.join(
|
|
8554
|
+
return path.join(index.getProjectPath(event.cwd), `${event.claudeSessionId}.jsonl`);
|
|
8545
8555
|
};
|
|
8546
8556
|
const persistBinding = (binding) => {
|
|
8547
8557
|
persistence.persistSession(binding.happySessionId, {
|
|
@@ -8557,8 +8567,8 @@ function createMirrorManager(deps) {
|
|
|
8557
8567
|
};
|
|
8558
8568
|
const sendMessages = (binding, messages) => {
|
|
8559
8569
|
for (const message of messages) {
|
|
8560
|
-
const key =
|
|
8561
|
-
binding.client.sendClaudeSessionMessage(message, key ? { localIdFor: (envelopeIndex) =>
|
|
8570
|
+
const key = index.mirrorLineKey(message);
|
|
8571
|
+
binding.client.sendClaudeSessionMessage(message, key ? { localIdFor: (envelopeIndex) => index.mirrorLocalId(key, envelopeIndex) } : void 0);
|
|
8562
8572
|
}
|
|
8563
8573
|
};
|
|
8564
8574
|
const attachScanner = (binding, opts) => {
|
|
@@ -8662,7 +8672,7 @@ function createMirrorManager(deps) {
|
|
|
8662
8672
|
archiveReason: void 0
|
|
8663
8673
|
});
|
|
8664
8674
|
const scanner = attachScanner(binding, { withTruncationNotice: false });
|
|
8665
|
-
scanner.addFile(path.join(
|
|
8675
|
+
scanner.addFile(path.join(index.getProjectPath(meta.path), `${meta.claudeSessionId}.jsonl`), "backfill-tail");
|
|
8666
8676
|
persistence.logger.debug(`[MIRROR] reconcile adopted terminal ${terminalId} \u2192 ${sessionId}`);
|
|
8667
8677
|
deps.onBindingsChanged?.();
|
|
8668
8678
|
};
|
|
@@ -8677,7 +8687,7 @@ function createMirrorManager(deps) {
|
|
|
8677
8687
|
archiveReason: void 0
|
|
8678
8688
|
});
|
|
8679
8689
|
const scanner = binding.scanner ?? attachScanner(binding, { withTruncationNotice: false });
|
|
8680
|
-
scanner.addFile(path.join(
|
|
8690
|
+
scanner.addFile(path.join(index.getProjectPath(binding.metadata.path), `${binding.claudeSessionId}.jsonl`), "backfill-tail");
|
|
8681
8691
|
persistence.logger.debug(`[MIRROR] reconcile reactivated (in place) terminal ${binding.terminalId}`);
|
|
8682
8692
|
deps.onBindingsChanged?.();
|
|
8683
8693
|
};
|
|
@@ -8780,7 +8790,7 @@ function createMirrorManager(deps) {
|
|
|
8780
8790
|
const parked = pendingCreates.get(event.terminalId);
|
|
8781
8791
|
if (parked && pendingCreateSupersededBy(parked, event)) pendingCreates.delete(event.terminalId);
|
|
8782
8792
|
const binding = bindings.get(event.terminalId) ?? null;
|
|
8783
|
-
const decision =
|
|
8793
|
+
const decision = index.decideMirrorBinding(
|
|
8784
8794
|
event,
|
|
8785
8795
|
binding ? { status: binding.status, claudeSessionId: binding.claudeSessionId } : null
|
|
8786
8796
|
);
|
|
@@ -8801,7 +8811,7 @@ function createMirrorManager(deps) {
|
|
|
8801
8811
|
};
|
|
8802
8812
|
return {
|
|
8803
8813
|
handleHookPayload(body) {
|
|
8804
|
-
const event =
|
|
8814
|
+
const event = index.parseTerminalHookPayload(body);
|
|
8805
8815
|
if (!event) {
|
|
8806
8816
|
persistence.logger.debug("[MIRROR] unparseable /terminal-hook payload dropped");
|
|
8807
8817
|
return;
|
|
@@ -8929,7 +8939,7 @@ function createMirrorManager(deps) {
|
|
|
8929
8939
|
};
|
|
8930
8940
|
bindings.set(terminalId, binding);
|
|
8931
8941
|
const scanner = attachScanner(binding, { withTruncationNotice: false });
|
|
8932
|
-
scanner.addFile(path.join(
|
|
8942
|
+
scanner.addFile(path.join(index.getProjectPath(meta.path), `${meta.claudeSessionId}.jsonl`), "backfill-tail");
|
|
8933
8943
|
persistence.logger.debug(`[MIRROR] restored binding terminal ${terminalId} \u2192 ${sessionId}`);
|
|
8934
8944
|
deps.onBindingsChanged?.();
|
|
8935
8945
|
}
|
|
@@ -9608,7 +9618,7 @@ function toSummary(id, persisted, extra) {
|
|
|
9608
9618
|
};
|
|
9609
9619
|
}
|
|
9610
9620
|
async function listSessions(options = {}) {
|
|
9611
|
-
const live = await
|
|
9621
|
+
const live = await index.listDaemonSessions();
|
|
9612
9622
|
return mergeSessionSummaries(live, persistence.readPersistedSessions(), options);
|
|
9613
9623
|
}
|
|
9614
9624
|
function summarizeAccountSession(row, persisted, liveIds, now) {
|
|
@@ -9678,7 +9688,7 @@ async function listAccountSessions(options = {}) {
|
|
|
9678
9688
|
});
|
|
9679
9689
|
const rows = Array.isArray(response.data?.sessions) ? response.data.sessions : [];
|
|
9680
9690
|
const persisted = persistence.readPersistedSessions();
|
|
9681
|
-
const live = await
|
|
9691
|
+
const live = await index.listDaemonSessions();
|
|
9682
9692
|
const liveIds = new Set(live.map((child) => child.happySessionId).filter((id) => typeof id === "string"));
|
|
9683
9693
|
const now = Date.now();
|
|
9684
9694
|
const summaries = rows.filter((row) => typeof row?.id === "string").map((row) => summarizeAccountSession(row, persisted[row.id], liveIds, now)).filter((summary) => options.includeArchived || !summary.archived);
|
|
@@ -9712,7 +9722,7 @@ async function readSessionTranscript(sessionId, limit) {
|
|
|
9712
9722
|
const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
|
|
9713
9723
|
messages.reverse();
|
|
9714
9724
|
const key = persistence.decodeBase64(persisted.encryptionKey);
|
|
9715
|
-
const live = await
|
|
9725
|
+
const live = await index.listDaemonSessions();
|
|
9716
9726
|
const bodies = messages.map((message) => {
|
|
9717
9727
|
if (message.content?.t !== "encrypted") return null;
|
|
9718
9728
|
try {
|
|
@@ -9728,7 +9738,7 @@ async function readSessionTranscript(sessionId, limit) {
|
|
|
9728
9738
|
};
|
|
9729
9739
|
}
|
|
9730
9740
|
async function stopSession(sessionId) {
|
|
9731
|
-
return await
|
|
9741
|
+
return await index.stopDaemonSession(sessionId);
|
|
9732
9742
|
}
|
|
9733
9743
|
async function archiveSession(sessionId) {
|
|
9734
9744
|
const token = await bearerToken();
|
|
@@ -10050,7 +10060,7 @@ function sanitizeImportTitle(value) {
|
|
|
10050
10060
|
return collapsed.length > 200 ? `${collapsed.slice(0, 199)}\u2026` : collapsed;
|
|
10051
10061
|
}
|
|
10052
10062
|
const hostSuffix = process.env.HAPPY_VARIANT === "dev" ? "-dev" : "";
|
|
10053
|
-
const startupCliAvailability =
|
|
10063
|
+
const startupCliAvailability = index.detectCLIAvailability();
|
|
10054
10064
|
const initialMachineMetadata = {
|
|
10055
10065
|
teamsVersion: 1,
|
|
10056
10066
|
teamLaunchVersion: 1,
|
|
@@ -10061,7 +10071,7 @@ const initialMachineMetadata = {
|
|
|
10061
10071
|
happyHomeDir: persistence.configuration.happyHomeDir,
|
|
10062
10072
|
happyLibDir: projectPath.projectPath(),
|
|
10063
10073
|
cliAvailability: startupCliAvailability,
|
|
10064
|
-
resumeSupport: { ...
|
|
10074
|
+
resumeSupport: { ...index.detectResumeSupport(), rpcAvailable: true }
|
|
10065
10075
|
};
|
|
10066
10076
|
async function preflightNewBundle(bundlePath) {
|
|
10067
10077
|
const home = fs$1.mkdtempSync(path$1.join(os.tmpdir(), "vh-preflight-"));
|
|
@@ -10116,6 +10126,12 @@ async function startDaemon() {
|
|
|
10116
10126
|
let cliUpdateStateRef = null;
|
|
10117
10127
|
let lastHandoverHold = null;
|
|
10118
10128
|
let claudeCredentialStoreSetting = (await persistence.readSettings()).claudeCredentialStore === "file" ? "file" : "auto";
|
|
10129
|
+
try {
|
|
10130
|
+
const homes = await index.refreshAgentHomes();
|
|
10131
|
+
persistence.logger.debug(`[DAEMON RUN] Claude config dir: ${index.describeAgentHome(homes.claudeConfigDir)}; Codex home: ${index.describeAgentHome(homes.codexHome)}`);
|
|
10132
|
+
} catch (error) {
|
|
10133
|
+
persistence.logger.debug("[DAEMON RUN] agent home resolution failed; using the daemon environment as-is", error);
|
|
10134
|
+
}
|
|
10119
10135
|
let requestShutdown;
|
|
10120
10136
|
let resolvesWhenShutdownRequested = new Promise((resolve) => {
|
|
10121
10137
|
requestShutdown = (source, errorMessage) => {
|
|
@@ -10160,7 +10176,7 @@ async function startDaemon() {
|
|
|
10160
10176
|
});
|
|
10161
10177
|
persistence.logger.debug("[DAEMON RUN] Starting daemon process...");
|
|
10162
10178
|
persistence.logger.debugLargeJson("[DAEMON RUN] Environment", getEnvironmentInfo());
|
|
10163
|
-
const runningDaemonVersionMatches = await
|
|
10179
|
+
const runningDaemonVersionMatches = await index.isDaemonRunningCurrentlyInstalledHappyVersion();
|
|
10164
10180
|
const runningDaemonState = await persistence.readDaemonState();
|
|
10165
10181
|
const runningDaemonEndpointsMatch = Boolean(runningDaemonState) && daemonEndpointsMatch(
|
|
10166
10182
|
runningDaemonState?.serverUrl,
|
|
@@ -10170,7 +10186,7 @@ async function startDaemon() {
|
|
|
10170
10186
|
);
|
|
10171
10187
|
if (!runningDaemonVersionMatches || !runningDaemonEndpointsMatch) {
|
|
10172
10188
|
persistence.logger.debug("[DAEMON RUN] Daemon version or endpoint mismatch detected, restarting daemon");
|
|
10173
|
-
await
|
|
10189
|
+
await index.stopDaemon();
|
|
10174
10190
|
} else {
|
|
10175
10191
|
persistence.logger.debug("[DAEMON RUN] Daemon version matches, keeping existing daemon");
|
|
10176
10192
|
console.log("Daemon already running with matching version");
|
|
@@ -10189,7 +10205,7 @@ async function startDaemon() {
|
|
|
10189
10205
|
const { credentials, machineId } = await authAndSetupMachineIfNeeded();
|
|
10190
10206
|
persistence.logger.debug("[DAEMON RUN] Auth and machine setup complete");
|
|
10191
10207
|
const pidToTrackedSession = /* @__PURE__ */ new Map();
|
|
10192
|
-
const turnActivity = new
|
|
10208
|
+
const turnActivity = new index.TurnActivityTracker();
|
|
10193
10209
|
const sessionIdToFinishedSession = /* @__PURE__ */ new Map();
|
|
10194
10210
|
let teamWorker;
|
|
10195
10211
|
const persisted = persistence.readPersistedSessions();
|
|
@@ -10281,7 +10297,8 @@ async function startDaemon() {
|
|
|
10281
10297
|
}
|
|
10282
10298
|
};
|
|
10283
10299
|
const assistantSpawnGate = createSpawnGate();
|
|
10284
|
-
const spawnSession = (options) => {
|
|
10300
|
+
const spawnSession = async (options) => {
|
|
10301
|
+
await index.refreshAgentHomes();
|
|
10285
10302
|
if (assistantSpawnMode(options) !== "claude-singleton") {
|
|
10286
10303
|
return spawnSessionImpl(options);
|
|
10287
10304
|
}
|
|
@@ -10289,7 +10306,7 @@ async function startDaemon() {
|
|
|
10289
10306
|
};
|
|
10290
10307
|
const currentCliAvailability = () => apiMachineRef?.getCLIAvailability() ?? startupCliAvailability;
|
|
10291
10308
|
const spawnSessionImpl = async (options) => {
|
|
10292
|
-
persistence.logger.debugLargeJson("[DAEMON RUN] Spawning session",
|
|
10309
|
+
persistence.logger.debugLargeJson("[DAEMON RUN] Spawning session", index.summarizeSpawnSessionForLog(options));
|
|
10293
10310
|
const spawnModel = options.agent === "pi" ? sanitizeSpawnModel(options.model) : null;
|
|
10294
10311
|
const spawnPermissionMode = sanitizeSpawnPermissionMode(options.permissionMode);
|
|
10295
10312
|
if (options.permissionMode !== void 0 && spawnPermissionMode === null) {
|
|
@@ -10354,7 +10371,7 @@ async function startDaemon() {
|
|
|
10354
10371
|
s.encryptionVariant
|
|
10355
10372
|
);
|
|
10356
10373
|
const claudeSessionId = resolveAssistantClaudeSessionId(s.metadata, serverMetadata);
|
|
10357
|
-
const canResumeClaude = !!claudeSessionId &&
|
|
10374
|
+
const canResumeClaude = !!claudeSessionId && index.locateClaudeConversation(assistantHome(), claudeSessionId) !== null;
|
|
10358
10375
|
return spawnTrackedHappyProcess({
|
|
10359
10376
|
args: [
|
|
10360
10377
|
"claude",
|
|
@@ -10373,6 +10390,8 @@ async function startDaemon() {
|
|
|
10373
10390
|
// B-297: see the resume/restart paths — every spawn carries the
|
|
10374
10391
|
// credentialStore=file shim, not just fresh sessions (B-276 D8).
|
|
10375
10392
|
...claudeAuthServiceRef?.claudeProcessEnvOverrides() ?? {},
|
|
10393
|
+
// B-478: point Claude at the directory the transcript was found in.
|
|
10394
|
+
...canResumeClaude ? index.agentHomeSpawnEnv({ workingDirectory: assistantHome(), claudeSessionId }) : {},
|
|
10376
10395
|
HAPPY_SESSION_VARIANT: "assistant",
|
|
10377
10396
|
HAPPY_RECONNECT_SESSION_ID: assistantSessionId,
|
|
10378
10397
|
HAPPY_RECONNECT_ENCRYPTION_KEY: s.encryptionKey,
|
|
@@ -10516,9 +10535,9 @@ async function startDaemon() {
|
|
|
10516
10535
|
};
|
|
10517
10536
|
}
|
|
10518
10537
|
const resumeId = agent === "claude" ? options.resumeClaudeSessionId : agent === "codex" ? options.resumeCodexThreadId : void 0;
|
|
10519
|
-
const resumeFragment = resumeId ? ` --resume ${
|
|
10520
|
-
const permissionModeFragment = spawnPermissionMode ? ` --permission-mode ${
|
|
10521
|
-
const modelFragment = spawnModel ? ` --model ${
|
|
10538
|
+
const resumeFragment = resumeId ? ` --resume ${index.shellescape(resumeId)}` : "";
|
|
10539
|
+
const permissionModeFragment = spawnPermissionMode ? ` --permission-mode ${index.shellescape(spawnPermissionMode)}` : "";
|
|
10540
|
+
const modelFragment = spawnModel ? ` --model ${index.shellescape(spawnModel)}` : "";
|
|
10522
10541
|
const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${agent} --happy-starting-mode remote --started-by daemon${resumeFragment}${permissionModeFragment}${modelFragment}`;
|
|
10523
10542
|
const windowName = `happy-${Date.now()}-${agent}`;
|
|
10524
10543
|
const tmuxEnv = {};
|
|
@@ -10527,6 +10546,7 @@ async function startDaemon() {
|
|
|
10527
10546
|
tmuxEnv[key] = value;
|
|
10528
10547
|
}
|
|
10529
10548
|
}
|
|
10549
|
+
Object.assign(tmuxEnv, index.agentHomeSpawnEnv({ workingDirectory: directory, claudeSessionId: options.resumeClaudeSessionId, codexThreadId: options.resumeCodexThreadId }));
|
|
10530
10550
|
Object.assign(tmuxEnv, extraEnv);
|
|
10531
10551
|
const tmuxResult = await tmux.spawnInTmux([fullCommand], {
|
|
10532
10552
|
sessionName: tmuxSessionName,
|
|
@@ -10621,6 +10641,9 @@ async function startDaemon() {
|
|
|
10621
10641
|
cwd: directory,
|
|
10622
10642
|
env: {
|
|
10623
10643
|
...process.env,
|
|
10644
|
+
// B-478: a fork/duplicate resumes a transcript that may live in
|
|
10645
|
+
// a moved config dir. Explicit environmentVariables still win.
|
|
10646
|
+
...index.agentHomeSpawnEnv({ workingDirectory: directory, claudeSessionId: options.resumeClaudeSessionId, codexThreadId: options.resumeCodexThreadId }),
|
|
10624
10647
|
...extraEnv
|
|
10625
10648
|
},
|
|
10626
10649
|
directoryCreated,
|
|
@@ -10771,6 +10794,7 @@ async function startDaemon() {
|
|
|
10771
10794
|
gate = createSpawnGate();
|
|
10772
10795
|
resumeGates.set(happySessionId, gate);
|
|
10773
10796
|
}
|
|
10797
|
+
await index.refreshAgentHomes();
|
|
10774
10798
|
try {
|
|
10775
10799
|
return await gate.join(() => resumeSessionImpl(happySessionId, options));
|
|
10776
10800
|
} finally {
|
|
@@ -10817,7 +10841,9 @@ async function startDaemon() {
|
|
|
10817
10841
|
}
|
|
10818
10842
|
const precheck = resumePrecheck(metadata, {
|
|
10819
10843
|
cwdExists: (p) => fs$1.existsSync(p),
|
|
10820
|
-
|
|
10844
|
+
// B-478: also looks in every other Claude config dir the user's shell
|
|
10845
|
+
// or settings point at, not just the one the daemon was started with.
|
|
10846
|
+
conversationExists: (cwd, claudeSessionId) => index.locateClaudeConversation(cwd, claudeSessionId) !== null
|
|
10821
10847
|
});
|
|
10822
10848
|
let freshConversation = false;
|
|
10823
10849
|
if (!precheck.ok) {
|
|
@@ -10848,6 +10874,9 @@ async function startDaemon() {
|
|
|
10848
10874
|
// fell back to `auto` reads a different credential store than the
|
|
10849
10875
|
// fresh one next to it (B-276 D8).
|
|
10850
10876
|
...claudeAuthServiceRef?.claudeProcessEnvOverrides() ?? {},
|
|
10877
|
+
// B-478: if the transcript lives in a Claude/Codex home other than
|
|
10878
|
+
// the resolved one, this spawn is pointed there.
|
|
10879
|
+
...freshConversation ? {} : index.agentHomeSpawnEnv({ workingDirectory: launch.cwd, claudeSessionId: metadata.claudeSessionId, codexThreadId: metadata.codexThreadId }),
|
|
10851
10880
|
HAPPY_RECONNECT_SESSION_ID: happySessionId,
|
|
10852
10881
|
HAPPY_RECONNECT_ENCRYPTION_KEY: persistence.encodeBase64(tracked.encryption.encryptionKey),
|
|
10853
10882
|
HAPPY_RECONNECT_ENCRYPTION_VARIANT: tracked.encryption.encryptionVariant,
|
|
@@ -10909,6 +10938,7 @@ async function startDaemon() {
|
|
|
10909
10938
|
gate = createSpawnGate();
|
|
10910
10939
|
resumeGates.set(happySessionId, gate);
|
|
10911
10940
|
}
|
|
10941
|
+
await index.refreshAgentHomes();
|
|
10912
10942
|
try {
|
|
10913
10943
|
return await gate.replace(() => restartSessionImpl(happySessionId, options));
|
|
10914
10944
|
} finally {
|
|
@@ -10944,7 +10974,9 @@ async function startDaemon() {
|
|
|
10944
10974
|
}
|
|
10945
10975
|
const precheck = resumePrecheck(metadata, {
|
|
10946
10976
|
cwdExists: (p) => fs$1.existsSync(p),
|
|
10947
|
-
|
|
10977
|
+
// B-478: also looks in every other Claude config dir the user's shell
|
|
10978
|
+
// or settings point at, not just the one the daemon was started with.
|
|
10979
|
+
conversationExists: (cwd2, claudeSessionId) => index.locateClaudeConversation(cwd2, claudeSessionId) !== null
|
|
10948
10980
|
});
|
|
10949
10981
|
let args;
|
|
10950
10982
|
let cwd;
|
|
@@ -10979,6 +11011,8 @@ async function startDaemon() {
|
|
|
10979
11011
|
// fell back to `auto` reads a different credential store than the
|
|
10980
11012
|
// fresh one next to it (B-276 D8).
|
|
10981
11013
|
...claudeAuthServiceRef?.claudeProcessEnvOverrides() ?? {},
|
|
11014
|
+
// B-478: see resumeSession.
|
|
11015
|
+
...precheck.ok ? index.agentHomeSpawnEnv({ workingDirectory: cwd, claudeSessionId: metadata.claudeSessionId, codexThreadId: metadata.codexThreadId }) : {},
|
|
10982
11016
|
HAPPY_RECONNECT_SESSION_ID: happySessionId,
|
|
10983
11017
|
HAPPY_RECONNECT_ENCRYPTION_KEY: persistence.encodeBase64(tracked.encryption.encryptionKey),
|
|
10984
11018
|
HAPPY_RECONNECT_ENCRYPTION_VARIANT: tracked.encryption.encryptionVariant,
|
|
@@ -11156,8 +11190,8 @@ async function startDaemon() {
|
|
|
11156
11190
|
httpPort: controlPort,
|
|
11157
11191
|
startedAt: Date.now()
|
|
11158
11192
|
};
|
|
11159
|
-
const api
|
|
11160
|
-
const machine = await api
|
|
11193
|
+
const api = await index.ApiClient.create(credentials);
|
|
11194
|
+
const machine = await api.getOrCreateMachine({
|
|
11161
11195
|
machineId,
|
|
11162
11196
|
metadata: initialMachineMetadata,
|
|
11163
11197
|
daemonState: initialDaemonState
|
|
@@ -11171,10 +11205,10 @@ async function startDaemon() {
|
|
|
11171
11205
|
if (identityConflict) {
|
|
11172
11206
|
persistence.logger.warn(`[DAEMON RUN] machine identity conflict (${identityConflict.confidence}): ${describeMachineIdentityConflict(identityConflict)}`);
|
|
11173
11207
|
}
|
|
11174
|
-
const apiMachine = api
|
|
11208
|
+
const apiMachine = api.machineSyncClient(machine);
|
|
11175
11209
|
apiMachineRef = apiMachine;
|
|
11176
11210
|
const mirrorManager = createMirrorManager({
|
|
11177
|
-
api
|
|
11211
|
+
api,
|
|
11178
11212
|
machineId,
|
|
11179
11213
|
onBindingsChanged: () => apiMachine.requestTerminalListRefresh()
|
|
11180
11214
|
});
|
|
@@ -11225,7 +11259,7 @@ async function startDaemon() {
|
|
|
11225
11259
|
}
|
|
11226
11260
|
let lastUpdateLog = "";
|
|
11227
11261
|
const updateController = createUpdateController({
|
|
11228
|
-
policy: () =>
|
|
11262
|
+
policy: () => index.fetchCliUpdateState(persistence.configuration.serverUrl, persistence.packageJson.version),
|
|
11229
11263
|
enabled: async () => ((await persistence.readSettings()).cliAutoUpdate ?? "idle") !== "off",
|
|
11230
11264
|
// B-466: "idle" = no agent turn in flight. Idle wrappers survive a
|
|
11231
11265
|
// handover (they talk to the server themselves) and web terminals live
|
|
@@ -11252,9 +11286,9 @@ async function startDaemon() {
|
|
|
11252
11286
|
persistence.logger.debug("[DAEMON RUN] CLI update policy check failed:", error);
|
|
11253
11287
|
});
|
|
11254
11288
|
void refreshCliUpdate();
|
|
11255
|
-
const cliUpdateIntervalMs =
|
|
11256
|
-
if (process.env.HAPPY_CLI_UPDATE_CHECK_INTERVAL && cliUpdateIntervalMs ===
|
|
11257
|
-
persistence.logger.warn(`[DAEMON RUN] Ignoring invalid HAPPY_CLI_UPDATE_CHECK_INTERVAL; using ${
|
|
11289
|
+
const cliUpdateIntervalMs = index.resolveCliUpdateCheckInterval(process.env.HAPPY_CLI_UPDATE_CHECK_INTERVAL);
|
|
11290
|
+
if (process.env.HAPPY_CLI_UPDATE_CHECK_INTERVAL && cliUpdateIntervalMs === index.DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS && process.env.HAPPY_CLI_UPDATE_CHECK_INTERVAL.trim() !== String(index.DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS)) {
|
|
11291
|
+
persistence.logger.warn(`[DAEMON RUN] Ignoring invalid HAPPY_CLI_UPDATE_CHECK_INTERVAL; using ${index.DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS}ms`);
|
|
11258
11292
|
}
|
|
11259
11293
|
const cliUpdateInterval = setInterval(() => void refreshCliUpdate(), cliUpdateIntervalMs);
|
|
11260
11294
|
const stopAgentVersionChecks = startAgentVersionChecks(
|
|
@@ -11305,7 +11339,7 @@ async function startDaemon() {
|
|
|
11305
11339
|
claudeAuthService.stop();
|
|
11306
11340
|
apiMachine.shutdown();
|
|
11307
11341
|
await stopControlServer();
|
|
11308
|
-
await
|
|
11342
|
+
await index.cleanupDaemonState();
|
|
11309
11343
|
await persistence.releaseDaemonLock(daemonLockHandle);
|
|
11310
11344
|
await stopCaffeinate();
|
|
11311
11345
|
try {
|
|
@@ -11378,7 +11412,7 @@ async function startDaemon() {
|
|
|
11378
11412
|
}
|
|
11379
11413
|
apiMachine.shutdown();
|
|
11380
11414
|
await stopControlServer();
|
|
11381
|
-
await
|
|
11415
|
+
await index.cleanupDaemonState();
|
|
11382
11416
|
await stopCaffeinate();
|
|
11383
11417
|
await persistence.releaseDaemonLock(daemonLockHandle);
|
|
11384
11418
|
persistence.logger.debug(`[DAEMON RUN] Cleanup completed, exiting process with code ${daemonExitCode(source)}`);
|
|
@@ -11645,7 +11679,7 @@ ${sessions.map(describeSummary).join("\n")}`);
|
|
|
11645
11679
|
limit: z.z.number().optional().describe("How many recent messages to fetch (default 20, max 100)")
|
|
11646
11680
|
}
|
|
11647
11681
|
}, async (args) => {
|
|
11648
|
-
if (!
|
|
11682
|
+
if (!index.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
|
|
11649
11683
|
try {
|
|
11650
11684
|
const result = await readSessionTranscript(args.sessionId, args.limit ?? 20);
|
|
11651
11685
|
const body = result.transcript.length > 0 ? result.transcript : "(no readable conversation content in this range)";
|
|
@@ -11664,7 +11698,7 @@ ${body}`);
|
|
|
11664
11698
|
text: z.z.string().describe("The message text to send")
|
|
11665
11699
|
}
|
|
11666
11700
|
}, async (args) => {
|
|
11667
|
-
if (!
|
|
11701
|
+
if (!index.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
|
|
11668
11702
|
if (typeof args.text !== "string" || args.text.trim().length === 0) return fail("text must be non-empty");
|
|
11669
11703
|
try {
|
|
11670
11704
|
const persisted = await waitForSessionKey(args.sessionId, 0);
|
|
@@ -11686,7 +11720,7 @@ ${body}`);
|
|
|
11686
11720
|
if (!normalized.ok) return fail(normalized.error);
|
|
11687
11721
|
const directory = normalized.directory;
|
|
11688
11722
|
try {
|
|
11689
|
-
const result = await
|
|
11723
|
+
const result = await index.spawnDaemonSession(directory, void 0, { spawnedBy: "assistant" });
|
|
11690
11724
|
if (result?.error || !result?.sessionId) {
|
|
11691
11725
|
return fail(`Failed to spawn session: ${result?.error ?? "daemon returned no session id"}`);
|
|
11692
11726
|
}
|
|
@@ -11713,7 +11747,7 @@ ${body}`);
|
|
|
11713
11747
|
sessionId: z.z.string().describe("The Happy session id to stop")
|
|
11714
11748
|
}
|
|
11715
11749
|
}, async (args) => {
|
|
11716
|
-
if (!
|
|
11750
|
+
if (!index.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
|
|
11717
11751
|
try {
|
|
11718
11752
|
const persisted = persistence.readPersistedSessions()[args.sessionId];
|
|
11719
11753
|
const success = await stopSession(args.sessionId);
|
|
@@ -11730,7 +11764,7 @@ ${body}`);
|
|
|
11730
11764
|
sessionId: z.z.string().describe("The Happy session id to archive")
|
|
11731
11765
|
}
|
|
11732
11766
|
}, async (args) => {
|
|
11733
|
-
if (!
|
|
11767
|
+
if (!index.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
|
|
11734
11768
|
try {
|
|
11735
11769
|
await archiveSession(args.sessionId);
|
|
11736
11770
|
const persisted = persistence.readPersistedSessions()[args.sessionId];
|
|
@@ -11746,7 +11780,7 @@ function registerAssistantMachineTools(mcp) {
|
|
|
11746
11780
|
title: "List Terminals",
|
|
11747
11781
|
inputSchema: {}
|
|
11748
11782
|
}, async () => {
|
|
11749
|
-
const r =
|
|
11783
|
+
const r = index.listVhTerminals();
|
|
11750
11784
|
if (!r.ok) return fail(`Failed to list terminals: ${r.error}`);
|
|
11751
11785
|
if (r.terminals.length === 0) return ok("No web terminals are open on this machine.");
|
|
11752
11786
|
const lines = r.terminals.map((t) => {
|
|
@@ -11766,8 +11800,8 @@ ${lines.join("\n")}`);
|
|
|
11766
11800
|
lines: z.z.number().optional().describe("How many history lines to read (default 80, max 2000)")
|
|
11767
11801
|
}
|
|
11768
11802
|
}, async (args) => {
|
|
11769
|
-
if (!
|
|
11770
|
-
const r =
|
|
11803
|
+
if (!index.isValidTerminalId(args.terminalId)) return fail("Invalid terminalId");
|
|
11804
|
+
const r = index.readVhTerminal(args.terminalId, args.lines ?? 80);
|
|
11771
11805
|
if (!r.ok) return fail(`Failed to read terminal ${args.terminalId}: ${r.error}`);
|
|
11772
11806
|
return ok(`terminal ${args.terminalId} \u2014 last output:
|
|
11773
11807
|
${r.text}`);
|
|
@@ -11781,10 +11815,10 @@ ${r.text}`);
|
|
|
11781
11815
|
submit: z.z.boolean().optional().describe("Also press Enter after pasting (default false)")
|
|
11782
11816
|
}
|
|
11783
11817
|
}, async (args) => {
|
|
11784
|
-
if (!
|
|
11818
|
+
if (!index.isValidTerminalId(args.terminalId)) return fail("Invalid terminalId");
|
|
11785
11819
|
if (typeof args.text !== "string" || args.text.length === 0) return fail("text must be non-empty");
|
|
11786
11820
|
const submit = args.submit === true;
|
|
11787
|
-
const r =
|
|
11821
|
+
const r = index.sendToVhTerminal(args.terminalId, args.text, submit);
|
|
11788
11822
|
if (!r.ok) return fail(`Failed to send to terminal ${args.terminalId}: ${r.error}`);
|
|
11789
11823
|
return ok(submit ? `Pasted and submitted (Enter sent) in terminal ${args.terminalId}.` : `Pasted into terminal ${args.terminalId} WITHOUT pressing Enter \u2014 the user (or a follow-up terminal_send with submit=true) decides whether it runs.`);
|
|
11790
11824
|
});
|
|
@@ -12056,7 +12090,7 @@ async function startHappyServer(client, options) {
|
|
|
12056
12090
|
return;
|
|
12057
12091
|
}
|
|
12058
12092
|
}
|
|
12059
|
-
const usage =
|
|
12093
|
+
const usage = index.ContextUsageSchema.parse({ ...JSON.parse(body), updatedAt: Date.now() });
|
|
12060
12094
|
options.onContextUsage(usage);
|
|
12061
12095
|
res.writeHead(204).end();
|
|
12062
12096
|
} catch {
|
|
@@ -12265,13 +12299,27 @@ function registerKillSessionHandler(rpcHandlerManager, killThisHappy, archiveEve
|
|
|
12265
12299
|
}
|
|
12266
12300
|
|
|
12267
12301
|
const SIDE_QUESTION_SYSTEM_PROMPT = [
|
|
12268
|
-
"
|
|
12269
|
-
"
|
|
12270
|
-
"Side questions cannot use tools: do not attempt to read files, run commands, or call tools \u2014 if",
|
|
12271
|
-
"the answer would require that, say what you would check and why.",
|
|
12272
|
-
"Do NOT continue, resume, or reference progress on the main task; the main conversation is",
|
|
12273
|
-
"unaffected by this exchange."
|
|
12302
|
+
"A user message wrapped in a side-question system-reminder is a SIDE QUESTION: answer it",
|
|
12303
|
+
"directly in one response from the conversation context, with no tools and no action on the main task."
|
|
12274
12304
|
].join(" ");
|
|
12305
|
+
const SIDE_QUESTION_REMINDER = [
|
|
12306
|
+
"<system-reminder>This is a side question from the user. You must answer this question directly in a single response.",
|
|
12307
|
+
"",
|
|
12308
|
+
"IMPORTANT CONTEXT:",
|
|
12309
|
+
"- You are a separate, lightweight agent spawned to answer this one question",
|
|
12310
|
+
"- The main agent is NOT interrupted - it continues working independently in the background",
|
|
12311
|
+
"- You share the conversation context but are a completely separate instance",
|
|
12312
|
+
'- Do NOT reference being interrupted or what you were "previously doing" - that framing is incorrect',
|
|
12313
|
+
"",
|
|
12314
|
+
"CRITICAL CONSTRAINTS:",
|
|
12315
|
+
"- You have NO tools available - you cannot read files, run commands, search, or take any actions",
|
|
12316
|
+
"- This is a one-off response - there will be no follow-up turns",
|
|
12317
|
+
"- You can ONLY provide information based on what you already know from the conversation context",
|
|
12318
|
+
`- NEVER say things like "Let me try...", "I'll now...", "Let me check...", or promise to take any action`,
|
|
12319
|
+
"- If you don't know the answer, say so - do not offer to look it up or investigate",
|
|
12320
|
+
"",
|
|
12321
|
+
"Simply answer the question with the information you have.</system-reminder>"
|
|
12322
|
+
].join("\n");
|
|
12275
12323
|
const MAX_HISTORY_CHARS = 2e3;
|
|
12276
12324
|
function clip(text, max) {
|
|
12277
12325
|
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
@@ -12279,14 +12327,17 @@ function clip(text, max) {
|
|
|
12279
12327
|
function buildSideQuestionPrompt(question, history = []) {
|
|
12280
12328
|
const trimmed = question.trim();
|
|
12281
12329
|
const prior = history.filter((h) => h.question.trim() && h.answer.trim()).slice(-12);
|
|
12282
|
-
|
|
12330
|
+
const body = `${SIDE_QUESTION_REMINDER}
|
|
12331
|
+
|
|
12332
|
+
${trimmed}`;
|
|
12333
|
+
if (prior.length === 0) return body;
|
|
12283
12334
|
const lines = prior.map((h) => `Q: ${clip(h.question.trim(), MAX_HISTORY_CHARS)}
|
|
12284
12335
|
A: ${clip(h.answer.trim(), MAX_HISTORY_CHARS)}`);
|
|
12285
12336
|
return `<earlier-side-questions>
|
|
12286
12337
|
${lines.join("\n\n")}
|
|
12287
12338
|
</earlier-side-questions>
|
|
12288
12339
|
|
|
12289
|
-
${
|
|
12340
|
+
${body}`;
|
|
12290
12341
|
}
|
|
12291
12342
|
function sideQuestionQueryOptions(input) {
|
|
12292
12343
|
return {
|
|
@@ -12307,17 +12358,36 @@ function sideQuestionQueryOptions(input) {
|
|
|
12307
12358
|
abort: input.signal
|
|
12308
12359
|
};
|
|
12309
12360
|
}
|
|
12310
|
-
function
|
|
12311
|
-
if (message.type !== "assistant") return
|
|
12361
|
+
function assistantBlocks(message) {
|
|
12362
|
+
if (message.type !== "assistant") return [];
|
|
12312
12363
|
const content = message.message?.content;
|
|
12313
|
-
|
|
12314
|
-
|
|
12364
|
+
return Array.isArray(content) ? content.filter((b) => Boolean(b) && typeof b === "object") : [];
|
|
12365
|
+
}
|
|
12366
|
+
function textFromAssistant(message) {
|
|
12367
|
+
return assistantBlocks(message).map((block) => block.type === "text" ? String(block.text ?? "") : "").join("");
|
|
12368
|
+
}
|
|
12369
|
+
function toolUseFromAssistant(message) {
|
|
12370
|
+
const block = assistantBlocks(message).find((b) => b.type === "tool_use");
|
|
12371
|
+
return block ? String(block.name ?? "a tool") : null;
|
|
12372
|
+
}
|
|
12373
|
+
function toolAttemptNotice(toolName) {
|
|
12374
|
+
return `(The model tried to call ${toolName} instead of answering directly. Try rephrasing or ask in the main conversation.)`;
|
|
12375
|
+
}
|
|
12376
|
+
function apiErrorFromSystem(message) {
|
|
12377
|
+
if (message.type !== "system") return null;
|
|
12378
|
+
const m = message;
|
|
12379
|
+
if (m.subtype !== "api_error") return null;
|
|
12380
|
+
if (typeof m.error === "string") return m.error;
|
|
12381
|
+
const detail = m.error?.formatted ?? m.error?.message;
|
|
12382
|
+
return typeof detail === "string" && detail ? detail : "API error";
|
|
12315
12383
|
}
|
|
12316
12384
|
async function runSideQuestion(query, input) {
|
|
12317
12385
|
const options = sideQuestionQueryOptions(input);
|
|
12318
12386
|
const stream = query({ prompt: buildSideQuestionPrompt(input.question, input.history), options });
|
|
12319
12387
|
let streamed = "";
|
|
12320
12388
|
let final = "";
|
|
12389
|
+
let toolAttempt = null;
|
|
12390
|
+
let lastApiError = null;
|
|
12321
12391
|
let resultSeen = false;
|
|
12322
12392
|
for await (const message of stream) {
|
|
12323
12393
|
if (message.type === "stream_event") {
|
|
@@ -12336,20 +12406,31 @@ async function runSideQuestion(query, input) {
|
|
|
12336
12406
|
${text}` : text;
|
|
12337
12407
|
input.onText?.(final);
|
|
12338
12408
|
}
|
|
12409
|
+
toolAttempt ??= toolUseFromAssistant(message);
|
|
12410
|
+
continue;
|
|
12411
|
+
}
|
|
12412
|
+
if (message.type === "system") {
|
|
12413
|
+
lastApiError = apiErrorFromSystem(message) ?? lastApiError;
|
|
12339
12414
|
continue;
|
|
12340
12415
|
}
|
|
12341
12416
|
if (message.type === "result") {
|
|
12342
12417
|
resultSeen = true;
|
|
12343
12418
|
const result = message;
|
|
12344
12419
|
if (result.subtype !== "success") {
|
|
12345
|
-
|
|
12420
|
+
if (toolAttempt && result.subtype === "error_max_turns") break;
|
|
12421
|
+
const detail = Array.isArray(result.errors) && result.errors.length > 0 ? String(result.errors[0]) : typeof result.result === "string" && result.result ? result.result : lastApiError ?? result.subtype ?? "unknown";
|
|
12346
12422
|
throw new Error(`Side question failed: ${detail}`);
|
|
12347
12423
|
}
|
|
12348
12424
|
if (!final && typeof result.result === "string") final = result.result;
|
|
12349
12425
|
}
|
|
12350
12426
|
}
|
|
12351
12427
|
if (input.signal?.aborted) throw new Error("Side question cancelled");
|
|
12352
|
-
if (
|
|
12428
|
+
if (toolAttempt && !final.trim() && !streamed.trim()) {
|
|
12429
|
+
const notice = toolAttemptNotice(toolAttempt);
|
|
12430
|
+
input.onText?.(notice);
|
|
12431
|
+
return { answer: notice, hadContext: Boolean(input.resumeSessionId) };
|
|
12432
|
+
}
|
|
12433
|
+
if (!resultSeen && !final && !streamed) throw new Error(`Side question ended without a result${lastApiError ? `: ${lastApiError}` : ""}`);
|
|
12353
12434
|
return { answer: final || streamed, hadContext: Boolean(input.resumeSessionId) };
|
|
12354
12435
|
}
|
|
12355
12436
|
|
|
@@ -12376,7 +12457,7 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12376
12457
|
const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
|
|
12377
12458
|
const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
|
|
12378
12459
|
const run = deps.run ?? (async (input) => {
|
|
12379
|
-
const { query } = await Promise.resolve().then(function () { return require('./index-
|
|
12460
|
+
const { query } = await Promise.resolve().then(function () { return require('./index-D4Smqppj.cjs'); });
|
|
12380
12461
|
return runSideQuestion(query, input);
|
|
12381
12462
|
});
|
|
12382
12463
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12399,7 +12480,12 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12399
12480
|
if (!question) throw new Error("Side question is empty");
|
|
12400
12481
|
if (question.length > SIDE_QUESTION_MAX_CHARS) throw new Error("Side question is too long");
|
|
12401
12482
|
prune();
|
|
12402
|
-
if (active)
|
|
12483
|
+
if (active) {
|
|
12484
|
+
const stale = active;
|
|
12485
|
+
stale.abort.abort("superseded");
|
|
12486
|
+
finish(stale, "cancelled");
|
|
12487
|
+
persistence.logger.debug(`[btw] side question ${stale.requestId} superseded by a new ask`);
|
|
12488
|
+
}
|
|
12403
12489
|
const resumeSessionId = deps.getClaudeSessionId() ?? null;
|
|
12404
12490
|
const slot = {
|
|
12405
12491
|
requestId: node_crypto.randomUUID(),
|
|
@@ -12478,7 +12564,7 @@ const MAX_TITLE_CHARS = 60;
|
|
|
12478
12564
|
const GENERATION_TIMEOUT_MS = 3e4;
|
|
12479
12565
|
function resolveClaudeBinary() {
|
|
12480
12566
|
try {
|
|
12481
|
-
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-
|
|
12567
|
+
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-DhwtFJEe.cjs', document.baseURI).href)));
|
|
12482
12568
|
const utilsPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_version_utils.cjs"));
|
|
12483
12569
|
const { getClaudeCliPath } = require$1(utilsPath);
|
|
12484
12570
|
const path$1 = getClaudeCliPath();
|
|
@@ -12490,7 +12576,7 @@ function resolveClaudeBinary() {
|
|
|
12490
12576
|
}
|
|
12491
12577
|
function buildPrompt(firstUserMessage) {
|
|
12492
12578
|
const sample = firstUserMessage.slice(0, MAX_PROMPT_SAMPLE_CHARS);
|
|
12493
|
-
return `${
|
|
12579
|
+
return `${index.TITLE_PROMPT_PREFIX} (match the message's language). Be concrete \u2014 name the actual task/subject. Do NOT output generic placeholders like "\u65B0\u5BF9\u8BDD"/"New chat"/"Conversation"/"Untitled". No quotes, no trailing punctuation. Reply with ONLY the title.
|
|
12494
12580
|
|
|
12495
12581
|
Message: "${sample}"`;
|
|
12496
12582
|
}
|
|
@@ -12686,7 +12772,7 @@ function buildBoardPrompt(input) {
|
|
|
12686
12772
|
const taskLines = tasks.length > 0 ? tasks.map((t) => `- ${t.id}: ${t.title}`).join("\n") : "(no tasks defined)";
|
|
12687
12773
|
const todoBlock = input.todos.length > 0 ? input.todos.join("\n") : "(none)";
|
|
12688
12774
|
return [
|
|
12689
|
-
`${
|
|
12775
|
+
`${index.BOARD_ANALYZER_PROMPT_PREFIX} Analyze this session snapshot and reply with ONLY a strict JSON object, no markdown, no explanations:`,
|
|
12690
12776
|
'{"taskId": string|null, "attention": "none"|"review"|"blocked", "progress": string}',
|
|
12691
12777
|
"",
|
|
12692
12778
|
"Rules:",
|
|
@@ -12890,8 +12976,8 @@ async function runClaude(credentials, options = {}) {
|
|
|
12890
12976
|
if (options.startedBy === "daemon" && options.startingMode === "local") {
|
|
12891
12977
|
throw new Error("Daemon-spawned sessions cannot use local/interactive mode. Use --happy-starting-mode remote or spawn sessions directly from terminal.");
|
|
12892
12978
|
}
|
|
12893
|
-
|
|
12894
|
-
const api
|
|
12979
|
+
index.connectionState.setBackend("Claude");
|
|
12980
|
+
const api = await index.ApiClient.create(credentials);
|
|
12895
12981
|
let state = {};
|
|
12896
12982
|
const settings = await persistence.readSettings();
|
|
12897
12983
|
let machineId = settings?.machineId;
|
|
@@ -12908,7 +12994,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
12908
12994
|
}
|
|
12909
12995
|
persistence.logger.debug(`Using machineId: ${machineId}`);
|
|
12910
12996
|
const sessionTag = node_crypto.randomUUID();
|
|
12911
|
-
await api
|
|
12997
|
+
await api.getOrCreateMachine({
|
|
12912
12998
|
machineId,
|
|
12913
12999
|
metadata: initialMachineMetadata
|
|
12914
13000
|
});
|
|
@@ -12978,16 +13064,16 @@ async function runClaude(credentials, options = {}) {
|
|
|
12978
13064
|
agentStateVersion: parseInt(reconnectAgentStateVersion || "0", 10)
|
|
12979
13065
|
};
|
|
12980
13066
|
} else {
|
|
12981
|
-
response = await api
|
|
13067
|
+
response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
12982
13068
|
}
|
|
12983
13069
|
if (!response) {
|
|
12984
13070
|
let offlineSessionId = null;
|
|
12985
|
-
const reconnection =
|
|
13071
|
+
const reconnection = index.startOfflineReconnection({
|
|
12986
13072
|
serverUrl: persistence.configuration.serverUrl,
|
|
12987
13073
|
onReconnected: async () => {
|
|
12988
|
-
const resp = await api
|
|
13074
|
+
const resp = await api.getOrCreateSession({ tag: node_crypto.randomUUID(), metadata, state });
|
|
12989
13075
|
if (!resp) throw new Error("Server unavailable");
|
|
12990
|
-
const session2 = api
|
|
13076
|
+
const session2 = api.sessionSyncClient(resp);
|
|
12991
13077
|
const scanner = await createSessionScanner({
|
|
12992
13078
|
sessionId: null,
|
|
12993
13079
|
workingDirectory,
|
|
@@ -13023,12 +13109,12 @@ async function runClaude(credentials, options = {}) {
|
|
|
13023
13109
|
}
|
|
13024
13110
|
persistence.logger.debug(`Session created: ${response.id}`);
|
|
13025
13111
|
await claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "claude" });
|
|
13026
|
-
if (reconnectSessionId && !await api
|
|
13112
|
+
if (reconnectSessionId && !await api.reactivateSession(response.id)) {
|
|
13027
13113
|
throw new Error(`Failed to reactivate archived session ${response.id}`);
|
|
13028
13114
|
}
|
|
13029
13115
|
let reconnectSeeded = false;
|
|
13030
13116
|
if (reconnectSessionId) {
|
|
13031
|
-
const snapshot = await api
|
|
13117
|
+
const snapshot = await api.getSession(response.id, response.encryptionKey, response.encryptionVariant);
|
|
13032
13118
|
if (snapshot.ok) {
|
|
13033
13119
|
response = applyServerSnapshot(response, snapshot.session);
|
|
13034
13120
|
reconnectSeeded = true;
|
|
@@ -13040,7 +13126,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13040
13126
|
}
|
|
13041
13127
|
try {
|
|
13042
13128
|
persistence.logger.debug(`[START] Reporting session ${response.id} to daemon`);
|
|
13043
|
-
const result = await
|
|
13129
|
+
const result = await index.notifyDaemonSessionStarted(response.id, metadata, {
|
|
13044
13130
|
encryptionKey: persistence.encodeBase64(response.encryptionKey),
|
|
13045
13131
|
encryptionVariant: response.encryptionVariant,
|
|
13046
13132
|
seq: response.seq,
|
|
@@ -13055,7 +13141,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13055
13141
|
} catch (error) {
|
|
13056
13142
|
persistence.logger.debug("[START] Failed to report to daemon (may not be running):", error);
|
|
13057
13143
|
}
|
|
13058
|
-
const session = api
|
|
13144
|
+
const session = api.sessionSyncClient(response, reconnectSeeded ? { initialSeq: response.seq } : void 0);
|
|
13059
13145
|
if (reconnectSessionId) {
|
|
13060
13146
|
session.suppressNextArchiveSignal();
|
|
13061
13147
|
if (!reconnectSeeded) session.skipExistingMessages();
|
|
@@ -13071,7 +13157,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13071
13157
|
}
|
|
13072
13158
|
const forkClaudeSessionId = process.env.HAPPY_FORK_CLAUDE_SESSION_ID;
|
|
13073
13159
|
if (!reconnectSessionId && forkClaudeSessionId) {
|
|
13074
|
-
const jsonlPath = path.join(
|
|
13160
|
+
const jsonlPath = path.join(index.getProjectPath(workingDirectory), `${forkClaudeSessionId}.jsonl`);
|
|
13075
13161
|
try {
|
|
13076
13162
|
const file = await promises.readFile(jsonlPath, "utf-8");
|
|
13077
13163
|
const lines = file.split("\n");
|
|
@@ -13084,7 +13170,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13084
13170
|
} catch {
|
|
13085
13171
|
continue;
|
|
13086
13172
|
}
|
|
13087
|
-
const result =
|
|
13173
|
+
const result = index.RawJSONLinesSchema.safeParse(parsed);
|
|
13088
13174
|
if (!result.success) continue;
|
|
13089
13175
|
session.sendClaudeSessionMessage(result.data);
|
|
13090
13176
|
backfilled += 1;
|
|
@@ -13126,7 +13212,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13126
13212
|
enabled: settings?.boardLlm === true,
|
|
13127
13213
|
selfReportState: boardSelfReport,
|
|
13128
13214
|
fetchTasks: async () => {
|
|
13129
|
-
const item = await api
|
|
13215
|
+
const item = await api.kvGet("vh.board-tasks.v1");
|
|
13130
13216
|
if (!item) return null;
|
|
13131
13217
|
try {
|
|
13132
13218
|
const parsed = JSON.parse(Buffer.from(item.value, "base64").toString("utf8"));
|
|
@@ -13162,7 +13248,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13162
13248
|
let currentSession = null;
|
|
13163
13249
|
const hookServer = await startHookServer({
|
|
13164
13250
|
onSessionHook: (sessionId, data) => {
|
|
13165
|
-
persistence.logger.debug(`[START] Session hook received: ${sessionId}`,
|
|
13251
|
+
persistence.logger.debug(`[START] Session hook received: ${sessionId}`, index.contentLogMetadata(data));
|
|
13166
13252
|
remoteScanner.onNewSession(sessionId, { treatExistingAsProcessed: true });
|
|
13167
13253
|
if (currentSession) {
|
|
13168
13254
|
const previousSessionId = currentSession.sessionId;
|
|
@@ -13380,7 +13466,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13380
13466
|
};
|
|
13381
13467
|
messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode2, attachmentsForThisMessage, message.localKey);
|
|
13382
13468
|
persistence.logger.debug("[start] /compact command pushed to queue:", {
|
|
13383
|
-
...
|
|
13469
|
+
...index.contentLogMetadata(message.content.text),
|
|
13384
13470
|
attachmentCount: attachmentsForThisMessage?.length ?? 0,
|
|
13385
13471
|
hasMetadata: Boolean(message.meta)
|
|
13386
13472
|
});
|
|
@@ -13400,7 +13486,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13400
13486
|
};
|
|
13401
13487
|
messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode2, attachmentsForThisMessage, message.localKey);
|
|
13402
13488
|
persistence.logger.debug("[start] /clear command pushed to queue:", {
|
|
13403
|
-
...
|
|
13489
|
+
...index.contentLogMetadata(message.content.text),
|
|
13404
13490
|
attachmentCount: attachmentsForThisMessage?.length ?? 0,
|
|
13405
13491
|
hasMetadata: Boolean(message.meta)
|
|
13406
13492
|
});
|
|
@@ -13462,7 +13548,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13462
13548
|
}) === true;
|
|
13463
13549
|
if (accepted) {
|
|
13464
13550
|
persistence.logger.debug("User message steered into current turn:", {
|
|
13465
|
-
...
|
|
13551
|
+
...index.contentLogMetadata(message.content.text),
|
|
13466
13552
|
attachmentCount: attachmentsForThisMessage?.length ?? 0
|
|
13467
13553
|
});
|
|
13468
13554
|
return;
|
|
@@ -13471,7 +13557,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13471
13557
|
}
|
|
13472
13558
|
messageQueue.push(message.content.text, enhancedMode, attachmentsForThisMessage, message.localKey);
|
|
13473
13559
|
persistence.logger.debug("User message pushed to queue:", {
|
|
13474
|
-
...
|
|
13560
|
+
...index.contentLogMetadata(message.content.text),
|
|
13475
13561
|
attachmentCount: attachmentsForThisMessage?.length ?? 0,
|
|
13476
13562
|
hasMetadata: Boolean(message.meta)
|
|
13477
13563
|
});
|
|
@@ -13492,7 +13578,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13492
13578
|
currentSession?.cleanup();
|
|
13493
13579
|
session.sendSessionDeath();
|
|
13494
13580
|
try {
|
|
13495
|
-
await api
|
|
13581
|
+
await api.deactivateSession(session.sessionId);
|
|
13496
13582
|
} catch (err) {
|
|
13497
13583
|
persistence.logger.debug("[START] deactivateSession during cleanup failed:", err);
|
|
13498
13584
|
}
|
|
@@ -13539,7 +13625,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13539
13625
|
permissionMode: initialPermissionMode,
|
|
13540
13626
|
startingMode: options.startingMode,
|
|
13541
13627
|
messageQueue,
|
|
13542
|
-
api
|
|
13628
|
+
api,
|
|
13543
13629
|
allowedTools: happyServer.toolNames.map((toolName) => `mcp__happy__${toolName}`),
|
|
13544
13630
|
onModeChange: (newMode) => {
|
|
13545
13631
|
currentRunMode = newMode;
|
|
@@ -13768,7 +13854,7 @@ async function handleAuthLogin(args) {
|
|
|
13768
13854
|
console.log(chalk.gray(" \u2022 Re-authenticate and register machine\n"));
|
|
13769
13855
|
try {
|
|
13770
13856
|
persistence.logger.debug("Stopping daemon for force auth...");
|
|
13771
|
-
await
|
|
13857
|
+
await index.stopDaemon();
|
|
13772
13858
|
console.log(chalk.gray("\u2713 Stopped daemon"));
|
|
13773
13859
|
} catch (error) {
|
|
13774
13860
|
persistence.logger.debug("Daemon was not running or failed to stop:", error);
|
|
@@ -13833,7 +13919,7 @@ async function handleAuthLogout() {
|
|
|
13833
13919
|
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
13834
13920
|
try {
|
|
13835
13921
|
try {
|
|
13836
|
-
await
|
|
13922
|
+
await index.stopDaemon();
|
|
13837
13923
|
console.log(chalk.gray("Stopped daemon"));
|
|
13838
13924
|
} catch {
|
|
13839
13925
|
}
|
|
@@ -13878,7 +13964,7 @@ async function handleAuthStatus() {
|
|
|
13878
13964
|
console.log(chalk.gray(`
|
|
13879
13965
|
Data directory: ${persistence.configuration.happyHomeDir}`));
|
|
13880
13966
|
try {
|
|
13881
|
-
const running = await
|
|
13967
|
+
const running = await index.checkIfDaemonRunningAndCleanupStaleState();
|
|
13882
13968
|
if (running) {
|
|
13883
13969
|
console.log(chalk.green("\u2713 Daemon running"));
|
|
13884
13970
|
} else {
|
|
@@ -14440,23 +14526,23 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
14440
14526
|
console.log(chalk.gray(' Please run "very-happy auth login" first'));
|
|
14441
14527
|
process.exit(1);
|
|
14442
14528
|
}
|
|
14443
|
-
const api
|
|
14529
|
+
const api = await index.ApiClient.create(credentials);
|
|
14444
14530
|
if (vendor === "codex") {
|
|
14445
14531
|
console.log("\u{1F680} Registering Codex token with server");
|
|
14446
14532
|
const codexAuthTokens = await authenticateCodex();
|
|
14447
|
-
await api
|
|
14533
|
+
await api.registerVendorToken("openai", { oauth: codexAuthTokens });
|
|
14448
14534
|
console.log("\u2705 Codex token registered with server");
|
|
14449
14535
|
process.exit(0);
|
|
14450
14536
|
} else if (vendor === "claude") {
|
|
14451
14537
|
console.log("\u{1F680} Registering Anthropic token with server");
|
|
14452
14538
|
const anthropicAuthTokens = await authenticateClaude();
|
|
14453
|
-
await api
|
|
14539
|
+
await api.registerVendorToken("anthropic", { oauth: anthropicAuthTokens });
|
|
14454
14540
|
console.log("\u2705 Anthropic token registered with server");
|
|
14455
14541
|
process.exit(0);
|
|
14456
14542
|
} else if (vendor === "gemini") {
|
|
14457
14543
|
console.log("\u{1F680} Registering Gemini token with server");
|
|
14458
14544
|
const geminiAuthTokens = await authenticateGemini();
|
|
14459
|
-
await api
|
|
14545
|
+
await api.registerVendorToken("gemini", { oauth: geminiAuthTokens });
|
|
14460
14546
|
console.log("\u2705 Gemini token registered with server");
|
|
14461
14547
|
updateLocalGeminiCredentials(geminiAuthTokens);
|
|
14462
14548
|
process.exit(0);
|
|
@@ -14472,7 +14558,7 @@ async function handleConnectStatus() {
|
|
|
14472
14558
|
console.log(chalk.gray(' Please run "very-happy auth login" first'));
|
|
14473
14559
|
process.exit(1);
|
|
14474
14560
|
}
|
|
14475
|
-
const api
|
|
14561
|
+
const api = await index.ApiClient.create(credentials);
|
|
14476
14562
|
const vendors = [
|
|
14477
14563
|
{ key: "gemini", name: "Gemini", display: "Google Gemini" },
|
|
14478
14564
|
{ key: "openai", name: "Codex", display: "OpenAI Codex" },
|
|
@@ -14480,7 +14566,7 @@ async function handleConnectStatus() {
|
|
|
14480
14566
|
];
|
|
14481
14567
|
for (const vendor of vendors) {
|
|
14482
14568
|
try {
|
|
14483
|
-
const token = await api
|
|
14569
|
+
const token = await api.getVendorToken(vendor.key);
|
|
14484
14570
|
if (token?.oauth) {
|
|
14485
14571
|
let userInfo = "";
|
|
14486
14572
|
if (token.oauth.id_token) {
|
|
@@ -14704,9 +14790,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
|
|
|
14704
14790
|
};
|
|
14705
14791
|
}
|
|
14706
14792
|
|
|
14707
|
-
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-
|
|
14793
|
+
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-DhwtFJEe.cjs', document.baseURI).href)));
|
|
14708
14794
|
const __dirname$1 = path.dirname(__filename$1);
|
|
14709
|
-
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
14795
|
+
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DhwtFJEe.cjs', document.baseURI).href)));
|
|
14710
14796
|
const PRISMA_QUERY_ENGINE_FILES = {
|
|
14711
14797
|
"arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
|
|
14712
14798
|
"x64-darwin": "libquery_engine-darwin.dylib.node",
|
|
@@ -15095,7 +15181,7 @@ const DAEMON_READY_TIMEOUT_MS = 5e3;
|
|
|
15095
15181
|
const DAEMON_READY_POLL_INTERVAL_MS = 100;
|
|
15096
15182
|
async function ensureDaemonRunning() {
|
|
15097
15183
|
persistence.logger.debug("Ensuring Happy background service is running & matches our version...");
|
|
15098
|
-
const versionMatches = await
|
|
15184
|
+
const versionMatches = await index.isDaemonRunningCurrentlyInstalledHappyVersion();
|
|
15099
15185
|
const state = await persistence.readDaemonState();
|
|
15100
15186
|
const endpointsMatch = Boolean(state) && daemonEndpointsMatch(
|
|
15101
15187
|
state?.serverUrl,
|
|
@@ -15106,9 +15192,9 @@ async function ensureDaemonRunning() {
|
|
|
15106
15192
|
if (versionMatches && endpointsMatch) {
|
|
15107
15193
|
return;
|
|
15108
15194
|
}
|
|
15109
|
-
if (await
|
|
15195
|
+
if (await index.checkIfDaemonRunningAndCleanupStaleState()) {
|
|
15110
15196
|
persistence.logger.debug("Stopping daemon whose version or configured relay no longer matches...");
|
|
15111
|
-
await
|
|
15197
|
+
await index.stopDaemon();
|
|
15112
15198
|
}
|
|
15113
15199
|
persistence.logger.debug("Starting Happy background service...");
|
|
15114
15200
|
const daemonProcess = spawnHappyCLI(["daemon", "start-sync"], {
|
|
@@ -15119,7 +15205,7 @@ async function ensureDaemonRunning() {
|
|
|
15119
15205
|
daemonProcess.unref();
|
|
15120
15206
|
const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
|
|
15121
15207
|
while (Date.now() < deadline) {
|
|
15122
|
-
if (await
|
|
15208
|
+
if (await index.checkIfDaemonRunningAndCleanupStaleState()) {
|
|
15123
15209
|
persistence.logger.debug("Happy background service is ready");
|
|
15124
15210
|
return;
|
|
15125
15211
|
}
|
|
@@ -15141,14 +15227,14 @@ function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagent
|
|
|
15141
15227
|
if (!subagent || startedSubagents.has(subagent)) {
|
|
15142
15228
|
return;
|
|
15143
15229
|
}
|
|
15144
|
-
envelopes.push(
|
|
15230
|
+
envelopes.push(index.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
|
|
15145
15231
|
startedSubagents.add(subagent);
|
|
15146
15232
|
activeSubagents.add(subagent);
|
|
15147
15233
|
}
|
|
15148
15234
|
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15149
15235
|
const envelopes = [];
|
|
15150
15236
|
for (const subagent of activeSubagents) {
|
|
15151
|
-
envelopes.push(
|
|
15237
|
+
envelopes.push(index.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
15152
15238
|
}
|
|
15153
15239
|
activeSubagents.clear();
|
|
15154
15240
|
startedSubagents.clear();
|
|
@@ -15244,7 +15330,7 @@ function turnStatus(turn) {
|
|
|
15244
15330
|
function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
|
|
15245
15331
|
const time = turnTimestampMs(turn);
|
|
15246
15332
|
const opts = { turn: turn.id, time, codexItemId: item.id };
|
|
15247
|
-
envelopes.push(
|
|
15333
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
15248
15334
|
t: "tool-call-start",
|
|
15249
15335
|
call: item.id,
|
|
15250
15336
|
name,
|
|
@@ -15256,7 +15342,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
|
|
|
15256
15342
|
id: `${item.id}:start`
|
|
15257
15343
|
}));
|
|
15258
15344
|
if (output && output.trim().length > 0) {
|
|
15259
|
-
envelopes.push(
|
|
15345
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
15260
15346
|
t: "text",
|
|
15261
15347
|
text: output,
|
|
15262
15348
|
thinking: true
|
|
@@ -15266,7 +15352,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
|
|
|
15266
15352
|
}));
|
|
15267
15353
|
}
|
|
15268
15354
|
if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
|
|
15269
|
-
envelopes.push(
|
|
15355
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
15270
15356
|
t: "tool-call-end",
|
|
15271
15357
|
call: item.id
|
|
15272
15358
|
}, {
|
|
@@ -15288,7 +15374,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
|
15288
15374
|
agentsStates: item.agentsStates ?? {}
|
|
15289
15375
|
};
|
|
15290
15376
|
const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
|
|
15291
|
-
const envelopes = [
|
|
15377
|
+
const envelopes = [index.createEnvelope("agent", {
|
|
15292
15378
|
t: "tool-call-start",
|
|
15293
15379
|
call: item.id,
|
|
15294
15380
|
name: "CodexCollaboration",
|
|
@@ -15297,7 +15383,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
|
15297
15383
|
args
|
|
15298
15384
|
}, { ...opts, id: startId })];
|
|
15299
15385
|
if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
|
|
15300
|
-
envelopes.push(
|
|
15386
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
15301
15387
|
t: "tool-call-end",
|
|
15302
15388
|
call: item.id,
|
|
15303
15389
|
result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
|
|
@@ -15310,7 +15396,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15310
15396
|
for (const turn of thread.turns ?? []) {
|
|
15311
15397
|
const startedAt = turnTimestampMs(turn);
|
|
15312
15398
|
const completedAt = completedTimestampMs(turn);
|
|
15313
|
-
envelopes.push(
|
|
15399
|
+
envelopes.push(index.createEnvelope("agent", { t: "turn-start" }, {
|
|
15314
15400
|
id: `${turn.id}:start`,
|
|
15315
15401
|
turn: turn.id,
|
|
15316
15402
|
time: startedAt
|
|
@@ -15325,7 +15411,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15325
15411
|
case "userMessage": {
|
|
15326
15412
|
const text = textFromInputItems(item.content);
|
|
15327
15413
|
if (text) {
|
|
15328
|
-
envelopes.push(
|
|
15414
|
+
envelopes.push(index.createEnvelope("user", { t: "text", text }, {
|
|
15329
15415
|
id: item.id,
|
|
15330
15416
|
time: startedAt,
|
|
15331
15417
|
codexItemId: item.id
|
|
@@ -15336,7 +15422,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15336
15422
|
case "agentMessage": {
|
|
15337
15423
|
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15338
15424
|
if (text.length > 0) {
|
|
15339
|
-
envelopes.push(
|
|
15425
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text }, {
|
|
15340
15426
|
id: item.id,
|
|
15341
15427
|
turn: turn.id,
|
|
15342
15428
|
time: completedAt,
|
|
@@ -15348,7 +15434,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15348
15434
|
case "reasoning": {
|
|
15349
15435
|
const text = reasoningText(item);
|
|
15350
15436
|
if (text) {
|
|
15351
|
-
envelopes.push(
|
|
15437
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15352
15438
|
id: item.id,
|
|
15353
15439
|
turn: turn.id,
|
|
15354
15440
|
time: startedAt,
|
|
@@ -15403,7 +15489,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
|
|
|
15403
15489
|
}
|
|
15404
15490
|
}
|
|
15405
15491
|
}
|
|
15406
|
-
envelopes.push(
|
|
15492
|
+
envelopes.push(index.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15407
15493
|
id: `${turn.id}:end`,
|
|
15408
15494
|
turn: turn.id,
|
|
15409
15495
|
time: completedAt
|
|
@@ -15458,7 +15544,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15458
15544
|
}
|
|
15459
15545
|
if (type === "task_started") {
|
|
15460
15546
|
const turnId = cuid2.createId();
|
|
15461
|
-
const turnStart =
|
|
15547
|
+
const turnStart = index.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15462
15548
|
startedSubagents.clear();
|
|
15463
15549
|
activeSubagents.clear();
|
|
15464
15550
|
providerSubagentToSessionSubagent.clear();
|
|
@@ -15489,7 +15575,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15489
15575
|
providerSubagentToSessionSubagent,
|
|
15490
15576
|
envelopes: [
|
|
15491
15577
|
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
15492
|
-
|
|
15578
|
+
index.createEnvelope("agent", {
|
|
15493
15579
|
t: "turn-end",
|
|
15494
15580
|
status: pickTurnEndStatus(message, type)
|
|
15495
15581
|
}, lifecycleOpts)
|
|
@@ -15519,7 +15605,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15519
15605
|
}
|
|
15520
15606
|
const envelopes = [];
|
|
15521
15607
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15522
|
-
envelopes.push(
|
|
15608
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text: message.message }, opts));
|
|
15523
15609
|
return {
|
|
15524
15610
|
currentTurnId: state.currentTurnId,
|
|
15525
15611
|
startedSubagents,
|
|
@@ -15541,7 +15627,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15541
15627
|
}
|
|
15542
15628
|
const envelopes = [];
|
|
15543
15629
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15544
|
-
envelopes.push(
|
|
15630
|
+
envelopes.push(index.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
15545
15631
|
return {
|
|
15546
15632
|
currentTurnId: state.currentTurnId,
|
|
15547
15633
|
startedSubagents,
|
|
@@ -15558,7 +15644,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15558
15644
|
const envelopes = [];
|
|
15559
15645
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15560
15646
|
envelopes.push(
|
|
15561
|
-
|
|
15647
|
+
index.createEnvelope("agent", {
|
|
15562
15648
|
t: "tool-call-start",
|
|
15563
15649
|
call,
|
|
15564
15650
|
name: "CodexBash",
|
|
@@ -15579,7 +15665,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15579
15665
|
const call = pickCallId(message);
|
|
15580
15666
|
const envelopes = [];
|
|
15581
15667
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15582
|
-
envelopes.push(
|
|
15668
|
+
envelopes.push(index.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15583
15669
|
return {
|
|
15584
15670
|
currentTurnId: state.currentTurnId,
|
|
15585
15671
|
startedSubagents,
|
|
@@ -15595,7 +15681,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15595
15681
|
const envelopes = [];
|
|
15596
15682
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15597
15683
|
envelopes.push(
|
|
15598
|
-
|
|
15684
|
+
index.createEnvelope("agent", {
|
|
15599
15685
|
t: "tool-call-start",
|
|
15600
15686
|
call,
|
|
15601
15687
|
name: "CodexPatch",
|
|
@@ -15619,7 +15705,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
|
15619
15705
|
const call = pickCallId(message);
|
|
15620
15706
|
const envelopes = [];
|
|
15621
15707
|
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15622
|
-
envelopes.push(
|
|
15708
|
+
envelopes.push(index.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15623
15709
|
return {
|
|
15624
15710
|
currentTurnId: state.currentTurnId,
|
|
15625
15711
|
startedSubagents,
|
|
@@ -15640,7 +15726,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
15640
15726
|
const toolLikeMessage = message;
|
|
15641
15727
|
const opts = buildEnvelopeOptions(state.currentTurnId);
|
|
15642
15728
|
if (message.type === "reasoning") {
|
|
15643
|
-
return [
|
|
15729
|
+
return [index.createEnvelope("agent", {
|
|
15644
15730
|
t: "text",
|
|
15645
15731
|
text: message.message,
|
|
15646
15732
|
thinking: true
|
|
@@ -15648,7 +15734,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
15648
15734
|
}
|
|
15649
15735
|
if (message.type === "tool-call") {
|
|
15650
15736
|
const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
|
|
15651
|
-
return [
|
|
15737
|
+
return [index.createEnvelope("agent", {
|
|
15652
15738
|
t: "tool-call-start",
|
|
15653
15739
|
call: toolLikeMessage.callId,
|
|
15654
15740
|
name: toolLikeMessage.name || "unknown",
|
|
@@ -15661,13 +15747,13 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
|
15661
15747
|
const envelopes = [];
|
|
15662
15748
|
const content = toolLikeMessage.output?.content;
|
|
15663
15749
|
if (typeof content === "string" && content.trim().length > 0) {
|
|
15664
|
-
envelopes.push(
|
|
15750
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
15665
15751
|
t: "text",
|
|
15666
15752
|
text: content,
|
|
15667
15753
|
thinking: true
|
|
15668
15754
|
}, opts));
|
|
15669
15755
|
}
|
|
15670
|
-
envelopes.push(
|
|
15756
|
+
envelopes.push(index.createEnvelope("agent", {
|
|
15671
15757
|
t: "tool-call-end",
|
|
15672
15758
|
call: toolLikeMessage.callId
|
|
15673
15759
|
}, opts));
|
|
@@ -16356,17 +16442,17 @@ function createOfflineSessionStub(sessionTag) {
|
|
|
16356
16442
|
}
|
|
16357
16443
|
|
|
16358
16444
|
function setupOfflineReconnection(opts) {
|
|
16359
|
-
const { api
|
|
16445
|
+
const { api, sessionTag, metadata, state, response, onSessionSwap } = opts;
|
|
16360
16446
|
let session;
|
|
16361
16447
|
let reconnectionHandle = null;
|
|
16362
16448
|
if (!response) {
|
|
16363
16449
|
session = createOfflineSessionStub(sessionTag);
|
|
16364
|
-
reconnectionHandle =
|
|
16450
|
+
reconnectionHandle = index.startOfflineReconnection({
|
|
16365
16451
|
serverUrl: persistence.configuration.serverUrl,
|
|
16366
16452
|
onReconnected: async () => {
|
|
16367
|
-
const resp = await api
|
|
16453
|
+
const resp = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
16368
16454
|
if (!resp) throw new Error("Server unavailable");
|
|
16369
|
-
const realSession = api
|
|
16455
|
+
const realSession = api.sessionSyncClient(resp);
|
|
16370
16456
|
onSessionSwap(realSession);
|
|
16371
16457
|
return realSession;
|
|
16372
16458
|
},
|
|
@@ -16376,7 +16462,7 @@ function setupOfflineReconnection(opts) {
|
|
|
16376
16462
|
});
|
|
16377
16463
|
return { session, reconnectionHandle, isOffline: true };
|
|
16378
16464
|
} else {
|
|
16379
|
-
session = api
|
|
16465
|
+
session = api.sessionSyncClient(response, opts.sessionClientOptions);
|
|
16380
16466
|
return { session, reconnectionHandle: null, isOffline: false };
|
|
16381
16467
|
}
|
|
16382
16468
|
}
|
|
@@ -16626,7 +16712,7 @@ function buildCodexTurnPrompt(opts) {
|
|
|
16626
16712
|
parts.push(opts.message);
|
|
16627
16713
|
if (opts.includeTitleInstruction) {
|
|
16628
16714
|
parts.push(CHANGE_TITLE_INSTRUCTION);
|
|
16629
|
-
parts.push(
|
|
16715
|
+
parts.push(index.BUILTIN_TODO_DISCOVERY);
|
|
16630
16716
|
}
|
|
16631
16717
|
return parts.join("\n\n");
|
|
16632
16718
|
}
|
|
@@ -16694,8 +16780,8 @@ async function runCodex(opts) {
|
|
|
16694
16780
|
process.exit(1);
|
|
16695
16781
|
}
|
|
16696
16782
|
const sessionTag = node_crypto.randomUUID();
|
|
16697
|
-
|
|
16698
|
-
const api
|
|
16783
|
+
index.connectionState.setBackend("Codex");
|
|
16784
|
+
const api = await index.ApiClient.create(opts.credentials);
|
|
16699
16785
|
persistence.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
|
|
16700
16786
|
const settings = await persistence.readSettings();
|
|
16701
16787
|
let machineId = settings?.machineId;
|
|
@@ -16705,7 +16791,7 @@ async function runCodex(opts) {
|
|
|
16705
16791
|
process.exit(1);
|
|
16706
16792
|
}
|
|
16707
16793
|
persistence.logger.debug(`Using machineId: ${machineId}`);
|
|
16708
|
-
await api
|
|
16794
|
+
await api.getOrCreateMachine({
|
|
16709
16795
|
machineId,
|
|
16710
16796
|
metadata: initialMachineMetadata
|
|
16711
16797
|
});
|
|
@@ -16749,17 +16835,17 @@ async function runCodex(opts) {
|
|
|
16749
16835
|
agentStateVersion: parseInt(reconnectAgentStateVersion || "0", 10)
|
|
16750
16836
|
};
|
|
16751
16837
|
} else {
|
|
16752
|
-
response = await api
|
|
16838
|
+
response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
16753
16839
|
}
|
|
16754
16840
|
if (response) {
|
|
16755
16841
|
await claimSessionOrExit(response.id, { takeover: !!reconnectSessionId, flavor: "codex" });
|
|
16756
16842
|
}
|
|
16757
|
-
if (reconnectSessionId && response && !await api
|
|
16843
|
+
if (reconnectSessionId && response && !await api.reactivateSession(response.id)) {
|
|
16758
16844
|
throw new Error(`Failed to reactivate archived session ${response.id}`);
|
|
16759
16845
|
}
|
|
16760
16846
|
let reconnectSeeded = false;
|
|
16761
16847
|
if (reconnectSessionId && response) {
|
|
16762
|
-
const snapshot = await api
|
|
16848
|
+
const snapshot = await api.getSession(response.id, response.encryptionKey, response.encryptionVariant);
|
|
16763
16849
|
if (snapshot.ok) {
|
|
16764
16850
|
response = applyServerSnapshot(response, snapshot.session);
|
|
16765
16851
|
reconnectSeeded = true;
|
|
@@ -16776,7 +16862,7 @@ async function runCodex(opts) {
|
|
|
16776
16862
|
let abortInProgress = null;
|
|
16777
16863
|
let turnActive = false;
|
|
16778
16864
|
const { session: initialSession, reconnectionHandle } = setupOfflineReconnection({
|
|
16779
|
-
api
|
|
16865
|
+
api,
|
|
16780
16866
|
sessionTag,
|
|
16781
16867
|
metadata,
|
|
16782
16868
|
state,
|
|
@@ -16799,7 +16885,7 @@ async function runCodex(opts) {
|
|
|
16799
16885
|
if (response) {
|
|
16800
16886
|
try {
|
|
16801
16887
|
persistence.logger.debug(`[START] Reporting session ${response.id} to daemon`);
|
|
16802
|
-
const result = await
|
|
16888
|
+
const result = await index.notifyDaemonSessionStarted(response.id, metadata, {
|
|
16803
16889
|
encryptionKey: persistence.encodeBase64(response.encryptionKey),
|
|
16804
16890
|
encryptionVariant: response.encryptionVariant,
|
|
16805
16891
|
seq: response.seq,
|
|
@@ -16932,7 +17018,7 @@ async function runCodex(opts) {
|
|
|
16932
17018
|
const sendReady = () => {
|
|
16933
17019
|
session.sendSessionEvent({ type: "ready" });
|
|
16934
17020
|
try {
|
|
16935
|
-
api
|
|
17021
|
+
api.push().sendSessionNotification({
|
|
16936
17022
|
kind: "done",
|
|
16937
17023
|
metadata: session.getMetadata(),
|
|
16938
17024
|
data: {
|
|
@@ -17062,7 +17148,7 @@ async function runCodex(opts) {
|
|
|
17062
17148
|
}
|
|
17063
17149
|
process.stdin.setEncoding("utf8");
|
|
17064
17150
|
}
|
|
17065
|
-
client = new
|
|
17151
|
+
client = new index.CodexAppServerClient(sandboxConfig);
|
|
17066
17152
|
const idleRelease = new CodexIdleReleaseTimer({
|
|
17067
17153
|
delayMs: resolveCodexIdleReleaseMs(process.env[CODEX_IDLE_RELEASE_ENV]),
|
|
17068
17154
|
isIdle: () => shouldReleaseIdleCodex({
|
|
@@ -17621,7 +17707,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17621
17707
|
return;
|
|
17622
17708
|
} else if (subcommand === "install-terminal-hooks") {
|
|
17623
17709
|
try {
|
|
17624
|
-
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-
|
|
17710
|
+
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-DspFGxBI.cjs'); });
|
|
17625
17711
|
const command = parseTerminalHooksArgs(args.slice(1));
|
|
17626
17712
|
if (command.action === "help") {
|
|
17627
17713
|
console.log(TERMINAL_HOOKS_HELP);
|
|
@@ -17636,9 +17722,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17636
17722
|
}
|
|
17637
17723
|
process.exit(1);
|
|
17638
17724
|
}
|
|
17725
|
+
} else if (subcommand === "agent-home") {
|
|
17726
|
+
try {
|
|
17727
|
+
const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-CGl7FF7a.cjs'); });
|
|
17728
|
+
await runAgentHomeCommand(parseAgentHomeArgs(args.slice(1)));
|
|
17729
|
+
process.exit(0);
|
|
17730
|
+
} catch (error) {
|
|
17731
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
17732
|
+
process.exit(1);
|
|
17733
|
+
}
|
|
17639
17734
|
} else if (subcommand === "spawn") {
|
|
17640
17735
|
try {
|
|
17641
|
-
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-
|
|
17736
|
+
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-BqgZc7YE.cjs'); });
|
|
17642
17737
|
await handleSpawnCommand(args.slice(1));
|
|
17643
17738
|
} catch (error) {
|
|
17644
17739
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17650,7 +17745,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17650
17745
|
return;
|
|
17651
17746
|
} else if (subcommand === "sessions") {
|
|
17652
17747
|
try {
|
|
17653
|
-
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-
|
|
17748
|
+
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-WBowxNO-.cjs'); });
|
|
17654
17749
|
await handleSessionsCommand(args.slice(1));
|
|
17655
17750
|
} catch (error) {
|
|
17656
17751
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17662,7 +17757,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17662
17757
|
return;
|
|
17663
17758
|
} else if (subcommand === "send") {
|
|
17664
17759
|
try {
|
|
17665
|
-
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-
|
|
17760
|
+
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-LBjLfAos.cjs'); });
|
|
17666
17761
|
await handleSendCommand(args.slice(1));
|
|
17667
17762
|
} catch (error) {
|
|
17668
17763
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17768,9 +17863,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17768
17863
|
if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
|
|
17769
17864
|
const projectId = args[3];
|
|
17770
17865
|
try {
|
|
17771
|
-
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
17772
|
-
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-
|
|
17773
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-
|
|
17866
|
+
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-CPfFuHP6.cjs'); });
|
|
17867
|
+
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DGZB-OVl.cjs'); }).then(function (n) { return n.persistence; });
|
|
17868
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-Bqo68u61.cjs'); }).then(function (n) { return n.api; });
|
|
17774
17869
|
let userEmail = void 0;
|
|
17775
17870
|
try {
|
|
17776
17871
|
const credentials = await readCredentialsForConfiguredRelay2();
|
|
@@ -17801,7 +17896,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17801
17896
|
}
|
|
17802
17897
|
if (geminiSubcommand === "project" && args[2] === "get") {
|
|
17803
17898
|
try {
|
|
17804
|
-
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
17899
|
+
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-CPfFuHP6.cjs'); });
|
|
17805
17900
|
const config = readGeminiLocalConfig();
|
|
17806
17901
|
if (config.googleCloudProject) {
|
|
17807
17902
|
console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
|
|
@@ -17841,7 +17936,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17841
17936
|
process.exit(0);
|
|
17842
17937
|
}
|
|
17843
17938
|
try {
|
|
17844
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
17939
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-ChCHjnIh.cjs'); });
|
|
17845
17940
|
let startedBy = void 0;
|
|
17846
17941
|
for (let i = 1; i < args.length; i++) {
|
|
17847
17942
|
if (args[i] === "--started-by") {
|
|
@@ -17864,7 +17959,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17864
17959
|
} else if (subcommand === "pi") {
|
|
17865
17960
|
if (args[1] === "--terminal") {
|
|
17866
17961
|
try {
|
|
17867
|
-
const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-
|
|
17962
|
+
const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-6P00O_w8.cjs'); });
|
|
17868
17963
|
process.exitCode = await runPiTerminal(args.slice(2));
|
|
17869
17964
|
} catch (error) {
|
|
17870
17965
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17881,7 +17976,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17881
17976
|
}
|
|
17882
17977
|
};
|
|
17883
17978
|
try {
|
|
17884
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
17979
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-avfJgX4p.cjs'); });
|
|
17885
17980
|
const parsed = parsePiRunnerArgs(args.slice(1));
|
|
17886
17981
|
const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
|
|
17887
17982
|
const { credentials } = await authAndSetupMachineIfNeeded();
|
|
@@ -17909,7 +18004,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17909
18004
|
return;
|
|
17910
18005
|
} else if (subcommand === "acp") {
|
|
17911
18006
|
try {
|
|
17912
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
18007
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-avfJgX4p.cjs'); });
|
|
17913
18008
|
let startedBy = void 0;
|
|
17914
18009
|
let verbose = false;
|
|
17915
18010
|
const acpArgs = [];
|
|
@@ -17949,7 +18044,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17949
18044
|
return;
|
|
17950
18045
|
} else if (subcommand === "openclaw") {
|
|
17951
18046
|
try {
|
|
17952
|
-
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-
|
|
18047
|
+
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-Cc4_zmHz.cjs'); });
|
|
17953
18048
|
let startedBy = void 0;
|
|
17954
18049
|
let verbose = false;
|
|
17955
18050
|
let gatewayUrl;
|
|
@@ -18000,7 +18095,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18000
18095
|
return;
|
|
18001
18096
|
} else if (subcommand === "todo") {
|
|
18002
18097
|
try {
|
|
18003
|
-
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-
|
|
18098
|
+
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-DeouLiGa.cjs'); });
|
|
18004
18099
|
await handleTodoCommand(args.slice(1));
|
|
18005
18100
|
} catch (error) {
|
|
18006
18101
|
console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
|
|
@@ -18009,7 +18104,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18009
18104
|
return;
|
|
18010
18105
|
} else if (subcommand === "teams") {
|
|
18011
18106
|
try {
|
|
18012
|
-
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-
|
|
18107
|
+
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-eIUxGmqg.cjs'); });
|
|
18013
18108
|
await handleTeamsCommand(args.slice(1));
|
|
18014
18109
|
} catch (error) {
|
|
18015
18110
|
console.error(error instanceof Error ? error.message : "Teams command failed");
|
|
@@ -18018,7 +18113,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18018
18113
|
return;
|
|
18019
18114
|
} else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
|
|
18020
18115
|
try {
|
|
18021
|
-
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-
|
|
18116
|
+
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-BioGrTC_.cjs'); });
|
|
18022
18117
|
await handleMcpCommand(args[1] === "--terminal-tools");
|
|
18023
18118
|
} catch (error) {
|
|
18024
18119
|
process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -18041,7 +18136,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18041
18136
|
const daemonSubcommand = args[1];
|
|
18042
18137
|
if (daemonSubcommand === "list") {
|
|
18043
18138
|
try {
|
|
18044
|
-
const sessions = await
|
|
18139
|
+
const sessions = await index.listDaemonSessions();
|
|
18045
18140
|
if (sessions.length === 0) {
|
|
18046
18141
|
console.log("No active sessions this daemon is aware of (they might have been started by a previous version of the daemon)");
|
|
18047
18142
|
} else {
|
|
@@ -18059,7 +18154,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18059
18154
|
process.exit(1);
|
|
18060
18155
|
}
|
|
18061
18156
|
try {
|
|
18062
|
-
const success = await
|
|
18157
|
+
const success = await index.stopDaemonSession(sessionId);
|
|
18063
18158
|
console.log(success ? "Session stopped" : "Failed to stop session");
|
|
18064
18159
|
} catch (error) {
|
|
18065
18160
|
console.log("No daemon running");
|
|
@@ -18067,7 +18162,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18067
18162
|
return;
|
|
18068
18163
|
} else if (daemonSubcommand === "start") {
|
|
18069
18164
|
const currentState = await persistence.readDaemonState();
|
|
18070
|
-
const running = await
|
|
18165
|
+
const running = await index.checkIfDaemonRunningAndCleanupStaleState();
|
|
18071
18166
|
const currentDaemonMatches = currentState && currentState.startedWithCliVersion === persistence.configuration.currentCliVersion && daemonEndpointsMatch(
|
|
18072
18167
|
currentState.serverUrl,
|
|
18073
18168
|
currentState.webappUrl,
|
|
@@ -18076,7 +18171,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18076
18171
|
);
|
|
18077
18172
|
if (running && !currentDaemonMatches) {
|
|
18078
18173
|
console.log("Configured relay or CLI version changed; restarting the existing daemon");
|
|
18079
|
-
await
|
|
18174
|
+
await index.stopDaemon();
|
|
18080
18175
|
}
|
|
18081
18176
|
const child = spawnHappyCLI(["daemon", "start-sync"], {
|
|
18082
18177
|
detached: true,
|
|
@@ -18086,7 +18181,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18086
18181
|
child.unref();
|
|
18087
18182
|
let started = false;
|
|
18088
18183
|
for (let i = 0; i < 50; i++) {
|
|
18089
|
-
if (await
|
|
18184
|
+
if (await index.checkIfDaemonRunningAndCleanupStaleState()) {
|
|
18090
18185
|
started = true;
|
|
18091
18186
|
break;
|
|
18092
18187
|
}
|
|
@@ -18103,7 +18198,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18103
18198
|
await startDaemon();
|
|
18104
18199
|
process.exit(0);
|
|
18105
18200
|
} else if (daemonSubcommand === "stop") {
|
|
18106
|
-
await
|
|
18201
|
+
await index.stopDaemon();
|
|
18107
18202
|
process.exit(0);
|
|
18108
18203
|
} else if (daemonSubcommand === "status") {
|
|
18109
18204
|
await runDoctorDaemon();
|
|
@@ -18248,6 +18343,8 @@ ${chalk.bold("Usage:")}
|
|
|
18248
18343
|
very-happy install-terminal-hooks
|
|
18249
18344
|
Install optional Claude terminal mirror hooks
|
|
18250
18345
|
(add --remove to uninstall them)
|
|
18346
|
+
very-happy agent-home Show or pin where Claude Code / Codex keep their
|
|
18347
|
+
conversations on this machine (agent-home --help)
|
|
18251
18348
|
very-happy connect Connect AI vendor API keys
|
|
18252
18349
|
very-happy todo Built-in Todos and official agent skill (todo --help)
|
|
18253
18350
|
very-happy sandbox Configure and manage OS-level sandboxing
|
|
@@ -18363,9 +18460,9 @@ ${chalk.bold("Examples:")}
|
|
|
18363
18460
|
}
|
|
18364
18461
|
console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
|
|
18365
18462
|
try {
|
|
18366
|
-
const api
|
|
18463
|
+
const api = await index.ApiClient.create(credentials);
|
|
18367
18464
|
const notificationTitle = title || "Happy";
|
|
18368
|
-
api
|
|
18465
|
+
api.push().sendToAllDevices(
|
|
18369
18466
|
notificationTitle,
|
|
18370
18467
|
message,
|
|
18371
18468
|
{
|