claudish 7.46.0 → 7.47.0
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/index.js +3184 -209
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
729
729
|
});
|
|
730
730
|
|
|
731
731
|
// src/version.ts
|
|
732
|
-
var VERSION = "7.
|
|
732
|
+
var VERSION = "7.47.0";
|
|
733
733
|
|
|
734
734
|
// src/logger.ts
|
|
735
735
|
var exports_logger = {};
|
|
@@ -29217,6 +29217,11 @@ function removeUriFormat(schema) {
|
|
|
29217
29217
|
}
|
|
29218
29218
|
return result;
|
|
29219
29219
|
}
|
|
29220
|
+
function stripBillingHeader(text) {
|
|
29221
|
+
if (!text || !text.includes("x-anthropic-billing-header"))
|
|
29222
|
+
return text;
|
|
29223
|
+
return text.replace(BILLING_HEADER_RE, "");
|
|
29224
|
+
}
|
|
29220
29225
|
function transformOpenAIToClaude(claudeRequestInput) {
|
|
29221
29226
|
const req = JSON.parse(JSON.stringify(claudeRequestInput));
|
|
29222
29227
|
const isO3Model = typeof req.model === "string" && (req.model.includes("o3") || req.model.includes("o1"));
|
|
@@ -29240,9 +29245,11 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
29240
29245
|
}
|
|
29241
29246
|
}
|
|
29242
29247
|
return JSON.stringify(item);
|
|
29243
|
-
}).filter((text) => text && text.trim() !== "").join(`
|
|
29248
|
+
}).map((text) => stripBillingHeader(text)).filter((text) => text && text.trim() !== "").join(`
|
|
29244
29249
|
|
|
29245
29250
|
`);
|
|
29251
|
+
} else if (typeof req.system === "string") {
|
|
29252
|
+
req.system = stripBillingHeader(req.system);
|
|
29246
29253
|
}
|
|
29247
29254
|
if (!Array.isArray(req.messages)) {
|
|
29248
29255
|
if (req.messages == null)
|
|
@@ -29264,7 +29271,10 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
29264
29271
|
isO3Model
|
|
29265
29272
|
};
|
|
29266
29273
|
}
|
|
29267
|
-
var
|
|
29274
|
+
var BILLING_HEADER_RE;
|
|
29275
|
+
var init_transform = __esm(() => {
|
|
29276
|
+
BILLING_HEADER_RE = /x-anthropic-billing-header:[^\n]*\n?/gi;
|
|
29277
|
+
});
|
|
29268
29278
|
|
|
29269
29279
|
// src/handlers/shared/format/openai-tools.ts
|
|
29270
29280
|
function emptyParamsSchema() {
|
|
@@ -33137,6 +33147,13 @@ function createStreamingResponseHandler(c, response, adapter, target, middleware
|
|
|
33137
33147
|
async start(controller) {
|
|
33138
33148
|
const send = (e, d) => {
|
|
33139
33149
|
if (!isClosed) {
|
|
33150
|
+
if (e === "content_block_start" && d?.content_block?.type === "tool_use") {
|
|
33151
|
+
try {
|
|
33152
|
+
behavior?.onToolCallObserved?.(String(d.content_block.name ?? ""));
|
|
33153
|
+
} catch (err) {
|
|
33154
|
+
log(`[Streaming] onToolCallObserved threw: ${err}`);
|
|
33155
|
+
}
|
|
33156
|
+
}
|
|
33140
33157
|
controller.enqueue(encoder.encode(`event: ${e}
|
|
33141
33158
|
data: ${JSON.stringify(d)}
|
|
33142
33159
|
|
|
@@ -33164,54 +33181,98 @@ data: ${JSON.stringify(d)}
|
|
|
33164
33181
|
send("ping", { type: "ping" });
|
|
33165
33182
|
}
|
|
33166
33183
|
}, 1000);
|
|
33184
|
+
const teardown = () => {
|
|
33185
|
+
if (!isClosed) {
|
|
33186
|
+
try {
|
|
33187
|
+
controller.enqueue(encoder.encode(`data: [DONE]
|
|
33188
|
+
|
|
33189
|
+
|
|
33190
|
+
`));
|
|
33191
|
+
} catch {}
|
|
33192
|
+
try {
|
|
33193
|
+
controller.close();
|
|
33194
|
+
} catch {}
|
|
33195
|
+
isClosed = true;
|
|
33196
|
+
}
|
|
33197
|
+
if (ping) {
|
|
33198
|
+
clearInterval(ping);
|
|
33199
|
+
ping = null;
|
|
33200
|
+
}
|
|
33201
|
+
};
|
|
33167
33202
|
const finalize = async (reason, err) => {
|
|
33168
|
-
if (state.finalized)
|
|
33203
|
+
if (state.finalized) {
|
|
33204
|
+
teardown();
|
|
33169
33205
|
return;
|
|
33206
|
+
}
|
|
33170
33207
|
state.finalized = true;
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
|
|
33174
|
-
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33208
|
+
try {
|
|
33209
|
+
if (state.accumulatedText.length > 0) {
|
|
33210
|
+
const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
|
|
33211
|
+
log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
|
|
33212
|
+
}
|
|
33213
|
+
const textToolCalls = extractToolCallsFromText(state.accumulatedText);
|
|
33214
|
+
log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
|
|
33215
|
+
if (textToolCalls.length > 0) {
|
|
33216
|
+
log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
|
|
33217
|
+
if (state.textStarted) {
|
|
33218
|
+
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
33219
|
+
state.textStarted = false;
|
|
33220
|
+
}
|
|
33221
|
+
for (const tc of textToolCalls) {
|
|
33222
|
+
const toolIdx = state.curIdx++;
|
|
33223
|
+
const toolId = `tool_${Date.now()}_${toolIdx}`;
|
|
33224
|
+
send("content_block_start", {
|
|
33225
|
+
type: "content_block_start",
|
|
33226
|
+
index: toolIdx,
|
|
33227
|
+
content_block: { type: "tool_use", id: toolId, name: tc.name }
|
|
33228
|
+
});
|
|
33229
|
+
send("content_block_delta", {
|
|
33230
|
+
type: "content_block_delta",
|
|
33231
|
+
index: toolIdx,
|
|
33232
|
+
delta: {
|
|
33233
|
+
type: "input_json_delta",
|
|
33234
|
+
partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
|
|
33235
|
+
}
|
|
33236
|
+
});
|
|
33237
|
+
send("content_block_stop", { type: "content_block_stop", index: toolIdx });
|
|
33238
|
+
}
|
|
33239
|
+
}
|
|
33240
|
+
if (state.reasoningStarted) {
|
|
33241
|
+
send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
|
|
33242
|
+
}
|
|
33179
33243
|
if (state.textStarted) {
|
|
33180
33244
|
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
33181
|
-
state.textStarted = false;
|
|
33182
33245
|
}
|
|
33183
|
-
for (const
|
|
33184
|
-
|
|
33185
|
-
|
|
33186
|
-
|
|
33187
|
-
|
|
33188
|
-
|
|
33189
|
-
|
|
33190
|
-
|
|
33191
|
-
|
|
33192
|
-
|
|
33193
|
-
|
|
33194
|
-
|
|
33195
|
-
|
|
33196
|
-
|
|
33197
|
-
|
|
33198
|
-
|
|
33199
|
-
|
|
33200
|
-
|
|
33201
|
-
|
|
33202
|
-
|
|
33203
|
-
|
|
33204
|
-
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
|
|
33208
|
-
|
|
33209
|
-
|
|
33210
|
-
|
|
33211
|
-
|
|
33212
|
-
|
|
33213
|
-
const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
|
|
33214
|
-
log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
|
|
33246
|
+
for (const t of Array.from(state.tools.values())) {
|
|
33247
|
+
if (!t.closed && t.buffered && !t.started) {
|
|
33248
|
+
if (toolSchemas && toolSchemas.length > 0) {
|
|
33249
|
+
const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
|
|
33250
|
+
if (validation.valid || validation.repaired && validation.repairedArgs) {
|
|
33251
|
+
const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
|
|
33252
|
+
log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
|
|
33253
|
+
send("content_block_start", {
|
|
33254
|
+
type: "content_block_start",
|
|
33255
|
+
index: t.blockIndex,
|
|
33256
|
+
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
33257
|
+
});
|
|
33258
|
+
send("content_block_delta", {
|
|
33259
|
+
type: "content_block_delta",
|
|
33260
|
+
index: t.blockIndex,
|
|
33261
|
+
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
33262
|
+
});
|
|
33263
|
+
send("content_block_stop", {
|
|
33264
|
+
type: "content_block_stop",
|
|
33265
|
+
index: t.blockIndex
|
|
33266
|
+
});
|
|
33267
|
+
t.started = true;
|
|
33268
|
+
t.closed = true;
|
|
33269
|
+
} else {
|
|
33270
|
+
log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
|
|
33271
|
+
t.closed = true;
|
|
33272
|
+
}
|
|
33273
|
+
} else {
|
|
33274
|
+
const argsJson = repairArgs(t.name, t.arguments || "{}");
|
|
33275
|
+
log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
|
|
33215
33276
|
send("content_block_start", {
|
|
33216
33277
|
type: "content_block_start",
|
|
33217
33278
|
index: t.blockIndex,
|
|
@@ -33228,83 +33289,51 @@ data: ${JSON.stringify(d)}
|
|
|
33228
33289
|
});
|
|
33229
33290
|
t.started = true;
|
|
33230
33291
|
t.closed = true;
|
|
33231
|
-
} else {
|
|
33232
|
-
log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
|
|
33233
|
-
t.closed = true;
|
|
33234
33292
|
}
|
|
33235
|
-
}
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
|
|
33239
|
-
|
|
33240
|
-
index: t.blockIndex,
|
|
33241
|
-
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
33242
|
-
});
|
|
33243
|
-
send("content_block_delta", {
|
|
33244
|
-
type: "content_block_delta",
|
|
33245
|
-
index: t.blockIndex,
|
|
33246
|
-
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
33247
|
-
});
|
|
33248
|
-
send("content_block_stop", {
|
|
33249
|
-
type: "content_block_stop",
|
|
33250
|
-
index: t.blockIndex
|
|
33251
|
-
});
|
|
33252
|
-
t.started = true;
|
|
33293
|
+
}
|
|
33294
|
+
}
|
|
33295
|
+
for (const t of Array.from(state.tools.values())) {
|
|
33296
|
+
if (t.started && !t.closed) {
|
|
33297
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
33253
33298
|
t.closed = true;
|
|
33254
33299
|
}
|
|
33255
33300
|
}
|
|
33256
|
-
|
|
33257
|
-
|
|
33258
|
-
if (t.started && !t.closed) {
|
|
33259
|
-
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
33260
|
-
t.closed = true;
|
|
33301
|
+
if (middlewareManager) {
|
|
33302
|
+
await middlewareManager.afterStreamComplete(target, streamMetadata);
|
|
33261
33303
|
}
|
|
33262
|
-
|
|
33263
|
-
|
|
33264
|
-
|
|
33265
|
-
|
|
33266
|
-
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
33270
|
-
|
|
33271
|
-
|
|
33272
|
-
|
|
33273
|
-
|
|
33274
|
-
|
|
33304
|
+
if (reason === "error") {
|
|
33305
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
33306
|
+
} else {
|
|
33307
|
+
const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
|
|
33308
|
+
const truncated = state.finishReason === "length";
|
|
33309
|
+
const refused = state.finishReason === "content_filter";
|
|
33310
|
+
const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
|
|
33311
|
+
if (truncated || refused) {
|
|
33312
|
+
log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
|
|
33313
|
+
}
|
|
33314
|
+
send("message_delta", {
|
|
33315
|
+
type: "message_delta",
|
|
33316
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
33317
|
+
usage: {
|
|
33318
|
+
...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
|
|
33319
|
+
output_tokens: state.usage?.completion_tokens || 0
|
|
33320
|
+
}
|
|
33321
|
+
});
|
|
33322
|
+
behavior?.onTurnEnd?.();
|
|
33323
|
+
send("message_stop", { type: "message_stop" });
|
|
33275
33324
|
}
|
|
33276
|
-
|
|
33277
|
-
|
|
33278
|
-
|
|
33279
|
-
|
|
33280
|
-
|
|
33281
|
-
|
|
33325
|
+
if (onTokenUpdate) {
|
|
33326
|
+
if (state.usage) {
|
|
33327
|
+
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
33328
|
+
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
|
|
33329
|
+
} else {
|
|
33330
|
+
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
33331
|
+
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
33332
|
+
onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
|
|
33282
33333
|
}
|
|
33283
|
-
});
|
|
33284
|
-
behavior?.onTurnEnd?.();
|
|
33285
|
-
send("message_stop", { type: "message_stop" });
|
|
33286
|
-
}
|
|
33287
|
-
if (onTokenUpdate) {
|
|
33288
|
-
if (state.usage) {
|
|
33289
|
-
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
33290
|
-
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
|
|
33291
|
-
} else {
|
|
33292
|
-
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
33293
|
-
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
33294
|
-
onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
|
|
33295
33334
|
}
|
|
33296
|
-
}
|
|
33297
|
-
|
|
33298
|
-
try {
|
|
33299
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
33300
|
-
|
|
33301
|
-
|
|
33302
|
-
`));
|
|
33303
|
-
} catch (e) {}
|
|
33304
|
-
controller.close();
|
|
33305
|
-
isClosed = true;
|
|
33306
|
-
if (ping)
|
|
33307
|
-
clearInterval(ping);
|
|
33335
|
+
} finally {
|
|
33336
|
+
teardown();
|
|
33308
33337
|
}
|
|
33309
33338
|
};
|
|
33310
33339
|
try {
|
|
@@ -39184,62 +39213,70 @@ data: ${JSON.stringify(data)}
|
|
|
39184
39213
|
send("ping", { type: "ping" });
|
|
39185
39214
|
}
|
|
39186
39215
|
}, 1000);
|
|
39216
|
+
const teardown = () => {
|
|
39217
|
+
if (!isClosed) {
|
|
39218
|
+
isClosed = true;
|
|
39219
|
+
try {
|
|
39220
|
+
controller.close();
|
|
39221
|
+
} catch {}
|
|
39222
|
+
}
|
|
39223
|
+
if (pingInterval) {
|
|
39224
|
+
clearInterval(pingInterval);
|
|
39225
|
+
pingInterval = null;
|
|
39226
|
+
}
|
|
39227
|
+
};
|
|
39187
39228
|
const finalize = async (reason, err) => {
|
|
39188
|
-
if (finalized2)
|
|
39229
|
+
if (finalized2) {
|
|
39230
|
+
teardown();
|
|
39189
39231
|
return;
|
|
39190
|
-
finalized2 = true;
|
|
39191
|
-
if (thinkingStarted) {
|
|
39192
|
-
send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
|
|
39193
39232
|
}
|
|
39194
|
-
|
|
39195
|
-
|
|
39196
|
-
|
|
39197
|
-
|
|
39198
|
-
if (t.started && !t.closed) {
|
|
39199
|
-
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39200
|
-
t.closed = true;
|
|
39233
|
+
finalized2 = true;
|
|
39234
|
+
try {
|
|
39235
|
+
if (thinkingStarted) {
|
|
39236
|
+
send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
|
|
39201
39237
|
}
|
|
39202
|
-
|
|
39203
|
-
|
|
39204
|
-
await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
|
|
39205
|
-
}
|
|
39206
|
-
const inputTokens = usage?.promptTokenCount || 0;
|
|
39207
|
-
const outputTokens = usage?.candidatesTokenCount || 0;
|
|
39208
|
-
if (usage) {
|
|
39209
|
-
log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
|
|
39210
|
-
}
|
|
39211
|
-
if (opts.onTokenUpdate) {
|
|
39212
|
-
opts.onTokenUpdate(inputTokens, outputTokens);
|
|
39213
|
-
}
|
|
39214
|
-
if (reason === "error") {
|
|
39215
|
-
log(`[GeminiSSE] Stream error: ${err}`);
|
|
39216
|
-
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39217
|
-
} else {
|
|
39218
|
-
const hasToolCalls = toolCalls.size > 0;
|
|
39219
|
-
const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
|
|
39220
|
-
if (truncated) {
|
|
39221
|
-
log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
|
|
39238
|
+
if (textStarted) {
|
|
39239
|
+
send("content_block_stop", { type: "content_block_stop", index: textIdx });
|
|
39222
39240
|
}
|
|
39223
|
-
|
|
39224
|
-
|
|
39225
|
-
|
|
39226
|
-
|
|
39227
|
-
...inputTokens > 0 ? { input_tokens: inputTokens } : {},
|
|
39228
|
-
output_tokens: outputTokens
|
|
39241
|
+
for (const t of toolCalls.values()) {
|
|
39242
|
+
if (t.started && !t.closed) {
|
|
39243
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39244
|
+
t.closed = true;
|
|
39229
39245
|
}
|
|
39230
|
-
});
|
|
39231
|
-
opts.onTurnEnd?.();
|
|
39232
|
-
send("message_stop", { type: "message_stop" });
|
|
39233
|
-
}
|
|
39234
|
-
if (!isClosed) {
|
|
39235
|
-
isClosed = true;
|
|
39236
|
-
if (pingInterval) {
|
|
39237
|
-
clearInterval(pingInterval);
|
|
39238
|
-
pingInterval = null;
|
|
39239
39246
|
}
|
|
39240
|
-
|
|
39241
|
-
|
|
39242
|
-
}
|
|
39247
|
+
if (opts.middlewareManager) {
|
|
39248
|
+
await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
|
|
39249
|
+
}
|
|
39250
|
+
const inputTokens = usage?.promptTokenCount || 0;
|
|
39251
|
+
const outputTokens = usage?.candidatesTokenCount || 0;
|
|
39252
|
+
if (usage) {
|
|
39253
|
+
log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
|
|
39254
|
+
}
|
|
39255
|
+
if (opts.onTokenUpdate) {
|
|
39256
|
+
opts.onTokenUpdate(inputTokens, outputTokens);
|
|
39257
|
+
}
|
|
39258
|
+
if (reason === "error") {
|
|
39259
|
+
log(`[GeminiSSE] Stream error: ${err}`);
|
|
39260
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39261
|
+
} else {
|
|
39262
|
+
const hasToolCalls = toolCalls.size > 0;
|
|
39263
|
+
const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
|
|
39264
|
+
if (truncated) {
|
|
39265
|
+
log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
|
|
39266
|
+
}
|
|
39267
|
+
send("message_delta", {
|
|
39268
|
+
type: "message_delta",
|
|
39269
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
39270
|
+
usage: {
|
|
39271
|
+
...inputTokens > 0 ? { input_tokens: inputTokens } : {},
|
|
39272
|
+
output_tokens: outputTokens
|
|
39273
|
+
}
|
|
39274
|
+
});
|
|
39275
|
+
opts.onTurnEnd?.();
|
|
39276
|
+
send("message_stop", { type: "message_stop" });
|
|
39277
|
+
}
|
|
39278
|
+
} finally {
|
|
39279
|
+
teardown();
|
|
39243
39280
|
}
|
|
39244
39281
|
};
|
|
39245
39282
|
try {
|
|
@@ -39457,38 +39494,48 @@ data: ${JSON.stringify(data)}
|
|
|
39457
39494
|
send("ping", { type: "ping" });
|
|
39458
39495
|
}
|
|
39459
39496
|
}, 1000);
|
|
39460
|
-
|
|
39461
|
-
|
|
39462
|
-
return;
|
|
39463
|
-
if (textStarted) {
|
|
39464
|
-
send("content_block_stop", { type: "content_block_stop", index: 0 });
|
|
39465
|
-
}
|
|
39466
|
-
if (reason === "error") {
|
|
39467
|
-
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39468
|
-
} else {
|
|
39469
|
-
send("message_delta", {
|
|
39470
|
-
type: "message_delta",
|
|
39471
|
-
delta: { stop_reason: "end_turn", stop_sequence: null },
|
|
39472
|
-
usage: {
|
|
39473
|
-
...promptTokens > 0 ? { input_tokens: promptTokens } : {},
|
|
39474
|
-
output_tokens: completionTokens
|
|
39475
|
-
}
|
|
39476
|
-
});
|
|
39477
|
-
send("message_stop", { type: "message_stop" });
|
|
39478
|
-
}
|
|
39479
|
-
if (opts.onTokenUpdate) {
|
|
39480
|
-
opts.onTokenUpdate(promptTokens, completionTokens);
|
|
39481
|
-
}
|
|
39497
|
+
let finalized2 = false;
|
|
39498
|
+
const teardown = () => {
|
|
39482
39499
|
if (!isClosed) {
|
|
39483
39500
|
isClosed = true;
|
|
39484
|
-
if (pingInterval) {
|
|
39485
|
-
clearInterval(pingInterval);
|
|
39486
|
-
pingInterval = null;
|
|
39487
|
-
}
|
|
39488
39501
|
try {
|
|
39489
39502
|
controller.close();
|
|
39490
39503
|
} catch {}
|
|
39491
39504
|
}
|
|
39505
|
+
if (pingInterval) {
|
|
39506
|
+
clearInterval(pingInterval);
|
|
39507
|
+
pingInterval = null;
|
|
39508
|
+
}
|
|
39509
|
+
};
|
|
39510
|
+
const finalize = (reason, err) => {
|
|
39511
|
+
if (finalized2) {
|
|
39512
|
+
teardown();
|
|
39513
|
+
return;
|
|
39514
|
+
}
|
|
39515
|
+
finalized2 = true;
|
|
39516
|
+
try {
|
|
39517
|
+
if (textStarted) {
|
|
39518
|
+
send("content_block_stop", { type: "content_block_stop", index: 0 });
|
|
39519
|
+
}
|
|
39520
|
+
if (reason === "error") {
|
|
39521
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39522
|
+
} else {
|
|
39523
|
+
send("message_delta", {
|
|
39524
|
+
type: "message_delta",
|
|
39525
|
+
delta: { stop_reason: "end_turn", stop_sequence: null },
|
|
39526
|
+
usage: {
|
|
39527
|
+
...promptTokens > 0 ? { input_tokens: promptTokens } : {},
|
|
39528
|
+
output_tokens: completionTokens
|
|
39529
|
+
}
|
|
39530
|
+
});
|
|
39531
|
+
send("message_stop", { type: "message_stop" });
|
|
39532
|
+
}
|
|
39533
|
+
if (opts.onTokenUpdate) {
|
|
39534
|
+
opts.onTokenUpdate(promptTokens, completionTokens);
|
|
39535
|
+
}
|
|
39536
|
+
} finally {
|
|
39537
|
+
teardown();
|
|
39538
|
+
}
|
|
39492
39539
|
};
|
|
39493
39540
|
try {
|
|
39494
39541
|
const reader = response.body.getReader();
|
|
@@ -39975,10 +40022,26 @@ class TokenTracker {
|
|
|
39975
40022
|
modelNameOverride;
|
|
39976
40023
|
planUsage;
|
|
39977
40024
|
lastPlanSerialized = "";
|
|
40025
|
+
toolCallsByName = new Map;
|
|
40026
|
+
startedAt = Date.now();
|
|
40027
|
+
sessionBilledInputTokens = 0;
|
|
39978
40028
|
constructor(port, config2) {
|
|
39979
40029
|
this.port = port;
|
|
39980
40030
|
this.config = config2;
|
|
39981
40031
|
}
|
|
40032
|
+
recordToolUse(name) {
|
|
40033
|
+
const key = name.trim() || "unknown";
|
|
40034
|
+
this.toolCallsByName.set(key, (this.toolCallsByName.get(key) ?? 0) + 1);
|
|
40035
|
+
}
|
|
40036
|
+
getToolCallCount() {
|
|
40037
|
+
let n = 0;
|
|
40038
|
+
for (const v of this.toolCallsByName.values())
|
|
40039
|
+
n += v;
|
|
40040
|
+
return n;
|
|
40041
|
+
}
|
|
40042
|
+
getToolCalls() {
|
|
40043
|
+
return [...this.toolCallsByName].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
|
|
40044
|
+
}
|
|
39982
40045
|
setActiveModelName(name) {
|
|
39983
40046
|
this.modelNameOverride = name;
|
|
39984
40047
|
}
|
|
@@ -40000,6 +40063,7 @@ class TokenTracker {
|
|
|
40000
40063
|
this.sessionInputTokens = inputTokens;
|
|
40001
40064
|
this.lastInputTokens = inputTokens;
|
|
40002
40065
|
this.sessionOutputTokens += outputTokens;
|
|
40066
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
40003
40067
|
const pricing = this.getPricing();
|
|
40004
40068
|
const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
40005
40069
|
this.sessionTotalCost += cost;
|
|
@@ -40012,6 +40076,7 @@ class TokenTracker {
|
|
|
40012
40076
|
const pricing = this.getPricing();
|
|
40013
40077
|
const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M;
|
|
40014
40078
|
this.sessionTotalCost = cost;
|
|
40079
|
+
this.sessionBilledInputTokens = this.sessionInputTokens;
|
|
40015
40080
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
40016
40081
|
}
|
|
40017
40082
|
updateWithDelta(inputTokens, outputTokens) {
|
|
@@ -40030,6 +40095,7 @@ class TokenTracker {
|
|
|
40030
40095
|
}
|
|
40031
40096
|
this.sessionOutputTokens += outputTokens;
|
|
40032
40097
|
const pricing = this.getPricing();
|
|
40098
|
+
this.sessionBilledInputTokens += incrementalInputTokens;
|
|
40033
40099
|
const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
40034
40100
|
this.sessionTotalCost += cost;
|
|
40035
40101
|
this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
@@ -40038,6 +40104,7 @@ class TokenTracker {
|
|
|
40038
40104
|
this.sessionInputTokens = inputTokens;
|
|
40039
40105
|
this.lastInputTokens = inputTokens;
|
|
40040
40106
|
this.sessionOutputTokens += outputTokens;
|
|
40107
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
40041
40108
|
if (typeof actualCost === "number" && actualCost > 0) {
|
|
40042
40109
|
this.sessionTotalCost += actualCost;
|
|
40043
40110
|
log(`[TokenTracker] Actual cost from API: $${actualCost.toFixed(6)}`);
|
|
@@ -40055,6 +40122,7 @@ class TokenTracker {
|
|
|
40055
40122
|
this.lastInputTokens = inputTokens;
|
|
40056
40123
|
}
|
|
40057
40124
|
this.sessionOutputTokens += outputTokens;
|
|
40125
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
40058
40126
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens);
|
|
40059
40127
|
}
|
|
40060
40128
|
setContextWindow(contextWindow) {
|
|
@@ -40107,7 +40175,12 @@ class TokenTracker {
|
|
|
40107
40175
|
provider_name: this.getDisplayName(),
|
|
40108
40176
|
updated_at: Date.now(),
|
|
40109
40177
|
is_free: isFreeModel,
|
|
40110
|
-
is_estimated: isEstimate || false
|
|
40178
|
+
is_estimated: isEstimate || false,
|
|
40179
|
+
started_at: this.startedAt,
|
|
40180
|
+
tool_calls: this.getToolCalls(),
|
|
40181
|
+
billed_input_tokens: this.sessionBilledInputTokens,
|
|
40182
|
+
input_per_m: pricing.inputCostPer1M,
|
|
40183
|
+
output_per_m: pricing.outputCostPer1M
|
|
40111
40184
|
};
|
|
40112
40185
|
const displayModel = stripProviderPrefix(this.modelNameOverride || this.config.modelName || "");
|
|
40113
40186
|
if (displayModel) {
|
|
@@ -40813,14 +40886,18 @@ class ComposedHandler {
|
|
|
40813
40886
|
};
|
|
40814
40887
|
const streamFormat = this.resolveStreamFormat();
|
|
40815
40888
|
const priorInputTokens = this.tokenTracker.getLastInputTokens();
|
|
40889
|
+
const observeToolCall = (name) => {
|
|
40890
|
+
this.tokenTracker.recordToolUse(name);
|
|
40891
|
+
behaviorSession?.observeToolCall(name);
|
|
40892
|
+
};
|
|
40816
40893
|
switch (streamFormat) {
|
|
40817
40894
|
case "openai-sse":
|
|
40818
|
-
return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens,
|
|
40819
|
-
shouldBufferTool: (name) => behaviorSession
|
|
40820
|
-
onToolCall: (name, argsJson) => behaviorSession
|
|
40821
|
-
onAssistantText: (text, kind) => behaviorSession
|
|
40822
|
-
onToolCallObserved:
|
|
40823
|
-
onTurnEnd: () => behaviorSession
|
|
40895
|
+
return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, {
|
|
40896
|
+
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40897
|
+
onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40898
|
+
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40899
|
+
onToolCallObserved: observeToolCall,
|
|
40900
|
+
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40824
40901
|
});
|
|
40825
40902
|
case "openai-responses-sse":
|
|
40826
40903
|
return createResponsesStreamHandler(c, response, {
|
|
@@ -40834,7 +40911,7 @@ class ComposedHandler {
|
|
|
40834
40911
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40835
40912
|
onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40836
40913
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40837
|
-
onToolCallObserved:
|
|
40914
|
+
onToolCallObserved: observeToolCall,
|
|
40838
40915
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40839
40916
|
});
|
|
40840
40917
|
case "anthropic-sse":
|
|
@@ -40845,7 +40922,7 @@ class ComposedHandler {
|
|
|
40845
40922
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40846
40923
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40847
40924
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40848
|
-
onToolCallObserved:
|
|
40925
|
+
onToolCallObserved: observeToolCall,
|
|
40849
40926
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40850
40927
|
});
|
|
40851
40928
|
case "gemini-sse": {
|
|
@@ -40862,7 +40939,7 @@ class ComposedHandler {
|
|
|
40862
40939
|
onToolCall,
|
|
40863
40940
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40864
40941
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40865
|
-
onToolCallObserved:
|
|
40942
|
+
onToolCallObserved: observeToolCall,
|
|
40866
40943
|
onTurnEnd: () => behaviorSession?.finishTurn(),
|
|
40867
40944
|
unwrapResponse: this.options.unwrapGeminiResponse,
|
|
40868
40945
|
priorInputTokens
|
|
@@ -40882,7 +40959,7 @@ class ComposedHandler {
|
|
|
40882
40959
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40883
40960
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40884
40961
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40885
|
-
onToolCallObserved:
|
|
40962
|
+
onToolCallObserved: observeToolCall,
|
|
40886
40963
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40887
40964
|
});
|
|
40888
40965
|
case "ollama-jsonl":
|
|
@@ -42891,6 +42968,9 @@ async function routeBare(model, nativeProvider, rules, defaultProvider, cachePat
|
|
|
42891
42968
|
const [primary, ...fallbacks] = credentialed;
|
|
42892
42969
|
return { kind: "ok", primary, fallbacks };
|
|
42893
42970
|
}
|
|
42971
|
+
function normalizeGlmSlug(model) {
|
|
42972
|
+
return model.replace(/^glm-(\d+)-(\d+)(-.*)?$/i, (_m, major, minor, suffix) => `glm-${major}.${minor}${suffix ?? ""}`);
|
|
42973
|
+
}
|
|
42894
42974
|
async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
|
|
42895
42975
|
const parsed = parseModelSpec(modelSpec);
|
|
42896
42976
|
if (parsed.isExplicitProvider) {
|
|
@@ -42898,7 +42978,7 @@ async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePat
|
|
|
42898
42978
|
}
|
|
42899
42979
|
const rules = rulesOverride ?? loadRoutingRules();
|
|
42900
42980
|
const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
|
|
42901
|
-
return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
|
|
42981
|
+
return routeBare(normalizeGlmSlug(parsed.model), parsed.provider, rules, defaultProvider, cachePath);
|
|
42902
42982
|
}
|
|
42903
42983
|
var init_routing_rules = __esm(() => {
|
|
42904
42984
|
init_model_catalog();
|
|
@@ -68509,6 +68589,8 @@ Usage: claudish --models --provider <slug>`);
|
|
|
68509
68589
|
if (rest.length > 0)
|
|
68510
68590
|
config3._hasPositionalPrompt = true;
|
|
68511
68591
|
break;
|
|
68592
|
+
} else if (arg === "--resume" && (i + 1 >= args.length || args[i + 1].startsWith("-"))) {
|
|
68593
|
+
config3._resumePicker = true;
|
|
68512
68594
|
} else if (arg.startsWith("-")) {
|
|
68513
68595
|
config3.claudeArgs.push(arg);
|
|
68514
68596
|
if (arg === "-p" || arg === "--print") {
|
|
@@ -78411,12 +78493,2864 @@ var init_team_grid = __esm(() => {
|
|
|
78411
78493
|
];
|
|
78412
78494
|
});
|
|
78413
78495
|
|
|
78496
|
+
// src/tui/viz/text.ts
|
|
78497
|
+
function columns(n, fn, arg = "width") {
|
|
78498
|
+
if (!Number.isFinite(n))
|
|
78499
|
+
throw new RangeError(`${fn}: ${arg} must be a finite number, got ${n}`);
|
|
78500
|
+
return Math.max(0, Math.floor(n));
|
|
78501
|
+
}
|
|
78502
|
+
function isWide2(cp) {
|
|
78503
|
+
let lo = 0;
|
|
78504
|
+
let hi = WIDE.length - 1;
|
|
78505
|
+
while (lo <= hi) {
|
|
78506
|
+
const mid = lo + hi >> 1;
|
|
78507
|
+
const [a, b] = WIDE[mid];
|
|
78508
|
+
if (cp < a)
|
|
78509
|
+
hi = mid - 1;
|
|
78510
|
+
else if (cp > b)
|
|
78511
|
+
lo = mid + 1;
|
|
78512
|
+
else
|
|
78513
|
+
return true;
|
|
78514
|
+
}
|
|
78515
|
+
return false;
|
|
78516
|
+
}
|
|
78517
|
+
function fallbackClusterWidth(cluster) {
|
|
78518
|
+
const cp = cluster.codePointAt(0) ?? 0;
|
|
78519
|
+
if (cp < 32 || cp >= 127 && cp <= 159 || LEADING_FORMAT.test(cluster))
|
|
78520
|
+
return 0;
|
|
78521
|
+
return isWide2(cp) || LEADING_EMOJI_PRESENTATION.test(cluster) || cluster.includes(VS16) ? 2 : 1;
|
|
78522
|
+
}
|
|
78523
|
+
function displayWidth(s) {
|
|
78524
|
+
if (NATIVE_WIDTH)
|
|
78525
|
+
return NATIVE_WIDTH(s);
|
|
78526
|
+
let w = 0;
|
|
78527
|
+
for (const { segment } of seg.segment(s))
|
|
78528
|
+
w += fallbackClusterWidth(segment);
|
|
78529
|
+
return w;
|
|
78530
|
+
}
|
|
78531
|
+
function truncate3(s, width) {
|
|
78532
|
+
const n = columns(width, "truncate");
|
|
78533
|
+
if (n === 0)
|
|
78534
|
+
return "";
|
|
78535
|
+
const clean = sanitize2(s);
|
|
78536
|
+
if (displayWidth(clean) <= n)
|
|
78537
|
+
return clean;
|
|
78538
|
+
let out = "";
|
|
78539
|
+
let w = 0;
|
|
78540
|
+
for (const { segment } of seg.segment(clean)) {
|
|
78541
|
+
const cw = displayWidth(segment);
|
|
78542
|
+
if (w + cw > n - 1)
|
|
78543
|
+
break;
|
|
78544
|
+
out += segment;
|
|
78545
|
+
w += cw;
|
|
78546
|
+
}
|
|
78547
|
+
return `${out}\u2026`;
|
|
78548
|
+
}
|
|
78549
|
+
function padTo(s, width) {
|
|
78550
|
+
const n = columns(width, "padTo");
|
|
78551
|
+
if (n === 0)
|
|
78552
|
+
return "";
|
|
78553
|
+
const clean = sanitize2(s);
|
|
78554
|
+
const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
|
|
78555
|
+
return clipped + " ".repeat(Math.max(0, n - displayWidth(clipped)));
|
|
78556
|
+
}
|
|
78557
|
+
function padStartTo(s, width) {
|
|
78558
|
+
const n = columns(width, "padStartTo");
|
|
78559
|
+
if (n === 0)
|
|
78560
|
+
return "";
|
|
78561
|
+
const clean = sanitize2(s);
|
|
78562
|
+
const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
|
|
78563
|
+
return " ".repeat(Math.max(0, n - displayWidth(clipped))) + clipped;
|
|
78564
|
+
}
|
|
78565
|
+
function splitCells(parts, cells) {
|
|
78566
|
+
const n = columns(cells, "splitCells", "cells");
|
|
78567
|
+
const finite = parts.map((p) => Number.isFinite(p) && p > 0 ? p : 0);
|
|
78568
|
+
const out = finite.map(() => 0);
|
|
78569
|
+
let safe = finite;
|
|
78570
|
+
let sum = finite.reduce((a, b) => a + b, 0);
|
|
78571
|
+
if (!Number.isFinite(sum)) {
|
|
78572
|
+
const max = finite.reduce((a, b) => b > a ? b : a, 0);
|
|
78573
|
+
safe = finite.map((p) => p / max);
|
|
78574
|
+
sum = safe.reduce((a, b) => a + b, 0);
|
|
78575
|
+
}
|
|
78576
|
+
if (n <= 0 || sum <= 0)
|
|
78577
|
+
return out;
|
|
78578
|
+
const byShare = safe.map((p, i) => ({ p, i })).sort((a, b) => b.p - a.p);
|
|
78579
|
+
if (n < byShare.filter(({ p }) => p > 0).length) {
|
|
78580
|
+
for (const { i } of byShare.slice(0, n))
|
|
78581
|
+
out[i] = 1;
|
|
78582
|
+
return out;
|
|
78583
|
+
}
|
|
78584
|
+
const exact = safe.map((p) => n * (p / sum));
|
|
78585
|
+
exact.forEach((e, i) => {
|
|
78586
|
+
out[i] = Math.floor(e);
|
|
78587
|
+
});
|
|
78588
|
+
const rank = exact.map((e, i) => ({ rem: e % 1, i })).sort((a, b) => b.rem - a.rem);
|
|
78589
|
+
for (let k = 0, left = n - out.reduce((a, b) => a + b, 0);left > 0; k++, left--)
|
|
78590
|
+
out[rank[k % rank.length].i] += 1;
|
|
78591
|
+
for (const { p, i } of byShare)
|
|
78592
|
+
if (p > 0 && out[i] === 0) {
|
|
78593
|
+
const d = byShare.reduce((m, c) => out[c.i] > out[m] ? c.i : m, 0);
|
|
78594
|
+
if (out[d] > 1) {
|
|
78595
|
+
out[d] -= 1;
|
|
78596
|
+
out[i] = 1;
|
|
78597
|
+
}
|
|
78598
|
+
}
|
|
78599
|
+
return out;
|
|
78600
|
+
}
|
|
78601
|
+
var NATIVE_WIDTH, WIDE, seg, VS16 = "\uFE0F", LEADING_FORMAT, LEADING_EMOJI_PRESENTATION, CONTROL, sanitize2 = (s) => s.replace(CONTROL, " ");
|
|
78602
|
+
var init_text = __esm(() => {
|
|
78603
|
+
NATIVE_WIDTH = (() => {
|
|
78604
|
+
const b = globalThis.Bun;
|
|
78605
|
+
return typeof b?.stringWidth === "function" ? b.stringWidth.bind(b) : null;
|
|
78606
|
+
})();
|
|
78607
|
+
WIDE = [
|
|
78608
|
+
[4352, 4447],
|
|
78609
|
+
[9001, 9002],
|
|
78610
|
+
[11904, 12350],
|
|
78611
|
+
[12353, 12771],
|
|
78612
|
+
[12774, 12871],
|
|
78613
|
+
[12880, 13311],
|
|
78614
|
+
[13312, 19903],
|
|
78615
|
+
[19968, 40959],
|
|
78616
|
+
[40960, 42191],
|
|
78617
|
+
[43360, 43391],
|
|
78618
|
+
[44032, 55203],
|
|
78619
|
+
[63744, 64255],
|
|
78620
|
+
[65040, 65049],
|
|
78621
|
+
[65072, 65135],
|
|
78622
|
+
[65280, 65376],
|
|
78623
|
+
[65504, 65510],
|
|
78624
|
+
[94176, 94180],
|
|
78625
|
+
[94192, 94193],
|
|
78626
|
+
[94208, 100343],
|
|
78627
|
+
[100352, 101589],
|
|
78628
|
+
[101632, 101640],
|
|
78629
|
+
[110576, 110579],
|
|
78630
|
+
[110581, 110587],
|
|
78631
|
+
[110589, 110590],
|
|
78632
|
+
[110592, 110882],
|
|
78633
|
+
[110898, 110898],
|
|
78634
|
+
[110928, 110930],
|
|
78635
|
+
[110933, 110933],
|
|
78636
|
+
[110948, 110951],
|
|
78637
|
+
[110960, 111355],
|
|
78638
|
+
[127488, 127490],
|
|
78639
|
+
[127504, 127547],
|
|
78640
|
+
[127552, 127560],
|
|
78641
|
+
[127568, 127569],
|
|
78642
|
+
[127584, 127589],
|
|
78643
|
+
[131072, 196605],
|
|
78644
|
+
[196608, 262141]
|
|
78645
|
+
];
|
|
78646
|
+
seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
78647
|
+
LEADING_FORMAT = /^\p{Cf}/u;
|
|
78648
|
+
LEADING_EMOJI_PRESENTATION = /^\p{Emoji_Presentation}/u;
|
|
78649
|
+
CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
78650
|
+
});
|
|
78651
|
+
|
|
78652
|
+
// src/tui/viz/tokens.ts
|
|
78653
|
+
var tokens, ramps;
|
|
78654
|
+
var init_tokens = __esm(() => {
|
|
78655
|
+
init_theme2();
|
|
78656
|
+
tokens = {
|
|
78657
|
+
fatal: C.red,
|
|
78658
|
+
error: C.red,
|
|
78659
|
+
warn: C.orange,
|
|
78660
|
+
info: C.cyan,
|
|
78661
|
+
debug: C.fgMuted,
|
|
78662
|
+
trace: C.dim,
|
|
78663
|
+
success: C.green,
|
|
78664
|
+
running: C.blue,
|
|
78665
|
+
idle: C.fgMuted,
|
|
78666
|
+
dead: C.dim,
|
|
78667
|
+
border: C.border,
|
|
78668
|
+
subtle: C.dim,
|
|
78669
|
+
text: C.fg,
|
|
78670
|
+
accent: C.focusBorder,
|
|
78671
|
+
bgPanel: C.bgAlt,
|
|
78672
|
+
ink: C.black
|
|
78673
|
+
};
|
|
78674
|
+
ramps = {
|
|
78675
|
+
load: [tokens.success, C.yellow, tokens.error],
|
|
78676
|
+
temperature: [tokens.running, tokens.success, C.orange, tokens.error],
|
|
78677
|
+
network: [tokens.success, C.yellow, tokens.error],
|
|
78678
|
+
savings: [tokens.error, C.yellow, tokens.success],
|
|
78679
|
+
volume: [C.border, C.blue, C.cyan]
|
|
78680
|
+
};
|
|
78681
|
+
});
|
|
78682
|
+
|
|
78683
|
+
// src/tui/viz/color.ts
|
|
78684
|
+
import { RGBA, parseColor, rgbToHex } from "@opentui/core";
|
|
78685
|
+
function mix(a, b, t) {
|
|
78686
|
+
const k = clamp01(t);
|
|
78687
|
+
return hex3(RGBA.fromValues(a.r + (b.r - a.r) * k, a.g + (b.g - a.g) * k, a.b + (b.b - a.b) * k, a.a + (b.a - a.a) * k));
|
|
78688
|
+
}
|
|
78689
|
+
function blend1D(steps, from, to) {
|
|
78690
|
+
if (!Number.isInteger(steps) || steps <= 0)
|
|
78691
|
+
return [];
|
|
78692
|
+
const a = rgba(from);
|
|
78693
|
+
if (steps === 1)
|
|
78694
|
+
return [hex3(a)];
|
|
78695
|
+
const b = rgba(to);
|
|
78696
|
+
return Array.from({ length: steps }, (_, i) => mix(a, b, i / (steps - 1)));
|
|
78697
|
+
}
|
|
78698
|
+
function blendStops(steps, ...stops) {
|
|
78699
|
+
if (!Number.isInteger(steps) || steps <= 0 || stops.length === 0)
|
|
78700
|
+
return [];
|
|
78701
|
+
if (stops.length === 1 || steps === 1)
|
|
78702
|
+
return blend1D(steps, stops[0], stops.at(-1));
|
|
78703
|
+
const pts = stops.map(rgba);
|
|
78704
|
+
const segs = pts.length - 1;
|
|
78705
|
+
return Array.from({ length: steps }, (_, i) => {
|
|
78706
|
+
const p = i / (steps - 1) * segs;
|
|
78707
|
+
const s = Math.min(segs - 1, Math.floor(p));
|
|
78708
|
+
return mix(pts[s], pts[s + 1], p - s);
|
|
78709
|
+
});
|
|
78710
|
+
}
|
|
78711
|
+
function luminance(c) {
|
|
78712
|
+
const f = (v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
|
|
78713
|
+
return 0.2126 * f(clamp01(c.r)) + 0.7152 * f(clamp01(c.g)) + 0.0722 * f(clamp01(c.b));
|
|
78714
|
+
}
|
|
78715
|
+
function contrastRatio(a, b) {
|
|
78716
|
+
const [x, y] = [luminance(a), luminance(b)];
|
|
78717
|
+
return x >= y ? (x + 0.05) / (y + 0.05) : (y + 0.05) / (x + 0.05);
|
|
78718
|
+
}
|
|
78719
|
+
function over(fg, bg) {
|
|
78720
|
+
const a = clamp01(fg.a);
|
|
78721
|
+
if (a >= 1)
|
|
78722
|
+
return fg;
|
|
78723
|
+
return RGBA.fromValues(fg.r * a + bg.r * (1 - a), fg.g * a + bg.g * (1 - a), fg.b * a + bg.b * (1 - a), 1);
|
|
78724
|
+
}
|
|
78725
|
+
function pickInk(bg, dark = tokens.ink, light = tokens.text) {
|
|
78726
|
+
const surface = over(rgba(bg), rgba(tokens.bgPanel));
|
|
78727
|
+
const inkDark = over(rgba(dark), surface);
|
|
78728
|
+
const inkLight = over(rgba(light), surface);
|
|
78729
|
+
return hex3(contrastRatio(inkDark, surface) >= contrastRatio(inkLight, surface) ? inkDark : inkLight);
|
|
78730
|
+
}
|
|
78731
|
+
var rgba = (c) => parseColor(c), hex3 = (c) => rgbToHex(c), clamp01 = (n) => Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
|
|
78732
|
+
var init_color = __esm(() => {
|
|
78733
|
+
init_tokens();
|
|
78734
|
+
});
|
|
78735
|
+
|
|
78736
|
+
// src/tui/viz/widgets.tsx
|
|
78737
|
+
import { createTextAttributes as createTextAttributes2 } from "@opentui/core";
|
|
78738
|
+
import { jsxDEV as jsxDEV18, Fragment as Fragment11 } from "@opentui/react/jsx-dev-runtime";
|
|
78739
|
+
function rampFor(width, stops) {
|
|
78740
|
+
const use = stops.length > 0 ? stops : ramps.load;
|
|
78741
|
+
const key = `${width}|${use.join(":")}`;
|
|
78742
|
+
const hit = RAMP_CACHE.get(key);
|
|
78743
|
+
if (hit)
|
|
78744
|
+
return hit;
|
|
78745
|
+
const built = blendStops(width, ...use);
|
|
78746
|
+
if (RAMP_CACHE.size >= RAMP_CACHE_MAX)
|
|
78747
|
+
RAMP_CACHE.delete(RAMP_CACHE.keys().next().value);
|
|
78748
|
+
RAMP_CACHE.set(key, built);
|
|
78749
|
+
return built;
|
|
78750
|
+
}
|
|
78751
|
+
function fillCells(pct, width) {
|
|
78752
|
+
const cells = Math.floor(width);
|
|
78753
|
+
if (!Number.isFinite(cells) || cells <= 0 || Number.isNaN(pct))
|
|
78754
|
+
return 0;
|
|
78755
|
+
return Math.round(Math.min(100, Math.max(0, pct)) / 100 * cells);
|
|
78756
|
+
}
|
|
78757
|
+
function MeterSpan({
|
|
78758
|
+
pct,
|
|
78759
|
+
width,
|
|
78760
|
+
ramp = ramps.load
|
|
78761
|
+
}) {
|
|
78762
|
+
const cells = Math.floor(width);
|
|
78763
|
+
if (!Number.isFinite(cells) || cells <= 0)
|
|
78764
|
+
return null;
|
|
78765
|
+
if (Number.isNaN(pct))
|
|
78766
|
+
return /* @__PURE__ */ jsxDEV18("span", {
|
|
78767
|
+
fg: tokens.dead,
|
|
78768
|
+
children: NODATA.repeat(cells)
|
|
78769
|
+
}, undefined, false, undefined, this);
|
|
78770
|
+
const cols = rampFor(cells, ramp);
|
|
78771
|
+
const filled = fillCells(pct, cells);
|
|
78772
|
+
return /* @__PURE__ */ jsxDEV18(Fragment11, {
|
|
78773
|
+
children: Array.from({ length: cells }, (_, i) => /* @__PURE__ */ jsxDEV18("span", {
|
|
78774
|
+
fg: i < filled ? cols[i] : tokens.border,
|
|
78775
|
+
children: i < filled ? FILL : TRACK
|
|
78776
|
+
}, i, false, undefined, this))
|
|
78777
|
+
}, undefined, false, undefined, this);
|
|
78778
|
+
}
|
|
78779
|
+
function Sparkline({
|
|
78780
|
+
values,
|
|
78781
|
+
fg = tokens.info,
|
|
78782
|
+
style,
|
|
78783
|
+
...layout
|
|
78784
|
+
}) {
|
|
78785
|
+
const row = sparkGlyphs(values);
|
|
78786
|
+
if (row === null)
|
|
78787
|
+
return null;
|
|
78788
|
+
return /* @__PURE__ */ jsxDEV18("text", {
|
|
78789
|
+
fg,
|
|
78790
|
+
flexShrink: 0,
|
|
78791
|
+
...layout,
|
|
78792
|
+
style,
|
|
78793
|
+
children: row
|
|
78794
|
+
}, undefined, false, undefined, this);
|
|
78795
|
+
}
|
|
78796
|
+
function sparkGlyphs(values) {
|
|
78797
|
+
if (values.length === 0)
|
|
78798
|
+
return null;
|
|
78799
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
78800
|
+
let min = Number.POSITIVE_INFINITY;
|
|
78801
|
+
for (const v of values)
|
|
78802
|
+
if (Number.isFinite(v)) {
|
|
78803
|
+
max = Math.max(max, v);
|
|
78804
|
+
min = Math.min(min, v);
|
|
78805
|
+
}
|
|
78806
|
+
if (max === Number.NEGATIVE_INFINITY)
|
|
78807
|
+
return GAP.repeat(values.length);
|
|
78808
|
+
const half = max / 2 - min / 2;
|
|
78809
|
+
const mid = SPARK[Math.floor((SPARK.length - 1) / 2)];
|
|
78810
|
+
const top = SPARK.length - 1;
|
|
78811
|
+
const glyph = (v) => SPARK[Math.min(top, Math.max(0, Math.round((v / 2 - min / 2) / half * top)))];
|
|
78812
|
+
return values.map((v) => !Number.isFinite(v) ? GAP : half > 0 ? glyph(v) : mid).join("");
|
|
78813
|
+
}
|
|
78814
|
+
function SparklineSpan({
|
|
78815
|
+
values,
|
|
78816
|
+
fg = tokens.info
|
|
78817
|
+
}) {
|
|
78818
|
+
const row = sparkGlyphs(values);
|
|
78819
|
+
return row === null ? null : /* @__PURE__ */ jsxDEV18("span", {
|
|
78820
|
+
fg,
|
|
78821
|
+
children: row
|
|
78822
|
+
}, undefined, false, undefined, this);
|
|
78823
|
+
}
|
|
78824
|
+
function badgePad(label, width) {
|
|
78825
|
+
const pad2 = Math.max(0, (width ?? 0) - displayWidth(label) - 2);
|
|
78826
|
+
return pad2 > 0 ? /* @__PURE__ */ jsxDEV18("span", {
|
|
78827
|
+
children: " ".repeat(pad2)
|
|
78828
|
+
}, undefined, false, undefined, this) : null;
|
|
78829
|
+
}
|
|
78830
|
+
function BadgeSpan({ label, bg, width }) {
|
|
78831
|
+
return /* @__PURE__ */ jsxDEV18(Fragment11, {
|
|
78832
|
+
children: [
|
|
78833
|
+
/* @__PURE__ */ jsxDEV18("span", {
|
|
78834
|
+
fg: pickInk(bg),
|
|
78835
|
+
bg,
|
|
78836
|
+
attributes: BOLD4,
|
|
78837
|
+
children: ` ${label} `
|
|
78838
|
+
}, undefined, false, undefined, this),
|
|
78839
|
+
badgePad(label, width)
|
|
78840
|
+
]
|
|
78841
|
+
}, undefined, true, undefined, this);
|
|
78842
|
+
}
|
|
78843
|
+
function Panel({
|
|
78844
|
+
title,
|
|
78845
|
+
focused = false,
|
|
78846
|
+
flush = false,
|
|
78847
|
+
children,
|
|
78848
|
+
style,
|
|
78849
|
+
...layout
|
|
78850
|
+
}) {
|
|
78851
|
+
return /* @__PURE__ */ jsxDEV18("box", {
|
|
78852
|
+
border: true,
|
|
78853
|
+
borderStyle: "rounded",
|
|
78854
|
+
borderColor: focused ? tokens.accent : tokens.border,
|
|
78855
|
+
backgroundColor: tokens.bgPanel,
|
|
78856
|
+
title,
|
|
78857
|
+
titleAlignment: "left",
|
|
78858
|
+
flexDirection: "column",
|
|
78859
|
+
overflow: "hidden",
|
|
78860
|
+
paddingLeft: flush ? 0 : 1,
|
|
78861
|
+
paddingRight: flush ? 0 : 1,
|
|
78862
|
+
...layout,
|
|
78863
|
+
style,
|
|
78864
|
+
children
|
|
78865
|
+
}, undefined, false, undefined, this);
|
|
78866
|
+
}
|
|
78867
|
+
var BOLD4, FILL = "\u2588", TRACK = "\u2591", SPARK, GAP = " ", NODATA = "\u254C", RAMP_CACHE, RAMP_CACHE_MAX = 64;
|
|
78868
|
+
var init_widgets = __esm(() => {
|
|
78869
|
+
init_color();
|
|
78870
|
+
init_text();
|
|
78871
|
+
init_tokens();
|
|
78872
|
+
BOLD4 = createTextAttributes2({ bold: true });
|
|
78873
|
+
SPARK = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
78874
|
+
RAMP_CACHE = new Map;
|
|
78875
|
+
});
|
|
78876
|
+
|
|
78877
|
+
// src/session/session-discovery.ts
|
|
78878
|
+
var exports_session_discovery = {};
|
|
78879
|
+
__export(exports_session_discovery, {
|
|
78880
|
+
slugForPath: () => slugForPath,
|
|
78881
|
+
sessionLabel: () => sessionLabel,
|
|
78882
|
+
mainConversationTurn: () => mainConversationTurn,
|
|
78883
|
+
isHarnessNoise: () => isHarnessNoise,
|
|
78884
|
+
isAgentSession: () => isAgentSession,
|
|
78885
|
+
isActive: () => isActive,
|
|
78886
|
+
hydrateSession: () => hydrateSession,
|
|
78887
|
+
hydrateConversation: () => hydrateConversation,
|
|
78888
|
+
getRepoContext: () => getRepoContext,
|
|
78889
|
+
findLatestSessionId: () => findLatestSessionId,
|
|
78890
|
+
enrichWorktreeGit: () => enrichWorktreeGit,
|
|
78891
|
+
discoverWorktreeGroups: () => discoverWorktreeGroups,
|
|
78892
|
+
PROJECTS_DIR: () => PROJECTS_DIR,
|
|
78893
|
+
ACTIVE_WINDOW_MS: () => ACTIVE_WINDOW_MS
|
|
78894
|
+
});
|
|
78895
|
+
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
78896
|
+
import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
78897
|
+
import { homedir as homedir34 } from "os";
|
|
78898
|
+
import { basename, join as join37 } from "path";
|
|
78899
|
+
function slugForPath(absPath) {
|
|
78900
|
+
return absPath.replace(/[/.]/g, "-");
|
|
78901
|
+
}
|
|
78902
|
+
function isAgentSession(row) {
|
|
78903
|
+
return row.entrypoint !== undefined && row.entrypoint !== "cli";
|
|
78904
|
+
}
|
|
78905
|
+
function getRepoContext(cwd = process.cwd()) {
|
|
78906
|
+
const git = (args) => {
|
|
78907
|
+
try {
|
|
78908
|
+
return execFileSync2("git", args, {
|
|
78909
|
+
cwd,
|
|
78910
|
+
encoding: "utf-8",
|
|
78911
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
78912
|
+
}).trim();
|
|
78913
|
+
} catch {
|
|
78914
|
+
return null;
|
|
78915
|
+
}
|
|
78916
|
+
};
|
|
78917
|
+
const current = git(["rev-parse", "--show-toplevel"]);
|
|
78918
|
+
const commonDir = git(["rev-parse", "--git-common-dir"]);
|
|
78919
|
+
if (!current || !commonDir)
|
|
78920
|
+
return null;
|
|
78921
|
+
const root = commonDir.endsWith("/.git") ? commonDir.slice(0, -"/.git".length) : current;
|
|
78922
|
+
const liveWorktrees = [];
|
|
78923
|
+
const branchByPath = new Map;
|
|
78924
|
+
const porcelain = git(["worktree", "list", "--porcelain"]);
|
|
78925
|
+
if (porcelain) {
|
|
78926
|
+
let currentPath = null;
|
|
78927
|
+
for (const line of porcelain.split(`
|
|
78928
|
+
`)) {
|
|
78929
|
+
if (line.startsWith("worktree ")) {
|
|
78930
|
+
currentPath = line.slice("worktree ".length);
|
|
78931
|
+
liveWorktrees.push(currentPath);
|
|
78932
|
+
} else if (line.startsWith("branch ") && currentPath) {
|
|
78933
|
+
branchByPath.set(currentPath, line.slice("branch ".length).replace(/^refs\/heads\//, ""));
|
|
78934
|
+
}
|
|
78935
|
+
}
|
|
78936
|
+
}
|
|
78937
|
+
return { root, current, liveWorktrees, branchByPath };
|
|
78938
|
+
}
|
|
78939
|
+
function projectDirs() {
|
|
78940
|
+
try {
|
|
78941
|
+
return readdirSync7(PROJECTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
78942
|
+
} catch {
|
|
78943
|
+
return [];
|
|
78944
|
+
}
|
|
78945
|
+
}
|
|
78946
|
+
function sessionsIn(dirName) {
|
|
78947
|
+
const dir = join37(PROJECTS_DIR, dirName);
|
|
78948
|
+
let names;
|
|
78949
|
+
try {
|
|
78950
|
+
names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
|
|
78951
|
+
} catch {
|
|
78952
|
+
return [];
|
|
78953
|
+
}
|
|
78954
|
+
const rows = [];
|
|
78955
|
+
for (const n of names) {
|
|
78956
|
+
const file2 = join37(dir, n);
|
|
78957
|
+
try {
|
|
78958
|
+
const st = statSync6(file2);
|
|
78959
|
+
if (st.size === 0)
|
|
78960
|
+
continue;
|
|
78961
|
+
const row = {
|
|
78962
|
+
id: basename(n, ".jsonl"),
|
|
78963
|
+
file: file2,
|
|
78964
|
+
mtimeMs: st.mtimeMs,
|
|
78965
|
+
sizeBytes: st.size
|
|
78966
|
+
};
|
|
78967
|
+
const head = readChunk(file2, 0, Math.min(ENTRYPOINT_BYTES, st.size));
|
|
78968
|
+
const m = /"entrypoint":"([a-z-]+)"/.exec(head);
|
|
78969
|
+
if (m)
|
|
78970
|
+
row.entrypoint = m[1];
|
|
78971
|
+
rows.push(row);
|
|
78972
|
+
} catch {}
|
|
78973
|
+
}
|
|
78974
|
+
return rows;
|
|
78975
|
+
}
|
|
78976
|
+
async function enrichWorktreeGit(groups, repoRoot) {
|
|
78977
|
+
const run = (cwd, args) => new Promise((resolve5) => {
|
|
78978
|
+
execFile("git", args, { cwd, encoding: "utf-8" }, (err, stdout) => resolve5(err ? "" : stdout));
|
|
78979
|
+
});
|
|
78980
|
+
const trackByBranch = new Map;
|
|
78981
|
+
const refs = await run(repoRoot, [
|
|
78982
|
+
"for-each-ref",
|
|
78983
|
+
"--format=%(refname:short)\t%(upstream:track)",
|
|
78984
|
+
"refs/heads/"
|
|
78985
|
+
]);
|
|
78986
|
+
for (const line of refs.split(`
|
|
78987
|
+
`)) {
|
|
78988
|
+
const [name, track] = line.split("\t");
|
|
78989
|
+
if (!name || !track)
|
|
78990
|
+
continue;
|
|
78991
|
+
const ahead = /ahead (\d+)/.exec(track)?.[1];
|
|
78992
|
+
const behind = /behind (\d+)/.exec(track)?.[1];
|
|
78993
|
+
if (ahead || behind) {
|
|
78994
|
+
trackByBranch.set(name, {
|
|
78995
|
+
...ahead ? { ahead: Number(ahead) } : {},
|
|
78996
|
+
...behind ? { behind: Number(behind) } : {}
|
|
78997
|
+
});
|
|
78998
|
+
}
|
|
78999
|
+
}
|
|
79000
|
+
await Promise.all(groups.map(async (g) => {
|
|
79001
|
+
if (g.branch)
|
|
79002
|
+
Object.assign(g, trackByBranch.get(g.branch) ?? {});
|
|
79003
|
+
if (!g.path || !g.live)
|
|
79004
|
+
return;
|
|
79005
|
+
const out = await run(g.path, ["status", "--porcelain"]);
|
|
79006
|
+
g.dirty = out.split(`
|
|
79007
|
+
`).filter((l) => l.trim().length > 0).length;
|
|
79008
|
+
}));
|
|
79009
|
+
}
|
|
79010
|
+
function isUnder(p, root) {
|
|
79011
|
+
return p === root || p.startsWith(`${root}/`);
|
|
79012
|
+
}
|
|
79013
|
+
function readProjectCwd(dirName) {
|
|
79014
|
+
const rows = sessionsIn(dirName);
|
|
79015
|
+
if (rows.length === 0)
|
|
79016
|
+
return null;
|
|
79017
|
+
rows.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
79018
|
+
for (const row of rows.slice(0, 2)) {
|
|
79019
|
+
for (const r of parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false)) {
|
|
79020
|
+
if (typeof r.cwd === "string" && r.cwd)
|
|
79021
|
+
return r.cwd;
|
|
79022
|
+
}
|
|
79023
|
+
}
|
|
79024
|
+
return null;
|
|
79025
|
+
}
|
|
79026
|
+
function isActive(row, now = Date.now()) {
|
|
79027
|
+
return now - row.mtimeMs < ACTIVE_WINDOW_MS;
|
|
79028
|
+
}
|
|
79029
|
+
function discoverWorktreeGroups(repo) {
|
|
79030
|
+
const rootSlug = slugForPath(repo.root);
|
|
79031
|
+
const mine = projectDirs();
|
|
79032
|
+
const known = [...repo.liveWorktrees].map((p) => ({ path: p, slug: slugForPath(p) })).sort((a, b) => b.slug.length - a.slug.length);
|
|
79033
|
+
const groups = new Map;
|
|
79034
|
+
const groupCwd = new Map;
|
|
79035
|
+
const upsert = (name, path2, live) => {
|
|
79036
|
+
let g = groups.get(name);
|
|
79037
|
+
if (!g) {
|
|
79038
|
+
g = {
|
|
79039
|
+
name,
|
|
79040
|
+
path: path2,
|
|
79041
|
+
live,
|
|
79042
|
+
current: path2 !== null && path2 === repo.current,
|
|
79043
|
+
sessions: [],
|
|
79044
|
+
lastActiveMs: 0,
|
|
79045
|
+
activeNow: false,
|
|
79046
|
+
...path2 ? { branch: repo.branchByPath.get(path2) } : {}
|
|
79047
|
+
};
|
|
79048
|
+
groups.set(name, g);
|
|
79049
|
+
}
|
|
79050
|
+
return g;
|
|
79051
|
+
};
|
|
79052
|
+
const WORKTREE_MARK = "--claude-worktrees-";
|
|
79053
|
+
const worktreeMatches = known.filter((k) => k.path !== repo.root);
|
|
79054
|
+
for (const dir of mine) {
|
|
79055
|
+
const at = dir.indexOf(WORKTREE_MARK);
|
|
79056
|
+
let name;
|
|
79057
|
+
let path2;
|
|
79058
|
+
let live;
|
|
79059
|
+
const hit = worktreeMatches.find((k) => dir === k.slug || dir.startsWith(`${k.slug}-`));
|
|
79060
|
+
if (hit) {
|
|
79061
|
+
path2 = hit.path;
|
|
79062
|
+
live = true;
|
|
79063
|
+
name = hit.path === repo.root ? "(root)" : basename(hit.path);
|
|
79064
|
+
} else if (at !== -1 && dir.startsWith(rootSlug)) {
|
|
79065
|
+
path2 = null;
|
|
79066
|
+
live = false;
|
|
79067
|
+
name = dir.slice(at + WORKTREE_MARK.length);
|
|
79068
|
+
} else if (dir === rootSlug) {
|
|
79069
|
+
name = "(root)";
|
|
79070
|
+
path2 = repo.root;
|
|
79071
|
+
live = true;
|
|
79072
|
+
} else if (dir.startsWith(`${rootSlug}-`) && at === -1) {
|
|
79073
|
+
const cwd = readProjectCwd(dir);
|
|
79074
|
+
if (!cwd || !isUnder(cwd, repo.root))
|
|
79075
|
+
continue;
|
|
79076
|
+
name = "(root)";
|
|
79077
|
+
path2 = repo.root;
|
|
79078
|
+
live = true;
|
|
79079
|
+
} else {
|
|
79080
|
+
continue;
|
|
79081
|
+
}
|
|
79082
|
+
const g = upsert(name, path2, live);
|
|
79083
|
+
if (path2)
|
|
79084
|
+
groupCwd.set(name, path2);
|
|
79085
|
+
else if (!groupCwd.has(name)) {
|
|
79086
|
+
const cwd = readProjectCwd(dir);
|
|
79087
|
+
if (cwd)
|
|
79088
|
+
groupCwd.set(name, cwd);
|
|
79089
|
+
}
|
|
79090
|
+
for (const s of sessionsIn(dir)) {
|
|
79091
|
+
g.sessions.push(s);
|
|
79092
|
+
if (s.mtimeMs > g.lastActiveMs)
|
|
79093
|
+
g.lastActiveMs = s.mtimeMs;
|
|
79094
|
+
}
|
|
79095
|
+
}
|
|
79096
|
+
const names = [...groups.keys()].sort((a, b) => b.length - a.length);
|
|
79097
|
+
for (const name of [...groups.keys()]) {
|
|
79098
|
+
const g = groups.get(name);
|
|
79099
|
+
if (!g || g.live)
|
|
79100
|
+
continue;
|
|
79101
|
+
const parent = names.find((n) => n !== name && name.startsWith(`${n}-`) && groups.has(n));
|
|
79102
|
+
if (!parent)
|
|
79103
|
+
continue;
|
|
79104
|
+
const into = groups.get(parent);
|
|
79105
|
+
const childCwd = groupCwd.get(name);
|
|
79106
|
+
const parentCwd = groupCwd.get(parent);
|
|
79107
|
+
if (!childCwd || !parentCwd || !isUnder(childCwd, parentCwd))
|
|
79108
|
+
continue;
|
|
79109
|
+
into.sessions.push(...g.sessions);
|
|
79110
|
+
into.lastActiveMs = Math.max(into.lastActiveMs, g.lastActiveMs);
|
|
79111
|
+
groups.delete(name);
|
|
79112
|
+
}
|
|
79113
|
+
for (const g of groups.values()) {
|
|
79114
|
+
g.sessions.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
79115
|
+
g.activeNow = g.sessions.some((s) => isActive(s));
|
|
79116
|
+
if (g.path) {
|
|
79117
|
+
try {
|
|
79118
|
+
g.createdMs = statSync6(g.path).birthtimeMs;
|
|
79119
|
+
} catch {}
|
|
79120
|
+
}
|
|
79121
|
+
if (!g.createdMs && g.sessions.length > 0) {
|
|
79122
|
+
g.createdMs = g.sessions.reduce((m, s) => Math.min(m, s.mtimeMs), Number.POSITIVE_INFINITY);
|
|
79123
|
+
}
|
|
79124
|
+
}
|
|
79125
|
+
return [...groups.values()].filter((g) => g.sessions.length > 0).sort((a, b) => {
|
|
79126
|
+
if (a.current !== b.current)
|
|
79127
|
+
return a.current ? -1 : 1;
|
|
79128
|
+
return b.lastActiveMs - a.lastActiveMs;
|
|
79129
|
+
});
|
|
79130
|
+
}
|
|
79131
|
+
function readChunk(file2, pos, len) {
|
|
79132
|
+
if (len <= 0)
|
|
79133
|
+
return "";
|
|
79134
|
+
let fd = null;
|
|
79135
|
+
try {
|
|
79136
|
+
fd = openSync5(file2, "r");
|
|
79137
|
+
const buf = Buffer.allocUnsafe(len);
|
|
79138
|
+
const n = readSync(fd, buf, 0, len, pos);
|
|
79139
|
+
return buf.subarray(0, n).toString("utf-8");
|
|
79140
|
+
} catch {
|
|
79141
|
+
return "";
|
|
79142
|
+
} finally {
|
|
79143
|
+
if (fd !== null) {
|
|
79144
|
+
try {
|
|
79145
|
+
closeSync5(fd);
|
|
79146
|
+
} catch {}
|
|
79147
|
+
}
|
|
79148
|
+
}
|
|
79149
|
+
}
|
|
79150
|
+
function parseRecords(chunk, dropFirstPartial) {
|
|
79151
|
+
const lines = chunk.split(`
|
|
79152
|
+
`);
|
|
79153
|
+
if (dropFirstPartial)
|
|
79154
|
+
lines.shift();
|
|
79155
|
+
else
|
|
79156
|
+
lines.pop();
|
|
79157
|
+
const out = [];
|
|
79158
|
+
for (const l of lines) {
|
|
79159
|
+
if (!l)
|
|
79160
|
+
continue;
|
|
79161
|
+
try {
|
|
79162
|
+
const o = JSON.parse(l);
|
|
79163
|
+
if (o && typeof o === "object")
|
|
79164
|
+
out.push(o);
|
|
79165
|
+
} catch {}
|
|
79166
|
+
}
|
|
79167
|
+
return out;
|
|
79168
|
+
}
|
|
79169
|
+
function contentText(content) {
|
|
79170
|
+
if (typeof content === "string")
|
|
79171
|
+
return content;
|
|
79172
|
+
if (!Array.isArray(content))
|
|
79173
|
+
return "";
|
|
79174
|
+
return content.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join(`
|
|
79175
|
+
`);
|
|
79176
|
+
}
|
|
79177
|
+
function isHarnessNoise(raw2) {
|
|
79178
|
+
const t = raw2.trimStart();
|
|
79179
|
+
if (t.startsWith("[Request interrupted by user"))
|
|
79180
|
+
return true;
|
|
79181
|
+
return HARNESS_ENVELOPES.some((e) => t.startsWith(e));
|
|
79182
|
+
}
|
|
79183
|
+
function mainConversationTurn(r) {
|
|
79184
|
+
if (r.type !== "user" && r.type !== "assistant")
|
|
79185
|
+
return null;
|
|
79186
|
+
if (r.isMeta || r.isSidechain)
|
|
79187
|
+
return null;
|
|
79188
|
+
const content = r.message?.content;
|
|
79189
|
+
if (Array.isArray(content) && content.some((b) => b?.type === "tool_result")) {
|
|
79190
|
+
return null;
|
|
79191
|
+
}
|
|
79192
|
+
const raw2 = contentText(content);
|
|
79193
|
+
if (!raw2.trim() || isHarnessNoise(raw2))
|
|
79194
|
+
return null;
|
|
79195
|
+
return { role: r.type === "user" ? "user" : "assistant", raw: raw2 };
|
|
79196
|
+
}
|
|
79197
|
+
function cleanPrompt(text) {
|
|
79198
|
+
return text.replace(/<command-[a-z-]+>[\s\S]*?<\/command-[a-z-]+>/g, " ").replace(/<local-command-[a-z-]+>[\s\S]*?<\/local-command-[a-z-]+>/g, " ").replace(/<[^>]{1,40}>/g, " ").replace(/\s+/g, " ").trim();
|
|
79199
|
+
}
|
|
79200
|
+
function hydrateSession(row) {
|
|
79201
|
+
if (row.hydrated)
|
|
79202
|
+
return row;
|
|
79203
|
+
row.hydrated = true;
|
|
79204
|
+
if (isActive(row)) {
|
|
79205
|
+
try {
|
|
79206
|
+
row.sizeBytes = statSync6(row.file).size;
|
|
79207
|
+
} catch {}
|
|
79208
|
+
}
|
|
79209
|
+
const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
|
|
79210
|
+
for (const r of head) {
|
|
79211
|
+
if (!row.gitBranch && typeof r.gitBranch === "string")
|
|
79212
|
+
row.gitBranch = r.gitBranch;
|
|
79213
|
+
if (!row.firstPrompt && r.type === "user" && !r.isMeta) {
|
|
79214
|
+
const raw2 = contentText(r.message?.content);
|
|
79215
|
+
const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
|
|
79216
|
+
if (t)
|
|
79217
|
+
row.firstPrompt = t;
|
|
79218
|
+
}
|
|
79219
|
+
if (row.gitBranch && row.firstPrompt)
|
|
79220
|
+
break;
|
|
79221
|
+
}
|
|
79222
|
+
const tailStart = Math.max(0, row.sizeBytes - TAIL_BYTES);
|
|
79223
|
+
const tail = parseRecords(readChunk(row.file, tailStart, row.sizeBytes - tailStart), tailStart > 0);
|
|
79224
|
+
for (let i = tail.length - 1;i >= 0; i--) {
|
|
79225
|
+
const r = tail[i];
|
|
79226
|
+
if (!row.title && r.type === "ai-title" && typeof r.aiTitle === "string" && r.aiTitle.trim()) {
|
|
79227
|
+
row.title = r.aiTitle.trim();
|
|
79228
|
+
}
|
|
79229
|
+
if (row.lastMessageChars === undefined && r.type === "user" && !r.isMeta) {
|
|
79230
|
+
const raw2 = contentText(r.message?.content);
|
|
79231
|
+
const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
|
|
79232
|
+
if (t)
|
|
79233
|
+
row.lastMessageChars = t.length;
|
|
79234
|
+
}
|
|
79235
|
+
}
|
|
79236
|
+
row.recentTurns = extractRecentTurns(tail);
|
|
79237
|
+
return row;
|
|
79238
|
+
}
|
|
79239
|
+
function extractRecentTurns(records) {
|
|
79240
|
+
const out = [];
|
|
79241
|
+
let ai = 0;
|
|
79242
|
+
let user = 0;
|
|
79243
|
+
for (let i = records.length - 1;i >= 0; i--) {
|
|
79244
|
+
if (ai >= RECENT_AI_TURNS && user >= RECENT_USER_TURNS)
|
|
79245
|
+
break;
|
|
79246
|
+
const r = records[i];
|
|
79247
|
+
if (r.type !== "user" && r.type !== "assistant")
|
|
79248
|
+
continue;
|
|
79249
|
+
const role = r.type === "user" ? "user" : "assistant";
|
|
79250
|
+
if (role === "assistant" ? ai >= RECENT_AI_TURNS : user >= RECENT_USER_TURNS)
|
|
79251
|
+
continue;
|
|
79252
|
+
const turn = mainConversationTurn(r);
|
|
79253
|
+
if (!turn)
|
|
79254
|
+
continue;
|
|
79255
|
+
const text = cleanPrompt(turn.raw);
|
|
79256
|
+
if (!text)
|
|
79257
|
+
continue;
|
|
79258
|
+
out.push({ role, text });
|
|
79259
|
+
if (role === "assistant")
|
|
79260
|
+
ai++;
|
|
79261
|
+
else
|
|
79262
|
+
user++;
|
|
79263
|
+
}
|
|
79264
|
+
return out.reverse();
|
|
79265
|
+
}
|
|
79266
|
+
function hydrateConversation(row) {
|
|
79267
|
+
if (row.conversationDeepened)
|
|
79268
|
+
return row;
|
|
79269
|
+
row.conversationDeepened = true;
|
|
79270
|
+
const turns = row.recentTurns;
|
|
79271
|
+
if (!turns || turns.some((t) => t.role === "user"))
|
|
79272
|
+
return row;
|
|
79273
|
+
const start = Math.max(0, row.sizeBytes - DEEP_TAIL_BYTES);
|
|
79274
|
+
const chunk = readChunk(row.file, start, row.sizeBytes - start);
|
|
79275
|
+
if (!chunk)
|
|
79276
|
+
return row;
|
|
79277
|
+
const lines = chunk.split(`
|
|
79278
|
+
`);
|
|
79279
|
+
if (start > 0)
|
|
79280
|
+
lines.shift();
|
|
79281
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
79282
|
+
const line = lines[i];
|
|
79283
|
+
if (!line.includes('"type":"user"'))
|
|
79284
|
+
continue;
|
|
79285
|
+
let r;
|
|
79286
|
+
try {
|
|
79287
|
+
r = JSON.parse(line);
|
|
79288
|
+
} catch {
|
|
79289
|
+
continue;
|
|
79290
|
+
}
|
|
79291
|
+
const turn = mainConversationTurn(r);
|
|
79292
|
+
if (!turn || turn.role !== "user")
|
|
79293
|
+
continue;
|
|
79294
|
+
const text = cleanPrompt(turn.raw);
|
|
79295
|
+
if (!text)
|
|
79296
|
+
continue;
|
|
79297
|
+
turns.unshift({ role: "user", text });
|
|
79298
|
+
if (row.lastMessageChars === undefined)
|
|
79299
|
+
row.lastMessageChars = text.length;
|
|
79300
|
+
return row;
|
|
79301
|
+
}
|
|
79302
|
+
return row;
|
|
79303
|
+
}
|
|
79304
|
+
function sessionLabel(row) {
|
|
79305
|
+
return row.title || row.firstPrompt || row.id;
|
|
79306
|
+
}
|
|
79307
|
+
function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
|
|
79308
|
+
const rows = sessionsIn(slugForPath(cwd)).filter((r) => r.mtimeMs >= sinceMs);
|
|
79309
|
+
if (rows.length === 0)
|
|
79310
|
+
return null;
|
|
79311
|
+
return rows.reduce((a, b) => b.mtimeMs > a.mtimeMs ? b : a).id;
|
|
79312
|
+
}
|
|
79313
|
+
var ENTRYPOINT_BYTES = 8192, PROJECTS_DIR, ACTIVE_WINDOW_MS = 120000, HEAD_BYTES, TAIL_BYTES, HARNESS_ENVELOPES, DEEP_TAIL_BYTES, RECENT_AI_TURNS = 5, RECENT_USER_TURNS = 1;
|
|
79314
|
+
var init_session_discovery = __esm(() => {
|
|
79315
|
+
PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
|
|
79316
|
+
HEAD_BYTES = 64 * 1024;
|
|
79317
|
+
TAIL_BYTES = 128 * 1024;
|
|
79318
|
+
HARNESS_ENVELOPES = [
|
|
79319
|
+
"<task-notification>",
|
|
79320
|
+
"<system-reminder>",
|
|
79321
|
+
"<local-command-stdout>",
|
|
79322
|
+
"<user-prompt-submit-hook>"
|
|
79323
|
+
];
|
|
79324
|
+
DEEP_TAIL_BYTES = 4 * 1024 * 1024;
|
|
79325
|
+
});
|
|
79326
|
+
|
|
79327
|
+
// src/session/conversation.ts
|
|
79328
|
+
import { closeSync as closeSync6, openSync as openSync6, readSync as readSync2, statSync as statSync7 } from "fs";
|
|
79329
|
+
import { StringDecoder } from "string_decoder";
|
|
79330
|
+
function looksLikeTurn(line) {
|
|
79331
|
+
const assistant = line.includes('"type":"assistant"');
|
|
79332
|
+
if (!assistant && !line.includes('"type":"user"'))
|
|
79333
|
+
return false;
|
|
79334
|
+
if (line.includes('"isSidechain":true'))
|
|
79335
|
+
return false;
|
|
79336
|
+
if (line.includes('"isMeta":true'))
|
|
79337
|
+
return false;
|
|
79338
|
+
if (line.includes('"type":"tool_result"'))
|
|
79339
|
+
return false;
|
|
79340
|
+
if (assistant && !line.includes('"type":"text"'))
|
|
79341
|
+
return false;
|
|
79342
|
+
return true;
|
|
79343
|
+
}
|
|
79344
|
+
function cleanTurnText(raw2) {
|
|
79345
|
+
let t = raw2;
|
|
79346
|
+
for (const re of INLINE_ENVELOPES)
|
|
79347
|
+
t = t.replace(re, "");
|
|
79348
|
+
t = t.replace(/<command-name>([\s\S]*?)<\/command-name>/g, "/$1");
|
|
79349
|
+
return t.replace(/\t/g, " ").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").replace(/\r\n?/g, `
|
|
79350
|
+
`).split(`
|
|
79351
|
+
`).map((l) => l.replace(/\s+$/, "")).join(`
|
|
79352
|
+
`).replace(/\n{3,}/g, `
|
|
79353
|
+
|
|
79354
|
+
`).trim();
|
|
79355
|
+
}
|
|
79356
|
+
function readConversation(file2, opts = {}) {
|
|
79357
|
+
const maxTurn = opts.maxTurnChars ?? MAX_TURN_CHARS;
|
|
79358
|
+
const maxTotal = opts.maxTotalChars ?? MAX_TOTAL_CHARS;
|
|
79359
|
+
const chunkBytes = opts.chunkBytes ?? CHUNK_BYTES;
|
|
79360
|
+
const started = Date.now();
|
|
79361
|
+
const turns = [];
|
|
79362
|
+
let chars = 0;
|
|
79363
|
+
let dropped = 0;
|
|
79364
|
+
let anyElided = false;
|
|
79365
|
+
let bytes2 = 0;
|
|
79366
|
+
const take = (turn) => {
|
|
79367
|
+
turns.push(turn);
|
|
79368
|
+
chars += turn.text.length;
|
|
79369
|
+
if (chars <= maxTotal)
|
|
79370
|
+
return;
|
|
79371
|
+
let freed = 0;
|
|
79372
|
+
let n = 0;
|
|
79373
|
+
while (n < turns.length - 1 && chars - freed > maxTotal) {
|
|
79374
|
+
freed += turns[n].text.length;
|
|
79375
|
+
n++;
|
|
79376
|
+
}
|
|
79377
|
+
turns.splice(0, n);
|
|
79378
|
+
chars -= freed;
|
|
79379
|
+
dropped += n;
|
|
79380
|
+
};
|
|
79381
|
+
let fd = null;
|
|
79382
|
+
try {
|
|
79383
|
+
const size = statSync7(file2).size;
|
|
79384
|
+
fd = openSync6(file2, "r");
|
|
79385
|
+
const buf = Buffer.allocUnsafe(chunkBytes);
|
|
79386
|
+
const decoder = new StringDecoder("utf-8");
|
|
79387
|
+
let pending = "";
|
|
79388
|
+
let pos = 0;
|
|
79389
|
+
const consume = (line) => {
|
|
79390
|
+
if (!line || !looksLikeTurn(line))
|
|
79391
|
+
return;
|
|
79392
|
+
let record4;
|
|
79393
|
+
try {
|
|
79394
|
+
record4 = JSON.parse(line);
|
|
79395
|
+
} catch {
|
|
79396
|
+
return;
|
|
79397
|
+
}
|
|
79398
|
+
if (!record4 || typeof record4 !== "object")
|
|
79399
|
+
return;
|
|
79400
|
+
const raw2 = mainConversationTurn(record4);
|
|
79401
|
+
if (!raw2)
|
|
79402
|
+
return;
|
|
79403
|
+
let text = cleanTurnText(raw2.raw);
|
|
79404
|
+
if (!text)
|
|
79405
|
+
return;
|
|
79406
|
+
const elided = text.length > maxTurn;
|
|
79407
|
+
if (elided) {
|
|
79408
|
+
anyElided = true;
|
|
79409
|
+
text = `${text.slice(0, maxTurn)}
|
|
79410
|
+
|
|
79411
|
+
\u2026 turn truncated at ${maxTurn.toLocaleString()} characters`;
|
|
79412
|
+
}
|
|
79413
|
+
take({ role: raw2.role, text, elided });
|
|
79414
|
+
};
|
|
79415
|
+
while (pos < size) {
|
|
79416
|
+
const n = readSync2(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
|
|
79417
|
+
if (n <= 0)
|
|
79418
|
+
break;
|
|
79419
|
+
pos += n;
|
|
79420
|
+
bytes2 += n;
|
|
79421
|
+
pending += decoder.write(buf.subarray(0, n));
|
|
79422
|
+
let from = 0;
|
|
79423
|
+
let nl = pending.indexOf(`
|
|
79424
|
+
`, from);
|
|
79425
|
+
while (nl !== -1) {
|
|
79426
|
+
consume(pending.slice(from, nl));
|
|
79427
|
+
from = nl + 1;
|
|
79428
|
+
nl = pending.indexOf(`
|
|
79429
|
+
`, from);
|
|
79430
|
+
}
|
|
79431
|
+
if (from > 0)
|
|
79432
|
+
pending = pending.slice(from);
|
|
79433
|
+
}
|
|
79434
|
+
pending += decoder.end();
|
|
79435
|
+
if (pending)
|
|
79436
|
+
consume(pending);
|
|
79437
|
+
} catch {} finally {
|
|
79438
|
+
if (fd !== null) {
|
|
79439
|
+
try {
|
|
79440
|
+
closeSync6(fd);
|
|
79441
|
+
} catch {}
|
|
79442
|
+
}
|
|
79443
|
+
}
|
|
79444
|
+
return { turns, bytes: bytes2, elapsedMs: Date.now() - started, chars, dropped, anyElided };
|
|
79445
|
+
}
|
|
79446
|
+
function cpWidth(cp) {
|
|
79447
|
+
if (cp >= 32 && cp < 127)
|
|
79448
|
+
return 1;
|
|
79449
|
+
return displayWidth(String.fromCodePoint(cp));
|
|
79450
|
+
}
|
|
79451
|
+
function wrapOffsets(text, width) {
|
|
79452
|
+
const out = [];
|
|
79453
|
+
const w = Math.max(1, Math.floor(width));
|
|
79454
|
+
const n = text.length;
|
|
79455
|
+
let paraStart = 0;
|
|
79456
|
+
for (;; ) {
|
|
79457
|
+
let nl = text.indexOf(`
|
|
79458
|
+
`, paraStart);
|
|
79459
|
+
if (nl === -1)
|
|
79460
|
+
nl = n;
|
|
79461
|
+
let s = paraStart;
|
|
79462
|
+
for (;; ) {
|
|
79463
|
+
if (s >= nl) {
|
|
79464
|
+
if (s === paraStart || paraStart === nl)
|
|
79465
|
+
out.push({ start: s, end: nl });
|
|
79466
|
+
break;
|
|
79467
|
+
}
|
|
79468
|
+
let cols = 0;
|
|
79469
|
+
let j = s;
|
|
79470
|
+
let lastSpace = -1;
|
|
79471
|
+
while (j < nl) {
|
|
79472
|
+
const cp = text.codePointAt(j);
|
|
79473
|
+
const size = cp > 65535 ? 2 : 1;
|
|
79474
|
+
const cw = cpWidth(cp);
|
|
79475
|
+
if (cols + cw > w)
|
|
79476
|
+
break;
|
|
79477
|
+
if (cp === 32 && j > s && text.charCodeAt(j - 1) !== 32)
|
|
79478
|
+
lastSpace = j;
|
|
79479
|
+
cols += cw;
|
|
79480
|
+
j += size;
|
|
79481
|
+
}
|
|
79482
|
+
if (j >= nl) {
|
|
79483
|
+
out.push({ start: s, end: nl });
|
|
79484
|
+
break;
|
|
79485
|
+
}
|
|
79486
|
+
const brk = text.charCodeAt(j) === 32 ? j : lastSpace > s ? lastSpace : j;
|
|
79487
|
+
out.push({ start: s, end: brk });
|
|
79488
|
+
s = brk === j && text.charCodeAt(j) !== 32 ? j : brk + 1;
|
|
79489
|
+
while (s < nl && text.charCodeAt(s) === 32)
|
|
79490
|
+
s++;
|
|
79491
|
+
}
|
|
79492
|
+
if (nl >= n)
|
|
79493
|
+
break;
|
|
79494
|
+
paraStart = nl + 1;
|
|
79495
|
+
if (paraStart > n)
|
|
79496
|
+
break;
|
|
79497
|
+
if (paraStart === n) {
|
|
79498
|
+
out.push({ start: n, end: n });
|
|
79499
|
+
break;
|
|
79500
|
+
}
|
|
79501
|
+
}
|
|
79502
|
+
return out;
|
|
79503
|
+
}
|
|
79504
|
+
var CHUNK_BYTES, MAX_TURN_CHARS = 32000, MAX_TOTAL_CHARS = 8000000, INLINE_ENVELOPES;
|
|
79505
|
+
var init_conversation = __esm(() => {
|
|
79506
|
+
init_text();
|
|
79507
|
+
init_session_discovery();
|
|
79508
|
+
CHUNK_BYTES = 1024 * 1024;
|
|
79509
|
+
INLINE_ENVELOPES = [
|
|
79510
|
+
/<system-reminder>[\s\S]*?<\/system-reminder>/g,
|
|
79511
|
+
/<command-message>[\s\S]*?<\/command-message>/g,
|
|
79512
|
+
/<command-args>[\s\S]*?<\/command-args>/g,
|
|
79513
|
+
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g,
|
|
79514
|
+
/<user-prompt-submit-hook>[\s\S]*?<\/user-prompt-submit-hook>/g
|
|
79515
|
+
];
|
|
79516
|
+
});
|
|
79517
|
+
|
|
79518
|
+
// src/session/conversation-reader.tsx
|
|
79519
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
79520
|
+
import { useEffect as useEffect5, useMemo as useMemo3, useState as useState6 } from "react";
|
|
79521
|
+
import { jsxDEV as jsxDEV19, Fragment as Fragment12 } from "@opentui/react/jsx-dev-runtime";
|
|
79522
|
+
function layoutRows(turns, textWidth) {
|
|
79523
|
+
const rows = [];
|
|
79524
|
+
const turnStart = [];
|
|
79525
|
+
const turnEnd = [];
|
|
79526
|
+
for (let t = 0;t < turns.length; t++) {
|
|
79527
|
+
turnStart.push(rows.length);
|
|
79528
|
+
const slices = wrapOffsets(turns[t].text, textWidth);
|
|
79529
|
+
for (let i = 0;i < slices.length; i++) {
|
|
79530
|
+
rows.push({ turn: t, first: i === 0, start: slices[i].start, end: slices[i].end });
|
|
79531
|
+
}
|
|
79532
|
+
turnEnd.push(rows.length);
|
|
79533
|
+
if (t < turns.length - 1)
|
|
79534
|
+
rows.push({ turn: -1, first: false, start: 0, end: 0 });
|
|
79535
|
+
}
|
|
79536
|
+
return { rows, turnStart, turnEnd };
|
|
79537
|
+
}
|
|
79538
|
+
function buildSearch(turns, rows, turnStart, turnEnd, query) {
|
|
79539
|
+
if (!query)
|
|
79540
|
+
return EMPTY_SEARCH;
|
|
79541
|
+
const q = query.toLowerCase();
|
|
79542
|
+
const hits = [];
|
|
79543
|
+
const ranges = new Map;
|
|
79544
|
+
let capped = false;
|
|
79545
|
+
for (let t = 0;t < turns.length && !capped; t++) {
|
|
79546
|
+
const hay = turns[t].text.toLowerCase();
|
|
79547
|
+
let at = hay.indexOf(q);
|
|
79548
|
+
if (at === -1)
|
|
79549
|
+
continue;
|
|
79550
|
+
const lo = turnStart[t];
|
|
79551
|
+
const hi = turnEnd[t];
|
|
79552
|
+
let r = lo;
|
|
79553
|
+
while (at !== -1) {
|
|
79554
|
+
if (hits.length >= MAX_MATCHES) {
|
|
79555
|
+
capped = true;
|
|
79556
|
+
break;
|
|
79557
|
+
}
|
|
79558
|
+
const end = at + q.length;
|
|
79559
|
+
while (r < hi - 1 && rows[r].end <= at)
|
|
79560
|
+
r++;
|
|
79561
|
+
const hit = hits.length;
|
|
79562
|
+
hits.push(r);
|
|
79563
|
+
for (let k = r;k < hi; k++) {
|
|
79564
|
+
const row = rows[k];
|
|
79565
|
+
if (row.start >= end)
|
|
79566
|
+
break;
|
|
79567
|
+
const s = Math.max(at, row.start);
|
|
79568
|
+
const e = Math.min(end, row.end);
|
|
79569
|
+
if (e > s) {
|
|
79570
|
+
const list = ranges.get(k);
|
|
79571
|
+
if (list)
|
|
79572
|
+
list.push({ s: s - row.start, e: e - row.start, hit });
|
|
79573
|
+
else
|
|
79574
|
+
ranges.set(k, [{ s: s - row.start, e: e - row.start, hit }]);
|
|
79575
|
+
}
|
|
79576
|
+
}
|
|
79577
|
+
at = hay.indexOf(q, at + 1);
|
|
79578
|
+
}
|
|
79579
|
+
}
|
|
79580
|
+
return { hits, ranges, capped };
|
|
79581
|
+
}
|
|
79582
|
+
function ConversationReader({
|
|
79583
|
+
row,
|
|
79584
|
+
conv,
|
|
79585
|
+
width,
|
|
79586
|
+
height: height2,
|
|
79587
|
+
onClose,
|
|
79588
|
+
onResume,
|
|
79589
|
+
onCancel
|
|
79590
|
+
}) {
|
|
79591
|
+
const [top, setTop] = useState6(0);
|
|
79592
|
+
const [query, setQuery] = useState6("");
|
|
79593
|
+
const [typing, setTyping] = useState6(false);
|
|
79594
|
+
const [matchIdx, setMatchIdx] = useState6(0);
|
|
79595
|
+
const CHROME_ROWS = 5;
|
|
79596
|
+
const viewport = Math.max(1, height2 - CHROME_ROWS);
|
|
79597
|
+
const inner = Math.max(20, width - 2);
|
|
79598
|
+
const contentW = inner - BAR_W;
|
|
79599
|
+
const textW = Math.max(8, contentW - GUTTER - 1);
|
|
79600
|
+
const turns = useMemo3(() => conv?.turns ?? NO_TURNS, [conv]);
|
|
79601
|
+
const { rows, turnStart, turnEnd } = useMemo3(() => layoutRows(turns, textW), [turns, textW]);
|
|
79602
|
+
const search = useMemo3(() => buildSearch(turns, rows, turnStart, turnEnd, query), [turns, rows, turnStart, turnEnd, query]);
|
|
79603
|
+
const maxTop = Math.max(0, rows.length - viewport);
|
|
79604
|
+
const clampTop = (v) => Math.max(0, Math.min(v, maxTop));
|
|
79605
|
+
const reveal = (r) => setTop(clampTop(r - Math.floor(viewport / 3)));
|
|
79606
|
+
useEffect5(() => {
|
|
79607
|
+
setTop(Math.max(0, rows.length - viewport));
|
|
79608
|
+
}, [rows.length, viewport]);
|
|
79609
|
+
useEffect5(() => {
|
|
79610
|
+
if (search.hits.length === 0)
|
|
79611
|
+
return;
|
|
79612
|
+
let i = search.hits.findIndex((r) => r >= top);
|
|
79613
|
+
if (i === -1)
|
|
79614
|
+
i = 0;
|
|
79615
|
+
setMatchIdx(i);
|
|
79616
|
+
reveal(search.hits[i]);
|
|
79617
|
+
}, [search]);
|
|
79618
|
+
const step = (d) => {
|
|
79619
|
+
if (search.hits.length === 0)
|
|
79620
|
+
return;
|
|
79621
|
+
const i = (matchIdx + d + search.hits.length) % search.hits.length;
|
|
79622
|
+
setMatchIdx(i);
|
|
79623
|
+
reveal(search.hits[i]);
|
|
79624
|
+
};
|
|
79625
|
+
useKeyboard3((key) => {
|
|
79626
|
+
const name = key.name;
|
|
79627
|
+
if (key.ctrl && name === "c") {
|
|
79628
|
+
onCancel();
|
|
79629
|
+
return;
|
|
79630
|
+
}
|
|
79631
|
+
if (name === "escape") {
|
|
79632
|
+
if (typing || query) {
|
|
79633
|
+
setTyping(false);
|
|
79634
|
+
setQuery("");
|
|
79635
|
+
return;
|
|
79636
|
+
}
|
|
79637
|
+
onClose();
|
|
79638
|
+
return;
|
|
79639
|
+
}
|
|
79640
|
+
if (name === "return" || name === "enter") {
|
|
79641
|
+
if (typing) {
|
|
79642
|
+
setTyping(false);
|
|
79643
|
+
return;
|
|
79644
|
+
}
|
|
79645
|
+
onResume();
|
|
79646
|
+
return;
|
|
79647
|
+
}
|
|
79648
|
+
if (name === "up")
|
|
79649
|
+
return setTop((t) => clampTop(t - 1));
|
|
79650
|
+
if (name === "down")
|
|
79651
|
+
return setTop((t) => clampTop(t + 1));
|
|
79652
|
+
if (name === "pageup")
|
|
79653
|
+
return setTop((t) => clampTop(t - viewport));
|
|
79654
|
+
if (name === "pagedown")
|
|
79655
|
+
return setTop((t) => clampTop(t + viewport));
|
|
79656
|
+
if (name === "home")
|
|
79657
|
+
return setTop(0);
|
|
79658
|
+
if (name === "end")
|
|
79659
|
+
return setTop(maxTop);
|
|
79660
|
+
if (name === "backspace") {
|
|
79661
|
+
if (typing)
|
|
79662
|
+
setQuery((q) => q.slice(0, -1));
|
|
79663
|
+
return;
|
|
79664
|
+
}
|
|
79665
|
+
const ch = key.raw;
|
|
79666
|
+
const printable = ch && ch.length === 1 && ch >= " " && ch !== "\x7F";
|
|
79667
|
+
if (typing) {
|
|
79668
|
+
if (printable)
|
|
79669
|
+
setQuery((q) => q + ch);
|
|
79670
|
+
return;
|
|
79671
|
+
}
|
|
79672
|
+
if (name === "slash" || ch === "/") {
|
|
79673
|
+
setTyping(true);
|
|
79674
|
+
return;
|
|
79675
|
+
}
|
|
79676
|
+
if (name === "n")
|
|
79677
|
+
return step(key.shift ? -1 : 1);
|
|
79678
|
+
if (ch === "g")
|
|
79679
|
+
return setTop(0);
|
|
79680
|
+
if (ch === "G")
|
|
79681
|
+
return setTop(maxTop);
|
|
79682
|
+
if (name === "space")
|
|
79683
|
+
return setTop((t) => clampTop(t + viewport));
|
|
79684
|
+
});
|
|
79685
|
+
const label = sessionLabel(row);
|
|
79686
|
+
const mb = row.sizeBytes / 1048576;
|
|
79687
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
|
|
79688
|
+
const stats = conv ? `${turns.length} turns \xB7 ${rows.length} lines \xB7 ${size} transcript` : `reading ${size}\u2026`;
|
|
79689
|
+
const visible = rows.slice(top, top + viewport);
|
|
79690
|
+
const barCells = useMemo3(() => scrollbarCells(viewport, rows.length, top, search.hits), [viewport, rows.length, top, search.hits]);
|
|
79691
|
+
const pct = rows.length <= viewport ? 100 : top / maxTop * 100;
|
|
79692
|
+
const POSITION_COLS = 26;
|
|
79693
|
+
const matchLabel = search.hits.length ? `${matchIdx + 1} of ${search.hits.length}${search.capped ? "+" : ""} matches` : query ? "no matches" : "";
|
|
79694
|
+
return /* @__PURE__ */ jsxDEV19("box", {
|
|
79695
|
+
flexDirection: "column",
|
|
79696
|
+
height: height2,
|
|
79697
|
+
backgroundColor: C.bg,
|
|
79698
|
+
children: [
|
|
79699
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79700
|
+
flexDirection: "row",
|
|
79701
|
+
justifyContent: "space-between",
|
|
79702
|
+
height: 1,
|
|
79703
|
+
paddingX: 1,
|
|
79704
|
+
children: [
|
|
79705
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79706
|
+
children: [
|
|
79707
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79708
|
+
fg: tokens.accent,
|
|
79709
|
+
attributes: A.bold,
|
|
79710
|
+
children: "claudish"
|
|
79711
|
+
}, undefined, false, undefined, this),
|
|
79712
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79713
|
+
fg: tokens.subtle,
|
|
79714
|
+
children: " reader"
|
|
79715
|
+
}, undefined, false, undefined, this),
|
|
79716
|
+
conv && conv.dropped > 0 ? /* @__PURE__ */ jsxDEV19("span", {
|
|
79717
|
+
fg: tokens.warn,
|
|
79718
|
+
children: ` ${conv.dropped} older turns dropped (cap)`
|
|
79719
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this),
|
|
79720
|
+
conv?.anyElided ? /* @__PURE__ */ jsxDEV19("span", {
|
|
79721
|
+
fg: tokens.warn,
|
|
79722
|
+
children: " long turns truncated"
|
|
79723
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this)
|
|
79724
|
+
]
|
|
79725
|
+
}, undefined, true, undefined, this),
|
|
79726
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79727
|
+
children: /* @__PURE__ */ jsxDEV19("span", {
|
|
79728
|
+
fg: tokens.subtle,
|
|
79729
|
+
children: stats
|
|
79730
|
+
}, undefined, false, undefined, this)
|
|
79731
|
+
}, undefined, false, undefined, this)
|
|
79732
|
+
]
|
|
79733
|
+
}, undefined, true, undefined, this),
|
|
79734
|
+
/* @__PURE__ */ jsxDEV19(Panel, {
|
|
79735
|
+
title: `conversation \xB7 ${truncate3(label, Math.max(10, width - 20))}`,
|
|
79736
|
+
flush: true,
|
|
79737
|
+
flexGrow: 1,
|
|
79738
|
+
flexBasis: 0,
|
|
79739
|
+
children: !conv ? /* @__PURE__ */ jsxDEV19("text", {
|
|
79740
|
+
fg: tokens.subtle,
|
|
79741
|
+
children: " reading transcript\u2026"
|
|
79742
|
+
}, undefined, false, undefined, this) : rows.length === 0 ? /* @__PURE__ */ jsxDEV19("text", {
|
|
79743
|
+
fg: tokens.trace,
|
|
79744
|
+
children: " no conversation recorded \u2014 this transcript has no main-thread prose"
|
|
79745
|
+
}, undefined, false, undefined, this) : visible.map((r, i) => {
|
|
79746
|
+
const line = top + i;
|
|
79747
|
+
return /* @__PURE__ */ jsxDEV19(ReaderRow, {
|
|
79748
|
+
row: r,
|
|
79749
|
+
turn: r.turn >= 0 ? turns[r.turn] : undefined,
|
|
79750
|
+
hl: search.ranges.get(line),
|
|
79751
|
+
current: matchIdx,
|
|
79752
|
+
width: contentW,
|
|
79753
|
+
bar: barCells[i] ?? "track"
|
|
79754
|
+
}, line, false, undefined, this);
|
|
79755
|
+
})
|
|
79756
|
+
}, undefined, false, undefined, this),
|
|
79757
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79758
|
+
flexDirection: "row",
|
|
79759
|
+
justifyContent: "space-between",
|
|
79760
|
+
height: 1,
|
|
79761
|
+
paddingX: 1,
|
|
79762
|
+
children: [
|
|
79763
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79764
|
+
children: query || typing ? /* @__PURE__ */ jsxDEV19(Fragment12, {
|
|
79765
|
+
children: [
|
|
79766
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79767
|
+
fg: tokens.warn,
|
|
79768
|
+
attributes: A.bold,
|
|
79769
|
+
children: truncate3(`/${query}`, Math.max(4, width - POSITION_COLS - displayWidth(matchLabel) - 4))
|
|
79770
|
+
}, undefined, false, undefined, this),
|
|
79771
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79772
|
+
fg: typing ? tokens.warn : tokens.trace,
|
|
79773
|
+
children: typing ? "\u258C" : " "
|
|
79774
|
+
}, undefined, false, undefined, this),
|
|
79775
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79776
|
+
fg: search.hits.length ? tokens.subtle : tokens.trace,
|
|
79777
|
+
children: matchLabel ? ` ${matchLabel}` : ""
|
|
79778
|
+
}, undefined, false, undefined, this)
|
|
79779
|
+
]
|
|
79780
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79781
|
+
fg: tokens.trace,
|
|
79782
|
+
children: "/ to search"
|
|
79783
|
+
}, undefined, false, undefined, this)
|
|
79784
|
+
}, undefined, false, undefined, this),
|
|
79785
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79786
|
+
children: [
|
|
79787
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79788
|
+
fg: tokens.subtle,
|
|
79789
|
+
children: `${Math.min(rows.length, top + viewport)}/${rows.length} `
|
|
79790
|
+
}, undefined, false, undefined, this),
|
|
79791
|
+
/* @__PURE__ */ jsxDEV19(MeterSpan, {
|
|
79792
|
+
pct,
|
|
79793
|
+
width: 12,
|
|
79794
|
+
ramp: ramps.volume
|
|
79795
|
+
}, undefined, false, undefined, this)
|
|
79796
|
+
]
|
|
79797
|
+
}, undefined, true, undefined, this)
|
|
79798
|
+
]
|
|
79799
|
+
}, undefined, true, undefined, this),
|
|
79800
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79801
|
+
flexDirection: "row",
|
|
79802
|
+
height: 1,
|
|
79803
|
+
paddingX: 1,
|
|
79804
|
+
gap: 2,
|
|
79805
|
+
children: [
|
|
79806
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79807
|
+
fg: tokens.subtle,
|
|
79808
|
+
children: "\u2191\u2193 \u21DE\u21DF scroll"
|
|
79809
|
+
}, undefined, false, undefined, this),
|
|
79810
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79811
|
+
fg: tokens.subtle,
|
|
79812
|
+
children: "g/G ends"
|
|
79813
|
+
}, undefined, false, undefined, this),
|
|
79814
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79815
|
+
fg: typing ? tokens.warn : tokens.subtle,
|
|
79816
|
+
children: "/ search"
|
|
79817
|
+
}, undefined, false, undefined, this),
|
|
79818
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79819
|
+
fg: search.hits.length ? tokens.subtle : tokens.trace,
|
|
79820
|
+
children: "n/N match"
|
|
79821
|
+
}, undefined, false, undefined, this),
|
|
79822
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79823
|
+
fg: tokens.accent,
|
|
79824
|
+
children: "\u23CE resume"
|
|
79825
|
+
}, undefined, false, undefined, this),
|
|
79826
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79827
|
+
fg: tokens.subtle,
|
|
79828
|
+
children: "esc back"
|
|
79829
|
+
}, undefined, false, undefined, this)
|
|
79830
|
+
]
|
|
79831
|
+
}, undefined, true, undefined, this)
|
|
79832
|
+
]
|
|
79833
|
+
}, undefined, true, undefined, this);
|
|
79834
|
+
}
|
|
79835
|
+
function scrollbarCells(viewport, total, top, hits) {
|
|
79836
|
+
const cells = new Array(viewport).fill("track");
|
|
79837
|
+
if (total <= 0)
|
|
79838
|
+
return cells;
|
|
79839
|
+
if (total > viewport) {
|
|
79840
|
+
const size = Math.max(1, Math.round(viewport / total * viewport));
|
|
79841
|
+
const span = Math.max(1, total - viewport);
|
|
79842
|
+
const start = Math.min(viewport - size, Math.round(top / span * (viewport - size)));
|
|
79843
|
+
for (let i = 0;i < size; i++)
|
|
79844
|
+
cells[start + i] = "thumb";
|
|
79845
|
+
} else {
|
|
79846
|
+
cells.fill("thumb");
|
|
79847
|
+
}
|
|
79848
|
+
for (const h of hits) {
|
|
79849
|
+
const i = Math.min(viewport - 1, Math.floor(h / total * viewport));
|
|
79850
|
+
if (i >= 0)
|
|
79851
|
+
cells[i] = "hit";
|
|
79852
|
+
}
|
|
79853
|
+
return cells;
|
|
79854
|
+
}
|
|
79855
|
+
function ReaderRow({
|
|
79856
|
+
row,
|
|
79857
|
+
turn,
|
|
79858
|
+
hl,
|
|
79859
|
+
current,
|
|
79860
|
+
width,
|
|
79861
|
+
bar
|
|
79862
|
+
}) {
|
|
79863
|
+
if (!turn) {
|
|
79864
|
+
return /* @__PURE__ */ jsxDEV19("text", {
|
|
79865
|
+
children: [
|
|
79866
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79867
|
+
children: " ".repeat(Math.max(0, width))
|
|
79868
|
+
}, undefined, false, undefined, this),
|
|
79869
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79870
|
+
bg: BAR_COLOR[bar],
|
|
79871
|
+
children: " "
|
|
79872
|
+
}, undefined, false, undefined, this)
|
|
79873
|
+
]
|
|
79874
|
+
}, undefined, true, undefined, this);
|
|
79875
|
+
}
|
|
79876
|
+
const text = turn.text.slice(row.start, row.end);
|
|
79877
|
+
const fg = turn.role === "user" ? tokens.text : C.fgMuted;
|
|
79878
|
+
const railFg = turn.role === "user" ? SPEAKER.you : SPEAKER.ai;
|
|
79879
|
+
const pad2 = Math.max(0, width - GUTTER - displayWidth(text));
|
|
79880
|
+
return /* @__PURE__ */ jsxDEV19("text", {
|
|
79881
|
+
children: [
|
|
79882
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79883
|
+
fg: railFg,
|
|
79884
|
+
children: RAIL
|
|
79885
|
+
}, undefined, false, undefined, this),
|
|
79886
|
+
row.first ? /* @__PURE__ */ jsxDEV19(BadgeSpan, {
|
|
79887
|
+
label: turn.role === "user" ? "you" : "ai",
|
|
79888
|
+
bg: turn.role === "user" ? SPEAKER.you : SPEAKER.ai,
|
|
79889
|
+
width: ROLE_W
|
|
79890
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79891
|
+
children: " ".repeat(ROLE_W)
|
|
79892
|
+
}, undefined, false, undefined, this),
|
|
79893
|
+
hl && hl.length > 0 ? highlighted(text, hl, current, fg) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79894
|
+
fg,
|
|
79895
|
+
children: text
|
|
79896
|
+
}, undefined, false, undefined, this),
|
|
79897
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79898
|
+
children: " ".repeat(pad2)
|
|
79899
|
+
}, undefined, false, undefined, this),
|
|
79900
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79901
|
+
bg: BAR_COLOR[bar],
|
|
79902
|
+
children: " "
|
|
79903
|
+
}, undefined, false, undefined, this)
|
|
79904
|
+
]
|
|
79905
|
+
}, undefined, true, undefined, this);
|
|
79906
|
+
}
|
|
79907
|
+
function highlighted(text, hl, current, fg) {
|
|
79908
|
+
const out = [];
|
|
79909
|
+
let from = 0;
|
|
79910
|
+
for (let i = 0;i < hl.length; i++) {
|
|
79911
|
+
const { s, e, hit } = hl[i];
|
|
79912
|
+
if (e <= from)
|
|
79913
|
+
continue;
|
|
79914
|
+
const start = Math.max(from, s);
|
|
79915
|
+
if (start > from)
|
|
79916
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
79917
|
+
fg,
|
|
79918
|
+
children: text.slice(from, start)
|
|
79919
|
+
}, `p${i}`, false, undefined, this));
|
|
79920
|
+
const bg = hit === current ? tokens.accent : tokens.warn;
|
|
79921
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
79922
|
+
fg: tokens.ink,
|
|
79923
|
+
bg,
|
|
79924
|
+
attributes: A.bold,
|
|
79925
|
+
children: text.slice(start, e)
|
|
79926
|
+
}, `h${i}`, false, undefined, this));
|
|
79927
|
+
from = e;
|
|
79928
|
+
}
|
|
79929
|
+
if (from < text.length)
|
|
79930
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
79931
|
+
fg,
|
|
79932
|
+
children: text.slice(from)
|
|
79933
|
+
}, "tail", false, undefined, this));
|
|
79934
|
+
return /* @__PURE__ */ jsxDEV19(Fragment12, {
|
|
79935
|
+
children: out
|
|
79936
|
+
}, undefined, false, undefined, this);
|
|
79937
|
+
}
|
|
79938
|
+
var RAIL = "\u258D", RAIL_W = 2, ROLE_W = 6, GUTTER, BAR_W = 1, SPEAKER, MAX_MATCHES = 5000, EMPTY_SEARCH, NO_TURNS, BAR_COLOR;
|
|
79939
|
+
var init_conversation_reader = __esm(() => {
|
|
79940
|
+
init_theme2();
|
|
79941
|
+
init_text();
|
|
79942
|
+
init_tokens();
|
|
79943
|
+
init_widgets();
|
|
79944
|
+
init_conversation();
|
|
79945
|
+
init_session_discovery();
|
|
79946
|
+
GUTTER = RAIL_W + ROLE_W;
|
|
79947
|
+
SPEAKER = {
|
|
79948
|
+
you: "#39d353",
|
|
79949
|
+
ai: "#39c5cf"
|
|
79950
|
+
};
|
|
79951
|
+
EMPTY_SEARCH = { hits: [], ranges: new Map, capped: false };
|
|
79952
|
+
NO_TURNS = [];
|
|
79953
|
+
BAR_COLOR = {
|
|
79954
|
+
track: tokens.border,
|
|
79955
|
+
thumb: tokens.accent,
|
|
79956
|
+
hit: tokens.warn
|
|
79957
|
+
};
|
|
79958
|
+
});
|
|
79959
|
+
|
|
79960
|
+
// src/session/resume-picker.tsx
|
|
79961
|
+
import { useKeyboard as useKeyboard4, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
|
|
79962
|
+
import { useEffect as useEffect6, useMemo as useMemo4, useState as useState7 } from "react";
|
|
79963
|
+
import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
|
|
79964
|
+
function detailTurns(height2) {
|
|
79965
|
+
return height2 >= 40 ? 6 : height2 >= 30 ? 4 : 2;
|
|
79966
|
+
}
|
|
79967
|
+
function fuzzy(needle, hay) {
|
|
79968
|
+
if (!needle)
|
|
79969
|
+
return true;
|
|
79970
|
+
const n = needle.toLowerCase();
|
|
79971
|
+
const h = hay.toLowerCase();
|
|
79972
|
+
let i = 0;
|
|
79973
|
+
for (let j = 0;j < h.length && i < n.length; j++)
|
|
79974
|
+
if (h[j] === n[i])
|
|
79975
|
+
i++;
|
|
79976
|
+
return i === n.length;
|
|
79977
|
+
}
|
|
79978
|
+
function age(ms) {
|
|
79979
|
+
const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
|
|
79980
|
+
if (s < 60)
|
|
79981
|
+
return `${s}s`;
|
|
79982
|
+
const m = Math.floor(s / 60);
|
|
79983
|
+
if (m < 60)
|
|
79984
|
+
return `${m}m`;
|
|
79985
|
+
const h = Math.floor(m / 60);
|
|
79986
|
+
if (h < 24)
|
|
79987
|
+
return `${h}h`;
|
|
79988
|
+
return `${Math.floor(h / 24)}d`;
|
|
79989
|
+
}
|
|
79990
|
+
function chipW(label) {
|
|
79991
|
+
return label + 2;
|
|
79992
|
+
}
|
|
79993
|
+
function blockWidth(c) {
|
|
79994
|
+
return LIVE_W + chipW(c.age) + chipW(c.count) + (c.dirty > 0 ? chipW(c.dirty + 1) : 0) + (c.sync > 0 ? 2 * chipW(c.sync + 1) : 0);
|
|
79995
|
+
}
|
|
79996
|
+
function sizePct(bytes2, max) {
|
|
79997
|
+
const lo = Math.log(SIZE_FLOOR_BYTES);
|
|
79998
|
+
const hi = Math.log(Math.max(max, SIZE_FLOOR_BYTES * 2));
|
|
79999
|
+
const v = Math.log(Math.max(bytes2, SIZE_FLOOR_BYTES));
|
|
80000
|
+
return Math.max(0, Math.min(100, (v - lo) / (hi - lo) * 100));
|
|
80001
|
+
}
|
|
80002
|
+
function Slot({
|
|
80003
|
+
label,
|
|
80004
|
+
bg,
|
|
80005
|
+
labelW
|
|
80006
|
+
}) {
|
|
80007
|
+
if (!label || !bg)
|
|
80008
|
+
return /* @__PURE__ */ jsxDEV20("span", {
|
|
80009
|
+
children: " ".repeat(chipW(labelW))
|
|
80010
|
+
}, undefined, false, undefined, this);
|
|
80011
|
+
return /* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80012
|
+
label,
|
|
80013
|
+
bg
|
|
80014
|
+
}, undefined, false, undefined, this);
|
|
80015
|
+
}
|
|
80016
|
+
function WorktreeRow({
|
|
80017
|
+
g,
|
|
80018
|
+
cursor,
|
|
80019
|
+
width,
|
|
80020
|
+
count,
|
|
80021
|
+
cols
|
|
80022
|
+
}) {
|
|
80023
|
+
const nameColor = !g.live ? tokens.dead : g.current ? tokens.success : tokens.text;
|
|
80024
|
+
const stale = !g.lastActiveMs || Date.now() - g.lastActiveMs >= STALE_MS;
|
|
80025
|
+
const room = Math.max(0, width - blockWidth(cols));
|
|
80026
|
+
const series = room >= SPARK_MIN_DAYS ? activitySeries(g.sessions, room) : null;
|
|
80027
|
+
const spark = series && hasActivity(series) ? series : null;
|
|
80028
|
+
const chips = [];
|
|
80029
|
+
if (cols.sync > 0 && g.ahead) {
|
|
80030
|
+
chips.push({
|
|
80031
|
+
label: `\u2191${padStartTo(String(g.ahead), cols.sync)}`,
|
|
80032
|
+
bg: CHIP.ahead,
|
|
80033
|
+
labelW: cols.sync + 1
|
|
80034
|
+
});
|
|
80035
|
+
}
|
|
80036
|
+
if (cols.sync > 0 && g.behind) {
|
|
80037
|
+
chips.push({
|
|
80038
|
+
label: `\u2193${padStartTo(String(g.behind), cols.sync)}`,
|
|
80039
|
+
bg: CHIP.behind,
|
|
80040
|
+
labelW: cols.sync + 1
|
|
80041
|
+
});
|
|
80042
|
+
}
|
|
80043
|
+
if (cols.dirty > 0 && g.dirty) {
|
|
80044
|
+
chips.push({
|
|
80045
|
+
label: `${DIRTY_GLYPH}${padStartTo(String(g.dirty), cols.dirty)}`,
|
|
80046
|
+
bg: CHIP.dirty,
|
|
80047
|
+
labelW: cols.dirty + 1
|
|
80048
|
+
});
|
|
80049
|
+
}
|
|
80050
|
+
chips.push({ label: padStartTo(String(count), cols.count), bg: CHIP.count, labelW: cols.count });
|
|
80051
|
+
chips.push({
|
|
80052
|
+
label: padStartTo(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014", cols.age),
|
|
80053
|
+
bg: stale ? CHIP.stale : CHIP.fresh,
|
|
80054
|
+
labelW: cols.age
|
|
80055
|
+
});
|
|
80056
|
+
const used = chips.reduce((w, c) => w + c.labelW + 2, 0) + LIVE_W;
|
|
80057
|
+
const gap = Math.max(0, width - room - used);
|
|
80058
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80059
|
+
flexDirection: "column",
|
|
80060
|
+
height: 2,
|
|
80061
|
+
backgroundColor: cursor ? C.bgHighlight : undefined,
|
|
80062
|
+
children: [
|
|
80063
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80064
|
+
children: /* @__PURE__ */ jsxDEV20("span", {
|
|
80065
|
+
fg: nameColor,
|
|
80066
|
+
attributes: cursor || g.current ? A.bold : undefined,
|
|
80067
|
+
children: truncate3(g.name, width)
|
|
80068
|
+
}, undefined, false, undefined, this)
|
|
80069
|
+
}, undefined, false, undefined, this),
|
|
80070
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80071
|
+
height: 1,
|
|
80072
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80073
|
+
children: [
|
|
80074
|
+
spark ? /* @__PURE__ */ jsxDEV20(SparklineSpan, {
|
|
80075
|
+
values: spark,
|
|
80076
|
+
fg: SPARK_FG
|
|
80077
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {
|
|
80078
|
+
children: " ".repeat(room)
|
|
80079
|
+
}, undefined, false, undefined, this),
|
|
80080
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80081
|
+
children: " ".repeat(gap)
|
|
80082
|
+
}, undefined, false, undefined, this),
|
|
80083
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80084
|
+
fg: tokens.success,
|
|
80085
|
+
children: g.activeNow ? "\u25CF " : " "
|
|
80086
|
+
}, undefined, false, undefined, this),
|
|
80087
|
+
chips.map((c) => /* @__PURE__ */ jsxDEV20(Slot, {
|
|
80088
|
+
label: c.label,
|
|
80089
|
+
bg: c.bg,
|
|
80090
|
+
labelW: c.labelW
|
|
80091
|
+
}, c.bg + c.label, false, undefined, this))
|
|
80092
|
+
]
|
|
80093
|
+
}, undefined, true, undefined, this)
|
|
80094
|
+
}, undefined, false, undefined, this)
|
|
80095
|
+
]
|
|
80096
|
+
}, undefined, true, undefined, this);
|
|
80097
|
+
}
|
|
80098
|
+
function dailyActivity(groups, days) {
|
|
80099
|
+
const day = 86400000;
|
|
80100
|
+
const today = Math.floor(Date.now() / day);
|
|
80101
|
+
const buckets = new Array(days).fill(0);
|
|
80102
|
+
for (const g of groups) {
|
|
80103
|
+
for (const s of g.sessions) {
|
|
80104
|
+
const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
|
|
80105
|
+
if (idx >= 0 && idx < days)
|
|
80106
|
+
buckets[idx] += 1;
|
|
80107
|
+
}
|
|
80108
|
+
}
|
|
80109
|
+
return buckets;
|
|
80110
|
+
}
|
|
80111
|
+
function activityLevels(days) {
|
|
80112
|
+
const nz = days.filter((v) => v > 0).sort((a, b) => a - b);
|
|
80113
|
+
if (nz.length === 0)
|
|
80114
|
+
return days.map(() => 0);
|
|
80115
|
+
const at = (p) => nz[Math.min(nz.length - 1, Math.floor(nz.length * p))];
|
|
80116
|
+
const q1 = at(0.25);
|
|
80117
|
+
const q2 = at(0.5);
|
|
80118
|
+
const q3 = at(0.75);
|
|
80119
|
+
return days.map((v) => v <= 0 ? 0 : v <= q1 ? 1 : v <= q2 ? 2 : v <= q3 ? 3 : 4);
|
|
80120
|
+
}
|
|
80121
|
+
function ActivityCalendar({
|
|
80122
|
+
days,
|
|
80123
|
+
width
|
|
80124
|
+
}) {
|
|
80125
|
+
const levels = activityLevels(days);
|
|
80126
|
+
const title = `activity \xB7 ${ACTIVITY_WEEKS}w `;
|
|
80127
|
+
const grid = Math.max(WEEK_DAYS, width - WEEK_LABEL_W);
|
|
80128
|
+
const base = Math.floor(grid / WEEK_DAYS);
|
|
80129
|
+
const extra = grid - base * WEEK_DAYS;
|
|
80130
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80131
|
+
flexDirection: "column",
|
|
80132
|
+
flexShrink: 0,
|
|
80133
|
+
paddingTop: 1,
|
|
80134
|
+
children: [
|
|
80135
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80136
|
+
children: [
|
|
80137
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80138
|
+
fg: tokens.subtle,
|
|
80139
|
+
attributes: A.bold,
|
|
80140
|
+
children: title
|
|
80141
|
+
}, undefined, false, undefined, this),
|
|
80142
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80143
|
+
fg: tokens.border,
|
|
80144
|
+
children: "\u2500".repeat(Math.max(0, width - title.length))
|
|
80145
|
+
}, undefined, false, undefined, this)
|
|
80146
|
+
]
|
|
80147
|
+
}, undefined, true, undefined, this),
|
|
80148
|
+
Array.from({ length: ACTIVITY_WEEKS }, (_, w) => {
|
|
80149
|
+
const ago = ACTIVITY_WEEKS - 1 - w;
|
|
80150
|
+
return /* @__PURE__ */ jsxDEV20("text", {
|
|
80151
|
+
children: [
|
|
80152
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80153
|
+
fg: tokens.trace,
|
|
80154
|
+
children: padTo(ago === 0 ? "now" : `-${ago}w`, WEEK_LABEL_W)
|
|
80155
|
+
}, undefined, false, undefined, this),
|
|
80156
|
+
Array.from({ length: WEEK_DAYS }, (_2, d) => /* @__PURE__ */ jsxDEV20("span", {
|
|
80157
|
+
bg: GH_LEVELS[levels[w * WEEK_DAYS + d] ?? 0],
|
|
80158
|
+
children: " ".repeat(base + (d < extra ? 1 : 0))
|
|
80159
|
+
}, d, false, undefined, this))
|
|
80160
|
+
]
|
|
80161
|
+
}, w, true, undefined, this);
|
|
80162
|
+
})
|
|
80163
|
+
]
|
|
80164
|
+
}, undefined, true, undefined, this);
|
|
80165
|
+
}
|
|
80166
|
+
function SectionHeader({ label, width }) {
|
|
80167
|
+
const text = `${label} `;
|
|
80168
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80169
|
+
flexDirection: "column",
|
|
80170
|
+
height: 2,
|
|
80171
|
+
children: [
|
|
80172
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80173
|
+
children: " "
|
|
80174
|
+
}, undefined, false, undefined, this),
|
|
80175
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80176
|
+
children: [
|
|
80177
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80178
|
+
fg: tokens.subtle,
|
|
80179
|
+
attributes: A.bold,
|
|
80180
|
+
children: text
|
|
80181
|
+
}, undefined, false, undefined, this),
|
|
80182
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80183
|
+
fg: tokens.border,
|
|
80184
|
+
children: "\u2500".repeat(Math.max(0, width - text.length))
|
|
80185
|
+
}, undefined, false, undefined, this)
|
|
80186
|
+
]
|
|
80187
|
+
}, undefined, true, undefined, this)
|
|
80188
|
+
]
|
|
80189
|
+
}, undefined, true, undefined, this);
|
|
80190
|
+
}
|
|
80191
|
+
function hasActivity(series) {
|
|
80192
|
+
return series.some((v) => v > 0);
|
|
80193
|
+
}
|
|
80194
|
+
function activitySeries(sessions2, days = WEEK_DAYS * 2) {
|
|
80195
|
+
const day = 86400000;
|
|
80196
|
+
const today = Math.floor(Date.now() / day);
|
|
80197
|
+
const buckets = new Array(days).fill(0);
|
|
80198
|
+
for (const s of sessions2) {
|
|
80199
|
+
const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
|
|
80200
|
+
if (idx >= 0 && idx < days)
|
|
80201
|
+
buckets[idx] += 1;
|
|
80202
|
+
}
|
|
80203
|
+
return buckets;
|
|
80204
|
+
}
|
|
80205
|
+
function SessionRowView({
|
|
80206
|
+
row,
|
|
80207
|
+
cursor,
|
|
80208
|
+
width,
|
|
80209
|
+
even,
|
|
80210
|
+
indent = 0,
|
|
80211
|
+
maxSize,
|
|
80212
|
+
meterW
|
|
80213
|
+
}) {
|
|
80214
|
+
const live = isActive(row);
|
|
80215
|
+
const pad2 = " ".repeat(indent);
|
|
80216
|
+
const mb = row.sizeBytes / 1048576;
|
|
80217
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
|
|
80218
|
+
const titleW = Math.max(10, width - indent - 2);
|
|
80219
|
+
const metaPad = `${pad2} `;
|
|
80220
|
+
const SIZE_COL = 8;
|
|
80221
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80222
|
+
flexDirection: "column",
|
|
80223
|
+
height: 2,
|
|
80224
|
+
backgroundColor: cursor ? C.bgHighlight : even ? undefined : C.bgAlt,
|
|
80225
|
+
children: [
|
|
80226
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80227
|
+
children: [
|
|
80228
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80229
|
+
fg: live ? tokens.success : tokens.border,
|
|
80230
|
+
children: `${pad2}${live ? "\u25CF" : "\xB7"} `
|
|
80231
|
+
}, undefined, false, undefined, this),
|
|
80232
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80233
|
+
fg: tokens.text,
|
|
80234
|
+
attributes: cursor ? A.bold : undefined,
|
|
80235
|
+
children: truncate3(sessionLabel(row), titleW)
|
|
80236
|
+
}, undefined, false, undefined, this)
|
|
80237
|
+
]
|
|
80238
|
+
}, undefined, true, undefined, this),
|
|
80239
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80240
|
+
children: [
|
|
80241
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80242
|
+
children: metaPad
|
|
80243
|
+
}, undefined, false, undefined, this),
|
|
80244
|
+
/* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80245
|
+
label: padStartTo(age(row.mtimeMs), SESSION_AGE_W),
|
|
80246
|
+
bg: Date.now() - row.mtimeMs < STALE_MS ? CHIP.fresh : CHIP.stale,
|
|
80247
|
+
width: SESSION_AGE_COL
|
|
80248
|
+
}, undefined, false, undefined, this),
|
|
80249
|
+
/* @__PURE__ */ jsxDEV20(MeterSpan, {
|
|
80250
|
+
pct: sizePct(row.sizeBytes, maxSize),
|
|
80251
|
+
width: meterW,
|
|
80252
|
+
ramp: ramps.volume
|
|
80253
|
+
}, undefined, false, undefined, this),
|
|
80254
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80255
|
+
fg: MUTED,
|
|
80256
|
+
children: padStartTo(size, SIZE_COL)
|
|
80257
|
+
}, undefined, false, undefined, this),
|
|
80258
|
+
row.gitBranch ? /* @__PURE__ */ jsxDEV20("span", {
|
|
80259
|
+
fg: tokens.trace,
|
|
80260
|
+
children: ` ${BRANCH_ICON} ${truncate3(row.gitBranch, Math.max(6, width - indent - 2 - SESSION_AGE_COL - meterW - SIZE_COL - BRANCH_LEAD_DENSE))}`
|
|
80261
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
|
|
80262
|
+
]
|
|
80263
|
+
}, undefined, true, undefined, this)
|
|
80264
|
+
]
|
|
80265
|
+
}, undefined, true, undefined, this);
|
|
80266
|
+
}
|
|
80267
|
+
function AgentNode({
|
|
80268
|
+
count,
|
|
80269
|
+
open,
|
|
80270
|
+
cursor,
|
|
80271
|
+
width
|
|
80272
|
+
}) {
|
|
80273
|
+
const label = `${count} agent session${count === 1 ? "" : "s"}`;
|
|
80274
|
+
const hint = open ? "enter to collapse" : "enter to expand \xB7 showing 3";
|
|
80275
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80276
|
+
height: 1,
|
|
80277
|
+
backgroundColor: cursor ? C.bgHighlight : undefined,
|
|
80278
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80279
|
+
children: [
|
|
80280
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80281
|
+
fg: cursor ? tokens.accent : tokens.trace,
|
|
80282
|
+
children: `${cursor ? "\u258D" : " "} `
|
|
80283
|
+
}, undefined, false, undefined, this),
|
|
80284
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80285
|
+
fg: tokens.warn,
|
|
80286
|
+
children: open ? "\u25BE " : "\u25B8 "
|
|
80287
|
+
}, undefined, false, undefined, this),
|
|
80288
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80289
|
+
fg: cursor ? tokens.text : tokens.subtle,
|
|
80290
|
+
children: label
|
|
80291
|
+
}, undefined, false, undefined, this),
|
|
80292
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80293
|
+
fg: tokens.trace,
|
|
80294
|
+
children: truncate3(` ${hint}`, Math.max(0, width - label.length - 6))
|
|
80295
|
+
}, undefined, false, undefined, this)
|
|
80296
|
+
]
|
|
80297
|
+
}, undefined, true, undefined, this)
|
|
80298
|
+
}, undefined, false, undefined, this);
|
|
80299
|
+
}
|
|
80300
|
+
function ResumePicker({ groups, onDone }) {
|
|
80301
|
+
const { width, height: height2 } = useTerminalDimensions3();
|
|
80302
|
+
const [pane, setPane] = useState7("worktrees");
|
|
80303
|
+
const [wtCursor, setWtCursor] = useState7(0);
|
|
80304
|
+
const [sessCursor, setSessCursor] = useState7(0);
|
|
80305
|
+
const [filter, setFilter] = useState7("");
|
|
80306
|
+
const [agentsOpen, setAgentsOpen] = useState7(false);
|
|
80307
|
+
const [reader, setReader] = useState7(null);
|
|
80308
|
+
const [, setTick] = useState7(0);
|
|
80309
|
+
const listed = useMemo4(() => {
|
|
80310
|
+
const m = new Map;
|
|
80311
|
+
for (const g of groups)
|
|
80312
|
+
m.set(g.name, g.sessions.filter((s) => !isAgentSession(s)));
|
|
80313
|
+
return m;
|
|
80314
|
+
}, [groups]);
|
|
80315
|
+
const { fresh, stale, visibleGroups } = useMemo4(() => {
|
|
80316
|
+
const withSessions = groups.filter((g) => (listed.get(g.name)?.length ?? 0) > 0);
|
|
80317
|
+
const matching = filter ? withSessions.filter((g) => fuzzy(filter, g.name)) : withSessions;
|
|
80318
|
+
const now = Date.now();
|
|
80319
|
+
const byRecency = (a, b) => b.lastActiveMs - a.lastActiveMs;
|
|
80320
|
+
const f = matching.filter((g) => g.current || now - g.lastActiveMs < STALE_MS).sort((a, b) => a.current !== b.current ? a.current ? -1 : 1 : byRecency(a, b));
|
|
80321
|
+
const st = matching.filter((g) => !g.current && now - g.lastActiveMs >= STALE_MS).sort(byRecency);
|
|
80322
|
+
return { fresh: f, stale: st, visibleGroups: [...f, ...st] };
|
|
80323
|
+
}, [groups, filter, listed]);
|
|
80324
|
+
const group = visibleGroups[Math.min(wtCursor, visibleGroups.length - 1)];
|
|
80325
|
+
const sessions2 = useMemo4(() => {
|
|
80326
|
+
if (!group)
|
|
80327
|
+
return [];
|
|
80328
|
+
const base = listed.get(group.name) ?? [];
|
|
80329
|
+
if (!filter)
|
|
80330
|
+
return base;
|
|
80331
|
+
if (fuzzy(filter, group.name))
|
|
80332
|
+
return base;
|
|
80333
|
+
return base.filter((s) => fuzzy(filter, sessionLabel(s)));
|
|
80334
|
+
}, [group, filter, listed]);
|
|
80335
|
+
const agentRows = useMemo4(() => group ? group.sessions.filter(isAgentSession) : [], [group]);
|
|
80336
|
+
const items = useMemo4(() => {
|
|
80337
|
+
const out = sessions2.map((row) => ({ kind: "session", row }));
|
|
80338
|
+
if (agentRows.length > 0 && !filter) {
|
|
80339
|
+
out.push({ kind: "agents", count: agentRows.length });
|
|
80340
|
+
for (const row of agentsOpen ? agentRows : agentRows.slice(0, 3)) {
|
|
80341
|
+
out.push({ kind: "agent", row });
|
|
80342
|
+
}
|
|
80343
|
+
}
|
|
80344
|
+
return out;
|
|
80345
|
+
}, [sessions2, agentRows, agentsOpen, filter]);
|
|
80346
|
+
const cursorItem = items[Math.min(sessCursor, items.length - 1)];
|
|
80347
|
+
const selected = cursorItem && cursorItem.kind !== "agents" ? cursorItem.row : undefined;
|
|
80348
|
+
const turns = detailTurns(height2);
|
|
80349
|
+
const sessionDetailH = DETAIL_CHROME + turns;
|
|
80350
|
+
const listRows = Math.max(3, height2 - sessionDetailH - WORKTREE_DETAIL_H - 4);
|
|
80351
|
+
useEffect6(() => {
|
|
80352
|
+
const start = Math.max(0, Math.min(sessCursor - 2, items.length - listRows));
|
|
80353
|
+
for (const it of items.slice(start, start + listRows + 2)) {
|
|
80354
|
+
if (it.kind !== "agents")
|
|
80355
|
+
hydrateSession(it.row);
|
|
80356
|
+
}
|
|
80357
|
+
if (selected) {
|
|
80358
|
+
hydrateSession(selected);
|
|
80359
|
+
hydrateConversation(selected);
|
|
80360
|
+
}
|
|
80361
|
+
setTick((t) => t + 1);
|
|
80362
|
+
}, [items, sessCursor, listRows, selected]);
|
|
80363
|
+
useEffect6(() => {
|
|
80364
|
+
if (!reader || reader.conv)
|
|
80365
|
+
return;
|
|
80366
|
+
const file2 = reader.row.file;
|
|
80367
|
+
const timer = setTimeout(() => {
|
|
80368
|
+
const conv = readConversation(file2);
|
|
80369
|
+
setReader((r) => r && r.row.file === file2 && !r.conv ? { row: r.row, conv } : r);
|
|
80370
|
+
}, 0);
|
|
80371
|
+
return () => clearTimeout(timer);
|
|
80372
|
+
}, [reader]);
|
|
80373
|
+
const clamp = (v, len) => Math.max(0, Math.min(v, len - 1));
|
|
80374
|
+
useKeyboard4((key) => {
|
|
80375
|
+
if (reader)
|
|
80376
|
+
return;
|
|
80377
|
+
const name = key.name;
|
|
80378
|
+
if (name === "escape") {
|
|
80379
|
+
if (filter) {
|
|
80380
|
+
setFilter("");
|
|
80381
|
+
return;
|
|
80382
|
+
}
|
|
80383
|
+
onDone(null);
|
|
80384
|
+
return;
|
|
80385
|
+
}
|
|
80386
|
+
if (key.ctrl && name === "c") {
|
|
80387
|
+
onDone(null);
|
|
80388
|
+
return;
|
|
80389
|
+
}
|
|
80390
|
+
if (name === "return" || name === "enter") {
|
|
80391
|
+
if (pane === "worktrees") {
|
|
80392
|
+
setPane("sessions");
|
|
80393
|
+
setSessCursor(0);
|
|
80394
|
+
return;
|
|
80395
|
+
}
|
|
80396
|
+
if (cursorItem?.kind === "agents") {
|
|
80397
|
+
setAgentsOpen((v) => !v);
|
|
80398
|
+
return;
|
|
80399
|
+
}
|
|
80400
|
+
if (selected)
|
|
80401
|
+
onDone(selected.id);
|
|
80402
|
+
return;
|
|
80403
|
+
}
|
|
80404
|
+
if (name === "tab") {
|
|
80405
|
+
setPane((p) => p === "worktrees" ? "sessions" : "worktrees");
|
|
80406
|
+
return;
|
|
80407
|
+
}
|
|
80408
|
+
if (name === "left") {
|
|
80409
|
+
setPane("worktrees");
|
|
80410
|
+
return;
|
|
80411
|
+
}
|
|
80412
|
+
if (name === "right") {
|
|
80413
|
+
setPane("sessions");
|
|
80414
|
+
return;
|
|
80415
|
+
}
|
|
80416
|
+
if (name === "up" || name === "down") {
|
|
80417
|
+
const d = name === "up" ? -1 : 1;
|
|
80418
|
+
if (pane === "worktrees") {
|
|
80419
|
+
setWtCursor((c) => clamp(c + d, visibleGroups.length));
|
|
80420
|
+
setSessCursor(0);
|
|
80421
|
+
} else {
|
|
80422
|
+
setSessCursor((c) => clamp(c + d, items.length));
|
|
80423
|
+
}
|
|
80424
|
+
return;
|
|
80425
|
+
}
|
|
80426
|
+
if (name === "a" && !key.ctrl && !key.meta) {
|
|
80427
|
+
setAgentsOpen((v) => !v);
|
|
80428
|
+
return;
|
|
80429
|
+
}
|
|
80430
|
+
if (name === "v" && !key.ctrl && !key.meta) {
|
|
80431
|
+
if (selected)
|
|
80432
|
+
setReader({ row: selected, conv: null });
|
|
80433
|
+
return;
|
|
80434
|
+
}
|
|
80435
|
+
if (name === "backspace") {
|
|
80436
|
+
setFilter((f) => f.slice(0, -1));
|
|
80437
|
+
setWtCursor(0);
|
|
80438
|
+
setSessCursor(0);
|
|
80439
|
+
return;
|
|
80440
|
+
}
|
|
80441
|
+
const ch = key.raw;
|
|
80442
|
+
if (ch && ch.length === 1 && ch >= " " && ch !== "\x7F" && ch !== "a" && ch !== "v") {
|
|
80443
|
+
setFilter((f) => f + ch);
|
|
80444
|
+
setWtCursor(0);
|
|
80445
|
+
setSessCursor(0);
|
|
80446
|
+
}
|
|
80447
|
+
});
|
|
80448
|
+
const bodyW = width;
|
|
80449
|
+
const sidebarW = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, Math.round(width * 0.34)));
|
|
80450
|
+
const rightW = Math.max(30, bodyW - sidebarW);
|
|
80451
|
+
const sessInner = rightW - PANEL_BORDER - SCROLL_CHROME;
|
|
80452
|
+
const sideInner = sidebarW - PANEL_BORDER - SCROLL_CHROME - 1;
|
|
80453
|
+
const sessionDetailInner = rightW - PANEL_CHROME;
|
|
80454
|
+
const worktreeDetailInner = width - PANEL_CHROME;
|
|
80455
|
+
const chipCols = useMemo4(() => {
|
|
80456
|
+
let cols = { age: 1, count: 1, dirty: 0, sync: 0 };
|
|
80457
|
+
for (const g of visibleGroups) {
|
|
80458
|
+
cols.age = Math.max(cols.age, displayWidth(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014"));
|
|
80459
|
+
cols.count = Math.max(cols.count, String(listed.get(g.name)?.length ?? 0).length);
|
|
80460
|
+
if (g.dirty)
|
|
80461
|
+
cols.dirty = Math.max(cols.dirty, String(g.dirty).length);
|
|
80462
|
+
if (g.ahead)
|
|
80463
|
+
cols.sync = Math.max(cols.sync, String(g.ahead).length);
|
|
80464
|
+
if (g.behind)
|
|
80465
|
+
cols.sync = Math.max(cols.sync, String(g.behind).length);
|
|
80466
|
+
}
|
|
80467
|
+
if (blockWidth(cols) > sideInner)
|
|
80468
|
+
cols = { ...cols, sync: 0 };
|
|
80469
|
+
if (blockWidth(cols) > sideInner)
|
|
80470
|
+
cols = { ...cols, dirty: 0 };
|
|
80471
|
+
return cols;
|
|
80472
|
+
}, [visibleGroups, listed, sideInner]);
|
|
80473
|
+
const maxSize = Math.max(1, ...sessions2.slice(0, 400).map((s) => s.sizeBytes));
|
|
80474
|
+
const meterW = Math.max(6, Math.min(14, Math.round(sessInner * 0.16)));
|
|
80475
|
+
const sidebarInnerH = height2 - 1 - WORKTREE_DETAIL_H - 1 - PANEL_BORDER;
|
|
80476
|
+
const listContentH = visibleGroups.length * 2 + (stale.length > 0 ? 2 : 0);
|
|
80477
|
+
const showActivity = sidebarInnerH - listContentH >= ACTIVITY_H;
|
|
80478
|
+
const activityDays = useMemo4(() => dailyActivity(visibleGroups, ACTIVITY_WEEKS * WEEK_DAYS), [visibleGroups]);
|
|
80479
|
+
const shownSessions = [...listed.values()].reduce((a, v) => a + v.length, 0);
|
|
80480
|
+
if (reader) {
|
|
80481
|
+
return /* @__PURE__ */ jsxDEV20(ConversationReader, {
|
|
80482
|
+
row: reader.row,
|
|
80483
|
+
conv: reader.conv,
|
|
80484
|
+
width,
|
|
80485
|
+
height: height2,
|
|
80486
|
+
onClose: () => setReader(null),
|
|
80487
|
+
onResume: () => onDone(reader.row.id),
|
|
80488
|
+
onCancel: () => onDone(null)
|
|
80489
|
+
}, undefined, false, undefined, this);
|
|
80490
|
+
}
|
|
80491
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80492
|
+
flexDirection: "column",
|
|
80493
|
+
height: height2,
|
|
80494
|
+
backgroundColor: C.bg,
|
|
80495
|
+
children: [
|
|
80496
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80497
|
+
flexDirection: "row",
|
|
80498
|
+
justifyContent: "space-between",
|
|
80499
|
+
height: 1,
|
|
80500
|
+
paddingX: 1,
|
|
80501
|
+
children: [
|
|
80502
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80503
|
+
children: [
|
|
80504
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80505
|
+
fg: tokens.accent,
|
|
80506
|
+
attributes: 1,
|
|
80507
|
+
children: "claudish"
|
|
80508
|
+
}, undefined, false, undefined, this),
|
|
80509
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80510
|
+
fg: tokens.subtle,
|
|
80511
|
+
children: " resume"
|
|
80512
|
+
}, undefined, false, undefined, this)
|
|
80513
|
+
]
|
|
80514
|
+
}, undefined, true, undefined, this),
|
|
80515
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80516
|
+
children: [
|
|
80517
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80518
|
+
fg: tokens.subtle,
|
|
80519
|
+
children: `${visibleGroups.length} worktrees \xB7 ${shownSessions} sessions`
|
|
80520
|
+
}, undefined, false, undefined, this),
|
|
80521
|
+
filter ? /* @__PURE__ */ jsxDEV20("span", {
|
|
80522
|
+
fg: tokens.warn,
|
|
80523
|
+
children: ` /${filter}`
|
|
80524
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
|
|
80525
|
+
]
|
|
80526
|
+
}, undefined, true, undefined, this)
|
|
80527
|
+
]
|
|
80528
|
+
}, undefined, true, undefined, this),
|
|
80529
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80530
|
+
flexDirection: "row",
|
|
80531
|
+
flexGrow: 1,
|
|
80532
|
+
minHeight: 0,
|
|
80533
|
+
children: [
|
|
80534
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80535
|
+
width: sidebarW,
|
|
80536
|
+
flexDirection: "column",
|
|
80537
|
+
minHeight: 0,
|
|
80538
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80539
|
+
title: "worktrees",
|
|
80540
|
+
focused: pane === "worktrees",
|
|
80541
|
+
flush: true,
|
|
80542
|
+
flexGrow: 1,
|
|
80543
|
+
flexBasis: 0,
|
|
80544
|
+
children: [
|
|
80545
|
+
/* @__PURE__ */ jsxDEV20("scrollbox", {
|
|
80546
|
+
focused: false,
|
|
80547
|
+
flexGrow: 1,
|
|
80548
|
+
scrollbarOptions: SCROLLBAR,
|
|
80549
|
+
children: [
|
|
80550
|
+
fresh.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
|
|
80551
|
+
g,
|
|
80552
|
+
cursor: i === wtCursor,
|
|
80553
|
+
width: sideInner,
|
|
80554
|
+
count: listed.get(g.name)?.length ?? 0,
|
|
80555
|
+
cols: chipCols
|
|
80556
|
+
}, g.name, false, undefined, this)),
|
|
80557
|
+
stale.length > 0 ? /* @__PURE__ */ jsxDEV20(SectionHeader, {
|
|
80558
|
+
label: `stale \xB7 ${stale.length} \xB7 idle 3d+`,
|
|
80559
|
+
width: sideInner
|
|
80560
|
+
}, undefined, false, undefined, this) : null,
|
|
80561
|
+
stale.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
|
|
80562
|
+
g,
|
|
80563
|
+
cursor: fresh.length + i === wtCursor,
|
|
80564
|
+
width: sideInner,
|
|
80565
|
+
count: listed.get(g.name)?.length ?? 0,
|
|
80566
|
+
cols: chipCols
|
|
80567
|
+
}, g.name, false, undefined, this))
|
|
80568
|
+
]
|
|
80569
|
+
}, undefined, true, undefined, this),
|
|
80570
|
+
showActivity ? /* @__PURE__ */ jsxDEV20(ActivityCalendar, {
|
|
80571
|
+
days: activityDays,
|
|
80572
|
+
width: sideInner
|
|
80573
|
+
}, undefined, false, undefined, this) : null
|
|
80574
|
+
]
|
|
80575
|
+
}, undefined, true, undefined, this)
|
|
80576
|
+
}, undefined, false, undefined, this),
|
|
80577
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80578
|
+
flexDirection: "column",
|
|
80579
|
+
flexGrow: 1,
|
|
80580
|
+
minWidth: 0,
|
|
80581
|
+
minHeight: 0,
|
|
80582
|
+
children: [
|
|
80583
|
+
/* @__PURE__ */ jsxDEV20(Panel, {
|
|
80584
|
+
title: group ? `sessions \xB7 ${truncate3(group.name, 28)}` : "sessions",
|
|
80585
|
+
focused: pane === "sessions",
|
|
80586
|
+
flush: true,
|
|
80587
|
+
flexGrow: 1,
|
|
80588
|
+
flexBasis: 0,
|
|
80589
|
+
children: /* @__PURE__ */ jsxDEV20("scrollbox", {
|
|
80590
|
+
focused: false,
|
|
80591
|
+
flexGrow: 1,
|
|
80592
|
+
scrollbarOptions: SCROLLBAR,
|
|
80593
|
+
children: items.length === 0 ? /* @__PURE__ */ jsxDEV20("text", {
|
|
80594
|
+
fg: tokens.subtle,
|
|
80595
|
+
children: " no sessions match"
|
|
80596
|
+
}, undefined, false, undefined, this) : items.map((it, i) => it.kind === "agents" ? /* @__PURE__ */ jsxDEV20(AgentNode, {
|
|
80597
|
+
count: it.count,
|
|
80598
|
+
open: agentsOpen,
|
|
80599
|
+
cursor: i === sessCursor && pane === "sessions",
|
|
80600
|
+
width: sessInner
|
|
80601
|
+
}, "agents", false, undefined, this) : /* @__PURE__ */ jsxDEV20(SessionRowView, {
|
|
80602
|
+
row: it.row,
|
|
80603
|
+
cursor: i === sessCursor && pane === "sessions",
|
|
80604
|
+
width: sessInner,
|
|
80605
|
+
even: i % 2 === 0,
|
|
80606
|
+
indent: it.kind === "agent" ? 2 : 0,
|
|
80607
|
+
maxSize,
|
|
80608
|
+
meterW
|
|
80609
|
+
}, it.row.id, false, undefined, this))
|
|
80610
|
+
}, undefined, false, undefined, this)
|
|
80611
|
+
}, undefined, false, undefined, this),
|
|
80612
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80613
|
+
height: sessionDetailH,
|
|
80614
|
+
flexShrink: 0,
|
|
80615
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80616
|
+
title: "session",
|
|
80617
|
+
flexGrow: 1,
|
|
80618
|
+
children: selected ? /* @__PURE__ */ jsxDEV20(SessionDetail, {
|
|
80619
|
+
row: selected,
|
|
80620
|
+
width: sessionDetailInner,
|
|
80621
|
+
turns
|
|
80622
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("text", {
|
|
80623
|
+
fg: tokens.subtle,
|
|
80624
|
+
children: cursorItem?.kind === "agents" ? "agent sessions \u2014 enter to expand" : "nothing selected"
|
|
80625
|
+
}, undefined, false, undefined, this)
|
|
80626
|
+
}, undefined, false, undefined, this)
|
|
80627
|
+
}, undefined, false, undefined, this)
|
|
80628
|
+
]
|
|
80629
|
+
}, undefined, true, undefined, this)
|
|
80630
|
+
]
|
|
80631
|
+
}, undefined, true, undefined, this),
|
|
80632
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80633
|
+
height: WORKTREE_DETAIL_H,
|
|
80634
|
+
flexShrink: 0,
|
|
80635
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80636
|
+
title: "worktree",
|
|
80637
|
+
flexGrow: 1,
|
|
80638
|
+
children: /* @__PURE__ */ jsxDEV20(WorktreeDetail, {
|
|
80639
|
+
group,
|
|
80640
|
+
width: worktreeDetailInner,
|
|
80641
|
+
count: sessions2.length
|
|
80642
|
+
}, undefined, false, undefined, this)
|
|
80643
|
+
}, undefined, false, undefined, this)
|
|
80644
|
+
}, undefined, false, undefined, this),
|
|
80645
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80646
|
+
flexDirection: "row",
|
|
80647
|
+
height: 1,
|
|
80648
|
+
paddingX: 1,
|
|
80649
|
+
gap: 2,
|
|
80650
|
+
children: [
|
|
80651
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80652
|
+
fg: tokens.subtle,
|
|
80653
|
+
children: "\u2191\u2193 move"
|
|
80654
|
+
}, undefined, false, undefined, this),
|
|
80655
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80656
|
+
fg: tokens.subtle,
|
|
80657
|
+
children: "\u21E5 pane"
|
|
80658
|
+
}, undefined, false, undefined, this),
|
|
80659
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80660
|
+
fg: tokens.subtle,
|
|
80661
|
+
children: "type to filter"
|
|
80662
|
+
}, undefined, false, undefined, this),
|
|
80663
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80664
|
+
fg: tokens.accent,
|
|
80665
|
+
children: "\u23CE resume"
|
|
80666
|
+
}, undefined, false, undefined, this),
|
|
80667
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80668
|
+
fg: selected ? tokens.info : tokens.trace,
|
|
80669
|
+
children: "v read"
|
|
80670
|
+
}, undefined, false, undefined, this),
|
|
80671
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80672
|
+
fg: agentRows.length > 0 ? tokens.subtle : tokens.trace,
|
|
80673
|
+
children: agentRows.length > 0 ? `a ${agentsOpen ? "collapse" : "expand"} agents` : ""
|
|
80674
|
+
}, undefined, false, undefined, this),
|
|
80675
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80676
|
+
fg: tokens.subtle,
|
|
80677
|
+
children: "esc cancel"
|
|
80678
|
+
}, undefined, false, undefined, this)
|
|
80679
|
+
]
|
|
80680
|
+
}, undefined, true, undefined, this)
|
|
80681
|
+
]
|
|
80682
|
+
}, undefined, true, undefined, this);
|
|
80683
|
+
}
|
|
80684
|
+
function Field({
|
|
80685
|
+
label,
|
|
80686
|
+
width,
|
|
80687
|
+
children
|
|
80688
|
+
}) {
|
|
80689
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80690
|
+
flexDirection: "row",
|
|
80691
|
+
height: 1,
|
|
80692
|
+
gap: 1,
|
|
80693
|
+
children: [
|
|
80694
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80695
|
+
fg: tokens.subtle,
|
|
80696
|
+
children: padTo(label, width)
|
|
80697
|
+
}, undefined, false, undefined, this),
|
|
80698
|
+
children
|
|
80699
|
+
]
|
|
80700
|
+
}, undefined, true, undefined, this);
|
|
80701
|
+
}
|
|
80702
|
+
function WorktreeDetail({
|
|
80703
|
+
group,
|
|
80704
|
+
width,
|
|
80705
|
+
count
|
|
80706
|
+
}) {
|
|
80707
|
+
if (!group)
|
|
80708
|
+
return /* @__PURE__ */ jsxDEV20("text", {
|
|
80709
|
+
fg: tokens.subtle,
|
|
80710
|
+
children: "no worktree selected"
|
|
80711
|
+
}, undefined, false, undefined, this);
|
|
80712
|
+
const L = 9;
|
|
80713
|
+
const badges = [];
|
|
80714
|
+
if (group.dirty !== undefined) {
|
|
80715
|
+
badges.push({
|
|
80716
|
+
label: group.dirty > 0 ? `${DIRTY_GLYPH}${group.dirty} uncommitted` : "clean",
|
|
80717
|
+
bg: group.dirty > 0 ? CHIP.dirty : CHIP.clean
|
|
80718
|
+
});
|
|
80719
|
+
}
|
|
80720
|
+
if (group.ahead)
|
|
80721
|
+
badges.push({ label: `\u2191${group.ahead}`, bg: CHIP.ahead });
|
|
80722
|
+
if (group.behind)
|
|
80723
|
+
badges.push({ label: `\u2193${group.behind}`, bg: CHIP.behind });
|
|
80724
|
+
const badgeW = badges.reduce((w, b) => w + displayWidth(b.label) + 2, 0);
|
|
80725
|
+
const marker = group.current ? " \u25B6 you are here" : !group.live ? " worktree deleted" : "";
|
|
80726
|
+
const branch = group.branch ?? (group.live ? "detached" : "\u2014");
|
|
80727
|
+
const idAvail = Math.max(12, width - L - displayWidth(marker) - (BRANCH_LEAD + 1) - badgeW - 1);
|
|
80728
|
+
const nameW = Math.max(8, Math.min(displayWidth(group.name), Math.floor(idAvail / 2)));
|
|
80729
|
+
const branchW = Math.max(6, idAvail - nameW);
|
|
80730
|
+
const spark = activitySeries(group.sessions);
|
|
80731
|
+
const summary = ` ${count} session${count === 1 ? "" : "s"} \xB7 created ${group.createdMs ? age(group.createdMs) : "?"} ago \xB7 used ${group.lastActiveMs ? age(group.lastActiveMs) : "?"} ago`;
|
|
80732
|
+
const locAvail = Math.max(20, width - L - spark.length - 2);
|
|
80733
|
+
const pathW = Math.min(displayWidth(group.path ?? "\u2014"), Math.max(16, Math.floor(locAvail * 0.45)));
|
|
80734
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80735
|
+
flexDirection: "column",
|
|
80736
|
+
children: [
|
|
80737
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80738
|
+
children: [
|
|
80739
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80740
|
+
fg: tokens.subtle,
|
|
80741
|
+
children: padTo("worktree", L)
|
|
80742
|
+
}, undefined, false, undefined, this),
|
|
80743
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80744
|
+
fg: tokens.text,
|
|
80745
|
+
attributes: A.bold,
|
|
80746
|
+
children: truncate3(group.name, nameW)
|
|
80747
|
+
}, undefined, false, undefined, this),
|
|
80748
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80749
|
+
fg: group.current ? HERE_FG : tokens.dead,
|
|
80750
|
+
children: marker
|
|
80751
|
+
}, undefined, false, undefined, this),
|
|
80752
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80753
|
+
fg: tokens.subtle,
|
|
80754
|
+
children: ` ${BRANCH_ICON} `
|
|
80755
|
+
}, undefined, false, undefined, this),
|
|
80756
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80757
|
+
fg: group.live ? tokens.info : tokens.dead,
|
|
80758
|
+
children: `${truncate3(branch, branchW)} `
|
|
80759
|
+
}, undefined, false, undefined, this),
|
|
80760
|
+
badges.map((b) => /* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80761
|
+
label: b.label,
|
|
80762
|
+
bg: b.bg
|
|
80763
|
+
}, b.label, false, undefined, this))
|
|
80764
|
+
]
|
|
80765
|
+
}, undefined, true, undefined, this),
|
|
80766
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80767
|
+
flexDirection: "row",
|
|
80768
|
+
height: 1,
|
|
80769
|
+
children: [
|
|
80770
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80771
|
+
fg: tokens.subtle,
|
|
80772
|
+
flexShrink: 0,
|
|
80773
|
+
children: padTo("path", L)
|
|
80774
|
+
}, undefined, false, undefined, this),
|
|
80775
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80776
|
+
fg: tokens.trace,
|
|
80777
|
+
flexShrink: 0,
|
|
80778
|
+
children: `${padTo(truncate3(group.path ?? "\u2014", pathW), pathW)} `
|
|
80779
|
+
}, undefined, false, undefined, this),
|
|
80780
|
+
/* @__PURE__ */ jsxDEV20(Sparkline, {
|
|
80781
|
+
values: hasActivity(spark) ? spark : [],
|
|
80782
|
+
fg: SPARK_FG
|
|
80783
|
+
}, undefined, false, undefined, this),
|
|
80784
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80785
|
+
fg: tokens.trace,
|
|
80786
|
+
flexShrink: 0,
|
|
80787
|
+
children: truncate3(summary, Math.max(0, locAvail - pathW))
|
|
80788
|
+
}, undefined, false, undefined, this)
|
|
80789
|
+
]
|
|
80790
|
+
}, undefined, true, undefined, this)
|
|
80791
|
+
]
|
|
80792
|
+
}, undefined, true, undefined, this);
|
|
80793
|
+
}
|
|
80794
|
+
function SessionDetail({
|
|
80795
|
+
row,
|
|
80796
|
+
width,
|
|
80797
|
+
turns
|
|
80798
|
+
}) {
|
|
80799
|
+
const mb = row.sizeBytes / 1048576;
|
|
80800
|
+
const L = 9;
|
|
80801
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.round(row.sizeBytes / 1024)} KB`;
|
|
80802
|
+
const full = `\xB7 ${size}${row.lastMessageChars !== undefined ? ` \xB7 last msg ${row.lastMessageChars} ch` : ""}`;
|
|
80803
|
+
const suffixRoom = width - L - 1 - row.id.length - 1;
|
|
80804
|
+
const suffix = displayWidth(full) <= suffixRoom ? full : displayWidth(`\xB7 ${size}`) <= suffixRoom ? `\xB7 ${size}` : "";
|
|
80805
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80806
|
+
flexDirection: "column",
|
|
80807
|
+
children: [
|
|
80808
|
+
/* @__PURE__ */ jsxDEV20(Field, {
|
|
80809
|
+
label: "title",
|
|
80810
|
+
width: L,
|
|
80811
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80812
|
+
fg: tokens.text,
|
|
80813
|
+
attributes: A.bold,
|
|
80814
|
+
children: truncate3(sessionLabel(row), width - L)
|
|
80815
|
+
}, undefined, false, undefined, this)
|
|
80816
|
+
}, undefined, false, undefined, this),
|
|
80817
|
+
/* @__PURE__ */ jsxDEV20(Field, {
|
|
80818
|
+
label: "id",
|
|
80819
|
+
width: L,
|
|
80820
|
+
children: [
|
|
80821
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80822
|
+
fg: tokens.info,
|
|
80823
|
+
flexShrink: 0,
|
|
80824
|
+
children: row.id
|
|
80825
|
+
}, undefined, false, undefined, this),
|
|
80826
|
+
suffix ? /* @__PURE__ */ jsxDEV20("text", {
|
|
80827
|
+
fg: tokens.trace,
|
|
80828
|
+
flexShrink: 0,
|
|
80829
|
+
children: suffix
|
|
80830
|
+
}, undefined, false, undefined, this) : null
|
|
80831
|
+
]
|
|
80832
|
+
}, undefined, true, undefined, this),
|
|
80833
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80834
|
+
height: 1
|
|
80835
|
+
}, undefined, false, undefined, this),
|
|
80836
|
+
/* @__PURE__ */ jsxDEV20(Conversation, {
|
|
80837
|
+
turns: row.recentTurns ?? [],
|
|
80838
|
+
width,
|
|
80839
|
+
max: turns
|
|
80840
|
+
}, undefined, false, undefined, this)
|
|
80841
|
+
]
|
|
80842
|
+
}, undefined, true, undefined, this);
|
|
80843
|
+
}
|
|
80844
|
+
function Conversation({
|
|
80845
|
+
turns,
|
|
80846
|
+
width,
|
|
80847
|
+
max
|
|
80848
|
+
}) {
|
|
80849
|
+
if (turns.length === 0) {
|
|
80850
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80851
|
+
flexDirection: "column",
|
|
80852
|
+
width,
|
|
80853
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80854
|
+
fg: tokens.trace,
|
|
80855
|
+
children: "no conversation recorded"
|
|
80856
|
+
}, undefined, false, undefined, this)
|
|
80857
|
+
}, undefined, false, undefined, this);
|
|
80858
|
+
}
|
|
80859
|
+
const ROLE_W2 = 6;
|
|
80860
|
+
const textW = Math.max(10, width - ROLE_W2 - 2);
|
|
80861
|
+
const shown = turns.slice(-max);
|
|
80862
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80863
|
+
flexDirection: "column",
|
|
80864
|
+
width,
|
|
80865
|
+
flexShrink: 0,
|
|
80866
|
+
children: shown.map((t, i) => /* @__PURE__ */ jsxDEV20("text", {
|
|
80867
|
+
children: [
|
|
80868
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80869
|
+
fg: tokens.trace,
|
|
80870
|
+
children: "\u258D"
|
|
80871
|
+
}, undefined, false, undefined, this),
|
|
80872
|
+
/* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80873
|
+
label: t.role === "user" ? "you" : "ai",
|
|
80874
|
+
bg: t.role === "user" ? SPEAKER.you : SPEAKER.ai,
|
|
80875
|
+
width: ROLE_W2
|
|
80876
|
+
}, undefined, false, undefined, this),
|
|
80877
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80878
|
+
fg: t.role === "user" ? tokens.text : MUTED,
|
|
80879
|
+
children: truncate3(t.text, textW)
|
|
80880
|
+
}, undefined, false, undefined, this)
|
|
80881
|
+
]
|
|
80882
|
+
}, `${i}-${t.text.slice(0, 12)}`, true, undefined, this))
|
|
80883
|
+
}, undefined, false, undefined, this);
|
|
80884
|
+
}
|
|
80885
|
+
var PANEL_CHROME = 4, PANEL_BORDER = 2, SCROLL_CHROME = 1, SIDEBAR_MIN = 28, SIDEBAR_MAX = 50, DETAIL_CHROME = 5, WORKTREE_DETAIL_H = 4, STALE_MS, GH_LEVELS, CHIP, HERE_FG, LIVE_W = 2, DIRTY_GLYPH = "+", SESSION_AGE_W = 3, SESSION_AGE_COL, SIZE_FLOOR_BYTES, MUTED, SCROLLBAR, WEEK_DAYS = 7, ACTIVITY_WEEKS = 6, BRANCH_ICON = "\u2387", BRANCH_LEAD = 5, BRANCH_LEAD_DENSE = 6, WEEK_LABEL_W = 4, ACTIVITY_H, SPARK_MIN_DAYS = 7, SPARK_FG = "#3f6f9e";
|
|
80886
|
+
var init_resume_picker = __esm(() => {
|
|
80887
|
+
init_theme2();
|
|
80888
|
+
init_text();
|
|
80889
|
+
init_tokens();
|
|
80890
|
+
init_widgets();
|
|
80891
|
+
init_conversation_reader();
|
|
80892
|
+
init_conversation();
|
|
80893
|
+
init_session_discovery();
|
|
80894
|
+
STALE_MS = 3 * 86400000;
|
|
80895
|
+
GH_LEVELS = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
|
|
80896
|
+
CHIP = {
|
|
80897
|
+
fresh: GH_LEVELS[4],
|
|
80898
|
+
stale: "#a1a9b3",
|
|
80899
|
+
count: "#58a6ff",
|
|
80900
|
+
dirty: "#d29922",
|
|
80901
|
+
clean: GH_LEVELS[3],
|
|
80902
|
+
ahead: "#bc8cff",
|
|
80903
|
+
behind: "#d2a8ff"
|
|
80904
|
+
};
|
|
80905
|
+
HERE_FG = GH_LEVELS[4];
|
|
80906
|
+
SESSION_AGE_COL = SESSION_AGE_W + 3;
|
|
80907
|
+
SIZE_FLOOR_BYTES = 16 * 1024;
|
|
80908
|
+
MUTED = C.fgMuted;
|
|
80909
|
+
SCROLLBAR = {
|
|
80910
|
+
showArrows: false,
|
|
80911
|
+
trackOptions: { backgroundColor: tokens.bgPanel, foregroundColor: tokens.border }
|
|
80912
|
+
};
|
|
80913
|
+
ACTIVITY_H = 2 + ACTIVITY_WEEKS;
|
|
80914
|
+
});
|
|
80915
|
+
|
|
80916
|
+
// src/session/resume-picker-run.tsx
|
|
80917
|
+
var exports_resume_picker_run = {};
|
|
80918
|
+
__export(exports_resume_picker_run, {
|
|
80919
|
+
runResumePicker: () => runResumePicker
|
|
80920
|
+
});
|
|
80921
|
+
import { createCliRenderer as createCliRenderer3 } from "@opentui/core";
|
|
80922
|
+
import { createRoot as createRoot3 } from "@opentui/react";
|
|
80923
|
+
import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
|
|
80924
|
+
async function runResumePicker(cwd = process.cwd()) {
|
|
80925
|
+
const repo = getRepoContext(cwd);
|
|
80926
|
+
if (!repo)
|
|
80927
|
+
return { sessionId: null, hadSessions: false };
|
|
80928
|
+
const groups = discoverWorktreeGroups(repo);
|
|
80929
|
+
if (groups.length === 0)
|
|
80930
|
+
return { sessionId: null, hadSessions: false };
|
|
80931
|
+
await enrichWorktreeGit(groups, repo.root);
|
|
80932
|
+
setStderrQuiet(true);
|
|
80933
|
+
const renderer = await createCliRenderer3({
|
|
80934
|
+
useAlternateScreen: true,
|
|
80935
|
+
exitOnCtrlC: false
|
|
80936
|
+
});
|
|
80937
|
+
const root = createRoot3(renderer);
|
|
80938
|
+
let chosen = null;
|
|
80939
|
+
try {
|
|
80940
|
+
await new Promise((resolve5) => {
|
|
80941
|
+
let settled = false;
|
|
80942
|
+
const done = (id) => {
|
|
80943
|
+
if (settled)
|
|
80944
|
+
return;
|
|
80945
|
+
settled = true;
|
|
80946
|
+
chosen = id;
|
|
80947
|
+
resolve5();
|
|
80948
|
+
};
|
|
80949
|
+
root.render(/* @__PURE__ */ jsxDEV21(ResumePicker, {
|
|
80950
|
+
groups,
|
|
80951
|
+
onDone: done
|
|
80952
|
+
}, undefined, false, undefined, this));
|
|
80953
|
+
});
|
|
80954
|
+
} finally {
|
|
80955
|
+
try {
|
|
80956
|
+
root.unmount();
|
|
80957
|
+
} catch {}
|
|
80958
|
+
try {
|
|
80959
|
+
renderer.destroy();
|
|
80960
|
+
} catch {}
|
|
80961
|
+
setStderrQuiet(false);
|
|
80962
|
+
}
|
|
80963
|
+
return { sessionId: chosen, hadSessions: true };
|
|
80964
|
+
}
|
|
80965
|
+
var init_resume_picker_run = __esm(() => {
|
|
80966
|
+
init_logger();
|
|
80967
|
+
init_resume_picker();
|
|
80968
|
+
init_session_discovery();
|
|
80969
|
+
});
|
|
80970
|
+
|
|
80971
|
+
// src/session/baseline-pricing.ts
|
|
80972
|
+
function resolveBaseline(alias, label) {
|
|
80973
|
+
const entry = findEntryByAlias(alias);
|
|
80974
|
+
if (!entry)
|
|
80975
|
+
return null;
|
|
80976
|
+
const firstParty = entry.aggregators?.find((a) => a.provider === FIRST_PARTY && typeof a.pricing?.input === "number");
|
|
80977
|
+
const input = firstParty?.pricing?.input;
|
|
80978
|
+
const output = firstParty?.pricing?.output;
|
|
80979
|
+
if (typeof input !== "number" || typeof output !== "number")
|
|
80980
|
+
return null;
|
|
80981
|
+
if (input <= 0 || output <= 0)
|
|
80982
|
+
return null;
|
|
80983
|
+
return { modelId: entry.modelId, label, inputPerM: input, outputPerM: output };
|
|
80984
|
+
}
|
|
80985
|
+
function getBaselines() {
|
|
80986
|
+
return BASELINE_ALIASES.map(({ alias, label }) => resolveBaseline(alias, label)).filter((b) => b !== null);
|
|
80987
|
+
}
|
|
80988
|
+
function baselineCost(b, inputTokens, outputTokens) {
|
|
80989
|
+
return inputTokens / 1e6 * b.inputPerM + outputTokens / 1e6 * b.outputPerM;
|
|
80990
|
+
}
|
|
80991
|
+
var BASELINE_ALIASES, FIRST_PARTY = "anthropic";
|
|
80992
|
+
var init_baseline_pricing = __esm(() => {
|
|
80993
|
+
init_catalog_query();
|
|
80994
|
+
BASELINE_ALIASES = [
|
|
80995
|
+
{ alias: "~anthropic/claude-sonnet-latest", label: "Sonnet" },
|
|
80996
|
+
{ alias: "~anthropic/claude-opus-latest", label: "Opus" }
|
|
80997
|
+
];
|
|
80998
|
+
});
|
|
80999
|
+
|
|
81000
|
+
// src/session/session-stats.ts
|
|
81001
|
+
var exports_session_stats = {};
|
|
81002
|
+
__export(exports_session_stats, {
|
|
81003
|
+
tokenFilePath: () => tokenFilePath,
|
|
81004
|
+
readSessionStats: () => readSessionStats,
|
|
81005
|
+
computeSavings: () => computeSavings
|
|
81006
|
+
});
|
|
81007
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
81008
|
+
import { homedir as homedir35 } from "os";
|
|
81009
|
+
import { join as join38 } from "path";
|
|
81010
|
+
function tokenFilePath(port) {
|
|
81011
|
+
return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
|
|
81012
|
+
}
|
|
81013
|
+
function readSessionStats(port) {
|
|
81014
|
+
let raw2;
|
|
81015
|
+
try {
|
|
81016
|
+
raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
|
|
81017
|
+
} catch {
|
|
81018
|
+
return null;
|
|
81019
|
+
}
|
|
81020
|
+
if (!raw2 || typeof raw2 !== "object")
|
|
81021
|
+
return null;
|
|
81022
|
+
const d = raw2;
|
|
81023
|
+
const inputTokens = num(d.input_tokens);
|
|
81024
|
+
const outputTokens = num(d.output_tokens);
|
|
81025
|
+
if (inputTokens <= 0 && outputTokens <= 0)
|
|
81026
|
+
return null;
|
|
81027
|
+
const costUsd = num(d.total_cost);
|
|
81028
|
+
const isFree = d.is_free === true;
|
|
81029
|
+
const contextWindow = typeof d.context_window === "number" ? d.context_window : null;
|
|
81030
|
+
const contextUsed = contextWindow && contextWindow > 0 ? Math.min(1, Math.max(0, inputTokens / contextWindow)) : null;
|
|
81031
|
+
const toolCalls = Array.isArray(d.tool_calls) ? d.tool_calls.map((t) => t).filter((t) => typeof t?.name === "string" && num(t.count) > 0).map((t) => ({ name: String(t.name), count: num(t.count) })) : [];
|
|
81032
|
+
const startedAt = num(d.started_at);
|
|
81033
|
+
const updatedAt = num(d.updated_at);
|
|
81034
|
+
const durationMs = startedAt > 0 && updatedAt > startedAt ? updatedAt - startedAt : 0;
|
|
81035
|
+
const billedInputTokens = num(d.billed_input_tokens) || inputTokens;
|
|
81036
|
+
const rawIn = billedInputTokens / 1e6 * num(d.input_per_m);
|
|
81037
|
+
const rawOut = outputTokens / 1e6 * num(d.output_per_m);
|
|
81038
|
+
const rawTotal = rawIn + rawOut;
|
|
81039
|
+
const scale = rawTotal > 0 && costUsd > 0 ? costUsd / rawTotal : 0;
|
|
81040
|
+
return {
|
|
81041
|
+
inputTokens,
|
|
81042
|
+
outputTokens,
|
|
81043
|
+
totalTokens: num(d.total_tokens) || inputTokens + outputTokens,
|
|
81044
|
+
costUsd,
|
|
81045
|
+
isFree,
|
|
81046
|
+
isEstimated: d.is_estimated === true,
|
|
81047
|
+
providerName: typeof d.provider_name === "string" ? d.provider_name : "",
|
|
81048
|
+
modelName: typeof d.model_name === "string" ? d.model_name : "",
|
|
81049
|
+
contextWindow,
|
|
81050
|
+
contextUsed,
|
|
81051
|
+
toolCalls,
|
|
81052
|
+
toolCallTotal: toolCalls.reduce((a, t) => a + t.count, 0),
|
|
81053
|
+
durationMs,
|
|
81054
|
+
savings: computeSavings(billedInputTokens, outputTokens, costUsd),
|
|
81055
|
+
inputCostUsd: rawIn * scale,
|
|
81056
|
+
outputCostUsd: rawOut * scale,
|
|
81057
|
+
billedInputTokens
|
|
81058
|
+
};
|
|
81059
|
+
}
|
|
81060
|
+
function computeSavings(inputTokens, outputTokens, actualUsd) {
|
|
81061
|
+
return getBaselines().map((b) => {
|
|
81062
|
+
const baselineUsd = baselineCost(b, inputTokens, outputTokens);
|
|
81063
|
+
return {
|
|
81064
|
+
label: b.label,
|
|
81065
|
+
modelId: b.modelId,
|
|
81066
|
+
baselineUsd,
|
|
81067
|
+
savedUsd: baselineUsd - actualUsd
|
|
81068
|
+
};
|
|
81069
|
+
});
|
|
81070
|
+
}
|
|
81071
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
81072
|
+
var init_session_stats = __esm(() => {
|
|
81073
|
+
init_baseline_pricing();
|
|
81074
|
+
});
|
|
81075
|
+
|
|
81076
|
+
// src/session/ansi-viz.ts
|
|
81077
|
+
function rgb(hex4) {
|
|
81078
|
+
return [
|
|
81079
|
+
Number.parseInt(hex4.slice(1, 3), 16),
|
|
81080
|
+
Number.parseInt(hex4.slice(3, 5), 16),
|
|
81081
|
+
Number.parseInt(hex4.slice(5, 7), 16)
|
|
81082
|
+
];
|
|
81083
|
+
}
|
|
81084
|
+
function fg(hex4) {
|
|
81085
|
+
const [r, g, b] = rgb(hex4);
|
|
81086
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
81087
|
+
}
|
|
81088
|
+
function bg(hex4) {
|
|
81089
|
+
const [r, g, b] = rgb(hex4);
|
|
81090
|
+
return `\x1B[48;2;${r};${g};${b}m`;
|
|
81091
|
+
}
|
|
81092
|
+
function paint(text, hex4, bold4 = false) {
|
|
81093
|
+
return `${bold4 ? BOLD5 : ""}${fg(hex4)}${text}${RESET4}`;
|
|
81094
|
+
}
|
|
81095
|
+
function meter(pct, width, ramp = ramps.load) {
|
|
81096
|
+
const cells = Math.floor(width);
|
|
81097
|
+
if (!Number.isFinite(cells) || cells <= 0)
|
|
81098
|
+
return "";
|
|
81099
|
+
if (Number.isNaN(pct))
|
|
81100
|
+
return paint(NODATA2.repeat(cells), tokens.dead);
|
|
81101
|
+
const cols = rampFor(cells, ramp);
|
|
81102
|
+
const filled = fillCells(pct, cells);
|
|
81103
|
+
let out = "";
|
|
81104
|
+
let last = "";
|
|
81105
|
+
for (let i = 0;i < cells; i++) {
|
|
81106
|
+
const hex4 = i < filled ? cols[i] : tokens.border;
|
|
81107
|
+
if (hex4 !== last) {
|
|
81108
|
+
out += fg(hex4);
|
|
81109
|
+
last = hex4;
|
|
81110
|
+
}
|
|
81111
|
+
out += i < filled ? FILL2 : TRACK2;
|
|
81112
|
+
}
|
|
81113
|
+
return out + RESET4;
|
|
81114
|
+
}
|
|
81115
|
+
function stackedBar(segments, width) {
|
|
81116
|
+
const cells = Math.floor(width);
|
|
81117
|
+
if (!Number.isFinite(cells) || cells <= 0 || segments.length === 0)
|
|
81118
|
+
return "";
|
|
81119
|
+
const split = splitCells(segments.map((s) => s.value), cells);
|
|
81120
|
+
if (split.reduce((a, b) => a + b, 0) === 0)
|
|
81121
|
+
return paint(TRACK2.repeat(cells), tokens.border);
|
|
81122
|
+
let out = "";
|
|
81123
|
+
for (let i = 0;i < split.length; i++) {
|
|
81124
|
+
const n = split[i];
|
|
81125
|
+
if (n > 0)
|
|
81126
|
+
out += `${bg(segments[i].color)}${" ".repeat(n)}`;
|
|
81127
|
+
}
|
|
81128
|
+
return out + RESET4;
|
|
81129
|
+
}
|
|
81130
|
+
function badge(label, hex4) {
|
|
81131
|
+
return `${BOLD5}${fg(pickInk(hex4))}${bg(hex4)} ${label} ${RESET4}`;
|
|
81132
|
+
}
|
|
81133
|
+
function stripAnsi4(s) {
|
|
81134
|
+
return s.replace(ANSI_RE3, "");
|
|
81135
|
+
}
|
|
81136
|
+
function visibleWidth(s) {
|
|
81137
|
+
return displayWidth(stripAnsi4(s));
|
|
81138
|
+
}
|
|
81139
|
+
function clipStyled(s, width) {
|
|
81140
|
+
if (width <= 0)
|
|
81141
|
+
return "";
|
|
81142
|
+
if (visibleWidth(s) <= width)
|
|
81143
|
+
return s;
|
|
81144
|
+
let out = "";
|
|
81145
|
+
let w = 0;
|
|
81146
|
+
let i = 0;
|
|
81147
|
+
while (i < s.length) {
|
|
81148
|
+
if (s[i] === "\x1B") {
|
|
81149
|
+
const end = s.indexOf("m", i);
|
|
81150
|
+
if (end === -1)
|
|
81151
|
+
break;
|
|
81152
|
+
out += s.slice(i, end + 1);
|
|
81153
|
+
i = end + 1;
|
|
81154
|
+
continue;
|
|
81155
|
+
}
|
|
81156
|
+
const ch = [...s.slice(i)][0] ?? "";
|
|
81157
|
+
const cw = displayWidth(ch);
|
|
81158
|
+
if (w + cw > width)
|
|
81159
|
+
break;
|
|
81160
|
+
out += ch;
|
|
81161
|
+
w += cw;
|
|
81162
|
+
i += ch.length;
|
|
81163
|
+
}
|
|
81164
|
+
return out + RESET4;
|
|
81165
|
+
}
|
|
81166
|
+
function padVisible2(s, width, align = "left") {
|
|
81167
|
+
const clipped = clipStyled(s, width);
|
|
81168
|
+
const pad2 = Math.max(0, width - visibleWidth(clipped));
|
|
81169
|
+
return align === "left" ? clipped + " ".repeat(pad2) : " ".repeat(pad2) + clipped;
|
|
81170
|
+
}
|
|
81171
|
+
function compact(n) {
|
|
81172
|
+
if (!Number.isFinite(n))
|
|
81173
|
+
return "\u2014";
|
|
81174
|
+
const a = Math.abs(n);
|
|
81175
|
+
if (a >= 1e9)
|
|
81176
|
+
return `${(n / 1e9).toFixed(1)}G`;
|
|
81177
|
+
if (a >= 1e6)
|
|
81178
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
81179
|
+
if (a >= 1000)
|
|
81180
|
+
return `${(n / 1000).toFixed(1)}K`;
|
|
81181
|
+
return String(Math.round(n));
|
|
81182
|
+
}
|
|
81183
|
+
function duration3(ms) {
|
|
81184
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
81185
|
+
return "\u2014";
|
|
81186
|
+
const s = Math.round(ms / 1000);
|
|
81187
|
+
if (s < 60)
|
|
81188
|
+
return `${s}s`;
|
|
81189
|
+
const m = Math.floor(s / 60);
|
|
81190
|
+
if (m < 60)
|
|
81191
|
+
return `${m}m ${String(s % 60).padStart(2, "0")}s`;
|
|
81192
|
+
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
81193
|
+
}
|
|
81194
|
+
function usd(n) {
|
|
81195
|
+
if (!Number.isFinite(n))
|
|
81196
|
+
return "\u2014";
|
|
81197
|
+
const a = Math.abs(n);
|
|
81198
|
+
if (a === 0)
|
|
81199
|
+
return "$0";
|
|
81200
|
+
if (a < 0.01)
|
|
81201
|
+
return `$${n.toFixed(4)}`;
|
|
81202
|
+
if (a < 1)
|
|
81203
|
+
return `$${n.toFixed(3)}`;
|
|
81204
|
+
return `$${n.toFixed(2)}`;
|
|
81205
|
+
}
|
|
81206
|
+
var RESET4 = "\x1B[0m", BOLD5 = "\x1B[1m", FILL2 = "\u2588", TRACK2 = "\u2591", NODATA2 = "\u254C", ANSI_RE3;
|
|
81207
|
+
var init_ansi_viz = __esm(() => {
|
|
81208
|
+
init_color();
|
|
81209
|
+
init_text();
|
|
81210
|
+
init_tokens();
|
|
81211
|
+
init_widgets();
|
|
81212
|
+
ANSI_RE3 = /\x1b\[[0-9;]*m/g;
|
|
81213
|
+
});
|
|
81214
|
+
|
|
81215
|
+
// src/session/session-summary.ts
|
|
81216
|
+
var exports_session_summary = {};
|
|
81217
|
+
__export(exports_session_summary, {
|
|
81218
|
+
renderSessionSummary: () => renderSessionSummary,
|
|
81219
|
+
printSessionSummary: () => printSessionSummary
|
|
81220
|
+
});
|
|
81221
|
+
function cardWidth() {
|
|
81222
|
+
const cols = process.stdout.columns || 80;
|
|
81223
|
+
return Math.max(MIN_W, Math.min(MAX_W, cols - 2));
|
|
81224
|
+
}
|
|
81225
|
+
function renderSessionSummary(input) {
|
|
81226
|
+
const { stats, modelSpec, resumeModelSpec, resumeId, exitCode } = input;
|
|
81227
|
+
const W2 = cardWidth();
|
|
81228
|
+
const inner = W2 - CHROME;
|
|
81229
|
+
const out = [];
|
|
81230
|
+
const dim3 = (s) => paint(s, tokens.subtle);
|
|
81231
|
+
const body = (s) => paint(s, tokens.text);
|
|
81232
|
+
const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
|
|
81233
|
+
const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
|
|
81234
|
+
const rule = "\u2500".repeat(Math.max(0, W2 - 2 - visibleWidth(titleText) - 1));
|
|
81235
|
+
out.push(paint("\u256D\u2500", tokens.border) + paint(titleText, titleHex) + paint(rule + "\u256E", tokens.border));
|
|
81236
|
+
const row = (s) => {
|
|
81237
|
+
out.push(`${paint("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint("\u2502", tokens.border)}`);
|
|
81238
|
+
};
|
|
81239
|
+
const blank = () => row("");
|
|
81240
|
+
const chips = [badge(truncate3(modelSpec, 34), tokens.accent)];
|
|
81241
|
+
if (stats.isFree)
|
|
81242
|
+
chips.push(badge("FREE", C.pillKeyBg));
|
|
81243
|
+
else if (stats.isEstimated)
|
|
81244
|
+
chips.push(badge("EST", "#8a7d1e"));
|
|
81245
|
+
if (exitCode !== 0)
|
|
81246
|
+
chips.push(badge(`EXIT ${exitCode}`, "#9e2b2b"));
|
|
81247
|
+
const right = body(duration3(stats.durationMs));
|
|
81248
|
+
const left = clipStyled(chips.join(" "), Math.max(0, inner - visibleWidth(right) - 1));
|
|
81249
|
+
const gap = Math.max(1, inner - visibleWidth(left) - visibleWidth(right));
|
|
81250
|
+
row(left + " ".repeat(gap) + right);
|
|
81251
|
+
if (stats.providerName)
|
|
81252
|
+
row(dim3(truncate3(stats.providerName, inner)));
|
|
81253
|
+
blank();
|
|
81254
|
+
const VALUE_W = 24;
|
|
81255
|
+
const barW = Math.max(12, inner - LABEL_W - VALUE_W);
|
|
81256
|
+
const dataRow = (label, bar, values) => {
|
|
81257
|
+
row(dim3(padTo(label, LABEL_W)) + bar + padVisible2(values, VALUE_W, "right"));
|
|
81258
|
+
};
|
|
81259
|
+
if (stats.contextUsed !== null && stats.contextWindow) {
|
|
81260
|
+
const pct = stats.contextUsed * 100;
|
|
81261
|
+
dataRow("context", meter(pct, barW, ramps.load), body(padStartTo(`${Math.round(pct)}%`, 4)) + dim3(` ${compact(stats.inputTokens)}/${compact(stats.contextWindow)}`));
|
|
81262
|
+
}
|
|
81263
|
+
dataRow("tokens", stackedBar([
|
|
81264
|
+
{ value: stats.inputTokens, color: C.blue },
|
|
81265
|
+
{ value: stats.outputTokens, color: C.cyan }
|
|
81266
|
+
], barW), dim3("in ") + body(compact(stats.inputTokens)) + dim3(" out ") + body(compact(stats.outputTokens)));
|
|
81267
|
+
if (!stats.isFree && stats.inputCostUsd + stats.outputCostUsd > 0) {
|
|
81268
|
+
dataRow("spend", stackedBar([
|
|
81269
|
+
{ value: stats.inputCostUsd, color: C.blue },
|
|
81270
|
+
{ value: stats.outputCostUsd, color: C.cyan }
|
|
81271
|
+
], barW), dim3("in ") + body(usd(stats.inputCostUsd)) + dim3(" out ") + body(usd(stats.outputCostUsd)));
|
|
81272
|
+
}
|
|
81273
|
+
if (stats.toolCallTotal > 0) {
|
|
81274
|
+
const shown = stats.toolCalls.slice(0, TOOL_COLORS.length);
|
|
81275
|
+
const rest = stats.toolCalls.slice(TOOL_COLORS.length).reduce((a, t) => a + t.count, 0);
|
|
81276
|
+
const segs = shown.map((t, i) => ({ value: t.count, color: TOOL_COLORS[i] }));
|
|
81277
|
+
if (rest > 0)
|
|
81278
|
+
segs.push({ value: rest, color: TOOL_OTHER });
|
|
81279
|
+
dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim3(" calls"));
|
|
81280
|
+
const legend = shown.map((t, i) => paint(`${t.name} ${t.count}`, TOOL_COLORS[i])).concat(rest > 0 ? [paint(`other ${rest}`, TOOL_OTHER)] : []);
|
|
81281
|
+
for (const line of wrapStyled(legend, dim3(" \xB7 "), inner - LABEL_W)) {
|
|
81282
|
+
row(" ".repeat(LABEL_W) + line);
|
|
81283
|
+
}
|
|
81284
|
+
}
|
|
81285
|
+
blank();
|
|
81286
|
+
row(dim3(padTo("cost", LABEL_W)) + paint(stats.isFree ? "free" : usd(stats.costUsd), stats.isFree ? tokens.success : tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim3(" estimated") : ""));
|
|
81287
|
+
for (const s of stats.savings) {
|
|
81288
|
+
const label = padTo(`vs ${s.label}`, LABEL_W);
|
|
81289
|
+
if (s.savedUsd >= 0) {
|
|
81290
|
+
const pct = s.baselineUsd > 0 ? s.savedUsd / s.baselineUsd * 100 : 0;
|
|
81291
|
+
dataRow(label, meter(pct, barW, ramps.savings), paint(padStartTo(`${Math.round(pct)}%`, 4), tokens.success) + dim3(" saved ") + paint(usd(s.savedUsd), tokens.success));
|
|
81292
|
+
} else {
|
|
81293
|
+
dataRow(label, meter(0, barW, ramps.savings), dim3("over by ") + paint(usd(-s.savedUsd), tokens.error));
|
|
81294
|
+
}
|
|
81295
|
+
}
|
|
81296
|
+
out.push(paint(`\u2570${"\u2500".repeat(W2 - 2)}\u256F`, tokens.border));
|
|
81297
|
+
if (resumeId) {
|
|
81298
|
+
out.push("");
|
|
81299
|
+
out.push(dim3("Resume this session with:"));
|
|
81300
|
+
const modelFlag = resumeModelSpec ? `--model ${resumeModelSpec} ` : "";
|
|
81301
|
+
out.push(`claudish ${modelFlag}--resume ${resumeId}`);
|
|
81302
|
+
}
|
|
81303
|
+
return out;
|
|
81304
|
+
}
|
|
81305
|
+
function wrapStyled(chips, sep, width) {
|
|
81306
|
+
const lines = [];
|
|
81307
|
+
let cur = "";
|
|
81308
|
+
let curW = 0;
|
|
81309
|
+
const sepW = visibleWidth(sep);
|
|
81310
|
+
for (const chip of chips) {
|
|
81311
|
+
const w = visibleWidth(chip);
|
|
81312
|
+
if (cur && curW + sepW + w > width) {
|
|
81313
|
+
lines.push(cur);
|
|
81314
|
+
cur = chip;
|
|
81315
|
+
curW = w;
|
|
81316
|
+
} else {
|
|
81317
|
+
cur = cur ? cur + sep + chip : chip;
|
|
81318
|
+
curW = cur === chip ? w : curW + sepW + w;
|
|
81319
|
+
}
|
|
81320
|
+
}
|
|
81321
|
+
if (cur)
|
|
81322
|
+
lines.push(cur);
|
|
81323
|
+
return lines;
|
|
81324
|
+
}
|
|
81325
|
+
function printSessionSummary(input, write) {
|
|
81326
|
+
for (const line of renderSessionSummary(input))
|
|
81327
|
+
write(line);
|
|
81328
|
+
write(RESET4);
|
|
81329
|
+
}
|
|
81330
|
+
var TOOL_COLORS, TOOL_OTHER, MIN_W = 62, MAX_W = 96, CHROME = 4, LABEL_W = 10;
|
|
81331
|
+
var init_session_summary = __esm(() => {
|
|
81332
|
+
init_theme2();
|
|
81333
|
+
init_text();
|
|
81334
|
+
init_tokens();
|
|
81335
|
+
init_ansi_viz();
|
|
81336
|
+
TOOL_COLORS = [
|
|
81337
|
+
C.blue,
|
|
81338
|
+
C.cyan,
|
|
81339
|
+
"#8a7d1e",
|
|
81340
|
+
"#1f6d75",
|
|
81341
|
+
C.magenta,
|
|
81342
|
+
"#2d6e3e",
|
|
81343
|
+
C.orange
|
|
81344
|
+
];
|
|
81345
|
+
TOOL_OTHER = C.dim;
|
|
81346
|
+
});
|
|
81347
|
+
|
|
78414
81348
|
// src/index.ts
|
|
78415
81349
|
init_op_source();
|
|
78416
81350
|
init_startup_trace();
|
|
78417
81351
|
var import_dotenv3 = __toESM(require_main(), 1);
|
|
78418
|
-
import { existsSync as existsSync29, readFileSync as
|
|
78419
|
-
import { join as
|
|
81352
|
+
import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
|
|
81353
|
+
import { join as join39, resolve as resolve5 } from "path";
|
|
78420
81354
|
import_dotenv3.config({ quiet: true });
|
|
78421
81355
|
function classifyStartupKind() {
|
|
78422
81356
|
const argv = process.argv.slice(2);
|
|
@@ -78659,14 +81593,14 @@ async function runCli() {
|
|
|
78659
81593
|
if (cliConfig.team && cliConfig.team.length > 0) {
|
|
78660
81594
|
let prompt = cliConfig.claudeArgs.join(" ");
|
|
78661
81595
|
if (cliConfig.inputFile) {
|
|
78662
|
-
prompt =
|
|
81596
|
+
prompt = readFileSync29(cliConfig.inputFile, "utf-8");
|
|
78663
81597
|
}
|
|
78664
81598
|
if (!prompt.trim()) {
|
|
78665
81599
|
console.error("Error: --team requires a prompt (positional args or -f <file>)");
|
|
78666
81600
|
process.exit(1);
|
|
78667
81601
|
}
|
|
78668
81602
|
const mode = cliConfig.teamMode ?? "default";
|
|
78669
|
-
const sessionPath =
|
|
81603
|
+
const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
78670
81604
|
if (mode === "json") {
|
|
78671
81605
|
const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
|
|
78672
81606
|
setupSession2(sessionPath, cliConfig.team, prompt);
|
|
@@ -78676,9 +81610,9 @@ async function runCli() {
|
|
|
78676
81610
|
});
|
|
78677
81611
|
const result = { ...status2, responses: {} };
|
|
78678
81612
|
for (const anonId of Object.keys(status2.models)) {
|
|
78679
|
-
const responsePath =
|
|
81613
|
+
const responsePath = join39(sessionPath, `response-${anonId}.md`);
|
|
78680
81614
|
try {
|
|
78681
|
-
const raw2 =
|
|
81615
|
+
const raw2 = readFileSync29(responsePath, "utf-8").trim();
|
|
78682
81616
|
try {
|
|
78683
81617
|
result.responses[anonId] = JSON.parse(raw2);
|
|
78684
81618
|
} catch {
|
|
@@ -78703,8 +81637,8 @@ async function runCli() {
|
|
|
78703
81637
|
Team Status`);
|
|
78704
81638
|
for (const id of modelIds) {
|
|
78705
81639
|
const m = status.models[id];
|
|
78706
|
-
const
|
|
78707
|
-
console.log(` ${id} ${m.state.padEnd(10)} ${
|
|
81640
|
+
const duration4 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
|
|
81641
|
+
console.log(` ${id} ${m.state.padEnd(10)} ${duration4}`);
|
|
78708
81642
|
}
|
|
78709
81643
|
process.exit(0);
|
|
78710
81644
|
}
|
|
@@ -78855,6 +81789,28 @@ Team Status`);
|
|
|
78855
81789
|
haiku: cliConfig.modelHaiku,
|
|
78856
81790
|
subagent: cliConfig.modelSubagent
|
|
78857
81791
|
};
|
|
81792
|
+
let resumedSessionId = (() => {
|
|
81793
|
+
const i = cliConfig.claudeArgs.indexOf("--resume");
|
|
81794
|
+
const v = i !== -1 ? cliConfig.claudeArgs[i + 1] : undefined;
|
|
81795
|
+
return v && !v.startsWith("-") ? v : null;
|
|
81796
|
+
})();
|
|
81797
|
+
if (cliConfig._resumePicker) {
|
|
81798
|
+
const canDrawTui = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
81799
|
+
if (!canDrawTui || cliConfig._hasPrintFlag || !cliConfig.interactive) {
|
|
81800
|
+
cliConfig.claudeArgs.push("--resume");
|
|
81801
|
+
} else {
|
|
81802
|
+
const { runResumePicker: runResumePicker2 } = await Promise.resolve().then(() => (init_resume_picker_run(), exports_resume_picker_run));
|
|
81803
|
+
const outcome = await runResumePicker2();
|
|
81804
|
+
if (!outcome.hadSessions) {
|
|
81805
|
+
cliConfig.claudeArgs.push("--resume");
|
|
81806
|
+
} else if (!outcome.sessionId) {
|
|
81807
|
+
process.exit(0);
|
|
81808
|
+
} else {
|
|
81809
|
+
cliConfig.claudeArgs.push("--resume", outcome.sessionId);
|
|
81810
|
+
resumedSessionId = outcome.sessionId;
|
|
81811
|
+
}
|
|
81812
|
+
}
|
|
81813
|
+
}
|
|
78858
81814
|
const proxy = await traceSpan("startup:proxy-start", () => createProxyServer2(port, cliConfig.monitor ? undefined : cliConfig.openrouterApiKey, cliConfig.monitor ? undefined : explicitModel, cliConfig.monitor, cliConfig.anthropicApiKey, modelMap, {
|
|
78859
81815
|
summarizeTools: cliConfig.summarizeTools,
|
|
78860
81816
|
quiet: cliConfig.quiet,
|
|
@@ -78888,6 +81844,25 @@ Team Status`);
|
|
|
78888
81844
|
const write = cliConfig.interactive ? console.log : console.error;
|
|
78889
81845
|
write(`[claudish] Done
|
|
78890
81846
|
`);
|
|
81847
|
+
try {
|
|
81848
|
+
const [{ readSessionStats: readSessionStats2 }, { printSessionSummary: printSessionSummary2 }, { findLatestSessionId: findLatestSessionId2 }] = await Promise.all([
|
|
81849
|
+
Promise.resolve().then(() => (init_session_stats(), exports_session_stats)),
|
|
81850
|
+
Promise.resolve().then(() => (init_session_summary(), exports_session_summary)),
|
|
81851
|
+
Promise.resolve().then(() => (init_session_discovery(), exports_session_discovery))
|
|
81852
|
+
]);
|
|
81853
|
+
const stats = readSessionStats2(port);
|
|
81854
|
+
if (stats) {
|
|
81855
|
+
printSessionSummary2({
|
|
81856
|
+
stats,
|
|
81857
|
+
modelSpec: explicitModel || stats.modelName || "",
|
|
81858
|
+
resumeModelSpec: explicitModel ?? null,
|
|
81859
|
+
resumeId: resumedSessionId ?? findLatestSessionId2(process.cwd(), Date.now() - stats.durationMs),
|
|
81860
|
+
exitCode
|
|
81861
|
+
}, write);
|
|
81862
|
+
}
|
|
81863
|
+
} catch (e) {
|
|
81864
|
+
console.error(`[claudish] session summary unavailable: ${e}`);
|
|
81865
|
+
}
|
|
78891
81866
|
}
|
|
78892
81867
|
const sessionLogPath = getAlwaysOnLogPath2();
|
|
78893
81868
|
if (exitCode !== 0 && sessionLogPath && !cliConfig.quiet) {
|