claudish 7.46.0 → 7.48.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 +3310 -227
- 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.48.0";
|
|
733
733
|
|
|
734
734
|
// src/logger.ts
|
|
735
735
|
var exports_logger = {};
|
|
@@ -28579,6 +28579,25 @@ var init_provider_definitions = __esm(() => {
|
|
|
28579
28579
|
isDirectApi: true,
|
|
28580
28580
|
description: "Qwen Plan (qc@)"
|
|
28581
28581
|
},
|
|
28582
|
+
{
|
|
28583
|
+
name: "qwen-payg",
|
|
28584
|
+
displayName: "Qwen PAYG",
|
|
28585
|
+
transport: "anthropic",
|
|
28586
|
+
baseUrl: "https://dashscope-intl.aliyuncs.com",
|
|
28587
|
+
baseUrlEnvVars: ["DASHSCOPE_BASE_URL"],
|
|
28588
|
+
apiPath: "/apps/anthropic/v1/messages",
|
|
28589
|
+
apiKeyEnvVar: "DASHSCOPE_API_KEY",
|
|
28590
|
+
apiKeyAliases: ["QWEN_API_KEY"],
|
|
28591
|
+
apiKeyDescription: "Alibaba Model Studio API Key (pay-as-you-go)",
|
|
28592
|
+
apiKeyUrl: "https://www.alibabacloud.com/help/en/model-studio/get-api-key",
|
|
28593
|
+
authScheme: "bearer",
|
|
28594
|
+
shortcuts: ["qp", "dashscope"],
|
|
28595
|
+
shortestPrefix: "qp",
|
|
28596
|
+
legacyPrefixes: [{ prefix: "qp/", stripPrefix: true }],
|
|
28597
|
+
modelDiscovery: { path: "/compatible-mode/v1/models", format: "openai-models-list" },
|
|
28598
|
+
isDirectApi: true,
|
|
28599
|
+
description: "Alibaba Model Studio pay-as-you-go (qp@)"
|
|
28600
|
+
},
|
|
28582
28601
|
{
|
|
28583
28602
|
name: "qwen",
|
|
28584
28603
|
displayName: "Qwen",
|
|
@@ -29217,6 +29236,11 @@ function removeUriFormat(schema) {
|
|
|
29217
29236
|
}
|
|
29218
29237
|
return result;
|
|
29219
29238
|
}
|
|
29239
|
+
function stripBillingHeader(text) {
|
|
29240
|
+
if (!text || !text.includes("x-anthropic-billing-header"))
|
|
29241
|
+
return text;
|
|
29242
|
+
return text.replace(BILLING_HEADER_RE, "");
|
|
29243
|
+
}
|
|
29220
29244
|
function transformOpenAIToClaude(claudeRequestInput) {
|
|
29221
29245
|
const req = JSON.parse(JSON.stringify(claudeRequestInput));
|
|
29222
29246
|
const isO3Model = typeof req.model === "string" && (req.model.includes("o3") || req.model.includes("o1"));
|
|
@@ -29240,9 +29264,11 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
29240
29264
|
}
|
|
29241
29265
|
}
|
|
29242
29266
|
return JSON.stringify(item);
|
|
29243
|
-
}).filter((text) => text && text.trim() !== "").join(`
|
|
29267
|
+
}).map((text) => stripBillingHeader(text)).filter((text) => text && text.trim() !== "").join(`
|
|
29244
29268
|
|
|
29245
29269
|
`);
|
|
29270
|
+
} else if (typeof req.system === "string") {
|
|
29271
|
+
req.system = stripBillingHeader(req.system);
|
|
29246
29272
|
}
|
|
29247
29273
|
if (!Array.isArray(req.messages)) {
|
|
29248
29274
|
if (req.messages == null)
|
|
@@ -29264,7 +29290,10 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
29264
29290
|
isO3Model
|
|
29265
29291
|
};
|
|
29266
29292
|
}
|
|
29267
|
-
var
|
|
29293
|
+
var BILLING_HEADER_RE;
|
|
29294
|
+
var init_transform = __esm(() => {
|
|
29295
|
+
BILLING_HEADER_RE = /x-anthropic-billing-header:[^\n]*\n?/gi;
|
|
29296
|
+
});
|
|
29268
29297
|
|
|
29269
29298
|
// src/handlers/shared/format/openai-tools.ts
|
|
29270
29299
|
function emptyParamsSchema() {
|
|
@@ -33137,6 +33166,13 @@ function createStreamingResponseHandler(c, response, adapter, target, middleware
|
|
|
33137
33166
|
async start(controller) {
|
|
33138
33167
|
const send = (e, d) => {
|
|
33139
33168
|
if (!isClosed) {
|
|
33169
|
+
if (e === "content_block_start" && d?.content_block?.type === "tool_use") {
|
|
33170
|
+
try {
|
|
33171
|
+
behavior?.onToolCallObserved?.(String(d.content_block.name ?? ""));
|
|
33172
|
+
} catch (err) {
|
|
33173
|
+
log(`[Streaming] onToolCallObserved threw: ${err}`);
|
|
33174
|
+
}
|
|
33175
|
+
}
|
|
33140
33176
|
controller.enqueue(encoder.encode(`event: ${e}
|
|
33141
33177
|
data: ${JSON.stringify(d)}
|
|
33142
33178
|
|
|
@@ -33164,54 +33200,98 @@ data: ${JSON.stringify(d)}
|
|
|
33164
33200
|
send("ping", { type: "ping" });
|
|
33165
33201
|
}
|
|
33166
33202
|
}, 1000);
|
|
33203
|
+
const teardown = () => {
|
|
33204
|
+
if (!isClosed) {
|
|
33205
|
+
try {
|
|
33206
|
+
controller.enqueue(encoder.encode(`data: [DONE]
|
|
33207
|
+
|
|
33208
|
+
|
|
33209
|
+
`));
|
|
33210
|
+
} catch {}
|
|
33211
|
+
try {
|
|
33212
|
+
controller.close();
|
|
33213
|
+
} catch {}
|
|
33214
|
+
isClosed = true;
|
|
33215
|
+
}
|
|
33216
|
+
if (ping) {
|
|
33217
|
+
clearInterval(ping);
|
|
33218
|
+
ping = null;
|
|
33219
|
+
}
|
|
33220
|
+
};
|
|
33167
33221
|
const finalize = async (reason, err) => {
|
|
33168
|
-
if (state.finalized)
|
|
33222
|
+
if (state.finalized) {
|
|
33223
|
+
teardown();
|
|
33169
33224
|
return;
|
|
33225
|
+
}
|
|
33170
33226
|
state.finalized = true;
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
|
|
33174
|
-
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33227
|
+
try {
|
|
33228
|
+
if (state.accumulatedText.length > 0) {
|
|
33229
|
+
const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
|
|
33230
|
+
log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
|
|
33231
|
+
}
|
|
33232
|
+
const textToolCalls = extractToolCallsFromText(state.accumulatedText);
|
|
33233
|
+
log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
|
|
33234
|
+
if (textToolCalls.length > 0) {
|
|
33235
|
+
log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
|
|
33236
|
+
if (state.textStarted) {
|
|
33237
|
+
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
33238
|
+
state.textStarted = false;
|
|
33239
|
+
}
|
|
33240
|
+
for (const tc of textToolCalls) {
|
|
33241
|
+
const toolIdx = state.curIdx++;
|
|
33242
|
+
const toolId = `tool_${Date.now()}_${toolIdx}`;
|
|
33243
|
+
send("content_block_start", {
|
|
33244
|
+
type: "content_block_start",
|
|
33245
|
+
index: toolIdx,
|
|
33246
|
+
content_block: { type: "tool_use", id: toolId, name: tc.name }
|
|
33247
|
+
});
|
|
33248
|
+
send("content_block_delta", {
|
|
33249
|
+
type: "content_block_delta",
|
|
33250
|
+
index: toolIdx,
|
|
33251
|
+
delta: {
|
|
33252
|
+
type: "input_json_delta",
|
|
33253
|
+
partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
|
|
33254
|
+
}
|
|
33255
|
+
});
|
|
33256
|
+
send("content_block_stop", { type: "content_block_stop", index: toolIdx });
|
|
33257
|
+
}
|
|
33258
|
+
}
|
|
33259
|
+
if (state.reasoningStarted) {
|
|
33260
|
+
send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
|
|
33261
|
+
}
|
|
33179
33262
|
if (state.textStarted) {
|
|
33180
33263
|
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
33181
|
-
state.textStarted = false;
|
|
33182
33264
|
}
|
|
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}`);
|
|
33265
|
+
for (const t of Array.from(state.tools.values())) {
|
|
33266
|
+
if (!t.closed && t.buffered && !t.started) {
|
|
33267
|
+
if (toolSchemas && toolSchemas.length > 0) {
|
|
33268
|
+
const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
|
|
33269
|
+
if (validation.valid || validation.repaired && validation.repairedArgs) {
|
|
33270
|
+
const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
|
|
33271
|
+
log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
|
|
33272
|
+
send("content_block_start", {
|
|
33273
|
+
type: "content_block_start",
|
|
33274
|
+
index: t.blockIndex,
|
|
33275
|
+
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
33276
|
+
});
|
|
33277
|
+
send("content_block_delta", {
|
|
33278
|
+
type: "content_block_delta",
|
|
33279
|
+
index: t.blockIndex,
|
|
33280
|
+
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
33281
|
+
});
|
|
33282
|
+
send("content_block_stop", {
|
|
33283
|
+
type: "content_block_stop",
|
|
33284
|
+
index: t.blockIndex
|
|
33285
|
+
});
|
|
33286
|
+
t.started = true;
|
|
33287
|
+
t.closed = true;
|
|
33288
|
+
} else {
|
|
33289
|
+
log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
|
|
33290
|
+
t.closed = true;
|
|
33291
|
+
}
|
|
33292
|
+
} else {
|
|
33293
|
+
const argsJson = repairArgs(t.name, t.arguments || "{}");
|
|
33294
|
+
log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
|
|
33215
33295
|
send("content_block_start", {
|
|
33216
33296
|
type: "content_block_start",
|
|
33217
33297
|
index: t.blockIndex,
|
|
@@ -33228,83 +33308,51 @@ data: ${JSON.stringify(d)}
|
|
|
33228
33308
|
});
|
|
33229
33309
|
t.started = true;
|
|
33230
33310
|
t.closed = true;
|
|
33231
|
-
} else {
|
|
33232
|
-
log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
|
|
33233
|
-
t.closed = true;
|
|
33234
33311
|
}
|
|
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;
|
|
33312
|
+
}
|
|
33313
|
+
}
|
|
33314
|
+
for (const t of Array.from(state.tools.values())) {
|
|
33315
|
+
if (t.started && !t.closed) {
|
|
33316
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
33253
33317
|
t.closed = true;
|
|
33254
33318
|
}
|
|
33255
33319
|
}
|
|
33256
|
-
|
|
33257
|
-
|
|
33258
|
-
if (t.started && !t.closed) {
|
|
33259
|
-
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
33260
|
-
t.closed = true;
|
|
33320
|
+
if (middlewareManager) {
|
|
33321
|
+
await middlewareManager.afterStreamComplete(target, streamMetadata);
|
|
33261
33322
|
}
|
|
33262
|
-
|
|
33263
|
-
|
|
33264
|
-
|
|
33265
|
-
|
|
33266
|
-
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
33270
|
-
|
|
33271
|
-
|
|
33272
|
-
|
|
33273
|
-
|
|
33274
|
-
|
|
33323
|
+
if (reason === "error") {
|
|
33324
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
33325
|
+
} else {
|
|
33326
|
+
const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
|
|
33327
|
+
const truncated = state.finishReason === "length";
|
|
33328
|
+
const refused = state.finishReason === "content_filter";
|
|
33329
|
+
const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
|
|
33330
|
+
if (truncated || refused) {
|
|
33331
|
+
log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
|
|
33332
|
+
}
|
|
33333
|
+
send("message_delta", {
|
|
33334
|
+
type: "message_delta",
|
|
33335
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
33336
|
+
usage: {
|
|
33337
|
+
...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
|
|
33338
|
+
output_tokens: state.usage?.completion_tokens || 0
|
|
33339
|
+
}
|
|
33340
|
+
});
|
|
33341
|
+
behavior?.onTurnEnd?.();
|
|
33342
|
+
send("message_stop", { type: "message_stop" });
|
|
33275
33343
|
}
|
|
33276
|
-
|
|
33277
|
-
|
|
33278
|
-
|
|
33279
|
-
|
|
33280
|
-
|
|
33281
|
-
|
|
33344
|
+
if (onTokenUpdate) {
|
|
33345
|
+
if (state.usage) {
|
|
33346
|
+
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
33347
|
+
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
|
|
33348
|
+
} else {
|
|
33349
|
+
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
33350
|
+
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
33351
|
+
onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
|
|
33282
33352
|
}
|
|
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
33353
|
}
|
|
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);
|
|
33354
|
+
} finally {
|
|
33355
|
+
teardown();
|
|
33308
33356
|
}
|
|
33309
33357
|
};
|
|
33310
33358
|
try {
|
|
@@ -37550,7 +37598,8 @@ var init_telemetry = __esm(() => {
|
|
|
37550
37598
|
"minimax-coding",
|
|
37551
37599
|
"kimi-coding",
|
|
37552
37600
|
"glm-coding",
|
|
37553
|
-
"qwen-cloud"
|
|
37601
|
+
"qwen-cloud",
|
|
37602
|
+
"qwen-payg"
|
|
37554
37603
|
]);
|
|
37555
37604
|
});
|
|
37556
37605
|
|
|
@@ -39184,62 +39233,70 @@ data: ${JSON.stringify(data)}
|
|
|
39184
39233
|
send("ping", { type: "ping" });
|
|
39185
39234
|
}
|
|
39186
39235
|
}, 1000);
|
|
39236
|
+
const teardown = () => {
|
|
39237
|
+
if (!isClosed) {
|
|
39238
|
+
isClosed = true;
|
|
39239
|
+
try {
|
|
39240
|
+
controller.close();
|
|
39241
|
+
} catch {}
|
|
39242
|
+
}
|
|
39243
|
+
if (pingInterval) {
|
|
39244
|
+
clearInterval(pingInterval);
|
|
39245
|
+
pingInterval = null;
|
|
39246
|
+
}
|
|
39247
|
+
};
|
|
39187
39248
|
const finalize = async (reason, err) => {
|
|
39188
|
-
if (finalized2)
|
|
39249
|
+
if (finalized2) {
|
|
39250
|
+
teardown();
|
|
39189
39251
|
return;
|
|
39190
|
-
finalized2 = true;
|
|
39191
|
-
if (thinkingStarted) {
|
|
39192
|
-
send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
|
|
39193
|
-
}
|
|
39194
|
-
if (textStarted) {
|
|
39195
|
-
send("content_block_stop", { type: "content_block_stop", index: textIdx });
|
|
39196
39252
|
}
|
|
39197
|
-
|
|
39198
|
-
|
|
39199
|
-
|
|
39200
|
-
|
|
39253
|
+
finalized2 = true;
|
|
39254
|
+
try {
|
|
39255
|
+
if (thinkingStarted) {
|
|
39256
|
+
send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
|
|
39201
39257
|
}
|
|
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");
|
|
39258
|
+
if (textStarted) {
|
|
39259
|
+
send("content_block_stop", { type: "content_block_stop", index: textIdx });
|
|
39222
39260
|
}
|
|
39223
|
-
|
|
39224
|
-
|
|
39225
|
-
|
|
39226
|
-
|
|
39227
|
-
...inputTokens > 0 ? { input_tokens: inputTokens } : {},
|
|
39228
|
-
output_tokens: outputTokens
|
|
39261
|
+
for (const t of toolCalls.values()) {
|
|
39262
|
+
if (t.started && !t.closed) {
|
|
39263
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39264
|
+
t.closed = true;
|
|
39229
39265
|
}
|
|
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
39266
|
}
|
|
39240
|
-
|
|
39241
|
-
|
|
39242
|
-
}
|
|
39267
|
+
if (opts.middlewareManager) {
|
|
39268
|
+
await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
|
|
39269
|
+
}
|
|
39270
|
+
const inputTokens = usage?.promptTokenCount || 0;
|
|
39271
|
+
const outputTokens = usage?.candidatesTokenCount || 0;
|
|
39272
|
+
if (usage) {
|
|
39273
|
+
log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
|
|
39274
|
+
}
|
|
39275
|
+
if (opts.onTokenUpdate) {
|
|
39276
|
+
opts.onTokenUpdate(inputTokens, outputTokens);
|
|
39277
|
+
}
|
|
39278
|
+
if (reason === "error") {
|
|
39279
|
+
log(`[GeminiSSE] Stream error: ${err}`);
|
|
39280
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39281
|
+
} else {
|
|
39282
|
+
const hasToolCalls = toolCalls.size > 0;
|
|
39283
|
+
const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
|
|
39284
|
+
if (truncated) {
|
|
39285
|
+
log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
|
|
39286
|
+
}
|
|
39287
|
+
send("message_delta", {
|
|
39288
|
+
type: "message_delta",
|
|
39289
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
39290
|
+
usage: {
|
|
39291
|
+
...inputTokens > 0 ? { input_tokens: inputTokens } : {},
|
|
39292
|
+
output_tokens: outputTokens
|
|
39293
|
+
}
|
|
39294
|
+
});
|
|
39295
|
+
opts.onTurnEnd?.();
|
|
39296
|
+
send("message_stop", { type: "message_stop" });
|
|
39297
|
+
}
|
|
39298
|
+
} finally {
|
|
39299
|
+
teardown();
|
|
39243
39300
|
}
|
|
39244
39301
|
};
|
|
39245
39302
|
try {
|
|
@@ -39457,38 +39514,48 @@ data: ${JSON.stringify(data)}
|
|
|
39457
39514
|
send("ping", { type: "ping" });
|
|
39458
39515
|
}
|
|
39459
39516
|
}, 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
|
-
}
|
|
39517
|
+
let finalized2 = false;
|
|
39518
|
+
const teardown = () => {
|
|
39482
39519
|
if (!isClosed) {
|
|
39483
39520
|
isClosed = true;
|
|
39484
|
-
if (pingInterval) {
|
|
39485
|
-
clearInterval(pingInterval);
|
|
39486
|
-
pingInterval = null;
|
|
39487
|
-
}
|
|
39488
39521
|
try {
|
|
39489
39522
|
controller.close();
|
|
39490
39523
|
} catch {}
|
|
39491
39524
|
}
|
|
39525
|
+
if (pingInterval) {
|
|
39526
|
+
clearInterval(pingInterval);
|
|
39527
|
+
pingInterval = null;
|
|
39528
|
+
}
|
|
39529
|
+
};
|
|
39530
|
+
const finalize = (reason, err) => {
|
|
39531
|
+
if (finalized2) {
|
|
39532
|
+
teardown();
|
|
39533
|
+
return;
|
|
39534
|
+
}
|
|
39535
|
+
finalized2 = true;
|
|
39536
|
+
try {
|
|
39537
|
+
if (textStarted) {
|
|
39538
|
+
send("content_block_stop", { type: "content_block_stop", index: 0 });
|
|
39539
|
+
}
|
|
39540
|
+
if (reason === "error") {
|
|
39541
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39542
|
+
} else {
|
|
39543
|
+
send("message_delta", {
|
|
39544
|
+
type: "message_delta",
|
|
39545
|
+
delta: { stop_reason: "end_turn", stop_sequence: null },
|
|
39546
|
+
usage: {
|
|
39547
|
+
...promptTokens > 0 ? { input_tokens: promptTokens } : {},
|
|
39548
|
+
output_tokens: completionTokens
|
|
39549
|
+
}
|
|
39550
|
+
});
|
|
39551
|
+
send("message_stop", { type: "message_stop" });
|
|
39552
|
+
}
|
|
39553
|
+
if (opts.onTokenUpdate) {
|
|
39554
|
+
opts.onTokenUpdate(promptTokens, completionTokens);
|
|
39555
|
+
}
|
|
39556
|
+
} finally {
|
|
39557
|
+
teardown();
|
|
39558
|
+
}
|
|
39492
39559
|
};
|
|
39493
39560
|
try {
|
|
39494
39561
|
const reader = response.body.getReader();
|
|
@@ -39975,10 +40042,26 @@ class TokenTracker {
|
|
|
39975
40042
|
modelNameOverride;
|
|
39976
40043
|
planUsage;
|
|
39977
40044
|
lastPlanSerialized = "";
|
|
40045
|
+
toolCallsByName = new Map;
|
|
40046
|
+
startedAt = Date.now();
|
|
40047
|
+
sessionBilledInputTokens = 0;
|
|
39978
40048
|
constructor(port, config2) {
|
|
39979
40049
|
this.port = port;
|
|
39980
40050
|
this.config = config2;
|
|
39981
40051
|
}
|
|
40052
|
+
recordToolUse(name) {
|
|
40053
|
+
const key = name.trim() || "unknown";
|
|
40054
|
+
this.toolCallsByName.set(key, (this.toolCallsByName.get(key) ?? 0) + 1);
|
|
40055
|
+
}
|
|
40056
|
+
getToolCallCount() {
|
|
40057
|
+
let n = 0;
|
|
40058
|
+
for (const v of this.toolCallsByName.values())
|
|
40059
|
+
n += v;
|
|
40060
|
+
return n;
|
|
40061
|
+
}
|
|
40062
|
+
getToolCalls() {
|
|
40063
|
+
return [...this.toolCallsByName].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
|
|
40064
|
+
}
|
|
39982
40065
|
setActiveModelName(name) {
|
|
39983
40066
|
this.modelNameOverride = name;
|
|
39984
40067
|
}
|
|
@@ -40000,6 +40083,7 @@ class TokenTracker {
|
|
|
40000
40083
|
this.sessionInputTokens = inputTokens;
|
|
40001
40084
|
this.lastInputTokens = inputTokens;
|
|
40002
40085
|
this.sessionOutputTokens += outputTokens;
|
|
40086
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
40003
40087
|
const pricing = this.getPricing();
|
|
40004
40088
|
const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
40005
40089
|
this.sessionTotalCost += cost;
|
|
@@ -40012,6 +40096,7 @@ class TokenTracker {
|
|
|
40012
40096
|
const pricing = this.getPricing();
|
|
40013
40097
|
const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M;
|
|
40014
40098
|
this.sessionTotalCost = cost;
|
|
40099
|
+
this.sessionBilledInputTokens = this.sessionInputTokens;
|
|
40015
40100
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
40016
40101
|
}
|
|
40017
40102
|
updateWithDelta(inputTokens, outputTokens) {
|
|
@@ -40030,6 +40115,7 @@ class TokenTracker {
|
|
|
40030
40115
|
}
|
|
40031
40116
|
this.sessionOutputTokens += outputTokens;
|
|
40032
40117
|
const pricing = this.getPricing();
|
|
40118
|
+
this.sessionBilledInputTokens += incrementalInputTokens;
|
|
40033
40119
|
const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
40034
40120
|
this.sessionTotalCost += cost;
|
|
40035
40121
|
this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
@@ -40038,6 +40124,7 @@ class TokenTracker {
|
|
|
40038
40124
|
this.sessionInputTokens = inputTokens;
|
|
40039
40125
|
this.lastInputTokens = inputTokens;
|
|
40040
40126
|
this.sessionOutputTokens += outputTokens;
|
|
40127
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
40041
40128
|
if (typeof actualCost === "number" && actualCost > 0) {
|
|
40042
40129
|
this.sessionTotalCost += actualCost;
|
|
40043
40130
|
log(`[TokenTracker] Actual cost from API: $${actualCost.toFixed(6)}`);
|
|
@@ -40055,6 +40142,7 @@ class TokenTracker {
|
|
|
40055
40142
|
this.lastInputTokens = inputTokens;
|
|
40056
40143
|
}
|
|
40057
40144
|
this.sessionOutputTokens += outputTokens;
|
|
40145
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
40058
40146
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens);
|
|
40059
40147
|
}
|
|
40060
40148
|
setContextWindow(contextWindow) {
|
|
@@ -40107,7 +40195,12 @@ class TokenTracker {
|
|
|
40107
40195
|
provider_name: this.getDisplayName(),
|
|
40108
40196
|
updated_at: Date.now(),
|
|
40109
40197
|
is_free: isFreeModel,
|
|
40110
|
-
is_estimated: isEstimate || false
|
|
40198
|
+
is_estimated: isEstimate || false,
|
|
40199
|
+
started_at: this.startedAt,
|
|
40200
|
+
tool_calls: this.getToolCalls(),
|
|
40201
|
+
billed_input_tokens: this.sessionBilledInputTokens,
|
|
40202
|
+
input_per_m: pricing.inputCostPer1M,
|
|
40203
|
+
output_per_m: pricing.outputCostPer1M
|
|
40111
40204
|
};
|
|
40112
40205
|
const displayModel = stripProviderPrefix(this.modelNameOverride || this.config.modelName || "");
|
|
40113
40206
|
if (displayModel) {
|
|
@@ -40813,14 +40906,18 @@ class ComposedHandler {
|
|
|
40813
40906
|
};
|
|
40814
40907
|
const streamFormat = this.resolveStreamFormat();
|
|
40815
40908
|
const priorInputTokens = this.tokenTracker.getLastInputTokens();
|
|
40909
|
+
const observeToolCall = (name) => {
|
|
40910
|
+
this.tokenTracker.recordToolUse(name);
|
|
40911
|
+
behaviorSession?.observeToolCall(name);
|
|
40912
|
+
};
|
|
40816
40913
|
switch (streamFormat) {
|
|
40817
40914
|
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
|
|
40915
|
+
return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, {
|
|
40916
|
+
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40917
|
+
onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40918
|
+
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40919
|
+
onToolCallObserved: observeToolCall,
|
|
40920
|
+
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40824
40921
|
});
|
|
40825
40922
|
case "openai-responses-sse":
|
|
40826
40923
|
return createResponsesStreamHandler(c, response, {
|
|
@@ -40834,7 +40931,7 @@ class ComposedHandler {
|
|
|
40834
40931
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40835
40932
|
onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40836
40933
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40837
|
-
onToolCallObserved:
|
|
40934
|
+
onToolCallObserved: observeToolCall,
|
|
40838
40935
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40839
40936
|
});
|
|
40840
40937
|
case "anthropic-sse":
|
|
@@ -40845,7 +40942,7 @@ class ComposedHandler {
|
|
|
40845
40942
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40846
40943
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40847
40944
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40848
|
-
onToolCallObserved:
|
|
40945
|
+
onToolCallObserved: observeToolCall,
|
|
40849
40946
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40850
40947
|
});
|
|
40851
40948
|
case "gemini-sse": {
|
|
@@ -40862,7 +40959,7 @@ class ComposedHandler {
|
|
|
40862
40959
|
onToolCall,
|
|
40863
40960
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40864
40961
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40865
|
-
onToolCallObserved:
|
|
40962
|
+
onToolCallObserved: observeToolCall,
|
|
40866
40963
|
onTurnEnd: () => behaviorSession?.finishTurn(),
|
|
40867
40964
|
unwrapResponse: this.options.unwrapGeminiResponse,
|
|
40868
40965
|
priorInputTokens
|
|
@@ -40882,7 +40979,7 @@ class ComposedHandler {
|
|
|
40882
40979
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40883
40980
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40884
40981
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40885
|
-
onToolCallObserved:
|
|
40982
|
+
onToolCallObserved: observeToolCall,
|
|
40886
40983
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40887
40984
|
});
|
|
40888
40985
|
case "ollama-jsonl":
|
|
@@ -41522,6 +41619,40 @@ function toRosterEntry(model) {
|
|
|
41522
41619
|
const { id, ...rest } = model;
|
|
41523
41620
|
return { wireId: id, ...rest };
|
|
41524
41621
|
}
|
|
41622
|
+
function recordFailure(failure) {
|
|
41623
|
+
_failures.set(failure.provider, failure);
|
|
41624
|
+
log(`[model-discovery:${failure.provider}] ${describeDiscoveryFailure(failure)}`);
|
|
41625
|
+
return [];
|
|
41626
|
+
}
|
|
41627
|
+
function getDiscoveryFailure(provider) {
|
|
41628
|
+
return _failures.get(provider);
|
|
41629
|
+
}
|
|
41630
|
+
function hasAuthHeader(headers) {
|
|
41631
|
+
return Object.entries(headers).some(([name, value]) => /^(authorization|x-api-key|api-key)$/i.test(name) && (value ?? "").trim().length > 0);
|
|
41632
|
+
}
|
|
41633
|
+
function oneLine(text, max = 200) {
|
|
41634
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
41635
|
+
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
41636
|
+
}
|
|
41637
|
+
function describeDiscoveryFailure(failure) {
|
|
41638
|
+
const { kind, endpoint, status, detail } = failure;
|
|
41639
|
+
const at = endpoint ? ` at ${endpoint}` : "";
|
|
41640
|
+
const because = detail ? ` \u2014 ${detail}` : "";
|
|
41641
|
+
switch (kind) {
|
|
41642
|
+
case "no-credentials":
|
|
41643
|
+
return `no usable credentials${because}`;
|
|
41644
|
+
case "unauthorized":
|
|
41645
|
+
return `the API key was rejected (HTTP ${status})${at}${because}`;
|
|
41646
|
+
case "http-error":
|
|
41647
|
+
return `the model list returned HTTP ${status}${at}${because}`;
|
|
41648
|
+
case "unreachable":
|
|
41649
|
+
return `the model list was unreachable${at}${because}`;
|
|
41650
|
+
case "malformed":
|
|
41651
|
+
return `the model list was not valid JSON${at}`;
|
|
41652
|
+
case "empty-roster":
|
|
41653
|
+
return `the endpoint answered${at} but listed no models`;
|
|
41654
|
+
}
|
|
41655
|
+
}
|
|
41525
41656
|
function resolveBaseUrl(catalogName) {
|
|
41526
41657
|
const def = getProviderByName(catalogName);
|
|
41527
41658
|
if (!def)
|
|
@@ -41585,14 +41716,14 @@ async function discoverProviderModels(providerName) {
|
|
|
41585
41716
|
const { getServedDevinModels: getServedDevinModels2 } = await Promise.resolve().then(() => (init_devin_models(), exports_devin_models));
|
|
41586
41717
|
const served = await getServedDevinModels2();
|
|
41587
41718
|
if (served.length === 0) {
|
|
41588
|
-
|
|
41589
|
-
return [];
|
|
41719
|
+
return recordFailure({ kind: "empty-roster", provider: providerName });
|
|
41590
41720
|
}
|
|
41591
41721
|
const { devinRosterEntry: devinRosterEntry2 } = await Promise.resolve().then(() => (init_devin(), exports_devin));
|
|
41592
41722
|
const models2 = served.map((model) => {
|
|
41593
41723
|
const { wireId, ...rest } = devinRosterEntry2(model);
|
|
41594
41724
|
return { id: wireId, ...rest };
|
|
41595
41725
|
});
|
|
41726
|
+
_failures.delete(providerName);
|
|
41596
41727
|
log(`[model-discovery:${providerName}] discovered ${models2.length} models`);
|
|
41597
41728
|
_cache.set(providerName, { models: models2, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
41598
41729
|
return models2;
|
|
@@ -41600,13 +41731,15 @@ async function discoverProviderModels(providerName) {
|
|
|
41600
41731
|
if (descriptor.format === "ollama-tags") {
|
|
41601
41732
|
const { fetchOllamaModels: fetchOllamaModels2 } = await Promise.resolve().then(() => exports_ollama_discovery);
|
|
41602
41733
|
const installed = await fetchOllamaModels2({ enrichCapabilities: false });
|
|
41603
|
-
if (installed.length === 0)
|
|
41604
|
-
return
|
|
41734
|
+
if (installed.length === 0) {
|
|
41735
|
+
return recordFailure({ kind: "empty-roster", provider: providerName });
|
|
41736
|
+
}
|
|
41605
41737
|
const models2 = installed.map((model) => ({
|
|
41606
41738
|
id: model.name,
|
|
41607
41739
|
displayName: model.name,
|
|
41608
41740
|
supportsTools: model.supportsTools
|
|
41609
41741
|
}));
|
|
41742
|
+
_failures.delete(providerName);
|
|
41610
41743
|
log(`[model-discovery:${providerName}] discovered ${models2.length} local models`);
|
|
41611
41744
|
_cache.set(providerName, { models: models2, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
41612
41745
|
return models2;
|
|
@@ -41625,8 +41758,15 @@ async function discoverProviderModels(providerName) {
|
|
|
41625
41758
|
const auth = await credentials.getRequestAuth(providerName, { model: "" });
|
|
41626
41759
|
headers = { ...auth.headers };
|
|
41627
41760
|
} catch (e) {
|
|
41628
|
-
|
|
41629
|
-
|
|
41761
|
+
return recordFailure({
|
|
41762
|
+
kind: "no-credentials",
|
|
41763
|
+
provider: providerName,
|
|
41764
|
+
endpoint,
|
|
41765
|
+
detail: oneLine(e?.message ?? "")
|
|
41766
|
+
});
|
|
41767
|
+
}
|
|
41768
|
+
if (!hasAuthHeader(headers)) {
|
|
41769
|
+
return recordFailure({ kind: "no-credentials", provider: providerName, endpoint });
|
|
41630
41770
|
}
|
|
41631
41771
|
}
|
|
41632
41772
|
let response;
|
|
@@ -41637,25 +41777,38 @@ async function discoverProviderModels(providerName) {
|
|
|
41637
41777
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
41638
41778
|
});
|
|
41639
41779
|
} catch (e) {
|
|
41640
|
-
|
|
41641
|
-
|
|
41780
|
+
return recordFailure({
|
|
41781
|
+
kind: "unreachable",
|
|
41782
|
+
provider: providerName,
|
|
41783
|
+
endpoint,
|
|
41784
|
+
detail: oneLine(e?.message ?? "")
|
|
41785
|
+
});
|
|
41642
41786
|
}
|
|
41643
41787
|
if (!response.ok) {
|
|
41644
|
-
|
|
41645
|
-
|
|
41788
|
+
const unauthorized = response.status === 401 || response.status === 403;
|
|
41789
|
+
let detail = "";
|
|
41790
|
+
try {
|
|
41791
|
+
detail = oneLine(await response.text());
|
|
41792
|
+
} catch {}
|
|
41793
|
+
return recordFailure({
|
|
41794
|
+
kind: unauthorized ? "unauthorized" : "http-error",
|
|
41795
|
+
provider: providerName,
|
|
41796
|
+
endpoint,
|
|
41797
|
+
status: response.status,
|
|
41798
|
+
detail
|
|
41799
|
+
});
|
|
41646
41800
|
}
|
|
41647
41801
|
let body;
|
|
41648
41802
|
try {
|
|
41649
41803
|
body = await response.json();
|
|
41650
41804
|
} catch {
|
|
41651
|
-
|
|
41652
|
-
return [];
|
|
41805
|
+
return recordFailure({ kind: "malformed", provider: providerName, endpoint });
|
|
41653
41806
|
}
|
|
41654
41807
|
const models = parseOpenAIModelsList(body);
|
|
41655
41808
|
if (models.length === 0) {
|
|
41656
|
-
|
|
41657
|
-
return [];
|
|
41809
|
+
return recordFailure({ kind: "empty-roster", provider: providerName, endpoint });
|
|
41658
41810
|
}
|
|
41811
|
+
_failures.delete(providerName);
|
|
41659
41812
|
log(`[model-discovery:${providerName}] discovered ${models.length} models: ` + models.map((m) => `${m.id}(${m.contextWindow ?? "?"})`).join(", "));
|
|
41660
41813
|
_cache.set(providerName, { models, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
41661
41814
|
return models;
|
|
@@ -41671,13 +41824,14 @@ function rankDiscoveredModels(models) {
|
|
|
41671
41824
|
return diff !== 0 ? diff : a.id.localeCompare(b.id);
|
|
41672
41825
|
});
|
|
41673
41826
|
}
|
|
41674
|
-
var CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, _cache, MIN_CREATED_SECONDS = 946684800, MAX_CREATED_SECONDS = 4102444800;
|
|
41827
|
+
var CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, _cache, _failures, MIN_CREATED_SECONDS = 946684800, MAX_CREATED_SECONDS = 4102444800;
|
|
41675
41828
|
var init_model_discovery = __esm(() => {
|
|
41676
41829
|
init_authority();
|
|
41677
41830
|
init_logger();
|
|
41678
41831
|
init_provider_definitions();
|
|
41679
41832
|
CACHE_TTL_MS = 5 * 60 * 1000;
|
|
41680
41833
|
_cache = new Map;
|
|
41834
|
+
_failures = new Map;
|
|
41681
41835
|
});
|
|
41682
41836
|
|
|
41683
41837
|
// src/providers/transport/probe-discovery.ts
|
|
@@ -42073,9 +42227,10 @@ class AnthropicProviderTransport {
|
|
|
42073
42227
|
}
|
|
42074
42228
|
const discovered = await discoverProviderModels(this.provider.name);
|
|
42075
42229
|
if (discovered.length === 0) {
|
|
42230
|
+
const failure = getDiscoveryFailure(this.provider.name);
|
|
42076
42231
|
return {
|
|
42077
42232
|
model: null,
|
|
42078
|
-
reason: `${this.displayName} listed no models at ${def.modelDiscovery.path} \u2014 check the API key and that the subscription is active`
|
|
42233
|
+
reason: failure ? `${this.displayName}: ${describeDiscoveryFailure(failure)}` : `${this.displayName} listed no models at ${def.modelDiscovery.path} \u2014 check the API key and that the subscription is active`
|
|
42079
42234
|
};
|
|
42080
42235
|
}
|
|
42081
42236
|
const ranked = rankDiscoveredModels(discovered).map((m) => m.id).filter(isChatCapable);
|
|
@@ -42128,6 +42283,7 @@ class AnthropicProviderTransport {
|
|
|
42128
42283
|
kimi: "Kimi",
|
|
42129
42284
|
"kimi-coding": "Kimi Coding",
|
|
42130
42285
|
"qwen-cloud": "Qwen Plan",
|
|
42286
|
+
"qwen-payg": "Qwen PAYG",
|
|
42131
42287
|
moonshot: "Kimi",
|
|
42132
42288
|
"z-ai": "Z.AI"
|
|
42133
42289
|
};
|
|
@@ -42674,6 +42830,7 @@ var init_routing_hints = __esm(() => {
|
|
|
42674
42830
|
sakana: { apiKeyEnvVar: "SAKANA_API_KEY" },
|
|
42675
42831
|
"sakana-subscription": { apiKeyEnvVar: "SAKANA_SUBSCRIPTION_API_KEY" },
|
|
42676
42832
|
"qwen-cloud": { apiKeyEnvVar: "QWEN_CLOUD_PLAN_API_KEY" },
|
|
42833
|
+
"qwen-payg": { apiKeyEnvVar: "DASHSCOPE_API_KEY" },
|
|
42677
42834
|
ollamacloud: { apiKeyEnvVar: "OLLAMA_API_KEY" },
|
|
42678
42835
|
"native-anthropic": { apiKeyEnvVar: "ANTHROPIC_API_KEY" },
|
|
42679
42836
|
openrouter: { apiKeyEnvVar: "OPENROUTER_API_KEY" },
|
|
@@ -42722,7 +42879,7 @@ var init_default_routing_rules = __esm(() => {
|
|
|
42722
42879
|
"k3*": ["kimi-coding", "opencode-zen-go", "kimi", "openrouter"],
|
|
42723
42880
|
"minimax-*": ["minimax-coding", "opencode-zen-go", "minimax", "openrouter"],
|
|
42724
42881
|
"glm-*": ["glm-coding", "opencode-zen-go", "glm", "openrouter"],
|
|
42725
|
-
"qwen3.*": ["qwen-cloud", "opencode-zen-go", "openrouter"],
|
|
42882
|
+
"qwen3.*": ["qwen-cloud", "opencode-zen-go", "qwen-payg", "openrouter"],
|
|
42726
42883
|
"z-ai-*": ["z-ai", "openrouter"],
|
|
42727
42884
|
"deepseek-*": ["opencode-zen-go", "deepseek", "openrouter"],
|
|
42728
42885
|
"mimo-*": ["opencode-zen-go", "openrouter"],
|
|
@@ -42891,6 +43048,9 @@ async function routeBare(model, nativeProvider, rules, defaultProvider, cachePat
|
|
|
42891
43048
|
const [primary, ...fallbacks] = credentialed;
|
|
42892
43049
|
return { kind: "ok", primary, fallbacks };
|
|
42893
43050
|
}
|
|
43051
|
+
function normalizeGlmSlug(model) {
|
|
43052
|
+
return model.replace(/^glm-(\d+)-(\d+)(-.*)?$/i, (_m, major, minor, suffix) => `glm-${major}.${minor}${suffix ?? ""}`);
|
|
43053
|
+
}
|
|
42894
43054
|
async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
|
|
42895
43055
|
const parsed = parseModelSpec(modelSpec);
|
|
42896
43056
|
if (parsed.isExplicitProvider) {
|
|
@@ -42898,7 +43058,7 @@ async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePat
|
|
|
42898
43058
|
}
|
|
42899
43059
|
const rules = rulesOverride ?? loadRoutingRules();
|
|
42900
43060
|
const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
|
|
42901
|
-
return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
|
|
43061
|
+
return routeBare(normalizeGlmSlug(parsed.model), parsed.provider, rules, defaultProvider, cachePath);
|
|
42902
43062
|
}
|
|
42903
43063
|
var init_routing_rules = __esm(() => {
|
|
42904
43064
|
init_model_catalog();
|
|
@@ -47004,6 +47164,7 @@ var init_api_key_map = __esm(() => {
|
|
|
47004
47164
|
aliases: ["SAKANA_CODING_API_KEY"]
|
|
47005
47165
|
},
|
|
47006
47166
|
"qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
|
|
47167
|
+
"qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
|
|
47007
47168
|
ollamacloud: { envVar: "OLLAMA_API_KEY" },
|
|
47008
47169
|
"opencode-zen": { envVar: "OPENCODE_API_KEY" },
|
|
47009
47170
|
"opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
|
|
@@ -48052,6 +48213,7 @@ var init_provider_profiles = __esm(() => {
|
|
|
48052
48213
|
kimi: anthropicCompatProfile,
|
|
48053
48214
|
"kimi-coding": anthropicCompatProfile,
|
|
48054
48215
|
"qwen-cloud": anthropicCompatProfile,
|
|
48216
|
+
"qwen-payg": anthropicCompatProfile,
|
|
48055
48217
|
"z-ai": anthropicCompatProfile,
|
|
48056
48218
|
glm: glmProfile,
|
|
48057
48219
|
"glm-coding": glmProfile,
|
|
@@ -64604,6 +64766,7 @@ var init_model_catalog2 = __esm(() => {
|
|
|
64604
64766
|
// src/model-selector.ts
|
|
64605
64767
|
var exports_model_selector = {};
|
|
64606
64768
|
__export(exports_model_selector, {
|
|
64769
|
+
warnDiscoveryFailure: () => warnDiscoveryFailure,
|
|
64607
64770
|
selectProfile: () => selectProfile,
|
|
64608
64771
|
selectModelsForProfile: () => selectModelsForProfile,
|
|
64609
64772
|
selectModel: () => selectModel,
|
|
@@ -65155,6 +65318,26 @@ function describeOffer(offer) {
|
|
|
65155
65318
|
});
|
|
65156
65319
|
return `FREE until ${until}`;
|
|
65157
65320
|
}
|
|
65321
|
+
function warnDiscoveryFailure(provider, displayName, def) {
|
|
65322
|
+
const failure = getDiscoveryFailure(provider);
|
|
65323
|
+
if (!failure || failure.kind === "empty-roster")
|
|
65324
|
+
return;
|
|
65325
|
+
process.stderr.write(`
|
|
65326
|
+
\u26A0 ${displayName} could not list its models: ${describeDiscoveryFailure(failure)}
|
|
65327
|
+
`);
|
|
65328
|
+
if (failure.kind === "unauthorized" || failure.kind === "no-credentials") {
|
|
65329
|
+
if (def.apiKeyEnvVar) {
|
|
65330
|
+
process.stderr.write(` Check ${def.apiKeyEnvVar} (a value in your shell overrides stored credentials).
|
|
65331
|
+
`);
|
|
65332
|
+
}
|
|
65333
|
+
if (def.apiKeyUrl)
|
|
65334
|
+
process.stderr.write(` Get a key: ${def.apiKeyUrl}
|
|
65335
|
+
`);
|
|
65336
|
+
}
|
|
65337
|
+
process.stderr.write(` Falling back to manual model entry.
|
|
65338
|
+
|
|
65339
|
+
`);
|
|
65340
|
+
}
|
|
65158
65341
|
async function buildDiscoveredModelRows(provider, displayName, catalog) {
|
|
65159
65342
|
const discovered = rankDiscoveredModels(await discoverProviderModels(provider)).filter((m) => isChatCapable(m.id));
|
|
65160
65343
|
if (discovered.length === 0)
|
|
@@ -65205,6 +65388,8 @@ async function selectModelFromProvider(provider, tierName, recommendedModels, _f
|
|
|
65205
65388
|
const picked = await pickModelFromList(provider, displayName, tierName, discoveredModels);
|
|
65206
65389
|
if (picked)
|
|
65207
65390
|
return picked;
|
|
65391
|
+
} else {
|
|
65392
|
+
warnDiscoveryFailure(provider, displayName, def);
|
|
65208
65393
|
}
|
|
65209
65394
|
}
|
|
65210
65395
|
if (isUserDeployedProvider(provider)) {
|
|
@@ -65419,6 +65604,7 @@ var init_model_selector = __esm(() => {
|
|
|
65419
65604
|
kimi: { name: "Kimi / Moonshot", description: "Direct API" },
|
|
65420
65605
|
"kimi-coding": { name: "Kimi Coding", description: "Coding subscription" },
|
|
65421
65606
|
"qwen-cloud": { name: "Qwen Plan", description: "Alibaba Model Studio subscription" },
|
|
65607
|
+
"qwen-payg": { name: "Qwen PAYG", description: "Alibaba Model Studio pay-as-you-go" },
|
|
65422
65608
|
glm: { name: "GLM / Zhipu", description: "Direct API" },
|
|
65423
65609
|
"glm-coding": { name: "GLM Coding Plan", description: "Coding subscription" },
|
|
65424
65610
|
"z-ai": { name: "Z.AI", description: "Direct API" },
|
|
@@ -65448,6 +65634,7 @@ var init_model_selector = __esm(() => {
|
|
|
65448
65634
|
"kimi",
|
|
65449
65635
|
"kimi-coding",
|
|
65450
65636
|
"qwen-cloud",
|
|
65637
|
+
"qwen-payg",
|
|
65451
65638
|
"glm",
|
|
65452
65639
|
"glm-coding",
|
|
65453
65640
|
"z-ai",
|
|
@@ -68509,6 +68696,8 @@ Usage: claudish --models --provider <slug>`);
|
|
|
68509
68696
|
if (rest.length > 0)
|
|
68510
68697
|
config3._hasPositionalPrompt = true;
|
|
68511
68698
|
break;
|
|
68699
|
+
} else if (arg === "--resume" && (i + 1 >= args.length || args[i + 1].startsWith("-"))) {
|
|
68700
|
+
config3._resumePicker = true;
|
|
68512
68701
|
} else if (arg.startsWith("-")) {
|
|
68513
68702
|
config3.claudeArgs.push(arg);
|
|
68514
68703
|
if (arg === "-p" || arg === "--print") {
|
|
@@ -69024,6 +69213,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
|
|
|
69024
69213
|
"kimi",
|
|
69025
69214
|
"kimi-coding",
|
|
69026
69215
|
"qwen-cloud",
|
|
69216
|
+
"qwen-payg",
|
|
69027
69217
|
"z-ai"
|
|
69028
69218
|
];
|
|
69029
69219
|
const isMinimaxModel = modelName.toLowerCase().includes("minimax");
|
|
@@ -78411,12 +78601,2864 @@ var init_team_grid = __esm(() => {
|
|
|
78411
78601
|
];
|
|
78412
78602
|
});
|
|
78413
78603
|
|
|
78604
|
+
// src/tui/viz/text.ts
|
|
78605
|
+
function columns(n, fn, arg = "width") {
|
|
78606
|
+
if (!Number.isFinite(n))
|
|
78607
|
+
throw new RangeError(`${fn}: ${arg} must be a finite number, got ${n}`);
|
|
78608
|
+
return Math.max(0, Math.floor(n));
|
|
78609
|
+
}
|
|
78610
|
+
function isWide2(cp) {
|
|
78611
|
+
let lo = 0;
|
|
78612
|
+
let hi = WIDE.length - 1;
|
|
78613
|
+
while (lo <= hi) {
|
|
78614
|
+
const mid = lo + hi >> 1;
|
|
78615
|
+
const [a, b] = WIDE[mid];
|
|
78616
|
+
if (cp < a)
|
|
78617
|
+
hi = mid - 1;
|
|
78618
|
+
else if (cp > b)
|
|
78619
|
+
lo = mid + 1;
|
|
78620
|
+
else
|
|
78621
|
+
return true;
|
|
78622
|
+
}
|
|
78623
|
+
return false;
|
|
78624
|
+
}
|
|
78625
|
+
function fallbackClusterWidth(cluster) {
|
|
78626
|
+
const cp = cluster.codePointAt(0) ?? 0;
|
|
78627
|
+
if (cp < 32 || cp >= 127 && cp <= 159 || LEADING_FORMAT.test(cluster))
|
|
78628
|
+
return 0;
|
|
78629
|
+
return isWide2(cp) || LEADING_EMOJI_PRESENTATION.test(cluster) || cluster.includes(VS16) ? 2 : 1;
|
|
78630
|
+
}
|
|
78631
|
+
function displayWidth(s) {
|
|
78632
|
+
if (NATIVE_WIDTH)
|
|
78633
|
+
return NATIVE_WIDTH(s);
|
|
78634
|
+
let w = 0;
|
|
78635
|
+
for (const { segment } of seg.segment(s))
|
|
78636
|
+
w += fallbackClusterWidth(segment);
|
|
78637
|
+
return w;
|
|
78638
|
+
}
|
|
78639
|
+
function truncate3(s, width) {
|
|
78640
|
+
const n = columns(width, "truncate");
|
|
78641
|
+
if (n === 0)
|
|
78642
|
+
return "";
|
|
78643
|
+
const clean = sanitize2(s);
|
|
78644
|
+
if (displayWidth(clean) <= n)
|
|
78645
|
+
return clean;
|
|
78646
|
+
let out = "";
|
|
78647
|
+
let w = 0;
|
|
78648
|
+
for (const { segment } of seg.segment(clean)) {
|
|
78649
|
+
const cw = displayWidth(segment);
|
|
78650
|
+
if (w + cw > n - 1)
|
|
78651
|
+
break;
|
|
78652
|
+
out += segment;
|
|
78653
|
+
w += cw;
|
|
78654
|
+
}
|
|
78655
|
+
return `${out}\u2026`;
|
|
78656
|
+
}
|
|
78657
|
+
function padTo(s, width) {
|
|
78658
|
+
const n = columns(width, "padTo");
|
|
78659
|
+
if (n === 0)
|
|
78660
|
+
return "";
|
|
78661
|
+
const clean = sanitize2(s);
|
|
78662
|
+
const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
|
|
78663
|
+
return clipped + " ".repeat(Math.max(0, n - displayWidth(clipped)));
|
|
78664
|
+
}
|
|
78665
|
+
function padStartTo(s, width) {
|
|
78666
|
+
const n = columns(width, "padStartTo");
|
|
78667
|
+
if (n === 0)
|
|
78668
|
+
return "";
|
|
78669
|
+
const clean = sanitize2(s);
|
|
78670
|
+
const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
|
|
78671
|
+
return " ".repeat(Math.max(0, n - displayWidth(clipped))) + clipped;
|
|
78672
|
+
}
|
|
78673
|
+
function splitCells(parts, cells) {
|
|
78674
|
+
const n = columns(cells, "splitCells", "cells");
|
|
78675
|
+
const finite = parts.map((p) => Number.isFinite(p) && p > 0 ? p : 0);
|
|
78676
|
+
const out = finite.map(() => 0);
|
|
78677
|
+
let safe = finite;
|
|
78678
|
+
let sum = finite.reduce((a, b) => a + b, 0);
|
|
78679
|
+
if (!Number.isFinite(sum)) {
|
|
78680
|
+
const max = finite.reduce((a, b) => b > a ? b : a, 0);
|
|
78681
|
+
safe = finite.map((p) => p / max);
|
|
78682
|
+
sum = safe.reduce((a, b) => a + b, 0);
|
|
78683
|
+
}
|
|
78684
|
+
if (n <= 0 || sum <= 0)
|
|
78685
|
+
return out;
|
|
78686
|
+
const byShare = safe.map((p, i) => ({ p, i })).sort((a, b) => b.p - a.p);
|
|
78687
|
+
if (n < byShare.filter(({ p }) => p > 0).length) {
|
|
78688
|
+
for (const { i } of byShare.slice(0, n))
|
|
78689
|
+
out[i] = 1;
|
|
78690
|
+
return out;
|
|
78691
|
+
}
|
|
78692
|
+
const exact = safe.map((p) => n * (p / sum));
|
|
78693
|
+
exact.forEach((e, i) => {
|
|
78694
|
+
out[i] = Math.floor(e);
|
|
78695
|
+
});
|
|
78696
|
+
const rank = exact.map((e, i) => ({ rem: e % 1, i })).sort((a, b) => b.rem - a.rem);
|
|
78697
|
+
for (let k = 0, left = n - out.reduce((a, b) => a + b, 0);left > 0; k++, left--)
|
|
78698
|
+
out[rank[k % rank.length].i] += 1;
|
|
78699
|
+
for (const { p, i } of byShare)
|
|
78700
|
+
if (p > 0 && out[i] === 0) {
|
|
78701
|
+
const d = byShare.reduce((m, c) => out[c.i] > out[m] ? c.i : m, 0);
|
|
78702
|
+
if (out[d] > 1) {
|
|
78703
|
+
out[d] -= 1;
|
|
78704
|
+
out[i] = 1;
|
|
78705
|
+
}
|
|
78706
|
+
}
|
|
78707
|
+
return out;
|
|
78708
|
+
}
|
|
78709
|
+
var NATIVE_WIDTH, WIDE, seg, VS16 = "\uFE0F", LEADING_FORMAT, LEADING_EMOJI_PRESENTATION, CONTROL, sanitize2 = (s) => s.replace(CONTROL, " ");
|
|
78710
|
+
var init_text = __esm(() => {
|
|
78711
|
+
NATIVE_WIDTH = (() => {
|
|
78712
|
+
const b = globalThis.Bun;
|
|
78713
|
+
return typeof b?.stringWidth === "function" ? b.stringWidth.bind(b) : null;
|
|
78714
|
+
})();
|
|
78715
|
+
WIDE = [
|
|
78716
|
+
[4352, 4447],
|
|
78717
|
+
[9001, 9002],
|
|
78718
|
+
[11904, 12350],
|
|
78719
|
+
[12353, 12771],
|
|
78720
|
+
[12774, 12871],
|
|
78721
|
+
[12880, 13311],
|
|
78722
|
+
[13312, 19903],
|
|
78723
|
+
[19968, 40959],
|
|
78724
|
+
[40960, 42191],
|
|
78725
|
+
[43360, 43391],
|
|
78726
|
+
[44032, 55203],
|
|
78727
|
+
[63744, 64255],
|
|
78728
|
+
[65040, 65049],
|
|
78729
|
+
[65072, 65135],
|
|
78730
|
+
[65280, 65376],
|
|
78731
|
+
[65504, 65510],
|
|
78732
|
+
[94176, 94180],
|
|
78733
|
+
[94192, 94193],
|
|
78734
|
+
[94208, 100343],
|
|
78735
|
+
[100352, 101589],
|
|
78736
|
+
[101632, 101640],
|
|
78737
|
+
[110576, 110579],
|
|
78738
|
+
[110581, 110587],
|
|
78739
|
+
[110589, 110590],
|
|
78740
|
+
[110592, 110882],
|
|
78741
|
+
[110898, 110898],
|
|
78742
|
+
[110928, 110930],
|
|
78743
|
+
[110933, 110933],
|
|
78744
|
+
[110948, 110951],
|
|
78745
|
+
[110960, 111355],
|
|
78746
|
+
[127488, 127490],
|
|
78747
|
+
[127504, 127547],
|
|
78748
|
+
[127552, 127560],
|
|
78749
|
+
[127568, 127569],
|
|
78750
|
+
[127584, 127589],
|
|
78751
|
+
[131072, 196605],
|
|
78752
|
+
[196608, 262141]
|
|
78753
|
+
];
|
|
78754
|
+
seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
78755
|
+
LEADING_FORMAT = /^\p{Cf}/u;
|
|
78756
|
+
LEADING_EMOJI_PRESENTATION = /^\p{Emoji_Presentation}/u;
|
|
78757
|
+
CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
78758
|
+
});
|
|
78759
|
+
|
|
78760
|
+
// src/tui/viz/tokens.ts
|
|
78761
|
+
var tokens, ramps;
|
|
78762
|
+
var init_tokens = __esm(() => {
|
|
78763
|
+
init_theme2();
|
|
78764
|
+
tokens = {
|
|
78765
|
+
fatal: C.red,
|
|
78766
|
+
error: C.red,
|
|
78767
|
+
warn: C.orange,
|
|
78768
|
+
info: C.cyan,
|
|
78769
|
+
debug: C.fgMuted,
|
|
78770
|
+
trace: C.dim,
|
|
78771
|
+
success: C.green,
|
|
78772
|
+
running: C.blue,
|
|
78773
|
+
idle: C.fgMuted,
|
|
78774
|
+
dead: C.dim,
|
|
78775
|
+
border: C.border,
|
|
78776
|
+
subtle: C.dim,
|
|
78777
|
+
text: C.fg,
|
|
78778
|
+
accent: C.focusBorder,
|
|
78779
|
+
bgPanel: C.bgAlt,
|
|
78780
|
+
ink: C.black
|
|
78781
|
+
};
|
|
78782
|
+
ramps = {
|
|
78783
|
+
load: [tokens.success, C.yellow, tokens.error],
|
|
78784
|
+
temperature: [tokens.running, tokens.success, C.orange, tokens.error],
|
|
78785
|
+
network: [tokens.success, C.yellow, tokens.error],
|
|
78786
|
+
savings: [tokens.error, C.yellow, tokens.success],
|
|
78787
|
+
volume: [C.border, C.blue, C.cyan]
|
|
78788
|
+
};
|
|
78789
|
+
});
|
|
78790
|
+
|
|
78791
|
+
// src/tui/viz/color.ts
|
|
78792
|
+
import { RGBA, parseColor, rgbToHex } from "@opentui/core";
|
|
78793
|
+
function mix(a, b, t) {
|
|
78794
|
+
const k = clamp01(t);
|
|
78795
|
+
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));
|
|
78796
|
+
}
|
|
78797
|
+
function blend1D(steps, from, to) {
|
|
78798
|
+
if (!Number.isInteger(steps) || steps <= 0)
|
|
78799
|
+
return [];
|
|
78800
|
+
const a = rgba(from);
|
|
78801
|
+
if (steps === 1)
|
|
78802
|
+
return [hex3(a)];
|
|
78803
|
+
const b = rgba(to);
|
|
78804
|
+
return Array.from({ length: steps }, (_, i) => mix(a, b, i / (steps - 1)));
|
|
78805
|
+
}
|
|
78806
|
+
function blendStops(steps, ...stops) {
|
|
78807
|
+
if (!Number.isInteger(steps) || steps <= 0 || stops.length === 0)
|
|
78808
|
+
return [];
|
|
78809
|
+
if (stops.length === 1 || steps === 1)
|
|
78810
|
+
return blend1D(steps, stops[0], stops.at(-1));
|
|
78811
|
+
const pts = stops.map(rgba);
|
|
78812
|
+
const segs = pts.length - 1;
|
|
78813
|
+
return Array.from({ length: steps }, (_, i) => {
|
|
78814
|
+
const p = i / (steps - 1) * segs;
|
|
78815
|
+
const s = Math.min(segs - 1, Math.floor(p));
|
|
78816
|
+
return mix(pts[s], pts[s + 1], p - s);
|
|
78817
|
+
});
|
|
78818
|
+
}
|
|
78819
|
+
function luminance(c) {
|
|
78820
|
+
const f = (v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
|
|
78821
|
+
return 0.2126 * f(clamp01(c.r)) + 0.7152 * f(clamp01(c.g)) + 0.0722 * f(clamp01(c.b));
|
|
78822
|
+
}
|
|
78823
|
+
function contrastRatio(a, b) {
|
|
78824
|
+
const [x, y] = [luminance(a), luminance(b)];
|
|
78825
|
+
return x >= y ? (x + 0.05) / (y + 0.05) : (y + 0.05) / (x + 0.05);
|
|
78826
|
+
}
|
|
78827
|
+
function over(fg, bg) {
|
|
78828
|
+
const a = clamp01(fg.a);
|
|
78829
|
+
if (a >= 1)
|
|
78830
|
+
return fg;
|
|
78831
|
+
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);
|
|
78832
|
+
}
|
|
78833
|
+
function pickInk(bg, dark = tokens.ink, light = tokens.text) {
|
|
78834
|
+
const surface = over(rgba(bg), rgba(tokens.bgPanel));
|
|
78835
|
+
const inkDark = over(rgba(dark), surface);
|
|
78836
|
+
const inkLight = over(rgba(light), surface);
|
|
78837
|
+
return hex3(contrastRatio(inkDark, surface) >= contrastRatio(inkLight, surface) ? inkDark : inkLight);
|
|
78838
|
+
}
|
|
78839
|
+
var rgba = (c) => parseColor(c), hex3 = (c) => rgbToHex(c), clamp01 = (n) => Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
|
|
78840
|
+
var init_color = __esm(() => {
|
|
78841
|
+
init_tokens();
|
|
78842
|
+
});
|
|
78843
|
+
|
|
78844
|
+
// src/tui/viz/widgets.tsx
|
|
78845
|
+
import { createTextAttributes as createTextAttributes2 } from "@opentui/core";
|
|
78846
|
+
import { jsxDEV as jsxDEV18, Fragment as Fragment11 } from "@opentui/react/jsx-dev-runtime";
|
|
78847
|
+
function rampFor(width, stops) {
|
|
78848
|
+
const use = stops.length > 0 ? stops : ramps.load;
|
|
78849
|
+
const key = `${width}|${use.join(":")}`;
|
|
78850
|
+
const hit = RAMP_CACHE.get(key);
|
|
78851
|
+
if (hit)
|
|
78852
|
+
return hit;
|
|
78853
|
+
const built = blendStops(width, ...use);
|
|
78854
|
+
if (RAMP_CACHE.size >= RAMP_CACHE_MAX)
|
|
78855
|
+
RAMP_CACHE.delete(RAMP_CACHE.keys().next().value);
|
|
78856
|
+
RAMP_CACHE.set(key, built);
|
|
78857
|
+
return built;
|
|
78858
|
+
}
|
|
78859
|
+
function fillCells(pct, width) {
|
|
78860
|
+
const cells = Math.floor(width);
|
|
78861
|
+
if (!Number.isFinite(cells) || cells <= 0 || Number.isNaN(pct))
|
|
78862
|
+
return 0;
|
|
78863
|
+
return Math.round(Math.min(100, Math.max(0, pct)) / 100 * cells);
|
|
78864
|
+
}
|
|
78865
|
+
function MeterSpan({
|
|
78866
|
+
pct,
|
|
78867
|
+
width,
|
|
78868
|
+
ramp = ramps.load
|
|
78869
|
+
}) {
|
|
78870
|
+
const cells = Math.floor(width);
|
|
78871
|
+
if (!Number.isFinite(cells) || cells <= 0)
|
|
78872
|
+
return null;
|
|
78873
|
+
if (Number.isNaN(pct))
|
|
78874
|
+
return /* @__PURE__ */ jsxDEV18("span", {
|
|
78875
|
+
fg: tokens.dead,
|
|
78876
|
+
children: NODATA.repeat(cells)
|
|
78877
|
+
}, undefined, false, undefined, this);
|
|
78878
|
+
const cols = rampFor(cells, ramp);
|
|
78879
|
+
const filled = fillCells(pct, cells);
|
|
78880
|
+
return /* @__PURE__ */ jsxDEV18(Fragment11, {
|
|
78881
|
+
children: Array.from({ length: cells }, (_, i) => /* @__PURE__ */ jsxDEV18("span", {
|
|
78882
|
+
fg: i < filled ? cols[i] : tokens.border,
|
|
78883
|
+
children: i < filled ? FILL : TRACK
|
|
78884
|
+
}, i, false, undefined, this))
|
|
78885
|
+
}, undefined, false, undefined, this);
|
|
78886
|
+
}
|
|
78887
|
+
function Sparkline({
|
|
78888
|
+
values,
|
|
78889
|
+
fg = tokens.info,
|
|
78890
|
+
style,
|
|
78891
|
+
...layout
|
|
78892
|
+
}) {
|
|
78893
|
+
const row = sparkGlyphs(values);
|
|
78894
|
+
if (row === null)
|
|
78895
|
+
return null;
|
|
78896
|
+
return /* @__PURE__ */ jsxDEV18("text", {
|
|
78897
|
+
fg,
|
|
78898
|
+
flexShrink: 0,
|
|
78899
|
+
...layout,
|
|
78900
|
+
style,
|
|
78901
|
+
children: row
|
|
78902
|
+
}, undefined, false, undefined, this);
|
|
78903
|
+
}
|
|
78904
|
+
function sparkGlyphs(values) {
|
|
78905
|
+
if (values.length === 0)
|
|
78906
|
+
return null;
|
|
78907
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
78908
|
+
let min = Number.POSITIVE_INFINITY;
|
|
78909
|
+
for (const v of values)
|
|
78910
|
+
if (Number.isFinite(v)) {
|
|
78911
|
+
max = Math.max(max, v);
|
|
78912
|
+
min = Math.min(min, v);
|
|
78913
|
+
}
|
|
78914
|
+
if (max === Number.NEGATIVE_INFINITY)
|
|
78915
|
+
return GAP.repeat(values.length);
|
|
78916
|
+
const half = max / 2 - min / 2;
|
|
78917
|
+
const mid = SPARK[Math.floor((SPARK.length - 1) / 2)];
|
|
78918
|
+
const top = SPARK.length - 1;
|
|
78919
|
+
const glyph = (v) => SPARK[Math.min(top, Math.max(0, Math.round((v / 2 - min / 2) / half * top)))];
|
|
78920
|
+
return values.map((v) => !Number.isFinite(v) ? GAP : half > 0 ? glyph(v) : mid).join("");
|
|
78921
|
+
}
|
|
78922
|
+
function SparklineSpan({
|
|
78923
|
+
values,
|
|
78924
|
+
fg = tokens.info
|
|
78925
|
+
}) {
|
|
78926
|
+
const row = sparkGlyphs(values);
|
|
78927
|
+
return row === null ? null : /* @__PURE__ */ jsxDEV18("span", {
|
|
78928
|
+
fg,
|
|
78929
|
+
children: row
|
|
78930
|
+
}, undefined, false, undefined, this);
|
|
78931
|
+
}
|
|
78932
|
+
function badgePad(label, width) {
|
|
78933
|
+
const pad2 = Math.max(0, (width ?? 0) - displayWidth(label) - 2);
|
|
78934
|
+
return pad2 > 0 ? /* @__PURE__ */ jsxDEV18("span", {
|
|
78935
|
+
children: " ".repeat(pad2)
|
|
78936
|
+
}, undefined, false, undefined, this) : null;
|
|
78937
|
+
}
|
|
78938
|
+
function BadgeSpan({ label, bg, width }) {
|
|
78939
|
+
return /* @__PURE__ */ jsxDEV18(Fragment11, {
|
|
78940
|
+
children: [
|
|
78941
|
+
/* @__PURE__ */ jsxDEV18("span", {
|
|
78942
|
+
fg: pickInk(bg),
|
|
78943
|
+
bg,
|
|
78944
|
+
attributes: BOLD4,
|
|
78945
|
+
children: ` ${label} `
|
|
78946
|
+
}, undefined, false, undefined, this),
|
|
78947
|
+
badgePad(label, width)
|
|
78948
|
+
]
|
|
78949
|
+
}, undefined, true, undefined, this);
|
|
78950
|
+
}
|
|
78951
|
+
function Panel({
|
|
78952
|
+
title,
|
|
78953
|
+
focused = false,
|
|
78954
|
+
flush = false,
|
|
78955
|
+
children,
|
|
78956
|
+
style,
|
|
78957
|
+
...layout
|
|
78958
|
+
}) {
|
|
78959
|
+
return /* @__PURE__ */ jsxDEV18("box", {
|
|
78960
|
+
border: true,
|
|
78961
|
+
borderStyle: "rounded",
|
|
78962
|
+
borderColor: focused ? tokens.accent : tokens.border,
|
|
78963
|
+
backgroundColor: tokens.bgPanel,
|
|
78964
|
+
title,
|
|
78965
|
+
titleAlignment: "left",
|
|
78966
|
+
flexDirection: "column",
|
|
78967
|
+
overflow: "hidden",
|
|
78968
|
+
paddingLeft: flush ? 0 : 1,
|
|
78969
|
+
paddingRight: flush ? 0 : 1,
|
|
78970
|
+
...layout,
|
|
78971
|
+
style,
|
|
78972
|
+
children
|
|
78973
|
+
}, undefined, false, undefined, this);
|
|
78974
|
+
}
|
|
78975
|
+
var BOLD4, FILL = "\u2588", TRACK = "\u2591", SPARK, GAP = " ", NODATA = "\u254C", RAMP_CACHE, RAMP_CACHE_MAX = 64;
|
|
78976
|
+
var init_widgets = __esm(() => {
|
|
78977
|
+
init_color();
|
|
78978
|
+
init_text();
|
|
78979
|
+
init_tokens();
|
|
78980
|
+
BOLD4 = createTextAttributes2({ bold: true });
|
|
78981
|
+
SPARK = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
78982
|
+
RAMP_CACHE = new Map;
|
|
78983
|
+
});
|
|
78984
|
+
|
|
78985
|
+
// src/session/session-discovery.ts
|
|
78986
|
+
var exports_session_discovery = {};
|
|
78987
|
+
__export(exports_session_discovery, {
|
|
78988
|
+
slugForPath: () => slugForPath,
|
|
78989
|
+
sessionLabel: () => sessionLabel,
|
|
78990
|
+
mainConversationTurn: () => mainConversationTurn,
|
|
78991
|
+
isHarnessNoise: () => isHarnessNoise,
|
|
78992
|
+
isAgentSession: () => isAgentSession,
|
|
78993
|
+
isActive: () => isActive,
|
|
78994
|
+
hydrateSession: () => hydrateSession,
|
|
78995
|
+
hydrateConversation: () => hydrateConversation,
|
|
78996
|
+
getRepoContext: () => getRepoContext,
|
|
78997
|
+
findLatestSessionId: () => findLatestSessionId,
|
|
78998
|
+
enrichWorktreeGit: () => enrichWorktreeGit,
|
|
78999
|
+
discoverWorktreeGroups: () => discoverWorktreeGroups,
|
|
79000
|
+
PROJECTS_DIR: () => PROJECTS_DIR,
|
|
79001
|
+
ACTIVE_WINDOW_MS: () => ACTIVE_WINDOW_MS
|
|
79002
|
+
});
|
|
79003
|
+
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
79004
|
+
import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
79005
|
+
import { homedir as homedir34 } from "os";
|
|
79006
|
+
import { basename, join as join37 } from "path";
|
|
79007
|
+
function slugForPath(absPath) {
|
|
79008
|
+
return absPath.replace(/[/.]/g, "-");
|
|
79009
|
+
}
|
|
79010
|
+
function isAgentSession(row) {
|
|
79011
|
+
return row.entrypoint !== undefined && row.entrypoint !== "cli";
|
|
79012
|
+
}
|
|
79013
|
+
function getRepoContext(cwd = process.cwd()) {
|
|
79014
|
+
const git = (args) => {
|
|
79015
|
+
try {
|
|
79016
|
+
return execFileSync2("git", args, {
|
|
79017
|
+
cwd,
|
|
79018
|
+
encoding: "utf-8",
|
|
79019
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
79020
|
+
}).trim();
|
|
79021
|
+
} catch {
|
|
79022
|
+
return null;
|
|
79023
|
+
}
|
|
79024
|
+
};
|
|
79025
|
+
const current = git(["rev-parse", "--show-toplevel"]);
|
|
79026
|
+
const commonDir = git(["rev-parse", "--git-common-dir"]);
|
|
79027
|
+
if (!current || !commonDir)
|
|
79028
|
+
return null;
|
|
79029
|
+
const root = commonDir.endsWith("/.git") ? commonDir.slice(0, -"/.git".length) : current;
|
|
79030
|
+
const liveWorktrees = [];
|
|
79031
|
+
const branchByPath = new Map;
|
|
79032
|
+
const porcelain = git(["worktree", "list", "--porcelain"]);
|
|
79033
|
+
if (porcelain) {
|
|
79034
|
+
let currentPath = null;
|
|
79035
|
+
for (const line of porcelain.split(`
|
|
79036
|
+
`)) {
|
|
79037
|
+
if (line.startsWith("worktree ")) {
|
|
79038
|
+
currentPath = line.slice("worktree ".length);
|
|
79039
|
+
liveWorktrees.push(currentPath);
|
|
79040
|
+
} else if (line.startsWith("branch ") && currentPath) {
|
|
79041
|
+
branchByPath.set(currentPath, line.slice("branch ".length).replace(/^refs\/heads\//, ""));
|
|
79042
|
+
}
|
|
79043
|
+
}
|
|
79044
|
+
}
|
|
79045
|
+
return { root, current, liveWorktrees, branchByPath };
|
|
79046
|
+
}
|
|
79047
|
+
function projectDirs() {
|
|
79048
|
+
try {
|
|
79049
|
+
return readdirSync7(PROJECTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
79050
|
+
} catch {
|
|
79051
|
+
return [];
|
|
79052
|
+
}
|
|
79053
|
+
}
|
|
79054
|
+
function sessionsIn(dirName) {
|
|
79055
|
+
const dir = join37(PROJECTS_DIR, dirName);
|
|
79056
|
+
let names;
|
|
79057
|
+
try {
|
|
79058
|
+
names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
|
|
79059
|
+
} catch {
|
|
79060
|
+
return [];
|
|
79061
|
+
}
|
|
79062
|
+
const rows = [];
|
|
79063
|
+
for (const n of names) {
|
|
79064
|
+
const file2 = join37(dir, n);
|
|
79065
|
+
try {
|
|
79066
|
+
const st = statSync6(file2);
|
|
79067
|
+
if (st.size === 0)
|
|
79068
|
+
continue;
|
|
79069
|
+
const row = {
|
|
79070
|
+
id: basename(n, ".jsonl"),
|
|
79071
|
+
file: file2,
|
|
79072
|
+
mtimeMs: st.mtimeMs,
|
|
79073
|
+
sizeBytes: st.size
|
|
79074
|
+
};
|
|
79075
|
+
const head = readChunk(file2, 0, Math.min(ENTRYPOINT_BYTES, st.size));
|
|
79076
|
+
const m = /"entrypoint":"([a-z-]+)"/.exec(head);
|
|
79077
|
+
if (m)
|
|
79078
|
+
row.entrypoint = m[1];
|
|
79079
|
+
rows.push(row);
|
|
79080
|
+
} catch {}
|
|
79081
|
+
}
|
|
79082
|
+
return rows;
|
|
79083
|
+
}
|
|
79084
|
+
async function enrichWorktreeGit(groups, repoRoot) {
|
|
79085
|
+
const run = (cwd, args) => new Promise((resolve5) => {
|
|
79086
|
+
execFile("git", args, { cwd, encoding: "utf-8" }, (err, stdout) => resolve5(err ? "" : stdout));
|
|
79087
|
+
});
|
|
79088
|
+
const trackByBranch = new Map;
|
|
79089
|
+
const refs = await run(repoRoot, [
|
|
79090
|
+
"for-each-ref",
|
|
79091
|
+
"--format=%(refname:short)\t%(upstream:track)",
|
|
79092
|
+
"refs/heads/"
|
|
79093
|
+
]);
|
|
79094
|
+
for (const line of refs.split(`
|
|
79095
|
+
`)) {
|
|
79096
|
+
const [name, track] = line.split("\t");
|
|
79097
|
+
if (!name || !track)
|
|
79098
|
+
continue;
|
|
79099
|
+
const ahead = /ahead (\d+)/.exec(track)?.[1];
|
|
79100
|
+
const behind = /behind (\d+)/.exec(track)?.[1];
|
|
79101
|
+
if (ahead || behind) {
|
|
79102
|
+
trackByBranch.set(name, {
|
|
79103
|
+
...ahead ? { ahead: Number(ahead) } : {},
|
|
79104
|
+
...behind ? { behind: Number(behind) } : {}
|
|
79105
|
+
});
|
|
79106
|
+
}
|
|
79107
|
+
}
|
|
79108
|
+
await Promise.all(groups.map(async (g) => {
|
|
79109
|
+
if (g.branch)
|
|
79110
|
+
Object.assign(g, trackByBranch.get(g.branch) ?? {});
|
|
79111
|
+
if (!g.path || !g.live)
|
|
79112
|
+
return;
|
|
79113
|
+
const out = await run(g.path, ["status", "--porcelain"]);
|
|
79114
|
+
g.dirty = out.split(`
|
|
79115
|
+
`).filter((l) => l.trim().length > 0).length;
|
|
79116
|
+
}));
|
|
79117
|
+
}
|
|
79118
|
+
function isUnder(p, root) {
|
|
79119
|
+
return p === root || p.startsWith(`${root}/`);
|
|
79120
|
+
}
|
|
79121
|
+
function readProjectCwd(dirName) {
|
|
79122
|
+
const rows = sessionsIn(dirName);
|
|
79123
|
+
if (rows.length === 0)
|
|
79124
|
+
return null;
|
|
79125
|
+
rows.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
79126
|
+
for (const row of rows.slice(0, 2)) {
|
|
79127
|
+
for (const r of parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false)) {
|
|
79128
|
+
if (typeof r.cwd === "string" && r.cwd)
|
|
79129
|
+
return r.cwd;
|
|
79130
|
+
}
|
|
79131
|
+
}
|
|
79132
|
+
return null;
|
|
79133
|
+
}
|
|
79134
|
+
function isActive(row, now = Date.now()) {
|
|
79135
|
+
return now - row.mtimeMs < ACTIVE_WINDOW_MS;
|
|
79136
|
+
}
|
|
79137
|
+
function discoverWorktreeGroups(repo) {
|
|
79138
|
+
const rootSlug = slugForPath(repo.root);
|
|
79139
|
+
const mine = projectDirs();
|
|
79140
|
+
const known = [...repo.liveWorktrees].map((p) => ({ path: p, slug: slugForPath(p) })).sort((a, b) => b.slug.length - a.slug.length);
|
|
79141
|
+
const groups = new Map;
|
|
79142
|
+
const groupCwd = new Map;
|
|
79143
|
+
const upsert = (name, path2, live) => {
|
|
79144
|
+
let g = groups.get(name);
|
|
79145
|
+
if (!g) {
|
|
79146
|
+
g = {
|
|
79147
|
+
name,
|
|
79148
|
+
path: path2,
|
|
79149
|
+
live,
|
|
79150
|
+
current: path2 !== null && path2 === repo.current,
|
|
79151
|
+
sessions: [],
|
|
79152
|
+
lastActiveMs: 0,
|
|
79153
|
+
activeNow: false,
|
|
79154
|
+
...path2 ? { branch: repo.branchByPath.get(path2) } : {}
|
|
79155
|
+
};
|
|
79156
|
+
groups.set(name, g);
|
|
79157
|
+
}
|
|
79158
|
+
return g;
|
|
79159
|
+
};
|
|
79160
|
+
const WORKTREE_MARK = "--claude-worktrees-";
|
|
79161
|
+
const worktreeMatches = known.filter((k) => k.path !== repo.root);
|
|
79162
|
+
for (const dir of mine) {
|
|
79163
|
+
const at = dir.indexOf(WORKTREE_MARK);
|
|
79164
|
+
let name;
|
|
79165
|
+
let path2;
|
|
79166
|
+
let live;
|
|
79167
|
+
const hit = worktreeMatches.find((k) => dir === k.slug || dir.startsWith(`${k.slug}-`));
|
|
79168
|
+
if (hit) {
|
|
79169
|
+
path2 = hit.path;
|
|
79170
|
+
live = true;
|
|
79171
|
+
name = hit.path === repo.root ? "(root)" : basename(hit.path);
|
|
79172
|
+
} else if (at !== -1 && dir.startsWith(rootSlug)) {
|
|
79173
|
+
path2 = null;
|
|
79174
|
+
live = false;
|
|
79175
|
+
name = dir.slice(at + WORKTREE_MARK.length);
|
|
79176
|
+
} else if (dir === rootSlug) {
|
|
79177
|
+
name = "(root)";
|
|
79178
|
+
path2 = repo.root;
|
|
79179
|
+
live = true;
|
|
79180
|
+
} else if (dir.startsWith(`${rootSlug}-`) && at === -1) {
|
|
79181
|
+
const cwd = readProjectCwd(dir);
|
|
79182
|
+
if (!cwd || !isUnder(cwd, repo.root))
|
|
79183
|
+
continue;
|
|
79184
|
+
name = "(root)";
|
|
79185
|
+
path2 = repo.root;
|
|
79186
|
+
live = true;
|
|
79187
|
+
} else {
|
|
79188
|
+
continue;
|
|
79189
|
+
}
|
|
79190
|
+
const g = upsert(name, path2, live);
|
|
79191
|
+
if (path2)
|
|
79192
|
+
groupCwd.set(name, path2);
|
|
79193
|
+
else if (!groupCwd.has(name)) {
|
|
79194
|
+
const cwd = readProjectCwd(dir);
|
|
79195
|
+
if (cwd)
|
|
79196
|
+
groupCwd.set(name, cwd);
|
|
79197
|
+
}
|
|
79198
|
+
for (const s of sessionsIn(dir)) {
|
|
79199
|
+
g.sessions.push(s);
|
|
79200
|
+
if (s.mtimeMs > g.lastActiveMs)
|
|
79201
|
+
g.lastActiveMs = s.mtimeMs;
|
|
79202
|
+
}
|
|
79203
|
+
}
|
|
79204
|
+
const names = [...groups.keys()].sort((a, b) => b.length - a.length);
|
|
79205
|
+
for (const name of [...groups.keys()]) {
|
|
79206
|
+
const g = groups.get(name);
|
|
79207
|
+
if (!g || g.live)
|
|
79208
|
+
continue;
|
|
79209
|
+
const parent = names.find((n) => n !== name && name.startsWith(`${n}-`) && groups.has(n));
|
|
79210
|
+
if (!parent)
|
|
79211
|
+
continue;
|
|
79212
|
+
const into = groups.get(parent);
|
|
79213
|
+
const childCwd = groupCwd.get(name);
|
|
79214
|
+
const parentCwd = groupCwd.get(parent);
|
|
79215
|
+
if (!childCwd || !parentCwd || !isUnder(childCwd, parentCwd))
|
|
79216
|
+
continue;
|
|
79217
|
+
into.sessions.push(...g.sessions);
|
|
79218
|
+
into.lastActiveMs = Math.max(into.lastActiveMs, g.lastActiveMs);
|
|
79219
|
+
groups.delete(name);
|
|
79220
|
+
}
|
|
79221
|
+
for (const g of groups.values()) {
|
|
79222
|
+
g.sessions.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
79223
|
+
g.activeNow = g.sessions.some((s) => isActive(s));
|
|
79224
|
+
if (g.path) {
|
|
79225
|
+
try {
|
|
79226
|
+
g.createdMs = statSync6(g.path).birthtimeMs;
|
|
79227
|
+
} catch {}
|
|
79228
|
+
}
|
|
79229
|
+
if (!g.createdMs && g.sessions.length > 0) {
|
|
79230
|
+
g.createdMs = g.sessions.reduce((m, s) => Math.min(m, s.mtimeMs), Number.POSITIVE_INFINITY);
|
|
79231
|
+
}
|
|
79232
|
+
}
|
|
79233
|
+
return [...groups.values()].filter((g) => g.sessions.length > 0).sort((a, b) => {
|
|
79234
|
+
if (a.current !== b.current)
|
|
79235
|
+
return a.current ? -1 : 1;
|
|
79236
|
+
return b.lastActiveMs - a.lastActiveMs;
|
|
79237
|
+
});
|
|
79238
|
+
}
|
|
79239
|
+
function readChunk(file2, pos, len) {
|
|
79240
|
+
if (len <= 0)
|
|
79241
|
+
return "";
|
|
79242
|
+
let fd = null;
|
|
79243
|
+
try {
|
|
79244
|
+
fd = openSync5(file2, "r");
|
|
79245
|
+
const buf = Buffer.allocUnsafe(len);
|
|
79246
|
+
const n = readSync(fd, buf, 0, len, pos);
|
|
79247
|
+
return buf.subarray(0, n).toString("utf-8");
|
|
79248
|
+
} catch {
|
|
79249
|
+
return "";
|
|
79250
|
+
} finally {
|
|
79251
|
+
if (fd !== null) {
|
|
79252
|
+
try {
|
|
79253
|
+
closeSync5(fd);
|
|
79254
|
+
} catch {}
|
|
79255
|
+
}
|
|
79256
|
+
}
|
|
79257
|
+
}
|
|
79258
|
+
function parseRecords(chunk, dropFirstPartial) {
|
|
79259
|
+
const lines = chunk.split(`
|
|
79260
|
+
`);
|
|
79261
|
+
if (dropFirstPartial)
|
|
79262
|
+
lines.shift();
|
|
79263
|
+
else
|
|
79264
|
+
lines.pop();
|
|
79265
|
+
const out = [];
|
|
79266
|
+
for (const l of lines) {
|
|
79267
|
+
if (!l)
|
|
79268
|
+
continue;
|
|
79269
|
+
try {
|
|
79270
|
+
const o = JSON.parse(l);
|
|
79271
|
+
if (o && typeof o === "object")
|
|
79272
|
+
out.push(o);
|
|
79273
|
+
} catch {}
|
|
79274
|
+
}
|
|
79275
|
+
return out;
|
|
79276
|
+
}
|
|
79277
|
+
function contentText(content) {
|
|
79278
|
+
if (typeof content === "string")
|
|
79279
|
+
return content;
|
|
79280
|
+
if (!Array.isArray(content))
|
|
79281
|
+
return "";
|
|
79282
|
+
return content.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join(`
|
|
79283
|
+
`);
|
|
79284
|
+
}
|
|
79285
|
+
function isHarnessNoise(raw2) {
|
|
79286
|
+
const t = raw2.trimStart();
|
|
79287
|
+
if (t.startsWith("[Request interrupted by user"))
|
|
79288
|
+
return true;
|
|
79289
|
+
return HARNESS_ENVELOPES.some((e) => t.startsWith(e));
|
|
79290
|
+
}
|
|
79291
|
+
function mainConversationTurn(r) {
|
|
79292
|
+
if (r.type !== "user" && r.type !== "assistant")
|
|
79293
|
+
return null;
|
|
79294
|
+
if (r.isMeta || r.isSidechain)
|
|
79295
|
+
return null;
|
|
79296
|
+
const content = r.message?.content;
|
|
79297
|
+
if (Array.isArray(content) && content.some((b) => b?.type === "tool_result")) {
|
|
79298
|
+
return null;
|
|
79299
|
+
}
|
|
79300
|
+
const raw2 = contentText(content);
|
|
79301
|
+
if (!raw2.trim() || isHarnessNoise(raw2))
|
|
79302
|
+
return null;
|
|
79303
|
+
return { role: r.type === "user" ? "user" : "assistant", raw: raw2 };
|
|
79304
|
+
}
|
|
79305
|
+
function cleanPrompt(text) {
|
|
79306
|
+
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();
|
|
79307
|
+
}
|
|
79308
|
+
function hydrateSession(row) {
|
|
79309
|
+
if (row.hydrated)
|
|
79310
|
+
return row;
|
|
79311
|
+
row.hydrated = true;
|
|
79312
|
+
if (isActive(row)) {
|
|
79313
|
+
try {
|
|
79314
|
+
row.sizeBytes = statSync6(row.file).size;
|
|
79315
|
+
} catch {}
|
|
79316
|
+
}
|
|
79317
|
+
const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
|
|
79318
|
+
for (const r of head) {
|
|
79319
|
+
if (!row.gitBranch && typeof r.gitBranch === "string")
|
|
79320
|
+
row.gitBranch = r.gitBranch;
|
|
79321
|
+
if (!row.firstPrompt && r.type === "user" && !r.isMeta) {
|
|
79322
|
+
const raw2 = contentText(r.message?.content);
|
|
79323
|
+
const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
|
|
79324
|
+
if (t)
|
|
79325
|
+
row.firstPrompt = t;
|
|
79326
|
+
}
|
|
79327
|
+
if (row.gitBranch && row.firstPrompt)
|
|
79328
|
+
break;
|
|
79329
|
+
}
|
|
79330
|
+
const tailStart = Math.max(0, row.sizeBytes - TAIL_BYTES);
|
|
79331
|
+
const tail = parseRecords(readChunk(row.file, tailStart, row.sizeBytes - tailStart), tailStart > 0);
|
|
79332
|
+
for (let i = tail.length - 1;i >= 0; i--) {
|
|
79333
|
+
const r = tail[i];
|
|
79334
|
+
if (!row.title && r.type === "ai-title" && typeof r.aiTitle === "string" && r.aiTitle.trim()) {
|
|
79335
|
+
row.title = r.aiTitle.trim();
|
|
79336
|
+
}
|
|
79337
|
+
if (row.lastMessageChars === undefined && r.type === "user" && !r.isMeta) {
|
|
79338
|
+
const raw2 = contentText(r.message?.content);
|
|
79339
|
+
const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
|
|
79340
|
+
if (t)
|
|
79341
|
+
row.lastMessageChars = t.length;
|
|
79342
|
+
}
|
|
79343
|
+
}
|
|
79344
|
+
row.recentTurns = extractRecentTurns(tail);
|
|
79345
|
+
return row;
|
|
79346
|
+
}
|
|
79347
|
+
function extractRecentTurns(records) {
|
|
79348
|
+
const out = [];
|
|
79349
|
+
let ai = 0;
|
|
79350
|
+
let user = 0;
|
|
79351
|
+
for (let i = records.length - 1;i >= 0; i--) {
|
|
79352
|
+
if (ai >= RECENT_AI_TURNS && user >= RECENT_USER_TURNS)
|
|
79353
|
+
break;
|
|
79354
|
+
const r = records[i];
|
|
79355
|
+
if (r.type !== "user" && r.type !== "assistant")
|
|
79356
|
+
continue;
|
|
79357
|
+
const role = r.type === "user" ? "user" : "assistant";
|
|
79358
|
+
if (role === "assistant" ? ai >= RECENT_AI_TURNS : user >= RECENT_USER_TURNS)
|
|
79359
|
+
continue;
|
|
79360
|
+
const turn = mainConversationTurn(r);
|
|
79361
|
+
if (!turn)
|
|
79362
|
+
continue;
|
|
79363
|
+
const text = cleanPrompt(turn.raw);
|
|
79364
|
+
if (!text)
|
|
79365
|
+
continue;
|
|
79366
|
+
out.push({ role, text });
|
|
79367
|
+
if (role === "assistant")
|
|
79368
|
+
ai++;
|
|
79369
|
+
else
|
|
79370
|
+
user++;
|
|
79371
|
+
}
|
|
79372
|
+
return out.reverse();
|
|
79373
|
+
}
|
|
79374
|
+
function hydrateConversation(row) {
|
|
79375
|
+
if (row.conversationDeepened)
|
|
79376
|
+
return row;
|
|
79377
|
+
row.conversationDeepened = true;
|
|
79378
|
+
const turns = row.recentTurns;
|
|
79379
|
+
if (!turns || turns.some((t) => t.role === "user"))
|
|
79380
|
+
return row;
|
|
79381
|
+
const start = Math.max(0, row.sizeBytes - DEEP_TAIL_BYTES);
|
|
79382
|
+
const chunk = readChunk(row.file, start, row.sizeBytes - start);
|
|
79383
|
+
if (!chunk)
|
|
79384
|
+
return row;
|
|
79385
|
+
const lines = chunk.split(`
|
|
79386
|
+
`);
|
|
79387
|
+
if (start > 0)
|
|
79388
|
+
lines.shift();
|
|
79389
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
79390
|
+
const line = lines[i];
|
|
79391
|
+
if (!line.includes('"type":"user"'))
|
|
79392
|
+
continue;
|
|
79393
|
+
let r;
|
|
79394
|
+
try {
|
|
79395
|
+
r = JSON.parse(line);
|
|
79396
|
+
} catch {
|
|
79397
|
+
continue;
|
|
79398
|
+
}
|
|
79399
|
+
const turn = mainConversationTurn(r);
|
|
79400
|
+
if (!turn || turn.role !== "user")
|
|
79401
|
+
continue;
|
|
79402
|
+
const text = cleanPrompt(turn.raw);
|
|
79403
|
+
if (!text)
|
|
79404
|
+
continue;
|
|
79405
|
+
turns.unshift({ role: "user", text });
|
|
79406
|
+
if (row.lastMessageChars === undefined)
|
|
79407
|
+
row.lastMessageChars = text.length;
|
|
79408
|
+
return row;
|
|
79409
|
+
}
|
|
79410
|
+
return row;
|
|
79411
|
+
}
|
|
79412
|
+
function sessionLabel(row) {
|
|
79413
|
+
return row.title || row.firstPrompt || row.id;
|
|
79414
|
+
}
|
|
79415
|
+
function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
|
|
79416
|
+
const rows = sessionsIn(slugForPath(cwd)).filter((r) => r.mtimeMs >= sinceMs);
|
|
79417
|
+
if (rows.length === 0)
|
|
79418
|
+
return null;
|
|
79419
|
+
return rows.reduce((a, b) => b.mtimeMs > a.mtimeMs ? b : a).id;
|
|
79420
|
+
}
|
|
79421
|
+
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;
|
|
79422
|
+
var init_session_discovery = __esm(() => {
|
|
79423
|
+
PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
|
|
79424
|
+
HEAD_BYTES = 64 * 1024;
|
|
79425
|
+
TAIL_BYTES = 128 * 1024;
|
|
79426
|
+
HARNESS_ENVELOPES = [
|
|
79427
|
+
"<task-notification>",
|
|
79428
|
+
"<system-reminder>",
|
|
79429
|
+
"<local-command-stdout>",
|
|
79430
|
+
"<user-prompt-submit-hook>"
|
|
79431
|
+
];
|
|
79432
|
+
DEEP_TAIL_BYTES = 4 * 1024 * 1024;
|
|
79433
|
+
});
|
|
79434
|
+
|
|
79435
|
+
// src/session/conversation.ts
|
|
79436
|
+
import { closeSync as closeSync6, openSync as openSync6, readSync as readSync2, statSync as statSync7 } from "fs";
|
|
79437
|
+
import { StringDecoder } from "string_decoder";
|
|
79438
|
+
function looksLikeTurn(line) {
|
|
79439
|
+
const assistant = line.includes('"type":"assistant"');
|
|
79440
|
+
if (!assistant && !line.includes('"type":"user"'))
|
|
79441
|
+
return false;
|
|
79442
|
+
if (line.includes('"isSidechain":true'))
|
|
79443
|
+
return false;
|
|
79444
|
+
if (line.includes('"isMeta":true'))
|
|
79445
|
+
return false;
|
|
79446
|
+
if (line.includes('"type":"tool_result"'))
|
|
79447
|
+
return false;
|
|
79448
|
+
if (assistant && !line.includes('"type":"text"'))
|
|
79449
|
+
return false;
|
|
79450
|
+
return true;
|
|
79451
|
+
}
|
|
79452
|
+
function cleanTurnText(raw2) {
|
|
79453
|
+
let t = raw2;
|
|
79454
|
+
for (const re of INLINE_ENVELOPES)
|
|
79455
|
+
t = t.replace(re, "");
|
|
79456
|
+
t = t.replace(/<command-name>([\s\S]*?)<\/command-name>/g, "/$1");
|
|
79457
|
+
return t.replace(/\t/g, " ").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").replace(/\r\n?/g, `
|
|
79458
|
+
`).split(`
|
|
79459
|
+
`).map((l) => l.replace(/\s+$/, "")).join(`
|
|
79460
|
+
`).replace(/\n{3,}/g, `
|
|
79461
|
+
|
|
79462
|
+
`).trim();
|
|
79463
|
+
}
|
|
79464
|
+
function readConversation(file2, opts = {}) {
|
|
79465
|
+
const maxTurn = opts.maxTurnChars ?? MAX_TURN_CHARS;
|
|
79466
|
+
const maxTotal = opts.maxTotalChars ?? MAX_TOTAL_CHARS;
|
|
79467
|
+
const chunkBytes = opts.chunkBytes ?? CHUNK_BYTES;
|
|
79468
|
+
const started = Date.now();
|
|
79469
|
+
const turns = [];
|
|
79470
|
+
let chars = 0;
|
|
79471
|
+
let dropped = 0;
|
|
79472
|
+
let anyElided = false;
|
|
79473
|
+
let bytes2 = 0;
|
|
79474
|
+
const take = (turn) => {
|
|
79475
|
+
turns.push(turn);
|
|
79476
|
+
chars += turn.text.length;
|
|
79477
|
+
if (chars <= maxTotal)
|
|
79478
|
+
return;
|
|
79479
|
+
let freed = 0;
|
|
79480
|
+
let n = 0;
|
|
79481
|
+
while (n < turns.length - 1 && chars - freed > maxTotal) {
|
|
79482
|
+
freed += turns[n].text.length;
|
|
79483
|
+
n++;
|
|
79484
|
+
}
|
|
79485
|
+
turns.splice(0, n);
|
|
79486
|
+
chars -= freed;
|
|
79487
|
+
dropped += n;
|
|
79488
|
+
};
|
|
79489
|
+
let fd = null;
|
|
79490
|
+
try {
|
|
79491
|
+
const size = statSync7(file2).size;
|
|
79492
|
+
fd = openSync6(file2, "r");
|
|
79493
|
+
const buf = Buffer.allocUnsafe(chunkBytes);
|
|
79494
|
+
const decoder = new StringDecoder("utf-8");
|
|
79495
|
+
let pending = "";
|
|
79496
|
+
let pos = 0;
|
|
79497
|
+
const consume = (line) => {
|
|
79498
|
+
if (!line || !looksLikeTurn(line))
|
|
79499
|
+
return;
|
|
79500
|
+
let record4;
|
|
79501
|
+
try {
|
|
79502
|
+
record4 = JSON.parse(line);
|
|
79503
|
+
} catch {
|
|
79504
|
+
return;
|
|
79505
|
+
}
|
|
79506
|
+
if (!record4 || typeof record4 !== "object")
|
|
79507
|
+
return;
|
|
79508
|
+
const raw2 = mainConversationTurn(record4);
|
|
79509
|
+
if (!raw2)
|
|
79510
|
+
return;
|
|
79511
|
+
let text = cleanTurnText(raw2.raw);
|
|
79512
|
+
if (!text)
|
|
79513
|
+
return;
|
|
79514
|
+
const elided = text.length > maxTurn;
|
|
79515
|
+
if (elided) {
|
|
79516
|
+
anyElided = true;
|
|
79517
|
+
text = `${text.slice(0, maxTurn)}
|
|
79518
|
+
|
|
79519
|
+
\u2026 turn truncated at ${maxTurn.toLocaleString()} characters`;
|
|
79520
|
+
}
|
|
79521
|
+
take({ role: raw2.role, text, elided });
|
|
79522
|
+
};
|
|
79523
|
+
while (pos < size) {
|
|
79524
|
+
const n = readSync2(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
|
|
79525
|
+
if (n <= 0)
|
|
79526
|
+
break;
|
|
79527
|
+
pos += n;
|
|
79528
|
+
bytes2 += n;
|
|
79529
|
+
pending += decoder.write(buf.subarray(0, n));
|
|
79530
|
+
let from = 0;
|
|
79531
|
+
let nl = pending.indexOf(`
|
|
79532
|
+
`, from);
|
|
79533
|
+
while (nl !== -1) {
|
|
79534
|
+
consume(pending.slice(from, nl));
|
|
79535
|
+
from = nl + 1;
|
|
79536
|
+
nl = pending.indexOf(`
|
|
79537
|
+
`, from);
|
|
79538
|
+
}
|
|
79539
|
+
if (from > 0)
|
|
79540
|
+
pending = pending.slice(from);
|
|
79541
|
+
}
|
|
79542
|
+
pending += decoder.end();
|
|
79543
|
+
if (pending)
|
|
79544
|
+
consume(pending);
|
|
79545
|
+
} catch {} finally {
|
|
79546
|
+
if (fd !== null) {
|
|
79547
|
+
try {
|
|
79548
|
+
closeSync6(fd);
|
|
79549
|
+
} catch {}
|
|
79550
|
+
}
|
|
79551
|
+
}
|
|
79552
|
+
return { turns, bytes: bytes2, elapsedMs: Date.now() - started, chars, dropped, anyElided };
|
|
79553
|
+
}
|
|
79554
|
+
function cpWidth(cp) {
|
|
79555
|
+
if (cp >= 32 && cp < 127)
|
|
79556
|
+
return 1;
|
|
79557
|
+
return displayWidth(String.fromCodePoint(cp));
|
|
79558
|
+
}
|
|
79559
|
+
function wrapOffsets(text, width) {
|
|
79560
|
+
const out = [];
|
|
79561
|
+
const w = Math.max(1, Math.floor(width));
|
|
79562
|
+
const n = text.length;
|
|
79563
|
+
let paraStart = 0;
|
|
79564
|
+
for (;; ) {
|
|
79565
|
+
let nl = text.indexOf(`
|
|
79566
|
+
`, paraStart);
|
|
79567
|
+
if (nl === -1)
|
|
79568
|
+
nl = n;
|
|
79569
|
+
let s = paraStart;
|
|
79570
|
+
for (;; ) {
|
|
79571
|
+
if (s >= nl) {
|
|
79572
|
+
if (s === paraStart || paraStart === nl)
|
|
79573
|
+
out.push({ start: s, end: nl });
|
|
79574
|
+
break;
|
|
79575
|
+
}
|
|
79576
|
+
let cols = 0;
|
|
79577
|
+
let j = s;
|
|
79578
|
+
let lastSpace = -1;
|
|
79579
|
+
while (j < nl) {
|
|
79580
|
+
const cp = text.codePointAt(j);
|
|
79581
|
+
const size = cp > 65535 ? 2 : 1;
|
|
79582
|
+
const cw = cpWidth(cp);
|
|
79583
|
+
if (cols + cw > w)
|
|
79584
|
+
break;
|
|
79585
|
+
if (cp === 32 && j > s && text.charCodeAt(j - 1) !== 32)
|
|
79586
|
+
lastSpace = j;
|
|
79587
|
+
cols += cw;
|
|
79588
|
+
j += size;
|
|
79589
|
+
}
|
|
79590
|
+
if (j >= nl) {
|
|
79591
|
+
out.push({ start: s, end: nl });
|
|
79592
|
+
break;
|
|
79593
|
+
}
|
|
79594
|
+
const brk = text.charCodeAt(j) === 32 ? j : lastSpace > s ? lastSpace : j;
|
|
79595
|
+
out.push({ start: s, end: brk });
|
|
79596
|
+
s = brk === j && text.charCodeAt(j) !== 32 ? j : brk + 1;
|
|
79597
|
+
while (s < nl && text.charCodeAt(s) === 32)
|
|
79598
|
+
s++;
|
|
79599
|
+
}
|
|
79600
|
+
if (nl >= n)
|
|
79601
|
+
break;
|
|
79602
|
+
paraStart = nl + 1;
|
|
79603
|
+
if (paraStart > n)
|
|
79604
|
+
break;
|
|
79605
|
+
if (paraStart === n) {
|
|
79606
|
+
out.push({ start: n, end: n });
|
|
79607
|
+
break;
|
|
79608
|
+
}
|
|
79609
|
+
}
|
|
79610
|
+
return out;
|
|
79611
|
+
}
|
|
79612
|
+
var CHUNK_BYTES, MAX_TURN_CHARS = 32000, MAX_TOTAL_CHARS = 8000000, INLINE_ENVELOPES;
|
|
79613
|
+
var init_conversation = __esm(() => {
|
|
79614
|
+
init_text();
|
|
79615
|
+
init_session_discovery();
|
|
79616
|
+
CHUNK_BYTES = 1024 * 1024;
|
|
79617
|
+
INLINE_ENVELOPES = [
|
|
79618
|
+
/<system-reminder>[\s\S]*?<\/system-reminder>/g,
|
|
79619
|
+
/<command-message>[\s\S]*?<\/command-message>/g,
|
|
79620
|
+
/<command-args>[\s\S]*?<\/command-args>/g,
|
|
79621
|
+
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g,
|
|
79622
|
+
/<user-prompt-submit-hook>[\s\S]*?<\/user-prompt-submit-hook>/g
|
|
79623
|
+
];
|
|
79624
|
+
});
|
|
79625
|
+
|
|
79626
|
+
// src/session/conversation-reader.tsx
|
|
79627
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
79628
|
+
import { useEffect as useEffect5, useMemo as useMemo3, useState as useState6 } from "react";
|
|
79629
|
+
import { jsxDEV as jsxDEV19, Fragment as Fragment12 } from "@opentui/react/jsx-dev-runtime";
|
|
79630
|
+
function layoutRows(turns, textWidth) {
|
|
79631
|
+
const rows = [];
|
|
79632
|
+
const turnStart = [];
|
|
79633
|
+
const turnEnd = [];
|
|
79634
|
+
for (let t = 0;t < turns.length; t++) {
|
|
79635
|
+
turnStart.push(rows.length);
|
|
79636
|
+
const slices = wrapOffsets(turns[t].text, textWidth);
|
|
79637
|
+
for (let i = 0;i < slices.length; i++) {
|
|
79638
|
+
rows.push({ turn: t, first: i === 0, start: slices[i].start, end: slices[i].end });
|
|
79639
|
+
}
|
|
79640
|
+
turnEnd.push(rows.length);
|
|
79641
|
+
if (t < turns.length - 1)
|
|
79642
|
+
rows.push({ turn: -1, first: false, start: 0, end: 0 });
|
|
79643
|
+
}
|
|
79644
|
+
return { rows, turnStart, turnEnd };
|
|
79645
|
+
}
|
|
79646
|
+
function buildSearch(turns, rows, turnStart, turnEnd, query) {
|
|
79647
|
+
if (!query)
|
|
79648
|
+
return EMPTY_SEARCH;
|
|
79649
|
+
const q = query.toLowerCase();
|
|
79650
|
+
const hits = [];
|
|
79651
|
+
const ranges = new Map;
|
|
79652
|
+
let capped = false;
|
|
79653
|
+
for (let t = 0;t < turns.length && !capped; t++) {
|
|
79654
|
+
const hay = turns[t].text.toLowerCase();
|
|
79655
|
+
let at = hay.indexOf(q);
|
|
79656
|
+
if (at === -1)
|
|
79657
|
+
continue;
|
|
79658
|
+
const lo = turnStart[t];
|
|
79659
|
+
const hi = turnEnd[t];
|
|
79660
|
+
let r = lo;
|
|
79661
|
+
while (at !== -1) {
|
|
79662
|
+
if (hits.length >= MAX_MATCHES) {
|
|
79663
|
+
capped = true;
|
|
79664
|
+
break;
|
|
79665
|
+
}
|
|
79666
|
+
const end = at + q.length;
|
|
79667
|
+
while (r < hi - 1 && rows[r].end <= at)
|
|
79668
|
+
r++;
|
|
79669
|
+
const hit = hits.length;
|
|
79670
|
+
hits.push(r);
|
|
79671
|
+
for (let k = r;k < hi; k++) {
|
|
79672
|
+
const row = rows[k];
|
|
79673
|
+
if (row.start >= end)
|
|
79674
|
+
break;
|
|
79675
|
+
const s = Math.max(at, row.start);
|
|
79676
|
+
const e = Math.min(end, row.end);
|
|
79677
|
+
if (e > s) {
|
|
79678
|
+
const list = ranges.get(k);
|
|
79679
|
+
if (list)
|
|
79680
|
+
list.push({ s: s - row.start, e: e - row.start, hit });
|
|
79681
|
+
else
|
|
79682
|
+
ranges.set(k, [{ s: s - row.start, e: e - row.start, hit }]);
|
|
79683
|
+
}
|
|
79684
|
+
}
|
|
79685
|
+
at = hay.indexOf(q, at + 1);
|
|
79686
|
+
}
|
|
79687
|
+
}
|
|
79688
|
+
return { hits, ranges, capped };
|
|
79689
|
+
}
|
|
79690
|
+
function ConversationReader({
|
|
79691
|
+
row,
|
|
79692
|
+
conv,
|
|
79693
|
+
width,
|
|
79694
|
+
height: height2,
|
|
79695
|
+
onClose,
|
|
79696
|
+
onResume,
|
|
79697
|
+
onCancel
|
|
79698
|
+
}) {
|
|
79699
|
+
const [top, setTop] = useState6(0);
|
|
79700
|
+
const [query, setQuery] = useState6("");
|
|
79701
|
+
const [typing, setTyping] = useState6(false);
|
|
79702
|
+
const [matchIdx, setMatchIdx] = useState6(0);
|
|
79703
|
+
const CHROME_ROWS = 5;
|
|
79704
|
+
const viewport = Math.max(1, height2 - CHROME_ROWS);
|
|
79705
|
+
const inner = Math.max(20, width - 2);
|
|
79706
|
+
const contentW = inner - BAR_W;
|
|
79707
|
+
const textW = Math.max(8, contentW - GUTTER - 1);
|
|
79708
|
+
const turns = useMemo3(() => conv?.turns ?? NO_TURNS, [conv]);
|
|
79709
|
+
const { rows, turnStart, turnEnd } = useMemo3(() => layoutRows(turns, textW), [turns, textW]);
|
|
79710
|
+
const search = useMemo3(() => buildSearch(turns, rows, turnStart, turnEnd, query), [turns, rows, turnStart, turnEnd, query]);
|
|
79711
|
+
const maxTop = Math.max(0, rows.length - viewport);
|
|
79712
|
+
const clampTop = (v) => Math.max(0, Math.min(v, maxTop));
|
|
79713
|
+
const reveal = (r) => setTop(clampTop(r - Math.floor(viewport / 3)));
|
|
79714
|
+
useEffect5(() => {
|
|
79715
|
+
setTop(Math.max(0, rows.length - viewport));
|
|
79716
|
+
}, [rows.length, viewport]);
|
|
79717
|
+
useEffect5(() => {
|
|
79718
|
+
if (search.hits.length === 0)
|
|
79719
|
+
return;
|
|
79720
|
+
let i = search.hits.findIndex((r) => r >= top);
|
|
79721
|
+
if (i === -1)
|
|
79722
|
+
i = 0;
|
|
79723
|
+
setMatchIdx(i);
|
|
79724
|
+
reveal(search.hits[i]);
|
|
79725
|
+
}, [search]);
|
|
79726
|
+
const step = (d) => {
|
|
79727
|
+
if (search.hits.length === 0)
|
|
79728
|
+
return;
|
|
79729
|
+
const i = (matchIdx + d + search.hits.length) % search.hits.length;
|
|
79730
|
+
setMatchIdx(i);
|
|
79731
|
+
reveal(search.hits[i]);
|
|
79732
|
+
};
|
|
79733
|
+
useKeyboard3((key) => {
|
|
79734
|
+
const name = key.name;
|
|
79735
|
+
if (key.ctrl && name === "c") {
|
|
79736
|
+
onCancel();
|
|
79737
|
+
return;
|
|
79738
|
+
}
|
|
79739
|
+
if (name === "escape") {
|
|
79740
|
+
if (typing || query) {
|
|
79741
|
+
setTyping(false);
|
|
79742
|
+
setQuery("");
|
|
79743
|
+
return;
|
|
79744
|
+
}
|
|
79745
|
+
onClose();
|
|
79746
|
+
return;
|
|
79747
|
+
}
|
|
79748
|
+
if (name === "return" || name === "enter") {
|
|
79749
|
+
if (typing) {
|
|
79750
|
+
setTyping(false);
|
|
79751
|
+
return;
|
|
79752
|
+
}
|
|
79753
|
+
onResume();
|
|
79754
|
+
return;
|
|
79755
|
+
}
|
|
79756
|
+
if (name === "up")
|
|
79757
|
+
return setTop((t) => clampTop(t - 1));
|
|
79758
|
+
if (name === "down")
|
|
79759
|
+
return setTop((t) => clampTop(t + 1));
|
|
79760
|
+
if (name === "pageup")
|
|
79761
|
+
return setTop((t) => clampTop(t - viewport));
|
|
79762
|
+
if (name === "pagedown")
|
|
79763
|
+
return setTop((t) => clampTop(t + viewport));
|
|
79764
|
+
if (name === "home")
|
|
79765
|
+
return setTop(0);
|
|
79766
|
+
if (name === "end")
|
|
79767
|
+
return setTop(maxTop);
|
|
79768
|
+
if (name === "backspace") {
|
|
79769
|
+
if (typing)
|
|
79770
|
+
setQuery((q) => q.slice(0, -1));
|
|
79771
|
+
return;
|
|
79772
|
+
}
|
|
79773
|
+
const ch = key.raw;
|
|
79774
|
+
const printable = ch && ch.length === 1 && ch >= " " && ch !== "\x7F";
|
|
79775
|
+
if (typing) {
|
|
79776
|
+
if (printable)
|
|
79777
|
+
setQuery((q) => q + ch);
|
|
79778
|
+
return;
|
|
79779
|
+
}
|
|
79780
|
+
if (name === "slash" || ch === "/") {
|
|
79781
|
+
setTyping(true);
|
|
79782
|
+
return;
|
|
79783
|
+
}
|
|
79784
|
+
if (name === "n")
|
|
79785
|
+
return step(key.shift ? -1 : 1);
|
|
79786
|
+
if (ch === "g")
|
|
79787
|
+
return setTop(0);
|
|
79788
|
+
if (ch === "G")
|
|
79789
|
+
return setTop(maxTop);
|
|
79790
|
+
if (name === "space")
|
|
79791
|
+
return setTop((t) => clampTop(t + viewport));
|
|
79792
|
+
});
|
|
79793
|
+
const label = sessionLabel(row);
|
|
79794
|
+
const mb = row.sizeBytes / 1048576;
|
|
79795
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
|
|
79796
|
+
const stats = conv ? `${turns.length} turns \xB7 ${rows.length} lines \xB7 ${size} transcript` : `reading ${size}\u2026`;
|
|
79797
|
+
const visible = rows.slice(top, top + viewport);
|
|
79798
|
+
const barCells = useMemo3(() => scrollbarCells(viewport, rows.length, top, search.hits), [viewport, rows.length, top, search.hits]);
|
|
79799
|
+
const pct = rows.length <= viewport ? 100 : top / maxTop * 100;
|
|
79800
|
+
const POSITION_COLS = 26;
|
|
79801
|
+
const matchLabel = search.hits.length ? `${matchIdx + 1} of ${search.hits.length}${search.capped ? "+" : ""} matches` : query ? "no matches" : "";
|
|
79802
|
+
return /* @__PURE__ */ jsxDEV19("box", {
|
|
79803
|
+
flexDirection: "column",
|
|
79804
|
+
height: height2,
|
|
79805
|
+
backgroundColor: C.bg,
|
|
79806
|
+
children: [
|
|
79807
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79808
|
+
flexDirection: "row",
|
|
79809
|
+
justifyContent: "space-between",
|
|
79810
|
+
height: 1,
|
|
79811
|
+
paddingX: 1,
|
|
79812
|
+
children: [
|
|
79813
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79814
|
+
children: [
|
|
79815
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79816
|
+
fg: tokens.accent,
|
|
79817
|
+
attributes: A.bold,
|
|
79818
|
+
children: "claudish"
|
|
79819
|
+
}, undefined, false, undefined, this),
|
|
79820
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79821
|
+
fg: tokens.subtle,
|
|
79822
|
+
children: " reader"
|
|
79823
|
+
}, undefined, false, undefined, this),
|
|
79824
|
+
conv && conv.dropped > 0 ? /* @__PURE__ */ jsxDEV19("span", {
|
|
79825
|
+
fg: tokens.warn,
|
|
79826
|
+
children: ` ${conv.dropped} older turns dropped (cap)`
|
|
79827
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this),
|
|
79828
|
+
conv?.anyElided ? /* @__PURE__ */ jsxDEV19("span", {
|
|
79829
|
+
fg: tokens.warn,
|
|
79830
|
+
children: " long turns truncated"
|
|
79831
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this)
|
|
79832
|
+
]
|
|
79833
|
+
}, undefined, true, undefined, this),
|
|
79834
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79835
|
+
children: /* @__PURE__ */ jsxDEV19("span", {
|
|
79836
|
+
fg: tokens.subtle,
|
|
79837
|
+
children: stats
|
|
79838
|
+
}, undefined, false, undefined, this)
|
|
79839
|
+
}, undefined, false, undefined, this)
|
|
79840
|
+
]
|
|
79841
|
+
}, undefined, true, undefined, this),
|
|
79842
|
+
/* @__PURE__ */ jsxDEV19(Panel, {
|
|
79843
|
+
title: `conversation \xB7 ${truncate3(label, Math.max(10, width - 20))}`,
|
|
79844
|
+
flush: true,
|
|
79845
|
+
flexGrow: 1,
|
|
79846
|
+
flexBasis: 0,
|
|
79847
|
+
children: !conv ? /* @__PURE__ */ jsxDEV19("text", {
|
|
79848
|
+
fg: tokens.subtle,
|
|
79849
|
+
children: " reading transcript\u2026"
|
|
79850
|
+
}, undefined, false, undefined, this) : rows.length === 0 ? /* @__PURE__ */ jsxDEV19("text", {
|
|
79851
|
+
fg: tokens.trace,
|
|
79852
|
+
children: " no conversation recorded \u2014 this transcript has no main-thread prose"
|
|
79853
|
+
}, undefined, false, undefined, this) : visible.map((r, i) => {
|
|
79854
|
+
const line = top + i;
|
|
79855
|
+
return /* @__PURE__ */ jsxDEV19(ReaderRow, {
|
|
79856
|
+
row: r,
|
|
79857
|
+
turn: r.turn >= 0 ? turns[r.turn] : undefined,
|
|
79858
|
+
hl: search.ranges.get(line),
|
|
79859
|
+
current: matchIdx,
|
|
79860
|
+
width: contentW,
|
|
79861
|
+
bar: barCells[i] ?? "track"
|
|
79862
|
+
}, line, false, undefined, this);
|
|
79863
|
+
})
|
|
79864
|
+
}, undefined, false, undefined, this),
|
|
79865
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79866
|
+
flexDirection: "row",
|
|
79867
|
+
justifyContent: "space-between",
|
|
79868
|
+
height: 1,
|
|
79869
|
+
paddingX: 1,
|
|
79870
|
+
children: [
|
|
79871
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79872
|
+
children: query || typing ? /* @__PURE__ */ jsxDEV19(Fragment12, {
|
|
79873
|
+
children: [
|
|
79874
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79875
|
+
fg: tokens.warn,
|
|
79876
|
+
attributes: A.bold,
|
|
79877
|
+
children: truncate3(`/${query}`, Math.max(4, width - POSITION_COLS - displayWidth(matchLabel) - 4))
|
|
79878
|
+
}, undefined, false, undefined, this),
|
|
79879
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79880
|
+
fg: typing ? tokens.warn : tokens.trace,
|
|
79881
|
+
children: typing ? "\u258C" : " "
|
|
79882
|
+
}, undefined, false, undefined, this),
|
|
79883
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79884
|
+
fg: search.hits.length ? tokens.subtle : tokens.trace,
|
|
79885
|
+
children: matchLabel ? ` ${matchLabel}` : ""
|
|
79886
|
+
}, undefined, false, undefined, this)
|
|
79887
|
+
]
|
|
79888
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79889
|
+
fg: tokens.trace,
|
|
79890
|
+
children: "/ to search"
|
|
79891
|
+
}, undefined, false, undefined, this)
|
|
79892
|
+
}, undefined, false, undefined, this),
|
|
79893
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79894
|
+
children: [
|
|
79895
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79896
|
+
fg: tokens.subtle,
|
|
79897
|
+
children: `${Math.min(rows.length, top + viewport)}/${rows.length} `
|
|
79898
|
+
}, undefined, false, undefined, this),
|
|
79899
|
+
/* @__PURE__ */ jsxDEV19(MeterSpan, {
|
|
79900
|
+
pct,
|
|
79901
|
+
width: 12,
|
|
79902
|
+
ramp: ramps.volume
|
|
79903
|
+
}, undefined, false, undefined, this)
|
|
79904
|
+
]
|
|
79905
|
+
}, undefined, true, undefined, this)
|
|
79906
|
+
]
|
|
79907
|
+
}, undefined, true, undefined, this),
|
|
79908
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79909
|
+
flexDirection: "row",
|
|
79910
|
+
height: 1,
|
|
79911
|
+
paddingX: 1,
|
|
79912
|
+
gap: 2,
|
|
79913
|
+
children: [
|
|
79914
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79915
|
+
fg: tokens.subtle,
|
|
79916
|
+
children: "\u2191\u2193 \u21DE\u21DF scroll"
|
|
79917
|
+
}, undefined, false, undefined, this),
|
|
79918
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79919
|
+
fg: tokens.subtle,
|
|
79920
|
+
children: "g/G ends"
|
|
79921
|
+
}, undefined, false, undefined, this),
|
|
79922
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79923
|
+
fg: typing ? tokens.warn : tokens.subtle,
|
|
79924
|
+
children: "/ search"
|
|
79925
|
+
}, undefined, false, undefined, this),
|
|
79926
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79927
|
+
fg: search.hits.length ? tokens.subtle : tokens.trace,
|
|
79928
|
+
children: "n/N match"
|
|
79929
|
+
}, undefined, false, undefined, this),
|
|
79930
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79931
|
+
fg: tokens.accent,
|
|
79932
|
+
children: "\u23CE resume"
|
|
79933
|
+
}, undefined, false, undefined, this),
|
|
79934
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79935
|
+
fg: tokens.subtle,
|
|
79936
|
+
children: "esc back"
|
|
79937
|
+
}, undefined, false, undefined, this)
|
|
79938
|
+
]
|
|
79939
|
+
}, undefined, true, undefined, this)
|
|
79940
|
+
]
|
|
79941
|
+
}, undefined, true, undefined, this);
|
|
79942
|
+
}
|
|
79943
|
+
function scrollbarCells(viewport, total, top, hits) {
|
|
79944
|
+
const cells = new Array(viewport).fill("track");
|
|
79945
|
+
if (total <= 0)
|
|
79946
|
+
return cells;
|
|
79947
|
+
if (total > viewport) {
|
|
79948
|
+
const size = Math.max(1, Math.round(viewport / total * viewport));
|
|
79949
|
+
const span = Math.max(1, total - viewport);
|
|
79950
|
+
const start = Math.min(viewport - size, Math.round(top / span * (viewport - size)));
|
|
79951
|
+
for (let i = 0;i < size; i++)
|
|
79952
|
+
cells[start + i] = "thumb";
|
|
79953
|
+
} else {
|
|
79954
|
+
cells.fill("thumb");
|
|
79955
|
+
}
|
|
79956
|
+
for (const h of hits) {
|
|
79957
|
+
const i = Math.min(viewport - 1, Math.floor(h / total * viewport));
|
|
79958
|
+
if (i >= 0)
|
|
79959
|
+
cells[i] = "hit";
|
|
79960
|
+
}
|
|
79961
|
+
return cells;
|
|
79962
|
+
}
|
|
79963
|
+
function ReaderRow({
|
|
79964
|
+
row,
|
|
79965
|
+
turn,
|
|
79966
|
+
hl,
|
|
79967
|
+
current,
|
|
79968
|
+
width,
|
|
79969
|
+
bar
|
|
79970
|
+
}) {
|
|
79971
|
+
if (!turn) {
|
|
79972
|
+
return /* @__PURE__ */ jsxDEV19("text", {
|
|
79973
|
+
children: [
|
|
79974
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79975
|
+
children: " ".repeat(Math.max(0, width))
|
|
79976
|
+
}, undefined, false, undefined, this),
|
|
79977
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79978
|
+
bg: BAR_COLOR[bar],
|
|
79979
|
+
children: " "
|
|
79980
|
+
}, undefined, false, undefined, this)
|
|
79981
|
+
]
|
|
79982
|
+
}, undefined, true, undefined, this);
|
|
79983
|
+
}
|
|
79984
|
+
const text = turn.text.slice(row.start, row.end);
|
|
79985
|
+
const fg = turn.role === "user" ? tokens.text : C.fgMuted;
|
|
79986
|
+
const railFg = turn.role === "user" ? SPEAKER.you : SPEAKER.ai;
|
|
79987
|
+
const pad2 = Math.max(0, width - GUTTER - displayWidth(text));
|
|
79988
|
+
return /* @__PURE__ */ jsxDEV19("text", {
|
|
79989
|
+
children: [
|
|
79990
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79991
|
+
fg: railFg,
|
|
79992
|
+
children: RAIL
|
|
79993
|
+
}, undefined, false, undefined, this),
|
|
79994
|
+
row.first ? /* @__PURE__ */ jsxDEV19(BadgeSpan, {
|
|
79995
|
+
label: turn.role === "user" ? "you" : "ai",
|
|
79996
|
+
bg: turn.role === "user" ? SPEAKER.you : SPEAKER.ai,
|
|
79997
|
+
width: ROLE_W
|
|
79998
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79999
|
+
children: " ".repeat(ROLE_W)
|
|
80000
|
+
}, undefined, false, undefined, this),
|
|
80001
|
+
hl && hl.length > 0 ? highlighted(text, hl, current, fg) : /* @__PURE__ */ jsxDEV19("span", {
|
|
80002
|
+
fg,
|
|
80003
|
+
children: text
|
|
80004
|
+
}, undefined, false, undefined, this),
|
|
80005
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
80006
|
+
children: " ".repeat(pad2)
|
|
80007
|
+
}, undefined, false, undefined, this),
|
|
80008
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
80009
|
+
bg: BAR_COLOR[bar],
|
|
80010
|
+
children: " "
|
|
80011
|
+
}, undefined, false, undefined, this)
|
|
80012
|
+
]
|
|
80013
|
+
}, undefined, true, undefined, this);
|
|
80014
|
+
}
|
|
80015
|
+
function highlighted(text, hl, current, fg) {
|
|
80016
|
+
const out = [];
|
|
80017
|
+
let from = 0;
|
|
80018
|
+
for (let i = 0;i < hl.length; i++) {
|
|
80019
|
+
const { s, e, hit } = hl[i];
|
|
80020
|
+
if (e <= from)
|
|
80021
|
+
continue;
|
|
80022
|
+
const start = Math.max(from, s);
|
|
80023
|
+
if (start > from)
|
|
80024
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
80025
|
+
fg,
|
|
80026
|
+
children: text.slice(from, start)
|
|
80027
|
+
}, `p${i}`, false, undefined, this));
|
|
80028
|
+
const bg = hit === current ? tokens.accent : tokens.warn;
|
|
80029
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
80030
|
+
fg: tokens.ink,
|
|
80031
|
+
bg,
|
|
80032
|
+
attributes: A.bold,
|
|
80033
|
+
children: text.slice(start, e)
|
|
80034
|
+
}, `h${i}`, false, undefined, this));
|
|
80035
|
+
from = e;
|
|
80036
|
+
}
|
|
80037
|
+
if (from < text.length)
|
|
80038
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
80039
|
+
fg,
|
|
80040
|
+
children: text.slice(from)
|
|
80041
|
+
}, "tail", false, undefined, this));
|
|
80042
|
+
return /* @__PURE__ */ jsxDEV19(Fragment12, {
|
|
80043
|
+
children: out
|
|
80044
|
+
}, undefined, false, undefined, this);
|
|
80045
|
+
}
|
|
80046
|
+
var RAIL = "\u258D", RAIL_W = 2, ROLE_W = 6, GUTTER, BAR_W = 1, SPEAKER, MAX_MATCHES = 5000, EMPTY_SEARCH, NO_TURNS, BAR_COLOR;
|
|
80047
|
+
var init_conversation_reader = __esm(() => {
|
|
80048
|
+
init_theme2();
|
|
80049
|
+
init_text();
|
|
80050
|
+
init_tokens();
|
|
80051
|
+
init_widgets();
|
|
80052
|
+
init_conversation();
|
|
80053
|
+
init_session_discovery();
|
|
80054
|
+
GUTTER = RAIL_W + ROLE_W;
|
|
80055
|
+
SPEAKER = {
|
|
80056
|
+
you: "#39d353",
|
|
80057
|
+
ai: "#39c5cf"
|
|
80058
|
+
};
|
|
80059
|
+
EMPTY_SEARCH = { hits: [], ranges: new Map, capped: false };
|
|
80060
|
+
NO_TURNS = [];
|
|
80061
|
+
BAR_COLOR = {
|
|
80062
|
+
track: tokens.border,
|
|
80063
|
+
thumb: tokens.accent,
|
|
80064
|
+
hit: tokens.warn
|
|
80065
|
+
};
|
|
80066
|
+
});
|
|
80067
|
+
|
|
80068
|
+
// src/session/resume-picker.tsx
|
|
80069
|
+
import { useKeyboard as useKeyboard4, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
|
|
80070
|
+
import { useEffect as useEffect6, useMemo as useMemo4, useState as useState7 } from "react";
|
|
80071
|
+
import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
|
|
80072
|
+
function detailTurns(height2) {
|
|
80073
|
+
return height2 >= 40 ? 6 : height2 >= 30 ? 4 : 2;
|
|
80074
|
+
}
|
|
80075
|
+
function fuzzy(needle, hay) {
|
|
80076
|
+
if (!needle)
|
|
80077
|
+
return true;
|
|
80078
|
+
const n = needle.toLowerCase();
|
|
80079
|
+
const h = hay.toLowerCase();
|
|
80080
|
+
let i = 0;
|
|
80081
|
+
for (let j = 0;j < h.length && i < n.length; j++)
|
|
80082
|
+
if (h[j] === n[i])
|
|
80083
|
+
i++;
|
|
80084
|
+
return i === n.length;
|
|
80085
|
+
}
|
|
80086
|
+
function age(ms) {
|
|
80087
|
+
const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
|
|
80088
|
+
if (s < 60)
|
|
80089
|
+
return `${s}s`;
|
|
80090
|
+
const m = Math.floor(s / 60);
|
|
80091
|
+
if (m < 60)
|
|
80092
|
+
return `${m}m`;
|
|
80093
|
+
const h = Math.floor(m / 60);
|
|
80094
|
+
if (h < 24)
|
|
80095
|
+
return `${h}h`;
|
|
80096
|
+
return `${Math.floor(h / 24)}d`;
|
|
80097
|
+
}
|
|
80098
|
+
function chipW(label) {
|
|
80099
|
+
return label + 2;
|
|
80100
|
+
}
|
|
80101
|
+
function blockWidth(c) {
|
|
80102
|
+
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);
|
|
80103
|
+
}
|
|
80104
|
+
function sizePct(bytes2, max) {
|
|
80105
|
+
const lo = Math.log(SIZE_FLOOR_BYTES);
|
|
80106
|
+
const hi = Math.log(Math.max(max, SIZE_FLOOR_BYTES * 2));
|
|
80107
|
+
const v = Math.log(Math.max(bytes2, SIZE_FLOOR_BYTES));
|
|
80108
|
+
return Math.max(0, Math.min(100, (v - lo) / (hi - lo) * 100));
|
|
80109
|
+
}
|
|
80110
|
+
function Slot({
|
|
80111
|
+
label,
|
|
80112
|
+
bg,
|
|
80113
|
+
labelW
|
|
80114
|
+
}) {
|
|
80115
|
+
if (!label || !bg)
|
|
80116
|
+
return /* @__PURE__ */ jsxDEV20("span", {
|
|
80117
|
+
children: " ".repeat(chipW(labelW))
|
|
80118
|
+
}, undefined, false, undefined, this);
|
|
80119
|
+
return /* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80120
|
+
label,
|
|
80121
|
+
bg
|
|
80122
|
+
}, undefined, false, undefined, this);
|
|
80123
|
+
}
|
|
80124
|
+
function WorktreeRow({
|
|
80125
|
+
g,
|
|
80126
|
+
cursor,
|
|
80127
|
+
width,
|
|
80128
|
+
count,
|
|
80129
|
+
cols
|
|
80130
|
+
}) {
|
|
80131
|
+
const nameColor = !g.live ? tokens.dead : g.current ? tokens.success : tokens.text;
|
|
80132
|
+
const stale = !g.lastActiveMs || Date.now() - g.lastActiveMs >= STALE_MS;
|
|
80133
|
+
const room = Math.max(0, width - blockWidth(cols));
|
|
80134
|
+
const series = room >= SPARK_MIN_DAYS ? activitySeries(g.sessions, room) : null;
|
|
80135
|
+
const spark = series && hasActivity(series) ? series : null;
|
|
80136
|
+
const chips = [];
|
|
80137
|
+
if (cols.sync > 0 && g.ahead) {
|
|
80138
|
+
chips.push({
|
|
80139
|
+
label: `\u2191${padStartTo(String(g.ahead), cols.sync)}`,
|
|
80140
|
+
bg: CHIP.ahead,
|
|
80141
|
+
labelW: cols.sync + 1
|
|
80142
|
+
});
|
|
80143
|
+
}
|
|
80144
|
+
if (cols.sync > 0 && g.behind) {
|
|
80145
|
+
chips.push({
|
|
80146
|
+
label: `\u2193${padStartTo(String(g.behind), cols.sync)}`,
|
|
80147
|
+
bg: CHIP.behind,
|
|
80148
|
+
labelW: cols.sync + 1
|
|
80149
|
+
});
|
|
80150
|
+
}
|
|
80151
|
+
if (cols.dirty > 0 && g.dirty) {
|
|
80152
|
+
chips.push({
|
|
80153
|
+
label: `${DIRTY_GLYPH}${padStartTo(String(g.dirty), cols.dirty)}`,
|
|
80154
|
+
bg: CHIP.dirty,
|
|
80155
|
+
labelW: cols.dirty + 1
|
|
80156
|
+
});
|
|
80157
|
+
}
|
|
80158
|
+
chips.push({ label: padStartTo(String(count), cols.count), bg: CHIP.count, labelW: cols.count });
|
|
80159
|
+
chips.push({
|
|
80160
|
+
label: padStartTo(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014", cols.age),
|
|
80161
|
+
bg: stale ? CHIP.stale : CHIP.fresh,
|
|
80162
|
+
labelW: cols.age
|
|
80163
|
+
});
|
|
80164
|
+
const used = chips.reduce((w, c) => w + c.labelW + 2, 0) + LIVE_W;
|
|
80165
|
+
const gap = Math.max(0, width - room - used);
|
|
80166
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80167
|
+
flexDirection: "column",
|
|
80168
|
+
height: 2,
|
|
80169
|
+
backgroundColor: cursor ? C.bgHighlight : undefined,
|
|
80170
|
+
children: [
|
|
80171
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80172
|
+
children: /* @__PURE__ */ jsxDEV20("span", {
|
|
80173
|
+
fg: nameColor,
|
|
80174
|
+
attributes: cursor || g.current ? A.bold : undefined,
|
|
80175
|
+
children: truncate3(g.name, width)
|
|
80176
|
+
}, undefined, false, undefined, this)
|
|
80177
|
+
}, undefined, false, undefined, this),
|
|
80178
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80179
|
+
height: 1,
|
|
80180
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80181
|
+
children: [
|
|
80182
|
+
spark ? /* @__PURE__ */ jsxDEV20(SparklineSpan, {
|
|
80183
|
+
values: spark,
|
|
80184
|
+
fg: SPARK_FG
|
|
80185
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {
|
|
80186
|
+
children: " ".repeat(room)
|
|
80187
|
+
}, undefined, false, undefined, this),
|
|
80188
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80189
|
+
children: " ".repeat(gap)
|
|
80190
|
+
}, undefined, false, undefined, this),
|
|
80191
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80192
|
+
fg: tokens.success,
|
|
80193
|
+
children: g.activeNow ? "\u25CF " : " "
|
|
80194
|
+
}, undefined, false, undefined, this),
|
|
80195
|
+
chips.map((c) => /* @__PURE__ */ jsxDEV20(Slot, {
|
|
80196
|
+
label: c.label,
|
|
80197
|
+
bg: c.bg,
|
|
80198
|
+
labelW: c.labelW
|
|
80199
|
+
}, c.bg + c.label, false, undefined, this))
|
|
80200
|
+
]
|
|
80201
|
+
}, undefined, true, undefined, this)
|
|
80202
|
+
}, undefined, false, undefined, this)
|
|
80203
|
+
]
|
|
80204
|
+
}, undefined, true, undefined, this);
|
|
80205
|
+
}
|
|
80206
|
+
function dailyActivity(groups, days) {
|
|
80207
|
+
const day = 86400000;
|
|
80208
|
+
const today = Math.floor(Date.now() / day);
|
|
80209
|
+
const buckets = new Array(days).fill(0);
|
|
80210
|
+
for (const g of groups) {
|
|
80211
|
+
for (const s of g.sessions) {
|
|
80212
|
+
const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
|
|
80213
|
+
if (idx >= 0 && idx < days)
|
|
80214
|
+
buckets[idx] += 1;
|
|
80215
|
+
}
|
|
80216
|
+
}
|
|
80217
|
+
return buckets;
|
|
80218
|
+
}
|
|
80219
|
+
function activityLevels(days) {
|
|
80220
|
+
const nz = days.filter((v) => v > 0).sort((a, b) => a - b);
|
|
80221
|
+
if (nz.length === 0)
|
|
80222
|
+
return days.map(() => 0);
|
|
80223
|
+
const at = (p) => nz[Math.min(nz.length - 1, Math.floor(nz.length * p))];
|
|
80224
|
+
const q1 = at(0.25);
|
|
80225
|
+
const q2 = at(0.5);
|
|
80226
|
+
const q3 = at(0.75);
|
|
80227
|
+
return days.map((v) => v <= 0 ? 0 : v <= q1 ? 1 : v <= q2 ? 2 : v <= q3 ? 3 : 4);
|
|
80228
|
+
}
|
|
80229
|
+
function ActivityCalendar({
|
|
80230
|
+
days,
|
|
80231
|
+
width
|
|
80232
|
+
}) {
|
|
80233
|
+
const levels = activityLevels(days);
|
|
80234
|
+
const title = `activity \xB7 ${ACTIVITY_WEEKS}w `;
|
|
80235
|
+
const grid = Math.max(WEEK_DAYS, width - WEEK_LABEL_W);
|
|
80236
|
+
const base = Math.floor(grid / WEEK_DAYS);
|
|
80237
|
+
const extra = grid - base * WEEK_DAYS;
|
|
80238
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80239
|
+
flexDirection: "column",
|
|
80240
|
+
flexShrink: 0,
|
|
80241
|
+
paddingTop: 1,
|
|
80242
|
+
children: [
|
|
80243
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80244
|
+
children: [
|
|
80245
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80246
|
+
fg: tokens.subtle,
|
|
80247
|
+
attributes: A.bold,
|
|
80248
|
+
children: title
|
|
80249
|
+
}, undefined, false, undefined, this),
|
|
80250
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80251
|
+
fg: tokens.border,
|
|
80252
|
+
children: "\u2500".repeat(Math.max(0, width - title.length))
|
|
80253
|
+
}, undefined, false, undefined, this)
|
|
80254
|
+
]
|
|
80255
|
+
}, undefined, true, undefined, this),
|
|
80256
|
+
Array.from({ length: ACTIVITY_WEEKS }, (_, w) => {
|
|
80257
|
+
const ago = ACTIVITY_WEEKS - 1 - w;
|
|
80258
|
+
return /* @__PURE__ */ jsxDEV20("text", {
|
|
80259
|
+
children: [
|
|
80260
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80261
|
+
fg: tokens.trace,
|
|
80262
|
+
children: padTo(ago === 0 ? "now" : `-${ago}w`, WEEK_LABEL_W)
|
|
80263
|
+
}, undefined, false, undefined, this),
|
|
80264
|
+
Array.from({ length: WEEK_DAYS }, (_2, d) => /* @__PURE__ */ jsxDEV20("span", {
|
|
80265
|
+
bg: GH_LEVELS[levels[w * WEEK_DAYS + d] ?? 0],
|
|
80266
|
+
children: " ".repeat(base + (d < extra ? 1 : 0))
|
|
80267
|
+
}, d, false, undefined, this))
|
|
80268
|
+
]
|
|
80269
|
+
}, w, true, undefined, this);
|
|
80270
|
+
})
|
|
80271
|
+
]
|
|
80272
|
+
}, undefined, true, undefined, this);
|
|
80273
|
+
}
|
|
80274
|
+
function SectionHeader({ label, width }) {
|
|
80275
|
+
const text = `${label} `;
|
|
80276
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80277
|
+
flexDirection: "column",
|
|
80278
|
+
height: 2,
|
|
80279
|
+
children: [
|
|
80280
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80281
|
+
children: " "
|
|
80282
|
+
}, undefined, false, undefined, this),
|
|
80283
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80284
|
+
children: [
|
|
80285
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80286
|
+
fg: tokens.subtle,
|
|
80287
|
+
attributes: A.bold,
|
|
80288
|
+
children: text
|
|
80289
|
+
}, undefined, false, undefined, this),
|
|
80290
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80291
|
+
fg: tokens.border,
|
|
80292
|
+
children: "\u2500".repeat(Math.max(0, width - text.length))
|
|
80293
|
+
}, undefined, false, undefined, this)
|
|
80294
|
+
]
|
|
80295
|
+
}, undefined, true, undefined, this)
|
|
80296
|
+
]
|
|
80297
|
+
}, undefined, true, undefined, this);
|
|
80298
|
+
}
|
|
80299
|
+
function hasActivity(series) {
|
|
80300
|
+
return series.some((v) => v > 0);
|
|
80301
|
+
}
|
|
80302
|
+
function activitySeries(sessions2, days = WEEK_DAYS * 2) {
|
|
80303
|
+
const day = 86400000;
|
|
80304
|
+
const today = Math.floor(Date.now() / day);
|
|
80305
|
+
const buckets = new Array(days).fill(0);
|
|
80306
|
+
for (const s of sessions2) {
|
|
80307
|
+
const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
|
|
80308
|
+
if (idx >= 0 && idx < days)
|
|
80309
|
+
buckets[idx] += 1;
|
|
80310
|
+
}
|
|
80311
|
+
return buckets;
|
|
80312
|
+
}
|
|
80313
|
+
function SessionRowView({
|
|
80314
|
+
row,
|
|
80315
|
+
cursor,
|
|
80316
|
+
width,
|
|
80317
|
+
even,
|
|
80318
|
+
indent = 0,
|
|
80319
|
+
maxSize,
|
|
80320
|
+
meterW
|
|
80321
|
+
}) {
|
|
80322
|
+
const live = isActive(row);
|
|
80323
|
+
const pad2 = " ".repeat(indent);
|
|
80324
|
+
const mb = row.sizeBytes / 1048576;
|
|
80325
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
|
|
80326
|
+
const titleW = Math.max(10, width - indent - 2);
|
|
80327
|
+
const metaPad = `${pad2} `;
|
|
80328
|
+
const SIZE_COL = 8;
|
|
80329
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80330
|
+
flexDirection: "column",
|
|
80331
|
+
height: 2,
|
|
80332
|
+
backgroundColor: cursor ? C.bgHighlight : even ? undefined : C.bgAlt,
|
|
80333
|
+
children: [
|
|
80334
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80335
|
+
children: [
|
|
80336
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80337
|
+
fg: live ? tokens.success : tokens.border,
|
|
80338
|
+
children: `${pad2}${live ? "\u25CF" : "\xB7"} `
|
|
80339
|
+
}, undefined, false, undefined, this),
|
|
80340
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80341
|
+
fg: tokens.text,
|
|
80342
|
+
attributes: cursor ? A.bold : undefined,
|
|
80343
|
+
children: truncate3(sessionLabel(row), titleW)
|
|
80344
|
+
}, undefined, false, undefined, this)
|
|
80345
|
+
]
|
|
80346
|
+
}, undefined, true, undefined, this),
|
|
80347
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80348
|
+
children: [
|
|
80349
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80350
|
+
children: metaPad
|
|
80351
|
+
}, undefined, false, undefined, this),
|
|
80352
|
+
/* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80353
|
+
label: padStartTo(age(row.mtimeMs), SESSION_AGE_W),
|
|
80354
|
+
bg: Date.now() - row.mtimeMs < STALE_MS ? CHIP.fresh : CHIP.stale,
|
|
80355
|
+
width: SESSION_AGE_COL
|
|
80356
|
+
}, undefined, false, undefined, this),
|
|
80357
|
+
/* @__PURE__ */ jsxDEV20(MeterSpan, {
|
|
80358
|
+
pct: sizePct(row.sizeBytes, maxSize),
|
|
80359
|
+
width: meterW,
|
|
80360
|
+
ramp: ramps.volume
|
|
80361
|
+
}, undefined, false, undefined, this),
|
|
80362
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80363
|
+
fg: MUTED,
|
|
80364
|
+
children: padStartTo(size, SIZE_COL)
|
|
80365
|
+
}, undefined, false, undefined, this),
|
|
80366
|
+
row.gitBranch ? /* @__PURE__ */ jsxDEV20("span", {
|
|
80367
|
+
fg: tokens.trace,
|
|
80368
|
+
children: ` ${BRANCH_ICON} ${truncate3(row.gitBranch, Math.max(6, width - indent - 2 - SESSION_AGE_COL - meterW - SIZE_COL - BRANCH_LEAD_DENSE))}`
|
|
80369
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
|
|
80370
|
+
]
|
|
80371
|
+
}, undefined, true, undefined, this)
|
|
80372
|
+
]
|
|
80373
|
+
}, undefined, true, undefined, this);
|
|
80374
|
+
}
|
|
80375
|
+
function AgentNode({
|
|
80376
|
+
count,
|
|
80377
|
+
open,
|
|
80378
|
+
cursor,
|
|
80379
|
+
width
|
|
80380
|
+
}) {
|
|
80381
|
+
const label = `${count} agent session${count === 1 ? "" : "s"}`;
|
|
80382
|
+
const hint = open ? "enter to collapse" : "enter to expand \xB7 showing 3";
|
|
80383
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80384
|
+
height: 1,
|
|
80385
|
+
backgroundColor: cursor ? C.bgHighlight : undefined,
|
|
80386
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80387
|
+
children: [
|
|
80388
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80389
|
+
fg: cursor ? tokens.accent : tokens.trace,
|
|
80390
|
+
children: `${cursor ? "\u258D" : " "} `
|
|
80391
|
+
}, undefined, false, undefined, this),
|
|
80392
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80393
|
+
fg: tokens.warn,
|
|
80394
|
+
children: open ? "\u25BE " : "\u25B8 "
|
|
80395
|
+
}, undefined, false, undefined, this),
|
|
80396
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80397
|
+
fg: cursor ? tokens.text : tokens.subtle,
|
|
80398
|
+
children: label
|
|
80399
|
+
}, undefined, false, undefined, this),
|
|
80400
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80401
|
+
fg: tokens.trace,
|
|
80402
|
+
children: truncate3(` ${hint}`, Math.max(0, width - label.length - 6))
|
|
80403
|
+
}, undefined, false, undefined, this)
|
|
80404
|
+
]
|
|
80405
|
+
}, undefined, true, undefined, this)
|
|
80406
|
+
}, undefined, false, undefined, this);
|
|
80407
|
+
}
|
|
80408
|
+
function ResumePicker({ groups, onDone }) {
|
|
80409
|
+
const { width, height: height2 } = useTerminalDimensions3();
|
|
80410
|
+
const [pane, setPane] = useState7("worktrees");
|
|
80411
|
+
const [wtCursor, setWtCursor] = useState7(0);
|
|
80412
|
+
const [sessCursor, setSessCursor] = useState7(0);
|
|
80413
|
+
const [filter, setFilter] = useState7("");
|
|
80414
|
+
const [agentsOpen, setAgentsOpen] = useState7(false);
|
|
80415
|
+
const [reader, setReader] = useState7(null);
|
|
80416
|
+
const [, setTick] = useState7(0);
|
|
80417
|
+
const listed = useMemo4(() => {
|
|
80418
|
+
const m = new Map;
|
|
80419
|
+
for (const g of groups)
|
|
80420
|
+
m.set(g.name, g.sessions.filter((s) => !isAgentSession(s)));
|
|
80421
|
+
return m;
|
|
80422
|
+
}, [groups]);
|
|
80423
|
+
const { fresh, stale, visibleGroups } = useMemo4(() => {
|
|
80424
|
+
const withSessions = groups.filter((g) => (listed.get(g.name)?.length ?? 0) > 0);
|
|
80425
|
+
const matching = filter ? withSessions.filter((g) => fuzzy(filter, g.name)) : withSessions;
|
|
80426
|
+
const now = Date.now();
|
|
80427
|
+
const byRecency = (a, b) => b.lastActiveMs - a.lastActiveMs;
|
|
80428
|
+
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));
|
|
80429
|
+
const st = matching.filter((g) => !g.current && now - g.lastActiveMs >= STALE_MS).sort(byRecency);
|
|
80430
|
+
return { fresh: f, stale: st, visibleGroups: [...f, ...st] };
|
|
80431
|
+
}, [groups, filter, listed]);
|
|
80432
|
+
const group = visibleGroups[Math.min(wtCursor, visibleGroups.length - 1)];
|
|
80433
|
+
const sessions2 = useMemo4(() => {
|
|
80434
|
+
if (!group)
|
|
80435
|
+
return [];
|
|
80436
|
+
const base = listed.get(group.name) ?? [];
|
|
80437
|
+
if (!filter)
|
|
80438
|
+
return base;
|
|
80439
|
+
if (fuzzy(filter, group.name))
|
|
80440
|
+
return base;
|
|
80441
|
+
return base.filter((s) => fuzzy(filter, sessionLabel(s)));
|
|
80442
|
+
}, [group, filter, listed]);
|
|
80443
|
+
const agentRows = useMemo4(() => group ? group.sessions.filter(isAgentSession) : [], [group]);
|
|
80444
|
+
const items = useMemo4(() => {
|
|
80445
|
+
const out = sessions2.map((row) => ({ kind: "session", row }));
|
|
80446
|
+
if (agentRows.length > 0 && !filter) {
|
|
80447
|
+
out.push({ kind: "agents", count: agentRows.length });
|
|
80448
|
+
for (const row of agentsOpen ? agentRows : agentRows.slice(0, 3)) {
|
|
80449
|
+
out.push({ kind: "agent", row });
|
|
80450
|
+
}
|
|
80451
|
+
}
|
|
80452
|
+
return out;
|
|
80453
|
+
}, [sessions2, agentRows, agentsOpen, filter]);
|
|
80454
|
+
const cursorItem = items[Math.min(sessCursor, items.length - 1)];
|
|
80455
|
+
const selected = cursorItem && cursorItem.kind !== "agents" ? cursorItem.row : undefined;
|
|
80456
|
+
const turns = detailTurns(height2);
|
|
80457
|
+
const sessionDetailH = DETAIL_CHROME + turns;
|
|
80458
|
+
const listRows = Math.max(3, height2 - sessionDetailH - WORKTREE_DETAIL_H - 4);
|
|
80459
|
+
useEffect6(() => {
|
|
80460
|
+
const start = Math.max(0, Math.min(sessCursor - 2, items.length - listRows));
|
|
80461
|
+
for (const it of items.slice(start, start + listRows + 2)) {
|
|
80462
|
+
if (it.kind !== "agents")
|
|
80463
|
+
hydrateSession(it.row);
|
|
80464
|
+
}
|
|
80465
|
+
if (selected) {
|
|
80466
|
+
hydrateSession(selected);
|
|
80467
|
+
hydrateConversation(selected);
|
|
80468
|
+
}
|
|
80469
|
+
setTick((t) => t + 1);
|
|
80470
|
+
}, [items, sessCursor, listRows, selected]);
|
|
80471
|
+
useEffect6(() => {
|
|
80472
|
+
if (!reader || reader.conv)
|
|
80473
|
+
return;
|
|
80474
|
+
const file2 = reader.row.file;
|
|
80475
|
+
const timer = setTimeout(() => {
|
|
80476
|
+
const conv = readConversation(file2);
|
|
80477
|
+
setReader((r) => r && r.row.file === file2 && !r.conv ? { row: r.row, conv } : r);
|
|
80478
|
+
}, 0);
|
|
80479
|
+
return () => clearTimeout(timer);
|
|
80480
|
+
}, [reader]);
|
|
80481
|
+
const clamp = (v, len) => Math.max(0, Math.min(v, len - 1));
|
|
80482
|
+
useKeyboard4((key) => {
|
|
80483
|
+
if (reader)
|
|
80484
|
+
return;
|
|
80485
|
+
const name = key.name;
|
|
80486
|
+
if (name === "escape") {
|
|
80487
|
+
if (filter) {
|
|
80488
|
+
setFilter("");
|
|
80489
|
+
return;
|
|
80490
|
+
}
|
|
80491
|
+
onDone(null);
|
|
80492
|
+
return;
|
|
80493
|
+
}
|
|
80494
|
+
if (key.ctrl && name === "c") {
|
|
80495
|
+
onDone(null);
|
|
80496
|
+
return;
|
|
80497
|
+
}
|
|
80498
|
+
if (name === "return" || name === "enter") {
|
|
80499
|
+
if (pane === "worktrees") {
|
|
80500
|
+
setPane("sessions");
|
|
80501
|
+
setSessCursor(0);
|
|
80502
|
+
return;
|
|
80503
|
+
}
|
|
80504
|
+
if (cursorItem?.kind === "agents") {
|
|
80505
|
+
setAgentsOpen((v) => !v);
|
|
80506
|
+
return;
|
|
80507
|
+
}
|
|
80508
|
+
if (selected)
|
|
80509
|
+
onDone(selected.id);
|
|
80510
|
+
return;
|
|
80511
|
+
}
|
|
80512
|
+
if (name === "tab") {
|
|
80513
|
+
setPane((p) => p === "worktrees" ? "sessions" : "worktrees");
|
|
80514
|
+
return;
|
|
80515
|
+
}
|
|
80516
|
+
if (name === "left") {
|
|
80517
|
+
setPane("worktrees");
|
|
80518
|
+
return;
|
|
80519
|
+
}
|
|
80520
|
+
if (name === "right") {
|
|
80521
|
+
setPane("sessions");
|
|
80522
|
+
return;
|
|
80523
|
+
}
|
|
80524
|
+
if (name === "up" || name === "down") {
|
|
80525
|
+
const d = name === "up" ? -1 : 1;
|
|
80526
|
+
if (pane === "worktrees") {
|
|
80527
|
+
setWtCursor((c) => clamp(c + d, visibleGroups.length));
|
|
80528
|
+
setSessCursor(0);
|
|
80529
|
+
} else {
|
|
80530
|
+
setSessCursor((c) => clamp(c + d, items.length));
|
|
80531
|
+
}
|
|
80532
|
+
return;
|
|
80533
|
+
}
|
|
80534
|
+
if (name === "a" && !key.ctrl && !key.meta) {
|
|
80535
|
+
setAgentsOpen((v) => !v);
|
|
80536
|
+
return;
|
|
80537
|
+
}
|
|
80538
|
+
if (name === "v" && !key.ctrl && !key.meta) {
|
|
80539
|
+
if (selected)
|
|
80540
|
+
setReader({ row: selected, conv: null });
|
|
80541
|
+
return;
|
|
80542
|
+
}
|
|
80543
|
+
if (name === "backspace") {
|
|
80544
|
+
setFilter((f) => f.slice(0, -1));
|
|
80545
|
+
setWtCursor(0);
|
|
80546
|
+
setSessCursor(0);
|
|
80547
|
+
return;
|
|
80548
|
+
}
|
|
80549
|
+
const ch = key.raw;
|
|
80550
|
+
if (ch && ch.length === 1 && ch >= " " && ch !== "\x7F" && ch !== "a" && ch !== "v") {
|
|
80551
|
+
setFilter((f) => f + ch);
|
|
80552
|
+
setWtCursor(0);
|
|
80553
|
+
setSessCursor(0);
|
|
80554
|
+
}
|
|
80555
|
+
});
|
|
80556
|
+
const bodyW = width;
|
|
80557
|
+
const sidebarW = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, Math.round(width * 0.34)));
|
|
80558
|
+
const rightW = Math.max(30, bodyW - sidebarW);
|
|
80559
|
+
const sessInner = rightW - PANEL_BORDER - SCROLL_CHROME;
|
|
80560
|
+
const sideInner = sidebarW - PANEL_BORDER - SCROLL_CHROME - 1;
|
|
80561
|
+
const sessionDetailInner = rightW - PANEL_CHROME;
|
|
80562
|
+
const worktreeDetailInner = width - PANEL_CHROME;
|
|
80563
|
+
const chipCols = useMemo4(() => {
|
|
80564
|
+
let cols = { age: 1, count: 1, dirty: 0, sync: 0 };
|
|
80565
|
+
for (const g of visibleGroups) {
|
|
80566
|
+
cols.age = Math.max(cols.age, displayWidth(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014"));
|
|
80567
|
+
cols.count = Math.max(cols.count, String(listed.get(g.name)?.length ?? 0).length);
|
|
80568
|
+
if (g.dirty)
|
|
80569
|
+
cols.dirty = Math.max(cols.dirty, String(g.dirty).length);
|
|
80570
|
+
if (g.ahead)
|
|
80571
|
+
cols.sync = Math.max(cols.sync, String(g.ahead).length);
|
|
80572
|
+
if (g.behind)
|
|
80573
|
+
cols.sync = Math.max(cols.sync, String(g.behind).length);
|
|
80574
|
+
}
|
|
80575
|
+
if (blockWidth(cols) > sideInner)
|
|
80576
|
+
cols = { ...cols, sync: 0 };
|
|
80577
|
+
if (blockWidth(cols) > sideInner)
|
|
80578
|
+
cols = { ...cols, dirty: 0 };
|
|
80579
|
+
return cols;
|
|
80580
|
+
}, [visibleGroups, listed, sideInner]);
|
|
80581
|
+
const maxSize = Math.max(1, ...sessions2.slice(0, 400).map((s) => s.sizeBytes));
|
|
80582
|
+
const meterW = Math.max(6, Math.min(14, Math.round(sessInner * 0.16)));
|
|
80583
|
+
const sidebarInnerH = height2 - 1 - WORKTREE_DETAIL_H - 1 - PANEL_BORDER;
|
|
80584
|
+
const listContentH = visibleGroups.length * 2 + (stale.length > 0 ? 2 : 0);
|
|
80585
|
+
const showActivity = sidebarInnerH - listContentH >= ACTIVITY_H;
|
|
80586
|
+
const activityDays = useMemo4(() => dailyActivity(visibleGroups, ACTIVITY_WEEKS * WEEK_DAYS), [visibleGroups]);
|
|
80587
|
+
const shownSessions = [...listed.values()].reduce((a, v) => a + v.length, 0);
|
|
80588
|
+
if (reader) {
|
|
80589
|
+
return /* @__PURE__ */ jsxDEV20(ConversationReader, {
|
|
80590
|
+
row: reader.row,
|
|
80591
|
+
conv: reader.conv,
|
|
80592
|
+
width,
|
|
80593
|
+
height: height2,
|
|
80594
|
+
onClose: () => setReader(null),
|
|
80595
|
+
onResume: () => onDone(reader.row.id),
|
|
80596
|
+
onCancel: () => onDone(null)
|
|
80597
|
+
}, undefined, false, undefined, this);
|
|
80598
|
+
}
|
|
80599
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80600
|
+
flexDirection: "column",
|
|
80601
|
+
height: height2,
|
|
80602
|
+
backgroundColor: C.bg,
|
|
80603
|
+
children: [
|
|
80604
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80605
|
+
flexDirection: "row",
|
|
80606
|
+
justifyContent: "space-between",
|
|
80607
|
+
height: 1,
|
|
80608
|
+
paddingX: 1,
|
|
80609
|
+
children: [
|
|
80610
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80611
|
+
children: [
|
|
80612
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80613
|
+
fg: tokens.accent,
|
|
80614
|
+
attributes: 1,
|
|
80615
|
+
children: "claudish"
|
|
80616
|
+
}, undefined, false, undefined, this),
|
|
80617
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80618
|
+
fg: tokens.subtle,
|
|
80619
|
+
children: " resume"
|
|
80620
|
+
}, undefined, false, undefined, this)
|
|
80621
|
+
]
|
|
80622
|
+
}, undefined, true, undefined, this),
|
|
80623
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80624
|
+
children: [
|
|
80625
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80626
|
+
fg: tokens.subtle,
|
|
80627
|
+
children: `${visibleGroups.length} worktrees \xB7 ${shownSessions} sessions`
|
|
80628
|
+
}, undefined, false, undefined, this),
|
|
80629
|
+
filter ? /* @__PURE__ */ jsxDEV20("span", {
|
|
80630
|
+
fg: tokens.warn,
|
|
80631
|
+
children: ` /${filter}`
|
|
80632
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
|
|
80633
|
+
]
|
|
80634
|
+
}, undefined, true, undefined, this)
|
|
80635
|
+
]
|
|
80636
|
+
}, undefined, true, undefined, this),
|
|
80637
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80638
|
+
flexDirection: "row",
|
|
80639
|
+
flexGrow: 1,
|
|
80640
|
+
minHeight: 0,
|
|
80641
|
+
children: [
|
|
80642
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80643
|
+
width: sidebarW,
|
|
80644
|
+
flexDirection: "column",
|
|
80645
|
+
minHeight: 0,
|
|
80646
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80647
|
+
title: "worktrees",
|
|
80648
|
+
focused: pane === "worktrees",
|
|
80649
|
+
flush: true,
|
|
80650
|
+
flexGrow: 1,
|
|
80651
|
+
flexBasis: 0,
|
|
80652
|
+
children: [
|
|
80653
|
+
/* @__PURE__ */ jsxDEV20("scrollbox", {
|
|
80654
|
+
focused: false,
|
|
80655
|
+
flexGrow: 1,
|
|
80656
|
+
scrollbarOptions: SCROLLBAR,
|
|
80657
|
+
children: [
|
|
80658
|
+
fresh.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
|
|
80659
|
+
g,
|
|
80660
|
+
cursor: i === wtCursor,
|
|
80661
|
+
width: sideInner,
|
|
80662
|
+
count: listed.get(g.name)?.length ?? 0,
|
|
80663
|
+
cols: chipCols
|
|
80664
|
+
}, g.name, false, undefined, this)),
|
|
80665
|
+
stale.length > 0 ? /* @__PURE__ */ jsxDEV20(SectionHeader, {
|
|
80666
|
+
label: `stale \xB7 ${stale.length} \xB7 idle 3d+`,
|
|
80667
|
+
width: sideInner
|
|
80668
|
+
}, undefined, false, undefined, this) : null,
|
|
80669
|
+
stale.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
|
|
80670
|
+
g,
|
|
80671
|
+
cursor: fresh.length + i === wtCursor,
|
|
80672
|
+
width: sideInner,
|
|
80673
|
+
count: listed.get(g.name)?.length ?? 0,
|
|
80674
|
+
cols: chipCols
|
|
80675
|
+
}, g.name, false, undefined, this))
|
|
80676
|
+
]
|
|
80677
|
+
}, undefined, true, undefined, this),
|
|
80678
|
+
showActivity ? /* @__PURE__ */ jsxDEV20(ActivityCalendar, {
|
|
80679
|
+
days: activityDays,
|
|
80680
|
+
width: sideInner
|
|
80681
|
+
}, undefined, false, undefined, this) : null
|
|
80682
|
+
]
|
|
80683
|
+
}, undefined, true, undefined, this)
|
|
80684
|
+
}, undefined, false, undefined, this),
|
|
80685
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80686
|
+
flexDirection: "column",
|
|
80687
|
+
flexGrow: 1,
|
|
80688
|
+
minWidth: 0,
|
|
80689
|
+
minHeight: 0,
|
|
80690
|
+
children: [
|
|
80691
|
+
/* @__PURE__ */ jsxDEV20(Panel, {
|
|
80692
|
+
title: group ? `sessions \xB7 ${truncate3(group.name, 28)}` : "sessions",
|
|
80693
|
+
focused: pane === "sessions",
|
|
80694
|
+
flush: true,
|
|
80695
|
+
flexGrow: 1,
|
|
80696
|
+
flexBasis: 0,
|
|
80697
|
+
children: /* @__PURE__ */ jsxDEV20("scrollbox", {
|
|
80698
|
+
focused: false,
|
|
80699
|
+
flexGrow: 1,
|
|
80700
|
+
scrollbarOptions: SCROLLBAR,
|
|
80701
|
+
children: items.length === 0 ? /* @__PURE__ */ jsxDEV20("text", {
|
|
80702
|
+
fg: tokens.subtle,
|
|
80703
|
+
children: " no sessions match"
|
|
80704
|
+
}, undefined, false, undefined, this) : items.map((it, i) => it.kind === "agents" ? /* @__PURE__ */ jsxDEV20(AgentNode, {
|
|
80705
|
+
count: it.count,
|
|
80706
|
+
open: agentsOpen,
|
|
80707
|
+
cursor: i === sessCursor && pane === "sessions",
|
|
80708
|
+
width: sessInner
|
|
80709
|
+
}, "agents", false, undefined, this) : /* @__PURE__ */ jsxDEV20(SessionRowView, {
|
|
80710
|
+
row: it.row,
|
|
80711
|
+
cursor: i === sessCursor && pane === "sessions",
|
|
80712
|
+
width: sessInner,
|
|
80713
|
+
even: i % 2 === 0,
|
|
80714
|
+
indent: it.kind === "agent" ? 2 : 0,
|
|
80715
|
+
maxSize,
|
|
80716
|
+
meterW
|
|
80717
|
+
}, it.row.id, false, undefined, this))
|
|
80718
|
+
}, undefined, false, undefined, this)
|
|
80719
|
+
}, undefined, false, undefined, this),
|
|
80720
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80721
|
+
height: sessionDetailH,
|
|
80722
|
+
flexShrink: 0,
|
|
80723
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80724
|
+
title: "session",
|
|
80725
|
+
flexGrow: 1,
|
|
80726
|
+
children: selected ? /* @__PURE__ */ jsxDEV20(SessionDetail, {
|
|
80727
|
+
row: selected,
|
|
80728
|
+
width: sessionDetailInner,
|
|
80729
|
+
turns
|
|
80730
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("text", {
|
|
80731
|
+
fg: tokens.subtle,
|
|
80732
|
+
children: cursorItem?.kind === "agents" ? "agent sessions \u2014 enter to expand" : "nothing selected"
|
|
80733
|
+
}, undefined, false, undefined, this)
|
|
80734
|
+
}, undefined, false, undefined, this)
|
|
80735
|
+
}, undefined, false, undefined, this)
|
|
80736
|
+
]
|
|
80737
|
+
}, undefined, true, undefined, this)
|
|
80738
|
+
]
|
|
80739
|
+
}, undefined, true, undefined, this),
|
|
80740
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80741
|
+
height: WORKTREE_DETAIL_H,
|
|
80742
|
+
flexShrink: 0,
|
|
80743
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80744
|
+
title: "worktree",
|
|
80745
|
+
flexGrow: 1,
|
|
80746
|
+
children: /* @__PURE__ */ jsxDEV20(WorktreeDetail, {
|
|
80747
|
+
group,
|
|
80748
|
+
width: worktreeDetailInner,
|
|
80749
|
+
count: sessions2.length
|
|
80750
|
+
}, undefined, false, undefined, this)
|
|
80751
|
+
}, undefined, false, undefined, this)
|
|
80752
|
+
}, undefined, false, undefined, this),
|
|
80753
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80754
|
+
flexDirection: "row",
|
|
80755
|
+
height: 1,
|
|
80756
|
+
paddingX: 1,
|
|
80757
|
+
gap: 2,
|
|
80758
|
+
children: [
|
|
80759
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80760
|
+
fg: tokens.subtle,
|
|
80761
|
+
children: "\u2191\u2193 move"
|
|
80762
|
+
}, undefined, false, undefined, this),
|
|
80763
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80764
|
+
fg: tokens.subtle,
|
|
80765
|
+
children: "\u21E5 pane"
|
|
80766
|
+
}, undefined, false, undefined, this),
|
|
80767
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80768
|
+
fg: tokens.subtle,
|
|
80769
|
+
children: "type to filter"
|
|
80770
|
+
}, undefined, false, undefined, this),
|
|
80771
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80772
|
+
fg: tokens.accent,
|
|
80773
|
+
children: "\u23CE resume"
|
|
80774
|
+
}, undefined, false, undefined, this),
|
|
80775
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80776
|
+
fg: selected ? tokens.info : tokens.trace,
|
|
80777
|
+
children: "v read"
|
|
80778
|
+
}, undefined, false, undefined, this),
|
|
80779
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80780
|
+
fg: agentRows.length > 0 ? tokens.subtle : tokens.trace,
|
|
80781
|
+
children: agentRows.length > 0 ? `a ${agentsOpen ? "collapse" : "expand"} agents` : ""
|
|
80782
|
+
}, undefined, false, undefined, this),
|
|
80783
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80784
|
+
fg: tokens.subtle,
|
|
80785
|
+
children: "esc cancel"
|
|
80786
|
+
}, undefined, false, undefined, this)
|
|
80787
|
+
]
|
|
80788
|
+
}, undefined, true, undefined, this)
|
|
80789
|
+
]
|
|
80790
|
+
}, undefined, true, undefined, this);
|
|
80791
|
+
}
|
|
80792
|
+
function Field({
|
|
80793
|
+
label,
|
|
80794
|
+
width,
|
|
80795
|
+
children
|
|
80796
|
+
}) {
|
|
80797
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80798
|
+
flexDirection: "row",
|
|
80799
|
+
height: 1,
|
|
80800
|
+
gap: 1,
|
|
80801
|
+
children: [
|
|
80802
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80803
|
+
fg: tokens.subtle,
|
|
80804
|
+
children: padTo(label, width)
|
|
80805
|
+
}, undefined, false, undefined, this),
|
|
80806
|
+
children
|
|
80807
|
+
]
|
|
80808
|
+
}, undefined, true, undefined, this);
|
|
80809
|
+
}
|
|
80810
|
+
function WorktreeDetail({
|
|
80811
|
+
group,
|
|
80812
|
+
width,
|
|
80813
|
+
count
|
|
80814
|
+
}) {
|
|
80815
|
+
if (!group)
|
|
80816
|
+
return /* @__PURE__ */ jsxDEV20("text", {
|
|
80817
|
+
fg: tokens.subtle,
|
|
80818
|
+
children: "no worktree selected"
|
|
80819
|
+
}, undefined, false, undefined, this);
|
|
80820
|
+
const L = 9;
|
|
80821
|
+
const badges = [];
|
|
80822
|
+
if (group.dirty !== undefined) {
|
|
80823
|
+
badges.push({
|
|
80824
|
+
label: group.dirty > 0 ? `${DIRTY_GLYPH}${group.dirty} uncommitted` : "clean",
|
|
80825
|
+
bg: group.dirty > 0 ? CHIP.dirty : CHIP.clean
|
|
80826
|
+
});
|
|
80827
|
+
}
|
|
80828
|
+
if (group.ahead)
|
|
80829
|
+
badges.push({ label: `\u2191${group.ahead}`, bg: CHIP.ahead });
|
|
80830
|
+
if (group.behind)
|
|
80831
|
+
badges.push({ label: `\u2193${group.behind}`, bg: CHIP.behind });
|
|
80832
|
+
const badgeW = badges.reduce((w, b) => w + displayWidth(b.label) + 2, 0);
|
|
80833
|
+
const marker = group.current ? " \u25B6 you are here" : !group.live ? " worktree deleted" : "";
|
|
80834
|
+
const branch = group.branch ?? (group.live ? "detached" : "\u2014");
|
|
80835
|
+
const idAvail = Math.max(12, width - L - displayWidth(marker) - (BRANCH_LEAD + 1) - badgeW - 1);
|
|
80836
|
+
const nameW = Math.max(8, Math.min(displayWidth(group.name), Math.floor(idAvail / 2)));
|
|
80837
|
+
const branchW = Math.max(6, idAvail - nameW);
|
|
80838
|
+
const spark = activitySeries(group.sessions);
|
|
80839
|
+
const summary = ` ${count} session${count === 1 ? "" : "s"} \xB7 created ${group.createdMs ? age(group.createdMs) : "?"} ago \xB7 used ${group.lastActiveMs ? age(group.lastActiveMs) : "?"} ago`;
|
|
80840
|
+
const locAvail = Math.max(20, width - L - spark.length - 2);
|
|
80841
|
+
const pathW = Math.min(displayWidth(group.path ?? "\u2014"), Math.max(16, Math.floor(locAvail * 0.45)));
|
|
80842
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80843
|
+
flexDirection: "column",
|
|
80844
|
+
children: [
|
|
80845
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80846
|
+
children: [
|
|
80847
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80848
|
+
fg: tokens.subtle,
|
|
80849
|
+
children: padTo("worktree", L)
|
|
80850
|
+
}, undefined, false, undefined, this),
|
|
80851
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80852
|
+
fg: tokens.text,
|
|
80853
|
+
attributes: A.bold,
|
|
80854
|
+
children: truncate3(group.name, nameW)
|
|
80855
|
+
}, undefined, false, undefined, this),
|
|
80856
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80857
|
+
fg: group.current ? HERE_FG : tokens.dead,
|
|
80858
|
+
children: marker
|
|
80859
|
+
}, undefined, false, undefined, this),
|
|
80860
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80861
|
+
fg: tokens.subtle,
|
|
80862
|
+
children: ` ${BRANCH_ICON} `
|
|
80863
|
+
}, undefined, false, undefined, this),
|
|
80864
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80865
|
+
fg: group.live ? tokens.info : tokens.dead,
|
|
80866
|
+
children: `${truncate3(branch, branchW)} `
|
|
80867
|
+
}, undefined, false, undefined, this),
|
|
80868
|
+
badges.map((b) => /* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80869
|
+
label: b.label,
|
|
80870
|
+
bg: b.bg
|
|
80871
|
+
}, b.label, false, undefined, this))
|
|
80872
|
+
]
|
|
80873
|
+
}, undefined, true, undefined, this),
|
|
80874
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80875
|
+
flexDirection: "row",
|
|
80876
|
+
height: 1,
|
|
80877
|
+
children: [
|
|
80878
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80879
|
+
fg: tokens.subtle,
|
|
80880
|
+
flexShrink: 0,
|
|
80881
|
+
children: padTo("path", L)
|
|
80882
|
+
}, undefined, false, undefined, this),
|
|
80883
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80884
|
+
fg: tokens.trace,
|
|
80885
|
+
flexShrink: 0,
|
|
80886
|
+
children: `${padTo(truncate3(group.path ?? "\u2014", pathW), pathW)} `
|
|
80887
|
+
}, undefined, false, undefined, this),
|
|
80888
|
+
/* @__PURE__ */ jsxDEV20(Sparkline, {
|
|
80889
|
+
values: hasActivity(spark) ? spark : [],
|
|
80890
|
+
fg: SPARK_FG
|
|
80891
|
+
}, undefined, false, undefined, this),
|
|
80892
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80893
|
+
fg: tokens.trace,
|
|
80894
|
+
flexShrink: 0,
|
|
80895
|
+
children: truncate3(summary, Math.max(0, locAvail - pathW))
|
|
80896
|
+
}, undefined, false, undefined, this)
|
|
80897
|
+
]
|
|
80898
|
+
}, undefined, true, undefined, this)
|
|
80899
|
+
]
|
|
80900
|
+
}, undefined, true, undefined, this);
|
|
80901
|
+
}
|
|
80902
|
+
function SessionDetail({
|
|
80903
|
+
row,
|
|
80904
|
+
width,
|
|
80905
|
+
turns
|
|
80906
|
+
}) {
|
|
80907
|
+
const mb = row.sizeBytes / 1048576;
|
|
80908
|
+
const L = 9;
|
|
80909
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.round(row.sizeBytes / 1024)} KB`;
|
|
80910
|
+
const full = `\xB7 ${size}${row.lastMessageChars !== undefined ? ` \xB7 last msg ${row.lastMessageChars} ch` : ""}`;
|
|
80911
|
+
const suffixRoom = width - L - 1 - row.id.length - 1;
|
|
80912
|
+
const suffix = displayWidth(full) <= suffixRoom ? full : displayWidth(`\xB7 ${size}`) <= suffixRoom ? `\xB7 ${size}` : "";
|
|
80913
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80914
|
+
flexDirection: "column",
|
|
80915
|
+
children: [
|
|
80916
|
+
/* @__PURE__ */ jsxDEV20(Field, {
|
|
80917
|
+
label: "title",
|
|
80918
|
+
width: L,
|
|
80919
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80920
|
+
fg: tokens.text,
|
|
80921
|
+
attributes: A.bold,
|
|
80922
|
+
children: truncate3(sessionLabel(row), width - L)
|
|
80923
|
+
}, undefined, false, undefined, this)
|
|
80924
|
+
}, undefined, false, undefined, this),
|
|
80925
|
+
/* @__PURE__ */ jsxDEV20(Field, {
|
|
80926
|
+
label: "id",
|
|
80927
|
+
width: L,
|
|
80928
|
+
children: [
|
|
80929
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80930
|
+
fg: tokens.info,
|
|
80931
|
+
flexShrink: 0,
|
|
80932
|
+
children: row.id
|
|
80933
|
+
}, undefined, false, undefined, this),
|
|
80934
|
+
suffix ? /* @__PURE__ */ jsxDEV20("text", {
|
|
80935
|
+
fg: tokens.trace,
|
|
80936
|
+
flexShrink: 0,
|
|
80937
|
+
children: suffix
|
|
80938
|
+
}, undefined, false, undefined, this) : null
|
|
80939
|
+
]
|
|
80940
|
+
}, undefined, true, undefined, this),
|
|
80941
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80942
|
+
height: 1
|
|
80943
|
+
}, undefined, false, undefined, this),
|
|
80944
|
+
/* @__PURE__ */ jsxDEV20(Conversation, {
|
|
80945
|
+
turns: row.recentTurns ?? [],
|
|
80946
|
+
width,
|
|
80947
|
+
max: turns
|
|
80948
|
+
}, undefined, false, undefined, this)
|
|
80949
|
+
]
|
|
80950
|
+
}, undefined, true, undefined, this);
|
|
80951
|
+
}
|
|
80952
|
+
function Conversation({
|
|
80953
|
+
turns,
|
|
80954
|
+
width,
|
|
80955
|
+
max
|
|
80956
|
+
}) {
|
|
80957
|
+
if (turns.length === 0) {
|
|
80958
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80959
|
+
flexDirection: "column",
|
|
80960
|
+
width,
|
|
80961
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80962
|
+
fg: tokens.trace,
|
|
80963
|
+
children: "no conversation recorded"
|
|
80964
|
+
}, undefined, false, undefined, this)
|
|
80965
|
+
}, undefined, false, undefined, this);
|
|
80966
|
+
}
|
|
80967
|
+
const ROLE_W2 = 6;
|
|
80968
|
+
const textW = Math.max(10, width - ROLE_W2 - 2);
|
|
80969
|
+
const shown = turns.slice(-max);
|
|
80970
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80971
|
+
flexDirection: "column",
|
|
80972
|
+
width,
|
|
80973
|
+
flexShrink: 0,
|
|
80974
|
+
children: shown.map((t, i) => /* @__PURE__ */ jsxDEV20("text", {
|
|
80975
|
+
children: [
|
|
80976
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80977
|
+
fg: tokens.trace,
|
|
80978
|
+
children: "\u258D"
|
|
80979
|
+
}, undefined, false, undefined, this),
|
|
80980
|
+
/* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80981
|
+
label: t.role === "user" ? "you" : "ai",
|
|
80982
|
+
bg: t.role === "user" ? SPEAKER.you : SPEAKER.ai,
|
|
80983
|
+
width: ROLE_W2
|
|
80984
|
+
}, undefined, false, undefined, this),
|
|
80985
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80986
|
+
fg: t.role === "user" ? tokens.text : MUTED,
|
|
80987
|
+
children: truncate3(t.text, textW)
|
|
80988
|
+
}, undefined, false, undefined, this)
|
|
80989
|
+
]
|
|
80990
|
+
}, `${i}-${t.text.slice(0, 12)}`, true, undefined, this))
|
|
80991
|
+
}, undefined, false, undefined, this);
|
|
80992
|
+
}
|
|
80993
|
+
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";
|
|
80994
|
+
var init_resume_picker = __esm(() => {
|
|
80995
|
+
init_theme2();
|
|
80996
|
+
init_text();
|
|
80997
|
+
init_tokens();
|
|
80998
|
+
init_widgets();
|
|
80999
|
+
init_conversation_reader();
|
|
81000
|
+
init_conversation();
|
|
81001
|
+
init_session_discovery();
|
|
81002
|
+
STALE_MS = 3 * 86400000;
|
|
81003
|
+
GH_LEVELS = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
|
|
81004
|
+
CHIP = {
|
|
81005
|
+
fresh: GH_LEVELS[4],
|
|
81006
|
+
stale: "#a1a9b3",
|
|
81007
|
+
count: "#58a6ff",
|
|
81008
|
+
dirty: "#d29922",
|
|
81009
|
+
clean: GH_LEVELS[3],
|
|
81010
|
+
ahead: "#bc8cff",
|
|
81011
|
+
behind: "#d2a8ff"
|
|
81012
|
+
};
|
|
81013
|
+
HERE_FG = GH_LEVELS[4];
|
|
81014
|
+
SESSION_AGE_COL = SESSION_AGE_W + 3;
|
|
81015
|
+
SIZE_FLOOR_BYTES = 16 * 1024;
|
|
81016
|
+
MUTED = C.fgMuted;
|
|
81017
|
+
SCROLLBAR = {
|
|
81018
|
+
showArrows: false,
|
|
81019
|
+
trackOptions: { backgroundColor: tokens.bgPanel, foregroundColor: tokens.border }
|
|
81020
|
+
};
|
|
81021
|
+
ACTIVITY_H = 2 + ACTIVITY_WEEKS;
|
|
81022
|
+
});
|
|
81023
|
+
|
|
81024
|
+
// src/session/resume-picker-run.tsx
|
|
81025
|
+
var exports_resume_picker_run = {};
|
|
81026
|
+
__export(exports_resume_picker_run, {
|
|
81027
|
+
runResumePicker: () => runResumePicker
|
|
81028
|
+
});
|
|
81029
|
+
import { createCliRenderer as createCliRenderer3 } from "@opentui/core";
|
|
81030
|
+
import { createRoot as createRoot3 } from "@opentui/react";
|
|
81031
|
+
import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
|
|
81032
|
+
async function runResumePicker(cwd = process.cwd()) {
|
|
81033
|
+
const repo = getRepoContext(cwd);
|
|
81034
|
+
if (!repo)
|
|
81035
|
+
return { sessionId: null, hadSessions: false };
|
|
81036
|
+
const groups = discoverWorktreeGroups(repo);
|
|
81037
|
+
if (groups.length === 0)
|
|
81038
|
+
return { sessionId: null, hadSessions: false };
|
|
81039
|
+
await enrichWorktreeGit(groups, repo.root);
|
|
81040
|
+
setStderrQuiet(true);
|
|
81041
|
+
const renderer = await createCliRenderer3({
|
|
81042
|
+
useAlternateScreen: true,
|
|
81043
|
+
exitOnCtrlC: false
|
|
81044
|
+
});
|
|
81045
|
+
const root = createRoot3(renderer);
|
|
81046
|
+
let chosen = null;
|
|
81047
|
+
try {
|
|
81048
|
+
await new Promise((resolve5) => {
|
|
81049
|
+
let settled = false;
|
|
81050
|
+
const done = (id) => {
|
|
81051
|
+
if (settled)
|
|
81052
|
+
return;
|
|
81053
|
+
settled = true;
|
|
81054
|
+
chosen = id;
|
|
81055
|
+
resolve5();
|
|
81056
|
+
};
|
|
81057
|
+
root.render(/* @__PURE__ */ jsxDEV21(ResumePicker, {
|
|
81058
|
+
groups,
|
|
81059
|
+
onDone: done
|
|
81060
|
+
}, undefined, false, undefined, this));
|
|
81061
|
+
});
|
|
81062
|
+
} finally {
|
|
81063
|
+
try {
|
|
81064
|
+
root.unmount();
|
|
81065
|
+
} catch {}
|
|
81066
|
+
try {
|
|
81067
|
+
renderer.destroy();
|
|
81068
|
+
} catch {}
|
|
81069
|
+
setStderrQuiet(false);
|
|
81070
|
+
}
|
|
81071
|
+
return { sessionId: chosen, hadSessions: true };
|
|
81072
|
+
}
|
|
81073
|
+
var init_resume_picker_run = __esm(() => {
|
|
81074
|
+
init_logger();
|
|
81075
|
+
init_resume_picker();
|
|
81076
|
+
init_session_discovery();
|
|
81077
|
+
});
|
|
81078
|
+
|
|
81079
|
+
// src/session/baseline-pricing.ts
|
|
81080
|
+
function resolveBaseline(alias, label) {
|
|
81081
|
+
const entry = findEntryByAlias(alias);
|
|
81082
|
+
if (!entry)
|
|
81083
|
+
return null;
|
|
81084
|
+
const firstParty = entry.aggregators?.find((a) => a.provider === FIRST_PARTY && typeof a.pricing?.input === "number");
|
|
81085
|
+
const input = firstParty?.pricing?.input;
|
|
81086
|
+
const output = firstParty?.pricing?.output;
|
|
81087
|
+
if (typeof input !== "number" || typeof output !== "number")
|
|
81088
|
+
return null;
|
|
81089
|
+
if (input <= 0 || output <= 0)
|
|
81090
|
+
return null;
|
|
81091
|
+
return { modelId: entry.modelId, label, inputPerM: input, outputPerM: output };
|
|
81092
|
+
}
|
|
81093
|
+
function getBaselines() {
|
|
81094
|
+
return BASELINE_ALIASES.map(({ alias, label }) => resolveBaseline(alias, label)).filter((b) => b !== null);
|
|
81095
|
+
}
|
|
81096
|
+
function baselineCost(b, inputTokens, outputTokens) {
|
|
81097
|
+
return inputTokens / 1e6 * b.inputPerM + outputTokens / 1e6 * b.outputPerM;
|
|
81098
|
+
}
|
|
81099
|
+
var BASELINE_ALIASES, FIRST_PARTY = "anthropic";
|
|
81100
|
+
var init_baseline_pricing = __esm(() => {
|
|
81101
|
+
init_catalog_query();
|
|
81102
|
+
BASELINE_ALIASES = [
|
|
81103
|
+
{ alias: "~anthropic/claude-sonnet-latest", label: "Sonnet" },
|
|
81104
|
+
{ alias: "~anthropic/claude-opus-latest", label: "Opus" }
|
|
81105
|
+
];
|
|
81106
|
+
});
|
|
81107
|
+
|
|
81108
|
+
// src/session/session-stats.ts
|
|
81109
|
+
var exports_session_stats = {};
|
|
81110
|
+
__export(exports_session_stats, {
|
|
81111
|
+
tokenFilePath: () => tokenFilePath,
|
|
81112
|
+
readSessionStats: () => readSessionStats,
|
|
81113
|
+
computeSavings: () => computeSavings
|
|
81114
|
+
});
|
|
81115
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
81116
|
+
import { homedir as homedir35 } from "os";
|
|
81117
|
+
import { join as join38 } from "path";
|
|
81118
|
+
function tokenFilePath(port) {
|
|
81119
|
+
return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
|
|
81120
|
+
}
|
|
81121
|
+
function readSessionStats(port) {
|
|
81122
|
+
let raw2;
|
|
81123
|
+
try {
|
|
81124
|
+
raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
|
|
81125
|
+
} catch {
|
|
81126
|
+
return null;
|
|
81127
|
+
}
|
|
81128
|
+
if (!raw2 || typeof raw2 !== "object")
|
|
81129
|
+
return null;
|
|
81130
|
+
const d = raw2;
|
|
81131
|
+
const inputTokens = num(d.input_tokens);
|
|
81132
|
+
const outputTokens = num(d.output_tokens);
|
|
81133
|
+
if (inputTokens <= 0 && outputTokens <= 0)
|
|
81134
|
+
return null;
|
|
81135
|
+
const costUsd = num(d.total_cost);
|
|
81136
|
+
const isFree = d.is_free === true;
|
|
81137
|
+
const contextWindow = typeof d.context_window === "number" ? d.context_window : null;
|
|
81138
|
+
const contextUsed = contextWindow && contextWindow > 0 ? Math.min(1, Math.max(0, inputTokens / contextWindow)) : null;
|
|
81139
|
+
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) })) : [];
|
|
81140
|
+
const startedAt = num(d.started_at);
|
|
81141
|
+
const updatedAt = num(d.updated_at);
|
|
81142
|
+
const durationMs = startedAt > 0 && updatedAt > startedAt ? updatedAt - startedAt : 0;
|
|
81143
|
+
const billedInputTokens = num(d.billed_input_tokens) || inputTokens;
|
|
81144
|
+
const rawIn = billedInputTokens / 1e6 * num(d.input_per_m);
|
|
81145
|
+
const rawOut = outputTokens / 1e6 * num(d.output_per_m);
|
|
81146
|
+
const rawTotal = rawIn + rawOut;
|
|
81147
|
+
const scale = rawTotal > 0 && costUsd > 0 ? costUsd / rawTotal : 0;
|
|
81148
|
+
return {
|
|
81149
|
+
inputTokens,
|
|
81150
|
+
outputTokens,
|
|
81151
|
+
totalTokens: num(d.total_tokens) || inputTokens + outputTokens,
|
|
81152
|
+
costUsd,
|
|
81153
|
+
isFree,
|
|
81154
|
+
isEstimated: d.is_estimated === true,
|
|
81155
|
+
providerName: typeof d.provider_name === "string" ? d.provider_name : "",
|
|
81156
|
+
modelName: typeof d.model_name === "string" ? d.model_name : "",
|
|
81157
|
+
contextWindow,
|
|
81158
|
+
contextUsed,
|
|
81159
|
+
toolCalls,
|
|
81160
|
+
toolCallTotal: toolCalls.reduce((a, t) => a + t.count, 0),
|
|
81161
|
+
durationMs,
|
|
81162
|
+
savings: computeSavings(billedInputTokens, outputTokens, costUsd),
|
|
81163
|
+
inputCostUsd: rawIn * scale,
|
|
81164
|
+
outputCostUsd: rawOut * scale,
|
|
81165
|
+
billedInputTokens
|
|
81166
|
+
};
|
|
81167
|
+
}
|
|
81168
|
+
function computeSavings(inputTokens, outputTokens, actualUsd) {
|
|
81169
|
+
return getBaselines().map((b) => {
|
|
81170
|
+
const baselineUsd = baselineCost(b, inputTokens, outputTokens);
|
|
81171
|
+
return {
|
|
81172
|
+
label: b.label,
|
|
81173
|
+
modelId: b.modelId,
|
|
81174
|
+
baselineUsd,
|
|
81175
|
+
savedUsd: baselineUsd - actualUsd
|
|
81176
|
+
};
|
|
81177
|
+
});
|
|
81178
|
+
}
|
|
81179
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
81180
|
+
var init_session_stats = __esm(() => {
|
|
81181
|
+
init_baseline_pricing();
|
|
81182
|
+
});
|
|
81183
|
+
|
|
81184
|
+
// src/session/ansi-viz.ts
|
|
81185
|
+
function rgb(hex4) {
|
|
81186
|
+
return [
|
|
81187
|
+
Number.parseInt(hex4.slice(1, 3), 16),
|
|
81188
|
+
Number.parseInt(hex4.slice(3, 5), 16),
|
|
81189
|
+
Number.parseInt(hex4.slice(5, 7), 16)
|
|
81190
|
+
];
|
|
81191
|
+
}
|
|
81192
|
+
function fg(hex4) {
|
|
81193
|
+
const [r, g, b] = rgb(hex4);
|
|
81194
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
81195
|
+
}
|
|
81196
|
+
function bg(hex4) {
|
|
81197
|
+
const [r, g, b] = rgb(hex4);
|
|
81198
|
+
return `\x1B[48;2;${r};${g};${b}m`;
|
|
81199
|
+
}
|
|
81200
|
+
function paint(text, hex4, bold4 = false) {
|
|
81201
|
+
return `${bold4 ? BOLD5 : ""}${fg(hex4)}${text}${RESET4}`;
|
|
81202
|
+
}
|
|
81203
|
+
function meter(pct, width, ramp = ramps.load) {
|
|
81204
|
+
const cells = Math.floor(width);
|
|
81205
|
+
if (!Number.isFinite(cells) || cells <= 0)
|
|
81206
|
+
return "";
|
|
81207
|
+
if (Number.isNaN(pct))
|
|
81208
|
+
return paint(NODATA2.repeat(cells), tokens.dead);
|
|
81209
|
+
const cols = rampFor(cells, ramp);
|
|
81210
|
+
const filled = fillCells(pct, cells);
|
|
81211
|
+
let out = "";
|
|
81212
|
+
let last = "";
|
|
81213
|
+
for (let i = 0;i < cells; i++) {
|
|
81214
|
+
const hex4 = i < filled ? cols[i] : tokens.border;
|
|
81215
|
+
if (hex4 !== last) {
|
|
81216
|
+
out += fg(hex4);
|
|
81217
|
+
last = hex4;
|
|
81218
|
+
}
|
|
81219
|
+
out += i < filled ? FILL2 : TRACK2;
|
|
81220
|
+
}
|
|
81221
|
+
return out + RESET4;
|
|
81222
|
+
}
|
|
81223
|
+
function stackedBar(segments, width) {
|
|
81224
|
+
const cells = Math.floor(width);
|
|
81225
|
+
if (!Number.isFinite(cells) || cells <= 0 || segments.length === 0)
|
|
81226
|
+
return "";
|
|
81227
|
+
const split = splitCells(segments.map((s) => s.value), cells);
|
|
81228
|
+
if (split.reduce((a, b) => a + b, 0) === 0)
|
|
81229
|
+
return paint(TRACK2.repeat(cells), tokens.border);
|
|
81230
|
+
let out = "";
|
|
81231
|
+
for (let i = 0;i < split.length; i++) {
|
|
81232
|
+
const n = split[i];
|
|
81233
|
+
if (n > 0)
|
|
81234
|
+
out += `${bg(segments[i].color)}${" ".repeat(n)}`;
|
|
81235
|
+
}
|
|
81236
|
+
return out + RESET4;
|
|
81237
|
+
}
|
|
81238
|
+
function badge(label, hex4) {
|
|
81239
|
+
return `${BOLD5}${fg(pickInk(hex4))}${bg(hex4)} ${label} ${RESET4}`;
|
|
81240
|
+
}
|
|
81241
|
+
function stripAnsi4(s) {
|
|
81242
|
+
return s.replace(ANSI_RE3, "");
|
|
81243
|
+
}
|
|
81244
|
+
function visibleWidth(s) {
|
|
81245
|
+
return displayWidth(stripAnsi4(s));
|
|
81246
|
+
}
|
|
81247
|
+
function clipStyled(s, width) {
|
|
81248
|
+
if (width <= 0)
|
|
81249
|
+
return "";
|
|
81250
|
+
if (visibleWidth(s) <= width)
|
|
81251
|
+
return s;
|
|
81252
|
+
let out = "";
|
|
81253
|
+
let w = 0;
|
|
81254
|
+
let i = 0;
|
|
81255
|
+
while (i < s.length) {
|
|
81256
|
+
if (s[i] === "\x1B") {
|
|
81257
|
+
const end = s.indexOf("m", i);
|
|
81258
|
+
if (end === -1)
|
|
81259
|
+
break;
|
|
81260
|
+
out += s.slice(i, end + 1);
|
|
81261
|
+
i = end + 1;
|
|
81262
|
+
continue;
|
|
81263
|
+
}
|
|
81264
|
+
const ch = [...s.slice(i)][0] ?? "";
|
|
81265
|
+
const cw = displayWidth(ch);
|
|
81266
|
+
if (w + cw > width)
|
|
81267
|
+
break;
|
|
81268
|
+
out += ch;
|
|
81269
|
+
w += cw;
|
|
81270
|
+
i += ch.length;
|
|
81271
|
+
}
|
|
81272
|
+
return out + RESET4;
|
|
81273
|
+
}
|
|
81274
|
+
function padVisible2(s, width, align = "left") {
|
|
81275
|
+
const clipped = clipStyled(s, width);
|
|
81276
|
+
const pad2 = Math.max(0, width - visibleWidth(clipped));
|
|
81277
|
+
return align === "left" ? clipped + " ".repeat(pad2) : " ".repeat(pad2) + clipped;
|
|
81278
|
+
}
|
|
81279
|
+
function compact(n) {
|
|
81280
|
+
if (!Number.isFinite(n))
|
|
81281
|
+
return "\u2014";
|
|
81282
|
+
const a = Math.abs(n);
|
|
81283
|
+
if (a >= 1e9)
|
|
81284
|
+
return `${(n / 1e9).toFixed(1)}G`;
|
|
81285
|
+
if (a >= 1e6)
|
|
81286
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
81287
|
+
if (a >= 1000)
|
|
81288
|
+
return `${(n / 1000).toFixed(1)}K`;
|
|
81289
|
+
return String(Math.round(n));
|
|
81290
|
+
}
|
|
81291
|
+
function duration3(ms) {
|
|
81292
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
81293
|
+
return "\u2014";
|
|
81294
|
+
const s = Math.round(ms / 1000);
|
|
81295
|
+
if (s < 60)
|
|
81296
|
+
return `${s}s`;
|
|
81297
|
+
const m = Math.floor(s / 60);
|
|
81298
|
+
if (m < 60)
|
|
81299
|
+
return `${m}m ${String(s % 60).padStart(2, "0")}s`;
|
|
81300
|
+
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
81301
|
+
}
|
|
81302
|
+
function usd(n) {
|
|
81303
|
+
if (!Number.isFinite(n))
|
|
81304
|
+
return "\u2014";
|
|
81305
|
+
const a = Math.abs(n);
|
|
81306
|
+
if (a === 0)
|
|
81307
|
+
return "$0";
|
|
81308
|
+
if (a < 0.01)
|
|
81309
|
+
return `$${n.toFixed(4)}`;
|
|
81310
|
+
if (a < 1)
|
|
81311
|
+
return `$${n.toFixed(3)}`;
|
|
81312
|
+
return `$${n.toFixed(2)}`;
|
|
81313
|
+
}
|
|
81314
|
+
var RESET4 = "\x1B[0m", BOLD5 = "\x1B[1m", FILL2 = "\u2588", TRACK2 = "\u2591", NODATA2 = "\u254C", ANSI_RE3;
|
|
81315
|
+
var init_ansi_viz = __esm(() => {
|
|
81316
|
+
init_color();
|
|
81317
|
+
init_text();
|
|
81318
|
+
init_tokens();
|
|
81319
|
+
init_widgets();
|
|
81320
|
+
ANSI_RE3 = /\x1b\[[0-9;]*m/g;
|
|
81321
|
+
});
|
|
81322
|
+
|
|
81323
|
+
// src/session/session-summary.ts
|
|
81324
|
+
var exports_session_summary = {};
|
|
81325
|
+
__export(exports_session_summary, {
|
|
81326
|
+
renderSessionSummary: () => renderSessionSummary,
|
|
81327
|
+
printSessionSummary: () => printSessionSummary
|
|
81328
|
+
});
|
|
81329
|
+
function cardWidth() {
|
|
81330
|
+
const cols = process.stdout.columns || 80;
|
|
81331
|
+
return Math.max(MIN_W, Math.min(MAX_W, cols - 2));
|
|
81332
|
+
}
|
|
81333
|
+
function renderSessionSummary(input) {
|
|
81334
|
+
const { stats, modelSpec, resumeModelSpec, resumeId, exitCode } = input;
|
|
81335
|
+
const W2 = cardWidth();
|
|
81336
|
+
const inner = W2 - CHROME;
|
|
81337
|
+
const out = [];
|
|
81338
|
+
const dim3 = (s) => paint(s, tokens.subtle);
|
|
81339
|
+
const body = (s) => paint(s, tokens.text);
|
|
81340
|
+
const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
|
|
81341
|
+
const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
|
|
81342
|
+
const rule = "\u2500".repeat(Math.max(0, W2 - 2 - visibleWidth(titleText) - 1));
|
|
81343
|
+
out.push(paint("\u256D\u2500", tokens.border) + paint(titleText, titleHex) + paint(rule + "\u256E", tokens.border));
|
|
81344
|
+
const row = (s) => {
|
|
81345
|
+
out.push(`${paint("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint("\u2502", tokens.border)}`);
|
|
81346
|
+
};
|
|
81347
|
+
const blank = () => row("");
|
|
81348
|
+
const chips = [badge(truncate3(modelSpec, 34), tokens.accent)];
|
|
81349
|
+
if (stats.isFree)
|
|
81350
|
+
chips.push(badge("FREE", C.pillKeyBg));
|
|
81351
|
+
else if (stats.isEstimated)
|
|
81352
|
+
chips.push(badge("EST", "#8a7d1e"));
|
|
81353
|
+
if (exitCode !== 0)
|
|
81354
|
+
chips.push(badge(`EXIT ${exitCode}`, "#9e2b2b"));
|
|
81355
|
+
const right = body(duration3(stats.durationMs));
|
|
81356
|
+
const left = clipStyled(chips.join(" "), Math.max(0, inner - visibleWidth(right) - 1));
|
|
81357
|
+
const gap = Math.max(1, inner - visibleWidth(left) - visibleWidth(right));
|
|
81358
|
+
row(left + " ".repeat(gap) + right);
|
|
81359
|
+
if (stats.providerName)
|
|
81360
|
+
row(dim3(truncate3(stats.providerName, inner)));
|
|
81361
|
+
blank();
|
|
81362
|
+
const VALUE_W = 24;
|
|
81363
|
+
const barW = Math.max(12, inner - LABEL_W - VALUE_W);
|
|
81364
|
+
const dataRow = (label, bar, values) => {
|
|
81365
|
+
row(dim3(padTo(label, LABEL_W)) + bar + padVisible2(values, VALUE_W, "right"));
|
|
81366
|
+
};
|
|
81367
|
+
if (stats.contextUsed !== null && stats.contextWindow) {
|
|
81368
|
+
const pct = stats.contextUsed * 100;
|
|
81369
|
+
dataRow("context", meter(pct, barW, ramps.load), body(padStartTo(`${Math.round(pct)}%`, 4)) + dim3(` ${compact(stats.inputTokens)}/${compact(stats.contextWindow)}`));
|
|
81370
|
+
}
|
|
81371
|
+
dataRow("tokens", stackedBar([
|
|
81372
|
+
{ value: stats.inputTokens, color: C.blue },
|
|
81373
|
+
{ value: stats.outputTokens, color: C.cyan }
|
|
81374
|
+
], barW), dim3("in ") + body(compact(stats.inputTokens)) + dim3(" out ") + body(compact(stats.outputTokens)));
|
|
81375
|
+
if (!stats.isFree && stats.inputCostUsd + stats.outputCostUsd > 0) {
|
|
81376
|
+
dataRow("spend", stackedBar([
|
|
81377
|
+
{ value: stats.inputCostUsd, color: C.blue },
|
|
81378
|
+
{ value: stats.outputCostUsd, color: C.cyan }
|
|
81379
|
+
], barW), dim3("in ") + body(usd(stats.inputCostUsd)) + dim3(" out ") + body(usd(stats.outputCostUsd)));
|
|
81380
|
+
}
|
|
81381
|
+
if (stats.toolCallTotal > 0) {
|
|
81382
|
+
const shown = stats.toolCalls.slice(0, TOOL_COLORS.length);
|
|
81383
|
+
const rest = stats.toolCalls.slice(TOOL_COLORS.length).reduce((a, t) => a + t.count, 0);
|
|
81384
|
+
const segs = shown.map((t, i) => ({ value: t.count, color: TOOL_COLORS[i] }));
|
|
81385
|
+
if (rest > 0)
|
|
81386
|
+
segs.push({ value: rest, color: TOOL_OTHER });
|
|
81387
|
+
dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim3(" calls"));
|
|
81388
|
+
const legend = shown.map((t, i) => paint(`${t.name} ${t.count}`, TOOL_COLORS[i])).concat(rest > 0 ? [paint(`other ${rest}`, TOOL_OTHER)] : []);
|
|
81389
|
+
for (const line of wrapStyled(legend, dim3(" \xB7 "), inner - LABEL_W)) {
|
|
81390
|
+
row(" ".repeat(LABEL_W) + line);
|
|
81391
|
+
}
|
|
81392
|
+
}
|
|
81393
|
+
blank();
|
|
81394
|
+
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") : ""));
|
|
81395
|
+
for (const s of stats.savings) {
|
|
81396
|
+
const label = padTo(`vs ${s.label}`, LABEL_W);
|
|
81397
|
+
if (s.savedUsd >= 0) {
|
|
81398
|
+
const pct = s.baselineUsd > 0 ? s.savedUsd / s.baselineUsd * 100 : 0;
|
|
81399
|
+
dataRow(label, meter(pct, barW, ramps.savings), paint(padStartTo(`${Math.round(pct)}%`, 4), tokens.success) + dim3(" saved ") + paint(usd(s.savedUsd), tokens.success));
|
|
81400
|
+
} else {
|
|
81401
|
+
dataRow(label, meter(0, barW, ramps.savings), dim3("over by ") + paint(usd(-s.savedUsd), tokens.error));
|
|
81402
|
+
}
|
|
81403
|
+
}
|
|
81404
|
+
out.push(paint(`\u2570${"\u2500".repeat(W2 - 2)}\u256F`, tokens.border));
|
|
81405
|
+
if (resumeId) {
|
|
81406
|
+
out.push("");
|
|
81407
|
+
out.push(dim3("Resume this session with:"));
|
|
81408
|
+
const modelFlag = resumeModelSpec ? `--model ${resumeModelSpec} ` : "";
|
|
81409
|
+
out.push(`claudish ${modelFlag}--resume ${resumeId}`);
|
|
81410
|
+
}
|
|
81411
|
+
return out;
|
|
81412
|
+
}
|
|
81413
|
+
function wrapStyled(chips, sep, width) {
|
|
81414
|
+
const lines = [];
|
|
81415
|
+
let cur = "";
|
|
81416
|
+
let curW = 0;
|
|
81417
|
+
const sepW = visibleWidth(sep);
|
|
81418
|
+
for (const chip of chips) {
|
|
81419
|
+
const w = visibleWidth(chip);
|
|
81420
|
+
if (cur && curW + sepW + w > width) {
|
|
81421
|
+
lines.push(cur);
|
|
81422
|
+
cur = chip;
|
|
81423
|
+
curW = w;
|
|
81424
|
+
} else {
|
|
81425
|
+
cur = cur ? cur + sep + chip : chip;
|
|
81426
|
+
curW = cur === chip ? w : curW + sepW + w;
|
|
81427
|
+
}
|
|
81428
|
+
}
|
|
81429
|
+
if (cur)
|
|
81430
|
+
lines.push(cur);
|
|
81431
|
+
return lines;
|
|
81432
|
+
}
|
|
81433
|
+
function printSessionSummary(input, write) {
|
|
81434
|
+
for (const line of renderSessionSummary(input))
|
|
81435
|
+
write(line);
|
|
81436
|
+
write(RESET4);
|
|
81437
|
+
}
|
|
81438
|
+
var TOOL_COLORS, TOOL_OTHER, MIN_W = 62, MAX_W = 96, CHROME = 4, LABEL_W = 10;
|
|
81439
|
+
var init_session_summary = __esm(() => {
|
|
81440
|
+
init_theme2();
|
|
81441
|
+
init_text();
|
|
81442
|
+
init_tokens();
|
|
81443
|
+
init_ansi_viz();
|
|
81444
|
+
TOOL_COLORS = [
|
|
81445
|
+
C.blue,
|
|
81446
|
+
C.cyan,
|
|
81447
|
+
"#8a7d1e",
|
|
81448
|
+
"#1f6d75",
|
|
81449
|
+
C.magenta,
|
|
81450
|
+
"#2d6e3e",
|
|
81451
|
+
C.orange
|
|
81452
|
+
];
|
|
81453
|
+
TOOL_OTHER = C.dim;
|
|
81454
|
+
});
|
|
81455
|
+
|
|
78414
81456
|
// src/index.ts
|
|
78415
81457
|
init_op_source();
|
|
78416
81458
|
init_startup_trace();
|
|
78417
81459
|
var import_dotenv3 = __toESM(require_main(), 1);
|
|
78418
|
-
import { existsSync as existsSync29, readFileSync as
|
|
78419
|
-
import { join as
|
|
81460
|
+
import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
|
|
81461
|
+
import { join as join39, resolve as resolve5 } from "path";
|
|
78420
81462
|
import_dotenv3.config({ quiet: true });
|
|
78421
81463
|
function classifyStartupKind() {
|
|
78422
81464
|
const argv = process.argv.slice(2);
|
|
@@ -78659,14 +81701,14 @@ async function runCli() {
|
|
|
78659
81701
|
if (cliConfig.team && cliConfig.team.length > 0) {
|
|
78660
81702
|
let prompt = cliConfig.claudeArgs.join(" ");
|
|
78661
81703
|
if (cliConfig.inputFile) {
|
|
78662
|
-
prompt =
|
|
81704
|
+
prompt = readFileSync29(cliConfig.inputFile, "utf-8");
|
|
78663
81705
|
}
|
|
78664
81706
|
if (!prompt.trim()) {
|
|
78665
81707
|
console.error("Error: --team requires a prompt (positional args or -f <file>)");
|
|
78666
81708
|
process.exit(1);
|
|
78667
81709
|
}
|
|
78668
81710
|
const mode = cliConfig.teamMode ?? "default";
|
|
78669
|
-
const sessionPath =
|
|
81711
|
+
const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
78670
81712
|
if (mode === "json") {
|
|
78671
81713
|
const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
|
|
78672
81714
|
setupSession2(sessionPath, cliConfig.team, prompt);
|
|
@@ -78676,9 +81718,9 @@ async function runCli() {
|
|
|
78676
81718
|
});
|
|
78677
81719
|
const result = { ...status2, responses: {} };
|
|
78678
81720
|
for (const anonId of Object.keys(status2.models)) {
|
|
78679
|
-
const responsePath =
|
|
81721
|
+
const responsePath = join39(sessionPath, `response-${anonId}.md`);
|
|
78680
81722
|
try {
|
|
78681
|
-
const raw2 =
|
|
81723
|
+
const raw2 = readFileSync29(responsePath, "utf-8").trim();
|
|
78682
81724
|
try {
|
|
78683
81725
|
result.responses[anonId] = JSON.parse(raw2);
|
|
78684
81726
|
} catch {
|
|
@@ -78703,8 +81745,8 @@ async function runCli() {
|
|
|
78703
81745
|
Team Status`);
|
|
78704
81746
|
for (const id of modelIds) {
|
|
78705
81747
|
const m = status.models[id];
|
|
78706
|
-
const
|
|
78707
|
-
console.log(` ${id} ${m.state.padEnd(10)} ${
|
|
81748
|
+
const duration4 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
|
|
81749
|
+
console.log(` ${id} ${m.state.padEnd(10)} ${duration4}`);
|
|
78708
81750
|
}
|
|
78709
81751
|
process.exit(0);
|
|
78710
81752
|
}
|
|
@@ -78855,6 +81897,28 @@ Team Status`);
|
|
|
78855
81897
|
haiku: cliConfig.modelHaiku,
|
|
78856
81898
|
subagent: cliConfig.modelSubagent
|
|
78857
81899
|
};
|
|
81900
|
+
let resumedSessionId = (() => {
|
|
81901
|
+
const i = cliConfig.claudeArgs.indexOf("--resume");
|
|
81902
|
+
const v = i !== -1 ? cliConfig.claudeArgs[i + 1] : undefined;
|
|
81903
|
+
return v && !v.startsWith("-") ? v : null;
|
|
81904
|
+
})();
|
|
81905
|
+
if (cliConfig._resumePicker) {
|
|
81906
|
+
const canDrawTui = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
81907
|
+
if (!canDrawTui || cliConfig._hasPrintFlag || !cliConfig.interactive) {
|
|
81908
|
+
cliConfig.claudeArgs.push("--resume");
|
|
81909
|
+
} else {
|
|
81910
|
+
const { runResumePicker: runResumePicker2 } = await Promise.resolve().then(() => (init_resume_picker_run(), exports_resume_picker_run));
|
|
81911
|
+
const outcome = await runResumePicker2();
|
|
81912
|
+
if (!outcome.hadSessions) {
|
|
81913
|
+
cliConfig.claudeArgs.push("--resume");
|
|
81914
|
+
} else if (!outcome.sessionId) {
|
|
81915
|
+
process.exit(0);
|
|
81916
|
+
} else {
|
|
81917
|
+
cliConfig.claudeArgs.push("--resume", outcome.sessionId);
|
|
81918
|
+
resumedSessionId = outcome.sessionId;
|
|
81919
|
+
}
|
|
81920
|
+
}
|
|
81921
|
+
}
|
|
78858
81922
|
const proxy = await traceSpan("startup:proxy-start", () => createProxyServer2(port, cliConfig.monitor ? undefined : cliConfig.openrouterApiKey, cliConfig.monitor ? undefined : explicitModel, cliConfig.monitor, cliConfig.anthropicApiKey, modelMap, {
|
|
78859
81923
|
summarizeTools: cliConfig.summarizeTools,
|
|
78860
81924
|
quiet: cliConfig.quiet,
|
|
@@ -78888,6 +81952,25 @@ Team Status`);
|
|
|
78888
81952
|
const write = cliConfig.interactive ? console.log : console.error;
|
|
78889
81953
|
write(`[claudish] Done
|
|
78890
81954
|
`);
|
|
81955
|
+
try {
|
|
81956
|
+
const [{ readSessionStats: readSessionStats2 }, { printSessionSummary: printSessionSummary2 }, { findLatestSessionId: findLatestSessionId2 }] = await Promise.all([
|
|
81957
|
+
Promise.resolve().then(() => (init_session_stats(), exports_session_stats)),
|
|
81958
|
+
Promise.resolve().then(() => (init_session_summary(), exports_session_summary)),
|
|
81959
|
+
Promise.resolve().then(() => (init_session_discovery(), exports_session_discovery))
|
|
81960
|
+
]);
|
|
81961
|
+
const stats = readSessionStats2(port);
|
|
81962
|
+
if (stats) {
|
|
81963
|
+
printSessionSummary2({
|
|
81964
|
+
stats,
|
|
81965
|
+
modelSpec: explicitModel || stats.modelName || "",
|
|
81966
|
+
resumeModelSpec: explicitModel ?? null,
|
|
81967
|
+
resumeId: resumedSessionId ?? findLatestSessionId2(process.cwd(), Date.now() - stats.durationMs),
|
|
81968
|
+
exitCode
|
|
81969
|
+
}, write);
|
|
81970
|
+
}
|
|
81971
|
+
} catch (e) {
|
|
81972
|
+
console.error(`[claudish] session summary unavailable: ${e}`);
|
|
81973
|
+
}
|
|
78891
81974
|
}
|
|
78892
81975
|
const sessionLogPath = getAlwaysOnLogPath2();
|
|
78893
81976
|
if (exitCode !== 0 && sessionLogPath && !cliConfig.quiet) {
|