bunnyquery 1.9.7 → 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 +5 -0
- package/bunnyquery.css +53 -2
- package/bunnyquery.js +1822 -177
- package/dist/engine.cjs +1746 -114
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +1271 -6
- package/dist/engine.d.ts +1271 -6
- package/dist/engine.mjs +1709 -115
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/config.ts +243 -0
- package/src/engine/errors.ts +87 -1
- package/src/engine/history.ts +113 -2
- package/src/engine/host.ts +85 -0
- package/src/engine/index.ts +108 -2
- package/src/engine/project_settings.ts +303 -0
- package/src/engine/prompts/chat_system_prompt.ts +15 -3
- package/src/engine/requests.ts +130 -1
- package/src/engine/session.ts +1553 -29
- package/src/engine/sse.ts +1054 -0
- package/src/widget.css +21 -2
- package/styles/chat.css +32 -0
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
|