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,5 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as
|
|
2
|
+
import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as readDaemonState, q as clearDaemonState, s as decryptBlob, t as prepareClipboardText, g as readSettings, r as readPersistedSessions, u as credentialRelayProblem, p as packageJson, v as readTrackedClaudeSessionIds, x as readTrackedCodexThreadIds, y as libsodiumEncryptForPublicKey, z as libsodiumPublicKeyFromSecretKey, A as getRandomBytes, B as redactLogValue } from './limits-nhD0Wran.mjs';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { io } from 'socket.io-client';
|
|
5
5
|
import * as z from 'zod';
|
|
@@ -982,6 +982,15 @@ z$1.object({
|
|
|
982
982
|
rpcAvailable: z$1.boolean(),
|
|
983
983
|
detectedAt: z$1.number()
|
|
984
984
|
}).optional(),
|
|
985
|
+
/**
|
|
986
|
+
* B-464 capability flag: this daemon answers `codex-list-history` /
|
|
987
|
+
* `codex-import-session` (import a Codex CLI / desktop thread that was never
|
|
988
|
+
* started through very-happy). Same trust rule (`detectedAt >= startedAt`).
|
|
989
|
+
*/
|
|
990
|
+
codexHistory: z$1.object({
|
|
991
|
+
rpcAvailable: z$1.boolean(),
|
|
992
|
+
detectedAt: z$1.number()
|
|
993
|
+
}).optional(),
|
|
985
994
|
/** Relay-owned CLI compatibility/update policy last checked by this daemon. */
|
|
986
995
|
cliUpdate: CliUpdateStateSchema.optional(),
|
|
987
996
|
/**
|
|
@@ -2889,6 +2898,268 @@ const MAX_CHAT_ATTACHMENT_SOURCE_BYTES = 50 * 1024 * 1024;
|
|
|
2889
2898
|
const CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES = 40;
|
|
2890
2899
|
const MAX_CHAT_ATTACHMENT_ENCRYPTED_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES + CHAT_ATTACHMENT_ENCRYPTION_OVERHEAD_BYTES;
|
|
2891
2900
|
|
|
2901
|
+
const TURN_LEASE_MS = 6e4;
|
|
2902
|
+
const TURN_BUSY_TTL_MS = 15e4;
|
|
2903
|
+
class TurnActivityTracker {
|
|
2904
|
+
constructor(ttlMs = TURN_BUSY_TTL_MS) {
|
|
2905
|
+
this.ttlMs = ttlMs;
|
|
2906
|
+
}
|
|
2907
|
+
busy = /* @__PURE__ */ new Map();
|
|
2908
|
+
apply(sessionId, event, now = Date.now()) {
|
|
2909
|
+
if (event === "turn_started") this.busy.set(sessionId, now);
|
|
2910
|
+
else this.busy.delete(sessionId);
|
|
2911
|
+
}
|
|
2912
|
+
/** The wrapper is gone: whatever it reported no longer holds. */
|
|
2913
|
+
forget(sessionId) {
|
|
2914
|
+
this.busy.delete(sessionId);
|
|
2915
|
+
}
|
|
2916
|
+
/** Sessions whose last renewal is still inside the TTL. */
|
|
2917
|
+
activeSessions(now = Date.now()) {
|
|
2918
|
+
const out = [];
|
|
2919
|
+
for (const [id, at] of this.busy) {
|
|
2920
|
+
if (now - at <= this.ttlMs) out.push(id);
|
|
2921
|
+
else this.busy.delete(id);
|
|
2922
|
+
}
|
|
2923
|
+
return out;
|
|
2924
|
+
}
|
|
2925
|
+
hasActiveTurn(now = Date.now()) {
|
|
2926
|
+
return this.activeSessions(now).length > 0;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
class TurnReporter {
|
|
2930
|
+
constructor(send, leaseMs = TURN_LEASE_MS) {
|
|
2931
|
+
this.send = send;
|
|
2932
|
+
this.leaseMs = leaseMs;
|
|
2933
|
+
}
|
|
2934
|
+
thinking = false;
|
|
2935
|
+
lastStartedAt = 0;
|
|
2936
|
+
/** Called on every keepAlive tick; emits only on an edge or a due renewal. */
|
|
2937
|
+
observe(thinking, now = Date.now()) {
|
|
2938
|
+
if (thinking) {
|
|
2939
|
+
const due = !this.thinking || now - this.lastStartedAt >= this.leaseMs;
|
|
2940
|
+
this.thinking = true;
|
|
2941
|
+
if (!due) return null;
|
|
2942
|
+
this.lastStartedAt = now;
|
|
2943
|
+
this.send("turn_started");
|
|
2944
|
+
return "turn_started";
|
|
2945
|
+
}
|
|
2946
|
+
if (!this.thinking) return null;
|
|
2947
|
+
this.thinking = false;
|
|
2948
|
+
this.lastStartedAt = 0;
|
|
2949
|
+
this.send("turn_ended");
|
|
2950
|
+
return "turn_ended";
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
function daemonControlHeaders(controlToken) {
|
|
2955
|
+
return {
|
|
2956
|
+
"Content-Type": "application/json",
|
|
2957
|
+
...controlToken ? { Authorization: `Bearer ${controlToken}` } : {}
|
|
2958
|
+
};
|
|
2959
|
+
}
|
|
2960
|
+
async function daemonPost(path, body) {
|
|
2961
|
+
const state = await readDaemonState();
|
|
2962
|
+
if (!state?.httpPort) {
|
|
2963
|
+
const errorMessage = "No daemon running, no state file found";
|
|
2964
|
+
logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
2965
|
+
return {
|
|
2966
|
+
error: errorMessage
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
try {
|
|
2970
|
+
process.kill(state.pid, 0);
|
|
2971
|
+
} catch (error) {
|
|
2972
|
+
const errorMessage = "Daemon is not running, file is stale";
|
|
2973
|
+
logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
2974
|
+
return {
|
|
2975
|
+
error: errorMessage
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
try {
|
|
2979
|
+
const timeout = process.env.HAPPY_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.HAPPY_DAEMON_HTTP_TIMEOUT) : 1e4;
|
|
2980
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
|
|
2981
|
+
method: "POST",
|
|
2982
|
+
// A state written by an old daemon has no token and must remain callable;
|
|
2983
|
+
// a new daemon always writes one and rejects requests without it.
|
|
2984
|
+
headers: daemonControlHeaders(state.controlToken),
|
|
2985
|
+
body: JSON.stringify(body || {}),
|
|
2986
|
+
// Mostly increased for stress test
|
|
2987
|
+
signal: AbortSignal.timeout(timeout)
|
|
2988
|
+
});
|
|
2989
|
+
if (!response.ok) {
|
|
2990
|
+
const failure = await response.json().catch(() => null);
|
|
2991
|
+
const reason = typeof failure?.error === "string" && failure.error ? failure.error : `HTTP ${response.status}`;
|
|
2992
|
+
const errorMessage = `Request failed: ${path}, ${reason}`;
|
|
2993
|
+
logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
2994
|
+
return {
|
|
2995
|
+
error: errorMessage
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
return await response.json();
|
|
2999
|
+
} catch (error) {
|
|
3000
|
+
const errorMessage = `Request failed: ${path}, ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
3001
|
+
logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
3002
|
+
return {
|
|
3003
|
+
error: errorMessage
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
const SESSION_STARTED_RETRY_TIMEOUT_MS = 3e3;
|
|
3008
|
+
const SESSION_STARTED_RETRY_INTERVAL_MS = 100;
|
|
3009
|
+
async function notifyDaemonSessionStarted(sessionId, metadata, encryption) {
|
|
3010
|
+
const payload = { sessionId, metadata, encryption };
|
|
3011
|
+
const deadline = Date.now() + SESSION_STARTED_RETRY_TIMEOUT_MS;
|
|
3012
|
+
let result;
|
|
3013
|
+
while (true) {
|
|
3014
|
+
result = await daemonPost("/session-started", payload);
|
|
3015
|
+
if (!result?.error) {
|
|
3016
|
+
return result;
|
|
3017
|
+
}
|
|
3018
|
+
if (Date.now() >= deadline) {
|
|
3019
|
+
return result;
|
|
3020
|
+
}
|
|
3021
|
+
await new Promise((resolve) => setTimeout(resolve, SESSION_STARTED_RETRY_INTERVAL_MS));
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
async function listDaemonSessions() {
|
|
3025
|
+
const result = await daemonPost("/list");
|
|
3026
|
+
return result.children || [];
|
|
3027
|
+
}
|
|
3028
|
+
async function stopDaemonSession(sessionId) {
|
|
3029
|
+
const result = await daemonPost("/stop-session", { sessionId });
|
|
3030
|
+
return result.success || false;
|
|
3031
|
+
}
|
|
3032
|
+
async function spawnDaemonSession(directory, sessionId, opts) {
|
|
3033
|
+
const result = await daemonPost("/spawn-session", {
|
|
3034
|
+
directory,
|
|
3035
|
+
sessionId,
|
|
3036
|
+
spawnedBy: opts?.spawnedBy,
|
|
3037
|
+
permissionMode: opts?.permissionMode,
|
|
3038
|
+
agent: opts?.agent,
|
|
3039
|
+
environmentVariables: opts?.environmentVariables
|
|
3040
|
+
});
|
|
3041
|
+
return result;
|
|
3042
|
+
}
|
|
3043
|
+
async function notifyDaemonSessionEvent(sessionId, event, spawnedBy) {
|
|
3044
|
+
return daemonPost("/session-event", { sessionId, event, spawnedBy });
|
|
3045
|
+
}
|
|
3046
|
+
async function notifyDaemonClaudeAuthFailed(sessionId) {
|
|
3047
|
+
return daemonPost("/session-event", { sessionId, event: "auth_failed" });
|
|
3048
|
+
}
|
|
3049
|
+
async function notifyDaemonTurnEvent(sessionId, event) {
|
|
3050
|
+
return daemonPost("/session-event", { sessionId, event });
|
|
3051
|
+
}
|
|
3052
|
+
async function stopDaemonHttp() {
|
|
3053
|
+
await daemonPost("/stop");
|
|
3054
|
+
}
|
|
3055
|
+
async function pushClipboardViaDaemon(text) {
|
|
3056
|
+
const result = await daemonPost("/clipboard", { text });
|
|
3057
|
+
if (result?.error && result.delivered === void 0) {
|
|
3058
|
+
return { delivered: false, error: result.error };
|
|
3059
|
+
}
|
|
3060
|
+
return result;
|
|
3061
|
+
}
|
|
3062
|
+
async function setTerminalTitleViaDaemon(terminalId, title, ifAbsent = false) {
|
|
3063
|
+
const result = await daemonPost("/terminal-title", { terminalId, title, ifAbsent });
|
|
3064
|
+
if (result?.error) {
|
|
3065
|
+
return { ok: false, error: result.error };
|
|
3066
|
+
}
|
|
3067
|
+
return { ok: result?.status === "ok", error: result?.status === "ok" ? void 0 : "unexpected daemon response" };
|
|
3068
|
+
}
|
|
3069
|
+
async function checkIfDaemonRunningAndCleanupStaleState() {
|
|
3070
|
+
const state = await readDaemonState();
|
|
3071
|
+
if (!state) {
|
|
3072
|
+
return false;
|
|
3073
|
+
}
|
|
3074
|
+
try {
|
|
3075
|
+
process.kill(state.pid, 0);
|
|
3076
|
+
} catch {
|
|
3077
|
+
logger.debug("[DAEMON RUN] Daemon PID not running, cleaning up state");
|
|
3078
|
+
await cleanupDaemonState();
|
|
3079
|
+
return false;
|
|
3080
|
+
}
|
|
3081
|
+
if (state.httpPort) {
|
|
3082
|
+
try {
|
|
3083
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}/list`, {
|
|
3084
|
+
method: "POST",
|
|
3085
|
+
headers: daemonControlHeaders(state.controlToken),
|
|
3086
|
+
body: "{}",
|
|
3087
|
+
signal: AbortSignal.timeout(2e3)
|
|
3088
|
+
});
|
|
3089
|
+
if (response.ok) {
|
|
3090
|
+
return true;
|
|
3091
|
+
}
|
|
3092
|
+
} catch {
|
|
3093
|
+
logger.debug(`[DAEMON RUN] PID ${state.pid} is alive but HTTP health check failed on port ${state.httpPort}, cleaning up stale state`);
|
|
3094
|
+
await cleanupDaemonState();
|
|
3095
|
+
return false;
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
return true;
|
|
3099
|
+
}
|
|
3100
|
+
async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
3101
|
+
logger.debug("[DAEMON CONTROL] Checking if daemon is running same version");
|
|
3102
|
+
const runningDaemon = await checkIfDaemonRunningAndCleanupStaleState();
|
|
3103
|
+
if (!runningDaemon) {
|
|
3104
|
+
logger.debug("[DAEMON CONTROL] No daemon running, returning false");
|
|
3105
|
+
return false;
|
|
3106
|
+
}
|
|
3107
|
+
const state = await readDaemonState();
|
|
3108
|
+
if (!state) {
|
|
3109
|
+
logger.debug("[DAEMON CONTROL] No daemon state found, returning false");
|
|
3110
|
+
return false;
|
|
3111
|
+
}
|
|
3112
|
+
const currentCliVersion = configuration.currentCliVersion;
|
|
3113
|
+
logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
3114
|
+
return currentCliVersion === state.startedWithCliVersion;
|
|
3115
|
+
}
|
|
3116
|
+
async function cleanupDaemonState() {
|
|
3117
|
+
try {
|
|
3118
|
+
await clearDaemonState();
|
|
3119
|
+
logger.debug("[DAEMON RUN] Daemon state file removed");
|
|
3120
|
+
} catch (error) {
|
|
3121
|
+
logger.debug("[DAEMON RUN] Error cleaning up daemon metadata", error);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
async function stopDaemon() {
|
|
3125
|
+
try {
|
|
3126
|
+
const state = await readDaemonState();
|
|
3127
|
+
if (!state) {
|
|
3128
|
+
logger.debug("No daemon state found");
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3131
|
+
logger.debug(`Stopping daemon with PID ${state.pid}`);
|
|
3132
|
+
try {
|
|
3133
|
+
await stopDaemonHttp();
|
|
3134
|
+
await waitForProcessDeath(state.pid, 2e3);
|
|
3135
|
+
logger.debug("Daemon stopped gracefully via HTTP");
|
|
3136
|
+
return;
|
|
3137
|
+
} catch (error) {
|
|
3138
|
+
logger.debug("HTTP stop failed, will force kill", error);
|
|
3139
|
+
}
|
|
3140
|
+
try {
|
|
3141
|
+
process.kill(state.pid, "SIGKILL");
|
|
3142
|
+
logger.debug("Force killed daemon");
|
|
3143
|
+
} catch (error) {
|
|
3144
|
+
logger.debug("Daemon already dead");
|
|
3145
|
+
}
|
|
3146
|
+
} catch (error) {
|
|
3147
|
+
logger.debug("Error stopping daemon", error);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
async function waitForProcessDeath(pid, timeout) {
|
|
3151
|
+
const start = Date.now();
|
|
3152
|
+
while (Date.now() - start < timeout) {
|
|
3153
|
+
try {
|
|
3154
|
+
process.kill(pid, 0);
|
|
3155
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3156
|
+
} catch {
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
throw new Error("Process did not die within timeout");
|
|
3161
|
+
}
|
|
3162
|
+
|
|
2892
3163
|
class ApiSessionClient extends EventEmitter {
|
|
2893
3164
|
token;
|
|
2894
3165
|
sessionId;
|
|
@@ -3670,10 +3941,14 @@ class ApiSessionClient extends EventEmitter {
|
|
|
3670
3941
|
/**
|
|
3671
3942
|
* Send a ping message to keep the connection alive
|
|
3672
3943
|
*/
|
|
3944
|
+
/** B-466: turn edges (and a periodic lease) reported to the local daemon so
|
|
3945
|
+
* auto-update waits for "no turn in flight" instead of "no sessions". */
|
|
3946
|
+
turnReporter = new TurnReporter((event) => void notifyDaemonTurnEvent(this.sessionId, event).catch((error) => logger.debug("[API] Failed to report turn event to daemon:", error)));
|
|
3673
3947
|
keepAlive(thinking, mode) {
|
|
3674
3948
|
if (process.env.DEBUG) {
|
|
3675
3949
|
logger.debug(`[API] Sending keep alive message: ${thinking}`);
|
|
3676
3950
|
}
|
|
3951
|
+
this.turnReporter.observe(thinking);
|
|
3677
3952
|
this.socket.volatile.emit("session-alive", {
|
|
3678
3953
|
sid: this.sessionId,
|
|
3679
3954
|
time: Date.now(),
|
|
@@ -12362,7 +12637,7 @@ function isVeryHappyOneShotPrompt(firstPrompt) {
|
|
|
12362
12637
|
return VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
12363
12638
|
}
|
|
12364
12639
|
|
|
12365
|
-
const UUID_RE$
|
|
12640
|
+
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;
|
|
12366
12641
|
async function listClaudeProjectDirs(projectsRoot) {
|
|
12367
12642
|
let names;
|
|
12368
12643
|
try {
|
|
@@ -12381,7 +12656,7 @@ async function listClaudeProjectDirs(projectsRoot) {
|
|
|
12381
12656
|
}
|
|
12382
12657
|
return dirs;
|
|
12383
12658
|
}
|
|
12384
|
-
async function collectCandidates(projectDirs, exclude) {
|
|
12659
|
+
async function collectCandidates$1(projectDirs, exclude) {
|
|
12385
12660
|
const out = [];
|
|
12386
12661
|
for (const dir of projectDirs) {
|
|
12387
12662
|
let names;
|
|
@@ -12393,7 +12668,7 @@ async function collectCandidates(projectDirs, exclude) {
|
|
|
12393
12668
|
for (const name of names) {
|
|
12394
12669
|
if (!name.endsWith(".jsonl")) continue;
|
|
12395
12670
|
const id = name.slice(0, -".jsonl".length);
|
|
12396
|
-
if (!UUID_RE$
|
|
12671
|
+
if (!UUID_RE$2.test(id) || exclude.has(id.toLowerCase())) continue;
|
|
12397
12672
|
const path = join$1(dir, name);
|
|
12398
12673
|
try {
|
|
12399
12674
|
const s = await stat$1(path);
|
|
@@ -12412,7 +12687,7 @@ async function collectCandidates(projectDirs, exclude) {
|
|
|
12412
12687
|
return true;
|
|
12413
12688
|
});
|
|
12414
12689
|
}
|
|
12415
|
-
async function readHead(path, bytes) {
|
|
12690
|
+
async function readHead$1(path, bytes) {
|
|
12416
12691
|
const fh = await open(path, "r");
|
|
12417
12692
|
try {
|
|
12418
12693
|
const buffer = Buffer.alloc(bytes);
|
|
@@ -12422,11 +12697,11 @@ async function readHead(path, bytes) {
|
|
|
12422
12697
|
await fh.close();
|
|
12423
12698
|
}
|
|
12424
12699
|
}
|
|
12425
|
-
function oneLine(text, max) {
|
|
12700
|
+
function oneLine$1(text, max) {
|
|
12426
12701
|
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
12427
12702
|
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
12428
12703
|
}
|
|
12429
|
-
function parseTimestamp(raw) {
|
|
12704
|
+
function parseTimestamp$1(raw) {
|
|
12430
12705
|
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
12431
12706
|
if (typeof raw === "string") {
|
|
12432
12707
|
const t = Date.parse(raw);
|
|
@@ -12453,7 +12728,7 @@ function parseClaudeHistoryHead(head, promptChars) {
|
|
|
12453
12728
|
}
|
|
12454
12729
|
if (!parsed || typeof parsed !== "object") continue;
|
|
12455
12730
|
if (parsed.type === "summary" && typeof parsed.summary === "string" && !summary) {
|
|
12456
|
-
const s = oneLine(parsed.summary, promptChars);
|
|
12731
|
+
const s = oneLine$1(parsed.summary, promptChars);
|
|
12457
12732
|
if (s) summary = s;
|
|
12458
12733
|
continue;
|
|
12459
12734
|
}
|
|
@@ -12461,7 +12736,7 @@ function parseClaudeHistoryHead(head, promptChars) {
|
|
|
12461
12736
|
if (typeof parsed.entrypoint === "string" && parsed.entrypoint && !entrypoint) entrypoint = parsed.entrypoint;
|
|
12462
12737
|
if (typeof parsed.gitBranch === "string" && parsed.gitBranch && !gitBranch) gitBranch = parsed.gitBranch;
|
|
12463
12738
|
if (typeof parsed.version === "string" && parsed.version && !version) version = parsed.version;
|
|
12464
|
-
if (startedAt === null) startedAt = parseTimestamp(parsed.timestamp);
|
|
12739
|
+
if (startedAt === null) startedAt = parseTimestamp$1(parsed.timestamp);
|
|
12465
12740
|
if (!firstPrompt && parsed.type === "user" && !parsed.isSidechain) {
|
|
12466
12741
|
const content = parsed.message?.content;
|
|
12467
12742
|
let text = null;
|
|
@@ -12473,7 +12748,7 @@ function parseClaudeHistoryHead(head, promptChars) {
|
|
|
12473
12748
|
}
|
|
12474
12749
|
if (text) {
|
|
12475
12750
|
if (isVeryHappyOneShotPrompt(text)) return null;
|
|
12476
|
-
const cleaned = oneLine(stripHarnessTags(text), promptChars);
|
|
12751
|
+
const cleaned = oneLine$1(stripHarnessTags(text), promptChars);
|
|
12477
12752
|
if (cleaned) firstPrompt = cleaned;
|
|
12478
12753
|
}
|
|
12479
12754
|
}
|
|
@@ -12499,7 +12774,7 @@ async function listClaudeSessionHistory(options) {
|
|
|
12499
12774
|
const headBytes = Math.max(4096, options.headBytes ?? 64 * 1024);
|
|
12500
12775
|
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
12501
12776
|
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
12502
|
-
const candidates = await collectCandidates(options.projectDirs, exclude);
|
|
12777
|
+
const candidates = await collectCandidates$1(options.projectDirs, exclude);
|
|
12503
12778
|
const entries = [];
|
|
12504
12779
|
let scanned = 0;
|
|
12505
12780
|
let truncated = false;
|
|
@@ -12511,7 +12786,7 @@ async function listClaudeSessionHistory(options) {
|
|
|
12511
12786
|
scanned += 1;
|
|
12512
12787
|
let head;
|
|
12513
12788
|
try {
|
|
12514
|
-
head = await readHead(candidate.path, headBytes);
|
|
12789
|
+
head = await readHead$1(candidate.path, headBytes);
|
|
12515
12790
|
} catch {
|
|
12516
12791
|
continue;
|
|
12517
12792
|
}
|
|
@@ -13950,6 +14225,235 @@ class CodexAppServerClient {
|
|
|
13950
14225
|
}
|
|
13951
14226
|
}
|
|
13952
14227
|
|
|
14228
|
+
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;
|
|
14229
|
+
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;
|
|
14230
|
+
const VERY_HAPPY_CODEX_ORIGINATOR = "happy-codex";
|
|
14231
|
+
function getCodexHomeDir() {
|
|
14232
|
+
return process.env.CODEX_HOME || join$1(homedir(), ".codex");
|
|
14233
|
+
}
|
|
14234
|
+
function getCodexSessionsRoot(codexHome = getCodexHomeDir()) {
|
|
14235
|
+
return join$1(codexHome, "sessions");
|
|
14236
|
+
}
|
|
14237
|
+
function getCodexSessionIndexPath(codexHome = getCodexHomeDir()) {
|
|
14238
|
+
return join$1(codexHome, "session_index.jsonl");
|
|
14239
|
+
}
|
|
14240
|
+
async function collectCandidates(root, exclude) {
|
|
14241
|
+
const out = [];
|
|
14242
|
+
async function walk(dir, depth) {
|
|
14243
|
+
let names;
|
|
14244
|
+
try {
|
|
14245
|
+
names = await readdir$1(dir);
|
|
14246
|
+
} catch {
|
|
14247
|
+
return;
|
|
14248
|
+
}
|
|
14249
|
+
for (const name of names) {
|
|
14250
|
+
const full = join$1(dir, name);
|
|
14251
|
+
const match = ROLLOUT_RE.exec(name);
|
|
14252
|
+
if (match) {
|
|
14253
|
+
const id = match[1].toLowerCase();
|
|
14254
|
+
if (exclude.has(id)) continue;
|
|
14255
|
+
try {
|
|
14256
|
+
const s = await stat$1(full);
|
|
14257
|
+
if (!s.isFile() || s.size === 0) continue;
|
|
14258
|
+
out.push({ path: full, id, updatedAt: s.mtimeMs, sizeBytes: s.size });
|
|
14259
|
+
} catch {
|
|
14260
|
+
}
|
|
14261
|
+
continue;
|
|
14262
|
+
}
|
|
14263
|
+
if (name.endsWith(".jsonl") || depth >= 4) continue;
|
|
14264
|
+
try {
|
|
14265
|
+
if ((await stat$1(full)).isDirectory()) await walk(full, depth + 1);
|
|
14266
|
+
} catch {
|
|
14267
|
+
}
|
|
14268
|
+
}
|
|
14269
|
+
}
|
|
14270
|
+
await walk(root, 0);
|
|
14271
|
+
out.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
14272
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14273
|
+
return out.filter((c) => {
|
|
14274
|
+
if (seen.has(c.id)) return false;
|
|
14275
|
+
seen.add(c.id);
|
|
14276
|
+
return true;
|
|
14277
|
+
});
|
|
14278
|
+
}
|
|
14279
|
+
async function readHead(path, bytes) {
|
|
14280
|
+
const fh = await open(path, "r");
|
|
14281
|
+
try {
|
|
14282
|
+
const buffer = Buffer.alloc(bytes);
|
|
14283
|
+
const { bytesRead } = await fh.read(buffer, 0, bytes, 0);
|
|
14284
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
14285
|
+
} finally {
|
|
14286
|
+
await fh.close();
|
|
14287
|
+
}
|
|
14288
|
+
}
|
|
14289
|
+
function oneLine(text, max) {
|
|
14290
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
14291
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
14292
|
+
}
|
|
14293
|
+
function parseTimestamp(raw) {
|
|
14294
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
14295
|
+
if (typeof raw === "string") {
|
|
14296
|
+
const t = Date.parse(raw);
|
|
14297
|
+
if (Number.isFinite(t)) return t;
|
|
14298
|
+
}
|
|
14299
|
+
return null;
|
|
14300
|
+
}
|
|
14301
|
+
function isHarnessUserText(text) {
|
|
14302
|
+
const t = text.trimStart();
|
|
14303
|
+
return t.startsWith("# AGENTS.md instructions") || t.startsWith("<environment_context>") || t.startsWith("<user_instructions>") || t.startsWith("<INSTRUCTIONS>");
|
|
14304
|
+
}
|
|
14305
|
+
function stripHarnessBlocks(text) {
|
|
14306
|
+
return text.replace(/<(environment_context|user_instructions|INSTRUCTIONS|turn_aborted|system-reminder)>[\s\S]*?<\/\1>/g, " ").replace(/^# AGENTS\.md instructions[^\n]*\n/m, " ");
|
|
14307
|
+
}
|
|
14308
|
+
function textFromUserContent(content) {
|
|
14309
|
+
if (typeof content === "string") return content;
|
|
14310
|
+
if (!Array.isArray(content)) return null;
|
|
14311
|
+
const parts = [];
|
|
14312
|
+
for (const part of content) {
|
|
14313
|
+
if (!part || typeof part !== "object") continue;
|
|
14314
|
+
const type = part.type;
|
|
14315
|
+
const text = part.text;
|
|
14316
|
+
if ((type === "input_text" || type === "text") && typeof text === "string") parts.push(text);
|
|
14317
|
+
}
|
|
14318
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
14319
|
+
}
|
|
14320
|
+
function parseCodexHistoryHead(head, promptChars) {
|
|
14321
|
+
let id;
|
|
14322
|
+
let cwd;
|
|
14323
|
+
let firstPrompt;
|
|
14324
|
+
let fallbackPrompt;
|
|
14325
|
+
let startedAt = null;
|
|
14326
|
+
let entrypoint;
|
|
14327
|
+
let originator;
|
|
14328
|
+
let gitBranch;
|
|
14329
|
+
let version;
|
|
14330
|
+
let sawMeta = false;
|
|
14331
|
+
for (const line of head.split("\n")) {
|
|
14332
|
+
if (line.length === 0) continue;
|
|
14333
|
+
let parsed;
|
|
14334
|
+
try {
|
|
14335
|
+
parsed = JSON.parse(line);
|
|
14336
|
+
} catch {
|
|
14337
|
+
continue;
|
|
14338
|
+
}
|
|
14339
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
14340
|
+
const payload = parsed.payload;
|
|
14341
|
+
if (parsed.type === "session_meta" && payload && typeof payload === "object") {
|
|
14342
|
+
sawMeta = true;
|
|
14343
|
+
if (typeof payload.id === "string" && UUID_RE$1.test(payload.id)) id = payload.id.toLowerCase();
|
|
14344
|
+
if (typeof payload.cwd === "string" && payload.cwd) cwd = payload.cwd;
|
|
14345
|
+
if (typeof payload.source === "string" && payload.source) entrypoint = payload.source;
|
|
14346
|
+
if (typeof payload.originator === "string" && payload.originator) originator = payload.originator;
|
|
14347
|
+
if (typeof payload.cli_version === "string" && payload.cli_version) version = payload.cli_version;
|
|
14348
|
+
const branch = payload.git && typeof payload.git === "object" ? payload.git.branch : void 0;
|
|
14349
|
+
if (typeof branch === "string" && branch) gitBranch = branch;
|
|
14350
|
+
startedAt = parseTimestamp(payload.timestamp) ?? parseTimestamp(parsed.timestamp);
|
|
14351
|
+
continue;
|
|
14352
|
+
}
|
|
14353
|
+
if (!sawMeta) return null;
|
|
14354
|
+
if (firstPrompt) break;
|
|
14355
|
+
if (!payload || typeof payload !== "object") continue;
|
|
14356
|
+
if (parsed.type === "event_msg") {
|
|
14357
|
+
if (payload.type === "user_message" && typeof payload.message === "string") {
|
|
14358
|
+
const cleaned = oneLine(stripHarnessBlocks(payload.message), promptChars);
|
|
14359
|
+
if (cleaned) firstPrompt = cleaned;
|
|
14360
|
+
} else if (payload.type === "item_completed" && payload.item && payload.item.type === "UserMessage") {
|
|
14361
|
+
const text = textFromUserContent(payload.item.content);
|
|
14362
|
+
const cleaned = text ? oneLine(stripHarnessBlocks(text), promptChars) : "";
|
|
14363
|
+
if (cleaned) firstPrompt = cleaned;
|
|
14364
|
+
}
|
|
14365
|
+
continue;
|
|
14366
|
+
}
|
|
14367
|
+
if (parsed.type === "response_item" && payload.type === "message" && payload.role === "user" && !fallbackPrompt) {
|
|
14368
|
+
const text = textFromUserContent(payload.content);
|
|
14369
|
+
if (text && !isHarnessUserText(text)) {
|
|
14370
|
+
const cleaned = oneLine(stripHarnessBlocks(text), promptChars);
|
|
14371
|
+
if (cleaned) fallbackPrompt = cleaned;
|
|
14372
|
+
}
|
|
14373
|
+
}
|
|
14374
|
+
}
|
|
14375
|
+
if (!sawMeta || !cwd) return null;
|
|
14376
|
+
const prompt = firstPrompt ?? fallbackPrompt;
|
|
14377
|
+
if (!prompt) return null;
|
|
14378
|
+
return {
|
|
14379
|
+
...id ? { id } : {},
|
|
14380
|
+
cwd,
|
|
14381
|
+
firstPrompt: prompt,
|
|
14382
|
+
startedAt: startedAt ?? 0,
|
|
14383
|
+
...entrypoint ? { entrypoint } : {},
|
|
14384
|
+
...originator ? { originator } : {},
|
|
14385
|
+
...gitBranch ? { gitBranch } : {},
|
|
14386
|
+
...version ? { version } : {}
|
|
14387
|
+
};
|
|
14388
|
+
}
|
|
14389
|
+
function parseCodexSessionIndex(text) {
|
|
14390
|
+
const names = /* @__PURE__ */ new Map();
|
|
14391
|
+
for (const line of text.split("\n")) {
|
|
14392
|
+
if (!line.trim()) continue;
|
|
14393
|
+
let parsed;
|
|
14394
|
+
try {
|
|
14395
|
+
parsed = JSON.parse(line);
|
|
14396
|
+
} catch {
|
|
14397
|
+
continue;
|
|
14398
|
+
}
|
|
14399
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
14400
|
+
if (typeof parsed.id !== "string" || !UUID_RE$1.test(parsed.id)) continue;
|
|
14401
|
+
if (typeof parsed.thread_name !== "string" || !parsed.thread_name.trim()) continue;
|
|
14402
|
+
names.set(parsed.id.toLowerCase(), parsed.thread_name.trim());
|
|
14403
|
+
}
|
|
14404
|
+
return names;
|
|
14405
|
+
}
|
|
14406
|
+
async function readSessionIndex(path) {
|
|
14407
|
+
if (!path) return /* @__PURE__ */ new Map();
|
|
14408
|
+
try {
|
|
14409
|
+
return parseCodexSessionIndex(await readFile$1(path, "utf-8"));
|
|
14410
|
+
} catch {
|
|
14411
|
+
return /* @__PURE__ */ new Map();
|
|
14412
|
+
}
|
|
14413
|
+
}
|
|
14414
|
+
async function listCodexSessionHistory(options) {
|
|
14415
|
+
const limit = Math.max(1, Math.min(options.limit ?? 60, 500));
|
|
14416
|
+
const headBytes = Math.max(4096, options.headBytes ?? 256 * 1024);
|
|
14417
|
+
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
14418
|
+
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
14419
|
+
const [candidates, names] = await Promise.all([
|
|
14420
|
+
collectCandidates(options.sessionsRoot, exclude),
|
|
14421
|
+
readSessionIndex(options.sessionIndexPath)
|
|
14422
|
+
]);
|
|
14423
|
+
const entries = [];
|
|
14424
|
+
let scanned = 0;
|
|
14425
|
+
let truncated = false;
|
|
14426
|
+
for (const candidate of candidates) {
|
|
14427
|
+
if (entries.length >= limit) {
|
|
14428
|
+
truncated = true;
|
|
14429
|
+
break;
|
|
14430
|
+
}
|
|
14431
|
+
scanned += 1;
|
|
14432
|
+
let head;
|
|
14433
|
+
try {
|
|
14434
|
+
head = await readHead(candidate.path, headBytes);
|
|
14435
|
+
} catch {
|
|
14436
|
+
continue;
|
|
14437
|
+
}
|
|
14438
|
+
const parsed = parseCodexHistoryHead(head, promptChars);
|
|
14439
|
+
if (!parsed) continue;
|
|
14440
|
+
if (parsed.originator === VERY_HAPPY_CODEX_ORIGINATOR) continue;
|
|
14441
|
+
if (options.directory && parsed.cwd !== options.directory) continue;
|
|
14442
|
+
const codexThreadId = parsed.id ?? candidate.id;
|
|
14443
|
+
if (exclude.has(codexThreadId)) continue;
|
|
14444
|
+
const { id: _id, ...rest } = parsed;
|
|
14445
|
+
const summary = names.get(codexThreadId);
|
|
14446
|
+
entries.push({
|
|
14447
|
+
codexThreadId,
|
|
14448
|
+
updatedAt: Math.round(candidate.updatedAt),
|
|
14449
|
+
sizeBytes: candidate.sizeBytes,
|
|
14450
|
+
...rest,
|
|
14451
|
+
...summary ? { summary: oneLine(summary, promptChars) } : {}
|
|
14452
|
+
});
|
|
14453
|
+
}
|
|
14454
|
+
return { entries, truncated, scanned };
|
|
14455
|
+
}
|
|
14456
|
+
|
|
13953
14457
|
function selectLowestLatencyRelay(candidates, probes) {
|
|
13954
14458
|
const rttById = new Map(probes.map((probe) => [probe.relayId, probe.rttMs]));
|
|
13955
14459
|
let selected = null;
|
|
@@ -14667,6 +15171,45 @@ class ApiMachineClient {
|
|
|
14667
15171
|
});
|
|
14668
15172
|
return { type: "success", ...result };
|
|
14669
15173
|
});
|
|
15174
|
+
this.rpcHandlerManager.registerHandler("codex-list-history", async (params) => {
|
|
15175
|
+
const { directory, limit, exclude } = params || {};
|
|
15176
|
+
if (directory !== void 0 && (typeof directory !== "string" || directory.length === 0)) {
|
|
15177
|
+
throw new Error("directory must be a non-empty string when provided");
|
|
15178
|
+
}
|
|
15179
|
+
const excludeIds = [
|
|
15180
|
+
...Array.isArray(exclude) ? exclude.filter((id) => typeof id === "string" && UUID_RE.test(id)) : [],
|
|
15181
|
+
...readTrackedCodexThreadIds()
|
|
15182
|
+
];
|
|
15183
|
+
const result = await listCodexSessionHistory({
|
|
15184
|
+
sessionsRoot: getCodexSessionsRoot(),
|
|
15185
|
+
sessionIndexPath: getCodexSessionIndexPath(),
|
|
15186
|
+
...typeof directory === "string" ? { directory } : {},
|
|
15187
|
+
limit: typeof limit === "number" && Number.isFinite(limit) ? Math.max(1, Math.min(Math.floor(limit), 200)) : 60,
|
|
15188
|
+
exclude: excludeIds
|
|
15189
|
+
});
|
|
15190
|
+
return { type: "success", ...result };
|
|
15191
|
+
});
|
|
15192
|
+
this.rpcHandlerManager.registerHandler("codex-import-session", async (params) => {
|
|
15193
|
+
const { directory, codexThreadId, approvedNewDirectoryCreation, permissionMode, title } = params || {};
|
|
15194
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
15195
|
+
throw new Error("directory is required");
|
|
15196
|
+
}
|
|
15197
|
+
if (typeof codexThreadId !== "string" || !UUID_RE.test(codexThreadId)) {
|
|
15198
|
+
throw new Error("codexThreadId must be a valid UUID");
|
|
15199
|
+
}
|
|
15200
|
+
const result = await spawnSession({
|
|
15201
|
+
directory,
|
|
15202
|
+
machineId: this.machine.id,
|
|
15203
|
+
approvedNewDirectoryCreation: approvedNewDirectoryCreation === true,
|
|
15204
|
+
agent: "codex",
|
|
15205
|
+
importCodexThreadId: codexThreadId.toLowerCase(),
|
|
15206
|
+
permissionMode: typeof permissionMode === "string" ? permissionMode : void 0,
|
|
15207
|
+
importTitle: typeof title === "string" ? title : void 0
|
|
15208
|
+
});
|
|
15209
|
+
if (result.type !== "success") return result;
|
|
15210
|
+
logger.debug(`[API MACHINE] Importing Codex thread ${codexThreadId} as session ${result.sessionId}`);
|
|
15211
|
+
return { type: "success", sessionId: result.sessionId };
|
|
15212
|
+
});
|
|
14670
15213
|
this.rpcHandlerManager.registerHandler("codex-fork-thread", async (params) => {
|
|
14671
15214
|
const directory = requireNonEmptyString(params?.directory, "directory");
|
|
14672
15215
|
const codexThreadId = requireNonEmptyString(params?.codexThreadId, "codexThreadId");
|
|
@@ -15015,6 +15558,8 @@ class ApiMachineClient {
|
|
|
15015
15558
|
tmuxSessions: { rpcAvailable: true, detectedAt: now, killAttached: true },
|
|
15016
15559
|
// B-290 capability flag: `claude-list-history` (same restamp discipline).
|
|
15017
15560
|
claudeHistory: { rpcAvailable: true, detectedAt: now },
|
|
15561
|
+
// B-464 capability flag: `codex-list-history` / `codex-import-session`.
|
|
15562
|
+
codexHistory: { rpcAvailable: true, detectedAt: now },
|
|
15018
15563
|
// B-084: closed records survive daemon restarts (persisted
|
|
15019
15564
|
// in closed-terminals.json), so the connect snapshot ships
|
|
15020
15565
|
// them too — not just the incremental pushes.
|
|
@@ -16371,4 +16916,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
|
|
|
16371
16916
|
}).passthrough()
|
|
16372
16917
|
]);
|
|
16373
16918
|
|
|
16374
|
-
export {
|
|
16919
|
+
export { shellescape as $, ApiClient as A, BUILTIN_TODO_SKILL as B, getLocalHappyAgentCredentialPath as C, readLocalHappyAgentCredentials as D, parseExactVersion as E, compareExactVersions as F, extractCompleteLines as G, decideBackfill as H, InvalidateSync as I, parseTerminalHookPayload as J, tmuxArgs as K, scrubTmuxClientEnv as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, decideMirrorBinding as N, MIRROR_BACKFILL_LINES_DEFAULT as O, mirrorLineKey as P, mirrorLocalId as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, listDaemonSessions as T, stopDaemonSession as U, detectCLIAvailability as V, detectResumeSupport as W, isDaemonRunningCurrentlyInstalledHappyVersion as X, stopDaemon as Y, TurnActivityTracker as Z, summarizeSpawnSessionForLog as _, ApiSessionClient as a, fetchCliUpdateState as a0, resolveCliUpdateCheckInterval as a1, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as a2, cleanupDaemonState as a3, listVhTerminals as a4, isValidTerminalId as a5, readVhTerminal as a6, sendToVhTerminal as a7, ContextUsageSchema as a8, TITLE_PROMPT_PREFIX as a9, BOARD_ANALYZER_PROMPT_PREFIX as aa, startOfflineReconnection as ab, CodexAppServerClient as ac, api as ad, streamKeyOf as b, checkIfDaemonRunningAndCleanupStaleState as c, createEnvelope as d, createBuiltinTodoClient as e, pushClipboardViaDaemon as f, setTerminalTitleViaDaemon as g, errorLogMetadata as h, isValidSessionId as i, contentLogMetadata as j, connectionState as k, BUILTIN_TODO_DISCOVERY as l, delay as m, notifyDaemonSessionStarted as n, notifyDaemonSessionEvent as o, projectPath as p, getProjectPath as q, initializeSandbox as r, spawnDaemonSession as s, AsyncLock as t, notifyDaemonClaudeAuthFailed as u, commandOnPath as v, wrapCommand as w, deriveLocalCliUpdateSummary as x, AGENT_VERSION_SOURCES as y, isClaudeSessionId as z };
|