bunnyquery 1.9.10 → 1.10.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 +55 -22
- package/bunnyquery.js +93 -22
- package/dist/engine.cjs +94 -20
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +83 -18
- package/dist/engine.d.ts +83 -18
- package/dist/engine.mjs +91 -21
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/budget.ts +46 -2
- package/src/engine/index.ts +3 -0
- package/src/engine/indexing_groups.ts +86 -7
- package/src/engine/office.ts +16 -4
- package/src/engine/prompts/chat_system_prompt.ts +6 -3
- package/src/engine/prompts/indexing_system_prompt.ts +8 -4
- package/src/engine/prompts/indexing_user_message.ts +39 -3
- package/src/engine/requests.ts +73 -7
package/src/engine/requests.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { isServerExtractable, isPagedReadFile, isImageVisionFile, isWindowedRead
|
|
|
15
15
|
import { chatEngineConfig, pollOpt, windowedIndexingEnabled, liveStreamingEnabled, liveStreamingRealtimeEnabled } from './config';
|
|
16
16
|
// Output sizing lives in budget.ts so the request cap and the reserve the input
|
|
17
17
|
// budget subtracts cannot drift; getMaxOutputTokens also clamps per model.
|
|
18
|
-
import { getMaxOutputTokens } from './budget';
|
|
18
|
+
import { getMaxOutputTokens, getModelContextWindow } from './budget';
|
|
19
19
|
|
|
20
20
|
export const ANTHROPIC_MESSAGES_API_URL = 'https://api.anthropic.com/v1/messages';
|
|
21
21
|
const ANTHROPIC_MODELS_API_URL = 'https://api.anthropic.com/v1/models';
|
|
@@ -39,6 +39,70 @@ export const DEFAULT_OPENAI_MODEL = 'gpt-5.6-luna';
|
|
|
39
39
|
|
|
40
40
|
const mcpUrl = () => chatEngineConfig().mcpBaseUrl;
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The MCP endpoint an INDEXING pass points at.
|
|
44
|
+
*
|
|
45
|
+
* Same server, same auth, same tools it can actually call: `?profile=index` only narrows
|
|
46
|
+
* the tools/list the model is SHOWN. An indexing pass reads one window of one file and
|
|
47
|
+
* writes records for it, so being told about deleteRecords, exportRecordsToFile,
|
|
48
|
+
* writeReport, getTemporaryUrl, getProfile and getProjectInfos costs about 2,400 tokens
|
|
49
|
+
* on every pass of every file and buys nothing: there is no user attached to hand a
|
|
50
|
+
* download or a report to, and re-index clears old records server-side before the pass
|
|
51
|
+
* runs. Measured: 30,577 B -> 20,755 B.
|
|
52
|
+
*
|
|
53
|
+
* A CHAT turn keeps the full list and always will. It is open ended, a person can ask
|
|
54
|
+
* for anything, and a tool that is missing from the list is a dead end the model cannot
|
|
55
|
+
* reason its way out of.
|
|
56
|
+
*
|
|
57
|
+
* Degrades in both directions, which is why it is a query param: an MCP server that
|
|
58
|
+
* predates it ignores the parameter and serves the full list, and a client that predates
|
|
59
|
+
* it just sends no parameter. Neither needs the other deployed first.
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* Append query parameters to an MCP endpoint, preserving any the base already carries.
|
|
63
|
+
*
|
|
64
|
+
* An explicit path before the query. The configured base has no trailing slash
|
|
65
|
+
* ("https://mcp-dev.broadwayinc.computer"), and appending "?profile=index" straight onto an
|
|
66
|
+
* authority with no path yields a URL that is legal but that intermediaries and clients
|
|
67
|
+
* normalise inconsistently. "/?profile=index" is unambiguous everywhere.
|
|
68
|
+
*
|
|
69
|
+
* Every parameter here is a HINT the server may ignore, which is what lets the two sides
|
|
70
|
+
* deploy in either order: a server that predates one ignores it, and a client that predates
|
|
71
|
+
* it sends nothing. The worker strips the whole query before appending its own /internal
|
|
72
|
+
* paths (_mcp_endpoint_and_token), so nothing added here reaches those routes.
|
|
73
|
+
*/
|
|
74
|
+
function withMcpParams(base: string, params: Record<string, string | number | undefined>): string {
|
|
75
|
+
if (!base) return base;
|
|
76
|
+
const pairs = Object.keys(params)
|
|
77
|
+
.filter(k => params[k] !== undefined && params[k] !== null && params[k] !== '')
|
|
78
|
+
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(String(params[k])));
|
|
79
|
+
if (!pairs.length) return base;
|
|
80
|
+
const [addr, existing] = base.split('?');
|
|
81
|
+
// Only supply the missing root path. Appending a slash to a base that ALREADY has a
|
|
82
|
+
// path would change where the request lands on a server mounted under one.
|
|
83
|
+
const hasPath = /^[a-z][a-z0-9+.-]*:\/\/[^/]+\/./i.test(addr);
|
|
84
|
+
const path = hasPath ? addr : addr.replace(/\/+$/, '') + '/';
|
|
85
|
+
return path + '?' + (existing ? existing + '&' : '') + pairs.join('&');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* How large a page of a paginated tool result this model can afford.
|
|
90
|
+
*
|
|
91
|
+
* The server slices big results into pages and the model spends one round trip per page, so
|
|
92
|
+
* the page size sets how long reading a large record set takes: 1000 spreadsheet records is
|
|
93
|
+
* 74 round trips at the server's floor of 10,000 chars and 13 at 60,000. The server cannot
|
|
94
|
+
* know which model is on the other end of an MCP connection, so it is told.
|
|
95
|
+
*
|
|
96
|
+
* The MODEL's own ceiling, not getContextWindow(): the project's context-window setting
|
|
97
|
+
* budgets this client's FIRST request, while these pages accumulate in the provider's
|
|
98
|
+
* server-side tool loop, which runs against what the model can actually hold.
|
|
99
|
+
*/
|
|
100
|
+
const mcpContextParam = (platform: 'claude' | 'openai', model?: string) =>
|
|
101
|
+
getModelContextWindow(platform, model);
|
|
102
|
+
|
|
103
|
+
const mcpIndexingUrl = (platform: 'claude' | 'openai' = 'openai', model?: string) =>
|
|
104
|
+
withMcpParams(mcpUrl(), { profile: 'index', ctx: mcpContextParam(platform, model) });
|
|
105
|
+
|
|
42
106
|
/**
|
|
43
107
|
* Where a chat turn's MCP tools point, and what they authenticate with.
|
|
44
108
|
*
|
|
@@ -697,7 +761,9 @@ export async function callClaudeWithPublicMcp(
|
|
|
697
761
|
fileUrls,
|
|
698
762
|
mcpServer: {
|
|
699
763
|
name: MCP_NAME,
|
|
700
|
-
url: endpoint.url,
|
|
764
|
+
url: withMcpParams(endpoint.url, {
|
|
765
|
+
ctx: mcpContextParam('claude', model || DEFAULT_CLAUDE_MODEL),
|
|
766
|
+
}),
|
|
701
767
|
// Omitted entirely for an anonymous turn; the `if (mcpServer.authorizationToken)`
|
|
702
768
|
// guard below drops the key rather than sending an empty one.
|
|
703
769
|
authorizationToken: endpoint.token,
|
|
@@ -782,7 +848,7 @@ export async function callOpenAIWithPublicMcp(
|
|
|
782
848
|
{
|
|
783
849
|
type: 'mcp',
|
|
784
850
|
server_label: MCP_NAME,
|
|
785
|
-
server_url: endpoint.url,
|
|
851
|
+
server_url: withMcpParams(endpoint.url, { ctx: mcpContextParam('openai', resolvedModel) }),
|
|
786
852
|
require_approval: 'never',
|
|
787
853
|
// No `headers` at all for an anonymous turn: `Bearer ` with an
|
|
788
854
|
// empty token is a credential the MCP server rejects, and the
|
|
@@ -1086,7 +1152,7 @@ export async function notifyAgentSaveAttachment(info: AttachmentSaveInfo) {
|
|
|
1086
1152
|
},
|
|
1087
1153
|
data: {
|
|
1088
1154
|
model: resolvedModel,
|
|
1089
|
-
max_output_tokens: getMaxOutputTokens('openai', resolvedModel),
|
|
1155
|
+
max_output_tokens: getMaxOutputTokens('openai', resolvedModel, 'indexing'),
|
|
1090
1156
|
// Nano-only transcription knobs. Indexing only; see variantIndexingOptions.
|
|
1091
1157
|
...variantIndexingOptions(resolvedModel),
|
|
1092
1158
|
...skapiExtract,
|
|
@@ -1104,7 +1170,7 @@ export async function notifyAgentSaveAttachment(info: AttachmentSaveInfo) {
|
|
|
1104
1170
|
{
|
|
1105
1171
|
type: 'mcp',
|
|
1106
1172
|
server_label: MCP_NAME,
|
|
1107
|
-
server_url:
|
|
1173
|
+
server_url: mcpIndexingUrl('openai', resolvedModel),
|
|
1108
1174
|
require_approval: 'never',
|
|
1109
1175
|
headers: { Authorization: 'Bearer $ACCESS_TOKEN' },
|
|
1110
1176
|
},
|
|
@@ -1138,7 +1204,7 @@ export async function notifyAgentSaveAttachment(info: AttachmentSaveInfo) {
|
|
|
1138
1204
|
},
|
|
1139
1205
|
data: {
|
|
1140
1206
|
model: resolvedModel,
|
|
1141
|
-
max_tokens: getMaxOutputTokens('claude', resolvedModel),
|
|
1207
|
+
max_tokens: getMaxOutputTokens('claude', resolvedModel, 'indexing'),
|
|
1142
1208
|
...skapiExtract,
|
|
1143
1209
|
...skapiRender,
|
|
1144
1210
|
...skapiWindow,
|
|
@@ -1160,7 +1226,7 @@ export async function notifyAgentSaveAttachment(info: AttachmentSaveInfo) {
|
|
|
1160
1226
|
{
|
|
1161
1227
|
type: 'url',
|
|
1162
1228
|
name: MCP_NAME,
|
|
1163
|
-
url:
|
|
1229
|
+
url: mcpIndexingUrl('claude', resolvedModel),
|
|
1164
1230
|
authorization_token: '$ACCESS_TOKEN',
|
|
1165
1231
|
},
|
|
1166
1232
|
],
|