@threadbase-sh/streamer 1.36.0 → 1.36.2
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/cli.cjs +621 -372
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +328 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +313 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1449,9 +1449,13 @@ var import_fs6 = require("fs");
|
|
|
1449
1449
|
var import_path6 = require("path");
|
|
1450
1450
|
|
|
1451
1451
|
// src/services/questions/detectPermissionGate.ts
|
|
1452
|
-
var
|
|
1452
|
+
var OSC_777_PERMISSION_RE = /\x1b\]777;notify;Claude Code;[^\x07\x1b]*needs your permission/;
|
|
1453
|
+
var OSC_777_WAITING_RE = /\x1b\]777;notify;Claude Code;[^\x07\x1b]*waiting for your input/;
|
|
1453
1454
|
function hasPermissionOsc(rawData) {
|
|
1454
|
-
return
|
|
1455
|
+
return OSC_777_PERMISSION_RE.test(rawData);
|
|
1456
|
+
}
|
|
1457
|
+
function hasWaitingForInputOsc(rawData) {
|
|
1458
|
+
return OSC_777_WAITING_RE.test(rawData);
|
|
1455
1459
|
}
|
|
1456
1460
|
var OPTION_RE = /^\s*(❯)?\s*(\d+)\.\s+(.+?)\s*$/;
|
|
1457
1461
|
var FOOTER_RE = /Enter to select|Esc to cancel|↑|↓|to navigate|to cancel/i;
|
|
@@ -1602,22 +1606,29 @@ function detectShellPrompt(lines) {
|
|
|
1602
1606
|
};
|
|
1603
1607
|
}
|
|
1604
1608
|
const lastNumberedIdx = (() => {
|
|
1605
|
-
|
|
1606
|
-
|
|
1609
|
+
let i = last.idx;
|
|
1610
|
+
if (!NUMBERED_RE.test(lines[i])) {
|
|
1611
|
+
if (i > 0 && (PRESS_ENTER_RE.test(lines[i].trim()) || CONTINUE_RE.test(lines[i].trim()))) {
|
|
1612
|
+
i--;
|
|
1613
|
+
} else {
|
|
1614
|
+
return -1;
|
|
1615
|
+
}
|
|
1607
1616
|
}
|
|
1608
|
-
|
|
1617
|
+
while (i >= 0 && !NUMBERED_RE.test(lines[i]) && lines[i].trim().length === 0) i--;
|
|
1618
|
+
return i >= 0 && NUMBERED_RE.test(lines[i]) ? i : -1;
|
|
1609
1619
|
})();
|
|
1610
1620
|
if (lastNumberedIdx >= 0) {
|
|
1611
1621
|
const options = [];
|
|
1612
|
-
|
|
1622
|
+
let firstRow = lastNumberedIdx;
|
|
1623
|
+
for (let i = lastNumberedIdx; i >= 0; i--) {
|
|
1613
1624
|
const m = NUMBERED_RE.exec(lines[i]);
|
|
1614
|
-
if (!m)
|
|
1625
|
+
if (!m) break;
|
|
1615
1626
|
const num = Number.parseInt(m[1], 10);
|
|
1616
|
-
if (!Number.isFinite(num))
|
|
1617
|
-
options.
|
|
1627
|
+
if (!Number.isFinite(num)) break;
|
|
1628
|
+
options.unshift({ index: num, label: m[2].trim(), answerKeys: `${num}${ENTER}` });
|
|
1629
|
+
firstRow = i;
|
|
1618
1630
|
}
|
|
1619
1631
|
if (options.length >= 2) {
|
|
1620
|
-
const firstRow = lines.findIndex((l) => NUMBERED_RE.test(l));
|
|
1621
1632
|
let prompt = "";
|
|
1622
1633
|
for (let i = firstRow - 1; i >= 0; i--) {
|
|
1623
1634
|
const t = lines[i].trim();
|
|
@@ -2186,12 +2197,13 @@ var PTYManager = class {
|
|
|
2186
2197
|
const session = this.sessions.get(sessionId);
|
|
2187
2198
|
if (!session) return;
|
|
2188
2199
|
const oscPermission = hasPermissionOsc(rawData);
|
|
2200
|
+
const oscWaitingForInput = hasWaitingForInputOsc(rawData);
|
|
2189
2201
|
const hasAskFooter = /Enter to select/i.test(stripped);
|
|
2190
2202
|
const hasPromptMarker = CLAUDE_PROMPT_MARKERS.some((m) => stripped.includes(m));
|
|
2191
2203
|
const hasShellPromptHint = /[[(]\s*y\s*\/\s*n\s*[\])]|press\s+(enter|return|any key)|\bcontinue\b\s*\?|^\s*(?:❯|>)?\s*\d+[.)]\s+\S/im.test(
|
|
2192
2204
|
stripped
|
|
2193
2205
|
);
|
|
2194
|
-
if (!oscPermission && !hasAskFooter && !hasShellPromptHint && !this.permissionOpen.has(sessionId) && !this.shellPromptOpen.has(sessionId) && !this.lastScreenQuestionKey.has(sessionId)) {
|
|
2206
|
+
if (!oscPermission && !oscWaitingForInput && !hasAskFooter && !hasShellPromptHint && !this.permissionOpen.has(sessionId) && !this.shellPromptOpen.has(sessionId) && !this.lastScreenQuestionKey.has(sessionId)) {
|
|
2195
2207
|
return;
|
|
2196
2208
|
}
|
|
2197
2209
|
const lines = await this.getOutputLines(sessionId, 60);
|
|
@@ -2214,11 +2226,11 @@ var PTYManager = class {
|
|
|
2214
2226
|
this.onPermissionChange?.(sessionId, gate ?? { options: [] });
|
|
2215
2227
|
} else if (this.permissionOpen.has(sessionId) && !askFooterOnScreen) {
|
|
2216
2228
|
const gate = scrapePermissionGate(lines);
|
|
2217
|
-
if (gate) {
|
|
2218
|
-
this.onPermissionChange?.(sessionId, gate);
|
|
2219
|
-
} else if (hasPromptMarker) {
|
|
2229
|
+
if (oscWaitingForInput || !gate && hasPromptMarker) {
|
|
2220
2230
|
this.permissionOpen.delete(sessionId);
|
|
2221
2231
|
this.onPermissionChange?.(sessionId, null);
|
|
2232
|
+
} else if (gate) {
|
|
2233
|
+
this.onPermissionChange?.(sessionId, gate);
|
|
2222
2234
|
}
|
|
2223
2235
|
}
|
|
2224
2236
|
if (askFooterOnScreen) {
|
|
@@ -2713,11 +2725,11 @@ var import_node_ws = require("@hono/node-ws");
|
|
|
2713
2725
|
var import_client = require("@temporalio/client");
|
|
2714
2726
|
var import_scanner3 = require("@threadbase-sh/scanner");
|
|
2715
2727
|
var import_events = require("events");
|
|
2716
|
-
var
|
|
2728
|
+
var import_fs18 = require("fs");
|
|
2717
2729
|
var import_promises7 = require("fs/promises");
|
|
2718
2730
|
var import_http = require("http");
|
|
2719
|
-
var
|
|
2720
|
-
var
|
|
2731
|
+
var import_os9 = require("os");
|
|
2732
|
+
var import_path18 = require("path");
|
|
2721
2733
|
var import_readline = require("readline");
|
|
2722
2734
|
|
|
2723
2735
|
// node_modules/nanoid/index.js
|
|
@@ -3630,8 +3642,8 @@ var createSessionRoutes = (deps) => {
|
|
|
3630
3642
|
await deps.handleStopSession(c.req.param("id"), c.env.outgoing);
|
|
3631
3643
|
return alreadyHandled6();
|
|
3632
3644
|
});
|
|
3633
|
-
app.get("/:id", (c) => {
|
|
3634
|
-
deps.handleGetSession(c.req.param("id"), c.env.outgoing);
|
|
3645
|
+
app.get("/:id", async (c) => {
|
|
3646
|
+
await deps.handleGetSession(c.req.param("id"), c.env.outgoing);
|
|
3635
3647
|
return alreadyHandled6();
|
|
3636
3648
|
});
|
|
3637
3649
|
return app;
|
|
@@ -5444,6 +5456,14 @@ function seal(plaintext, recipientPublicKeyBase64) {
|
|
|
5444
5456
|
};
|
|
5445
5457
|
}
|
|
5446
5458
|
|
|
5459
|
+
// src/services/cache/cacheMetadata.ts
|
|
5460
|
+
function getCacheMetadata(repo, key) {
|
|
5461
|
+
return repo.getCacheMetadata(key);
|
|
5462
|
+
}
|
|
5463
|
+
function setCacheMetadata(repo, key, value) {
|
|
5464
|
+
repo.setCacheMetadata(key, value);
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5447
5467
|
// src/services/cache-integrity/cacheIntegrityMonitor.ts
|
|
5448
5468
|
var import_crypto8 = require("crypto");
|
|
5449
5469
|
var import_fs13 = require("fs");
|
|
@@ -5984,6 +6004,140 @@ function pruneAgentConversations(cache) {
|
|
|
5984
6004
|
return { scanned: rows.length, pruned, missing };
|
|
5985
6005
|
}
|
|
5986
6006
|
|
|
6007
|
+
// src/utils/dates.ts
|
|
6008
|
+
var import_date_fns = require("date-fns");
|
|
6009
|
+
function parseIsoDateOrNull(value) {
|
|
6010
|
+
if (!value) return null;
|
|
6011
|
+
const parsed = (0, import_date_fns.parseISO)(value);
|
|
6012
|
+
return (0, import_date_fns.isValid)(parsed) ? parsed : null;
|
|
6013
|
+
}
|
|
6014
|
+
function compareIsoDesc(a, b) {
|
|
6015
|
+
const dateA = parseIsoDateOrNull(a);
|
|
6016
|
+
const dateB = parseIsoDateOrNull(b);
|
|
6017
|
+
if (!dateA && !dateB) return 0;
|
|
6018
|
+
if (!dateA) return 1;
|
|
6019
|
+
if (!dateB) return -1;
|
|
6020
|
+
return (0, import_date_fns.compareDesc)(dateA, dateB);
|
|
6021
|
+
}
|
|
6022
|
+
|
|
6023
|
+
// src/services/projects/ensureProjectsForConversations.ts
|
|
6024
|
+
function ensureProjectsForConversations(repo, conversations) {
|
|
6025
|
+
const conversationsByPath = /* @__PURE__ */ new Map();
|
|
6026
|
+
for (const conversation of conversations) {
|
|
6027
|
+
if (!conversation.projectPath) continue;
|
|
6028
|
+
const canonical = canonicalizeProjectPath(conversation.projectPath);
|
|
6029
|
+
if (!canonical) continue;
|
|
6030
|
+
const existing = conversationsByPath.get(canonical) ?? [];
|
|
6031
|
+
existing.push(conversation);
|
|
6032
|
+
conversationsByPath.set(canonical, existing);
|
|
6033
|
+
}
|
|
6034
|
+
const pathToProjectId = /* @__PURE__ */ new Map();
|
|
6035
|
+
for (const [path, projectConversations] of conversationsByPath) {
|
|
6036
|
+
const latest = pickLatestConversation(projectConversations);
|
|
6037
|
+
const project = repo.upsertProjectByPath(path, {
|
|
6038
|
+
lastConversationId: latest?.id ?? null,
|
|
6039
|
+
lastConversationCreatedAt: latest?.createdAt ?? null,
|
|
6040
|
+
latestMessageAt: latest?.latestMessageAt ?? null
|
|
6041
|
+
});
|
|
6042
|
+
pathToProjectId.set(path, project.id);
|
|
6043
|
+
}
|
|
6044
|
+
return pathToProjectId;
|
|
6045
|
+
}
|
|
6046
|
+
function pickLatestConversation(conversations) {
|
|
6047
|
+
if (conversations.length === 0) return void 0;
|
|
6048
|
+
return [...conversations].sort((a, b) => {
|
|
6049
|
+
const cmp = compareIsoDesc(a.latestMessageAt ?? null, b.latestMessageAt ?? null);
|
|
6050
|
+
if (cmp !== 0) return cmp;
|
|
6051
|
+
return compareIsoDesc(a.createdAt ?? null, b.createdAt ?? null);
|
|
6052
|
+
})[0];
|
|
6053
|
+
}
|
|
6054
|
+
|
|
6055
|
+
// src/services/conversations/refreshConversationCache.ts
|
|
6056
|
+
function refreshConversationCache(deps) {
|
|
6057
|
+
const { projectsRepo, conversationsRepo, cacheMetadataRepo } = deps;
|
|
6058
|
+
const conversations = conversationsRepo.listConversationsForProjectBackfill();
|
|
6059
|
+
const pathToProjectId = ensureProjectsForConversations(
|
|
6060
|
+
projectsRepo,
|
|
6061
|
+
conversations.map((c) => ({
|
|
6062
|
+
id: c.id,
|
|
6063
|
+
projectPath: c.projectPath,
|
|
6064
|
+
latestMessageAt: c.lastActivity ?? null,
|
|
6065
|
+
createdAt: c.lastActivity ?? null
|
|
6066
|
+
}))
|
|
6067
|
+
);
|
|
6068
|
+
let conversationsBackfilled = 0;
|
|
6069
|
+
for (const conversation of conversations) {
|
|
6070
|
+
if (!conversation.projectPath) continue;
|
|
6071
|
+
if (conversation.projectId) continue;
|
|
6072
|
+
const projectId = pathToProjectId.get(canonicalizeProjectPath(conversation.projectPath));
|
|
6073
|
+
if (!projectId) continue;
|
|
6074
|
+
conversationsRepo.updateConversationProjectId({
|
|
6075
|
+
conversationId: conversation.id,
|
|
6076
|
+
projectId
|
|
6077
|
+
});
|
|
6078
|
+
conversationsBackfilled += 1;
|
|
6079
|
+
}
|
|
6080
|
+
const latest = conversationsRepo.getLatestConversation();
|
|
6081
|
+
if (latest) {
|
|
6082
|
+
setCacheMetadata(cacheMetadataRepo, "last_conversation_id", latest.id);
|
|
6083
|
+
if (latest.lastActivity) {
|
|
6084
|
+
setCacheMetadata(cacheMetadataRepo, "last_conversation_created_at", latest.lastActivity);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
setCacheMetadata(cacheMetadataRepo, "conversations_last_indexed_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
6088
|
+
return {
|
|
6089
|
+
projectsTouched: pathToProjectId.size,
|
|
6090
|
+
conversationsBackfilled,
|
|
6091
|
+
latestConversationId: latest?.id ?? null
|
|
6092
|
+
};
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
// src/services/conversations/shouldRefreshProjectsFromHdd.ts
|
|
6096
|
+
var import_fs16 = require("fs");
|
|
6097
|
+
var import_os8 = require("os");
|
|
6098
|
+
var import_path16 = require("path");
|
|
6099
|
+
var DEFAULT_PROJECTS_DIR = (0, import_path16.join)((0, import_os8.homedir)(), ".claude", "projects");
|
|
6100
|
+
function maxProjectsTreeMtimeMs(projectsDir) {
|
|
6101
|
+
let maxMs;
|
|
6102
|
+
try {
|
|
6103
|
+
maxMs = (0, import_fs16.statSync)(projectsDir).mtimeMs;
|
|
6104
|
+
} catch {
|
|
6105
|
+
return null;
|
|
6106
|
+
}
|
|
6107
|
+
try {
|
|
6108
|
+
for (const ent of (0, import_fs16.readdirSync)(projectsDir, { withFileTypes: true })) {
|
|
6109
|
+
if (!ent.isDirectory()) continue;
|
|
6110
|
+
try {
|
|
6111
|
+
const childMs = (0, import_fs16.statSync)((0, import_path16.join)(projectsDir, ent.name)).mtimeMs;
|
|
6112
|
+
if (childMs > maxMs) maxMs = childMs;
|
|
6113
|
+
} catch {
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
} catch {
|
|
6117
|
+
}
|
|
6118
|
+
return maxMs;
|
|
6119
|
+
}
|
|
6120
|
+
function shouldRefreshProjectsFromHdd(conversationsRepo, cacheMetadataRepo, opts = {}) {
|
|
6121
|
+
if (conversationsRepo.hasOrphanRows()) return true;
|
|
6122
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
6123
|
+
if (opts.projectsDirs) {
|
|
6124
|
+
for (const d of opts.projectsDirs) dirs.add(d);
|
|
6125
|
+
}
|
|
6126
|
+
dirs.add(opts.projectsDir ?? DEFAULT_PROJECTS_DIR);
|
|
6127
|
+
let newestMs = null;
|
|
6128
|
+
for (const dir of dirs) {
|
|
6129
|
+
const ms = maxProjectsTreeMtimeMs(dir);
|
|
6130
|
+
if (ms === null) continue;
|
|
6131
|
+
if (newestMs === null || ms > newestMs) newestMs = ms;
|
|
6132
|
+
}
|
|
6133
|
+
if (newestMs === null) return false;
|
|
6134
|
+
const lastIndexedIso = getCacheMetadata(cacheMetadataRepo, "conversations_last_indexed_at");
|
|
6135
|
+
if (!lastIndexedIso) return true;
|
|
6136
|
+
const lastIndexedMs = Date.parse(lastIndexedIso);
|
|
6137
|
+
if (Number.isNaN(lastIndexedMs)) return true;
|
|
6138
|
+
return newestMs > lastIndexedMs;
|
|
6139
|
+
}
|
|
6140
|
+
|
|
5987
6141
|
// src/services/projectChats/deriveProjectChatTitle.ts
|
|
5988
6142
|
function deriveProjectChatTitle(input) {
|
|
5989
6143
|
const trimmed = input.title?.trim();
|
|
@@ -5995,6 +6149,32 @@ function deriveProjectChatTitle(input) {
|
|
|
5995
6149
|
return `Untitled \xB7 ${input.id.slice(0, 8)}`;
|
|
5996
6150
|
}
|
|
5997
6151
|
|
|
6152
|
+
// src/services/questions/parseStatusLine.ts
|
|
6153
|
+
var MODEL_RE = /(Opus|Sonnet|Haiku|Fable)\s+[\d.]+(?:\s*\([^)]*\))?/;
|
|
6154
|
+
var EFFORT_RE = /●\s*([A-Za-z]+)\s*·\s*\/effort/;
|
|
6155
|
+
var PERMISSION_MODE_RE = /⏵⏵\s*([^(·\n]+?)\s*(?:\(|·|$)/;
|
|
6156
|
+
function parseStatusLine(lines) {
|
|
6157
|
+
const info = {};
|
|
6158
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
6159
|
+
const line = lines[i];
|
|
6160
|
+
if (!line) continue;
|
|
6161
|
+
if (info.effort === void 0) {
|
|
6162
|
+
const m = EFFORT_RE.exec(line);
|
|
6163
|
+
if (m) info.effort = m[1];
|
|
6164
|
+
}
|
|
6165
|
+
if (info.permissionMode === void 0) {
|
|
6166
|
+
const m = PERMISSION_MODE_RE.exec(line);
|
|
6167
|
+
if (m) info.permissionMode = m[1].trim();
|
|
6168
|
+
}
|
|
6169
|
+
if (info.model === void 0) {
|
|
6170
|
+
const m = MODEL_RE.exec(line);
|
|
6171
|
+
if (m) info.model = m[0].replace(/\s+/g, " ").trim();
|
|
6172
|
+
}
|
|
6173
|
+
if (info.model && info.effort && info.permissionMode) break;
|
|
6174
|
+
}
|
|
6175
|
+
return info;
|
|
6176
|
+
}
|
|
6177
|
+
|
|
5998
6178
|
// src/services/questions/detectAskUserQuestion.ts
|
|
5999
6179
|
function normalizeContent2(raw) {
|
|
6000
6180
|
if (Array.isArray(raw)) return raw;
|
|
@@ -6119,7 +6299,7 @@ function resolveAnswer(pending, body) {
|
|
|
6119
6299
|
}
|
|
6120
6300
|
|
|
6121
6301
|
// src/services/sessions/conversationBusy.ts
|
|
6122
|
-
var
|
|
6302
|
+
var import_fs17 = require("fs");
|
|
6123
6303
|
var RESUME_BUSY_WINDOW_MS = 12e4;
|
|
6124
6304
|
function resolveResumeBusyWindowMs(env = process.env) {
|
|
6125
6305
|
const raw = env.THREADBASE_RESUME_BUSY_WINDOW_MS;
|
|
@@ -6136,7 +6316,7 @@ function conversationBusy(input) {
|
|
|
6136
6316
|
let lastActivityMs = null;
|
|
6137
6317
|
if (input.jsonlPath) {
|
|
6138
6318
|
try {
|
|
6139
|
-
const mtimeMs = (0,
|
|
6319
|
+
const mtimeMs = (0, import_fs17.statSync)(input.jsonlPath).mtimeMs;
|
|
6140
6320
|
const age = now - mtimeMs;
|
|
6141
6321
|
lastActivityMs = Math.max(0, age);
|
|
6142
6322
|
const isSelfEcho = input.selfPtyEndedAt != null && mtimeMs <= input.selfPtyEndedAt + SELF_ACTIVITY_SKEW_MS;
|
|
@@ -6364,7 +6544,7 @@ function discoveredToResponse(d, conversationId) {
|
|
|
6364
6544
|
var import_crypto9 = require("crypto");
|
|
6365
6545
|
var import_promises6 = require("fs/promises");
|
|
6366
6546
|
var import_heic_convert = __toESM(require("heic-convert"), 1);
|
|
6367
|
-
var
|
|
6547
|
+
var import_path17 = require("path");
|
|
6368
6548
|
var UPLOAD_DIR_NAME = ".threadbase-uploads";
|
|
6369
6549
|
var MAX_BYTES = 25 * 1024 * 1024;
|
|
6370
6550
|
var HEIC_MIMES = /* @__PURE__ */ new Set(["image/heic", "image/heif"]);
|
|
@@ -6397,9 +6577,9 @@ async function saveUploadFile(input) {
|
|
|
6397
6577
|
}
|
|
6398
6578
|
const id = `up_${(0, import_crypto9.randomBytes)(8).toString("hex")}`;
|
|
6399
6579
|
const safeName = sanitizeFilename(originalName) || `file${MIME_TO_EXT[mimeType] ?? ""}`;
|
|
6400
|
-
const dir = (0,
|
|
6580
|
+
const dir = (0, import_path17.join)(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
|
|
6401
6581
|
await (0, import_promises6.mkdir)(dir, { recursive: true });
|
|
6402
|
-
const filePath = (0,
|
|
6582
|
+
const filePath = (0, import_path17.join)(dir, `${Date.now()}-${id}-${safeName}`);
|
|
6403
6583
|
await (0, import_promises6.writeFile)(filePath, buffer);
|
|
6404
6584
|
return {
|
|
6405
6585
|
id,
|
|
@@ -6509,14 +6689,6 @@ function computeConversationEtag({
|
|
|
6509
6689
|
return `"${digest}"`;
|
|
6510
6690
|
}
|
|
6511
6691
|
|
|
6512
|
-
// src/utils/dates.ts
|
|
6513
|
-
var import_date_fns = require("date-fns");
|
|
6514
|
-
function parseIsoDateOrNull(value) {
|
|
6515
|
-
if (!value) return null;
|
|
6516
|
-
const parsed = (0, import_date_fns.parseISO)(value);
|
|
6517
|
-
return (0, import_date_fns.isValid)(parsed) ? parsed : null;
|
|
6518
|
-
}
|
|
6519
|
-
|
|
6520
6692
|
// src/utils/isScannedSnapshotStale.ts
|
|
6521
6693
|
var STALENESS_TOLERANCE_MS = 1e3;
|
|
6522
6694
|
function isScannedSnapshotStale(snapshotTimestamp, fileMtimeMs) {
|
|
@@ -6817,13 +6989,13 @@ var StreamerServer = class {
|
|
|
6817
6989
|
this.disableDb = config.disableDb ?? false;
|
|
6818
6990
|
this.scannerPersistenceDisabled = config.scannerPersistent === false;
|
|
6819
6991
|
this.scanProfiles = config.scanProfiles;
|
|
6820
|
-
this.codexRoots = config.codexRoots ?? [(0,
|
|
6992
|
+
this.codexRoots = config.codexRoots ?? [(0, import_path18.join)((0, import_os9.homedir)(), ".codex", "sessions")];
|
|
6821
6993
|
this.ptyGracePeriodMs = config.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
6822
6994
|
this.defaultSystemPrompt = config.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
6823
6995
|
this.defaultPermissionMode = config.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
|
|
6824
6996
|
this.defaultModel = config.defaultModel ?? "sonnet";
|
|
6825
6997
|
this.defaultEffort = config.defaultEffort ?? "low";
|
|
6826
|
-
this.cacheDir = config.cacheDir ?? loadCacheDir() ?? (0,
|
|
6998
|
+
this.cacheDir = config.cacheDir ?? loadCacheDir() ?? (0, import_path18.join)((0, import_os9.homedir)(), ".threadbase", "cache");
|
|
6827
6999
|
this.tailSize = config.tailSize ?? loadTailSize() ?? 10;
|
|
6828
7000
|
this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config.directoryScanDebounceMs ?? 1e3;
|
|
6829
7001
|
this.markScannerStaleDebounced = debounce(() => {
|
|
@@ -6864,7 +7036,7 @@ var StreamerServer = class {
|
|
|
6864
7036
|
const seqs = cache.extendMessageIndex(
|
|
6865
7037
|
filePath,
|
|
6866
7038
|
spans,
|
|
6867
|
-
(0,
|
|
7039
|
+
(0, import_fs18.statSync)(filePath),
|
|
6868
7040
|
readFrom,
|
|
6869
7041
|
endOffset
|
|
6870
7042
|
);
|
|
@@ -7030,7 +7202,7 @@ var StreamerServer = class {
|
|
|
7030
7202
|
temporalClient,
|
|
7031
7203
|
taskQueue: agentConfig.temporal.taskQueue
|
|
7032
7204
|
});
|
|
7033
|
-
const conversationsBaseDir = agentConfig.conversationsDir || (0,
|
|
7205
|
+
const conversationsBaseDir = agentConfig.conversationsDir || (0, import_path18.join)((0, import_path18.dirname)(this.cacheDir), "conversations");
|
|
7034
7206
|
conversationWriter = createConversationWriter({
|
|
7035
7207
|
baseDir: conversationsBaseDir
|
|
7036
7208
|
});
|
|
@@ -7151,7 +7323,7 @@ var StreamerServer = class {
|
|
|
7151
7323
|
}
|
|
7152
7324
|
}
|
|
7153
7325
|
if (msg.type === "hold_session" && typeof msg.sessionId === "string") {
|
|
7154
|
-
this.startGraceTimer(msg.sessionId,
|
|
7326
|
+
this.startGraceTimer(msg.sessionId, this.ptyGracePeriodMs);
|
|
7155
7327
|
}
|
|
7156
7328
|
} catch {
|
|
7157
7329
|
}
|
|
@@ -7353,7 +7525,7 @@ var StreamerServer = class {
|
|
|
7353
7525
|
});
|
|
7354
7526
|
try {
|
|
7355
7527
|
this.cache = ConversationCache.open(
|
|
7356
|
-
(0,
|
|
7528
|
+
(0, import_path18.join)(this.cacheDir, "cache.db"),
|
|
7357
7529
|
this.tailSize,
|
|
7358
7530
|
void 0,
|
|
7359
7531
|
{
|
|
@@ -7398,7 +7570,7 @@ var StreamerServer = class {
|
|
|
7398
7570
|
this.fileWatcher.watchDirectory(dir);
|
|
7399
7571
|
}
|
|
7400
7572
|
for (const dir of this.codexRoots) {
|
|
7401
|
-
if (!(0,
|
|
7573
|
+
if (!(0, import_fs18.existsSync)(dir)) continue;
|
|
7402
7574
|
this.fileWatcher.watchDirectory(dir);
|
|
7403
7575
|
}
|
|
7404
7576
|
} catch (err) {
|
|
@@ -7479,6 +7651,20 @@ var StreamerServer = class {
|
|
|
7479
7651
|
count: pruned.length,
|
|
7480
7652
|
event: "cache.prune_ghosts"
|
|
7481
7653
|
});
|
|
7654
|
+
if (this.projectsRepo && this.conversationsRepo && this.cacheMetadataRepo) {
|
|
7655
|
+
refreshConversationCache({
|
|
7656
|
+
cache: this.cache,
|
|
7657
|
+
projectsRepo: this.projectsRepo,
|
|
7658
|
+
conversationsRepo: this.conversationsRepo,
|
|
7659
|
+
cacheMetadataRepo: this.cacheMetadataRepo
|
|
7660
|
+
});
|
|
7661
|
+
} else if (this.cacheMetadataRepo) {
|
|
7662
|
+
setCacheMetadata(
|
|
7663
|
+
this.cacheMetadataRepo,
|
|
7664
|
+
"conversations_last_indexed_at",
|
|
7665
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
7666
|
+
);
|
|
7667
|
+
}
|
|
7482
7668
|
}
|
|
7483
7669
|
}).catch((err) => {
|
|
7484
7670
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7718,6 +7904,56 @@ var StreamerServer = class {
|
|
|
7718
7904
|
checkSessionInputRateLimit(sessionId) {
|
|
7719
7905
|
return this.checkRateLimit(this.sessionInputAttempts, sessionId, 500, 6e4);
|
|
7720
7906
|
}
|
|
7907
|
+
/** Project roots watched for conversation JSONLs (profiles or ~/.claude/projects). */
|
|
7908
|
+
projectsDirsForFreshnessCheck() {
|
|
7909
|
+
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
7910
|
+
return this.scanProfiles.filter((p) => p.enabled).map((p) => (0, import_path18.join)(p.configDir, "projects"));
|
|
7911
|
+
}
|
|
7912
|
+
return [(0, import_path18.join)((0, import_os9.homedir)(), ".claude", "projects")];
|
|
7913
|
+
}
|
|
7914
|
+
/**
|
|
7915
|
+
* Full-glob scan + cache upsert/delete reconcile. Used by ?refresh=1 and by
|
|
7916
|
+
* the automatic freshness path when the directory watcher marked the scanner
|
|
7917
|
+
* stale or shouldRefreshProjectsFromHdd detected disk drift.
|
|
7918
|
+
*/
|
|
7919
|
+
async reconcileConversationsCacheFromDisk() {
|
|
7920
|
+
if (!this.cache) return;
|
|
7921
|
+
const scanner = await this.rescanForRefresh();
|
|
7922
|
+
const metas = [...scanner.getMetadataCache().values()];
|
|
7923
|
+
try {
|
|
7924
|
+
this.cache.upsertFromScannerMeta(metas);
|
|
7925
|
+
if (!this.cacheMonitor?.pending) {
|
|
7926
|
+
this.cache.reconcileDeletions(canonicalLivePathSet(metas));
|
|
7927
|
+
}
|
|
7928
|
+
if (this.projectsRepo && this.conversationsRepo && this.cacheMetadataRepo) {
|
|
7929
|
+
refreshConversationCache({
|
|
7930
|
+
cache: this.cache,
|
|
7931
|
+
projectsRepo: this.projectsRepo,
|
|
7932
|
+
conversationsRepo: this.conversationsRepo,
|
|
7933
|
+
cacheMetadataRepo: this.cacheMetadataRepo
|
|
7934
|
+
});
|
|
7935
|
+
} else if (this.cacheMetadataRepo) {
|
|
7936
|
+
setCacheMetadata(
|
|
7937
|
+
this.cacheMetadataRepo,
|
|
7938
|
+
"conversations_last_indexed_at",
|
|
7939
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
7940
|
+
);
|
|
7941
|
+
}
|
|
7942
|
+
} catch (err) {
|
|
7943
|
+
this.log.warn(
|
|
7944
|
+
`refresh reconcile failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
7945
|
+
{ event: "conversations.reconcile_failed" }
|
|
7946
|
+
);
|
|
7947
|
+
}
|
|
7948
|
+
}
|
|
7949
|
+
shouldAutoReconcileConversationList() {
|
|
7950
|
+
if (!this.cache) return false;
|
|
7951
|
+
if (this.scannerStale) return true;
|
|
7952
|
+
if (!this.conversationsRepo || !this.cacheMetadataRepo) return false;
|
|
7953
|
+
return shouldRefreshProjectsFromHdd(this.conversationsRepo, this.cacheMetadataRepo, {
|
|
7954
|
+
projectsDirs: this.projectsDirsForFreshnessCheck()
|
|
7955
|
+
});
|
|
7956
|
+
}
|
|
7721
7957
|
async handleListConversations(url, res) {
|
|
7722
7958
|
if (this.rejectIfWarmingUp(res)) return;
|
|
7723
7959
|
const limit = intParam(url, "limit", 50);
|
|
@@ -7726,19 +7962,14 @@ var StreamerServer = class {
|
|
|
7726
7962
|
const project = url.searchParams.get("project") ?? void 0;
|
|
7727
7963
|
const providerFilter = url.searchParams.get("provider") ?? void 0;
|
|
7728
7964
|
const bustCache = url.searchParams.get("refresh") === "1";
|
|
7729
|
-
if (
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
if (!this.cacheMonitor?.pending) {
|
|
7735
|
-
this.cache.reconcileDeletions(canonicalLivePathSet(metas2));
|
|
7736
|
-
}
|
|
7737
|
-
} catch (err) {
|
|
7738
|
-
this.log.warn(
|
|
7739
|
-
`refresh reconcile failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
7740
|
-
{ event: "conversations.reconcile_failed" }
|
|
7965
|
+
if (this.cache && (bustCache || this.shouldAutoReconcileConversationList())) {
|
|
7966
|
+
if (bustCache) {
|
|
7967
|
+
await this.withWarmup(
|
|
7968
|
+
"conversation_refresh",
|
|
7969
|
+
() => this.reconcileConversationsCacheFromDisk()
|
|
7741
7970
|
);
|
|
7971
|
+
} else {
|
|
7972
|
+
await this.reconcileConversationsCacheFromDisk();
|
|
7742
7973
|
}
|
|
7743
7974
|
}
|
|
7744
7975
|
if (this.cache) {
|
|
@@ -8000,22 +8231,22 @@ var StreamerServer = class {
|
|
|
8000
8231
|
*/
|
|
8001
8232
|
projectsDirs() {
|
|
8002
8233
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
8003
|
-
return this.scanProfiles.filter((p) => p.enabled).map((p) => (0,
|
|
8234
|
+
return this.scanProfiles.filter((p) => p.enabled).map((p) => (0, import_path18.join)(p.configDir, "projects"));
|
|
8004
8235
|
}
|
|
8005
|
-
return [(0,
|
|
8236
|
+
return [(0, import_path18.join)((0, import_os9.homedir)(), ".claude", "projects")];
|
|
8006
8237
|
}
|
|
8007
8238
|
findJsonlPath(uuid) {
|
|
8008
8239
|
const filename = `${uuid}.jsonl`;
|
|
8009
8240
|
for (const projectsDir of this.projectsDirs()) {
|
|
8010
|
-
if (!(0,
|
|
8011
|
-
for (const dir of (0,
|
|
8012
|
-
const fp = (0,
|
|
8013
|
-
if ((0,
|
|
8014
|
-
const projectDir = (0,
|
|
8241
|
+
if (!(0, import_fs18.existsSync)(projectsDir)) continue;
|
|
8242
|
+
for (const dir of (0, import_fs18.readdirSync)(projectsDir)) {
|
|
8243
|
+
const fp = (0, import_path18.join)(projectsDir, dir, filename);
|
|
8244
|
+
if ((0, import_fs18.existsSync)(fp)) return fp;
|
|
8245
|
+
const projectDir = (0, import_path18.join)(projectsDir, dir);
|
|
8015
8246
|
try {
|
|
8016
|
-
for (const sub of (0,
|
|
8017
|
-
const subagentPath = (0,
|
|
8018
|
-
if ((0,
|
|
8247
|
+
for (const sub of (0, import_fs18.readdirSync)(projectDir)) {
|
|
8248
|
+
const subagentPath = (0, import_path18.join)(projectDir, sub, "subagents", filename);
|
|
8249
|
+
if ((0, import_fs18.existsSync)(subagentPath)) return subagentPath;
|
|
8019
8250
|
}
|
|
8020
8251
|
} catch {
|
|
8021
8252
|
}
|
|
@@ -8025,7 +8256,7 @@ var StreamerServer = class {
|
|
|
8025
8256
|
}
|
|
8026
8257
|
async readCwdFromJsonl(filePath) {
|
|
8027
8258
|
return new Promise((resolve2) => {
|
|
8028
|
-
const rl = (0, import_readline.createInterface)({ input: (0,
|
|
8259
|
+
const rl = (0, import_readline.createInterface)({ input: (0, import_fs18.createReadStream)(filePath), crlfDelay: Infinity });
|
|
8029
8260
|
let found = false;
|
|
8030
8261
|
rl.on("line", (line) => {
|
|
8031
8262
|
if (found) return;
|
|
@@ -8115,7 +8346,7 @@ var StreamerServer = class {
|
|
|
8115
8346
|
if (this.isManagedTailPath(key)) return;
|
|
8116
8347
|
let mtimeMs;
|
|
8117
8348
|
try {
|
|
8118
|
-
mtimeMs = (0,
|
|
8349
|
+
mtimeMs = (0, import_fs18.statSync)(filePath).mtimeMs;
|
|
8119
8350
|
} catch {
|
|
8120
8351
|
return;
|
|
8121
8352
|
}
|
|
@@ -8297,7 +8528,7 @@ var StreamerServer = class {
|
|
|
8297
8528
|
if (!conv.filePath) return false;
|
|
8298
8529
|
let mtimeMs = null;
|
|
8299
8530
|
try {
|
|
8300
|
-
mtimeMs = (0,
|
|
8531
|
+
mtimeMs = (0, import_fs18.statSync)(conv.filePath).mtimeMs;
|
|
8301
8532
|
} catch {
|
|
8302
8533
|
return false;
|
|
8303
8534
|
}
|
|
@@ -8666,13 +8897,23 @@ var StreamerServer = class {
|
|
|
8666
8897
|
throw err;
|
|
8667
8898
|
}
|
|
8668
8899
|
}
|
|
8669
|
-
handleGetSession(sessionId, res) {
|
|
8900
|
+
async handleGetSession(sessionId, res) {
|
|
8670
8901
|
if (this.rejectIfWarmingUp(res)) return;
|
|
8671
8902
|
const session = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
8672
8903
|
if (session) {
|
|
8673
|
-
if (!(0,
|
|
8904
|
+
if (!(0, import_fs18.existsSync)(session.projectPath)) {
|
|
8674
8905
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
8675
8906
|
}
|
|
8907
|
+
if (this.ptyManager.hasSession(sessionId)) {
|
|
8908
|
+
try {
|
|
8909
|
+
const lines = await this.ptyManager.getOutputLines(sessionId, 10);
|
|
8910
|
+
const status = parseStatusLine(lines);
|
|
8911
|
+
if (session.model == null && status.model) session.model = status.model;
|
|
8912
|
+
if (status.effort) session.effort = status.effort;
|
|
8913
|
+
if (status.permissionMode) session.permissionMode = status.permissionMode;
|
|
8914
|
+
} catch {
|
|
8915
|
+
}
|
|
8916
|
+
}
|
|
8676
8917
|
json(res, 200, session);
|
|
8677
8918
|
return;
|
|
8678
8919
|
}
|
|
@@ -9131,7 +9372,7 @@ var StreamerServer = class {
|
|
|
9131
9372
|
const jsonlCwd = jsonlPath ? await this.readCwdFromJsonl(jsonlPath) : null;
|
|
9132
9373
|
if (jsonlCwd) {
|
|
9133
9374
|
projectPath = jsonlCwd;
|
|
9134
|
-
projectName = projectName || (0,
|
|
9375
|
+
projectName = projectName || (0, import_path18.basename)(jsonlCwd);
|
|
9135
9376
|
}
|
|
9136
9377
|
}
|
|
9137
9378
|
if (!projectPath) {
|
|
@@ -9207,7 +9448,7 @@ var StreamerServer = class {
|
|
|
9207
9448
|
sessionStore: this.sessionStore,
|
|
9208
9449
|
// biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
|
|
9209
9450
|
agentClient: this.agentClient,
|
|
9210
|
-
conversationsDir: this.cacheDir ? (0,
|
|
9451
|
+
conversationsDir: this.cacheDir ? (0, import_path18.join)((0, import_path18.dirname)(this.cacheDir), "conversations") : "",
|
|
9211
9452
|
agentConfig: this.agentConfig
|
|
9212
9453
|
});
|
|
9213
9454
|
json(res, result.status, result.body);
|
|
@@ -9351,7 +9592,7 @@ var StreamerServer = class {
|
|
|
9351
9592
|
// file isn't slurped in full.
|
|
9352
9593
|
readFirstLineSessionId(filePath) {
|
|
9353
9594
|
try {
|
|
9354
|
-
const content = (0,
|
|
9595
|
+
const content = (0, import_fs18.readFileSync)(filePath, "utf8");
|
|
9355
9596
|
const nl = content.indexOf("\n");
|
|
9356
9597
|
const firstLine = nl === -1 ? content : content.slice(0, nl);
|
|
9357
9598
|
if (!firstLine.trim()) return null;
|
|
@@ -9366,9 +9607,9 @@ var StreamerServer = class {
|
|
|
9366
9607
|
// was passed to Claude via --session-id so the filename matches from the start.
|
|
9367
9608
|
watchForJsonl(sessionId, projectPath) {
|
|
9368
9609
|
const encoded = projectPath.replace(/[/\\:.]/g, "-");
|
|
9369
|
-
const projectsDir = (0,
|
|
9610
|
+
const projectsDir = (0, import_path18.join)((0, import_os9.homedir)(), ".claude", "projects", encoded);
|
|
9370
9611
|
const expectedFile = `${sessionId}.jsonl`;
|
|
9371
|
-
const filePath = (0,
|
|
9612
|
+
const filePath = (0, import_path18.join)(projectsDir, expectedFile);
|
|
9372
9613
|
const deadline = Date.now() + 12e4;
|
|
9373
9614
|
let watcher = null;
|
|
9374
9615
|
const cleanup = () => {
|
|
@@ -9386,14 +9627,14 @@ var StreamerServer = class {
|
|
|
9386
9627
|
cleanup();
|
|
9387
9628
|
return;
|
|
9388
9629
|
}
|
|
9389
|
-
let resolvedFilePath = (0,
|
|
9390
|
-
if (!resolvedFilePath && (0,
|
|
9630
|
+
let resolvedFilePath = (0, import_fs18.existsSync)(filePath) ? filePath : null;
|
|
9631
|
+
if (!resolvedFilePath && (0, import_fs18.existsSync)(projectsDir)) {
|
|
9391
9632
|
try {
|
|
9392
9633
|
const now = Date.now();
|
|
9393
|
-
const match = (0,
|
|
9394
|
-
({ f }) => (0,
|
|
9634
|
+
const match = (0, import_fs18.readdirSync)(projectsDir).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, mtime: (0, import_fs18.statSync)((0, import_path18.join)(projectsDir, f)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).filter(
|
|
9635
|
+
({ f }) => (0, import_path18.basename)(f, ".jsonl") === sessionId || this.readFirstLineSessionId((0, import_path18.join)(projectsDir, f)) === sessionId
|
|
9395
9636
|
).sort((a, b) => b.mtime - a.mtime)[0];
|
|
9396
|
-
if (match) resolvedFilePath = (0,
|
|
9637
|
+
if (match) resolvedFilePath = (0, import_path18.join)(projectsDir, match.f);
|
|
9397
9638
|
} catch {
|
|
9398
9639
|
}
|
|
9399
9640
|
}
|
|
@@ -9401,7 +9642,7 @@ var StreamerServer = class {
|
|
|
9401
9642
|
cleanup();
|
|
9402
9643
|
this.sessionFileMap.set(sessionId, resolvedFilePath);
|
|
9403
9644
|
try {
|
|
9404
|
-
const existing = (0,
|
|
9645
|
+
const existing = (0, import_fs18.readFileSync)(resolvedFilePath, "utf8").split("\n").filter(Boolean);
|
|
9405
9646
|
if (existing.length > 0) {
|
|
9406
9647
|
this.broadcastConversationLines(sessionId, existing);
|
|
9407
9648
|
}
|
|
@@ -9425,7 +9666,7 @@ var StreamerServer = class {
|
|
|
9425
9666
|
if (this.sessionFileMap.has(sessionId)) return;
|
|
9426
9667
|
try {
|
|
9427
9668
|
require("fs").mkdirSync(projectsDir, { recursive: true });
|
|
9428
|
-
watcher = (0,
|
|
9669
|
+
watcher = (0, import_fs18.watch)(projectsDir, tryWire);
|
|
9429
9670
|
watcher.on("error", cleanup);
|
|
9430
9671
|
} catch {
|
|
9431
9672
|
}
|
|
@@ -9441,7 +9682,7 @@ var StreamerServer = class {
|
|
|
9441
9682
|
watchForCodexRollout(sessionId, projectPath) {
|
|
9442
9683
|
const deadline = Date.now() + 12e4;
|
|
9443
9684
|
const now = /* @__PURE__ */ new Date();
|
|
9444
|
-
const dateDir = (0,
|
|
9685
|
+
const dateDir = (0, import_path18.join)(
|
|
9445
9686
|
String(now.getFullYear()),
|
|
9446
9687
|
String(now.getMonth() + 1).padStart(2, "0"),
|
|
9447
9688
|
String(now.getDate()).padStart(2, "0")
|
|
@@ -9454,7 +9695,7 @@ var StreamerServer = class {
|
|
|
9454
9695
|
};
|
|
9455
9696
|
const matchesProjectPath = (candidatePath) => {
|
|
9456
9697
|
try {
|
|
9457
|
-
const firstLine = (0,
|
|
9698
|
+
const firstLine = (0, import_fs18.readFileSync)(candidatePath, "utf8").split("\n", 1)[0];
|
|
9458
9699
|
if (!firstLine) return null;
|
|
9459
9700
|
const parsed = JSON.parse(firstLine);
|
|
9460
9701
|
if (parsed?.type !== "session_meta") return null;
|
|
@@ -9482,18 +9723,18 @@ var StreamerServer = class {
|
|
|
9482
9723
|
this.sessionStore.listManaged().filter((s) => s.id !== sessionId && s.boundConversationId != null).map((s) => s.boundConversationId)
|
|
9483
9724
|
);
|
|
9484
9725
|
for (const root of this.codexRoots) {
|
|
9485
|
-
const sessionsDir = (0,
|
|
9486
|
-
if (!(0,
|
|
9726
|
+
const sessionsDir = (0, import_path18.join)(root, dateDir);
|
|
9727
|
+
if (!(0, import_fs18.existsSync)(sessionsDir)) continue;
|
|
9487
9728
|
let candidateFiles;
|
|
9488
9729
|
try {
|
|
9489
|
-
candidateFiles = (0,
|
|
9730
|
+
candidateFiles = (0, import_fs18.readdirSync)(sessionsDir).filter((f) => f.endsWith(".jsonl"));
|
|
9490
9731
|
} catch {
|
|
9491
9732
|
continue;
|
|
9492
9733
|
}
|
|
9493
9734
|
const nowMs = Date.now();
|
|
9494
|
-
const recentCandidates = candidateFiles.map((f) => ({ f, mtime: (0,
|
|
9735
|
+
const recentCandidates = candidateFiles.map((f) => ({ f, mtime: (0, import_fs18.statSync)((0, import_path18.join)(sessionsDir, f)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b) => b.mtime - a.mtime);
|
|
9495
9736
|
for (const { f } of recentCandidates) {
|
|
9496
|
-
const candidatePath = (0,
|
|
9737
|
+
const candidatePath = (0, import_path18.join)(sessionsDir, f);
|
|
9497
9738
|
const match = matchesProjectPath(candidatePath);
|
|
9498
9739
|
if (!match) continue;
|
|
9499
9740
|
if (boundElsewhere.has(match.id)) continue;
|
|
@@ -9503,7 +9744,7 @@ var StreamerServer = class {
|
|
|
9503
9744
|
this.sessionFileMap.set(sessionId, candidatePath);
|
|
9504
9745
|
this.fileWatcher.watch(candidatePath);
|
|
9505
9746
|
try {
|
|
9506
|
-
const existing = (0,
|
|
9747
|
+
const existing = (0, import_fs18.readFileSync)(candidatePath, "utf8").split("\n").filter(Boolean);
|
|
9507
9748
|
if (existing.length > 0) {
|
|
9508
9749
|
this.broadcastConversationLines(sessionId, existing);
|
|
9509
9750
|
}
|
|
@@ -9637,7 +9878,7 @@ async function waitForProcessExit(pid, timeoutMs, pollMs = ADOPT_KILL_POLL_MS) {
|
|
|
9637
9878
|
}
|
|
9638
9879
|
function classifyResumability(cwd) {
|
|
9639
9880
|
if (!cwd) return { resumable: true };
|
|
9640
|
-
if ((0,
|
|
9881
|
+
if ((0, import_fs18.existsSync)(cwd)) return { resumable: true };
|
|
9641
9882
|
const ranInWorktree = /\/\.worktrees\//.test(cwd) || /\/\.claude\/worktrees\//.test(cwd);
|
|
9642
9883
|
return {
|
|
9643
9884
|
resumable: false,
|