very-happy-cli 0.2.102 → 0.2.104
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-CpKkdwIL.cjs → AcpBackend-DDGfk3HJ.cjs} +1 -1
- package/dist/{AcpBackend-C1nF06ZR.mjs → AcpBackend-De4iQ_JG.mjs} +1 -1
- package/dist/{AcpSessionManager-DjWIwGTR.cjs → AcpSessionManager-Cdv6Xgwy.cjs} +1 -1
- package/dist/{AcpSessionManager-BDad8lK3.mjs → AcpSessionManager-DRzAgHtT.mjs} +1 -1
- package/dist/{config-DSF29tTS.cjs → config-CqM5UxVd.cjs} +2 -2
- package/dist/{config-BlTfR0KA.mjs → config-E3KdWyaQ.mjs} +2 -2
- package/dist/{index-CF3rNFlZ.mjs → index--TiLl7-A.mjs} +22 -16
- package/dist/{index-DjYlt3fw.mjs → index-Bzj1XdPx.mjs} +2 -2
- package/dist/{index-D_2eCeep.cjs → index-CX_3uUem.cjs} +26 -20
- package/dist/{index-CPC-a5lH.mjs → index-C_dmtMJ6.mjs} +5 -5
- package/dist/{index-C8HuYyZq.cjs → index-De6H3U0N.cjs} +2 -2
- package/dist/{index-yLIv8JPD.cjs → index-fuavV8uc.cjs} +4 -4
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{installTerminalHooks-BVMhXRZO.cjs → installTerminalHooks-BXMBwrvq.cjs} +2 -2
- package/dist/{installTerminalHooks-DAR3YEo3.mjs → installTerminalHooks-DV0e8-V3.mjs} +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +17 -0
- package/dist/lib.d.mts +17 -0
- package/dist/lib.mjs +1 -1
- package/dist/{mcp-LCZtdhF0.cjs → mcp-Cq3On_Qo.cjs} +2 -2
- package/dist/{mcp--I-Uz_wH.mjs → mcp-DFS9Zohz.mjs} +2 -2
- package/dist/{runGemini-cLR7rfj6.mjs → runGemini-BFjtlkZI.mjs} +4 -4
- package/dist/{runGemini-Crna_6uM.cjs → runGemini-DFvjh8cO.cjs} +4 -4
- package/dist/{runOpenClaw-CWjOCdWg.cjs → runOpenClaw-B5Og1FED.cjs} +3 -3
- package/dist/{runOpenClaw-C1u9xEH7.mjs → runOpenClaw-BFYj1F4u.mjs} +3 -3
- package/dist/{send-BtQ9qf_z.mjs → send-B666yos0.mjs} +2 -2
- package/dist/{send-CufsYMXq.cjs → send-Bog0YPxo.cjs} +2 -2
- package/dist/{spawn-DPllkoUN.cjs → spawn-BSdMkMPX.cjs} +2 -2
- package/dist/{spawn-rdb0uM9R.mjs → spawn-DrqvVoNF.mjs} +2 -2
- package/dist/{types-7-mfp-j5.cjs → types-14LATr9X.cjs} +349 -105
- package/dist/{types-B3Dnzv_q.mjs → types-D9LyZitb.mjs} +347 -105
- package/package.json +7 -7
|
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
|
|
|
51
51
|
var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
|
|
52
52
|
|
|
53
53
|
var name = "very-happy-cli";
|
|
54
|
-
var version = "0.2.
|
|
54
|
+
var version = "0.2.104";
|
|
55
55
|
var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
|
|
56
56
|
var author = "Kirill Dubovitskiy";
|
|
57
57
|
var contributors = [
|
|
@@ -164,12 +164,12 @@ var dependencies = {
|
|
|
164
164
|
zod: "^4.0.0"
|
|
165
165
|
};
|
|
166
166
|
var optionalDependencies = {
|
|
167
|
-
"very-happy-tools-arm64-darwin": "0.2.
|
|
168
|
-
"very-happy-tools-x64-darwin": "0.2.
|
|
169
|
-
"very-happy-tools-arm64-linux": "0.2.
|
|
170
|
-
"very-happy-tools-x64-linux": "0.2.
|
|
171
|
-
"very-happy-tools-arm64-win32": "0.2.
|
|
172
|
-
"very-happy-tools-x64-win32": "0.2.
|
|
167
|
+
"very-happy-tools-arm64-darwin": "0.2.104",
|
|
168
|
+
"very-happy-tools-x64-darwin": "0.2.104",
|
|
169
|
+
"very-happy-tools-arm64-linux": "0.2.104",
|
|
170
|
+
"very-happy-tools-x64-linux": "0.2.104",
|
|
171
|
+
"very-happy-tools-arm64-win32": "0.2.104",
|
|
172
|
+
"very-happy-tools-x64-win32": "0.2.104"
|
|
173
173
|
};
|
|
174
174
|
var devDependencies = {
|
|
175
175
|
"@slopus/happy-wire": "workspace:*",
|
|
@@ -1127,6 +1127,15 @@ z.z.object({
|
|
|
1127
1127
|
/** B-282: `kill-terminal` honours `alsoAttached`. */
|
|
1128
1128
|
killAttached: z.z.boolean().optional()
|
|
1129
1129
|
}).optional(),
|
|
1130
|
+
/**
|
|
1131
|
+
* B-290 capability flag: this daemon answers `claude-list-history` (import a
|
|
1132
|
+
* Claude Code conversation that was never started through very-happy).
|
|
1133
|
+
* Same trust rule (`detectedAt >= startedAt`).
|
|
1134
|
+
*/
|
|
1135
|
+
claudeHistory: z.z.object({
|
|
1136
|
+
rpcAvailable: z.z.boolean(),
|
|
1137
|
+
detectedAt: z.z.number()
|
|
1138
|
+
}).optional(),
|
|
1130
1139
|
/** Relay-owned CLI compatibility/update policy last checked by this daemon. */
|
|
1131
1140
|
cliUpdate: CliUpdateStateSchema.optional(),
|
|
1132
1141
|
/**
|
|
@@ -1585,7 +1594,7 @@ class RpcHandlerManager {
|
|
|
1585
1594
|
}
|
|
1586
1595
|
}
|
|
1587
1596
|
|
|
1588
|
-
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
1597
|
+
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-14LATr9X.cjs', document.baseURI).href))));
|
|
1589
1598
|
function projectPath() {
|
|
1590
1599
|
const path = path$1.resolve(__dirname$1, "..");
|
|
1591
1600
|
return path;
|
|
@@ -2116,102 +2125,66 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
2116
2125
|
}
|
|
2117
2126
|
|
|
2118
2127
|
const PRICING = {
|
|
2119
|
-
// --- Claude
|
|
2120
|
-
"claude-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
},
|
|
2126
|
-
"claude-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
},
|
|
2132
|
-
"claude-4-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
},
|
|
2138
|
-
"claude-4.5-sonnet": {
|
|
2139
|
-
input: 3,
|
|
2140
|
-
output: 15,
|
|
2141
|
-
cache_write: 3.75,
|
|
2142
|
-
cache_read: 0.3
|
|
2143
|
-
},
|
|
2144
|
-
"claude-4-sonnet": {
|
|
2145
|
-
input: 3,
|
|
2146
|
-
output: 15,
|
|
2147
|
-
cache_write: 3.75,
|
|
2148
|
-
cache_read: 0.3
|
|
2149
|
-
},
|
|
2150
|
-
"claude-4.5-haiku": {
|
|
2151
|
-
input: 1,
|
|
2152
|
-
output: 5,
|
|
2153
|
-
cache_write: 1.25,
|
|
2154
|
-
cache_read: 0.1
|
|
2155
|
-
},
|
|
2128
|
+
// --- Claude Fable / Mythos (same tier, same price) ---
|
|
2129
|
+
"claude-fable-5-1": { input: 10, output: 50, cache_write: 12.5, cache_read: 0.25 },
|
|
2130
|
+
"claude-fable-5": { input: 10, output: 50, cache_write: 12.5, cache_read: 1 },
|
|
2131
|
+
"claude-mythos-5-1": { input: 10, output: 50, cache_write: 12.5, cache_read: 0.25 },
|
|
2132
|
+
"claude-mythos-5": { input: 10, output: 50, cache_write: 12.5, cache_read: 1 },
|
|
2133
|
+
// --- Claude 5 ---
|
|
2134
|
+
"claude-opus-5": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2135
|
+
"claude-sonnet-5": { input: 2, output: 10, cache_write: 2.5, cache_read: 0.2 },
|
|
2136
|
+
// --- Claude 4.x ---
|
|
2137
|
+
"claude-opus-4-8": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2138
|
+
"claude-opus-4-7": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2139
|
+
"claude-opus-4-6": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2140
|
+
"claude-opus-4-5": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2141
|
+
"claude-opus-4-1": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2142
|
+
"claude-opus-4": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2143
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2144
|
+
"claude-sonnet-4-5": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2145
|
+
"claude-sonnet-4": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2146
|
+
"claude-haiku-4-5": { input: 1, output: 5, cache_write: 1.25, cache_read: 0.1 },
|
|
2156
2147
|
// --- Legacy / Claude 3 ---
|
|
2157
|
-
"claude-3-opus-20240229": {
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
},
|
|
2163
|
-
"claude-3-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
"claude-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
cache_read: 0.3
|
|
2174
|
-
},
|
|
2175
|
-
// New Sonnet 3.5 updated model
|
|
2176
|
-
"claude-3-5-sonnet-20241022": {
|
|
2177
|
-
input: 3,
|
|
2178
|
-
output: 15,
|
|
2179
|
-
cache_write: 3.75,
|
|
2180
|
-
cache_read: 0.3
|
|
2181
|
-
},
|
|
2182
|
-
"claude-3-haiku-20240307": {
|
|
2183
|
-
input: 0.25,
|
|
2184
|
-
output: 1.25,
|
|
2185
|
-
cache_write: 0.3125,
|
|
2186
|
-
cache_read: 0.025
|
|
2187
|
-
},
|
|
2188
|
-
"claude-3-5-haiku-20241022": {
|
|
2189
|
-
input: 0.8,
|
|
2190
|
-
output: 4,
|
|
2191
|
-
cache_write: 1,
|
|
2192
|
-
// Approx based on 1.25x rule usually or custom
|
|
2193
|
-
cache_read: 0.08
|
|
2194
|
-
}
|
|
2148
|
+
"claude-3-opus-20240229": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2149
|
+
"claude-3-sonnet-20240229": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2150
|
+
"claude-3-5-sonnet-20240620": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2151
|
+
"claude-3-5-sonnet-20241022": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2152
|
+
"claude-3-7-sonnet-20250219": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2153
|
+
"claude-3-haiku-20240307": { input: 0.25, output: 1.25, cache_write: 0.3125, cache_read: 0.025 },
|
|
2154
|
+
"claude-3-5-haiku-20241022": { input: 0.8, output: 4, cache_write: 1, cache_read: 0.08 }
|
|
2155
|
+
};
|
|
2156
|
+
const DEFAULT_MODEL = "claude-opus-5";
|
|
2157
|
+
const FAMILY_DEFAULTS = {
|
|
2158
|
+
fable: "claude-fable-5-1",
|
|
2159
|
+
// Project Glasswing counterpart of Fable: same tier, same published rates.
|
|
2160
|
+
mythos: "claude-mythos-5-1",
|
|
2161
|
+
opus: "claude-opus-5",
|
|
2162
|
+
sonnet: "claude-sonnet-5",
|
|
2163
|
+
haiku: "claude-haiku-4-5"
|
|
2195
2164
|
};
|
|
2196
|
-
|
|
2165
|
+
function resolvePricingKey(modelId) {
|
|
2166
|
+
if (!modelId) return DEFAULT_MODEL;
|
|
2167
|
+
const raw = modelId.trim().toLowerCase();
|
|
2168
|
+
if (raw in PRICING) return raw;
|
|
2169
|
+
const id = raw.replace(/\[1m\]$/, "");
|
|
2170
|
+
if (id in PRICING) return id;
|
|
2171
|
+
const family = ["fable", "mythos", "opus", "sonnet", "haiku"].find((f) => id.includes(f));
|
|
2172
|
+
if (!family) return DEFAULT_MODEL;
|
|
2173
|
+
const version = id.match(/(?:^|[^0-9])(\d)(?:[-._](\d))?(?:[^0-9]|$)/);
|
|
2174
|
+
const major = version ? version[1] : null;
|
|
2175
|
+
const minor = version && version[2] ? version[2] : null;
|
|
2176
|
+
const key = major ? `claude-${family}-${major}${minor ? `-${minor}` : ""}` : null;
|
|
2177
|
+
if (key && key in PRICING) return key;
|
|
2178
|
+
if (major === "3" && family !== "fable" && family !== "mythos") {
|
|
2179
|
+
if (family === "opus") return "claude-3-opus-20240229";
|
|
2180
|
+
if (family === "haiku") return minor === "5" ? "claude-3-5-haiku-20241022" : "claude-3-haiku-20240307";
|
|
2181
|
+
if (minor === "7") return "claude-3-7-sonnet-20250219";
|
|
2182
|
+
return minor === "5" ? "claude-3-5-sonnet-20241022" : "claude-3-sonnet-20240229";
|
|
2183
|
+
}
|
|
2184
|
+
return FAMILY_DEFAULTS[family];
|
|
2185
|
+
}
|
|
2197
2186
|
function calculateCost(usage, modelId) {
|
|
2198
|
-
|
|
2199
|
-
if (!pricing) {
|
|
2200
|
-
if (modelId?.includes("opus")) {
|
|
2201
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-opus"];
|
|
2202
|
-
else if (modelId.includes("4.1")) pricing = PRICING["claude-4.1-opus"];
|
|
2203
|
-
else if (modelId.includes("4")) pricing = PRICING["claude-4-opus"];
|
|
2204
|
-
else pricing = PRICING["claude-3-opus-20240229"];
|
|
2205
|
-
} else if (modelId?.includes("sonnet")) {
|
|
2206
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-sonnet"];
|
|
2207
|
-
else if (modelId.includes("4")) pricing = PRICING["claude-4-sonnet"];
|
|
2208
|
-
else pricing = PRICING["claude-3-5-sonnet-20241022"];
|
|
2209
|
-
} else if (modelId?.includes("haiku")) {
|
|
2210
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-haiku"];
|
|
2211
|
-
else if (modelId.includes("3.5")) pricing = PRICING["claude-3-5-haiku-20241022"];
|
|
2212
|
-
else pricing = PRICING["claude-3-haiku-20240307"];
|
|
2213
|
-
} else pricing = PRICING[DEFAULT_MODEL];
|
|
2214
|
-
}
|
|
2187
|
+
const pricing = PRICING[resolvePricingKey(modelId)];
|
|
2215
2188
|
const inputCost = usage.input_tokens / 1e6 * pricing.input;
|
|
2216
2189
|
const outputCost = usage.output_tokens / 1e6 * pricing.output;
|
|
2217
2190
|
const cacheWriteCost = (usage.cache_creation_input_tokens || 0) / 1e6 * pricing.cache_write;
|
|
@@ -4056,6 +4029,7 @@ function summarizeSpawnSessionForLog(value) {
|
|
|
4056
4029
|
environmentVariableNames: Object.keys(environmentVariables).sort(),
|
|
4057
4030
|
resumesClaude: typeof input.resumeClaudeSessionId === "string" && input.resumeClaudeSessionId.length > 0,
|
|
4058
4031
|
resumesCodex: typeof input.resumeCodexThreadId === "string" && input.resumeCodexThreadId.length > 0,
|
|
4032
|
+
importsClaude: typeof input.importedFromClaudeSessionId === "string" && input.importedFromClaudeSessionId.length > 0,
|
|
4059
4033
|
forceNew: input.forceNew === true,
|
|
4060
4034
|
...typeof input.permissionMode === "string" ? { permissionMode: input.permissionMode } : {}
|
|
4061
4035
|
};
|
|
@@ -4593,6 +4567,23 @@ function readPersistedSessions() {
|
|
|
4593
4567
|
return {};
|
|
4594
4568
|
}
|
|
4595
4569
|
}
|
|
4570
|
+
function readTrackedClaudeSessionIds() {
|
|
4571
|
+
try {
|
|
4572
|
+
if (!node_fs.existsSync(configuration.sessionsFile)) return [];
|
|
4573
|
+
const data = JSON.parse(node_fs.readFileSync(configuration.sessionsFile, "utf-8"));
|
|
4574
|
+
if (!data?.sessions || typeof data.sessions !== "object") return [];
|
|
4575
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4576
|
+
for (const session of Object.values(data.sessions)) {
|
|
4577
|
+
const metadata = (session ?? {}).metadata;
|
|
4578
|
+
for (const id of [metadata?.claudeSessionId, metadata?.importedFromClaudeSessionId]) {
|
|
4579
|
+
if (typeof id === "string" && id.length > 0) ids.add(id.toLowerCase());
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return [...ids];
|
|
4583
|
+
} catch {
|
|
4584
|
+
return [];
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4596
4587
|
function persistSession(sessionId, session) {
|
|
4597
4588
|
try {
|
|
4598
4589
|
const existing = readPersistedSessions();
|
|
@@ -4640,6 +4631,7 @@ var persistence = /*#__PURE__*/Object.freeze({
|
|
|
4640
4631
|
readDaemonState: readDaemonState,
|
|
4641
4632
|
readPersistedSessions: readPersistedSessions,
|
|
4642
4633
|
readSettings: readSettings,
|
|
4634
|
+
readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
|
|
4643
4635
|
releaseDaemonLock: releaseDaemonLock,
|
|
4644
4636
|
updateSettings: updateSettings,
|
|
4645
4637
|
writeCredentialsDataKey: writeCredentialsDataKey,
|
|
@@ -5005,10 +4997,15 @@ function scrubTmuxClientEnv(env) {
|
|
|
5005
4997
|
return env;
|
|
5006
4998
|
}
|
|
5007
4999
|
|
|
5000
|
+
function getClaudeConfigDir() {
|
|
5001
|
+
return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
|
|
5002
|
+
}
|
|
5003
|
+
function getClaudeProjectsRoot() {
|
|
5004
|
+
return path.join(getClaudeConfigDir(), "projects");
|
|
5005
|
+
}
|
|
5008
5006
|
function getProjectPath(workingDirectory) {
|
|
5009
5007
|
const projectId = path.resolve(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
5010
|
-
|
|
5011
|
-
return path.join(claudeConfigDir, "projects", projectId);
|
|
5008
|
+
return path.join(getClaudeProjectsRoot(), projectId);
|
|
5012
5009
|
}
|
|
5013
5010
|
|
|
5014
5011
|
var xtermHeadless = {};
|
|
@@ -12591,6 +12588,182 @@ function detectResumeSupport(happyHomeDir = configuration.happyHomeDir) {
|
|
|
12591
12588
|
};
|
|
12592
12589
|
}
|
|
12593
12590
|
|
|
12591
|
+
const TITLE_PROMPT_PREFIX = "Summarize the SPECIFIC topic/request of this message into a 3-6 word title";
|
|
12592
|
+
const BOARD_ANALYZER_PROMPT_PREFIX = "You are a status analyzer for a task board of coding-agent sessions.";
|
|
12593
|
+
const VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES = [
|
|
12594
|
+
TITLE_PROMPT_PREFIX,
|
|
12595
|
+
BOARD_ANALYZER_PROMPT_PREFIX
|
|
12596
|
+
];
|
|
12597
|
+
function isVeryHappyOneShotPrompt(firstPrompt) {
|
|
12598
|
+
const text = firstPrompt.trimStart();
|
|
12599
|
+
return VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
12600
|
+
}
|
|
12601
|
+
|
|
12602
|
+
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;
|
|
12603
|
+
async function listClaudeProjectDirs(projectsRoot) {
|
|
12604
|
+
let names;
|
|
12605
|
+
try {
|
|
12606
|
+
names = await promises.readdir(projectsRoot);
|
|
12607
|
+
} catch (error) {
|
|
12608
|
+
if (error.code === "ENOENT") return [];
|
|
12609
|
+
throw error;
|
|
12610
|
+
}
|
|
12611
|
+
const dirs = [];
|
|
12612
|
+
for (const name of names) {
|
|
12613
|
+
const full = path.join(projectsRoot, name);
|
|
12614
|
+
try {
|
|
12615
|
+
if ((await promises.stat(full)).isDirectory()) dirs.push(full);
|
|
12616
|
+
} catch {
|
|
12617
|
+
}
|
|
12618
|
+
}
|
|
12619
|
+
return dirs;
|
|
12620
|
+
}
|
|
12621
|
+
async function collectCandidates(projectDirs, exclude) {
|
|
12622
|
+
const out = [];
|
|
12623
|
+
for (const dir of projectDirs) {
|
|
12624
|
+
let names;
|
|
12625
|
+
try {
|
|
12626
|
+
names = await promises.readdir(dir);
|
|
12627
|
+
} catch {
|
|
12628
|
+
continue;
|
|
12629
|
+
}
|
|
12630
|
+
for (const name of names) {
|
|
12631
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
12632
|
+
const id = name.slice(0, -".jsonl".length);
|
|
12633
|
+
if (!UUID_RE$1.test(id) || exclude.has(id.toLowerCase())) continue;
|
|
12634
|
+
const path$1 = path.join(dir, name);
|
|
12635
|
+
try {
|
|
12636
|
+
const s = await promises.stat(path$1);
|
|
12637
|
+
if (!s.isFile() || s.size === 0) continue;
|
|
12638
|
+
out.push({ path: path$1, id, updatedAt: s.mtimeMs, sizeBytes: s.size });
|
|
12639
|
+
} catch {
|
|
12640
|
+
}
|
|
12641
|
+
}
|
|
12642
|
+
}
|
|
12643
|
+
out.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
12644
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12645
|
+
return out.filter((c) => {
|
|
12646
|
+
const key = c.id.toLowerCase();
|
|
12647
|
+
if (seen.has(key)) return false;
|
|
12648
|
+
seen.add(key);
|
|
12649
|
+
return true;
|
|
12650
|
+
});
|
|
12651
|
+
}
|
|
12652
|
+
async function readHead(path, bytes) {
|
|
12653
|
+
const fh = await promises.open(path, "r");
|
|
12654
|
+
try {
|
|
12655
|
+
const buffer = Buffer.alloc(bytes);
|
|
12656
|
+
const { bytesRead } = await fh.read(buffer, 0, bytes, 0);
|
|
12657
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
12658
|
+
} finally {
|
|
12659
|
+
await fh.close();
|
|
12660
|
+
}
|
|
12661
|
+
}
|
|
12662
|
+
function oneLine(text, max) {
|
|
12663
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
12664
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
12665
|
+
}
|
|
12666
|
+
function parseTimestamp(raw) {
|
|
12667
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
12668
|
+
if (typeof raw === "string") {
|
|
12669
|
+
const t = Date.parse(raw);
|
|
12670
|
+
if (Number.isFinite(t)) return t;
|
|
12671
|
+
}
|
|
12672
|
+
return null;
|
|
12673
|
+
}
|
|
12674
|
+
function parseClaudeHistoryHead(head, promptChars) {
|
|
12675
|
+
let cwd;
|
|
12676
|
+
let firstPrompt;
|
|
12677
|
+
let summary;
|
|
12678
|
+
let startedAt = null;
|
|
12679
|
+
let entrypoint;
|
|
12680
|
+
let gitBranch;
|
|
12681
|
+
let version;
|
|
12682
|
+
const lines = head.split("\n");
|
|
12683
|
+
for (const line of lines) {
|
|
12684
|
+
if (line.length === 0) continue;
|
|
12685
|
+
let parsed;
|
|
12686
|
+
try {
|
|
12687
|
+
parsed = JSON.parse(line);
|
|
12688
|
+
} catch {
|
|
12689
|
+
continue;
|
|
12690
|
+
}
|
|
12691
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
12692
|
+
if (parsed.type === "summary" && typeof parsed.summary === "string" && !summary) {
|
|
12693
|
+
const s = oneLine(parsed.summary, promptChars);
|
|
12694
|
+
if (s) summary = s;
|
|
12695
|
+
continue;
|
|
12696
|
+
}
|
|
12697
|
+
if (typeof parsed.cwd === "string" && parsed.cwd && !cwd) cwd = parsed.cwd;
|
|
12698
|
+
if (typeof parsed.entrypoint === "string" && parsed.entrypoint && !entrypoint) entrypoint = parsed.entrypoint;
|
|
12699
|
+
if (typeof parsed.gitBranch === "string" && parsed.gitBranch && !gitBranch) gitBranch = parsed.gitBranch;
|
|
12700
|
+
if (typeof parsed.version === "string" && parsed.version && !version) version = parsed.version;
|
|
12701
|
+
if (startedAt === null) startedAt = parseTimestamp(parsed.timestamp);
|
|
12702
|
+
if (!firstPrompt && parsed.type === "user" && !parsed.isSidechain) {
|
|
12703
|
+
const content = parsed.message?.content;
|
|
12704
|
+
let text = null;
|
|
12705
|
+
if (typeof content === "string") {
|
|
12706
|
+
text = content;
|
|
12707
|
+
} else if (Array.isArray(content)) {
|
|
12708
|
+
const block = content.find((b) => b && b.type === "text" && typeof b.text === "string");
|
|
12709
|
+
if (block) text = block.text;
|
|
12710
|
+
}
|
|
12711
|
+
if (text) {
|
|
12712
|
+
if (isVeryHappyOneShotPrompt(text)) return null;
|
|
12713
|
+
const cleaned = oneLine(stripHarnessTags(text), promptChars);
|
|
12714
|
+
if (cleaned) firstPrompt = cleaned;
|
|
12715
|
+
}
|
|
12716
|
+
}
|
|
12717
|
+
if (cwd && firstPrompt && summary !== void 0 && startedAt !== null) break;
|
|
12718
|
+
}
|
|
12719
|
+
if (!cwd) return null;
|
|
12720
|
+
if (!firstPrompt && !summary) return null;
|
|
12721
|
+
return {
|
|
12722
|
+
cwd,
|
|
12723
|
+
firstPrompt: firstPrompt ?? summary ?? "",
|
|
12724
|
+
...summary ? { summary } : {},
|
|
12725
|
+
startedAt: startedAt ?? 0,
|
|
12726
|
+
...entrypoint ? { entrypoint } : {},
|
|
12727
|
+
...gitBranch ? { gitBranch } : {},
|
|
12728
|
+
...version ? { version } : {}
|
|
12729
|
+
};
|
|
12730
|
+
}
|
|
12731
|
+
function stripHarnessTags(text) {
|
|
12732
|
+
return text.replace(/<(system-reminder|task-notification|command-message|command-name|command-args|local-command-stdout|local-command-caveat)>[\s\S]*?<\/\1>/g, " ").replace(/<\/?(system-reminder|task-notification)>/g, " ");
|
|
12733
|
+
}
|
|
12734
|
+
async function listClaudeSessionHistory(options) {
|
|
12735
|
+
const limit = Math.max(1, Math.min(options.limit ?? 60, 500));
|
|
12736
|
+
const headBytes = Math.max(4096, options.headBytes ?? 64 * 1024);
|
|
12737
|
+
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
12738
|
+
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
12739
|
+
const candidates = await collectCandidates(options.projectDirs, exclude);
|
|
12740
|
+
const entries = [];
|
|
12741
|
+
let scanned = 0;
|
|
12742
|
+
let truncated = false;
|
|
12743
|
+
for (const candidate of candidates) {
|
|
12744
|
+
if (entries.length >= limit) {
|
|
12745
|
+
truncated = true;
|
|
12746
|
+
break;
|
|
12747
|
+
}
|
|
12748
|
+
scanned += 1;
|
|
12749
|
+
let head;
|
|
12750
|
+
try {
|
|
12751
|
+
head = await readHead(candidate.path, headBytes);
|
|
12752
|
+
} catch {
|
|
12753
|
+
continue;
|
|
12754
|
+
}
|
|
12755
|
+
const parsed = parseClaudeHistoryHead(head, promptChars);
|
|
12756
|
+
if (!parsed) continue;
|
|
12757
|
+
entries.push({
|
|
12758
|
+
claudeSessionId: candidate.id.toLowerCase(),
|
|
12759
|
+
updatedAt: Math.round(candidate.updatedAt),
|
|
12760
|
+
sizeBytes: candidate.sizeBytes,
|
|
12761
|
+
...parsed
|
|
12762
|
+
});
|
|
12763
|
+
}
|
|
12764
|
+
return { entries, truncated, scanned };
|
|
12765
|
+
}
|
|
12766
|
+
|
|
12594
12767
|
class ForkTruncateUuidNotFoundError extends Error {
|
|
12595
12768
|
constructor(cutAfterUuid, sourcePath) {
|
|
12596
12769
|
super(`Truncation UUID ${cutAfterUuid} not found in ${sourcePath}`);
|
|
@@ -12615,6 +12788,16 @@ function isUserPrompt(parsed) {
|
|
|
12615
12788
|
const content = parsed.message?.content;
|
|
12616
12789
|
return typeof content === "string";
|
|
12617
12790
|
}
|
|
12791
|
+
async function discardForkedSession(projectDir, claudeSessionId) {
|
|
12792
|
+
try {
|
|
12793
|
+
await promises.unlink(jsonlPath(projectDir, claudeSessionId));
|
|
12794
|
+
logger.debug(`[CLAUDE FORK] Discarded unused fork ${claudeSessionId}`);
|
|
12795
|
+
} catch (error) {
|
|
12796
|
+
if (error.code !== "ENOENT") {
|
|
12797
|
+
logger.debug(`[CLAUDE FORK] Failed to discard fork ${claudeSessionId}:`, error);
|
|
12798
|
+
}
|
|
12799
|
+
}
|
|
12800
|
+
}
|
|
12618
12801
|
async function forkSession(projectDir, sourceClaudeSessionId) {
|
|
12619
12802
|
const newId = node_crypto.randomUUID();
|
|
12620
12803
|
const src = jsonlPath(projectDir, sourceClaudeSessionId);
|
|
@@ -14211,12 +14394,12 @@ class ApiMachineClient {
|
|
|
14211
14394
|
this.stopSessionHandler = stopSession;
|
|
14212
14395
|
this.listTrackedSessionIds = listTrackedSessionIds ?? null;
|
|
14213
14396
|
this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
|
|
14214
|
-
const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, variant, forceNew, permissionMode } = params || {};
|
|
14397
|
+
const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, importedFromClaudeSessionId, variant, forceNew, permissionMode } = params || {};
|
|
14215
14398
|
logger.debug("[API MACHINE] Spawning session:", summarizeSpawnSessionForLog(params));
|
|
14216
14399
|
if (!directory && variant !== "assistant") {
|
|
14217
14400
|
throw new Error("Directory is required");
|
|
14218
14401
|
}
|
|
14219
|
-
const result = await spawnSession({ directory: directory || "", sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, variant: variant === "assistant" ? "assistant" : void 0, forceNew: forceNew === true, permissionMode: typeof permissionMode === "string" ? permissionMode : void 0 });
|
|
14402
|
+
const result = await spawnSession({ directory: directory || "", sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, importedFromClaudeSessionId: typeof importedFromClaudeSessionId === "string" ? importedFromClaudeSessionId : void 0, variant: variant === "assistant" ? "assistant" : void 0, forceNew: forceNew === true, permissionMode: typeof permissionMode === "string" ? permissionMode : void 0 });
|
|
14220
14403
|
switch (result.type) {
|
|
14221
14404
|
case "success":
|
|
14222
14405
|
logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
|
|
@@ -14228,6 +14411,46 @@ class ApiMachineClient {
|
|
|
14228
14411
|
throw new Error(result.errorMessage);
|
|
14229
14412
|
}
|
|
14230
14413
|
});
|
|
14414
|
+
this.rpcHandlerManager.registerHandler("claude-import-session", async (params) => {
|
|
14415
|
+
const { directory, claudeSessionId, approvedNewDirectoryCreation, permissionMode } = params || {};
|
|
14416
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
14417
|
+
throw new Error("directory is required");
|
|
14418
|
+
}
|
|
14419
|
+
if (typeof claudeSessionId !== "string" || !UUID_RE.test(claudeSessionId)) {
|
|
14420
|
+
throw new Error("claudeSessionId must be a valid UUID");
|
|
14421
|
+
}
|
|
14422
|
+
const projectDir = getProjectPath(directory);
|
|
14423
|
+
let newClaudeSessionId;
|
|
14424
|
+
try {
|
|
14425
|
+
newClaudeSessionId = await forkSession(projectDir, claudeSessionId);
|
|
14426
|
+
} catch (error) {
|
|
14427
|
+
if (error instanceof ForkSourceMissingError) {
|
|
14428
|
+
throw new Error("Claude session file not found on this machine");
|
|
14429
|
+
}
|
|
14430
|
+
throw error;
|
|
14431
|
+
}
|
|
14432
|
+
let result;
|
|
14433
|
+
try {
|
|
14434
|
+
result = await spawnSession({
|
|
14435
|
+
directory,
|
|
14436
|
+
machineId: this.machine.id,
|
|
14437
|
+
approvedNewDirectoryCreation: approvedNewDirectoryCreation === true,
|
|
14438
|
+
agent: "claude",
|
|
14439
|
+
resumeClaudeSessionId: newClaudeSessionId,
|
|
14440
|
+
importedFromClaudeSessionId: claudeSessionId,
|
|
14441
|
+
permissionMode: typeof permissionMode === "string" ? permissionMode : void 0
|
|
14442
|
+
});
|
|
14443
|
+
} catch (error) {
|
|
14444
|
+
await discardForkedSession(projectDir, newClaudeSessionId);
|
|
14445
|
+
throw error;
|
|
14446
|
+
}
|
|
14447
|
+
if (result.type !== "success") {
|
|
14448
|
+
await discardForkedSession(projectDir, newClaudeSessionId);
|
|
14449
|
+
return result;
|
|
14450
|
+
}
|
|
14451
|
+
logger.debug(`[API MACHINE] Imported Claude conversation ${claudeSessionId} as session ${result.sessionId}`);
|
|
14452
|
+
return { type: "success", sessionId: result.sessionId, newClaudeSessionId };
|
|
14453
|
+
});
|
|
14231
14454
|
this.syncResumeSessionRpcRegistration();
|
|
14232
14455
|
this.syncRestartSessionRpcRegistration();
|
|
14233
14456
|
this.rpcHandlerManager.registerHandler("open-terminal", async (params) => {
|
|
@@ -14419,6 +14642,23 @@ class ApiMachineClient {
|
|
|
14419
14642
|
throw error;
|
|
14420
14643
|
}
|
|
14421
14644
|
});
|
|
14645
|
+
this.rpcHandlerManager.registerHandler("claude-list-history", async (params) => {
|
|
14646
|
+
const { directory, limit, exclude } = params || {};
|
|
14647
|
+
if (directory !== void 0 && (typeof directory !== "string" || directory.length === 0)) {
|
|
14648
|
+
throw new Error("directory must be a non-empty string when provided");
|
|
14649
|
+
}
|
|
14650
|
+
const projectDirs = typeof directory === "string" ? [getProjectPath(directory)] : await listClaudeProjectDirs(getClaudeProjectsRoot());
|
|
14651
|
+
const excludeIds = [
|
|
14652
|
+
...Array.isArray(exclude) ? exclude.filter((id) => typeof id === "string" && UUID_RE.test(id)) : [],
|
|
14653
|
+
...readTrackedClaudeSessionIds()
|
|
14654
|
+
];
|
|
14655
|
+
const result = await listClaudeSessionHistory({
|
|
14656
|
+
projectDirs,
|
|
14657
|
+
limit: typeof limit === "number" && Number.isFinite(limit) ? Math.max(1, Math.min(Math.floor(limit), 200)) : 60,
|
|
14658
|
+
exclude: excludeIds
|
|
14659
|
+
});
|
|
14660
|
+
return { type: "success", ...result };
|
|
14661
|
+
});
|
|
14422
14662
|
this.rpcHandlerManager.registerHandler("codex-fork-thread", async (params) => {
|
|
14423
14663
|
const directory = requireNonEmptyString(params?.directory, "directory");
|
|
14424
14664
|
const codexThreadId = requireNonEmptyString(params?.codexThreadId, "codexThreadId");
|
|
@@ -14716,6 +14956,8 @@ class ApiMachineClient {
|
|
|
14716
14956
|
terminalRestore: { rpcAvailable: true, detectedAt: now },
|
|
14717
14957
|
// B-273 capability flag (same restamp discipline).
|
|
14718
14958
|
tmuxSessions: { rpcAvailable: true, detectedAt: now, killAttached: true },
|
|
14959
|
+
// B-290 capability flag: `claude-list-history` (same restamp discipline).
|
|
14960
|
+
claudeHistory: { rpcAvailable: true, detectedAt: now },
|
|
14719
14961
|
// B-084: closed records survive daemon restarts (persisted
|
|
14720
14962
|
// in closed-terminals.json), so the connect snapshot ships
|
|
14721
14963
|
// them too — not just the incremental pushes.
|
|
@@ -16060,6 +16302,7 @@ const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
|
16060
16302
|
exports.ApiClient = ApiClient;
|
|
16061
16303
|
exports.ApiSessionClient = ApiSessionClient;
|
|
16062
16304
|
exports.AsyncLock = AsyncLock;
|
|
16305
|
+
exports.BOARD_ANALYZER_PROMPT_PREFIX = BOARD_ANALYZER_PROMPT_PREFIX;
|
|
16063
16306
|
exports.CodexAppServerClient = CodexAppServerClient;
|
|
16064
16307
|
exports.DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS = DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS;
|
|
16065
16308
|
exports.InvalidateSync = InvalidateSync;
|
|
@@ -16067,6 +16310,7 @@ exports.MAX_CHAT_ATTACHMENT_SOURCE_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES;
|
|
|
16067
16310
|
exports.MIRROR_BACKFILL_LINES_DEFAULT = MIRROR_BACKFILL_LINES_DEFAULT;
|
|
16068
16311
|
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
16069
16312
|
exports.SandboxConfigSchema = SandboxConfigSchema;
|
|
16313
|
+
exports.TITLE_PROMPT_PREFIX = TITLE_PROMPT_PREFIX;
|
|
16070
16314
|
exports.acquireDaemonLock = acquireDaemonLock;
|
|
16071
16315
|
exports.api = api;
|
|
16072
16316
|
exports.clearCredentials = clearCredentials;
|