@ganju/utils 0.0.14 → 0.0.15
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 +26 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +107 -9
- package/dist/index.d.ts +263 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +70 -0
- package/dist/jsonSchemaToZodShape.d.ts +5 -1
- package/dist/jsonSchemaToZodShape.d.ts.map +1 -1
- package/dist/jsonSchemaToZodShape.js +21 -9
- 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.map +1 -1
- package/dist/resourceRerank.js +3 -1
- package/dist/schema.d.ts +90 -26
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +87 -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 +2 -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 +111 -9
- package/src/index.ts +152 -2
- package/src/jsonSchemaToZodShape.ts +31 -13
- package/src/pagination.ts +193 -0
- package/src/processQueueBatch.ts +42 -12
- package/src/queueFailure.ts +63 -0
- package/src/resourceRerank.ts +5 -1
- package/src/schema.ts +100 -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
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.overlapTail = void 0;
|
|
4
|
+
// The tail of the previous chunk a chunk opens with, so a sentence cut at the
|
|
5
|
+
// boundary still reads whole on one side of it. `overlap` of zero (or less)
|
|
6
|
+
// borrows nothing — `slice(-0)` is `slice(0)`, the whole previous chunk, which
|
|
7
|
+
// would double every chunk's text.
|
|
8
|
+
const overlapTail = (previous, overlap) => overlap > 0 ? previous.slice(-overlap) : '';
|
|
9
|
+
exports.overlapTail = overlapTail;
|
package/dist/chunking.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../src/chunking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../src/chunking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,OAAO,KAAK,EACV,iBAAiB,EACjB,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AASpE,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,QAAQ,MAAM,EACd,aAAY,SAAS,SAAS,EAA+B,KAC5D,MAAM,EA8BR,CAAC;AA4BF,eAAO,MAAM,SAAS,GACpB,MAAM,MAAM,EACZ,UAAU;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,KACxD,MAAM,EAAoD,CAAC;AAE9D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,CAAC;AAqBhD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO;IACtC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,KAAG,aAAa,GAAG,IAUnB,CAAC;AAqFF,eAAO,MAAM,WAAW,GAAI,OAAO;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,KAAG,MASW,CAAC;AAIhB,MAAM,WAAW,aAAc,SAAQ,OAAO,CAAC,yBAAyB,CAAC;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CAChC;AAID,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,WAAW,iBAAiB,EAAE,GAAG,IAAI,EACrC,iBAAiB,MAAM,GAAG,IAAI,EAC9B,UAAU;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,KACA,aAAa,EA4Cf,CAAC"}
|
package/dist/chunking.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.prepareChunks = exports.buildHeader = exports.headingSyntaxFor = exports.chunkText = exports.splitRecursive = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
|
+
const chunkOverlap_1 = require("./chunkOverlap");
|
|
5
6
|
const sanitize_1 = require("./sanitize");
|
|
6
7
|
const trySplit = (text, sep) => {
|
|
7
8
|
if (typeof sep === 'string') {
|
|
@@ -55,7 +56,7 @@ const chunkPieces = (text, options) => {
|
|
|
55
56
|
const rawChunks = (0, exports.splitRecursive)(trimmed, target);
|
|
56
57
|
const pieces = [];
|
|
57
58
|
for (let i = 0; i < rawChunks.length; i++) {
|
|
58
|
-
const prev = i > 0 ? rawChunks[i - 1]
|
|
59
|
+
const prev = i > 0 ? (0, chunkOverlap_1.overlapTail)(rawChunks[i - 1], overlap) : '';
|
|
59
60
|
pieces.push({
|
|
60
61
|
raw: rawChunks[i],
|
|
61
62
|
content: (prev ? prev + '\n' : '') + rawChunks[i]
|
|
@@ -68,6 +69,12 @@ exports.chunkText = chunkText;
|
|
|
68
69
|
const MARKDOWN_HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)(?:[ \t]+#+)?[ \t]*$/;
|
|
69
70
|
const MARKDOWN_FENCE = /^ {0,3}(```|~~~)/;
|
|
70
71
|
const HTML_HEADING = /<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1\s*>/gi;
|
|
72
|
+
// A heading tag still open at the end of a piece, from its opening tag on: the
|
|
73
|
+
// chunker split inside it, and the rest of it opens the next piece.
|
|
74
|
+
const OPEN_HTML_HEADING = /<h([1-6])\b[^>]*>(?![\s\S]*<\/h\1\s*>)[\s\S]*$/i;
|
|
75
|
+
// Longer than any heading worth carrying: an opening tag this far back with no
|
|
76
|
+
// close is broken markup, not a heading that happened to be split.
|
|
77
|
+
const MAX_OPEN_HEADING_CHARS = 1000;
|
|
71
78
|
const MAX_HEADING_CHARS = 120;
|
|
72
79
|
const MARKDOWN_FILE_NAME = /\.(md|markdown|mdx)$/i;
|
|
73
80
|
/**
|
|
@@ -128,14 +135,27 @@ const scanHtmlHeadings = (text) => [...text.matchAll(HTML_HEADING)]
|
|
|
128
135
|
const createHeadingTracker = (syntax) => {
|
|
129
136
|
let stack = [];
|
|
130
137
|
const fence = { inFence: false };
|
|
138
|
+
// The unfinished HTML heading the previous piece ended in, if any.
|
|
139
|
+
let carry = '';
|
|
131
140
|
const apply = (event) => {
|
|
132
141
|
stack = stack.filter(h => h.level < event.level);
|
|
133
142
|
stack.push({ level: event.level, text: event.text });
|
|
134
143
|
};
|
|
144
|
+
// An HTML heading split between two pieces is read whole from the second,
|
|
145
|
+
// where it ends, as if that piece opened with it: it does, mid-heading. The
|
|
146
|
+
// separator the chunker dropped at the split is restored as a space.
|
|
147
|
+
const scanHtml = (raw) => {
|
|
148
|
+
const text = carry ? `${carry} ${raw}` : raw;
|
|
149
|
+
const shift = text.length - raw.length;
|
|
150
|
+
const open = OPEN_HTML_HEADING.exec(text)?.[0] ?? '';
|
|
151
|
+
carry = open.length <= MAX_OPEN_HEADING_CHARS ? open : '';
|
|
152
|
+
return scanHtmlHeadings(text).map(event => ({
|
|
153
|
+
...event,
|
|
154
|
+
offset: Math.max(0, event.offset - shift)
|
|
155
|
+
}));
|
|
156
|
+
};
|
|
135
157
|
return (raw) => {
|
|
136
|
-
const events = syntax === 'markdown'
|
|
137
|
-
? scanMarkdownHeadings(raw, fence)
|
|
138
|
-
: scanHtmlHeadings(raw);
|
|
158
|
+
const events = syntax === 'markdown' ? scanMarkdownHeadings(raw, fence) : scanHtml(raw);
|
|
139
159
|
const [first, ...rest] = events;
|
|
140
160
|
// Before a document's first heading there is no section to continue — only
|
|
141
161
|
// the metadata header, or the markup around an HTML body — so a chunk that
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Source } from './sources';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the answer cites this exact URL, not merely one that starts with it:
|
|
4
|
+
* on a crawled docs site the index page is a prefix of every page beneath it,
|
|
5
|
+
* so a plain substring test kept "/docs" and the site root in the footer of an
|
|
6
|
+
* answer that cited "/docs/api". A trailing slash and a #fragment still count
|
|
7
|
+
* as the same page.
|
|
8
|
+
*/
|
|
9
|
+
export declare const citesUrl: (answer: string, url: string) => boolean;
|
|
10
|
+
/** Every page number the answer cites, ranges expanded. */
|
|
11
|
+
export declare const citedPages: (answer: string) => Set<number>;
|
|
12
|
+
/**
|
|
13
|
+
* Narrow a turn's sources to the ones its answer actually cites.
|
|
14
|
+
*
|
|
15
|
+
* Every search hit used to become a footer line, so an answer drawn from one
|
|
16
|
+
* handbook section was followed by eight unrelated pages the search had also
|
|
17
|
+
* returned. A search hit is kept when the answer names its resource — by title,
|
|
18
|
+
* file name or URL — and, for a paged file, only the pages the answer cites
|
|
19
|
+
* (or the best-ranked one, when it cites none). Resources the model read whole
|
|
20
|
+
* are always kept: reading one is itself the citation.
|
|
21
|
+
*
|
|
22
|
+
* An answer that names nothing gets no search sources at all. The model is told
|
|
23
|
+
* to cite what it uses, so a reply that names nothing is a greeting or "I
|
|
24
|
+
* couldn't find that" — and a footer under it would claim sources it never
|
|
25
|
+
* used, just because search runs on every message.
|
|
26
|
+
*/
|
|
27
|
+
export declare const selectCitedSources: (sources: Source[], answerText: string) => Source[];
|
|
28
|
+
//# sourceMappingURL=citations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"citations.d.ts","sourceRoot":"","sources":["../src/citations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAyCxC;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,EAAE,KAAK,MAAM,KAAG,OAetD,CAAC;AAEF,2DAA2D;AAC3D,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,KAAG,GAAG,CAAC,MAAM,CAYrD,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EAAE,EACjB,YAAY,MAAM,KACjB,MAAM,EA4BR,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.selectCitedSources = exports.citedPages = exports.citesUrl = void 0;
|
|
4
|
+
// "p. 125", "pp. 123–126", "page 12", "págs. 4-5", "páginas 3 a 5". An
|
|
5
|
+
// abbreviation counts only with its dot and a number has to stand alone, so
|
|
6
|
+
// "p2p", "mp3" and "pp 2026" are not page references. The lookarounds, not \b,
|
|
7
|
+
// mark the edges: \b sees "á" as a word break.
|
|
8
|
+
const PAGE_REFERENCE = /(?<![\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
|
+
// Lowercased words only, so "ETag header — HTTP | MDN" in an answer matches the
|
|
11
|
+
// title "ETag header - HTTP | MDN", and "mml-book.pdf" matches "mml-book".
|
|
12
|
+
const normalize = (text) => ` ${text
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.normalize('NFKD')
|
|
15
|
+
.replace(/[̀-ͯ]/g, '')
|
|
16
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
17
|
+
.trim()} `;
|
|
18
|
+
// The ways an answer refers to a source: its whole title, the title without a
|
|
19
|
+
// parenthetical or a site suffix ("Northwind Handbook (draft)", "ETag header
|
|
20
|
+
// - HTTP | MDN"), its file name with and without the extension, and its URL.
|
|
21
|
+
const namesFor = (source) => {
|
|
22
|
+
const names = [source.title, source.title.replace(/\s*\([^)]*\)\s*/g, ' ')];
|
|
23
|
+
const lead = source.title.split(/\s+[|\-–—:]\s+/)[0];
|
|
24
|
+
// A one-word lead ("Introduction", "Pricing") names half the corpus.
|
|
25
|
+
if (lead.trim().split(/\s+/).length >= 2)
|
|
26
|
+
names.push(lead);
|
|
27
|
+
if (source.fileName) {
|
|
28
|
+
names.push(source.fileName, source.fileName.replace(/\.[a-z0-9]+$/i, ''));
|
|
29
|
+
}
|
|
30
|
+
return names.map(normalize).filter(name => name.trim().length >= 4);
|
|
31
|
+
};
|
|
32
|
+
// The characters that can carry a URL on past the point we matched. Brackets,
|
|
33
|
+
// quotes and whitespace end it — that is how a URL sits inside Markdown links,
|
|
34
|
+
// parentheses and prose.
|
|
35
|
+
const URL_CONTINUATION = /^[^\s<>()[\]{}"'`|]*/;
|
|
36
|
+
// Punctuation that closes a sentence around a URL rather than belonging to it.
|
|
37
|
+
const TRAILING_PUNCTUATION = /[.,;:!?*_]+$/;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the answer cites this exact URL, not merely one that starts with it:
|
|
40
|
+
* on a crawled docs site the index page is a prefix of every page beneath it,
|
|
41
|
+
* so a plain substring test kept "/docs" and the site root in the footer of an
|
|
42
|
+
* answer that cited "/docs/api". A trailing slash and a #fragment still count
|
|
43
|
+
* as the same page.
|
|
44
|
+
*/
|
|
45
|
+
const citesUrl = (answer, url) => {
|
|
46
|
+
const target = url.replace(/\/+$/, '');
|
|
47
|
+
if (!target)
|
|
48
|
+
return false;
|
|
49
|
+
for (let at = answer.indexOf(target); at !== -1; at = answer.indexOf(target, at + 1)) {
|
|
50
|
+
const rest = answer
|
|
51
|
+
.slice(at + target.length)
|
|
52
|
+
.match(URL_CONTINUATION)[0]
|
|
53
|
+
.replace(TRAILING_PUNCTUATION, '');
|
|
54
|
+
if (/^\/*(#.*)?$/.test(rest))
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
exports.citesUrl = citesUrl;
|
|
60
|
+
/** Every page number the answer cites, ranges expanded. */
|
|
61
|
+
const citedPages = (answer) => {
|
|
62
|
+
const pages = new Set();
|
|
63
|
+
for (const match of answer.matchAll(PAGE_REFERENCE)) {
|
|
64
|
+
const from = Number(match[1]);
|
|
65
|
+
const to = match[2] ? Number(match[2]) : from;
|
|
66
|
+
if (to < from || to - from > MAX_PAGE_RANGE) {
|
|
67
|
+
pages.add(from);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
for (let page = from; page <= to; page++)
|
|
71
|
+
pages.add(page);
|
|
72
|
+
}
|
|
73
|
+
return pages;
|
|
74
|
+
};
|
|
75
|
+
exports.citedPages = citedPages;
|
|
76
|
+
/**
|
|
77
|
+
* Narrow a turn's sources to the ones its answer actually cites.
|
|
78
|
+
*
|
|
79
|
+
* Every search hit used to become a footer line, so an answer drawn from one
|
|
80
|
+
* handbook section was followed by eight unrelated pages the search had also
|
|
81
|
+
* returned. A search hit is kept when the answer names its resource — by title,
|
|
82
|
+
* file name or URL — and, for a paged file, only the pages the answer cites
|
|
83
|
+
* (or the best-ranked one, when it cites none). Resources the model read whole
|
|
84
|
+
* are always kept: reading one is itself the citation.
|
|
85
|
+
*
|
|
86
|
+
* An answer that names nothing gets no search sources at all. The model is told
|
|
87
|
+
* to cite what it uses, so a reply that names nothing is a greeting or "I
|
|
88
|
+
* couldn't find that" — and a footer under it would claim sources it never
|
|
89
|
+
* used, just because search runs on every message.
|
|
90
|
+
*/
|
|
91
|
+
const selectCitedSources = (sources, answerText) => {
|
|
92
|
+
const searchEntries = sources.filter(s => s.score !== undefined);
|
|
93
|
+
const readEntries = sources.filter(s => s.score === undefined);
|
|
94
|
+
if (searchEntries.length === 0)
|
|
95
|
+
return sources;
|
|
96
|
+
const answer = normalize(answerText);
|
|
97
|
+
const pages = (0, exports.citedPages)(answerText);
|
|
98
|
+
const citedResources = new Set(searchEntries
|
|
99
|
+
.filter(s => (0, exports.citesUrl)(answerText, s.uri) ||
|
|
100
|
+
namesFor(s).some(name => answer.includes(name)))
|
|
101
|
+
.map(s => s.resourceId));
|
|
102
|
+
const kept = [];
|
|
103
|
+
for (const resourceId of citedResources) {
|
|
104
|
+
const entries = searchEntries.filter(s => s.resourceId === resourceId);
|
|
105
|
+
const onCitedPage = entries.filter(s => s.pageNumber !== undefined && pages.has(s.pageNumber));
|
|
106
|
+
kept.push(...(onCitedPage.length > 0 ? onCitedPage : entries.slice(0, 1)));
|
|
107
|
+
}
|
|
108
|
+
const cited = searchEntries.filter(s => kept.includes(s));
|
|
109
|
+
return [...cited, ...readEntries];
|
|
110
|
+
};
|
|
111
|
+
exports.selectCitedSources = selectCitedSources;
|
package/dist/constants.d.ts
CHANGED
|
@@ -333,6 +333,7 @@ export declare const constants: {
|
|
|
333
333
|
CRAWL_DEFAULT_MAX_DEPTH: number;
|
|
334
334
|
CRAWL_MAX_DEPTH_LIMIT: number;
|
|
335
335
|
CRAWL_PAGE_FETCH_TIMEOUT_MS: number;
|
|
336
|
+
CRAWL_MAX_REDIRECTS: number;
|
|
336
337
|
CRAWL_PAGE_QUEUE_BATCH_SIZE: number;
|
|
337
338
|
CRAWL_USER_AGENT: string;
|
|
338
339
|
RESOURCE_ICON_THEME_DARK: string;
|
|
@@ -350,6 +351,7 @@ export declare const constants: {
|
|
|
350
351
|
TELEGRAM_MAX_FILE_BYTES: number;
|
|
351
352
|
RATE_LIMIT_BACKOFF_SECONDS: number;
|
|
352
353
|
TRANSIENT_BACKOFF_SECONDS: number;
|
|
354
|
+
QUEUE_MAX_RETRIES: number;
|
|
353
355
|
ENCODINGS: string[];
|
|
354
356
|
ENCODING_UTF8: "utf-8";
|
|
355
357
|
CHANNEL_PLATFORM_TELEGRAM: "telegram";
|
|
@@ -395,6 +397,16 @@ export declare const constants: {
|
|
|
395
397
|
MCP_TOOL_CONFIRMATION_TTL_MS: number;
|
|
396
398
|
MCP_TOOL_CONFIRMATION_ARG: string;
|
|
397
399
|
MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION: string;
|
|
400
|
+
MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION_CONDITIONAL: string;
|
|
401
|
+
TOOL_APPROVAL_PATH: string;
|
|
402
|
+
TOOL_APPROVAL_DECISION_APPROVE: string;
|
|
403
|
+
TOOL_APPROVAL_DECISION_DENY: string;
|
|
404
|
+
TOOL_APPROVAL_DECISIONS: readonly ["approve", "deny"];
|
|
405
|
+
TOOL_APPROVAL_STATUS_PENDING: string;
|
|
406
|
+
TOOL_APPROVAL_STATUS_APPROVED: string;
|
|
407
|
+
TOOL_APPROVAL_STATUS_DENIED: string;
|
|
408
|
+
TOOL_APPROVAL_STATUS_USED: string;
|
|
409
|
+
TOOL_APPROVAL_STATUS_EXPIRED: string;
|
|
398
410
|
CHANNEL_DEBOUNCE_DEFAULT_MS: number;
|
|
399
411
|
CHANNEL_DEBOUNCE_MIN_MS: number;
|
|
400
412
|
CHANNEL_DEBOUNCE_MAX_MS: number;
|
|
@@ -467,6 +479,9 @@ export declare const constants: {
|
|
|
467
479
|
RESOURCE_SEARCH_COMMON_TERM_MIN: number;
|
|
468
480
|
RESOURCE_SEARCH_RERANK_CANDIDATES: number;
|
|
469
481
|
RESOURCE_SEARCH_RERANK_MODEL: string;
|
|
482
|
+
RESOURCE_SEARCH_EXACT_MAX_RESOURCES: number;
|
|
483
|
+
RESOURCE_SEARCH_EXACT_MAX_CHUNKS: number;
|
|
484
|
+
RESOURCE_SEARCH_SUBSTRING_MAX_CHUNKS: number;
|
|
470
485
|
RESOURCE_HANDLER_SLEEP_AFTER: string;
|
|
471
486
|
DOCS_URL: string;
|
|
472
487
|
BASE64_DATA_URI_RE: RegExp;
|
|
@@ -477,6 +492,8 @@ export declare const constants: {
|
|
|
477
492
|
}[];
|
|
478
493
|
DEFAULT_MAX_TOKENS: number;
|
|
479
494
|
RESOURCE_TOOL_KEY_LIST_RESOURCES: string;
|
|
495
|
+
LIST_RESOURCES_DEFAULT_LIMIT: number;
|
|
496
|
+
LIST_RESOURCES_MAX_LIMIT: number;
|
|
480
497
|
RESOURCE_TOOL_KEY_SEARCH_RESOURCES: string;
|
|
481
498
|
RESOURCE_TOOL_KEY_READ_RESOURCE: string;
|
|
482
499
|
RESOURCE_TOOL_KEY_SEND_RESOURCE: string;
|
|
@@ -650,6 +667,7 @@ export declare const constants: {
|
|
|
650
667
|
MCP_INTERNAL_HEADER: string;
|
|
651
668
|
MCP_CHANNEL_ID_HEADER: string;
|
|
652
669
|
MCP_CHANNEL_PLATFORM_HEADER: string;
|
|
670
|
+
MCP_CHANNEL_CONVERSATION_HEADER: string;
|
|
653
671
|
MCP_CHANNEL_CLIENT_USER_AGENT: string;
|
|
654
672
|
MCP_SESSION_HEADER: string;
|
|
655
673
|
MCP_REQUEST_METHOD_INITIALIZE: "initialize";
|
|
@@ -687,6 +705,14 @@ export declare const constants: {
|
|
|
687
705
|
ACCESS_TOKEN_SCOPE_MESSAGE: string;
|
|
688
706
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT: number;
|
|
689
707
|
CUSTOM_CODE_LOGS_MAX_LIMIT: number;
|
|
708
|
+
OBSERVABILITY_RANGE_DAYS: number[];
|
|
709
|
+
OBSERVABILITY_UNUSED_DAYS: number;
|
|
710
|
+
OBSERVABILITY_FOLLOW_UP_SECONDS: number;
|
|
711
|
+
OBSERVABILITY_SCHEMA_REJECTION_PREFIX: string;
|
|
712
|
+
OBSERVABILITY_TOP_ERRORS: number;
|
|
713
|
+
OBSERVABILITY_CALLS_DEFAULT_LIMIT: number;
|
|
714
|
+
OBSERVABILITY_CALLS_MAX_LIMIT: number;
|
|
715
|
+
OBSERVABILITY_SESSION_MAX_CALLS: number;
|
|
690
716
|
BOT_GRANT_TYPE: string;
|
|
691
717
|
EXTERNAL_LINK_VERIFICATION_PREFIX: string;
|
|
692
718
|
EXTERNAL_LINK_TTL_SECONDS: number;
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AA6JA,QAAA,MAAM,yBAAyB,EAAiB,UAAU,CAAC;AAC3D,QAAA,MAAM,+BAA+B,EAAuB,gBAAgB,CAAC;AAC7E,QAAA,MAAM,8BAA8B,EAAsB,eAAe,CAAC;AAC1E,QAAA,MAAM,wBAAwB,EAAgB,SAAS,CAAC;AAuCxD,QAAA,MAAM,sBAAsB,EAAiB,UAAU,CAAC;AACxD,QAAA,MAAM,4BAA4B,EAAuB,gBAAgB,CAAC;AAC1E,QAAA,MAAM,oBAAoB,EAAe,QAAQ,CAAC;AAClD,QAAA,MAAM,oBAAoB,EAAe,QAAQ,CAAC;AAoFlD,QAAA,MAAM,4BAA4B,EAAc,OAAO,CAAC;AACxD,QAAA,MAAM,6BAA6B,EAAe,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AA6JA,QAAA,MAAM,yBAAyB,EAAiB,UAAU,CAAC;AAC3D,QAAA,MAAM,+BAA+B,EAAuB,gBAAgB,CAAC;AAC7E,QAAA,MAAM,8BAA8B,EAAsB,eAAe,CAAC;AAC1E,QAAA,MAAM,wBAAwB,EAAgB,SAAS,CAAC;AAuCxD,QAAA,MAAM,sBAAsB,EAAiB,UAAU,CAAC;AACxD,QAAA,MAAM,4BAA4B,EAAuB,gBAAgB,CAAC;AAC1E,QAAA,MAAM,oBAAoB,EAAe,QAAQ,CAAC;AAClD,QAAA,MAAM,oBAAoB,EAAe,QAAQ,CAAC;AAoFlD,QAAA,MAAM,4BAA4B,EAAc,OAAO,CAAC;AACxD,QAAA,MAAM,6BAA6B,EAAe,QAAQ,CAAC;AA2V3D,QAAA,MAAM,sBAAsB,EAAkB,WAAW,CAAC;AAC1D,QAAA,MAAM,mBAAmB,EAAe,QAAQ,CAAC;AAGjD,QAAA,MAAM,mBAAmB,EAAe,QAAQ,CAAC;AAgcjD,MAAM,MAAM,mBAAmB,GAC3B;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAigCpE,UAAU,UAAU;IAElB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAGhC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAMjC,sBAAsB,EAAE,MAAM,CAAC;IAM/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IAKnB,eAAe,EAAE,OAAO,CAAC;IAOzB,gBAAgB,EAAE,OAAO,CAAC;IAK1B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAQ3C,iBAAiB,EAAE,MAAM,CAAC;IAM1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAG/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAI9B,iBAAiB,EAAE,aAAa,CAC9B,OAAO,4BAA4B,GAAG,OAAO,6BAA6B,CAC3E,CAAC;CACH;AA2PD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAluEhB,OAAO,yBAAyB,GAChC,OAAO,+BAA+B,GACtC,OAAO,8BAA8B,GACrC,OAAO,wBAAwB;eAC5B,MAAM;;;;;;;;;;;;;;;;;;eAsCT,OAAO,sBAAsB,GAC7B,OAAO,4BAA4B,GACnC,OAAO,oBAAoB,GAC3B,OAAO,oBAAoB;eACxB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0bT,OAAO,mBAAmB,GAC1B,OAAO,mBAAmB,GAC1B,OAAO,sBAAsB;eAC1B,MAAM;eACN,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAqWwB,MAAM,GAAG,MAAM;cAAQ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6gEnE,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -252,6 +252,9 @@ const CRAWL_MAX_PAGES_LIMIT = 1000;
|
|
|
252
252
|
const CRAWL_DEFAULT_MAX_DEPTH = 3;
|
|
253
253
|
const CRAWL_MAX_DEPTH_LIMIT = 10;
|
|
254
254
|
const CRAWL_PAGE_FETCH_TIMEOUT_MS = 30000;
|
|
255
|
+
// Redirects one crawl fetch may follow. Each hop is screened on its own, so a
|
|
256
|
+
// public URL can't bounce the crawler onto a private address.
|
|
257
|
+
const CRAWL_MAX_REDIRECTS = 5;
|
|
255
258
|
const CRAWL_USER_AGENT = 'Mozilla/5.0 (compatible; GanjuCrawler/1.0; +https://ganju.ai)';
|
|
256
259
|
const CRAWL_PAGE_QUEUE_BATCH_SIZE = 100;
|
|
257
260
|
const RESOURCE_ICON_THEME_DARK = 'dark';
|
|
@@ -484,6 +487,10 @@ const RATE_LIMIT_BACKOFF_SECONDS = 60;
|
|
|
484
487
|
// Short backoff for transient Cloudflare platform errors (e.g. a Durable Object
|
|
485
488
|
// / Container reset) so the retried job lands after the object has recovered.
|
|
486
489
|
const TRANSIENT_BACKOFF_SECONDS = 5;
|
|
490
|
+
// Retries every resource queue consumer is configured with (`max_retries` in
|
|
491
|
+
// the API worker's wrangler config). A job's last delivery is attempt
|
|
492
|
+
// QUEUE_MAX_RETRIES + 1; only then is its work marked failed.
|
|
493
|
+
const QUEUE_MAX_RETRIES = 3;
|
|
487
494
|
const CHANNEL_PLATFORM_TELEGRAM = 'telegram';
|
|
488
495
|
const CHANNEL_PLATFORM_SLACK = 'slack';
|
|
489
496
|
const CHANNEL_PLATFORM_WHATSAPP = 'whatsapp';
|
|
@@ -667,14 +674,37 @@ const TOOL_CONFIRMATION_ASK_INSTRUCTION = 'IMPORTANT: these actions have NOT bee
|
|
|
667
674
|
'confirm, and must not say or suggest that they were done, nor describe ' +
|
|
668
675
|
'or make up any result from them.';
|
|
669
676
|
// For MCP clients, whose chat the server can't see: a sensitive call without a
|
|
670
|
-
// valid confirmation isn't run
|
|
671
|
-
// again with a token that binds the second call to these exact
|
|
672
|
-
//
|
|
677
|
+
// valid confirmation isn't run. Ordinarily the model is told to ask the user
|
|
678
|
+
// and call again with a token that binds the second call to these exact
|
|
679
|
+
// arguments; once the session has read outside content, the user approves it
|
|
680
|
+
// on a dashboard page instead, and the second call carries that approval's
|
|
681
|
+
// id. How long either stays good, and the argument that carries it.
|
|
673
682
|
const MCP_TOOL_CONFIRMATION_TTL_MS = 10 * 60 * 1000;
|
|
674
683
|
const MCP_TOOL_CONFIRMATION_ARG = 'confirmation';
|
|
675
684
|
const MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION = "Leave out on the first call. This action needs the user's yes: the " +
|
|
676
|
-
'first call returns a
|
|
677
|
-
'same arguments and that
|
|
685
|
+
'first call returns a code and says how to get that yes; once the user ' +
|
|
686
|
+
'has given it, call again with the same arguments and that code here.';
|
|
687
|
+
// The same argument where only some calls ask: those made after the session
|
|
688
|
+
// read outside content.
|
|
689
|
+
const MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION_CONDITIONAL = "Leave out on the first call. If this action needs the user's yes, the " +
|
|
690
|
+
'first call returns a code instead of running and says how to get that ' +
|
|
691
|
+
'yes; once the user has given it, call again with the same arguments and ' +
|
|
692
|
+
'that code here.';
|
|
693
|
+
// The dashboard page where a signed-in user approves or denies one such call,
|
|
694
|
+
// as `${TOOL_APPROVAL_PATH}/<approval id>`, and the two answers it sends.
|
|
695
|
+
const TOOL_APPROVAL_PATH = '/tool-approval';
|
|
696
|
+
const TOOL_APPROVAL_DECISION_APPROVE = 'approve';
|
|
697
|
+
const TOOL_APPROVAL_DECISION_DENY = 'deny';
|
|
698
|
+
const TOOL_APPROVAL_DECISIONS = [
|
|
699
|
+
TOOL_APPROVAL_DECISION_APPROVE,
|
|
700
|
+
TOOL_APPROVAL_DECISION_DENY
|
|
701
|
+
];
|
|
702
|
+
// Where an approval stands, as the page shows it.
|
|
703
|
+
const TOOL_APPROVAL_STATUS_PENDING = 'pending';
|
|
704
|
+
const TOOL_APPROVAL_STATUS_APPROVED = 'approved';
|
|
705
|
+
const TOOL_APPROVAL_STATUS_DENIED = 'denied';
|
|
706
|
+
const TOOL_APPROVAL_STATUS_USED = 'used';
|
|
707
|
+
const TOOL_APPROVAL_STATUS_EXPIRED = 'expired';
|
|
678
708
|
// The question when the model wrote none, above the runner's own summary.
|
|
679
709
|
const TOOL_CONFIRMATION_FALLBACK_QUESTION = 'Before I do this, please confirm. Reply yes to go ahead, or no to cancel.';
|
|
680
710
|
const TELEGRAM_SECRET_HEADER = 'x-telegram-bot-api-secret-token';
|
|
@@ -695,13 +725,15 @@ const SLACK_MESSAGE_LIMIT = 2900;
|
|
|
695
725
|
const SLACK_MAX_SOURCE_BUTTONS = 10;
|
|
696
726
|
// The Slack app configuration Ganju needs, surfaced in the UI when connecting a
|
|
697
727
|
// Slack channel. Required scopes back the runner's calls (mentions, DMs,
|
|
698
|
-
// posting, file upload
|
|
699
|
-
//
|
|
728
|
+
// posting, file upload, downloading a voice clip to transcribe it);
|
|
729
|
+
// recommended scopes only enrich names/titles and degrade gracefully; bot
|
|
730
|
+
// events are what the webhook subscribes to.
|
|
700
731
|
const SLACK_REQUIRED_SCOPES = [
|
|
701
732
|
'app_mentions:read',
|
|
702
733
|
'im:history',
|
|
703
734
|
'chat:write',
|
|
704
|
-
'files:write'
|
|
735
|
+
'files:write',
|
|
736
|
+
'files:read'
|
|
705
737
|
];
|
|
706
738
|
const SLACK_RECOMMENDED_SCOPES = ['users:read', 'channels:read', 'groups:read'];
|
|
707
739
|
const SLACK_BOT_EVENTS = ['app_mention', 'message.im'];
|
|
@@ -814,6 +846,19 @@ const RESOURCE_SEARCH_COMMON_TERM_MIN = 5;
|
|
|
814
846
|
// call per search; a failure falls back to the fused order.
|
|
815
847
|
const RESOURCE_SEARCH_RERANK_CANDIDATES = 30;
|
|
816
848
|
const RESOURCE_SEARCH_RERANK_MODEL = '@cf/baai/bge-reranker-base';
|
|
849
|
+
// An identifier in the query (a code mixing letters and digits, a snake_case
|
|
850
|
+
// name, or a number, dotted or hyphenated name typed on its own) found in at
|
|
851
|
+
// most this many documents is what the user is after: its chunks go first,
|
|
852
|
+
// whatever the reranker thinks. A cross-encoder reads "86400" or "PGRST116" as noise and
|
|
853
|
+
// would bury the one chunk that contains it under pages that merely look
|
|
854
|
+
// related. Past EXACT_MAX_CHUNKS matching chunks the identifier is common
|
|
855
|
+
// enough that the usual ranking is left to decide, and the scan stops there.
|
|
856
|
+
const RESOURCE_SEARCH_EXACT_MAX_RESOURCES = 5;
|
|
857
|
+
const RESOURCE_SEARCH_EXACT_MAX_CHUNKS = 30;
|
|
858
|
+
// An identifier the index never saw is looked for as plain text, which reads
|
|
859
|
+
// every chunk of the artifact when nothing matches. Past this many chunks that
|
|
860
|
+
// costs more than the rare hit is worth, so a large artifact skips it.
|
|
861
|
+
const RESOURCE_SEARCH_SUBSTRING_MAX_CHUNKS = 20000;
|
|
817
862
|
const RESOURCE_HANDLER_SLEEP_AFTER = '10m';
|
|
818
863
|
const DOCS_URL = 'https://ganju.ai/docs/';
|
|
819
864
|
const BASE64_DATA_URI_RE = /data:image\/[a-zA-Z0-9+\-.]+;base64,[A-Za-z0-9+/=\s]+/g;
|
|
@@ -826,6 +871,11 @@ const CHUNK_SEPARATORS = [
|
|
|
826
871
|
{ split: ' ', join: ' ' }
|
|
827
872
|
];
|
|
828
873
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
874
|
+
// One page of `list-resources`. A knowledge base with a crawled site runs to
|
|
875
|
+
// thousands of resources; listed whole, that is one result of several hundred
|
|
876
|
+
// KB in the model's context.
|
|
877
|
+
const LIST_RESOURCES_DEFAULT_LIMIT = 100;
|
|
878
|
+
const LIST_RESOURCES_MAX_LIMIT = 500;
|
|
829
879
|
const RESOURCE_TOOL_KEY_LIST_RESOURCES = 'list-resources';
|
|
830
880
|
const RESOURCE_TOOL_KEY_SEARCH_RESOURCES = 'search-resources';
|
|
831
881
|
const RESOURCE_TOOL_KEY_READ_RESOURCE = 'read-resource';
|
|
@@ -1288,7 +1338,9 @@ const RESERVED_TOOL_NAMES = [
|
|
|
1288
1338
|
TOOL_DEFINITION_KEY_HTTP_ENDPOINT,
|
|
1289
1339
|
TOOL_DEFINITION_KEY_MCP_PROXY,
|
|
1290
1340
|
TOOL_DEFINITION_KEY_CUSTOM_CODE,
|
|
1291
|
-
sdkConstants_1.CUSTOM_CODE_HEALTH_TOOL
|
|
1341
|
+
sdkConstants_1.CUSTOM_CODE_HEALTH_TOOL,
|
|
1342
|
+
// The channel runner's own memory tool (PARTICIPANT_MEMORY_TOOL_NAME).
|
|
1343
|
+
'remember-customer'
|
|
1292
1344
|
];
|
|
1293
1345
|
// Shared by both write paths so there is one string to translate. It has to
|
|
1294
1346
|
// carry a word `matchStatus` recognises (errorHandler.ts), because the
|
|
@@ -1518,6 +1570,10 @@ const MCP_SESSION_HEADER = 'mcp-session-id';
|
|
|
1518
1570
|
const MCP_INTERNAL_HEADER = 'x-ganju-internal-secret';
|
|
1519
1571
|
const MCP_CHANNEL_ID_HEADER = 'x-ganju-channel-id';
|
|
1520
1572
|
const MCP_CHANNEL_PLATFORM_HEADER = 'x-ganju-channel-platform';
|
|
1573
|
+
// The conversation a channel turn belongs to. The MCP worker files that turn's
|
|
1574
|
+
// calls under one session per conversation, so a call can be traced back to
|
|
1575
|
+
// the chat it ran in.
|
|
1576
|
+
const MCP_CHANNEL_CONVERSATION_HEADER = 'x-ganju-channel-conversation-id';
|
|
1521
1577
|
const MCP_CHANNEL_CLIENT_USER_AGENT = 'ganju-channel/0.0.1';
|
|
1522
1578
|
const JWKS_KV_KEY = 'jwks:v1';
|
|
1523
1579
|
const JWKS_TTL_SECONDS = 600;
|
|
@@ -1604,6 +1660,22 @@ const ACCESS_TOKEN_SCOPE_MESSAGE = 'This token is scoped to a different project'
|
|
|
1604
1660
|
// Recent custom-tool invocations, as `ganju logs` reads them.
|
|
1605
1661
|
const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
|
|
1606
1662
|
const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
|
|
1663
|
+
// Tool observability on the project home. The windows match the activity
|
|
1664
|
+
// chart's, and none reaches past `RETENTION_DAYS.mcpRequest`.
|
|
1665
|
+
const OBSERVABILITY_RANGE_DAYS = [7, 30, 90];
|
|
1666
|
+
// An enabled tool with no call in this many days is offered for disabling.
|
|
1667
|
+
const OBSERVABILITY_UNUSED_DAYS = 30;
|
|
1668
|
+
// Two calls in one session this close together are one attempt followed by
|
|
1669
|
+
// the next: a retry when it's the same tool, a switch when it's another tool
|
|
1670
|
+
// after an error.
|
|
1671
|
+
const OBSERVABILITY_FOLLOW_UP_SECONDS = 120;
|
|
1672
|
+
// How a call rejected by its tool's input schema reads in `error_message`.
|
|
1673
|
+
const OBSERVABILITY_SCHEMA_REJECTION_PREFIX = 'Input validation error';
|
|
1674
|
+
const OBSERVABILITY_TOP_ERRORS = 8;
|
|
1675
|
+
const OBSERVABILITY_CALLS_DEFAULT_LIMIT = 25;
|
|
1676
|
+
const OBSERVABILITY_CALLS_MAX_LIMIT = 100;
|
|
1677
|
+
// A session timeline is read whole up to this many calls, newest kept.
|
|
1678
|
+
const OBSERVABILITY_SESSION_MAX_CALLS = 500;
|
|
1607
1679
|
const BOT_GRANT_TYPE = 'urn:ganju:bot-on-behalf-of';
|
|
1608
1680
|
const EXTERNAL_LINK_VERIFICATION_PREFIX = 'external_link:';
|
|
1609
1681
|
const EXTERNAL_LINK_TTL_SECONDS = 600;
|
|
@@ -2251,6 +2323,7 @@ exports.constants = {
|
|
|
2251
2323
|
CRAWL_DEFAULT_MAX_DEPTH,
|
|
2252
2324
|
CRAWL_MAX_DEPTH_LIMIT,
|
|
2253
2325
|
CRAWL_PAGE_FETCH_TIMEOUT_MS,
|
|
2326
|
+
CRAWL_MAX_REDIRECTS,
|
|
2254
2327
|
CRAWL_PAGE_QUEUE_BATCH_SIZE,
|
|
2255
2328
|
CRAWL_USER_AGENT,
|
|
2256
2329
|
RESOURCE_ICON_THEME_DARK,
|
|
@@ -2268,6 +2341,7 @@ exports.constants = {
|
|
|
2268
2341
|
TELEGRAM_MAX_FILE_BYTES,
|
|
2269
2342
|
RATE_LIMIT_BACKOFF_SECONDS,
|
|
2270
2343
|
TRANSIENT_BACKOFF_SECONDS,
|
|
2344
|
+
QUEUE_MAX_RETRIES,
|
|
2271
2345
|
ENCODINGS,
|
|
2272
2346
|
ENCODING_UTF8,
|
|
2273
2347
|
CHANNEL_PLATFORM_TELEGRAM,
|
|
@@ -2309,6 +2383,16 @@ exports.constants = {
|
|
|
2309
2383
|
MCP_TOOL_CONFIRMATION_TTL_MS,
|
|
2310
2384
|
MCP_TOOL_CONFIRMATION_ARG,
|
|
2311
2385
|
MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION,
|
|
2386
|
+
MCP_TOOL_CONFIRMATION_ARG_DESCRIPTION_CONDITIONAL,
|
|
2387
|
+
TOOL_APPROVAL_PATH,
|
|
2388
|
+
TOOL_APPROVAL_DECISION_APPROVE,
|
|
2389
|
+
TOOL_APPROVAL_DECISION_DENY,
|
|
2390
|
+
TOOL_APPROVAL_DECISIONS,
|
|
2391
|
+
TOOL_APPROVAL_STATUS_PENDING,
|
|
2392
|
+
TOOL_APPROVAL_STATUS_APPROVED,
|
|
2393
|
+
TOOL_APPROVAL_STATUS_DENIED,
|
|
2394
|
+
TOOL_APPROVAL_STATUS_USED,
|
|
2395
|
+
TOOL_APPROVAL_STATUS_EXPIRED,
|
|
2312
2396
|
CHANNEL_DEBOUNCE_DEFAULT_MS,
|
|
2313
2397
|
CHANNEL_DEBOUNCE_MIN_MS,
|
|
2314
2398
|
CHANNEL_DEBOUNCE_MAX_MS,
|
|
@@ -2381,6 +2465,9 @@ exports.constants = {
|
|
|
2381
2465
|
RESOURCE_SEARCH_COMMON_TERM_MIN,
|
|
2382
2466
|
RESOURCE_SEARCH_RERANK_CANDIDATES,
|
|
2383
2467
|
RESOURCE_SEARCH_RERANK_MODEL,
|
|
2468
|
+
RESOURCE_SEARCH_EXACT_MAX_RESOURCES,
|
|
2469
|
+
RESOURCE_SEARCH_EXACT_MAX_CHUNKS,
|
|
2470
|
+
RESOURCE_SEARCH_SUBSTRING_MAX_CHUNKS,
|
|
2384
2471
|
RESOURCE_HANDLER_SLEEP_AFTER,
|
|
2385
2472
|
DOCS_URL,
|
|
2386
2473
|
BASE64_DATA_URI_RE,
|
|
@@ -2388,6 +2475,8 @@ exports.constants = {
|
|
|
2388
2475
|
CHUNK_SEPARATORS,
|
|
2389
2476
|
DEFAULT_MAX_TOKENS,
|
|
2390
2477
|
RESOURCE_TOOL_KEY_LIST_RESOURCES,
|
|
2478
|
+
LIST_RESOURCES_DEFAULT_LIMIT,
|
|
2479
|
+
LIST_RESOURCES_MAX_LIMIT,
|
|
2391
2480
|
RESOURCE_TOOL_KEY_SEARCH_RESOURCES,
|
|
2392
2481
|
RESOURCE_TOOL_KEY_READ_RESOURCE,
|
|
2393
2482
|
RESOURCE_TOOL_KEY_SEND_RESOURCE,
|
|
@@ -2561,6 +2650,7 @@ exports.constants = {
|
|
|
2561
2650
|
MCP_INTERNAL_HEADER,
|
|
2562
2651
|
MCP_CHANNEL_ID_HEADER,
|
|
2563
2652
|
MCP_CHANNEL_PLATFORM_HEADER,
|
|
2653
|
+
MCP_CHANNEL_CONVERSATION_HEADER,
|
|
2564
2654
|
MCP_CHANNEL_CLIENT_USER_AGENT,
|
|
2565
2655
|
MCP_SESSION_HEADER,
|
|
2566
2656
|
MCP_REQUEST_METHOD_INITIALIZE,
|
|
@@ -2598,6 +2688,14 @@ exports.constants = {
|
|
|
2598
2688
|
ACCESS_TOKEN_SCOPE_MESSAGE,
|
|
2599
2689
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
|
|
2600
2690
|
CUSTOM_CODE_LOGS_MAX_LIMIT,
|
|
2691
|
+
OBSERVABILITY_RANGE_DAYS,
|
|
2692
|
+
OBSERVABILITY_UNUSED_DAYS,
|
|
2693
|
+
OBSERVABILITY_FOLLOW_UP_SECONDS,
|
|
2694
|
+
OBSERVABILITY_SCHEMA_REJECTION_PREFIX,
|
|
2695
|
+
OBSERVABILITY_TOP_ERRORS,
|
|
2696
|
+
OBSERVABILITY_CALLS_DEFAULT_LIMIT,
|
|
2697
|
+
OBSERVABILITY_CALLS_MAX_LIMIT,
|
|
2698
|
+
OBSERVABILITY_SESSION_MAX_CALLS,
|
|
2601
2699
|
BOT_GRANT_TYPE,
|
|
2602
2700
|
EXTERNAL_LINK_VERIFICATION_PREFIX,
|
|
2603
2701
|
EXTERNAL_LINK_TTL_SECONDS,
|