bunnyquery 1.10.12 → 1.11.1
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 +23 -11
- package/bunnyquery.js +139 -62
- package/dist/engine.cjs +78 -23
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +137 -35
- package/dist/engine.d.ts +137 -35
- package/dist/engine.mjs +78 -23
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/budget.ts +13 -3
- package/src/engine/config.ts +249 -39
- package/src/engine/history.ts +1 -1
- package/src/engine/requests.ts +25 -17
- package/src/engine/session.ts +8 -9
- package/src/engine/sse.ts +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,11 @@ call `BunnyQuery.init()`:
|
|
|
86
86
|
That's it. BunnyQuery takes over the `#chatbox` element and renders the login or
|
|
87
87
|
chat view depending on the user's session.
|
|
88
88
|
|
|
89
|
+
The widget works with whichever `skapi-js` your page loads. On a current SDK it calls the
|
|
90
|
+
`forwardRequest` family, and on an earlier one it falls back to the `clientSecretRequest` family,
|
|
91
|
+
deciding by whether the SDK has `forwardRequestHistory`. It never decides by `forwardRequest` alone,
|
|
92
|
+
because on an earlier SDK that name belongs to a different, retired method.
|
|
93
|
+
|
|
89
94
|
## What's in the package
|
|
90
95
|
|
|
91
96
|
| Path | Purpose |
|
|
@@ -130,7 +135,7 @@ Mounts the widget. Returns the `BunnyQuery` object.
|
|
|
130
135
|
| `attachmentParsers` | `array` | `null` | Client-side attachment parsers. See [Attachment parser plugins](#attachment-parser-plugins). |
|
|
131
136
|
| `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. |
|
|
132
137
|
| `allowAnonymous` | `boolean` | `null` | Open the chat with no login for visitors without an account. `null` follows the project's own "Allow anonymous users" setting (`getConnectionInfo().conf.require_login`); `true`/`false` pins it. |
|
|
133
|
-
| `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. |
|
|
138
|
+
| `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 `forwardRequestStream` and `forwardRequestFinalize`, or their older names `clientSecretRequestStream` and `clientSecretRequestFinalize` on an earlier `skapi-js`), 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. |
|
|
134
139
|
| `liveStreamingRealtime` | `boolean` | `false` | Deliver streamed chunks over skapi's websocket instead of waiting for the next poll tick. Requires `liveStreaming`. Off unless you ask for it: skapi's `joinRealtime` **replaces** the connection's group, so for the length of a turn it takes the room out from under whatever else your app uses realtime for. Purely an accelerator; with it off the reply still streams, on the poll's cadence. |
|
|
135
140
|
|
|
136
141
|
### Rebranding the widget
|
|
@@ -447,9 +452,9 @@ import 'bunnyquery/styles/chat.css';
|
|
|
447
452
|
|
|
448
453
|
// 1. Inject the skapi transport + MCP endpoint ONCE at startup.
|
|
449
454
|
configureChatEngine({
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
skapi.
|
|
455
|
+
forwardRequest: (form, opts) => skapi.forwardRequest(form, opts),
|
|
456
|
+
forwardRequestHistory: (params, fetchOptions) =>
|
|
457
|
+
skapi.forwardRequestHistory(params, fetchOptions),
|
|
453
458
|
mcpBaseUrl: 'https://mcp.broadwayinc.computer',
|
|
454
459
|
poll: 0, // see the note below
|
|
455
460
|
});
|
|
@@ -474,22 +479,29 @@ helpers. See the `.d.ts` shipped with `bunnyquery/engine`.
|
|
|
474
479
|
|
|
475
480
|
| Option | Type | Description |
|
|
476
481
|
| ----------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
|
|
477
|
-
| `
|
|
478
|
-
| `
|
|
482
|
+
| `forwardRequest` | `function` | `skapi.forwardRequest`, bound to your Skapi instance. The engine calls it as `forwardRequest(null, opts)`. **Required**, or its deprecated alias below. |
|
|
483
|
+
| `forwardRequestHistory` | `function` | `skapi.forwardRequestHistory`, bound to your Skapi instance. **Required**, or its deprecated alias below. |
|
|
479
484
|
| `mcpBaseUrl` | `string` | MCP server base URL (you resolve prod vs dev). **Required.** |
|
|
480
|
-
| `poll` | `number?` | Value attached as `poll` on every request. Omit it if your `
|
|
485
|
+
| `poll` | `number?` | Value attached as `poll` on every request. Omit it if your `forwardRequest` 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). |
|
|
481
486
|
| `attachmentParsers` | `array?` | Client-side attachment parsers, registered at configure time. More can be added later with `registerAttachmentParser()`. See [Attachment parser plugins](#attachment-parser-plugins). |
|
|
482
487
|
| `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. |
|
|
483
|
-
| `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 `
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
488
|
+
| `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 `forwardRequestFinalize` and `forwardRequestStream`, and gate it on `skapiSupportsStreaming(skapi)`. |
|
|
489
|
+
| `forwardRequestFinalize` | `function?` | `skapi.forwardRequestFinalize`, 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. |
|
|
490
|
+
| `forwardRequestStream` | `function?` | `skapi.forwardRequestStream`, 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. |
|
|
486
491
|
| `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. |
|
|
487
492
|
| `liveStreamingRealtime` | `boolean?` | Push relayed chunks over skapi's websocket as well. Requires `liveStreaming`. Off by default: `joinRealtime` replaces the connection's group for the length of a turn, so only a host that owns its skapi instance should opt in. |
|
|
488
|
-
| `streamRecovery` | `boolean?` | Set `false` to force the read-back of already-streamed turns **off**, even though the chunk reader is injected. There is no need to set it to turn recovery on: injecting `
|
|
493
|
+
| `streamRecovery` | `boolean?` | Set `false` to force the read-back of already-streamed turns **off**, even though the chunk reader is injected. There is no need to set it to turn recovery on: injecting `forwardRequestStream` is what arms it. |
|
|
489
494
|
| `mintIndexDoneMarker` | `function?` | Write the durable "indexing finished" marker (`done::<path>`, reference `src::<path>`, table `__INDEXING__`) for the runs this client knows are complete. Best-effort, must never throw. Without it the engine falls back to inference. |
|
|
490
495
|
| `upsertIndexRunRecord` | `function?` | Create-or-update the per-file run record (`run::<path>`, reference `src::<path>`, table `__INDEXING__`), which is what lets chat rows and files-page badges paint without scanning background history. You implement the upsert (the records API has none) and the status precedence: `'working'` must never overwrite a terminal status. Without it the engine uses the legacy scan/probe path. |
|
|
491
496
|
| `csrHistoryItemLookup` | `function?` | Single-item `csr-poll` point lookup, used by `ChatSession.hydrateCompactItems` to fetch a compact history stub's real body when an indexing row is expanded. Without it stubs keep their server-extracted heads. |
|
|
492
497
|
|
|
498
|
+
**Deprecated config keys.** `clientSecretRequest`, `clientSecretRequestHistory`,
|
|
499
|
+
`clientSecretRequestFinalize` and `clientSecretRequestStream` are still accepted, so a host written
|
|
500
|
+
against an earlier release keeps working unchanged. When both spellings are given the new one wins.
|
|
501
|
+
Through the old `clientSecretRequest` key the engine sends the secret's name as `clientSecretName`,
|
|
502
|
+
exactly as before, and through `forwardRequest` it sends `secretName`. Every request also names the
|
|
503
|
+
project it runs against in `service` and `owner`, and both keys pass those through.
|
|
504
|
+
|
|
493
505
|
### Display and paging helpers
|
|
494
506
|
|
|
495
507
|
Two shared transforms exist so that a second chat UI behaves identically to the
|
package/bunnyquery.js
CHANGED
|
@@ -56,6 +56,46 @@
|
|
|
56
56
|
}
|
|
57
57
|
return _config;
|
|
58
58
|
}
|
|
59
|
+
function resolveForwardRequest() {
|
|
60
|
+
const cfg = chatEngineConfig();
|
|
61
|
+
const fwd = cfg.forwardRequest;
|
|
62
|
+
if (typeof fwd === "function") {
|
|
63
|
+
return (opts) => fwd.call(cfg, null, opts);
|
|
64
|
+
}
|
|
65
|
+
const legacy = cfg.clientSecretRequest;
|
|
66
|
+
if (typeof legacy === "function") {
|
|
67
|
+
return (opts) => {
|
|
68
|
+
if (!opts || typeof opts !== "object" || !("secretName" in opts)) return legacy.call(cfg, opts);
|
|
69
|
+
const { secretName, ...rest } = opts;
|
|
70
|
+
return legacy.call(cfg, Object.assign({ clientSecretName: secretName }, rest));
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
throw new Error(
|
|
74
|
+
"[chat-engine] No request transport: configureChatEngine() needs forwardRequest (or the deprecated clientSecretRequest), bound to a skapi instance."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function resolveForwardRequestHistory() {
|
|
78
|
+
const cfg = chatEngineConfig();
|
|
79
|
+
const hist = typeof cfg.forwardRequestHistory === "function" ? cfg.forwardRequestHistory : cfg.clientSecretRequestHistory;
|
|
80
|
+
if (typeof hist === "function") {
|
|
81
|
+
return (params, fetchOptions) => hist.call(cfg, params, fetchOptions);
|
|
82
|
+
}
|
|
83
|
+
throw new Error(
|
|
84
|
+
"[chat-engine] No history transport: configureChatEngine() needs forwardRequestHistory (or the deprecated clientSecretRequestHistory), bound to a skapi instance."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
function resolveForwardRequestFinalize() {
|
|
88
|
+
const cfg = chatEngineConfig();
|
|
89
|
+
if (typeof cfg.forwardRequestFinalize === "function") return cfg.forwardRequestFinalize;
|
|
90
|
+
if (typeof cfg.clientSecretRequestFinalize === "function") return cfg.clientSecretRequestFinalize;
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
function resolveForwardRequestStream() {
|
|
94
|
+
const cfg = chatEngineConfig();
|
|
95
|
+
if (typeof cfg.forwardRequestStream === "function") return cfg.forwardRequestStream;
|
|
96
|
+
if (typeof cfg.clientSecretRequestStream === "function") return cfg.clientSecretRequestStream;
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
59
99
|
function windowedIndexingEnabled() {
|
|
60
100
|
return _config?.windowedIndexing === true;
|
|
61
101
|
}
|
|
@@ -66,11 +106,13 @@
|
|
|
66
106
|
return _config?.liveStreaming === true;
|
|
67
107
|
}
|
|
68
108
|
function streamRecoveryEnabled() {
|
|
69
|
-
if (_config
|
|
70
|
-
|
|
109
|
+
if (!_config) return false;
|
|
110
|
+
if (_config.streamRecovery === false) return false;
|
|
111
|
+
return typeof resolveForwardRequestStream() === "function";
|
|
71
112
|
}
|
|
72
113
|
function skapiSupportsStreaming(sk) {
|
|
73
|
-
|
|
114
|
+
if (!sk) return false;
|
|
115
|
+
return typeof sk.forwardRequestStream === "function" && typeof sk.forwardRequestFinalize === "function" || typeof sk.clientSecretRequestStream === "function" && typeof sk.clientSecretRequestFinalize === "function";
|
|
74
116
|
}
|
|
75
117
|
function pollOpt() {
|
|
76
118
|
const p = _config?.poll;
|
|
@@ -982,6 +1024,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
982
1024
|
var CONTEXT_WINDOW_DEFAULT = { claude: 2e5, openai: 128e3 };
|
|
983
1025
|
var CONTEXT_WINDOW_BY_MODEL = {
|
|
984
1026
|
// claude, exact ids
|
|
1027
|
+
"claude-fable-5-1": 1e6,
|
|
1028
|
+
"claude-opus-5-5": 1e6,
|
|
1029
|
+
"claude-sonnet-5-5": 1e6,
|
|
985
1030
|
"claude-fable-5": 1e6,
|
|
986
1031
|
"claude-opus-5": 1e6,
|
|
987
1032
|
"claude-opus-4-8": 1e6,
|
|
@@ -995,6 +1040,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
995
1040
|
"claude-haiku-4-5": 2e5,
|
|
996
1041
|
"claude-3-5-sonnet": 2e5,
|
|
997
1042
|
// openai, exact ids
|
|
1043
|
+
"gpt-6-astra": 105e4,
|
|
1044
|
+
"gpt-6-sol": 105e4,
|
|
1045
|
+
"gpt-6-luna": 105e4,
|
|
998
1046
|
"gpt-5.6-sol": 105e4,
|
|
999
1047
|
"gpt-5.6-terra": 105e4,
|
|
1000
1048
|
"gpt-5.6-luna": 105e4,
|
|
@@ -1011,11 +1059,15 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1011
1059
|
"claude-opus": 1e6,
|
|
1012
1060
|
"claude-sonnet": 1e6,
|
|
1013
1061
|
"claude-haiku": 2e5,
|
|
1062
|
+
"gpt-6": 105e4,
|
|
1014
1063
|
"gpt-5.6": 105e4,
|
|
1015
1064
|
"gpt-5": 128e3
|
|
1016
1065
|
};
|
|
1017
1066
|
var MAX_OUTPUT_BY_MODEL = {
|
|
1018
1067
|
// claude
|
|
1068
|
+
"claude-fable-5-1": 128e3,
|
|
1069
|
+
"claude-opus-5-5": 128e3,
|
|
1070
|
+
"claude-sonnet-5-5": 128e3,
|
|
1019
1071
|
"claude-fable-5": 128e3,
|
|
1020
1072
|
"claude-opus-5": 128e3,
|
|
1021
1073
|
"claude-opus-4-8": 128e3,
|
|
@@ -1024,6 +1076,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1024
1076
|
"claude-haiku-4-5": 64e3,
|
|
1025
1077
|
"claude-3-5-sonnet": 8e3,
|
|
1026
1078
|
// openai
|
|
1079
|
+
"gpt-6-astra": 128e3,
|
|
1080
|
+
"gpt-6-sol": 128e3,
|
|
1081
|
+
"gpt-6-luna": 128e3,
|
|
1027
1082
|
"gpt-5.6-sol": 128e3,
|
|
1028
1083
|
"gpt-5.6-terra": 128e3,
|
|
1029
1084
|
"gpt-5.6-luna": 128e3,
|
|
@@ -1040,6 +1095,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1040
1095
|
"claude-opus": 128e3,
|
|
1041
1096
|
"claude-sonnet": 64e3,
|
|
1042
1097
|
"claude-haiku": 64e3,
|
|
1098
|
+
"gpt-6": 128e3,
|
|
1043
1099
|
"gpt-5.6": 128e3,
|
|
1044
1100
|
"gpt-5": 128e3
|
|
1045
1101
|
};
|
|
@@ -2092,7 +2148,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2092
2148
|
const project = publicProjectId || service;
|
|
2093
2149
|
return { url: String(mcpUrl()).replace(/\/+$/, "") + "/p/" + project };
|
|
2094
2150
|
}
|
|
2095
|
-
var
|
|
2151
|
+
var forwardRequest = (opts) => resolveForwardRequest()(opts);
|
|
2096
2152
|
var CHAT_STREAM_ON = Object.freeze({
|
|
2097
2153
|
transport: Object.freeze({ stream: true }),
|
|
2098
2154
|
body: Object.freeze({ stream: true })
|
|
@@ -2283,8 +2339,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2283
2339
|
mcpServerDefinition.authorization_token = mcpServer.authorizationToken;
|
|
2284
2340
|
}
|
|
2285
2341
|
const stream = chatStreamWiring(userId || service);
|
|
2286
|
-
return
|
|
2287
|
-
|
|
2342
|
+
return forwardRequest({
|
|
2343
|
+
secretName: "claude",
|
|
2288
2344
|
queue: userId || service,
|
|
2289
2345
|
service,
|
|
2290
2346
|
owner,
|
|
@@ -2389,8 +2445,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2389
2445
|
}))
|
|
2390
2446
|
];
|
|
2391
2447
|
const stream = chatStreamWiring(userId || service);
|
|
2392
|
-
return
|
|
2393
|
-
|
|
2448
|
+
return forwardRequest({
|
|
2449
|
+
secretName: "openai",
|
|
2394
2450
|
queue: userId || service,
|
|
2395
2451
|
service,
|
|
2396
2452
|
owner,
|
|
@@ -2539,8 +2595,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2539
2595
|
if (platform === "openai") {
|
|
2540
2596
|
const resolvedModel2 = info.model || DEFAULT_OPENAI_MODEL;
|
|
2541
2597
|
const imageDetail = getOpenAIImageDetail(resolvedModel2);
|
|
2542
|
-
return tapDispatchFailure(
|
|
2543
|
-
|
|
2598
|
+
return tapDispatchFailure(forwardRequest({
|
|
2599
|
+
secretName: "openai",
|
|
2544
2600
|
queue: bgIndexingQueueName(info.userId, service),
|
|
2545
2601
|
service,
|
|
2546
2602
|
owner,
|
|
@@ -2586,8 +2642,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2586
2642
|
}));
|
|
2587
2643
|
}
|
|
2588
2644
|
const resolvedModel = info.model || DEFAULT_CLAUDE_MODEL;
|
|
2589
|
-
return tapDispatchFailure(
|
|
2590
|
-
|
|
2645
|
+
return tapDispatchFailure(forwardRequest({
|
|
2646
|
+
secretName: "claude",
|
|
2591
2647
|
queue: bgIndexingQueueName(info.userId, service),
|
|
2592
2648
|
service,
|
|
2593
2649
|
owner,
|
|
@@ -2721,7 +2777,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2721
2777
|
params.compact ? { compact: true } : {},
|
|
2722
2778
|
params.queue_exclude ? { queue_exclude: params.queue_exclude } : {}
|
|
2723
2779
|
);
|
|
2724
|
-
return
|
|
2780
|
+
return resolveForwardRequestHistory()(
|
|
2725
2781
|
p,
|
|
2726
2782
|
Object.assign({ ascending: false, limit: CHAT_HISTORY_PAGE_LIMIT }, fetchOptions)
|
|
2727
2783
|
);
|
|
@@ -4460,7 +4516,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4460
4516
|
_finalizeStreamedTurn(st) {
|
|
4461
4517
|
if (st.finalized) return;
|
|
4462
4518
|
if (!this._mayFinalize(st)) return;
|
|
4463
|
-
var fin =
|
|
4519
|
+
var fin = resolveForwardRequestFinalize();
|
|
4464
4520
|
if (!fin || st.finalBody == null) return;
|
|
4465
4521
|
st.finalized = true;
|
|
4466
4522
|
var url = st.platform === "openai" ? OPENAI_RESPONSES_API_URL : ANTHROPIC_MESSAGES_API_URL;
|
|
@@ -4471,10 +4527,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4471
4527
|
service: st.projectId,
|
|
4472
4528
|
owner: st.owner
|
|
4473
4529
|
})).catch(function(err) {
|
|
4474
|
-
console.warn("[chat-engine]
|
|
4530
|
+
console.warn("[chat-engine] forwardRequestFinalize failed", err);
|
|
4475
4531
|
});
|
|
4476
4532
|
} catch (e) {
|
|
4477
|
-
console.warn("[chat-engine]
|
|
4533
|
+
console.warn("[chat-engine] forwardRequestFinalize threw", e);
|
|
4478
4534
|
}
|
|
4479
4535
|
}
|
|
4480
4536
|
/** Painted-but-unsettled live text on a bubble, for the typewriter to resume from.
|
|
@@ -4689,8 +4745,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4689
4745
|
return this._readBackStreamedTurn(itemId, this.getHistoryCacheKey(), platform, id ? id.projectId : "", id ? id.owner : "", true);
|
|
4690
4746
|
}
|
|
4691
4747
|
_readBackStreamedTurn(itemId, ownerKey, platform, projectId, owner, manual) {
|
|
4692
|
-
var
|
|
4693
|
-
var read = cfg.clientSecretRequestStream;
|
|
4748
|
+
var read = resolveForwardRequestStream();
|
|
4694
4749
|
if (!read || !itemId) return Promise.resolve();
|
|
4695
4750
|
if (this._rec().inflight[itemId]) return Promise.resolve();
|
|
4696
4751
|
if (!manual && this._rec().attempted[itemId]) {
|
|
@@ -4803,7 +4858,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4803
4858
|
}
|
|
4804
4859
|
if (!degraded) delete this._rec().incomplete[itemId];
|
|
4805
4860
|
if (!store || body == null || isErr) return;
|
|
4806
|
-
var fin =
|
|
4861
|
+
var fin = resolveForwardRequestFinalize();
|
|
4807
4862
|
if (!fin) return;
|
|
4808
4863
|
var url = platform === "openai" ? OPENAI_RESPONSES_API_URL : ANTHROPIC_MESSAGES_API_URL;
|
|
4809
4864
|
try {
|
|
@@ -8319,7 +8374,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8319
8374
|
(function() {
|
|
8320
8375
|
var MCP_PROD = "https://mcp.broadwayinc.computer";
|
|
8321
8376
|
var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
|
|
8322
|
-
var BQ_VERSION = "1.
|
|
8377
|
+
var BQ_VERSION = "1.11.1" ;
|
|
8323
8378
|
var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
|
|
8324
8379
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
8325
8380
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -8587,6 +8642,50 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8587
8642
|
return null;
|
|
8588
8643
|
});
|
|
8589
8644
|
}
|
|
8645
|
+
function skapiHasForwardRequest(sk) {
|
|
8646
|
+
return !!sk && typeof sk.forwardRequestHistory === "function" && typeof sk.forwardRequest === "function";
|
|
8647
|
+
}
|
|
8648
|
+
function skapiForwardWithSecret(secretName, request) {
|
|
8649
|
+
if (skapiHasForwardRequest(S.skapi)) {
|
|
8650
|
+
return S.skapi.forwardRequest(null, Object.assign({ secretName }, request));
|
|
8651
|
+
}
|
|
8652
|
+
return S.skapi.clientSecretRequest(Object.assign({ clientSecretName: secretName }, request));
|
|
8653
|
+
}
|
|
8654
|
+
function skapiCancelRequest(opts) {
|
|
8655
|
+
return skapiHasForwardRequest(S.skapi) ? S.skapi.cancelForwardRequest(opts) : S.skapi.cancelClientSecretRequest(opts);
|
|
8656
|
+
}
|
|
8657
|
+
function skapiEngineTransport(canStream) {
|
|
8658
|
+
if (skapiHasForwardRequest(S.skapi)) {
|
|
8659
|
+
return {
|
|
8660
|
+
forwardRequest: function(form, o) {
|
|
8661
|
+
return S.skapi.forwardRequest(form, o);
|
|
8662
|
+
},
|
|
8663
|
+
forwardRequestHistory: function(p, f) {
|
|
8664
|
+
return S.skapi.forwardRequestHistory(p, f);
|
|
8665
|
+
},
|
|
8666
|
+
forwardRequestFinalize: canStream ? function(requestId, data, options) {
|
|
8667
|
+
return S.skapi.forwardRequestFinalize(requestId, data, options);
|
|
8668
|
+
} : void 0,
|
|
8669
|
+
forwardRequestStream: canStream ? function(requestId, options) {
|
|
8670
|
+
return S.skapi.forwardRequestStream(requestId, options);
|
|
8671
|
+
} : void 0
|
|
8672
|
+
};
|
|
8673
|
+
}
|
|
8674
|
+
return {
|
|
8675
|
+
clientSecretRequest: function(o) {
|
|
8676
|
+
return S.skapi.clientSecretRequest(o);
|
|
8677
|
+
},
|
|
8678
|
+
clientSecretRequestHistory: function(p, f) {
|
|
8679
|
+
return S.skapi.clientSecretRequestHistory(p, f);
|
|
8680
|
+
},
|
|
8681
|
+
clientSecretRequestFinalize: canStream ? function(requestId, data, options) {
|
|
8682
|
+
return S.skapi.clientSecretRequestFinalize(requestId, data, options);
|
|
8683
|
+
} : void 0,
|
|
8684
|
+
clientSecretRequestStream: canStream ? function(requestId, options) {
|
|
8685
|
+
return S.skapi.clientSecretRequestStream(requestId, options);
|
|
8686
|
+
} : void 0
|
|
8687
|
+
};
|
|
8688
|
+
}
|
|
8590
8689
|
function render(viewName, builder) {
|
|
8591
8690
|
if (!S.root) return;
|
|
8592
8691
|
S.view = viewName;
|
|
@@ -8880,8 +8979,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8880
8979
|
var code = getQueryParam("code");
|
|
8881
8980
|
var redirectUrl = ssGet(skey(SK.googleRedirect)) || window.location.origin + window.location.pathname;
|
|
8882
8981
|
var secretName = S.opts.googleClientSecretName || "ggl";
|
|
8883
|
-
return
|
|
8884
|
-
clientSecretName: secretName,
|
|
8982
|
+
return skapiForwardWithSecret(secretName, {
|
|
8885
8983
|
url: GOOGLE_TOKEN_URL,
|
|
8886
8984
|
method: "POST",
|
|
8887
8985
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -9559,9 +9657,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
9559
9657
|
onAction ? h("button", { class: "bq-link" + (opts.dangerAction ? " bq-link--danger" : ""), type: "button", onclick: onAction, text: actionLabel || "Change" }) : null
|
|
9560
9658
|
);
|
|
9561
9659
|
}
|
|
9562
|
-
function getNewsletterStatus() {
|
|
9660
|
+
function getNewsletterStatus(user) {
|
|
9661
|
+
var userId = user && typeof user.user_id === "string" ? user.user_id : null;
|
|
9662
|
+
if (!userId) return Promise.resolve(false);
|
|
9563
9663
|
try {
|
|
9564
|
-
return Promise.resolve(S.skapi.getNewsletterSubscription({ group: "authorized" })).then(function(res) {
|
|
9664
|
+
return Promise.resolve(S.skapi.getNewsletterSubscription({ group: "authorized", user_id: userId })).then(function(res) {
|
|
9565
9665
|
var list = res && res.list ? res.list : res;
|
|
9566
9666
|
if (!Array.isArray(list)) return false;
|
|
9567
9667
|
return list.some(function(s) {
|
|
@@ -9601,9 +9701,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
9601
9701
|
{ class: "bq-chat-settings" },
|
|
9602
9702
|
h("div", { class: "bq-chat-settings-loading" }, bunnyLoader("Loading..."))
|
|
9603
9703
|
));
|
|
9604
|
-
|
|
9605
|
-
if (
|
|
9606
|
-
S.
|
|
9704
|
+
getProfile().then(function(user) {
|
|
9705
|
+
if (user) S.user = user;
|
|
9706
|
+
return getNewsletterStatus(S.user);
|
|
9707
|
+
}).then(function(subscribed) {
|
|
9708
|
+
S.newsletterSubscribed = subscribed;
|
|
9607
9709
|
renderSettingsIntoBox();
|
|
9608
9710
|
}).catch(function() {
|
|
9609
9711
|
renderSettingsIntoBox();
|
|
@@ -10080,7 +10182,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
10080
10182
|
settleScrollAfterRefresh();
|
|
10081
10183
|
},
|
|
10082
10184
|
cancelRequest: function(opts) {
|
|
10083
|
-
return
|
|
10185
|
+
return skapiCancelRequest(opts);
|
|
10084
10186
|
},
|
|
10085
10187
|
refreshSession: function() {
|
|
10086
10188
|
return refreshSkapiSession();
|
|
@@ -12852,7 +12954,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
12852
12954
|
if (liveStreaming && !canStream) {
|
|
12853
12955
|
liveStreaming = false;
|
|
12854
12956
|
console.warn(
|
|
12855
|
-
"[bunnyquery] liveStreaming was requested but this page's skapi-js has
|
|
12957
|
+
"[bunnyquery] liveStreaming was requested but this page's skapi-js has neither forwardRequestStream/forwardRequestFinalize nor the older clientSecretRequestStream/clientSecretRequestFinalize, so skapi's half of the stream flag would be dropped and every reply would read back empty. Falling back to buffered replies - update skapi-js to enable streaming."
|
|
12856
12958
|
);
|
|
12857
12959
|
}
|
|
12858
12960
|
configureProjectSettings(function(service) {
|
|
@@ -12862,13 +12964,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
12862
12964
|
return rec && rec.data || null;
|
|
12863
12965
|
});
|
|
12864
12966
|
});
|
|
12865
|
-
configureChatEngine({
|
|
12866
|
-
clientSecretRequest: function(o) {
|
|
12867
|
-
return S.skapi.clientSecretRequest(o);
|
|
12868
|
-
},
|
|
12869
|
-
clientSecretRequestHistory: function(p, f) {
|
|
12870
|
-
return S.skapi.clientSecretRequestHistory(p, f);
|
|
12871
|
-
},
|
|
12967
|
+
configureChatEngine(Object.assign({
|
|
12872
12968
|
// Single-item csr-poll point lookup: how the engine hydrates a
|
|
12873
12969
|
// compact history stub's real body when an indexing row expands.
|
|
12874
12970
|
csrHistoryItemLookup: function(fullId, service, owner) {
|
|
@@ -12912,32 +13008,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
12912
13008
|
liveStreaming,
|
|
12913
13009
|
// Requires liveStreaming, and cannot outlive it: the AND is what stops an
|
|
12914
13010
|
// embedder turning on socket delivery for a reply that is not streamed.
|
|
12915
|
-
liveStreamingRealtime: liveStreaming && S.opts.liveStreamingRealtime === true
|
|
12916
|
-
//
|
|
12917
|
-
//
|
|
12918
|
-
//
|
|
12919
|
-
//
|
|
12920
|
-
//
|
|
12921
|
-
|
|
12922
|
-
// "is this host able to?" checks answer honestly instead of a call
|
|
12923
|
-
// reaching an undefined method mid-turn.
|
|
12924
|
-
clientSecretRequestFinalize: canStream ? function(requestId, data, options) {
|
|
12925
|
-
return S.skapi.clientSecretRequestFinalize(requestId, data, options);
|
|
12926
|
-
} : void 0,
|
|
12927
|
-
// THE SECOND HALF OF THE DURABILITY GUARANTEE. A streamed row settles
|
|
12928
|
-
// with a status and NO body: the answer is chunks until finalize copies
|
|
12929
|
-
// a version onto the row. A row that settles while no poll is attached
|
|
12930
|
-
// (the tab was closed, a mobile browser discarded it, the device slept
|
|
12931
|
-
// and the interval stopped) is therefore never finalized, and without
|
|
12932
|
-
// this hook the engine has no way back to it - the answer reads as gone
|
|
12933
|
-
// from the conversation with every byte of it still stored. Given the
|
|
12934
|
-
// request id this drains that turn's chunks in one pass, and the engine
|
|
12935
|
-
// parses them exactly as it parses a live stream, finalizing what it
|
|
12936
|
-
// read so the row becomes ordinary history and is never re-read.
|
|
12937
|
-
clientSecretRequestStream: canStream ? function(requestId, options) {
|
|
12938
|
-
return S.skapi.clientSecretRequestStream(requestId, options);
|
|
12939
|
-
} : void 0
|
|
12940
|
-
});
|
|
13011
|
+
liveStreamingRealtime: liveStreaming && S.opts.liveStreamingRealtime === true
|
|
13012
|
+
// The transport: dispatch and history, plus the finalize hook that stores
|
|
13013
|
+
// a streamed turn's kept version and the chunk reader that is the second
|
|
13014
|
+
// half of the durability guarantee. The two optional hooks are handed
|
|
13015
|
+
// over only when canStream says the SDK has them. skapiEngineTransport
|
|
13016
|
+
// says what each one is for, and picks the family.
|
|
13017
|
+
}, skapiEngineTransport(canStream)));
|
|
12941
13018
|
if (!S._resizeBound && typeof window !== "undefined" && window.addEventListener) {
|
|
12942
13019
|
S._resizeBound = true;
|
|
12943
13020
|
window.addEventListener("resize", function() {
|