very-happy-cli 0.2.138 → 0.2.140
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-BUoDXZnv.cjs → AcpBackend-C0Lq4uoA.cjs} +2 -2
- package/dist/{AcpBackend-bqVGMkPh.mjs → AcpBackend-DsSYq8T-.mjs} +2 -2
- package/dist/{AcpSessionManager-BBAQ8EQf.mjs → AcpSessionManager-BAErE9s0.mjs} +1 -1
- package/dist/{AcpSessionManager-DmmA52Ep.cjs → AcpSessionManager-DIqByTH7.cjs} +1 -1
- package/dist/{agentGuidance-4HV-BOge.cjs → agentGuidance-Dl3apItH.cjs} +1 -1
- package/dist/{agentGuidance-WHZsBKpm.mjs → agentGuidance-xk3v4_7E.mjs} +1 -1
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-BRXqgJJj.mjs → config-BVoHul9V.mjs} +4 -4
- package/dist/{config-DpuZN2Rs.cjs → config-D0-jnCOC.cjs} +4 -4
- package/dist/{index-DQe7ljLR.mjs → index-BNydKYvS.mjs} +7 -7
- package/dist/{index-D8Q3OhdR.mjs → index-BPnAbcz_.mjs} +4 -4
- package/dist/{index-CHP265gs.cjs → index-BuQn9kUK.cjs} +4 -4
- package/dist/{index-Dbl5U_N0.mjs → index-D0IP7gXG.mjs} +116 -232
- package/dist/{index-3N-qu-RG.cjs → index-DJvzGXVN.cjs} +7 -7
- package/dist/{index-CRRK5P1r.cjs → index-__h-pCTK.cjs} +147 -268
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{installTerminalHooks-DfUGgeyb.mjs → installTerminalHooks-D2ice1a-.mjs} +4 -4
- package/dist/{installTerminalHooks-CWGkoWDD.cjs → installTerminalHooks-D3jfvbyY.cjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +21 -0
- package/dist/lib.d.mts +21 -0
- package/dist/lib.mjs +2 -2
- package/dist/{limits-CfzVummd.cjs → limits-Dbip6eVO.cjs} +26 -7
- package/dist/{limits-DEsg_gHi.mjs → limits-nhD0Wran.mjs} +26 -8
- package/dist/{mcp-CN2UVfut.cjs → mcp-DeapmTWN.cjs} +21 -21
- package/dist/{mcp-UnjB3I8_.mjs → mcp-V26C05fN.mjs} +19 -19
- package/dist/{runGemini-BUVjTJKn.cjs → runGemini-D34eCFpz.cjs} +7 -7
- package/dist/{runGemini-BZ0xXRj-.mjs → runGemini-sa5kAgPe.mjs} +6 -6
- package/dist/{runOpenClaw-Bj2EgRHB.cjs → runOpenClaw-B8F8D--R.cjs} +6 -6
- package/dist/{runOpenClaw-BcUmnBpW.mjs → runOpenClaw-Bdsq8KBa.mjs} +5 -5
- package/dist/{send-BkypdBpK.cjs → send-DP_S3wn0.cjs} +4 -4
- package/dist/{send-snK_NGAA.mjs → send-KICC_ACz.mjs} +4 -4
- package/dist/{sessions-DxlC4C4g.mjs → sessions-C6EvnC4B.mjs} +4 -4
- package/dist/{sessions-9A-uoJL_.cjs → sessions-CGBBgVZn.cjs} +4 -4
- package/dist/{spawn-BORLpj60.cjs → spawn-BhUK0Vt2.cjs} +8 -8
- package/dist/{spawn-DFfHCafP.mjs → spawn-C1OqXJNb.mjs} +6 -6
- package/dist/{teams-9x2FO9RW.cjs → teams-C29nEHFD.cjs} +4 -4
- package/dist/{teams-Cfr-2Yd-.mjs → teams-Dm5HGpNz.mjs} +4 -4
- package/dist/{todo-CU5cjQZU.mjs → todo-C-vovA0N.mjs} +3 -3
- package/dist/{todo-DydKcknX.cjs → todo-CeJX0z_2.cjs} +3 -3
- package/dist/{types-CRxf_3n8.cjs → types-DCVS050m.cjs} +571 -13
- package/dist/{types-D5EsIAOe.mjs → types-thyvQB2a.mjs} +558 -13
- package/package.json +7 -7
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var os = require('node:os');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
|
-
var api = require('./types-
|
|
7
|
-
var persistence = require('./limits-
|
|
6
|
+
var api = require('./types-DCVS050m.cjs');
|
|
7
|
+
var persistence = require('./limits-Dbip6eVO.cjs');
|
|
8
8
|
var spawn = require('cross-spawn');
|
|
9
9
|
var path = require('node:path');
|
|
10
10
|
var node_readline = require('node:readline');
|
|
11
11
|
var node_fs = require('node:fs');
|
|
12
|
-
var agentGuidance = require('./agentGuidance-
|
|
12
|
+
var agentGuidance = require('./agentGuidance-Dl3apItH.cjs');
|
|
13
13
|
var promises = require('node:fs/promises');
|
|
14
14
|
var fs = require('fs/promises');
|
|
15
15
|
var ink = require('ink');
|
|
@@ -115,212 +115,6 @@ function withoutServerSnapshot(response) {
|
|
|
115
115
|
return { ...response, metadataVersion: 0, agentStateVersion: 0 };
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function daemonControlHeaders(controlToken) {
|
|
119
|
-
return {
|
|
120
|
-
"Content-Type": "application/json",
|
|
121
|
-
...controlToken ? { Authorization: `Bearer ${controlToken}` } : {}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
async function daemonPost(path, body) {
|
|
125
|
-
const state = await persistence.readDaemonState();
|
|
126
|
-
if (!state?.httpPort) {
|
|
127
|
-
const errorMessage = "No daemon running, no state file found";
|
|
128
|
-
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
129
|
-
return {
|
|
130
|
-
error: errorMessage
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
try {
|
|
134
|
-
process.kill(state.pid, 0);
|
|
135
|
-
} catch (error) {
|
|
136
|
-
const errorMessage = "Daemon is not running, file is stale";
|
|
137
|
-
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
138
|
-
return {
|
|
139
|
-
error: errorMessage
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
const timeout = process.env.HAPPY_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.HAPPY_DAEMON_HTTP_TIMEOUT) : 1e4;
|
|
144
|
-
const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
|
|
145
|
-
method: "POST",
|
|
146
|
-
// A state written by an old daemon has no token and must remain callable;
|
|
147
|
-
// a new daemon always writes one and rejects requests without it.
|
|
148
|
-
headers: daemonControlHeaders(state.controlToken),
|
|
149
|
-
body: JSON.stringify(body || {}),
|
|
150
|
-
// Mostly increased for stress test
|
|
151
|
-
signal: AbortSignal.timeout(timeout)
|
|
152
|
-
});
|
|
153
|
-
if (!response.ok) {
|
|
154
|
-
const failure = await response.json().catch(() => null);
|
|
155
|
-
const reason = typeof failure?.error === "string" && failure.error ? failure.error : `HTTP ${response.status}`;
|
|
156
|
-
const errorMessage = `Request failed: ${path}, ${reason}`;
|
|
157
|
-
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
158
|
-
return {
|
|
159
|
-
error: errorMessage
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
return await response.json();
|
|
163
|
-
} catch (error) {
|
|
164
|
-
const errorMessage = `Request failed: ${path}, ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
165
|
-
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
166
|
-
return {
|
|
167
|
-
error: errorMessage
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
const SESSION_STARTED_RETRY_TIMEOUT_MS = 3e3;
|
|
172
|
-
const SESSION_STARTED_RETRY_INTERVAL_MS = 100;
|
|
173
|
-
async function notifyDaemonSessionStarted(sessionId, metadata, encryption) {
|
|
174
|
-
const payload = { sessionId, metadata, encryption };
|
|
175
|
-
const deadline = Date.now() + SESSION_STARTED_RETRY_TIMEOUT_MS;
|
|
176
|
-
let result;
|
|
177
|
-
while (true) {
|
|
178
|
-
result = await daemonPost("/session-started", payload);
|
|
179
|
-
if (!result?.error) {
|
|
180
|
-
return result;
|
|
181
|
-
}
|
|
182
|
-
if (Date.now() >= deadline) {
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
await new Promise((resolve) => setTimeout(resolve, SESSION_STARTED_RETRY_INTERVAL_MS));
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
async function listDaemonSessions() {
|
|
189
|
-
const result = await daemonPost("/list");
|
|
190
|
-
return result.children || [];
|
|
191
|
-
}
|
|
192
|
-
async function stopDaemonSession(sessionId) {
|
|
193
|
-
const result = await daemonPost("/stop-session", { sessionId });
|
|
194
|
-
return result.success || false;
|
|
195
|
-
}
|
|
196
|
-
async function spawnDaemonSession(directory, sessionId, opts) {
|
|
197
|
-
const result = await daemonPost("/spawn-session", {
|
|
198
|
-
directory,
|
|
199
|
-
sessionId,
|
|
200
|
-
spawnedBy: opts?.spawnedBy,
|
|
201
|
-
permissionMode: opts?.permissionMode,
|
|
202
|
-
agent: opts?.agent,
|
|
203
|
-
environmentVariables: opts?.environmentVariables
|
|
204
|
-
});
|
|
205
|
-
return result;
|
|
206
|
-
}
|
|
207
|
-
async function notifyDaemonSessionEvent(sessionId, event, spawnedBy) {
|
|
208
|
-
return daemonPost("/session-event", { sessionId, event, spawnedBy });
|
|
209
|
-
}
|
|
210
|
-
async function notifyDaemonClaudeAuthFailed(sessionId) {
|
|
211
|
-
return daemonPost("/session-event", { sessionId, event: "auth_failed" });
|
|
212
|
-
}
|
|
213
|
-
async function stopDaemonHttp() {
|
|
214
|
-
await daemonPost("/stop");
|
|
215
|
-
}
|
|
216
|
-
async function pushClipboardViaDaemon(text) {
|
|
217
|
-
const result = await daemonPost("/clipboard", { text });
|
|
218
|
-
if (result?.error && result.delivered === void 0) {
|
|
219
|
-
return { delivered: false, error: result.error };
|
|
220
|
-
}
|
|
221
|
-
return result;
|
|
222
|
-
}
|
|
223
|
-
async function setTerminalTitleViaDaemon(terminalId, title, ifAbsent = false) {
|
|
224
|
-
const result = await daemonPost("/terminal-title", { terminalId, title, ifAbsent });
|
|
225
|
-
if (result?.error) {
|
|
226
|
-
return { ok: false, error: result.error };
|
|
227
|
-
}
|
|
228
|
-
return { ok: result?.status === "ok", error: result?.status === "ok" ? void 0 : "unexpected daemon response" };
|
|
229
|
-
}
|
|
230
|
-
async function checkIfDaemonRunningAndCleanupStaleState() {
|
|
231
|
-
const state = await persistence.readDaemonState();
|
|
232
|
-
if (!state) {
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
try {
|
|
236
|
-
process.kill(state.pid, 0);
|
|
237
|
-
} catch {
|
|
238
|
-
persistence.logger.debug("[DAEMON RUN] Daemon PID not running, cleaning up state");
|
|
239
|
-
await cleanupDaemonState();
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
if (state.httpPort) {
|
|
243
|
-
try {
|
|
244
|
-
const response = await fetch(`http://127.0.0.1:${state.httpPort}/list`, {
|
|
245
|
-
method: "POST",
|
|
246
|
-
headers: daemonControlHeaders(state.controlToken),
|
|
247
|
-
body: "{}",
|
|
248
|
-
signal: AbortSignal.timeout(2e3)
|
|
249
|
-
});
|
|
250
|
-
if (response.ok) {
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
} catch {
|
|
254
|
-
persistence.logger.debug(`[DAEMON RUN] PID ${state.pid} is alive but HTTP health check failed on port ${state.httpPort}, cleaning up stale state`);
|
|
255
|
-
await cleanupDaemonState();
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
261
|
-
async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
262
|
-
persistence.logger.debug("[DAEMON CONTROL] Checking if daemon is running same version");
|
|
263
|
-
const runningDaemon = await checkIfDaemonRunningAndCleanupStaleState();
|
|
264
|
-
if (!runningDaemon) {
|
|
265
|
-
persistence.logger.debug("[DAEMON CONTROL] No daemon running, returning false");
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
const state = await persistence.readDaemonState();
|
|
269
|
-
if (!state) {
|
|
270
|
-
persistence.logger.debug("[DAEMON CONTROL] No daemon state found, returning false");
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
const currentCliVersion = persistence.configuration.currentCliVersion;
|
|
274
|
-
persistence.logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
275
|
-
return currentCliVersion === state.startedWithCliVersion;
|
|
276
|
-
}
|
|
277
|
-
async function cleanupDaemonState() {
|
|
278
|
-
try {
|
|
279
|
-
await persistence.clearDaemonState();
|
|
280
|
-
persistence.logger.debug("[DAEMON RUN] Daemon state file removed");
|
|
281
|
-
} catch (error) {
|
|
282
|
-
persistence.logger.debug("[DAEMON RUN] Error cleaning up daemon metadata", error);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
async function stopDaemon() {
|
|
286
|
-
try {
|
|
287
|
-
const state = await persistence.readDaemonState();
|
|
288
|
-
if (!state) {
|
|
289
|
-
persistence.logger.debug("No daemon state found");
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
persistence.logger.debug(`Stopping daemon with PID ${state.pid}`);
|
|
293
|
-
try {
|
|
294
|
-
await stopDaemonHttp();
|
|
295
|
-
await waitForProcessDeath(state.pid, 2e3);
|
|
296
|
-
persistence.logger.debug("Daemon stopped gracefully via HTTP");
|
|
297
|
-
return;
|
|
298
|
-
} catch (error) {
|
|
299
|
-
persistence.logger.debug("HTTP stop failed, will force kill", error);
|
|
300
|
-
}
|
|
301
|
-
try {
|
|
302
|
-
process.kill(state.pid, "SIGKILL");
|
|
303
|
-
persistence.logger.debug("Force killed daemon");
|
|
304
|
-
} catch (error) {
|
|
305
|
-
persistence.logger.debug("Daemon already dead");
|
|
306
|
-
}
|
|
307
|
-
} catch (error) {
|
|
308
|
-
persistence.logger.debug("Error stopping daemon", error);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
async function waitForProcessDeath(pid, timeout) {
|
|
312
|
-
const start = Date.now();
|
|
313
|
-
while (Date.now() - start < timeout) {
|
|
314
|
-
try {
|
|
315
|
-
process.kill(pid, 0);
|
|
316
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
317
|
-
} catch {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
throw new Error("Process did not die within timeout");
|
|
322
|
-
}
|
|
323
|
-
|
|
324
118
|
class Session {
|
|
325
119
|
path;
|
|
326
120
|
logPath;
|
|
@@ -493,7 +287,7 @@ class Session {
|
|
|
493
287
|
reportEventToDaemon = (event) => {
|
|
494
288
|
const spawnedBy = process.env.HAPPY_SPAWNED_BY;
|
|
495
289
|
if (!spawnedBy) return;
|
|
496
|
-
void notifyDaemonSessionEvent(this.client.sessionId, event, spawnedBy).catch((error) => persistence.logger.debug("[Session] Failed to report session event to daemon:", error));
|
|
290
|
+
void api.notifyDaemonSessionEvent(this.client.sessionId, event, spawnedBy).catch((error) => persistence.logger.debug("[Session] Failed to report session event to daemon:", error));
|
|
497
291
|
};
|
|
498
292
|
/** Produce an `error` notification for an unexpected session failure. */
|
|
499
293
|
onSessionError = (message) => {
|
|
@@ -3924,6 +3718,7 @@ function applyClaudeSdkMetadata(current, update) {
|
|
|
3924
3718
|
function createTurnSteeringController() {
|
|
3925
3719
|
let interrupt = null;
|
|
3926
3720
|
let steering = false;
|
|
3721
|
+
let interrupting = false;
|
|
3927
3722
|
return {
|
|
3928
3723
|
setInterrupt(next) {
|
|
3929
3724
|
interrupt = next;
|
|
@@ -3948,17 +3743,31 @@ function createTurnSteeringController() {
|
|
|
3948
3743
|
*/
|
|
3949
3744
|
async interruptTurn() {
|
|
3950
3745
|
if (!interrupt) return false;
|
|
3951
|
-
|
|
3952
|
-
|
|
3746
|
+
interrupting = true;
|
|
3747
|
+
try {
|
|
3748
|
+
await interrupt();
|
|
3749
|
+
return true;
|
|
3750
|
+
} catch (error) {
|
|
3751
|
+
interrupting = false;
|
|
3752
|
+
throw error;
|
|
3753
|
+
}
|
|
3953
3754
|
},
|
|
3954
3755
|
consumeReady() {
|
|
3955
3756
|
if (!steering) return false;
|
|
3956
3757
|
steering = false;
|
|
3957
3758
|
return true;
|
|
3958
3759
|
},
|
|
3760
|
+
/** The next `result` belongs to a stop-button interrupt: close the
|
|
3761
|
+
* turn as cancelled and say "Aborted by user", not "failed". */
|
|
3762
|
+
consumeInterrupted() {
|
|
3763
|
+
if (!interrupting) return false;
|
|
3764
|
+
interrupting = false;
|
|
3765
|
+
return true;
|
|
3766
|
+
},
|
|
3959
3767
|
reset() {
|
|
3960
3768
|
interrupt = null;
|
|
3961
3769
|
steering = false;
|
|
3770
|
+
interrupting = false;
|
|
3962
3771
|
}
|
|
3963
3772
|
};
|
|
3964
3773
|
}
|
|
@@ -4120,6 +3929,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4120
3929
|
return;
|
|
4121
3930
|
}
|
|
4122
3931
|
persistence.logger.debug("[remote]: interrupt did not stop the turn in time; escalating to hard abort");
|
|
3932
|
+
turnSteering.consumeInterrupted();
|
|
4123
3933
|
}
|
|
4124
3934
|
await abort();
|
|
4125
3935
|
}
|
|
@@ -4442,7 +4252,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4442
4252
|
},
|
|
4443
4253
|
onAuthFailure: (reason) => {
|
|
4444
4254
|
session.client.sendSessionEvent({ type: "message", message: `Claude Code auth: ${reason}`, kind: "claude-auth-failed" });
|
|
4445
|
-
void notifyDaemonClaudeAuthFailed(session.client.sessionId).catch((error) => persistence.logger.debug("[remote]: auth_failed report to daemon failed:", error));
|
|
4255
|
+
void api.notifyDaemonClaudeAuthFailed(session.client.sessionId).catch((error) => persistence.logger.debug("[remote]: auth_failed report to daemon failed:", error));
|
|
4446
4256
|
},
|
|
4447
4257
|
onSessionReset: () => {
|
|
4448
4258
|
persistence.logger.debug("[remote]: Session reset");
|
|
@@ -4453,6 +4263,11 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4453
4263
|
session.client.closeClaudeSessionTurn("cancelled");
|
|
4454
4264
|
return;
|
|
4455
4265
|
}
|
|
4266
|
+
if (turnSteering.consumeInterrupted()) {
|
|
4267
|
+
session.client.closeClaudeSessionTurn("cancelled");
|
|
4268
|
+
session.client.sendSessionEvent({ type: "message", message: "Aborted by user" });
|
|
4269
|
+
return;
|
|
4270
|
+
}
|
|
4456
4271
|
applyClaudeResultLifecycle(result, {
|
|
4457
4272
|
closeCompleted: () => session.client.closeClaudeSessionTurn("completed"),
|
|
4458
4273
|
closeFailed: (error) => session.client.closeClaudeSessionTurn("failed", { error }),
|
|
@@ -5313,7 +5128,7 @@ function getLogFiles(logDir) {
|
|
|
5313
5128
|
async function runDoctorDaemon() {
|
|
5314
5129
|
console.log(chalk.bold("\n\u{1F916} Daemon Status"));
|
|
5315
5130
|
try {
|
|
5316
|
-
const isRunning = await checkIfDaemonRunningAndCleanupStaleState();
|
|
5131
|
+
const isRunning = await api.checkIfDaemonRunningAndCleanupStaleState();
|
|
5317
5132
|
const state = await persistence.readDaemonState();
|
|
5318
5133
|
if (isRunning && state) {
|
|
5319
5134
|
console.log(chalk.green("\u2713 Daemon is running"));
|
|
@@ -5516,7 +5331,7 @@ ${typeLabels[type] || type}:`));
|
|
|
5516
5331
|
}
|
|
5517
5332
|
console.log(chalk.bold("\n\u{1F916} Daemon Status"));
|
|
5518
5333
|
try {
|
|
5519
|
-
const isRunning = await checkIfDaemonRunningAndCleanupStaleState();
|
|
5334
|
+
const isRunning = await api.checkIfDaemonRunningAndCleanupStaleState();
|
|
5520
5335
|
const state = await persistence.readDaemonState();
|
|
5521
5336
|
const summary = daemonReadiness(authenticated, isRunning, Boolean(state));
|
|
5522
5337
|
if (summary.level === "ready" && state) {
|
|
@@ -6281,6 +6096,7 @@ function startDaemonControlServer({
|
|
|
6281
6096
|
onHappySessionWebhook,
|
|
6282
6097
|
onSessionStateEvent,
|
|
6283
6098
|
onClaudeAuthFailed,
|
|
6099
|
+
onSessionTurnEvent,
|
|
6284
6100
|
pushClipboard,
|
|
6285
6101
|
onTerminalHook,
|
|
6286
6102
|
setTerminalTitle
|
|
@@ -6337,7 +6153,7 @@ function startDaemonControlServer({
|
|
|
6337
6153
|
schema: {
|
|
6338
6154
|
body: z.z.object({
|
|
6339
6155
|
sessionId: z.z.string(),
|
|
6340
|
-
event: z.z.enum(["completed", "needs_input", "auth_failed"]),
|
|
6156
|
+
event: z.z.enum(["completed", "needs_input", "auth_failed", "turn_started", "turn_ended"]),
|
|
6341
6157
|
spawnedBy: z.z.string().optional()
|
|
6342
6158
|
}),
|
|
6343
6159
|
response: {
|
|
@@ -6353,6 +6169,10 @@ function startDaemonControlServer({
|
|
|
6353
6169
|
onClaudeAuthFailed?.(sessionId);
|
|
6354
6170
|
return { status: "ok" };
|
|
6355
6171
|
}
|
|
6172
|
+
if (event === "turn_started" || event === "turn_ended") {
|
|
6173
|
+
onSessionTurnEvent?.(sessionId, event);
|
|
6174
|
+
return { status: "ok" };
|
|
6175
|
+
}
|
|
6356
6176
|
onSessionStateEvent?.(sessionId, event, spawnedBy);
|
|
6357
6177
|
return { status: "ok" };
|
|
6358
6178
|
});
|
|
@@ -8055,7 +7875,7 @@ function parseJsonObject(text) {
|
|
|
8055
7875
|
return null;
|
|
8056
7876
|
}
|
|
8057
7877
|
}
|
|
8058
|
-
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-
|
|
7878
|
+
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-__h-pCTK.cjs', document.baseURI).href))).resolve(s)) {
|
|
8059
7879
|
const platformArch = `${platform}-${arch}`;
|
|
8060
7880
|
const candidates = [
|
|
8061
7881
|
`@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
|
|
@@ -9670,7 +9490,7 @@ function toSummary(id, persisted, extra) {
|
|
|
9670
9490
|
};
|
|
9671
9491
|
}
|
|
9672
9492
|
async function listSessions(options = {}) {
|
|
9673
|
-
const live = await listDaemonSessions();
|
|
9493
|
+
const live = await api.listDaemonSessions();
|
|
9674
9494
|
return mergeSessionSummaries(live, persistence.readPersistedSessions(), options);
|
|
9675
9495
|
}
|
|
9676
9496
|
function summarizeAccountSession(row, persisted, liveIds, now) {
|
|
@@ -9740,7 +9560,7 @@ async function listAccountSessions(options = {}) {
|
|
|
9740
9560
|
});
|
|
9741
9561
|
const rows = Array.isArray(response.data?.sessions) ? response.data.sessions : [];
|
|
9742
9562
|
const persisted = persistence.readPersistedSessions();
|
|
9743
|
-
const live = await listDaemonSessions();
|
|
9563
|
+
const live = await api.listDaemonSessions();
|
|
9744
9564
|
const liveIds = new Set(live.map((child) => child.happySessionId).filter((id) => typeof id === "string"));
|
|
9745
9565
|
const now = Date.now();
|
|
9746
9566
|
const summaries = rows.filter((row) => typeof row?.id === "string").map((row) => summarizeAccountSession(row, persisted[row.id], liveIds, now)).filter((summary) => options.includeArchived || !summary.archived);
|
|
@@ -9774,7 +9594,7 @@ async function readSessionTranscript(sessionId, limit) {
|
|
|
9774
9594
|
const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
|
|
9775
9595
|
messages.reverse();
|
|
9776
9596
|
const key = persistence.decodeBase64(persisted.encryptionKey);
|
|
9777
|
-
const live = await listDaemonSessions();
|
|
9597
|
+
const live = await api.listDaemonSessions();
|
|
9778
9598
|
const bodies = messages.map((message) => {
|
|
9779
9599
|
if (message.content?.t !== "encrypted") return null;
|
|
9780
9600
|
try {
|
|
@@ -9790,7 +9610,7 @@ async function readSessionTranscript(sessionId, limit) {
|
|
|
9790
9610
|
};
|
|
9791
9611
|
}
|
|
9792
9612
|
async function stopSession(sessionId) {
|
|
9793
|
-
return await stopDaemonSession(sessionId);
|
|
9613
|
+
return await api.stopDaemonSession(sessionId);
|
|
9794
9614
|
}
|
|
9795
9615
|
async function archiveSession(sessionId) {
|
|
9796
9616
|
const token = await bearerToken();
|
|
@@ -10218,7 +10038,7 @@ async function startDaemon() {
|
|
|
10218
10038
|
});
|
|
10219
10039
|
persistence.logger.debug("[DAEMON RUN] Starting daemon process...");
|
|
10220
10040
|
persistence.logger.debugLargeJson("[DAEMON RUN] Environment", getEnvironmentInfo());
|
|
10221
|
-
const runningDaemonVersionMatches = await isDaemonRunningCurrentlyInstalledHappyVersion();
|
|
10041
|
+
const runningDaemonVersionMatches = await api.isDaemonRunningCurrentlyInstalledHappyVersion();
|
|
10222
10042
|
const runningDaemonState = await persistence.readDaemonState();
|
|
10223
10043
|
const runningDaemonEndpointsMatch = Boolean(runningDaemonState) && daemonEndpointsMatch(
|
|
10224
10044
|
runningDaemonState?.serverUrl,
|
|
@@ -10228,7 +10048,7 @@ async function startDaemon() {
|
|
|
10228
10048
|
);
|
|
10229
10049
|
if (!runningDaemonVersionMatches || !runningDaemonEndpointsMatch) {
|
|
10230
10050
|
persistence.logger.debug("[DAEMON RUN] Daemon version or endpoint mismatch detected, restarting daemon");
|
|
10231
|
-
await stopDaemon();
|
|
10051
|
+
await api.stopDaemon();
|
|
10232
10052
|
} else {
|
|
10233
10053
|
persistence.logger.debug("[DAEMON RUN] Daemon version matches, keeping existing daemon");
|
|
10234
10054
|
console.log("Daemon already running with matching version");
|
|
@@ -10247,6 +10067,7 @@ async function startDaemon() {
|
|
|
10247
10067
|
const { credentials, machineId } = await authAndSetupMachineIfNeeded();
|
|
10248
10068
|
persistence.logger.debug("[DAEMON RUN] Auth and machine setup complete");
|
|
10249
10069
|
const pidToTrackedSession = /* @__PURE__ */ new Map();
|
|
10070
|
+
const turnActivity = new api.TurnActivityTracker();
|
|
10250
10071
|
const sessionIdToFinishedSession = /* @__PURE__ */ new Map();
|
|
10251
10072
|
let teamWorker;
|
|
10252
10073
|
const persisted = persistence.readPersistedSessions();
|
|
@@ -10518,6 +10339,9 @@ async function startDaemon() {
|
|
|
10518
10339
|
if (options.resumeCodexThreadId) {
|
|
10519
10340
|
extraEnv.HAPPY_FORK_CODEX_THREAD_ID = options.resumeCodexThreadId;
|
|
10520
10341
|
}
|
|
10342
|
+
if (options.importCodexThreadId && UUID_RE.test(options.importCodexThreadId)) {
|
|
10343
|
+
extraEnv.HAPPY_IMPORT_CODEX_THREAD_ID = options.importCodexThreadId;
|
|
10344
|
+
}
|
|
10521
10345
|
if (options.variant === "assistant") {
|
|
10522
10346
|
extraEnv.HAPPY_SESSION_VARIANT = "assistant";
|
|
10523
10347
|
}
|
|
@@ -11082,6 +10906,7 @@ async function startDaemon() {
|
|
|
11082
10906
|
const onChildExited = (pid) => {
|
|
11083
10907
|
const session = pidToTrackedSession.get(pid);
|
|
11084
10908
|
if (session?.happySessionId && (session.spawnedBy === "teams" || teamWorker?.hasSession(session.happySessionId))) teamWorker?.report(session.happySessionId, "exited");
|
|
10909
|
+
if (session?.happySessionId) turnActivity.forget(session.happySessionId);
|
|
11085
10910
|
if (session?.happySessionId && session.encryption) {
|
|
11086
10911
|
sessionIdToFinishedSession.set(session.happySessionId, session);
|
|
11087
10912
|
persistence.logger.debug(`[DAEMON RUN] Process PID ${pid} exited, preserved session ${session.happySessionId} for resume`);
|
|
@@ -11163,6 +10988,7 @@ async function startDaemon() {
|
|
|
11163
10988
|
onHappySessionWebhook,
|
|
11164
10989
|
onSessionStateEvent,
|
|
11165
10990
|
onClaudeAuthFailed: (sessionId) => claudeAuthServiceRef?.signalAuthFailed(sessionId),
|
|
10991
|
+
onSessionTurnEvent: (sessionId, event) => turnActivity.apply(sessionId, event),
|
|
11166
10992
|
pushClipboard: (text) => {
|
|
11167
10993
|
if (!apiMachineRef) {
|
|
11168
10994
|
return { delivered: false, truncated: false, totalBytes: 0, error: "daemon is still starting up" };
|
|
@@ -11275,7 +11101,10 @@ async function startDaemon() {
|
|
|
11275
11101
|
const updateController = createUpdateController({
|
|
11276
11102
|
policy: () => api.fetchCliUpdateState(persistence.configuration.serverUrl, persistence.packageJson.version),
|
|
11277
11103
|
enabled: async () => ((await persistence.readSettings()).cliAutoUpdate ?? "idle") !== "off",
|
|
11278
|
-
|
|
11104
|
+
// B-466: "idle" = no agent turn in flight. Idle wrappers survive a
|
|
11105
|
+
// handover (they talk to the server themselves) and web terminals live
|
|
11106
|
+
// in tmux, so neither is a reason to hold an update.
|
|
11107
|
+
idle: () => !turnActivity.hasActiveTurn(),
|
|
11279
11108
|
install: installCliSafely,
|
|
11280
11109
|
publish: (state) => {
|
|
11281
11110
|
cliUpdateStateRef = state;
|
|
@@ -11330,7 +11159,7 @@ async function startDaemon() {
|
|
|
11330
11159
|
} catch {
|
|
11331
11160
|
}
|
|
11332
11161
|
}
|
|
11333
|
-
if (bundleReplaced && !teamWorker?.busy && !updateController.isRunning() &&
|
|
11162
|
+
if (bundleReplaced && !teamWorker?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
|
|
11334
11163
|
const preflight = await preflightNewBundle(bundlePath);
|
|
11335
11164
|
if (preflight.action === "hold") {
|
|
11336
11165
|
if (lastHandoverHold !== preflight.reason) {
|
|
@@ -11340,7 +11169,7 @@ async function startDaemon() {
|
|
|
11340
11169
|
updateController.holdHandover(preflight.reason);
|
|
11341
11170
|
return;
|
|
11342
11171
|
}
|
|
11343
|
-
if (teamWorker?.busy || updateController.isRunning() ||
|
|
11172
|
+
if (teamWorker?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
|
|
11344
11173
|
lastHandoverHold = null;
|
|
11345
11174
|
persistence.logger.debug("[DAEMON RUN] Daemon bundle replaced on disk and verified, handing off to new daemon");
|
|
11346
11175
|
teamWorker?.stop();
|
|
@@ -11350,7 +11179,7 @@ async function startDaemon() {
|
|
|
11350
11179
|
claudeAuthService.stop();
|
|
11351
11180
|
apiMachine.shutdown();
|
|
11352
11181
|
await stopControlServer();
|
|
11353
|
-
await cleanupDaemonState();
|
|
11182
|
+
await api.cleanupDaemonState();
|
|
11354
11183
|
await persistence.releaseDaemonLock(daemonLockHandle);
|
|
11355
11184
|
await stopCaffeinate();
|
|
11356
11185
|
try {
|
|
@@ -11423,7 +11252,7 @@ async function startDaemon() {
|
|
|
11423
11252
|
}
|
|
11424
11253
|
apiMachine.shutdown();
|
|
11425
11254
|
await stopControlServer();
|
|
11426
|
-
await cleanupDaemonState();
|
|
11255
|
+
await api.cleanupDaemonState();
|
|
11427
11256
|
await stopCaffeinate();
|
|
11428
11257
|
await persistence.releaseDaemonLock(daemonLockHandle);
|
|
11429
11258
|
persistence.logger.debug("[DAEMON RUN] Cleanup completed, exiting process");
|
|
@@ -11731,7 +11560,7 @@ ${body}`);
|
|
|
11731
11560
|
if (!normalized.ok) return fail(normalized.error);
|
|
11732
11561
|
const directory = normalized.directory;
|
|
11733
11562
|
try {
|
|
11734
|
-
const result = await spawnDaemonSession(directory, void 0, { spawnedBy: "assistant" });
|
|
11563
|
+
const result = await api.spawnDaemonSession(directory, void 0, { spawnedBy: "assistant" });
|
|
11735
11564
|
if (result?.error || !result?.sessionId) {
|
|
11736
11565
|
return fail(`Failed to spawn session: ${result?.error ?? "daemon returned no session id"}`);
|
|
11737
11566
|
}
|
|
@@ -12498,7 +12327,7 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12498
12327
|
const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
|
|
12499
12328
|
const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
|
|
12500
12329
|
const run = deps.run ?? (async (input) => {
|
|
12501
|
-
const { query } = await Promise.resolve().then(function () { return require('./index-
|
|
12330
|
+
const { query } = await Promise.resolve().then(function () { return require('./index-BuQn9kUK.cjs'); });
|
|
12502
12331
|
return runSideQuestion(query, input);
|
|
12503
12332
|
});
|
|
12504
12333
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12600,7 +12429,7 @@ const MAX_TITLE_CHARS = 60;
|
|
|
12600
12429
|
const GENERATION_TIMEOUT_MS = 3e4;
|
|
12601
12430
|
function resolveClaudeBinary() {
|
|
12602
12431
|
try {
|
|
12603
|
-
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-
|
|
12432
|
+
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-__h-pCTK.cjs', document.baseURI).href)));
|
|
12604
12433
|
const utilsPath = path.resolve(path.join(api.projectPath(), "scripts", "claude_version_utils.cjs"));
|
|
12605
12434
|
const { getClaudeCliPath } = require$1(utilsPath);
|
|
12606
12435
|
const path$1 = getClaudeCliPath();
|
|
@@ -13162,7 +12991,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
13162
12991
|
}
|
|
13163
12992
|
try {
|
|
13164
12993
|
persistence.logger.debug(`[START] Reporting session ${response.id} to daemon`);
|
|
13165
|
-
const result = await notifyDaemonSessionStarted(response.id, metadata, {
|
|
12994
|
+
const result = await api.notifyDaemonSessionStarted(response.id, metadata, {
|
|
13166
12995
|
encryptionKey: persistence.encodeBase64(response.encryptionKey),
|
|
13167
12996
|
encryptionVariant: response.encryptionVariant,
|
|
13168
12997
|
seq: response.seq,
|
|
@@ -13890,7 +13719,7 @@ async function handleAuthLogin(args) {
|
|
|
13890
13719
|
console.log(chalk.gray(" \u2022 Re-authenticate and register machine\n"));
|
|
13891
13720
|
try {
|
|
13892
13721
|
persistence.logger.debug("Stopping daemon for force auth...");
|
|
13893
|
-
await stopDaemon();
|
|
13722
|
+
await api.stopDaemon();
|
|
13894
13723
|
console.log(chalk.gray("\u2713 Stopped daemon"));
|
|
13895
13724
|
} catch (error) {
|
|
13896
13725
|
persistence.logger.debug("Daemon was not running or failed to stop:", error);
|
|
@@ -13955,7 +13784,7 @@ async function handleAuthLogout() {
|
|
|
13955
13784
|
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
13956
13785
|
try {
|
|
13957
13786
|
try {
|
|
13958
|
-
await stopDaemon();
|
|
13787
|
+
await api.stopDaemon();
|
|
13959
13788
|
console.log(chalk.gray("Stopped daemon"));
|
|
13960
13789
|
} catch {
|
|
13961
13790
|
}
|
|
@@ -14000,7 +13829,7 @@ async function handleAuthStatus() {
|
|
|
14000
13829
|
console.log(chalk.gray(`
|
|
14001
13830
|
Data directory: ${persistence.configuration.happyHomeDir}`));
|
|
14002
13831
|
try {
|
|
14003
|
-
const running = await checkIfDaemonRunningAndCleanupStaleState();
|
|
13832
|
+
const running = await api.checkIfDaemonRunningAndCleanupStaleState();
|
|
14004
13833
|
if (running) {
|
|
14005
13834
|
console.log(chalk.green("\u2713 Daemon running"));
|
|
14006
13835
|
} else {
|
|
@@ -14826,9 +14655,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
|
|
|
14826
14655
|
};
|
|
14827
14656
|
}
|
|
14828
14657
|
|
|
14829
|
-
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-
|
|
14658
|
+
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-__h-pCTK.cjs', document.baseURI).href)));
|
|
14830
14659
|
const __dirname$1 = path.dirname(__filename$1);
|
|
14831
|
-
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
14660
|
+
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-__h-pCTK.cjs', document.baseURI).href)));
|
|
14832
14661
|
const PRISMA_QUERY_ENGINE_FILES = {
|
|
14833
14662
|
"arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
|
|
14834
14663
|
"x64-darwin": "libquery_engine-darwin.dylib.node",
|
|
@@ -15217,7 +15046,7 @@ const DAEMON_READY_TIMEOUT_MS = 5e3;
|
|
|
15217
15046
|
const DAEMON_READY_POLL_INTERVAL_MS = 100;
|
|
15218
15047
|
async function ensureDaemonRunning() {
|
|
15219
15048
|
persistence.logger.debug("Ensuring Happy background service is running & matches our version...");
|
|
15220
|
-
const versionMatches = await isDaemonRunningCurrentlyInstalledHappyVersion();
|
|
15049
|
+
const versionMatches = await api.isDaemonRunningCurrentlyInstalledHappyVersion();
|
|
15221
15050
|
const state = await persistence.readDaemonState();
|
|
15222
15051
|
const endpointsMatch = Boolean(state) && daemonEndpointsMatch(
|
|
15223
15052
|
state?.serverUrl,
|
|
@@ -15228,9 +15057,9 @@ async function ensureDaemonRunning() {
|
|
|
15228
15057
|
if (versionMatches && endpointsMatch) {
|
|
15229
15058
|
return;
|
|
15230
15059
|
}
|
|
15231
|
-
if (await checkIfDaemonRunningAndCleanupStaleState()) {
|
|
15060
|
+
if (await api.checkIfDaemonRunningAndCleanupStaleState()) {
|
|
15232
15061
|
persistence.logger.debug("Stopping daemon whose version or configured relay no longer matches...");
|
|
15233
|
-
await stopDaemon();
|
|
15062
|
+
await api.stopDaemon();
|
|
15234
15063
|
}
|
|
15235
15064
|
persistence.logger.debug("Starting Happy background service...");
|
|
15236
15065
|
const daemonProcess = spawnHappyCLI(["daemon", "start-sync"], {
|
|
@@ -15241,7 +15070,7 @@ async function ensureDaemonRunning() {
|
|
|
15241
15070
|
daemonProcess.unref();
|
|
15242
15071
|
const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
|
|
15243
15072
|
while (Date.now() < deadline) {
|
|
15244
|
-
if (await checkIfDaemonRunningAndCleanupStaleState()) {
|
|
15073
|
+
if (await api.checkIfDaemonRunningAndCleanupStaleState()) {
|
|
15245
15074
|
persistence.logger.debug("Happy background service is ready");
|
|
15246
15075
|
return;
|
|
15247
15076
|
}
|
|
@@ -16594,6 +16423,43 @@ async function resumeExistingThread(opts) {
|
|
|
16594
16423
|
}
|
|
16595
16424
|
}
|
|
16596
16425
|
|
|
16426
|
+
const IMPORT_CODEX_THREAD_ENV = "HAPPY_IMPORT_CODEX_THREAD_ID";
|
|
16427
|
+
async function importCodexThread(opts) {
|
|
16428
|
+
let forked;
|
|
16429
|
+
try {
|
|
16430
|
+
forked = await opts.client.forkThread({
|
|
16431
|
+
threadId: opts.threadId,
|
|
16432
|
+
cwd: opts.cwd,
|
|
16433
|
+
mcpServers: opts.mcpServers
|
|
16434
|
+
});
|
|
16435
|
+
} catch (error) {
|
|
16436
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
16437
|
+
throw new Error(`Failed to import Codex thread ${opts.threadId}: ${reason}`);
|
|
16438
|
+
}
|
|
16439
|
+
if (!forked.threadId || forked.threadId === opts.threadId) {
|
|
16440
|
+
throw new Error(`Failed to import Codex thread ${opts.threadId}: Codex did not create an independent copy`);
|
|
16441
|
+
}
|
|
16442
|
+
opts.session.updateMetadata((currentMetadata) => ({
|
|
16443
|
+
...currentMetadata,
|
|
16444
|
+
codexThreadId: forked.threadId,
|
|
16445
|
+
importedFromCodexThreadId: opts.threadId
|
|
16446
|
+
}));
|
|
16447
|
+
let thread = forked.thread;
|
|
16448
|
+
if (!Array.isArray(thread?.turns)) {
|
|
16449
|
+
thread = (await opts.client.readThread({ threadId: forked.threadId, includeTurns: true })).thread;
|
|
16450
|
+
}
|
|
16451
|
+
const envelopes = mapCodexThreadToSessionEnvelopes(thread);
|
|
16452
|
+
for (const envelope of envelopes) {
|
|
16453
|
+
opts.session.sendSessionProtocolMessage(envelope);
|
|
16454
|
+
}
|
|
16455
|
+
opts.messageBuffer.addMessage(`Imported thread ${agentGuidance.trimIdent(opts.threadId)} as ${agentGuidance.trimIdent(forked.threadId)}`, "status");
|
|
16456
|
+
opts.session.sendSessionEvent({
|
|
16457
|
+
type: "message",
|
|
16458
|
+
message: `Imported Codex thread ${opts.threadId} (continuing as ${forked.threadId})`
|
|
16459
|
+
});
|
|
16460
|
+
return { threadId: forked.threadId, model: forked.model, replayed: envelopes.length };
|
|
16461
|
+
}
|
|
16462
|
+
|
|
16597
16463
|
function emitReadyIfIdle({ pending, queueSize, shouldExit, sendReady, notify }) {
|
|
16598
16464
|
if (shouldExit) {
|
|
16599
16465
|
return false;
|
|
@@ -16808,6 +16674,12 @@ async function runCodex(opts) {
|
|
|
16808
16674
|
...forkedFromMessageId ? { forkedFromMessageId } : {}
|
|
16809
16675
|
});
|
|
16810
16676
|
metadata.attachmentKinds = [...CLAUDE_ATTACHMENT_KINDS];
|
|
16677
|
+
const importCodexThreadId = process.env[IMPORT_CODEX_THREAD_ENV];
|
|
16678
|
+
if (importCodexThreadId) {
|
|
16679
|
+
metadata.importedFromCodexThreadId = importCodexThreadId;
|
|
16680
|
+
const importTitle = process.env.HAPPY_IMPORT_TITLE;
|
|
16681
|
+
if (importTitle) metadata.summary = { text: importTitle, updatedAt: Date.now() };
|
|
16682
|
+
}
|
|
16811
16683
|
const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
|
|
16812
16684
|
const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
|
|
16813
16685
|
const reconnectVariant = process.env.HAPPY_RECONNECT_ENCRYPTION_VARIANT;
|
|
@@ -16878,7 +16750,7 @@ async function runCodex(opts) {
|
|
|
16878
16750
|
if (response) {
|
|
16879
16751
|
try {
|
|
16880
16752
|
persistence.logger.debug(`[START] Reporting session ${response.id} to daemon`);
|
|
16881
|
-
const result = await notifyDaemonSessionStarted(response.id, metadata, {
|
|
16753
|
+
const result = await api.notifyDaemonSessionStarted(response.id, metadata, {
|
|
16882
16754
|
encryptionKey: persistence.encodeBase64(response.encryptionKey),
|
|
16883
16755
|
encryptionVariant: response.encryptionVariant,
|
|
16884
16756
|
seq: response.seq,
|
|
@@ -17326,6 +17198,18 @@ async function runCodex(opts) {
|
|
|
17326
17198
|
first = false;
|
|
17327
17199
|
appendSystemPromptInjected = true;
|
|
17328
17200
|
}
|
|
17201
|
+
if (!reconnectSessionId && !opts.resumeThreadId && importCodexThreadId) {
|
|
17202
|
+
await importCodexThread({
|
|
17203
|
+
client,
|
|
17204
|
+
session,
|
|
17205
|
+
messageBuffer,
|
|
17206
|
+
threadId: importCodexThreadId,
|
|
17207
|
+
cwd: process.cwd(),
|
|
17208
|
+
mcpServers
|
|
17209
|
+
});
|
|
17210
|
+
first = false;
|
|
17211
|
+
appendSystemPromptInjected = true;
|
|
17212
|
+
}
|
|
17329
17213
|
const forkCodexThreadId = process.env.HAPPY_FORK_CODEX_THREAD_ID;
|
|
17330
17214
|
if (!reconnectSessionId && forkCodexThreadId) {
|
|
17331
17215
|
try {
|
|
@@ -17679,7 +17563,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17679
17563
|
process.exit(0);
|
|
17680
17564
|
} else if (subcommand === "install-terminal-hooks") {
|
|
17681
17565
|
try {
|
|
17682
|
-
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-
|
|
17566
|
+
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-D3jfvbyY.cjs'); });
|
|
17683
17567
|
const command = parseTerminalHooksArgs(args.slice(1));
|
|
17684
17568
|
if (command.action === "help") {
|
|
17685
17569
|
console.log(TERMINAL_HOOKS_HELP);
|
|
@@ -17696,7 +17580,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17696
17580
|
}
|
|
17697
17581
|
} else if (subcommand === "spawn") {
|
|
17698
17582
|
try {
|
|
17699
|
-
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-
|
|
17583
|
+
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-BhUK0Vt2.cjs'); });
|
|
17700
17584
|
await handleSpawnCommand(args.slice(1));
|
|
17701
17585
|
} catch (error) {
|
|
17702
17586
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17708,7 +17592,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17708
17592
|
return;
|
|
17709
17593
|
} else if (subcommand === "sessions") {
|
|
17710
17594
|
try {
|
|
17711
|
-
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-
|
|
17595
|
+
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-CGBBgVZn.cjs'); });
|
|
17712
17596
|
await handleSessionsCommand(args.slice(1));
|
|
17713
17597
|
} catch (error) {
|
|
17714
17598
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17720,7 +17604,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17720
17604
|
return;
|
|
17721
17605
|
} else if (subcommand === "send") {
|
|
17722
17606
|
try {
|
|
17723
|
-
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-
|
|
17607
|
+
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DP_S3wn0.cjs'); });
|
|
17724
17608
|
await handleSendCommand(args.slice(1));
|
|
17725
17609
|
} catch (error) {
|
|
17726
17610
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17826,9 +17710,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17826
17710
|
if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
|
|
17827
17711
|
const projectId = args[3];
|
|
17828
17712
|
try {
|
|
17829
|
-
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
17830
|
-
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-
|
|
17831
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-
|
|
17713
|
+
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-D0-jnCOC.cjs'); });
|
|
17714
|
+
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-Dbip6eVO.cjs'); }).then(function (n) { return n.persistence; });
|
|
17715
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-DCVS050m.cjs'); }).then(function (n) { return n.api; });
|
|
17832
17716
|
let userEmail = void 0;
|
|
17833
17717
|
try {
|
|
17834
17718
|
const credentials = await readCredentialsForConfiguredRelay2();
|
|
@@ -17859,7 +17743,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17859
17743
|
}
|
|
17860
17744
|
if (geminiSubcommand === "project" && args[2] === "get") {
|
|
17861
17745
|
try {
|
|
17862
|
-
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-
|
|
17746
|
+
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-D0-jnCOC.cjs'); });
|
|
17863
17747
|
const config = readGeminiLocalConfig();
|
|
17864
17748
|
if (config.googleCloudProject) {
|
|
17865
17749
|
console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
|
|
@@ -17899,7 +17783,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17899
17783
|
process.exit(0);
|
|
17900
17784
|
}
|
|
17901
17785
|
try {
|
|
17902
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
17786
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-D34eCFpz.cjs'); });
|
|
17903
17787
|
let startedBy = void 0;
|
|
17904
17788
|
for (let i = 1; i < args.length; i++) {
|
|
17905
17789
|
if (args[i] === "--started-by") {
|
|
@@ -17929,7 +17813,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17929
17813
|
}
|
|
17930
17814
|
};
|
|
17931
17815
|
try {
|
|
17932
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
17816
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DJvzGXVN.cjs'); });
|
|
17933
17817
|
const parsed = parsePiRunnerArgs(args.slice(1));
|
|
17934
17818
|
const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
|
|
17935
17819
|
const { credentials } = await authAndSetupMachineIfNeeded();
|
|
@@ -17957,7 +17841,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17957
17841
|
return;
|
|
17958
17842
|
} else if (subcommand === "acp") {
|
|
17959
17843
|
try {
|
|
17960
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
17844
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DJvzGXVN.cjs'); });
|
|
17961
17845
|
let startedBy = void 0;
|
|
17962
17846
|
let verbose = false;
|
|
17963
17847
|
const acpArgs = [];
|
|
@@ -17997,7 +17881,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17997
17881
|
return;
|
|
17998
17882
|
} else if (subcommand === "openclaw") {
|
|
17999
17883
|
try {
|
|
18000
|
-
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-
|
|
17884
|
+
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-B8F8D--R.cjs'); });
|
|
18001
17885
|
let startedBy = void 0;
|
|
18002
17886
|
let verbose = false;
|
|
18003
17887
|
let gatewayUrl;
|
|
@@ -18048,7 +17932,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18048
17932
|
return;
|
|
18049
17933
|
} else if (subcommand === "todo") {
|
|
18050
17934
|
try {
|
|
18051
|
-
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-
|
|
17935
|
+
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-CeJX0z_2.cjs'); });
|
|
18052
17936
|
await handleTodoCommand(args.slice(1));
|
|
18053
17937
|
} catch (error) {
|
|
18054
17938
|
console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
|
|
@@ -18057,7 +17941,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18057
17941
|
return;
|
|
18058
17942
|
} else if (subcommand === "teams") {
|
|
18059
17943
|
try {
|
|
18060
|
-
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-
|
|
17944
|
+
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-C29nEHFD.cjs'); });
|
|
18061
17945
|
await handleTeamsCommand(args.slice(1));
|
|
18062
17946
|
} catch (error) {
|
|
18063
17947
|
console.error(error instanceof Error ? error.message : "Teams command failed");
|
|
@@ -18066,7 +17950,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18066
17950
|
return;
|
|
18067
17951
|
} else if (subcommand === "mcp" && args.length === 1) {
|
|
18068
17952
|
try {
|
|
18069
|
-
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-
|
|
17953
|
+
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-DeapmTWN.cjs'); });
|
|
18070
17954
|
await handleMcpCommand();
|
|
18071
17955
|
} catch (error) {
|
|
18072
17956
|
process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -18089,7 +17973,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18089
17973
|
const daemonSubcommand = args[1];
|
|
18090
17974
|
if (daemonSubcommand === "list") {
|
|
18091
17975
|
try {
|
|
18092
|
-
const sessions = await listDaemonSessions();
|
|
17976
|
+
const sessions = await api.listDaemonSessions();
|
|
18093
17977
|
if (sessions.length === 0) {
|
|
18094
17978
|
console.log("No active sessions this daemon is aware of (they might have been started by a previous version of the daemon)");
|
|
18095
17979
|
} else {
|
|
@@ -18107,7 +17991,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18107
17991
|
process.exit(1);
|
|
18108
17992
|
}
|
|
18109
17993
|
try {
|
|
18110
|
-
const success = await stopDaemonSession(sessionId);
|
|
17994
|
+
const success = await api.stopDaemonSession(sessionId);
|
|
18111
17995
|
console.log(success ? "Session stopped" : "Failed to stop session");
|
|
18112
17996
|
} catch (error) {
|
|
18113
17997
|
console.log("No daemon running");
|
|
@@ -18115,7 +17999,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18115
17999
|
return;
|
|
18116
18000
|
} else if (daemonSubcommand === "start") {
|
|
18117
18001
|
const currentState = await persistence.readDaemonState();
|
|
18118
|
-
const running = await checkIfDaemonRunningAndCleanupStaleState();
|
|
18002
|
+
const running = await api.checkIfDaemonRunningAndCleanupStaleState();
|
|
18119
18003
|
const currentDaemonMatches = currentState && currentState.startedWithCliVersion === persistence.configuration.currentCliVersion && daemonEndpointsMatch(
|
|
18120
18004
|
currentState.serverUrl,
|
|
18121
18005
|
currentState.webappUrl,
|
|
@@ -18124,7 +18008,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18124
18008
|
);
|
|
18125
18009
|
if (running && !currentDaemonMatches) {
|
|
18126
18010
|
console.log("Configured relay or CLI version changed; restarting the existing daemon");
|
|
18127
|
-
await stopDaemon();
|
|
18011
|
+
await api.stopDaemon();
|
|
18128
18012
|
}
|
|
18129
18013
|
const child = spawnHappyCLI(["daemon", "start-sync"], {
|
|
18130
18014
|
detached: true,
|
|
@@ -18134,7 +18018,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18134
18018
|
child.unref();
|
|
18135
18019
|
let started = false;
|
|
18136
18020
|
for (let i = 0; i < 50; i++) {
|
|
18137
|
-
if (await checkIfDaemonRunningAndCleanupStaleState()) {
|
|
18021
|
+
if (await api.checkIfDaemonRunningAndCleanupStaleState()) {
|
|
18138
18022
|
started = true;
|
|
18139
18023
|
break;
|
|
18140
18024
|
}
|
|
@@ -18151,7 +18035,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
18151
18035
|
await startDaemon();
|
|
18152
18036
|
process.exit(0);
|
|
18153
18037
|
} else if (daemonSubcommand === "stop") {
|
|
18154
|
-
await stopDaemon();
|
|
18038
|
+
await api.stopDaemon();
|
|
18155
18039
|
process.exit(0);
|
|
18156
18040
|
} else if (daemonSubcommand === "status") {
|
|
18157
18041
|
await runDoctorDaemon();
|
|
@@ -18448,7 +18332,6 @@ exports.TEAM_SKILL = TEAM_SKILL;
|
|
|
18448
18332
|
exports.TitleGenerator = TitleGenerator;
|
|
18449
18333
|
exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
|
|
18450
18334
|
exports.archiveSession = archiveSession;
|
|
18451
|
-
exports.checkIfDaemonRunningAndCleanupStaleState = checkIfDaemonRunningAndCleanupStaleState;
|
|
18452
18335
|
exports.createSessionMetadata = createSessionMetadata;
|
|
18453
18336
|
exports.hashObject = hashObject;
|
|
18454
18337
|
exports.initialMachineMetadata = initialMachineMetadata;
|
|
@@ -18456,8 +18339,6 @@ exports.isValidSpawnOrigin = isValidSpawnOrigin;
|
|
|
18456
18339
|
exports.listAccountSessions = listAccountSessions;
|
|
18457
18340
|
exports.listSessions = listSessions;
|
|
18458
18341
|
exports.normalizeAcpPermissionMode = normalizeAcpPermissionMode;
|
|
18459
|
-
exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
|
|
18460
|
-
exports.pushClipboardViaDaemon = pushClipboardViaDaemon;
|
|
18461
18342
|
exports.query = query;
|
|
18462
18343
|
exports.readSessionTranscript = readSessionTranscript;
|
|
18463
18344
|
exports.registerAgentAttachmentDownloads = registerAgentAttachmentDownloads;
|
|
@@ -18468,9 +18349,7 @@ exports.resolvePermissionRequest = resolvePermissionRequest;
|
|
|
18468
18349
|
exports.sanitizeSpawnPermissionMode = sanitizeSpawnPermissionMode;
|
|
18469
18350
|
exports.sendUserMessage = sendUserMessage;
|
|
18470
18351
|
exports.sessionWebUrl = sessionWebUrl;
|
|
18471
|
-
exports.setTerminalTitleViaDaemon = setTerminalTitleViaDaemon;
|
|
18472
18352
|
exports.setupOfflineReconnection = setupOfflineReconnection;
|
|
18473
|
-
exports.spawnDaemonSession = spawnDaemonSession;
|
|
18474
18353
|
exports.stageClaudeAttachments = stageClaudeAttachments;
|
|
18475
18354
|
exports.startHappyServer = startHappyServer;
|
|
18476
18355
|
exports.stopSession = stopSession;
|