@threadbase-sh/streamer 1.27.2 → 1.27.3
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 +156 -102
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +51 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +51 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -5134,6 +5134,7 @@ var WSHub = class {
|
|
|
5134
5134
|
var BROWSE_SYSTEM_PROMPT = (browseRoot) => `You are working within the project boundary: ${browseRoot}. Do not read, write, or execute commands that access files or directories outside this boundary.`;
|
|
5135
5135
|
var DEFAULT_SYSTEM_PROMPT = "When presenting options or choices to the user, limit the options to at most 3.";
|
|
5136
5136
|
var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
|
|
5137
|
+
var REFRESH_TTL_MS = 2e3;
|
|
5137
5138
|
var START_READY_TIMEOUT_MS = 15e3;
|
|
5138
5139
|
function parseIncludeAgentsEnv(raw) {
|
|
5139
5140
|
if (raw === void 0) return false;
|
|
@@ -5173,6 +5174,12 @@ var StreamerServer = class {
|
|
|
5173
5174
|
// Set by onConversationChanged while a scan is in-flight; getScanner() does
|
|
5174
5175
|
// a single rescan after the current one completes instead of restarting it.
|
|
5175
5176
|
scannerStale = false;
|
|
5177
|
+
// Single-flight + TTL guard around scanner.refreshFile (see refreshFileGuarded).
|
|
5178
|
+
// A live file's mtime is always newer than the snapshot, so an unguarded
|
|
5179
|
+
// refresh fires on every request and re-parses the whole file from byte 0.
|
|
5180
|
+
// Keyed by filePath; entries drop on settle + TTL expiry, so the map stays
|
|
5181
|
+
// bounded by the active-file set.
|
|
5182
|
+
refreshInFlight = /* @__PURE__ */ new Map();
|
|
5176
5183
|
// True only while bindWithRetry is actively retrying. The persistent
|
|
5177
5184
|
// listener-level 'error' handler demotes EADDRINUSE to debug during this
|
|
5178
5185
|
// window so the self-healing kickstart-relaunch race doesn't spam warn.
|
|
@@ -5371,7 +5378,7 @@ var StreamerServer = class {
|
|
|
5371
5378
|
if (session.status === "waiting_input" || session.status === "idle") {
|
|
5372
5379
|
const filePath = this.sessionFileMap.get(session.id);
|
|
5373
5380
|
if (filePath) {
|
|
5374
|
-
this.getScanner().then((scanner) =>
|
|
5381
|
+
this.getScanner().then((scanner) => this.refreshFileGuarded(scanner, filePath)).then((meta) => {
|
|
5375
5382
|
this.log.info("scanner.refreshFile: ok", {
|
|
5376
5383
|
event: "scanner.refresh",
|
|
5377
5384
|
sessionId: session.id,
|
|
@@ -5820,6 +5827,34 @@ var StreamerServer = class {
|
|
|
5820
5827
|
this.inFlightCacheWrites.delete(guarded);
|
|
5821
5828
|
});
|
|
5822
5829
|
}
|
|
5830
|
+
// Single-flight + TTL wrapper for scanner.refreshFile. Both call sites (the
|
|
5831
|
+
// detail-stale branch and the per-turn refresh) route through here so that
|
|
5832
|
+
// N stacked retries on a live, actively-appended file cost one parse, not N:
|
|
5833
|
+
// - a refresh already in flight for the path → await the same promise;
|
|
5834
|
+
// - a refresh that settled within REFRESH_TTL_MS → skip, return null (the
|
|
5835
|
+
// caller keeps serving the current snapshot);
|
|
5836
|
+
// - otherwise start one, cache the promise, and stamp completedAt on settle.
|
|
5837
|
+
// The map is bounded by the active-file set (entries only live while a
|
|
5838
|
+
// refresh is in flight or within its TTL and are overwritten on the next
|
|
5839
|
+
// refresh of the same path).
|
|
5840
|
+
refreshFileGuarded(scanner, filePath) {
|
|
5841
|
+
const existing = this.refreshInFlight.get(filePath);
|
|
5842
|
+
if (existing) {
|
|
5843
|
+
const settled = existing.completedAt > 0;
|
|
5844
|
+
if (!settled) {
|
|
5845
|
+
return existing.promise;
|
|
5846
|
+
}
|
|
5847
|
+
if (Date.now() - existing.completedAt < REFRESH_TTL_MS) {
|
|
5848
|
+
return Promise.resolve(null);
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
const entry = { promise: Promise.resolve(null), completedAt: 0 };
|
|
5852
|
+
entry.promise = scanner.refreshFile(filePath).finally(() => {
|
|
5853
|
+
entry.completedAt = Date.now();
|
|
5854
|
+
});
|
|
5855
|
+
this.refreshInFlight.set(filePath, entry);
|
|
5856
|
+
return entry.promise;
|
|
5857
|
+
}
|
|
5823
5858
|
async close() {
|
|
5824
5859
|
for (const timer of this.ptyGraceTimers.values()) clearTimeout(timer);
|
|
5825
5860
|
this.ptyGraceTimers.clear();
|
|
@@ -6284,10 +6319,22 @@ var StreamerServer = class {
|
|
|
6284
6319
|
const scanner = await this.getScanner(true);
|
|
6285
6320
|
const fromIndex = await scanner.getConversation(uuid);
|
|
6286
6321
|
if (fromIndex) {
|
|
6322
|
+
if (this.ptyManager.hasSession(uuid)) {
|
|
6323
|
+
return fromIndex;
|
|
6324
|
+
}
|
|
6287
6325
|
if (fromIndex.filePath && this.isConversationSnapshotStale(fromIndex)) {
|
|
6288
|
-
const
|
|
6289
|
-
|
|
6290
|
-
|
|
6326
|
+
const filePath2 = fromIndex.filePath;
|
|
6327
|
+
this.trackCacheWrite(
|
|
6328
|
+
this.refreshFileGuarded(scanner, filePath2).catch((err) => {
|
|
6329
|
+
this.log.warn("scanner.refreshFile: failed", {
|
|
6330
|
+
event: "scanner.refresh_failed",
|
|
6331
|
+
conversationId: uuid,
|
|
6332
|
+
filePath: filePath2,
|
|
6333
|
+
trigger: "detail-swr",
|
|
6334
|
+
err
|
|
6335
|
+
});
|
|
6336
|
+
})
|
|
6337
|
+
);
|
|
6291
6338
|
}
|
|
6292
6339
|
return fromIndex;
|
|
6293
6340
|
}
|