@ganju/utils 0.0.14 → 0.0.16
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/dist/channelCustomer.d.ts +99 -0
- package/dist/channelCustomer.d.ts.map +1 -0
- package/dist/channelCustomer.js +533 -0
- package/dist/channelDebounce.d.ts +7 -0
- package/dist/channelDebounce.d.ts.map +1 -1
- package/dist/chunkOverlap.d.ts +2 -0
- package/dist/chunkOverlap.d.ts.map +1 -0
- package/dist/chunkOverlap.js +9 -0
- package/dist/chunking.d.ts.map +1 -1
- package/dist/chunking.js +24 -4
- package/dist/citations.d.ts +28 -0
- package/dist/citations.d.ts.map +1 -0
- package/dist/citations.js +111 -0
- package/dist/constants.d.ts +49 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +202 -17
- package/dist/data.d.ts +9 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +78 -0
- package/dist/dataApi.d.ts +511 -0
- package/dist/dataApi.d.ts.map +1 -0
- package/dist/dataApi.js +355 -0
- package/dist/dataCompiler.d.ts +68 -0
- package/dist/dataCompiler.d.ts.map +1 -0
- package/dist/dataCompiler.js +726 -0
- package/dist/dataDdl.d.ts +29 -0
- package/dist/dataDdl.d.ts.map +1 -0
- package/dist/dataDdl.js +354 -0
- package/dist/dataEngine.d.ts +143 -0
- package/dist/dataEngine.d.ts.map +1 -0
- package/dist/dataEngine.js +2022 -0
- package/dist/dataIdentifiers.d.ts +8 -0
- package/dist/dataIdentifiers.d.ts.map +1 -0
- package/dist/dataIdentifiers.js +55 -0
- package/dist/dataImport.d.ts +77 -0
- package/dist/dataImport.d.ts.map +1 -0
- package/dist/dataImport.js +450 -0
- package/dist/dataRpc.d.ts +131 -0
- package/dist/dataRpc.d.ts.map +1 -0
- package/dist/dataRpc.js +2 -0
- package/dist/dataTypes.d.ts +157 -0
- package/dist/dataTypes.d.ts.map +1 -0
- package/dist/dataTypes.js +160 -0
- package/dist/index.d.ts +570 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -0
- package/dist/jsonSchemaToZodShape.d.ts +5 -1
- package/dist/jsonSchemaToZodShape.d.ts.map +1 -1
- package/dist/jsonSchemaToZodShape.js +21 -9
- package/dist/oauth.d.ts +14 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +42 -2
- package/dist/pagination.d.ts +47 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +160 -0
- package/dist/processQueueBatch.d.ts +6 -1
- package/dist/processQueueBatch.d.ts.map +1 -1
- package/dist/processQueueBatch.js +23 -10
- package/dist/queueFailure.d.ts +14 -0
- package/dist/queueFailure.d.ts.map +1 -0
- package/dist/queueFailure.js +43 -0
- package/dist/resourceRerank.d.ts +3 -2
- package/dist/resourceRerank.d.ts.map +1 -1
- package/dist/resourceRerank.js +8 -3
- package/dist/schema.d.ts +374 -29
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +290 -6
- package/dist/ssrf.d.ts +1 -0
- package/dist/ssrf.d.ts.map +1 -1
- package/dist/ssrf.js +48 -12
- package/dist/toolConfirmation.d.ts +48 -0
- package/dist/toolConfirmation.d.ts.map +1 -1
- package/dist/toolConfirmation.js +75 -1
- package/dist/toolCount.d.ts +13 -0
- package/dist/toolCount.d.ts.map +1 -0
- package/dist/toolCount.js +45 -0
- package/dist/toolLint.d.ts +1 -1
- package/dist/toolLint.d.ts.map +1 -1
- package/dist/toolLint.js +20 -0
- package/dist/untrustedContent.d.ts +135 -0
- package/dist/untrustedContent.d.ts.map +1 -0
- package/dist/untrustedContent.js +402 -0
- package/package.json +17 -1
- package/src/channelCustomer.ts +604 -0
- package/src/channelDebounce.ts +17 -0
- package/src/chunkOverlap.ts +6 -0
- package/src/chunking.ts +25 -4
- package/src/citations.ts +127 -0
- package/src/constants.ts +219 -17
- package/src/data.ts +74 -0
- package/src/dataApi.ts +428 -0
- package/src/dataCompiler.ts +948 -0
- package/src/dataDdl.ts +419 -0
- package/src/dataEngine.ts +2753 -0
- package/src/dataIdentifiers.ts +63 -0
- package/src/dataImport.ts +554 -0
- package/src/dataRpc.ts +121 -0
- package/src/dataTypes.ts +312 -0
- package/src/index.ts +156 -2
- package/src/jsonSchemaToZodShape.ts +31 -13
- package/src/oauth.ts +42 -1
- package/src/pagination.ts +193 -0
- package/src/processQueueBatch.ts +42 -12
- package/src/queueFailure.ts +63 -0
- package/src/resourceRerank.ts +10 -3
- package/src/schema.ts +346 -4
- package/src/ssrf.ts +40 -11
- package/src/toolConfirmation.ts +123 -0
- package/src/toolCount.ts +53 -0
- package/src/toolLint.ts +27 -0
- package/src/untrustedContent.ts +476 -0
package/src/chunking.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { constants } from './constants';
|
|
2
|
+
import { overlapTail } from './chunkOverlap';
|
|
2
3
|
import { decodeEntities } from './sanitize';
|
|
3
4
|
|
|
4
5
|
import type {
|
|
@@ -68,7 +69,7 @@ const chunkPieces = (
|
|
|
68
69
|
const rawChunks = splitRecursive(trimmed, target);
|
|
69
70
|
const pieces: Array<{ raw: string; content: string }> = [];
|
|
70
71
|
for (let i = 0; i < rawChunks.length; i++) {
|
|
71
|
-
const prev = i > 0 ? rawChunks[i - 1]
|
|
72
|
+
const prev = i > 0 ? overlapTail(rawChunks[i - 1], overlap) : '';
|
|
72
73
|
pieces.push({
|
|
73
74
|
raw: rawChunks[i],
|
|
74
75
|
content: (prev ? prev + '\n' : '') + rawChunks[i]
|
|
@@ -93,6 +94,12 @@ interface HeadingEvent {
|
|
|
93
94
|
const MARKDOWN_HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)(?:[ \t]+#+)?[ \t]*$/;
|
|
94
95
|
const MARKDOWN_FENCE = /^ {0,3}(```|~~~)/;
|
|
95
96
|
const HTML_HEADING = /<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1\s*>/gi;
|
|
97
|
+
// A heading tag still open at the end of a piece, from its opening tag on: the
|
|
98
|
+
// chunker split inside it, and the rest of it opens the next piece.
|
|
99
|
+
const OPEN_HTML_HEADING = /<h([1-6])\b[^>]*>(?![\s\S]*<\/h\1\s*>)[\s\S]*$/i;
|
|
100
|
+
// Longer than any heading worth carrying: an opening tag this far back with no
|
|
101
|
+
// close is broken markup, not a heading that happened to be split.
|
|
102
|
+
const MAX_OPEN_HEADING_CHARS = 1000;
|
|
96
103
|
const MAX_HEADING_CHARS = 120;
|
|
97
104
|
|
|
98
105
|
const MARKDOWN_FILE_NAME = /\.(md|markdown|mdx)$/i;
|
|
@@ -163,16 +170,30 @@ const scanHtmlHeadings = (text: string): HeadingEvent[] =>
|
|
|
163
170
|
const createHeadingTracker = (syntax: HeadingSyntax) => {
|
|
164
171
|
let stack: Array<{ level: number; text: string }> = [];
|
|
165
172
|
const fence = { inFence: false };
|
|
173
|
+
// The unfinished HTML heading the previous piece ended in, if any.
|
|
174
|
+
let carry = '';
|
|
166
175
|
const apply = (event: HeadingEvent) => {
|
|
167
176
|
stack = stack.filter(h => h.level < event.level);
|
|
168
177
|
stack.push({ level: event.level, text: event.text });
|
|
169
178
|
};
|
|
170
179
|
|
|
180
|
+
// An HTML heading split between two pieces is read whole from the second,
|
|
181
|
+
// where it ends, as if that piece opened with it: it does, mid-heading. The
|
|
182
|
+
// separator the chunker dropped at the split is restored as a space.
|
|
183
|
+
const scanHtml = (raw: string): HeadingEvent[] => {
|
|
184
|
+
const text = carry ? `${carry} ${raw}` : raw;
|
|
185
|
+
const shift = text.length - raw.length;
|
|
186
|
+
const open = OPEN_HTML_HEADING.exec(text)?.[0] ?? '';
|
|
187
|
+
carry = open.length <= MAX_OPEN_HEADING_CHARS ? open : '';
|
|
188
|
+
return scanHtmlHeadings(text).map(event => ({
|
|
189
|
+
...event,
|
|
190
|
+
offset: Math.max(0, event.offset - shift)
|
|
191
|
+
}));
|
|
192
|
+
};
|
|
193
|
+
|
|
171
194
|
return (raw: string): string[] => {
|
|
172
195
|
const events =
|
|
173
|
-
syntax === 'markdown'
|
|
174
|
-
? scanMarkdownHeadings(raw, fence)
|
|
175
|
-
: scanHtmlHeadings(raw);
|
|
196
|
+
syntax === 'markdown' ? scanMarkdownHeadings(raw, fence) : scanHtml(raw);
|
|
176
197
|
const [first, ...rest] = events;
|
|
177
198
|
// Before a document's first heading there is no section to continue — only
|
|
178
199
|
// the metadata header, or the markup around an HTML body — so a chunk that
|
package/src/citations.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { Source } from './sources';
|
|
2
|
+
|
|
3
|
+
// "p. 125", "pp. 123–126", "page 12", "págs. 4-5", "páginas 3 a 5". An
|
|
4
|
+
// abbreviation counts only with its dot and a number has to stand alone, so
|
|
5
|
+
// "p2p", "mp3" and "pp 2026" are not page references. The lookarounds, not \b,
|
|
6
|
+
// mark the edges: \b sees "á" as a word break.
|
|
7
|
+
const PAGE_REFERENCE =
|
|
8
|
+
/(?<![\p{L}\p{N}])(?:(?:pp?|p[aá]gs?)\.|pages?|p[aá]ginas?)\s*(\d{1,5})(?![\p{L}\p{N}])(?:(?:\s*[-–—]\s*|\s+(?:to|a)\s+)(\d{1,5})(?![\p{L}\p{N}]))?/giu;
|
|
9
|
+
const MAX_PAGE_RANGE = 50;
|
|
10
|
+
|
|
11
|
+
// Lowercased words only, so "ETag header — HTTP | MDN" in an answer matches the
|
|
12
|
+
// title "ETag header - HTTP | MDN", and "mml-book.pdf" matches "mml-book".
|
|
13
|
+
const normalize = (text: string): string =>
|
|
14
|
+
` ${text
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.normalize('NFKD')
|
|
17
|
+
.replace(/[̀-ͯ]/g, '')
|
|
18
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
19
|
+
.trim()} `;
|
|
20
|
+
|
|
21
|
+
// The ways an answer refers to a source: its whole title, the title without a
|
|
22
|
+
// parenthetical or a site suffix ("Northwind Handbook (draft)", "ETag header
|
|
23
|
+
// - HTTP | MDN"), its file name with and without the extension, and its URL.
|
|
24
|
+
const namesFor = (source: Source): string[] => {
|
|
25
|
+
const names = [source.title, source.title.replace(/\s*\([^)]*\)\s*/g, ' ')];
|
|
26
|
+
const lead = source.title.split(/\s+[|\-–—:]\s+/)[0];
|
|
27
|
+
// A one-word lead ("Introduction", "Pricing") names half the corpus.
|
|
28
|
+
if (lead.trim().split(/\s+/).length >= 2) names.push(lead);
|
|
29
|
+
if (source.fileName) {
|
|
30
|
+
names.push(source.fileName, source.fileName.replace(/\.[a-z0-9]+$/i, ''));
|
|
31
|
+
}
|
|
32
|
+
return names.map(normalize).filter(name => name.trim().length >= 4);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// The characters that can carry a URL on past the point we matched. Brackets,
|
|
36
|
+
// quotes and whitespace end it — that is how a URL sits inside Markdown links,
|
|
37
|
+
// parentheses and prose.
|
|
38
|
+
const URL_CONTINUATION = /^[^\s<>()[\]{}"'`|]*/;
|
|
39
|
+
// Punctuation that closes a sentence around a URL rather than belonging to it.
|
|
40
|
+
const TRAILING_PUNCTUATION = /[.,;:!?*_]+$/;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Whether the answer cites this exact URL, not merely one that starts with it:
|
|
44
|
+
* on a crawled docs site the index page is a prefix of every page beneath it,
|
|
45
|
+
* so a plain substring test kept "/docs" and the site root in the footer of an
|
|
46
|
+
* answer that cited "/docs/api". A trailing slash and a #fragment still count
|
|
47
|
+
* as the same page.
|
|
48
|
+
*/
|
|
49
|
+
export const citesUrl = (answer: string, url: string): boolean => {
|
|
50
|
+
const target = url.replace(/\/+$/, '');
|
|
51
|
+
if (!target) return false;
|
|
52
|
+
for (
|
|
53
|
+
let at = answer.indexOf(target);
|
|
54
|
+
at !== -1;
|
|
55
|
+
at = answer.indexOf(target, at + 1)
|
|
56
|
+
) {
|
|
57
|
+
const rest = answer
|
|
58
|
+
.slice(at + target.length)
|
|
59
|
+
.match(URL_CONTINUATION)![0]
|
|
60
|
+
.replace(TRAILING_PUNCTUATION, '');
|
|
61
|
+
if (/^\/*(#.*)?$/.test(rest)) return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Every page number the answer cites, ranges expanded. */
|
|
67
|
+
export const citedPages = (answer: string): Set<number> => {
|
|
68
|
+
const pages = new Set<number>();
|
|
69
|
+
for (const match of answer.matchAll(PAGE_REFERENCE)) {
|
|
70
|
+
const from = Number(match[1]);
|
|
71
|
+
const to = match[2] ? Number(match[2]) : from;
|
|
72
|
+
if (to < from || to - from > MAX_PAGE_RANGE) {
|
|
73
|
+
pages.add(from);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
for (let page = from; page <= to; page++) pages.add(page);
|
|
77
|
+
}
|
|
78
|
+
return pages;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Narrow a turn's sources to the ones its answer actually cites.
|
|
83
|
+
*
|
|
84
|
+
* Every search hit used to become a footer line, so an answer drawn from one
|
|
85
|
+
* handbook section was followed by eight unrelated pages the search had also
|
|
86
|
+
* returned. A search hit is kept when the answer names its resource — by title,
|
|
87
|
+
* file name or URL — and, for a paged file, only the pages the answer cites
|
|
88
|
+
* (or the best-ranked one, when it cites none). Resources the model read whole
|
|
89
|
+
* are always kept: reading one is itself the citation.
|
|
90
|
+
*
|
|
91
|
+
* An answer that names nothing gets no search sources at all. The model is told
|
|
92
|
+
* to cite what it uses, so a reply that names nothing is a greeting or "I
|
|
93
|
+
* couldn't find that" — and a footer under it would claim sources it never
|
|
94
|
+
* used, just because search runs on every message.
|
|
95
|
+
*/
|
|
96
|
+
export const selectCitedSources = (
|
|
97
|
+
sources: Source[],
|
|
98
|
+
answerText: string
|
|
99
|
+
): Source[] => {
|
|
100
|
+
const searchEntries = sources.filter(s => s.score !== undefined);
|
|
101
|
+
const readEntries = sources.filter(s => s.score === undefined);
|
|
102
|
+
if (searchEntries.length === 0) return sources;
|
|
103
|
+
|
|
104
|
+
const answer = normalize(answerText);
|
|
105
|
+
const pages = citedPages(answerText);
|
|
106
|
+
const citedResources = new Set(
|
|
107
|
+
searchEntries
|
|
108
|
+
.filter(
|
|
109
|
+
s =>
|
|
110
|
+
citesUrl(answerText, s.uri) ||
|
|
111
|
+
namesFor(s).some(name => answer.includes(name))
|
|
112
|
+
)
|
|
113
|
+
.map(s => s.resourceId)
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const kept: Source[] = [];
|
|
117
|
+
for (const resourceId of citedResources) {
|
|
118
|
+
const entries = searchEntries.filter(s => s.resourceId === resourceId);
|
|
119
|
+
const onCitedPage = entries.filter(
|
|
120
|
+
s => s.pageNumber !== undefined && pages.has(s.pageNumber)
|
|
121
|
+
);
|
|
122
|
+
kept.push(...(onCitedPage.length > 0 ? onCitedPage : entries.slice(0, 1)));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const cited = searchEntries.filter(s => kept.includes(s));
|
|
126
|
+
return [...cited, ...readEntries];
|
|
127
|
+
};
|
package/src/constants.ts
CHANGED
|
@@ -144,6 +144,11 @@ const OAUTH_TOKEN_URLS: Record<string, string> = {
|
|
|
144
144
|
// MCP-proxy OAuth, in both apps/api and apps/mcp).
|
|
145
145
|
const CREDENTIAL_REFRESH_BUFFER_MS = 60 * 1000;
|
|
146
146
|
|
|
147
|
+
// A refresh the token endpoint rejects (4xx) without a reauth code still flags
|
|
148
|
+
// the credential once its access token has been expired this long, so the
|
|
149
|
+
// owner sees it as expired instead of every call failing behind "Connected".
|
|
150
|
+
const CREDENTIAL_REFRESH_GIVE_UP_MS = 60 * 60 * 1000;
|
|
151
|
+
|
|
147
152
|
const GOOGLE_CALENDAR_API_BASE = 'https://www.googleapis.com/calendar/v3';
|
|
148
153
|
|
|
149
154
|
const GOOGLE_DRIVE_API_BASE = 'https://www.googleapis.com/drive/v3';
|
|
@@ -228,7 +233,18 @@ const ONE_DRIVE_TAB_LABELS: ReadonlyArray<{
|
|
|
228
233
|
{ value: ONE_DRIVE_TAB_DRIVES, label: ONE_DRIVE_TAB_LABEL_DRIVES }
|
|
229
234
|
];
|
|
230
235
|
|
|
231
|
-
|
|
236
|
+
// Token-endpoint error codes that mean the stored refresh token is dead and
|
|
237
|
+
// only a reconnect brings it back. `unauthorized_client` is Google's answer
|
|
238
|
+
// when a token no longer belongs to the app it is refreshed with.
|
|
239
|
+
const REAUTH_ERROR_CODES = [
|
|
240
|
+
'invalid_grant',
|
|
241
|
+
'invalid_token',
|
|
242
|
+
'unauthorized_client'
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
// Token-endpoint error codes that are about our own client id / secret, not
|
|
246
|
+
// the user's token: a reconnect can't fix them, so they never flag one.
|
|
247
|
+
const OAUTH_CLIENT_ERROR_CODES = ['invalid_client'];
|
|
232
248
|
|
|
233
249
|
const RESOURCE_TYPE_TOOLTIP_MIN_WIDTH = 300;
|
|
234
250
|
|
|
@@ -324,6 +340,9 @@ const CRAWL_MAX_PAGES_LIMIT = 1000;
|
|
|
324
340
|
const CRAWL_DEFAULT_MAX_DEPTH = 3;
|
|
325
341
|
const CRAWL_MAX_DEPTH_LIMIT = 10;
|
|
326
342
|
const CRAWL_PAGE_FETCH_TIMEOUT_MS = 30000;
|
|
343
|
+
// Redirects one crawl fetch may follow. Each hop is screened on its own, so a
|
|
344
|
+
// public URL can't bounce the crawler onto a private address.
|
|
345
|
+
const CRAWL_MAX_REDIRECTS = 5;
|
|
327
346
|
const CRAWL_USER_AGENT =
|
|
328
347
|
'Mozilla/5.0 (compatible; GanjuCrawler/1.0; +https://ganju.ai)';
|
|
329
348
|
const CRAWL_PAGE_QUEUE_BATCH_SIZE = 100;
|
|
@@ -597,6 +616,11 @@ const RATE_LIMIT_BACKOFF_SECONDS = 60;
|
|
|
597
616
|
// / Container reset) so the retried job lands after the object has recovered.
|
|
598
617
|
const TRANSIENT_BACKOFF_SECONDS = 5;
|
|
599
618
|
|
|
619
|
+
// Retries every resource queue consumer is configured with (`max_retries` in
|
|
620
|
+
// the API worker's wrangler config). A job's last delivery is attempt
|
|
621
|
+
// QUEUE_MAX_RETRIES + 1; only then is its work marked failed.
|
|
622
|
+
const QUEUE_MAX_RETRIES = 3;
|
|
623
|
+
|
|
600
624
|
const CHANNEL_PLATFORM_TELEGRAM = 'telegram' as 'telegram';
|
|
601
625
|
const CHANNEL_PLATFORM_SLACK = 'slack' as 'slack';
|
|
602
626
|
const CHANNEL_PLATFORM_WHATSAPP = 'whatsapp' as 'whatsapp';
|
|
@@ -808,15 +832,39 @@ const TOOL_CONFIRMATION_ASK_INSTRUCTION =
|
|
|
808
832
|
'confirm, and must not say or suggest that they were done, nor describe ' +
|
|
809
833
|
'or make up any result from them.';
|
|
810
834
|
// For MCP clients, whose chat the server can't see: a sensitive call without a
|
|
811
|
-
// valid confirmation isn't run
|
|
812
|
-
// again with a token that binds the second call to these exact
|
|
813
|
-
//
|
|
835
|
+
// valid confirmation isn't run. Ordinarily the model is told to ask the user
|
|
836
|
+
// and call again with a token that binds the second call to these exact
|
|
837
|
+
// arguments; once the session has read outside content, the user approves it
|
|
838
|
+
// on a dashboard page instead, and the second call carries that approval's
|
|
839
|
+
// id. How long either stays good, and the argument that carries it.
|
|
814
840
|
const MCP_TOOL_CONFIRMATION_TTL_MS = 10 * 60 * 1000;
|
|
815
841
|
const MCP_TOOL_CONFIRMATION_ARG = 'confirmation';
|
|
816
842
|
const MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION =
|
|
817
843
|
"Leave out on the first call. This action needs the user's yes: the " +
|
|
818
|
-
'first call returns a
|
|
819
|
-
'same arguments and that
|
|
844
|
+
'first call returns a code and says how to get that yes; once the user ' +
|
|
845
|
+
'has given it, call again with the same arguments and that code here.';
|
|
846
|
+
// The same argument where only some calls ask: those made after the session
|
|
847
|
+
// read outside content.
|
|
848
|
+
const MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION_CONDITIONAL =
|
|
849
|
+
"Leave out on the first call. If this action needs the user's yes, the " +
|
|
850
|
+
'first call returns a code instead of running and says how to get that ' +
|
|
851
|
+
'yes; once the user has given it, call again with the same arguments and ' +
|
|
852
|
+
'that code here.';
|
|
853
|
+
// The dashboard page where a signed-in user approves or denies one such call,
|
|
854
|
+
// as `${TOOL_APPROVAL_PATH}/<approval id>`, and the two answers it sends.
|
|
855
|
+
const TOOL_APPROVAL_PATH = '/tool-approval';
|
|
856
|
+
const TOOL_APPROVAL_DECISION_APPROVE = 'approve';
|
|
857
|
+
const TOOL_APPROVAL_DECISION_DENY = 'deny';
|
|
858
|
+
const TOOL_APPROVAL_DECISIONS = [
|
|
859
|
+
TOOL_APPROVAL_DECISION_APPROVE,
|
|
860
|
+
TOOL_APPROVAL_DECISION_DENY
|
|
861
|
+
] as const;
|
|
862
|
+
// Where an approval stands, as the page shows it.
|
|
863
|
+
const TOOL_APPROVAL_STATUS_PENDING = 'pending';
|
|
864
|
+
const TOOL_APPROVAL_STATUS_APPROVED = 'approved';
|
|
865
|
+
const TOOL_APPROVAL_STATUS_DENIED = 'denied';
|
|
866
|
+
const TOOL_APPROVAL_STATUS_USED = 'used';
|
|
867
|
+
const TOOL_APPROVAL_STATUS_EXPIRED = 'expired';
|
|
820
868
|
// The question when the model wrote none, above the runner's own summary.
|
|
821
869
|
const TOOL_CONFIRMATION_FALLBACK_QUESTION =
|
|
822
870
|
'Before I do this, please confirm. Reply yes to go ahead, or no to cancel.';
|
|
@@ -841,13 +889,15 @@ const SLACK_MAX_SOURCE_BUTTONS = 10;
|
|
|
841
889
|
|
|
842
890
|
// The Slack app configuration Ganju needs, surfaced in the UI when connecting a
|
|
843
891
|
// Slack channel. Required scopes back the runner's calls (mentions, DMs,
|
|
844
|
-
// posting, file upload
|
|
845
|
-
//
|
|
892
|
+
// posting, file upload, downloading a voice clip to transcribe it);
|
|
893
|
+
// recommended scopes only enrich names/titles and degrade gracefully; bot
|
|
894
|
+
// events are what the webhook subscribes to.
|
|
846
895
|
const SLACK_REQUIRED_SCOPES = [
|
|
847
896
|
'app_mentions:read',
|
|
848
897
|
'im:history',
|
|
849
898
|
'chat:write',
|
|
850
|
-
'files:write'
|
|
899
|
+
'files:write',
|
|
900
|
+
'files:read'
|
|
851
901
|
];
|
|
852
902
|
const SLACK_RECOMMENDED_SCOPES = ['users:read', 'channels:read', 'groups:read'];
|
|
853
903
|
const SLACK_BOT_EVENTS = ['app_mention', 'message.im'];
|
|
@@ -958,12 +1008,29 @@ const RESOURCE_SEARCH_RRF_K = 60;
|
|
|
958
1008
|
// keeps its words.
|
|
959
1009
|
const RESOURCE_SEARCH_COMMON_TERM_DIVISOR = 10;
|
|
960
1010
|
const RESOURCE_SEARCH_COMMON_TERM_MIN = 5;
|
|
961
|
-
// Second stage
|
|
962
|
-
//
|
|
963
|
-
//
|
|
964
|
-
//
|
|
1011
|
+
// Second stage: the top fused candidates re-scored by a cross-encoder, which
|
|
1012
|
+
// reads query and chunk together instead of comparing two independently
|
|
1013
|
+
// computed vectors. One model call per search; a failure falls back to the
|
|
1014
|
+
// fused order. Off by default: on the golden sets (Spanish and English) the
|
|
1015
|
+
// fused order matched or beat it on every set — 10 cases better, 5 worse — and
|
|
1016
|
+
// it added 0.5–0.9 s a search plus a ~6 s cold start. The search eval can
|
|
1017
|
+
// still turn it on per request to re-measure.
|
|
1018
|
+
const RESOURCE_SEARCH_RERANK_ENABLED = false;
|
|
965
1019
|
const RESOURCE_SEARCH_RERANK_CANDIDATES = 30;
|
|
966
1020
|
const RESOURCE_SEARCH_RERANK_MODEL = '@cf/baai/bge-reranker-base';
|
|
1021
|
+
// An identifier in the query (a code mixing letters and digits, a snake_case
|
|
1022
|
+
// name, or a number, dotted or hyphenated name typed on its own) found in at
|
|
1023
|
+
// most this many documents is what the user is after: its chunks go first,
|
|
1024
|
+
// whatever the reranker thinks. A cross-encoder reads "86400" or "PGRST116" as noise and
|
|
1025
|
+
// would bury the one chunk that contains it under pages that merely look
|
|
1026
|
+
// related. Past EXACT_MAX_CHUNKS matching chunks the identifier is common
|
|
1027
|
+
// enough that the usual ranking is left to decide, and the scan stops there.
|
|
1028
|
+
const RESOURCE_SEARCH_EXACT_MAX_RESOURCES = 5;
|
|
1029
|
+
const RESOURCE_SEARCH_EXACT_MAX_CHUNKS = 30;
|
|
1030
|
+
// An identifier the index never saw is looked for as plain text, which reads
|
|
1031
|
+
// every chunk of the artifact when nothing matches. Past this many chunks that
|
|
1032
|
+
// costs more than the rare hit is worth, so a large artifact skips it.
|
|
1033
|
+
const RESOURCE_SEARCH_SUBSTRING_MAX_CHUNKS = 20000;
|
|
967
1034
|
|
|
968
1035
|
const RESOURCE_HANDLER_SLEEP_AFTER = '10m';
|
|
969
1036
|
|
|
@@ -983,6 +1050,12 @@ const CHUNK_SEPARATORS: Array<{ split: string | RegExp; join: string }> = [
|
|
|
983
1050
|
|
|
984
1051
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
985
1052
|
|
|
1053
|
+
// One page of `list-resources`. A knowledge base with a crawled site runs to
|
|
1054
|
+
// thousands of resources; listed whole, that is one result of several hundred
|
|
1055
|
+
// KB in the model's context.
|
|
1056
|
+
const LIST_RESOURCES_DEFAULT_LIMIT = 100;
|
|
1057
|
+
const LIST_RESOURCES_MAX_LIMIT = 500;
|
|
1058
|
+
|
|
986
1059
|
const RESOURCE_TOOL_KEY_LIST_RESOURCES = 'list-resources';
|
|
987
1060
|
const RESOURCE_TOOL_KEY_SEARCH_RESOURCES = 'search-resources';
|
|
988
1061
|
const RESOURCE_TOOL_KEY_READ_RESOURCE = 'read-resource';
|
|
@@ -1514,7 +1587,9 @@ const RESERVED_TOOL_NAMES = [
|
|
|
1514
1587
|
TOOL_DEFINITION_KEY_HTTP_ENDPOINT,
|
|
1515
1588
|
TOOL_DEFINITION_KEY_MCP_PROXY,
|
|
1516
1589
|
TOOL_DEFINITION_KEY_CUSTOM_CODE,
|
|
1517
|
-
CUSTOM_CODE_HEALTH_TOOL
|
|
1590
|
+
CUSTOM_CODE_HEALTH_TOOL,
|
|
1591
|
+
// The channel runner's own memory tool (PARTICIPANT_MEMORY_TOOL_NAME).
|
|
1592
|
+
'remember-customer'
|
|
1518
1593
|
];
|
|
1519
1594
|
|
|
1520
1595
|
// Shared by both write paths so there is one string to translate. It has to
|
|
@@ -1779,8 +1854,16 @@ const MCP_AUTH_KINDS = [MCP_AUTH_KIND_JWT, MCP_AUTH_KIND_INTERNAL];
|
|
|
1779
1854
|
const MCP_SESSION_HEADER = 'mcp-session-id';
|
|
1780
1855
|
|
|
1781
1856
|
const MCP_INTERNAL_HEADER = 'x-ganju-internal-secret';
|
|
1857
|
+
// Sent with the internal secret by the search eval to measure the reranker:
|
|
1858
|
+
// `on` or `off` overrides RESOURCE_SEARCH_RERANK_ENABLED for that request.
|
|
1859
|
+
// Ignored on any other request.
|
|
1860
|
+
const MCP_EVAL_RERANK_HEADER = 'x-ganju-eval-rerank';
|
|
1782
1861
|
const MCP_CHANNEL_ID_HEADER = 'x-ganju-channel-id';
|
|
1783
1862
|
const MCP_CHANNEL_PLATFORM_HEADER = 'x-ganju-channel-platform';
|
|
1863
|
+
// The conversation a channel turn belongs to. The MCP worker files that turn's
|
|
1864
|
+
// calls under one session per conversation, so a call can be traced back to
|
|
1865
|
+
// the chat it ran in.
|
|
1866
|
+
const MCP_CHANNEL_CONVERSATION_HEADER = 'x-ganju-channel-conversation-id';
|
|
1784
1867
|
const MCP_CHANNEL_CLIENT_USER_AGENT = 'ganju-channel/0.0.1';
|
|
1785
1868
|
const JWKS_KV_KEY = 'jwks:v1';
|
|
1786
1869
|
const JWKS_TTL_SECONDS = 600;
|
|
@@ -1882,6 +1965,23 @@ const ACCESS_TOKEN_SCOPE_MESSAGE =
|
|
|
1882
1965
|
const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
|
|
1883
1966
|
const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
|
|
1884
1967
|
|
|
1968
|
+
// Tool observability on the project home. The windows match the activity
|
|
1969
|
+
// chart's, and none reaches past `RETENTION_DAYS.mcpRequest`.
|
|
1970
|
+
const OBSERVABILITY_RANGE_DAYS = [7, 30, 90];
|
|
1971
|
+
// An enabled tool with no call in this many days is offered for disabling.
|
|
1972
|
+
const OBSERVABILITY_UNUSED_DAYS = 30;
|
|
1973
|
+
// Two calls in one session this close together are one attempt followed by
|
|
1974
|
+
// the next: a retry when it's the same tool, a switch when it's another tool
|
|
1975
|
+
// after an error.
|
|
1976
|
+
const OBSERVABILITY_FOLLOW_UP_SECONDS = 120;
|
|
1977
|
+
// How a call rejected by its tool's input schema reads in `error_message`.
|
|
1978
|
+
const OBSERVABILITY_SCHEMA_REJECTION_PREFIX = 'Input validation error';
|
|
1979
|
+
const OBSERVABILITY_TOP_ERRORS = 8;
|
|
1980
|
+
const OBSERVABILITY_CALLS_DEFAULT_LIMIT = 25;
|
|
1981
|
+
const OBSERVABILITY_CALLS_MAX_LIMIT = 100;
|
|
1982
|
+
// A session timeline is read whole up to this many calls, newest kept.
|
|
1983
|
+
const OBSERVABILITY_SESSION_MAX_CALLS = 500;
|
|
1984
|
+
|
|
1885
1985
|
const BOT_GRANT_TYPE = 'urn:ganju:bot-on-behalf-of';
|
|
1886
1986
|
const EXTERNAL_LINK_VERIFICATION_PREFIX = 'external_link:';
|
|
1887
1987
|
const EXTERNAL_LINK_TTL_SECONDS = 600;
|
|
@@ -2051,6 +2151,40 @@ const PRICING_TOOL_CALL_PER_M_USD = 5;
|
|
|
2051
2151
|
const PRICING_TOOL_CALL_HARD_CAP = 20_000_000;
|
|
2052
2152
|
const PRICING_CUSTOM_DOMAIN_USD = 15;
|
|
2053
2153
|
|
|
2154
|
+
// Project data (one SQLite Durable Object per artifact). Cloudflare caps a
|
|
2155
|
+
// database at 10 GB; we stop writes that add data at 9 GB so there is room to
|
|
2156
|
+
// delete, archive and export.
|
|
2157
|
+
const DATA_MAX_DATABASE_BYTES = 9 * GB;
|
|
2158
|
+
const DATA_MAX_TABLES_PER_DATABASE = 50;
|
|
2159
|
+
const DATA_TRIGGER_RUN_HARD_CAP = 5_000_000;
|
|
2160
|
+
// A deleted database can be restored for this long before its storage is
|
|
2161
|
+
// freed.
|
|
2162
|
+
const DATA_DELETE_GRACE_DAYS = 7;
|
|
2163
|
+
const ARTIFACT_DATABASE_STATUS_CREATING = 'CREATING' as 'CREATING';
|
|
2164
|
+
const ARTIFACT_DATABASE_STATUS_READY = 'READY' as 'READY';
|
|
2165
|
+
const ARTIFACT_DATABASE_STATUS_FAILED = 'FAILED' as 'FAILED';
|
|
2166
|
+
const ARTIFACT_DATABASE_STATUS_DELETING = 'DELETING' as 'DELETING';
|
|
2167
|
+
// The hourly purge has claimed the row and is emptying the object; a restore
|
|
2168
|
+
// can no longer take it back.
|
|
2169
|
+
const ARTIFACT_DATABASE_STATUS_PURGING = 'PURGING' as 'PURGING';
|
|
2170
|
+
const ARTIFACT_DATABASE_STATUS_DELETED = 'DELETED' as 'DELETED';
|
|
2171
|
+
const ARTIFACT_DATABASE_STATUSES = [
|
|
2172
|
+
ARTIFACT_DATABASE_STATUS_CREATING,
|
|
2173
|
+
ARTIFACT_DATABASE_STATUS_READY,
|
|
2174
|
+
ARTIFACT_DATABASE_STATUS_FAILED,
|
|
2175
|
+
ARTIFACT_DATABASE_STATUS_DELETING,
|
|
2176
|
+
ARTIFACT_DATABASE_STATUS_PURGING,
|
|
2177
|
+
ARTIFACT_DATABASE_STATUS_DELETED
|
|
2178
|
+
];
|
|
2179
|
+
// A purge that claimed a row and never finished (the Worker died) is picked
|
|
2180
|
+
// up again after this long.
|
|
2181
|
+
const DATA_PURGE_STALE_MS = 60 * 60 * 1000;
|
|
2182
|
+
// Rows purged per hourly run: each is a call to its database object.
|
|
2183
|
+
const DATA_PURGE_BATCH = 20;
|
|
2184
|
+
// last_read_at / last_write_at move at most this often, so they aren't a
|
|
2185
|
+
// Postgres write per query.
|
|
2186
|
+
const DATA_ACTIVITY_THROTTLE_MS = 60 * 60 * 1000;
|
|
2187
|
+
|
|
2054
2188
|
interface PlanLimits {
|
|
2055
2189
|
// null on any field means "no hard limit" for that plan.
|
|
2056
2190
|
maxProjects: number | null;
|
|
@@ -2116,6 +2250,16 @@ interface PlanLimits {
|
|
|
2116
2250
|
autoSyncIntervals: ReadonlyArray<
|
|
2117
2251
|
typeof RESOURCE_SYNC_INTERVAL_DAILY | typeof RESOURCE_SYNC_INTERVAL_WEEKLY
|
|
2118
2252
|
>;
|
|
2253
|
+
// Whether a project may create a database (tables and rows). Paid-only: the
|
|
2254
|
+
// rows live in a Durable Object we pay storage and rows for. An org that
|
|
2255
|
+
// downgrades keeps its databases, read-only.
|
|
2256
|
+
canUseData: boolean;
|
|
2257
|
+
// Writes that add data stop here, under Cloudflare's 10 GB per database, so
|
|
2258
|
+
// the owner meets a readable "database full" rather than SQLITE_FULL.
|
|
2259
|
+
maxDataBytes: number | null;
|
|
2260
|
+
maxDataTablesPerDatabase: number | null;
|
|
2261
|
+
// Abuse backstop on data trigger runs a month. `null` = no backstop.
|
|
2262
|
+
triggerRunHardCap: number | null;
|
|
2119
2263
|
}
|
|
2120
2264
|
|
|
2121
2265
|
const PLAN_LIMITS: Record<
|
|
@@ -2156,7 +2300,11 @@ const PLAN_LIMITS: Record<
|
|
|
2156
2300
|
toolCallHardCap: 10_000,
|
|
2157
2301
|
includedMessages: 100,
|
|
2158
2302
|
includedEmbeddedBytes: 5 * MB,
|
|
2159
|
-
autoSyncIntervals: []
|
|
2303
|
+
autoSyncIntervals: [],
|
|
2304
|
+
canUseData: false,
|
|
2305
|
+
maxDataBytes: null,
|
|
2306
|
+
maxDataTablesPerDatabase: null,
|
|
2307
|
+
triggerRunHardCap: 0
|
|
2160
2308
|
},
|
|
2161
2309
|
PRO: {
|
|
2162
2310
|
maxProjects: null,
|
|
@@ -2183,7 +2331,11 @@ const PLAN_LIMITS: Record<
|
|
|
2183
2331
|
autoSyncIntervals: [
|
|
2184
2332
|
RESOURCE_SYNC_INTERVAL_DAILY,
|
|
2185
2333
|
RESOURCE_SYNC_INTERVAL_WEEKLY
|
|
2186
|
-
]
|
|
2334
|
+
],
|
|
2335
|
+
canUseData: true,
|
|
2336
|
+
maxDataBytes: DATA_MAX_DATABASE_BYTES,
|
|
2337
|
+
maxDataTablesPerDatabase: DATA_MAX_TABLES_PER_DATABASE,
|
|
2338
|
+
triggerRunHardCap: DATA_TRIGGER_RUN_HARD_CAP
|
|
2187
2339
|
},
|
|
2188
2340
|
ENTERPRISE: {
|
|
2189
2341
|
maxProjects: null,
|
|
@@ -2210,7 +2362,11 @@ const PLAN_LIMITS: Record<
|
|
|
2210
2362
|
autoSyncIntervals: [
|
|
2211
2363
|
RESOURCE_SYNC_INTERVAL_DAILY,
|
|
2212
2364
|
RESOURCE_SYNC_INTERVAL_WEEKLY
|
|
2213
|
-
]
|
|
2365
|
+
],
|
|
2366
|
+
canUseData: true,
|
|
2367
|
+
maxDataBytes: DATA_MAX_DATABASE_BYTES,
|
|
2368
|
+
maxDataTablesPerDatabase: DATA_MAX_TABLES_PER_DATABASE,
|
|
2369
|
+
triggerRunHardCap: null
|
|
2214
2370
|
}
|
|
2215
2371
|
};
|
|
2216
2372
|
|
|
@@ -2230,6 +2386,7 @@ const PLAN_FEATURE_EMBEDDED_STORAGE = 'embeddedStorage' as 'embeddedStorage';
|
|
|
2230
2386
|
const PLAN_FEATURE_MESSAGE = 'message' as 'message';
|
|
2231
2387
|
const PLAN_FEATURE_TOOL_CALL = 'toolCall' as 'toolCall';
|
|
2232
2388
|
const PLAN_FEATURE_RESOURCE_SYNC = 'resourceSync' as 'resourceSync';
|
|
2389
|
+
const PLAN_FEATURE_DATA = 'data' as 'data';
|
|
2233
2390
|
|
|
2234
2391
|
// Stable code returned on a quota block so clients can branch on it (402).
|
|
2235
2392
|
const PLAN_LIMIT_ERROR_CODE = 'PLAN_LIMIT_EXCEEDED';
|
|
@@ -2399,6 +2556,20 @@ export const constants = {
|
|
|
2399
2556
|
PRICING_TOOL_CALL_PER_M_USD,
|
|
2400
2557
|
PRICING_TOOL_CALL_HARD_CAP,
|
|
2401
2558
|
PRICING_CUSTOM_DOMAIN_USD,
|
|
2559
|
+
DATA_MAX_DATABASE_BYTES,
|
|
2560
|
+
DATA_MAX_TABLES_PER_DATABASE,
|
|
2561
|
+
DATA_TRIGGER_RUN_HARD_CAP,
|
|
2562
|
+
DATA_DELETE_GRACE_DAYS,
|
|
2563
|
+
DATA_PURGE_BATCH,
|
|
2564
|
+
DATA_ACTIVITY_THROTTLE_MS,
|
|
2565
|
+
ARTIFACT_DATABASE_STATUS_CREATING,
|
|
2566
|
+
ARTIFACT_DATABASE_STATUS_READY,
|
|
2567
|
+
ARTIFACT_DATABASE_STATUS_FAILED,
|
|
2568
|
+
ARTIFACT_DATABASE_STATUS_DELETING,
|
|
2569
|
+
ARTIFACT_DATABASE_STATUS_PURGING,
|
|
2570
|
+
ARTIFACT_DATABASE_STATUS_DELETED,
|
|
2571
|
+
DATA_PURGE_STALE_MS,
|
|
2572
|
+
ARTIFACT_DATABASE_STATUSES,
|
|
2402
2573
|
PLAN_FEATURE_ORGANIZATION,
|
|
2403
2574
|
PLAN_FEATURE_PROJECT,
|
|
2404
2575
|
PLAN_FEATURE_TOOL,
|
|
@@ -2413,6 +2584,7 @@ export const constants = {
|
|
|
2413
2584
|
PLAN_FEATURE_MESSAGE,
|
|
2414
2585
|
PLAN_FEATURE_TOOL_CALL,
|
|
2415
2586
|
PLAN_FEATURE_RESOURCE_SYNC,
|
|
2587
|
+
PLAN_FEATURE_DATA,
|
|
2416
2588
|
PLAN_LIMIT_ERROR_CODE,
|
|
2417
2589
|
BILLING_METER_MESSAGES,
|
|
2418
2590
|
BILLING_METER_SHARED_MESSAGES,
|
|
@@ -2501,6 +2673,7 @@ export const constants = {
|
|
|
2501
2673
|
SLACK_OAUTH_TOKEN_URL,
|
|
2502
2674
|
OAUTH_TOKEN_URLS,
|
|
2503
2675
|
CREDENTIAL_REFRESH_BUFFER_MS,
|
|
2676
|
+
CREDENTIAL_REFRESH_GIVE_UP_MS,
|
|
2504
2677
|
GOOGLE_CALENDAR_API_BASE,
|
|
2505
2678
|
GOOGLE_DRIVE_API_BASE,
|
|
2506
2679
|
GOOGLE_DRIVE_DEFAULT_PAGE_SIZE,
|
|
@@ -2536,6 +2709,7 @@ export const constants = {
|
|
|
2536
2709
|
ONE_DRIVE_TAB_LABEL_DRIVES,
|
|
2537
2710
|
ONE_DRIVE_TAB_LABELS,
|
|
2538
2711
|
REAUTH_ERROR_CODES,
|
|
2712
|
+
OAUTH_CLIENT_ERROR_CODES,
|
|
2539
2713
|
RESOURCE_TYPE_TOOLTIP_MIN_WIDTH,
|
|
2540
2714
|
SEARCH_DEBOUNCE_MS,
|
|
2541
2715
|
GOOGLE_DRIVE_EXPORT_MIME_TYPES,
|
|
@@ -2624,6 +2798,7 @@ export const constants = {
|
|
|
2624
2798
|
CRAWL_DEFAULT_MAX_DEPTH,
|
|
2625
2799
|
CRAWL_MAX_DEPTH_LIMIT,
|
|
2626
2800
|
CRAWL_PAGE_FETCH_TIMEOUT_MS,
|
|
2801
|
+
CRAWL_MAX_REDIRECTS,
|
|
2627
2802
|
CRAWL_PAGE_QUEUE_BATCH_SIZE,
|
|
2628
2803
|
CRAWL_USER_AGENT,
|
|
2629
2804
|
RESOURCE_ICON_THEME_DARK,
|
|
@@ -2641,6 +2816,7 @@ export const constants = {
|
|
|
2641
2816
|
TELEGRAM_MAX_FILE_BYTES,
|
|
2642
2817
|
RATE_LIMIT_BACKOFF_SECONDS,
|
|
2643
2818
|
TRANSIENT_BACKOFF_SECONDS,
|
|
2819
|
+
QUEUE_MAX_RETRIES,
|
|
2644
2820
|
ENCODINGS,
|
|
2645
2821
|
ENCODING_UTF8,
|
|
2646
2822
|
CHANNEL_PLATFORM_TELEGRAM,
|
|
@@ -2682,6 +2858,16 @@ export const constants = {
|
|
|
2682
2858
|
MCP_TOOL_CONFIRMATION_TTL_MS,
|
|
2683
2859
|
MCP_TOOL_CONFIRMATION_ARG,
|
|
2684
2860
|
MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION,
|
|
2861
|
+
MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION_CONDITIONAL,
|
|
2862
|
+
TOOL_APPROVAL_PATH,
|
|
2863
|
+
TOOL_APPROVAL_DECISION_APPROVE,
|
|
2864
|
+
TOOL_APPROVAL_DECISION_DENY,
|
|
2865
|
+
TOOL_APPROVAL_DECISIONS,
|
|
2866
|
+
TOOL_APPROVAL_STATUS_PENDING,
|
|
2867
|
+
TOOL_APPROVAL_STATUS_APPROVED,
|
|
2868
|
+
TOOL_APPROVAL_STATUS_DENIED,
|
|
2869
|
+
TOOL_APPROVAL_STATUS_USED,
|
|
2870
|
+
TOOL_APPROVAL_STATUS_EXPIRED,
|
|
2685
2871
|
CHANNEL_DEBOUNCE_DEFAULT_MS,
|
|
2686
2872
|
CHANNEL_DEBOUNCE_MIN_MS,
|
|
2687
2873
|
CHANNEL_DEBOUNCE_MAX_MS,
|
|
@@ -2752,8 +2938,12 @@ export const constants = {
|
|
|
2752
2938
|
RESOURCE_SEARCH_RRF_K,
|
|
2753
2939
|
RESOURCE_SEARCH_COMMON_TERM_DIVISOR,
|
|
2754
2940
|
RESOURCE_SEARCH_COMMON_TERM_MIN,
|
|
2941
|
+
RESOURCE_SEARCH_RERANK_ENABLED,
|
|
2755
2942
|
RESOURCE_SEARCH_RERANK_CANDIDATES,
|
|
2756
2943
|
RESOURCE_SEARCH_RERANK_MODEL,
|
|
2944
|
+
RESOURCE_SEARCH_EXACT_MAX_RESOURCES,
|
|
2945
|
+
RESOURCE_SEARCH_EXACT_MAX_CHUNKS,
|
|
2946
|
+
RESOURCE_SEARCH_SUBSTRING_MAX_CHUNKS,
|
|
2757
2947
|
RESOURCE_HANDLER_SLEEP_AFTER,
|
|
2758
2948
|
DOCS_URL,
|
|
2759
2949
|
BASE64_DATA_URI_RE,
|
|
@@ -2761,6 +2951,8 @@ export const constants = {
|
|
|
2761
2951
|
CHUNK_SEPARATORS,
|
|
2762
2952
|
DEFAULT_MAX_TOKENS,
|
|
2763
2953
|
RESOURCE_TOOL_KEY_LIST_RESOURCES,
|
|
2954
|
+
LIST_RESOURCES_DEFAULT_LIMIT,
|
|
2955
|
+
LIST_RESOURCES_MAX_LIMIT,
|
|
2764
2956
|
RESOURCE_TOOL_KEY_SEARCH_RESOURCES,
|
|
2765
2957
|
RESOURCE_TOOL_KEY_READ_RESOURCE,
|
|
2766
2958
|
RESOURCE_TOOL_KEY_SEND_RESOURCE,
|
|
@@ -2932,8 +3124,10 @@ export const constants = {
|
|
|
2932
3124
|
CUSTOM_CODE_RESOURCE_FILE_TOO_LARGE_MESSAGE,
|
|
2933
3125
|
RESERVED_SLUGS,
|
|
2934
3126
|
MCP_INTERNAL_HEADER,
|
|
3127
|
+
MCP_EVAL_RERANK_HEADER,
|
|
2935
3128
|
MCP_CHANNEL_ID_HEADER,
|
|
2936
3129
|
MCP_CHANNEL_PLATFORM_HEADER,
|
|
3130
|
+
MCP_CHANNEL_CONVERSATION_HEADER,
|
|
2937
3131
|
MCP_CHANNEL_CLIENT_USER_AGENT,
|
|
2938
3132
|
MCP_SESSION_HEADER,
|
|
2939
3133
|
MCP_REQUEST_METHOD_INITIALIZE,
|
|
@@ -2971,6 +3165,14 @@ export const constants = {
|
|
|
2971
3165
|
ACCESS_TOKEN_SCOPE_MESSAGE,
|
|
2972
3166
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
|
|
2973
3167
|
CUSTOM_CODE_LOGS_MAX_LIMIT,
|
|
3168
|
+
OBSERVABILITY_RANGE_DAYS,
|
|
3169
|
+
OBSERVABILITY_UNUSED_DAYS,
|
|
3170
|
+
OBSERVABILITY_FOLLOW_UP_SECONDS,
|
|
3171
|
+
OBSERVABILITY_SCHEMA_REJECTION_PREFIX,
|
|
3172
|
+
OBSERVABILITY_TOP_ERRORS,
|
|
3173
|
+
OBSERVABILITY_CALLS_DEFAULT_LIMIT,
|
|
3174
|
+
OBSERVABILITY_CALLS_MAX_LIMIT,
|
|
3175
|
+
OBSERVABILITY_SESSION_MAX_CALLS,
|
|
2974
3176
|
BOT_GRANT_TYPE,
|
|
2975
3177
|
EXTERNAL_LINK_VERIFICATION_PREFIX,
|
|
2976
3178
|
EXTERNAL_LINK_TTL_SECONDS,
|