bunnyquery 1.9.6 → 1.9.10

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/README.md CHANGED
@@ -124,6 +124,7 @@ Mounts the widget. Returns the `BunnyQuery` object.
124
124
  | `hostDomain` | `string` | `null` | db-CDN host for temporary file URLs. Defaults to `skapi.app` (dev) / `skapi.com` (prod). |
125
125
  | `attachmentParsers` | `array` | `null` | Client-side attachment parsers. See [Attachment parser plugins](#attachment-parser-plugins). |
126
126
  | `windowedIndexing` | `boolean` | `true` | Server-driven windowed indexing for text and grid files (see [file types](#supported-file-types)). Pass `false` to fall back to agent-driven paging, which keeps the traversal inside the model's turn budget and the tab open. |
127
+ | `liveStreaming` | `boolean` | `false` | Paint a chat answer into its bubble as it arrives, instead of at the end. A **request**, not a switch: the widget honours it only when your page's `skapi-js` actually carries skapi's half of the stream flag (it checks for `clientSecretRequestStream` and `clientSecretRequestFinalize`), and otherwise warns once and falls back to buffered replies. An older SDK silently drops the flag, which would leave the destination streaming SSE into a buffered row that reads back empty. It still also needs a polling worker that relays the response bytes, which the widget cannot check, so leave it off until the region you talk to is deployed. |
127
128
 
128
129
  ### Methods
129
130
 
@@ -428,6 +429,10 @@ helpers. See the `.d.ts` shipped with `bunnyquery/engine`.
428
429
  | `poll` | `number?` | Value attached as `poll` on every request. Omit it if your `clientSecretRequest` already resolves with the final body; pass `0` for the deployed `skapi-js@latest` (needed for the early ack + a manual `.poll()` handle that powers queued-send cancel, the widget's case). |
429
430
  | `attachmentParsers` | `array?` | Client-side attachment parsers, registered at configure time. More can be added later with `registerAttachmentParser()`. See [Attachment parser plugins](#attachment-parser-plugins). |
430
431
  | `windowedIndexing` | `boolean?` | Opt in to **server-driven** windowed indexing for text and grid files (see [file types](#supported-file-types)). Off by default in the engine; the widget passes it as `true`. The deployed skapi workers support it; only leave it off against a self-hosted worker that does not yet strip the `_skapi_window` directive, where it would reach the provider as an unknown body field and fail the call terminally with no retry. |
432
+ | `liveStreaming` | `boolean?` | Opt in to **live streaming** of chat turns. Off by default, and the backend ships first: a streamed row settles with a status and NO body (the answer was the stream), so against a worker that does not relay, the turn reads back empty. Pair it with `clientSecretRequestFinalize` and `clientSecretRequestStream`, and gate it on `skapiSupportsStreaming(skapi)`. |
433
+ | `clientSecretRequestFinalize` | `function?` | `skapi.clientSecretRequestFinalize`, bound to your Skapi instance. Stores the version of a streamed turn that history keeps (the engine sends the assembled provider body, so it reads back exactly like a buffered turn) and releases that request's chunks. Without it a streamed turn is never finalized and its row stays empty. |
434
+ | `clientSecretRequestStream` | `function?` | `skapi.clientSecretRequestStream`, bound to your Skapi instance. The **second half of the durability guarantee**: a row that settles while no poll is attached (closed tab, discarded background tab, slept device) is never finalized, so its answer stays in the chunk store and its history row is terminal and empty. Given the request id this drains that turn's chunks in one pass; the engine parses them exactly as it parses a live stream and finalizes what it read, so each row is recovered at most once. Without it the engine mints no recovery marker at all and behaves as it did before streaming. |
435
+ | `onLiveStreamUpdate` | `function?` | Observation hook for a streaming turn (`{ serverItemId, ownerKey, phase, text, thinkingText, toolNames, complete, errored }`). The engine already paints the answer text itself, so this is only for affordances it does not decide the presentation of. Never throw from it. |
431
436
 
432
437
  ### Display and paging helpers
433
438
 
package/bunnyquery.css CHANGED
@@ -664,23 +664,42 @@
664
664
  background: var(--bq-paper);
665
665
  }
666
666
 
667
+ /* ZERO HEIGHT IN FLOW, DELIBERATELY. Mirrors agent.vue, where the reasoning is
668
+ written out in full: this bar mounts when a history fetch starts and unmounts
669
+ when the page lands, both while the reader sits at the top of the list, and as
670
+ an in-flow sticky element it moved every row by its own height each time
671
+ (measured in Chrome with overflow-anchor:none). The outer box now contributes
672
+ nothing to layout and the inner strip overflows it, so the bar still sits
673
+ pinned at the top of the viewport while mounting it moves nothing. */
667
674
  .bq-history-loading {
668
675
  position: sticky;
669
676
  top: 0;
670
677
  z-index: 1;
678
+ height: 0;
679
+ padding: 0;
680
+ overflow: visible;
681
+ pointer-events: none;
682
+ }
683
+ .bq-history-loading-inner {
671
684
  display: flex;
685
+ align-items: center;
672
686
  justify-content: center;
673
687
  gap: 0.1em;
674
688
  padding: 0.5rem 0 1rem;
675
689
  font-size: 0.8rem;
676
690
  color: var(--bq-muted);
677
- background: var(--bq-paper);
691
+ background: linear-gradient(to bottom, var(--bq-paper) 70%, transparent);
678
692
  }
679
- /* initial first-page load: center it in the (empty) messages area */
693
+ /* initial first-page load: center it in the (empty) messages area. A full-box
694
+ overlay rather than a top strip, so it takes its height back - and it replaces
695
+ an EMPTY messages area, so it has nothing to move. */
680
696
  .bq-history-loading.is-initial {
681
697
  position: absolute;
682
698
  inset: 0;
699
+ height: auto;
700
+ display: flex;
683
701
  align-items: center;
702
+ justify-content: center;
684
703
  padding: 0;
685
704
  }
686
705
 
@@ -1236,6 +1255,38 @@
1236
1255
  .bq-cancel-queue-btn:hover:not(.is-disabled) { background: var(--bq-warning-bg); color: var(--bq-warning); }
1237
1256
  .bq-cancel-queue-btn.is-disabled { opacity: 0.3; cursor: not-allowed; pointer-events: none; }
1238
1257
 
1258
+ /* ---- unfinalized streamed answer (ask for it) ----------------------------
1259
+ A turn whose answer was streamed and never finalized keeps its bytes in the
1260
+ chunk store, and this bubble is what the reader gets when nothing is currently
1261
+ fetching them: the per-load recovery cap left this one behind, or a read failed.
1262
+ It is deliberately NOT the danger red of an error bubble - nothing is broken and
1263
+ nothing is lost, the answer is simply not here yet - and deliberately not a
1264
+ spinner, which would promise an arrival nobody has scheduled. Shared by both
1265
+ chatboxes (this file is bunnyquery/styles/chat.css, which agent.vue imports). */
1266
+ .bq-stream-recover-note {
1267
+ display: block;
1268
+ font-size: 0.72rem;
1269
+ color: var(--bq-muted);
1270
+ font-style: italic;
1271
+ }
1272
+ .bq-bubble.is-stream-failed .bq-stream-recover-note { color: var(--bq-warning); }
1273
+ .bq-stream-recover-btn {
1274
+ display: inline-block;
1275
+ margin-top: 0.4rem;
1276
+ padding: 0.18rem 0.55rem;
1277
+ min-height: 0;
1278
+ border: 1px solid var(--bq-line);
1279
+ background: transparent;
1280
+ color: inherit;
1281
+ font-size: 0.72rem;
1282
+ line-height: 1.3;
1283
+ cursor: pointer;
1284
+ box-shadow: none;
1285
+ border-radius: 0;
1286
+ }
1287
+ .bq-stream-recover-btn:hover { background: rgba(127, 127, 127, 0.12); }
1288
+ .bq-bubble.is-stream-failed .bq-stream-recover-btn { border-color: var(--bq-warning-border); color: var(--bq-warning); }
1289
+
1239
1290
  /* ---- collapsed background-indexing group ---------------------------------*/
1240
1291
  /* One file's many indexing passes (first pass + every CONTINUE pass, each with
1241
1292
  a request AND a response bubble) render as a single status row instead of