bunnyquery 1.10.5 → 1.10.7
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/bunnyquery.js +30 -2
- package/dist/engine.cjs +26 -1
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +28 -1
- package/dist/engine.d.ts +28 -1
- package/dist/engine.mjs +26 -2
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/history.ts +17 -0
- package/src/engine/host.ts +10 -0
- package/src/engine/session.ts +39 -1
- package/src/engine/time.ts +29 -0
package/package.json
CHANGED
package/src/engine/history.ts
CHANGED
|
@@ -792,6 +792,12 @@ export function mapHistoryListToMessages(list: any[], platform: 'claude' | 'open
|
|
|
792
792
|
// if one is missing (older records only carried `updated`).
|
|
793
793
|
var createdTs = Number(item && item.created);
|
|
794
794
|
var updatedTs = Number(item && item.updated);
|
|
795
|
+
// When the WORKER began executing this pass, not when it was enqueued. The
|
|
796
|
+
// SDK normalises it to ms (the worker stamps it in seconds). Absent on a row
|
|
797
|
+
// that never ran, and on rows written before the SDK projected it -- in both
|
|
798
|
+
// cases the pass simply shows no duration, which is right: the honest answer
|
|
799
|
+
// to "how long did it take" is nothing, not the queue wait dressed up as it.
|
|
800
|
+
var executedTs = Number(item && item.executed);
|
|
795
801
|
var userTs = isFinite(createdTs) && createdTs > 0 ? createdTs : (isFinite(updatedTs) && updatedTs > 0 ? updatedTs : undefined);
|
|
796
802
|
var replyTs = isFinite(updatedTs) && updatedTs > 0 ? updatedTs : (isFinite(createdTs) && createdTs > 0 ? createdTs : undefined);
|
|
797
803
|
|
|
@@ -874,6 +880,17 @@ export function mapHistoryListToMessages(list: any[], platform: 'claude' | 'open
|
|
|
874
880
|
if (isCompact) okm._compact = true;
|
|
875
881
|
if (serverItemId !== undefined) okm._serverItemId = serverItemId;
|
|
876
882
|
if (replyTs !== undefined) okm._ts = replyTs;
|
|
883
|
+
// How long THIS pass took: from the moment the worker began EXECUTING it to
|
|
884
|
+
// the moment it resolved. Carried on the reply so the bubble can show it
|
|
885
|
+
// without its request bubble (a continuation's request is hidden from the
|
|
886
|
+
// expanded row). Only for an indexing pass: `_tsStart`'s presence is what
|
|
887
|
+
// scopes the duration to indexing responses in both views.
|
|
888
|
+
//
|
|
889
|
+
// Deliberately NOT `created`: that is the enqueue time, so a pass that sat
|
|
890
|
+
// in the queue for an hour before running would have reported the wait as
|
|
891
|
+
// though it were the read. No fallback to it either, for the same reason --
|
|
892
|
+
// a missing `executed` means no duration is shown at all.
|
|
893
|
+
if (indexFile && isFinite(executedTs) && executedTs > 0) okm._tsStart = executedTs;
|
|
877
894
|
if (reportedComplete) okm._indexComplete = true;
|
|
878
895
|
mapped.push(okm);
|
|
879
896
|
}
|
package/src/engine/host.ts
CHANGED
|
@@ -225,6 +225,16 @@ export interface ChatMessage {
|
|
|
225
225
|
* it is created, then reconciled to the server value on the next history load.
|
|
226
226
|
* Absent while a turn is still pending, so no time shows on a "Thinking" bubble. */
|
|
227
227
|
_ts?: number;
|
|
228
|
+
/** Epoch ms the worker began EXECUTING this indexing pass (the SDK's `executed`,
|
|
229
|
+
* not the enqueue time `created`), carried on the ASSISTANT bubble so it can
|
|
230
|
+
* show how long the pass actually took
|
|
231
|
+
* without reaching for its request bubble (a continuation's request is
|
|
232
|
+
* deliberately hidden from the expanded row, so it is not always there).
|
|
233
|
+
*
|
|
234
|
+
* Stamped ONLY on an indexing pass's reply. Its presence is therefore also
|
|
235
|
+
* what SCOPES the duration to indexing responses: an ordinary chat answer
|
|
236
|
+
* never carries it, so neither view needs a second test. */
|
|
237
|
+
_tsStart?: number;
|
|
228
238
|
// History cache key (`projectId#platform`) this bubble was created under.
|
|
229
239
|
// Stamped on LOCALLY-created bubbles only (the optimistic user message and
|
|
230
240
|
// its "Thinking..." placeholder); server-mapped bubbles are identified by
|
package/src/engine/session.ts
CHANGED
|
@@ -81,6 +81,13 @@ const INDEX_DISPATCH_CLAIM_MS = 2 * 60 * 1000;
|
|
|
81
81
|
// that is still alive. Fixed and finite: this must never become a poll.
|
|
82
82
|
const WORKER_PASS_ADOPT_ATTEMPTS = [0, 2000, 6000];
|
|
83
83
|
|
|
84
|
+
/** How recent a loaded indexing pass has to be to count as evidence that a worker
|
|
85
|
+
* chain may still be advancing. Generous on purpose: it only has to outlast the
|
|
86
|
+
* gap between two passes of one file, and the cost of being wrong is three queue
|
|
87
|
+
* round trips, while the cost of being too tight is a file that reads "Indexed"
|
|
88
|
+
* while it is still being read. */
|
|
89
|
+
const RECENT_INDEX_PASS_EVIDENCE_MS = 10 * 60 * 1000;
|
|
90
|
+
|
|
84
91
|
// awaitIndexingDrained: how often it re-asks the background-indexing queue, and
|
|
85
92
|
// how many consecutive empty answers it needs before believing the chains are
|
|
86
93
|
// over. More than one for the same reason the adopt ladder above looks twice —
|
|
@@ -4435,7 +4442,38 @@ export class ChatSession {
|
|
|
4435
4442
|
// full passive ladder for indexing that does not exist.
|
|
4436
4443
|
var found = false;
|
|
4437
4444
|
this.historyItemPolls.forEach(function (h: any) { if (h && h.kind === 'bg') found = true; });
|
|
4438
|
-
return
|
|
4445
|
+
if (found) return true;
|
|
4446
|
+
|
|
4447
|
+
// A chain this client did not START leaves none of the three signals above,
|
|
4448
|
+
// and they are all signals of LOCAL DISPATCH: the bgTaskQueue entry covers
|
|
4449
|
+
// only the pass this page sent and is spliced out as soon as it settles, a
|
|
4450
|
+
// reload clears that array outright, and liveIndexKeys is empty in exactly
|
|
4451
|
+
// the moment the probe just missed. A run begun on the db-files page and
|
|
4452
|
+
// then opened in the chat has none of them, so the ladder never climbed for
|
|
4453
|
+
// it: one probe landing in the gap between pass N resolving and pass N+1
|
|
4454
|
+
// being enqueued marked the file finished, and with no local pass left to
|
|
4455
|
+
// settle, nothing asked again. The row stayed green on a file still being
|
|
4456
|
+
// read.
|
|
4457
|
+
//
|
|
4458
|
+
// The passes themselves are the evidence in that case. Bounded by recency,
|
|
4459
|
+
// because the thing this gate exists to prevent is the climb running on an
|
|
4460
|
+
// idle tab whose indexing finished long ago - and such a chat's newest
|
|
4461
|
+
// indexing pass is hours or days old, so it still does not climb.
|
|
4462
|
+
// Date.now(), NOT nowMs(): `_ts` is a wall-clock epoch (the same clock the
|
|
4463
|
+
// run:: records are stamped with), while nowMs() is performance.now() --
|
|
4464
|
+
// milliseconds since process start. Subtracting the window from that gives a
|
|
4465
|
+
// NEGATIVE cutoff, every epoch `_ts` clears it, and the gate would then be
|
|
4466
|
+
// true for any chat that has ever indexed anything: exactly the always-climb
|
|
4467
|
+
// this gate was added to stop.
|
|
4468
|
+
var cutoff = Date.now() - RECENT_INDEX_PASS_EVIDENCE_MS;
|
|
4469
|
+
var msgs = this.state.messages;
|
|
4470
|
+
for (var mi = msgs.length - 1; mi >= 0; mi--) {
|
|
4471
|
+
var m = msgs[mi];
|
|
4472
|
+
if (!m || !m._indexFile) continue;
|
|
4473
|
+
if (typeof m._ts !== 'number') continue;
|
|
4474
|
+
if (m._ts >= cutoff) return true;
|
|
4475
|
+
}
|
|
4476
|
+
return false;
|
|
4439
4477
|
}
|
|
4440
4478
|
|
|
4441
4479
|
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
package/src/engine/time.ts
CHANGED
|
@@ -32,3 +32,32 @@ export function formatChatTimestamp(ms?: number): string {
|
|
|
32
32
|
return '';
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* "4s", "2m 4s", "1h 2m 4s" — how long one indexing pass took.
|
|
38
|
+
*
|
|
39
|
+
* HOURS ARE THE LARGEST UNIT, deliberately: a pass is a single model turn, so a
|
|
40
|
+
* span of days is not a long read but bad data (a stale `updated` stamp, a clock
|
|
41
|
+
* skew, a row rewritten much later). Rolling those into hours makes them read as
|
|
42
|
+
* the anomaly they are — "49h" — instead of dressing them up as a plausible
|
|
43
|
+
* "2d 1h".
|
|
44
|
+
*
|
|
45
|
+
* A zero unit is dropped rather than padded ("1h 4s", not "1h 0m 4s"); seconds
|
|
46
|
+
* are always shown so the string is never bare.
|
|
47
|
+
*
|
|
48
|
+
* Empty string for anything under a second, negative, or non-finite — a pass
|
|
49
|
+
* cannot take no time, so those are skew or missing data, and callers gate on
|
|
50
|
+
* the result being truthy exactly as they do for formatChatTimestamp.
|
|
51
|
+
*/
|
|
52
|
+
export function formatDuration(ms?: number): string {
|
|
53
|
+
if (typeof ms !== 'number' || !isFinite(ms) || ms < 1000) return '';
|
|
54
|
+
var total = Math.floor(ms / 1000);
|
|
55
|
+
var h = Math.floor(total / 3600);
|
|
56
|
+
var m = Math.floor((total % 3600) / 60);
|
|
57
|
+
var s = total % 60;
|
|
58
|
+
var out = [];
|
|
59
|
+
if (h) out.push(h + 'h');
|
|
60
|
+
if (m) out.push(m + 'm');
|
|
61
|
+
out.push(s + 's');
|
|
62
|
+
return out.join(' ');
|
|
63
|
+
}
|