@threadbase-sh/streamer 1.27.1 → 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 +1081 -804
- 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.d.cts
CHANGED
|
@@ -882,6 +882,7 @@ declare class StreamerServer {
|
|
|
882
882
|
private allScanners;
|
|
883
883
|
private scannerReady;
|
|
884
884
|
private scannerStale;
|
|
885
|
+
private refreshInFlight;
|
|
885
886
|
private binding;
|
|
886
887
|
private cacheReady;
|
|
887
888
|
private inFlightCacheWrites;
|
|
@@ -945,6 +946,7 @@ declare class StreamerServer {
|
|
|
945
946
|
private bindWithRetry;
|
|
946
947
|
private bindWithRetryLoop;
|
|
947
948
|
private trackCacheWrite;
|
|
949
|
+
private refreshFileGuarded;
|
|
948
950
|
close(): Promise<void>;
|
|
949
951
|
private handleRequest;
|
|
950
952
|
private handlePairStart;
|
package/dist/index.d.ts
CHANGED
|
@@ -882,6 +882,7 @@ declare class StreamerServer {
|
|
|
882
882
|
private allScanners;
|
|
883
883
|
private scannerReady;
|
|
884
884
|
private scannerStale;
|
|
885
|
+
private refreshInFlight;
|
|
885
886
|
private binding;
|
|
886
887
|
private cacheReady;
|
|
887
888
|
private inFlightCacheWrites;
|
|
@@ -945,6 +946,7 @@ declare class StreamerServer {
|
|
|
945
946
|
private bindWithRetry;
|
|
946
947
|
private bindWithRetryLoop;
|
|
947
948
|
private trackCacheWrite;
|
|
949
|
+
private refreshFileGuarded;
|
|
948
950
|
close(): Promise<void>;
|
|
949
951
|
private handleRequest;
|
|
950
952
|
private handlePairStart;
|
package/dist/index.js
CHANGED
|
@@ -5095,6 +5095,7 @@ var WSHub = class {
|
|
|
5095
5095
|
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.`;
|
|
5096
5096
|
var DEFAULT_SYSTEM_PROMPT = "When presenting options or choices to the user, limit the options to at most 3.";
|
|
5097
5097
|
var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
|
|
5098
|
+
var REFRESH_TTL_MS = 2e3;
|
|
5098
5099
|
var START_READY_TIMEOUT_MS = 15e3;
|
|
5099
5100
|
function parseIncludeAgentsEnv(raw) {
|
|
5100
5101
|
if (raw === void 0) return false;
|
|
@@ -5134,6 +5135,12 @@ var StreamerServer = class {
|
|
|
5134
5135
|
// Set by onConversationChanged while a scan is in-flight; getScanner() does
|
|
5135
5136
|
// a single rescan after the current one completes instead of restarting it.
|
|
5136
5137
|
scannerStale = false;
|
|
5138
|
+
// Single-flight + TTL guard around scanner.refreshFile (see refreshFileGuarded).
|
|
5139
|
+
// A live file's mtime is always newer than the snapshot, so an unguarded
|
|
5140
|
+
// refresh fires on every request and re-parses the whole file from byte 0.
|
|
5141
|
+
// Keyed by filePath; entries drop on settle + TTL expiry, so the map stays
|
|
5142
|
+
// bounded by the active-file set.
|
|
5143
|
+
refreshInFlight = /* @__PURE__ */ new Map();
|
|
5137
5144
|
// True only while bindWithRetry is actively retrying. The persistent
|
|
5138
5145
|
// listener-level 'error' handler demotes EADDRINUSE to debug during this
|
|
5139
5146
|
// window so the self-healing kickstart-relaunch race doesn't spam warn.
|
|
@@ -5332,7 +5339,7 @@ var StreamerServer = class {
|
|
|
5332
5339
|
if (session.status === "waiting_input" || session.status === "idle") {
|
|
5333
5340
|
const filePath = this.sessionFileMap.get(session.id);
|
|
5334
5341
|
if (filePath) {
|
|
5335
|
-
this.getScanner().then((scanner) =>
|
|
5342
|
+
this.getScanner().then((scanner) => this.refreshFileGuarded(scanner, filePath)).then((meta) => {
|
|
5336
5343
|
this.log.info("scanner.refreshFile: ok", {
|
|
5337
5344
|
event: "scanner.refresh",
|
|
5338
5345
|
sessionId: session.id,
|
|
@@ -5781,6 +5788,34 @@ var StreamerServer = class {
|
|
|
5781
5788
|
this.inFlightCacheWrites.delete(guarded);
|
|
5782
5789
|
});
|
|
5783
5790
|
}
|
|
5791
|
+
// Single-flight + TTL wrapper for scanner.refreshFile. Both call sites (the
|
|
5792
|
+
// detail-stale branch and the per-turn refresh) route through here so that
|
|
5793
|
+
// N stacked retries on a live, actively-appended file cost one parse, not N:
|
|
5794
|
+
// - a refresh already in flight for the path → await the same promise;
|
|
5795
|
+
// - a refresh that settled within REFRESH_TTL_MS → skip, return null (the
|
|
5796
|
+
// caller keeps serving the current snapshot);
|
|
5797
|
+
// - otherwise start one, cache the promise, and stamp completedAt on settle.
|
|
5798
|
+
// The map is bounded by the active-file set (entries only live while a
|
|
5799
|
+
// refresh is in flight or within its TTL and are overwritten on the next
|
|
5800
|
+
// refresh of the same path).
|
|
5801
|
+
refreshFileGuarded(scanner, filePath) {
|
|
5802
|
+
const existing = this.refreshInFlight.get(filePath);
|
|
5803
|
+
if (existing) {
|
|
5804
|
+
const settled = existing.completedAt > 0;
|
|
5805
|
+
if (!settled) {
|
|
5806
|
+
return existing.promise;
|
|
5807
|
+
}
|
|
5808
|
+
if (Date.now() - existing.completedAt < REFRESH_TTL_MS) {
|
|
5809
|
+
return Promise.resolve(null);
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
const entry = { promise: Promise.resolve(null), completedAt: 0 };
|
|
5813
|
+
entry.promise = scanner.refreshFile(filePath).finally(() => {
|
|
5814
|
+
entry.completedAt = Date.now();
|
|
5815
|
+
});
|
|
5816
|
+
this.refreshInFlight.set(filePath, entry);
|
|
5817
|
+
return entry.promise;
|
|
5818
|
+
}
|
|
5784
5819
|
async close() {
|
|
5785
5820
|
for (const timer of this.ptyGraceTimers.values()) clearTimeout(timer);
|
|
5786
5821
|
this.ptyGraceTimers.clear();
|
|
@@ -6245,10 +6280,22 @@ var StreamerServer = class {
|
|
|
6245
6280
|
const scanner = await this.getScanner(true);
|
|
6246
6281
|
const fromIndex = await scanner.getConversation(uuid);
|
|
6247
6282
|
if (fromIndex) {
|
|
6283
|
+
if (this.ptyManager.hasSession(uuid)) {
|
|
6284
|
+
return fromIndex;
|
|
6285
|
+
}
|
|
6248
6286
|
if (fromIndex.filePath && this.isConversationSnapshotStale(fromIndex)) {
|
|
6249
|
-
const
|
|
6250
|
-
|
|
6251
|
-
|
|
6287
|
+
const filePath2 = fromIndex.filePath;
|
|
6288
|
+
this.trackCacheWrite(
|
|
6289
|
+
this.refreshFileGuarded(scanner, filePath2).catch((err) => {
|
|
6290
|
+
this.log.warn("scanner.refreshFile: failed", {
|
|
6291
|
+
event: "scanner.refresh_failed",
|
|
6292
|
+
conversationId: uuid,
|
|
6293
|
+
filePath: filePath2,
|
|
6294
|
+
trigger: "detail-swr",
|
|
6295
|
+
err
|
|
6296
|
+
});
|
|
6297
|
+
})
|
|
6298
|
+
);
|
|
6252
6299
|
}
|
|
6253
6300
|
return fromIndex;
|
|
6254
6301
|
}
|