bunnyquery 1.10.6 → 1.10.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunnyquery",
3
- "version": "1.10.6",
3
+ "version": "1.10.8",
4
4
  "description": "Embeddable BunnyQuery AI chat widget + its framework-agnostic chat engine",
5
5
  "main": "bunnyquery.js",
6
6
  "exports": {
@@ -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
 
@@ -852,6 +858,13 @@ export function mapHistoryListToMessages(list: any[], platform: 'claude' | 'open
852
858
  if (item._isBgTask) em.isBackgroundTask = true;
853
859
  if (serverItemId !== undefined) em._serverItemId = serverItemId;
854
860
  if (replyTs !== undefined) em._ts = replyTs;
861
+ // A pass that FAILED still ran, and how long it ran before failing is worth
862
+ // as much as a successful pass's duration -- often more, since that is where
863
+ // a timeout shows itself. `att` is stamped before the upstream call fires,
864
+ // so a failed row carries it exactly as a resolved one does. (Same stamp as
865
+ // the success branch below; the stream-pending branch deliberately has none,
866
+ // having no end time yet.)
867
+ if (indexFile && isFinite(executedTs) && executedTs > 0) em._tsStart = executedTs;
855
868
  mapped.push(em);
856
869
  } else if (isStreamPending) {
857
870
  // The bubble stands for the turn so the merge has something to key on and
@@ -874,6 +887,17 @@ export function mapHistoryListToMessages(list: any[], platform: 'claude' | 'open
874
887
  if (isCompact) okm._compact = true;
875
888
  if (serverItemId !== undefined) okm._serverItemId = serverItemId;
876
889
  if (replyTs !== undefined) okm._ts = replyTs;
890
+ // How long THIS pass took: from the moment the worker began EXECUTING it to
891
+ // the moment it resolved. Carried on the reply so the bubble can show it
892
+ // without its request bubble (a continuation's request is hidden from the
893
+ // expanded row). Only for an indexing pass: `_tsStart`'s presence is what
894
+ // scopes the duration to indexing responses in both views.
895
+ //
896
+ // Deliberately NOT `created`: that is the enqueue time, so a pass that sat
897
+ // in the queue for an hour before running would have reported the wait as
898
+ // though it were the read. No fallback to it either, for the same reason --
899
+ // a missing `executed` means no duration is shown at all.
900
+ if (indexFile && isFinite(executedTs) && executedTs > 0) okm._tsStart = executedTs;
877
901
  if (reportedComplete) okm._indexComplete = true;
878
902
  mapped.push(okm);
879
903
  }
@@ -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
@@ -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
+ }