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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var axios = require('axios');
|
|
4
|
-
var persistence = require('./limits-
|
|
4
|
+
var persistence = require('./limits-Dbip6eVO.cjs');
|
|
5
5
|
var node_events = require('node:events');
|
|
6
6
|
var socket_ioClient = require('socket.io-client');
|
|
7
7
|
var z = require('zod');
|
|
@@ -1004,6 +1004,15 @@ z.z.object({
|
|
|
1004
1004
|
rpcAvailable: z.z.boolean(),
|
|
1005
1005
|
detectedAt: z.z.number()
|
|
1006
1006
|
}).optional(),
|
|
1007
|
+
/**
|
|
1008
|
+
* B-464 capability flag: this daemon answers `codex-list-history` /
|
|
1009
|
+
* `codex-import-session` (import a Codex CLI / desktop thread that was never
|
|
1010
|
+
* started through very-happy). Same trust rule (`detectedAt >= startedAt`).
|
|
1011
|
+
*/
|
|
1012
|
+
codexHistory: z.z.object({
|
|
1013
|
+
rpcAvailable: z.z.boolean(),
|
|
1014
|
+
detectedAt: z.z.number()
|
|
1015
|
+
}).optional(),
|
|
1007
1016
|
/** Relay-owned CLI compatibility/update policy last checked by this daemon. */
|
|
1008
1017
|
cliUpdate: CliUpdateStateSchema.optional(),
|
|
1009
1018
|
/**
|
|
@@ -1369,7 +1378,7 @@ class RpcHandlerManager {
|
|
|
1369
1378
|
}
|
|
1370
1379
|
}
|
|
1371
1380
|
|
|
1372
|
-
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
1381
|
+
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-DCVS050m.cjs', document.baseURI).href))));
|
|
1373
1382
|
function projectPath() {
|
|
1374
1383
|
const path$1 = path.resolve(__dirname$1, "..");
|
|
1375
1384
|
return path$1;
|
|
@@ -2911,6 +2920,268 @@ const MAX_CHAT_ATTACHMENT_SOURCE_BYTES = 50 * 1024 * 1024;
|
|
|
2911
2920
|
const CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES = 40;
|
|
2912
2921
|
const MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES + CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES;
|
|
2913
2922
|
|
|
2923
|
+
const TURN_LEASE_MS = 6e4;
|
|
2924
|
+
const TURN_BUSY_TTL_MS = 15e4;
|
|
2925
|
+
class TurnActivityTracker {
|
|
2926
|
+
constructor(ttlMs = TURN_BUSY_TTL_MS) {
|
|
2927
|
+
this.ttlMs = ttlMs;
|
|
2928
|
+
}
|
|
2929
|
+
busy = /* @__PURE__ */ new Map();
|
|
2930
|
+
apply(sessionId, event, now = Date.now()) {
|
|
2931
|
+
if (event === "turn_started") this.busy.set(sessionId, now);
|
|
2932
|
+
else this.busy.delete(sessionId);
|
|
2933
|
+
}
|
|
2934
|
+
/** The wrapper is gone: whatever it reported no longer holds. */
|
|
2935
|
+
forget(sessionId) {
|
|
2936
|
+
this.busy.delete(sessionId);
|
|
2937
|
+
}
|
|
2938
|
+
/** Sessions whose last renewal is still inside the TTL. */
|
|
2939
|
+
activeSessions(now = Date.now()) {
|
|
2940
|
+
const out = [];
|
|
2941
|
+
for (const [id, at] of this.busy) {
|
|
2942
|
+
if (now - at <= this.ttlMs) out.push(id);
|
|
2943
|
+
else this.busy.delete(id);
|
|
2944
|
+
}
|
|
2945
|
+
return out;
|
|
2946
|
+
}
|
|
2947
|
+
hasActiveTurn(now = Date.now()) {
|
|
2948
|
+
return this.activeSessions(now).length > 0;
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
class TurnReporter {
|
|
2952
|
+
constructor(send, leaseMs = TURN_LEASE_MS) {
|
|
2953
|
+
this.send = send;
|
|
2954
|
+
this.leaseMs = leaseMs;
|
|
2955
|
+
}
|
|
2956
|
+
thinking = false;
|
|
2957
|
+
lastStartedAt = 0;
|
|
2958
|
+
/** Called on every keepAlive tick; emits only on an edge or a due renewal. */
|
|
2959
|
+
observe(thinking, now = Date.now()) {
|
|
2960
|
+
if (thinking) {
|
|
2961
|
+
const due = !this.thinking || now - this.lastStartedAt >= this.leaseMs;
|
|
2962
|
+
this.thinking = true;
|
|
2963
|
+
if (!due) return null;
|
|
2964
|
+
this.lastStartedAt = now;
|
|
2965
|
+
this.send("turn_started");
|
|
2966
|
+
return "turn_started";
|
|
2967
|
+
}
|
|
2968
|
+
if (!this.thinking) return null;
|
|
2969
|
+
this.thinking = false;
|
|
2970
|
+
this.lastStartedAt = 0;
|
|
2971
|
+
this.send("turn_ended");
|
|
2972
|
+
return "turn_ended";
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
function daemonControlHeaders(controlToken) {
|
|
2977
|
+
return {
|
|
2978
|
+
"Content-Type": "application/json",
|
|
2979
|
+
...controlToken ? { Authorization: `Bearer ${controlToken}` } : {}
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
async function daemonPost(path, body) {
|
|
2983
|
+
const state = await persistence.readDaemonState();
|
|
2984
|
+
if (!state?.httpPort) {
|
|
2985
|
+
const errorMessage = "No daemon running, no state file found";
|
|
2986
|
+
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
2987
|
+
return {
|
|
2988
|
+
error: errorMessage
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
try {
|
|
2992
|
+
process.kill(state.pid, 0);
|
|
2993
|
+
} catch (error) {
|
|
2994
|
+
const errorMessage = "Daemon is not running, file is stale";
|
|
2995
|
+
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
2996
|
+
return {
|
|
2997
|
+
error: errorMessage
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
try {
|
|
3001
|
+
const timeout = process.env.HAPPY_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.HAPPY_DAEMON_HTTP_TIMEOUT) : 1e4;
|
|
3002
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
|
|
3003
|
+
method: "POST",
|
|
3004
|
+
// A state written by an old daemon has no token and must remain callable;
|
|
3005
|
+
// a new daemon always writes one and rejects requests without it.
|
|
3006
|
+
headers: daemonControlHeaders(state.controlToken),
|
|
3007
|
+
body: JSON.stringify(body || {}),
|
|
3008
|
+
// Mostly increased for stress test
|
|
3009
|
+
signal: AbortSignal.timeout(timeout)
|
|
3010
|
+
});
|
|
3011
|
+
if (!response.ok) {
|
|
3012
|
+
const failure = await response.json().catch(() => null);
|
|
3013
|
+
const reason = typeof failure?.error === "string" && failure.error ? failure.error : `HTTP ${response.status}`;
|
|
3014
|
+
const errorMessage = `Request failed: ${path}, ${reason}`;
|
|
3015
|
+
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
3016
|
+
return {
|
|
3017
|
+
error: errorMessage
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
return await response.json();
|
|
3021
|
+
} catch (error) {
|
|
3022
|
+
const errorMessage = `Request failed: ${path}, ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
3023
|
+
persistence.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
3024
|
+
return {
|
|
3025
|
+
error: errorMessage
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
const SESSION_STARTED_RETRY_TIMEOUT_MS = 3e3;
|
|
3030
|
+
const SESSION_STARTED_RETRY_INTERVAL_MS = 100;
|
|
3031
|
+
async function notifyDaemonSessionStarted(sessionId, metadata, encryption) {
|
|
3032
|
+
const payload = { sessionId, metadata, encryption };
|
|
3033
|
+
const deadline = Date.now() + SESSION_STARTED_RETRY_TIMEOUT_MS;
|
|
3034
|
+
let result;
|
|
3035
|
+
while (true) {
|
|
3036
|
+
result = await daemonPost("/session-started", payload);
|
|
3037
|
+
if (!result?.error) {
|
|
3038
|
+
return result;
|
|
3039
|
+
}
|
|
3040
|
+
if (Date.now() >= deadline) {
|
|
3041
|
+
return result;
|
|
3042
|
+
}
|
|
3043
|
+
await new Promise((resolve) => setTimeout(resolve, SESSION_STARTED_RETRY_INTERVAL_MS));
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
async function listDaemonSessions() {
|
|
3047
|
+
const result = await daemonPost("/list");
|
|
3048
|
+
return result.children || [];
|
|
3049
|
+
}
|
|
3050
|
+
async function stopDaemonSession(sessionId) {
|
|
3051
|
+
const result = await daemonPost("/stop-session", { sessionId });
|
|
3052
|
+
return result.success || false;
|
|
3053
|
+
}
|
|
3054
|
+
async function spawnDaemonSession(directory, sessionId, opts) {
|
|
3055
|
+
const result = await daemonPost("/spawn-session", {
|
|
3056
|
+
directory,
|
|
3057
|
+
sessionId,
|
|
3058
|
+
spawnedBy: opts?.spawnedBy,
|
|
3059
|
+
permissionMode: opts?.permissionMode,
|
|
3060
|
+
agent: opts?.agent,
|
|
3061
|
+
environmentVariables: opts?.environmentVariables
|
|
3062
|
+
});
|
|
3063
|
+
return result;
|
|
3064
|
+
}
|
|
3065
|
+
async function notifyDaemonSessionEvent(sessionId, event, spawnedBy) {
|
|
3066
|
+
return daemonPost("/session-event", { sessionId, event, spawnedBy });
|
|
3067
|
+
}
|
|
3068
|
+
async function notifyDaemonClaudeAuthFailed(sessionId) {
|
|
3069
|
+
return daemonPost("/session-event", { sessionId, event: "auth_failed" });
|
|
3070
|
+
}
|
|
3071
|
+
async function notifyDaemonTurnEvent(sessionId, event) {
|
|
3072
|
+
return daemonPost("/session-event", { sessionId, event });
|
|
3073
|
+
}
|
|
3074
|
+
async function stopDaemonHttp() {
|
|
3075
|
+
await daemonPost("/stop");
|
|
3076
|
+
}
|
|
3077
|
+
async function pushClipboardViaDaemon(text) {
|
|
3078
|
+
const result = await daemonPost("/clipboard", { text });
|
|
3079
|
+
if (result?.error && result.delivered === void 0) {
|
|
3080
|
+
return { delivered: false, error: result.error };
|
|
3081
|
+
}
|
|
3082
|
+
return result;
|
|
3083
|
+
}
|
|
3084
|
+
async function setTerminalTitleViaDaemon(terminalId, title, ifAbsent = false) {
|
|
3085
|
+
const result = await daemonPost("/terminal-title", { terminalId, title, ifAbsent });
|
|
3086
|
+
if (result?.error) {
|
|
3087
|
+
return { ok: false, error: result.error };
|
|
3088
|
+
}
|
|
3089
|
+
return { ok: result?.status === "ok", error: result?.status === "ok" ? void 0 : "unexpected daemon response" };
|
|
3090
|
+
}
|
|
3091
|
+
async function checkIfDaemonRunningAndCleanupStaleState() {
|
|
3092
|
+
const state = await persistence.readDaemonState();
|
|
3093
|
+
if (!state) {
|
|
3094
|
+
return false;
|
|
3095
|
+
}
|
|
3096
|
+
try {
|
|
3097
|
+
process.kill(state.pid, 0);
|
|
3098
|
+
} catch {
|
|
3099
|
+
persistence.logger.debug("[DAEMON RUN] Daemon PID not running, cleaning up state");
|
|
3100
|
+
await cleanupDaemonState();
|
|
3101
|
+
return false;
|
|
3102
|
+
}
|
|
3103
|
+
if (state.httpPort) {
|
|
3104
|
+
try {
|
|
3105
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}/list`, {
|
|
3106
|
+
method: "POST",
|
|
3107
|
+
headers: daemonControlHeaders(state.controlToken),
|
|
3108
|
+
body: "{}",
|
|
3109
|
+
signal: AbortSignal.timeout(2e3)
|
|
3110
|
+
});
|
|
3111
|
+
if (response.ok) {
|
|
3112
|
+
return true;
|
|
3113
|
+
}
|
|
3114
|
+
} catch {
|
|
3115
|
+
persistence.logger.debug(`[DAEMON RUN] PID ${state.pid} is alive but HTTP health check failed on port ${state.httpPort}, cleaning up stale state`);
|
|
3116
|
+
await cleanupDaemonState();
|
|
3117
|
+
return false;
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
return true;
|
|
3121
|
+
}
|
|
3122
|
+
async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
3123
|
+
persistence.logger.debug("[DAEMON CONTROL] Checking if daemon is running same version");
|
|
3124
|
+
const runningDaemon = await checkIfDaemonRunningAndCleanupStaleState();
|
|
3125
|
+
if (!runningDaemon) {
|
|
3126
|
+
persistence.logger.debug("[DAEMON CONTROL] No daemon running, returning false");
|
|
3127
|
+
return false;
|
|
3128
|
+
}
|
|
3129
|
+
const state = await persistence.readDaemonState();
|
|
3130
|
+
if (!state) {
|
|
3131
|
+
persistence.logger.debug("[DAEMON CONTROL] No daemon state found, returning false");
|
|
3132
|
+
return false;
|
|
3133
|
+
}
|
|
3134
|
+
const currentCliVersion = persistence.configuration.currentCliVersion;
|
|
3135
|
+
persistence.logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
3136
|
+
return currentCliVersion === state.startedWithCliVersion;
|
|
3137
|
+
}
|
|
3138
|
+
async function cleanupDaemonState() {
|
|
3139
|
+
try {
|
|
3140
|
+
await persistence.clearDaemonState();
|
|
3141
|
+
persistence.logger.debug("[DAEMON RUN] Daemon state file removed");
|
|
3142
|
+
} catch (error) {
|
|
3143
|
+
persistence.logger.debug("[DAEMON RUN] Error cleaning up daemon metadata", error);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
async function stopDaemon() {
|
|
3147
|
+
try {
|
|
3148
|
+
const state = await persistence.readDaemonState();
|
|
3149
|
+
if (!state) {
|
|
3150
|
+
persistence.logger.debug("No daemon state found");
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
persistence.logger.debug(`Stopping daemon with PID ${state.pid}`);
|
|
3154
|
+
try {
|
|
3155
|
+
await stopDaemonHttp();
|
|
3156
|
+
await waitForProcessDeath(state.pid, 2e3);
|
|
3157
|
+
persistence.logger.debug("Daemon stopped gracefully via HTTP");
|
|
3158
|
+
return;
|
|
3159
|
+
} catch (error) {
|
|
3160
|
+
persistence.logger.debug("HTTP stop failed, will force kill", error);
|
|
3161
|
+
}
|
|
3162
|
+
try {
|
|
3163
|
+
process.kill(state.pid, "SIGKILL");
|
|
3164
|
+
persistence.logger.debug("Force killed daemon");
|
|
3165
|
+
} catch (error) {
|
|
3166
|
+
persistence.logger.debug("Daemon already dead");
|
|
3167
|
+
}
|
|
3168
|
+
} catch (error) {
|
|
3169
|
+
persistence.logger.debug("Error stopping daemon", error);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
async function waitForProcessDeath(pid, timeout) {
|
|
3173
|
+
const start = Date.now();
|
|
3174
|
+
while (Date.now() - start < timeout) {
|
|
3175
|
+
try {
|
|
3176
|
+
process.kill(pid, 0);
|
|
3177
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3178
|
+
} catch {
|
|
3179
|
+
return;
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
throw new Error("Process did not die within timeout");
|
|
3183
|
+
}
|
|
3184
|
+
|
|
2914
3185
|
class ApiSessionClient extends node_events.EventEmitter {
|
|
2915
3186
|
token;
|
|
2916
3187
|
sessionId;
|
|
@@ -3692,10 +3963,14 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3692
3963
|
/**
|
|
3693
3964
|
* Send a ping message to keep the connection alive
|
|
3694
3965
|
*/
|
|
3966
|
+
/** B-466: turn edges (and a periodic lease) reported to the local daemon so
|
|
3967
|
+
* auto-update waits for "no turn in flight" instead of "no sessions". */
|
|
3968
|
+
turnReporter = new TurnReporter((event) => void notifyDaemonTurnEvent(this.sessionId, event).catch((error) => persistence.logger.debug("[API] Failed to report turn event to daemon:", error)));
|
|
3695
3969
|
keepAlive(thinking, mode) {
|
|
3696
3970
|
if (process.env.DEBUG) {
|
|
3697
3971
|
persistence.logger.debug(`[API] Sending keep alive message: ${thinking}`);
|
|
3698
3972
|
}
|
|
3973
|
+
this.turnReporter.observe(thinking);
|
|
3699
3974
|
this.socket.volatile.emit("session-alive", {
|
|
3700
3975
|
sid: this.sessionId,
|
|
3701
3976
|
time: Date.now(),
|
|
@@ -12384,7 +12659,7 @@ function isVeryHappyOneShotPrompt(firstPrompt) {
|
|
|
12384
12659
|
return VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
12385
12660
|
}
|
|
12386
12661
|
|
|
12387
|
-
const UUID_RE$
|
|
12662
|
+
const UUID_RE$2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12388
12663
|
async function listClaudeProjectDirs(projectsRoot) {
|
|
12389
12664
|
let names;
|
|
12390
12665
|
try {
|
|
@@ -12403,7 +12678,7 @@ async function listClaudeProjectDirs(projectsRoot) {
|
|
|
12403
12678
|
}
|
|
12404
12679
|
return dirs;
|
|
12405
12680
|
}
|
|
12406
|
-
async function collectCandidates(projectDirs, exclude) {
|
|
12681
|
+
async function collectCandidates$1(projectDirs, exclude) {
|
|
12407
12682
|
const out = [];
|
|
12408
12683
|
for (const dir of projectDirs) {
|
|
12409
12684
|
let names;
|
|
@@ -12415,7 +12690,7 @@ async function collectCandidates(projectDirs, exclude) {
|
|
|
12415
12690
|
for (const name of names) {
|
|
12416
12691
|
if (!name.endsWith(".jsonl")) continue;
|
|
12417
12692
|
const id = name.slice(0, -".jsonl".length);
|
|
12418
|
-
if (!UUID_RE$
|
|
12693
|
+
if (!UUID_RE$2.test(id) || exclude.has(id.toLowerCase())) continue;
|
|
12419
12694
|
const path = path$1.join(dir, name);
|
|
12420
12695
|
try {
|
|
12421
12696
|
const s = await promises.stat(path);
|
|
@@ -12434,7 +12709,7 @@ async function collectCandidates(projectDirs, exclude) {
|
|
|
12434
12709
|
return true;
|
|
12435
12710
|
});
|
|
12436
12711
|
}
|
|
12437
|
-
async function readHead(path, bytes) {
|
|
12712
|
+
async function readHead$1(path, bytes) {
|
|
12438
12713
|
const fh = await promises.open(path, "r");
|
|
12439
12714
|
try {
|
|
12440
12715
|
const buffer = Buffer.alloc(bytes);
|
|
@@ -12444,11 +12719,11 @@ async function readHead(path, bytes) {
|
|
|
12444
12719
|
await fh.close();
|
|
12445
12720
|
}
|
|
12446
12721
|
}
|
|
12447
|
-
function oneLine(text, max) {
|
|
12722
|
+
function oneLine$1(text, max) {
|
|
12448
12723
|
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
12449
12724
|
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
12450
12725
|
}
|
|
12451
|
-
function parseTimestamp(raw) {
|
|
12726
|
+
function parseTimestamp$1(raw) {
|
|
12452
12727
|
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
12453
12728
|
if (typeof raw === "string") {
|
|
12454
12729
|
const t = Date.parse(raw);
|
|
@@ -12475,7 +12750,7 @@ function parseClaudeHistoryHead(head, promptChars) {
|
|
|
12475
12750
|
}
|
|
12476
12751
|
if (!parsed || typeof parsed !== "object") continue;
|
|
12477
12752
|
if (parsed.type === "summary" && typeof parsed.summary === "string" && !summary) {
|
|
12478
|
-
const s = oneLine(parsed.summary, promptChars);
|
|
12753
|
+
const s = oneLine$1(parsed.summary, promptChars);
|
|
12479
12754
|
if (s) summary = s;
|
|
12480
12755
|
continue;
|
|
12481
12756
|
}
|
|
@@ -12483,7 +12758,7 @@ function parseClaudeHistoryHead(head, promptChars) {
|
|
|
12483
12758
|
if (typeof parsed.entrypoint === "string" && parsed.entrypoint && !entrypoint) entrypoint = parsed.entrypoint;
|
|
12484
12759
|
if (typeof parsed.gitBranch === "string" && parsed.gitBranch && !gitBranch) gitBranch = parsed.gitBranch;
|
|
12485
12760
|
if (typeof parsed.version === "string" && parsed.version && !version) version = parsed.version;
|
|
12486
|
-
if (startedAt === null) startedAt = parseTimestamp(parsed.timestamp);
|
|
12761
|
+
if (startedAt === null) startedAt = parseTimestamp$1(parsed.timestamp);
|
|
12487
12762
|
if (!firstPrompt && parsed.type === "user" && !parsed.isSidechain) {
|
|
12488
12763
|
const content = parsed.message?.content;
|
|
12489
12764
|
let text = null;
|
|
@@ -12495,7 +12770,7 @@ function parseClaudeHistoryHead(head, promptChars) {
|
|
|
12495
12770
|
}
|
|
12496
12771
|
if (text) {
|
|
12497
12772
|
if (isVeryHappyOneShotPrompt(text)) return null;
|
|
12498
|
-
const cleaned = oneLine(stripHarnessTags(text), promptChars);
|
|
12773
|
+
const cleaned = oneLine$1(stripHarnessTags(text), promptChars);
|
|
12499
12774
|
if (cleaned) firstPrompt = cleaned;
|
|
12500
12775
|
}
|
|
12501
12776
|
}
|
|
@@ -12521,7 +12796,7 @@ async function listClaudeSessionHistory(options) {
|
|
|
12521
12796
|
const headBytes = Math.max(4096, options.headBytes ?? 64 * 1024);
|
|
12522
12797
|
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
12523
12798
|
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
12524
|
-
const candidates = await collectCandidates(options.projectDirs, exclude);
|
|
12799
|
+
const candidates = await collectCandidates$1(options.projectDirs, exclude);
|
|
12525
12800
|
const entries = [];
|
|
12526
12801
|
let scanned = 0;
|
|
12527
12802
|
let truncated = false;
|
|
@@ -12533,7 +12808,7 @@ async function listClaudeSessionHistory(options) {
|
|
|
12533
12808
|
scanned += 1;
|
|
12534
12809
|
let head;
|
|
12535
12810
|
try {
|
|
12536
|
-
head = await readHead(candidate.path, headBytes);
|
|
12811
|
+
head = await readHead$1(candidate.path, headBytes);
|
|
12537
12812
|
} catch {
|
|
12538
12813
|
continue;
|
|
12539
12814
|
}
|
|
@@ -13972,6 +14247,235 @@ class CodexAppServerClient {
|
|
|
13972
14247
|
}
|
|
13973
14248
|
}
|
|
13974
14249
|
|
|
14250
|
+
const UUID_RE$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14251
|
+
const ROLLOUT_RE = /^rollout-.*-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
14252
|
+
const VERY_HAPPY_CODEX_ORIGINATOR = "happy-codex";
|
|
14253
|
+
function getCodexHomeDir() {
|
|
14254
|
+
return process.env.CODEX_HOME || path$1.join(os$1.homedir(), ".codex");
|
|
14255
|
+
}
|
|
14256
|
+
function getCodexSessionsRoot(codexHome = getCodexHomeDir()) {
|
|
14257
|
+
return path$1.join(codexHome, "sessions");
|
|
14258
|
+
}
|
|
14259
|
+
function getCodexSessionIndexPath(codexHome = getCodexHomeDir()) {
|
|
14260
|
+
return path$1.join(codexHome, "session_index.jsonl");
|
|
14261
|
+
}
|
|
14262
|
+
async function collectCandidates(root, exclude) {
|
|
14263
|
+
const out = [];
|
|
14264
|
+
async function walk(dir, depth) {
|
|
14265
|
+
let names;
|
|
14266
|
+
try {
|
|
14267
|
+
names = await promises.readdir(dir);
|
|
14268
|
+
} catch {
|
|
14269
|
+
return;
|
|
14270
|
+
}
|
|
14271
|
+
for (const name of names) {
|
|
14272
|
+
const full = path$1.join(dir, name);
|
|
14273
|
+
const match = ROLLOUT_RE.exec(name);
|
|
14274
|
+
if (match) {
|
|
14275
|
+
const id = match[1].toLowerCase();
|
|
14276
|
+
if (exclude.has(id)) continue;
|
|
14277
|
+
try {
|
|
14278
|
+
const s = await promises.stat(full);
|
|
14279
|
+
if (!s.isFile() || s.size === 0) continue;
|
|
14280
|
+
out.push({ path: full, id, updatedAt: s.mtimeMs, sizeBytes: s.size });
|
|
14281
|
+
} catch {
|
|
14282
|
+
}
|
|
14283
|
+
continue;
|
|
14284
|
+
}
|
|
14285
|
+
if (name.endsWith(".jsonl") || depth >= 4) continue;
|
|
14286
|
+
try {
|
|
14287
|
+
if ((await promises.stat(full)).isDirectory()) await walk(full, depth + 1);
|
|
14288
|
+
} catch {
|
|
14289
|
+
}
|
|
14290
|
+
}
|
|
14291
|
+
}
|
|
14292
|
+
await walk(root, 0);
|
|
14293
|
+
out.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
14294
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14295
|
+
return out.filter((c) => {
|
|
14296
|
+
if (seen.has(c.id)) return false;
|
|
14297
|
+
seen.add(c.id);
|
|
14298
|
+
return true;
|
|
14299
|
+
});
|
|
14300
|
+
}
|
|
14301
|
+
async function readHead(path, bytes) {
|
|
14302
|
+
const fh = await promises.open(path, "r");
|
|
14303
|
+
try {
|
|
14304
|
+
const buffer = Buffer.alloc(bytes);
|
|
14305
|
+
const { bytesRead } = await fh.read(buffer, 0, bytes, 0);
|
|
14306
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
14307
|
+
} finally {
|
|
14308
|
+
await fh.close();
|
|
14309
|
+
}
|
|
14310
|
+
}
|
|
14311
|
+
function oneLine(text, max) {
|
|
14312
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
14313
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
14314
|
+
}
|
|
14315
|
+
function parseTimestamp(raw) {
|
|
14316
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
14317
|
+
if (typeof raw === "string") {
|
|
14318
|
+
const t = Date.parse(raw);
|
|
14319
|
+
if (Number.isFinite(t)) return t;
|
|
14320
|
+
}
|
|
14321
|
+
return null;
|
|
14322
|
+
}
|
|
14323
|
+
function isHarnessUserText(text) {
|
|
14324
|
+
const t = text.trimStart();
|
|
14325
|
+
return t.startsWith("# AGENTS.md instructions") || t.startsWith("<environment_context>") || t.startsWith("<user_instructions>") || t.startsWith("<INSTRUCTIONS>");
|
|
14326
|
+
}
|
|
14327
|
+
function stripHarnessBlocks(text) {
|
|
14328
|
+
return text.replace(/<(environment_context|user_instructions|INSTRUCTIONS|turn_aborted|system-reminder)>[\s\S]*?<\/\1>/g, " ").replace(/^# AGENTS\.md instructions[^\n]*\n/m, " ");
|
|
14329
|
+
}
|
|
14330
|
+
function textFromUserContent(content) {
|
|
14331
|
+
if (typeof content === "string") return content;
|
|
14332
|
+
if (!Array.isArray(content)) return null;
|
|
14333
|
+
const parts = [];
|
|
14334
|
+
for (const part of content) {
|
|
14335
|
+
if (!part || typeof part !== "object") continue;
|
|
14336
|
+
const type = part.type;
|
|
14337
|
+
const text = part.text;
|
|
14338
|
+
if ((type === "input_text" || type === "text") && typeof text === "string") parts.push(text);
|
|
14339
|
+
}
|
|
14340
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
14341
|
+
}
|
|
14342
|
+
function parseCodexHistoryHead(head, promptChars) {
|
|
14343
|
+
let id;
|
|
14344
|
+
let cwd;
|
|
14345
|
+
let firstPrompt;
|
|
14346
|
+
let fallbackPrompt;
|
|
14347
|
+
let startedAt = null;
|
|
14348
|
+
let entrypoint;
|
|
14349
|
+
let originator;
|
|
14350
|
+
let gitBranch;
|
|
14351
|
+
let version;
|
|
14352
|
+
let sawMeta = false;
|
|
14353
|
+
for (const line of head.split("\n")) {
|
|
14354
|
+
if (line.length === 0) continue;
|
|
14355
|
+
let parsed;
|
|
14356
|
+
try {
|
|
14357
|
+
parsed = JSON.parse(line);
|
|
14358
|
+
} catch {
|
|
14359
|
+
continue;
|
|
14360
|
+
}
|
|
14361
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
14362
|
+
const payload = parsed.payload;
|
|
14363
|
+
if (parsed.type === "session_meta" && payload && typeof payload === "object") {
|
|
14364
|
+
sawMeta = true;
|
|
14365
|
+
if (typeof payload.id === "string" && UUID_RE$1.test(payload.id)) id = payload.id.toLowerCase();
|
|
14366
|
+
if (typeof payload.cwd === "string" && payload.cwd) cwd = payload.cwd;
|
|
14367
|
+
if (typeof payload.source === "string" && payload.source) entrypoint = payload.source;
|
|
14368
|
+
if (typeof payload.originator === "string" && payload.originator) originator = payload.originator;
|
|
14369
|
+
if (typeof payload.cli_version === "string" && payload.cli_version) version = payload.cli_version;
|
|
14370
|
+
const branch = payload.git && typeof payload.git === "object" ? payload.git.branch : void 0;
|
|
14371
|
+
if (typeof branch === "string" && branch) gitBranch = branch;
|
|
14372
|
+
startedAt = parseTimestamp(payload.timestamp) ?? parseTimestamp(parsed.timestamp);
|
|
14373
|
+
continue;
|
|
14374
|
+
}
|
|
14375
|
+
if (!sawMeta) return null;
|
|
14376
|
+
if (firstPrompt) break;
|
|
14377
|
+
if (!payload || typeof payload !== "object") continue;
|
|
14378
|
+
if (parsed.type === "event_msg") {
|
|
14379
|
+
if (payload.type === "user_message" && typeof payload.message === "string") {
|
|
14380
|
+
const cleaned = oneLine(stripHarnessBlocks(payload.message), promptChars);
|
|
14381
|
+
if (cleaned) firstPrompt = cleaned;
|
|
14382
|
+
} else if (payload.type === "item_completed" && payload.item && payload.item.type === "UserMessage") {
|
|
14383
|
+
const text = textFromUserContent(payload.item.content);
|
|
14384
|
+
const cleaned = text ? oneLine(stripHarnessBlocks(text), promptChars) : "";
|
|
14385
|
+
if (cleaned) firstPrompt = cleaned;
|
|
14386
|
+
}
|
|
14387
|
+
continue;
|
|
14388
|
+
}
|
|
14389
|
+
if (parsed.type === "response_item" && payload.type === "message" && payload.role === "user" && !fallbackPrompt) {
|
|
14390
|
+
const text = textFromUserContent(payload.content);
|
|
14391
|
+
if (text && !isHarnessUserText(text)) {
|
|
14392
|
+
const cleaned = oneLine(stripHarnessBlocks(text), promptChars);
|
|
14393
|
+
if (cleaned) fallbackPrompt = cleaned;
|
|
14394
|
+
}
|
|
14395
|
+
}
|
|
14396
|
+
}
|
|
14397
|
+
if (!sawMeta || !cwd) return null;
|
|
14398
|
+
const prompt = firstPrompt ?? fallbackPrompt;
|
|
14399
|
+
if (!prompt) return null;
|
|
14400
|
+
return {
|
|
14401
|
+
...id ? { id } : {},
|
|
14402
|
+
cwd,
|
|
14403
|
+
firstPrompt: prompt,
|
|
14404
|
+
startedAt: startedAt ?? 0,
|
|
14405
|
+
...entrypoint ? { entrypoint } : {},
|
|
14406
|
+
...originator ? { originator } : {},
|
|
14407
|
+
...gitBranch ? { gitBranch } : {},
|
|
14408
|
+
...version ? { version } : {}
|
|
14409
|
+
};
|
|
14410
|
+
}
|
|
14411
|
+
function parseCodexSessionIndex(text) {
|
|
14412
|
+
const names = /* @__PURE__ */ new Map();
|
|
14413
|
+
for (const line of text.split("\n")) {
|
|
14414
|
+
if (!line.trim()) continue;
|
|
14415
|
+
let parsed;
|
|
14416
|
+
try {
|
|
14417
|
+
parsed = JSON.parse(line);
|
|
14418
|
+
} catch {
|
|
14419
|
+
continue;
|
|
14420
|
+
}
|
|
14421
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
14422
|
+
if (typeof parsed.id !== "string" || !UUID_RE$1.test(parsed.id)) continue;
|
|
14423
|
+
if (typeof parsed.thread_name !== "string" || !parsed.thread_name.trim()) continue;
|
|
14424
|
+
names.set(parsed.id.toLowerCase(), parsed.thread_name.trim());
|
|
14425
|
+
}
|
|
14426
|
+
return names;
|
|
14427
|
+
}
|
|
14428
|
+
async function readSessionIndex(path) {
|
|
14429
|
+
if (!path) return /* @__PURE__ */ new Map();
|
|
14430
|
+
try {
|
|
14431
|
+
return parseCodexSessionIndex(await promises.readFile(path, "utf-8"));
|
|
14432
|
+
} catch {
|
|
14433
|
+
return /* @__PURE__ */ new Map();
|
|
14434
|
+
}
|
|
14435
|
+
}
|
|
14436
|
+
async function listCodexSessionHistory(options) {
|
|
14437
|
+
const limit = Math.max(1, Math.min(options.limit ?? 60, 500));
|
|
14438
|
+
const headBytes = Math.max(4096, options.headBytes ?? 256 * 1024);
|
|
14439
|
+
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
14440
|
+
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
14441
|
+
const [candidates, names] = await Promise.all([
|
|
14442
|
+
collectCandidates(options.sessionsRoot, exclude),
|
|
14443
|
+
readSessionIndex(options.sessionIndexPath)
|
|
14444
|
+
]);
|
|
14445
|
+
const entries = [];
|
|
14446
|
+
let scanned = 0;
|
|
14447
|
+
let truncated = false;
|
|
14448
|
+
for (const candidate of candidates) {
|
|
14449
|
+
if (entries.length >= limit) {
|
|
14450
|
+
truncated = true;
|
|
14451
|
+
break;
|
|
14452
|
+
}
|
|
14453
|
+
scanned += 1;
|
|
14454
|
+
let head;
|
|
14455
|
+
try {
|
|
14456
|
+
head = await readHead(candidate.path, headBytes);
|
|
14457
|
+
} catch {
|
|
14458
|
+
continue;
|
|
14459
|
+
}
|
|
14460
|
+
const parsed = parseCodexHistoryHead(head, promptChars);
|
|
14461
|
+
if (!parsed) continue;
|
|
14462
|
+
if (parsed.originator === VERY_HAPPY_CODEX_ORIGINATOR) continue;
|
|
14463
|
+
if (options.directory && parsed.cwd !== options.directory) continue;
|
|
14464
|
+
const codexThreadId = parsed.id ?? candidate.id;
|
|
14465
|
+
if (exclude.has(codexThreadId)) continue;
|
|
14466
|
+
const { id: _id, ...rest } = parsed;
|
|
14467
|
+
const summary = names.get(codexThreadId);
|
|
14468
|
+
entries.push({
|
|
14469
|
+
codexThreadId,
|
|
14470
|
+
updatedAt: Math.round(candidate.updatedAt),
|
|
14471
|
+
sizeBytes: candidate.sizeBytes,
|
|
14472
|
+
...rest,
|
|
14473
|
+
...summary ? { summary: oneLine(summary, promptChars) } : {}
|
|
14474
|
+
});
|
|
14475
|
+
}
|
|
14476
|
+
return { entries, truncated, scanned };
|
|
14477
|
+
}
|
|
14478
|
+
|
|
13975
14479
|
function selectLowestLatencyRelay(candidates, probes) {
|
|
13976
14480
|
const rttById = new Map(probes.map((probe) => [probe.relayId, probe.rttMs]));
|
|
13977
14481
|
let selected = null;
|
|
@@ -14689,6 +15193,45 @@ class ApiMachineClient {
|
|
|
14689
15193
|
});
|
|
14690
15194
|
return { type: "success", ...result };
|
|
14691
15195
|
});
|
|
15196
|
+
this.rpcHandlerManager.registerHandler("codex-list-history", async (params) => {
|
|
15197
|
+
const { directory, limit, exclude } = params || {};
|
|
15198
|
+
if (directory !== void 0 && (typeof directory !== "string" || directory.length === 0)) {
|
|
15199
|
+
throw new Error("directory must be a non-empty string when provided");
|
|
15200
|
+
}
|
|
15201
|
+
const excludeIds = [
|
|
15202
|
+
...Array.isArray(exclude) ? exclude.filter((id) => typeof id === "string" && UUID_RE.test(id)) : [],
|
|
15203
|
+
...persistence.readTrackedCodexThreadIds()
|
|
15204
|
+
];
|
|
15205
|
+
const result = await listCodexSessionHistory({
|
|
15206
|
+
sessionsRoot: getCodexSessionsRoot(),
|
|
15207
|
+
sessionIndexPath: getCodexSessionIndexPath(),
|
|
15208
|
+
...typeof directory === "string" ? { directory } : {},
|
|
15209
|
+
limit: typeof limit === "number" && Number.isFinite(limit) ? Math.max(1, Math.min(Math.floor(limit), 200)) : 60,
|
|
15210
|
+
exclude: excludeIds
|
|
15211
|
+
});
|
|
15212
|
+
return { type: "success", ...result };
|
|
15213
|
+
});
|
|
15214
|
+
this.rpcHandlerManager.registerHandler("codex-import-session", async (params) => {
|
|
15215
|
+
const { directory, codexThreadId, approvedNewDirectoryCreation, permissionMode, title } = params || {};
|
|
15216
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
15217
|
+
throw new Error("directory is required");
|
|
15218
|
+
}
|
|
15219
|
+
if (typeof codexThreadId !== "string" || !UUID_RE.test(codexThreadId)) {
|
|
15220
|
+
throw new Error("codexThreadId must be a valid UUID");
|
|
15221
|
+
}
|
|
15222
|
+
const result = await spawnSession({
|
|
15223
|
+
directory,
|
|
15224
|
+
machineId: this.machine.id,
|
|
15225
|
+
approvedNewDirectoryCreation: approvedNewDirectoryCreation === true,
|
|
15226
|
+
agent: "codex",
|
|
15227
|
+
importCodexThreadId: codexThreadId.toLowerCase(),
|
|
15228
|
+
permissionMode: typeof permissionMode === "string" ? permissionMode : void 0,
|
|
15229
|
+
importTitle: typeof title === "string" ? title : void 0
|
|
15230
|
+
});
|
|
15231
|
+
if (result.type !== "success") return result;
|
|
15232
|
+
persistence.logger.debug(`[API MACHINE] Importing Codex thread ${codexThreadId} as session ${result.sessionId}`);
|
|
15233
|
+
return { type: "success", sessionId: result.sessionId };
|
|
15234
|
+
});
|
|
14692
15235
|
this.rpcHandlerManager.registerHandler("codex-fork-thread", async (params) => {
|
|
14693
15236
|
const directory = requireNonEmptyString(params?.directory, "directory");
|
|
14694
15237
|
const codexThreadId = requireNonEmptyString(params?.codexThreadId, "codexThreadId");
|
|
@@ -15037,6 +15580,8 @@ class ApiMachineClient {
|
|
|
15037
15580
|
tmuxSessions: { rpcAvailable: true, detectedAt: now, killAttached: true },
|
|
15038
15581
|
// B-290 capability flag: `claude-list-history` (same restamp discipline).
|
|
15039
15582
|
claudeHistory: { rpcAvailable: true, detectedAt: now },
|
|
15583
|
+
// B-464 capability flag: `codex-list-history` / `codex-import-session`.
|
|
15584
|
+
codexHistory: { rpcAvailable: true, detectedAt: now },
|
|
15040
15585
|
// B-084: closed records survive daemon restarts (persisted
|
|
15041
15586
|
// in closed-terminals.json), so the connect snapshot ships
|
|
15042
15587
|
// them too — not just the incremental pushes.
|
|
@@ -16409,7 +16954,10 @@ exports.MIRROR_BACKFILL_LINES_DEFAULT = MIRROR_BACKFILL_LINES_DEFAULT;
|
|
|
16409
16954
|
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
16410
16955
|
exports.STREAM_DELTA_MAX_CHARS = STREAM_DELTA_MAX_CHARS;
|
|
16411
16956
|
exports.TITLE_PROMPT_PREFIX = TITLE_PROMPT_PREFIX;
|
|
16957
|
+
exports.TurnActivityTracker = TurnActivityTracker;
|
|
16412
16958
|
exports.api = api;
|
|
16959
|
+
exports.checkIfDaemonRunningAndCleanupStaleState = checkIfDaemonRunningAndCleanupStaleState;
|
|
16960
|
+
exports.cleanupDaemonState = cleanupDaemonState;
|
|
16413
16961
|
exports.commandOnPath = commandOnPath;
|
|
16414
16962
|
exports.compareExactVersions = compareExactVersions;
|
|
16415
16963
|
exports.connectionState = connectionState;
|
|
@@ -16429,21 +16977,31 @@ exports.getLocalHappyAgentCredentialPath = getLocalHappyAgentCredentialPath;
|
|
|
16429
16977
|
exports.getProjectPath = getProjectPath;
|
|
16430
16978
|
exports.initializeSandbox = initializeSandbox;
|
|
16431
16979
|
exports.isClaudeSessionId = isClaudeSessionId;
|
|
16980
|
+
exports.isDaemonRunningCurrentlyInstalledHappyVersion = isDaemonRunningCurrentlyInstalledHappyVersion;
|
|
16432
16981
|
exports.isValidSessionId = isValidSessionId;
|
|
16433
16982
|
exports.isValidTerminalId = isValidTerminalId;
|
|
16983
|
+
exports.listDaemonSessions = listDaemonSessions;
|
|
16434
16984
|
exports.listVhTerminals = listVhTerminals;
|
|
16435
16985
|
exports.mirrorLineKey = mirrorLineKey;
|
|
16436
16986
|
exports.mirrorLocalId = mirrorLocalId;
|
|
16987
|
+
exports.notifyDaemonClaudeAuthFailed = notifyDaemonClaudeAuthFailed;
|
|
16988
|
+
exports.notifyDaemonSessionEvent = notifyDaemonSessionEvent;
|
|
16989
|
+
exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
|
|
16437
16990
|
exports.parseExactVersion = parseExactVersion;
|
|
16438
16991
|
exports.parseTerminalHookPayload = parseTerminalHookPayload;
|
|
16439
16992
|
exports.projectPath = projectPath;
|
|
16993
|
+
exports.pushClipboardViaDaemon = pushClipboardViaDaemon;
|
|
16440
16994
|
exports.readLocalHappyAgentCredentials = readLocalHappyAgentCredentials;
|
|
16441
16995
|
exports.readVhTerminal = readVhTerminal;
|
|
16442
16996
|
exports.resolveCliUpdateCheckInterval = resolveCliUpdateCheckInterval;
|
|
16443
16997
|
exports.scrubTmuxClientEnv = scrubTmuxClientEnv;
|
|
16444
16998
|
exports.sendToVhTerminal = sendToVhTerminal;
|
|
16999
|
+
exports.setTerminalTitleViaDaemon = setTerminalTitleViaDaemon;
|
|
16445
17000
|
exports.shellescape = shellescape;
|
|
17001
|
+
exports.spawnDaemonSession = spawnDaemonSession;
|
|
16446
17002
|
exports.startOfflineReconnection = startOfflineReconnection;
|
|
17003
|
+
exports.stopDaemon = stopDaemon;
|
|
17004
|
+
exports.stopDaemonSession = stopDaemonSession;
|
|
16447
17005
|
exports.streamKeyOf = streamKeyOf;
|
|
16448
17006
|
exports.summarizeSpawnSessionForLog = summarizeSpawnSessionForLog;
|
|
16449
17007
|
exports.tmuxArgs = tmuxArgs;
|