nexus-ai-pro 2.2.0 → 2.4.0
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/API_STABILITY.md +225 -0
- package/CHANGELOG.md +325 -1
- package/README.md +20 -6
- package/SECURITY.md +140 -0
- package/dist/adapter-kit/contracts.d.ts +87 -0
- package/dist/adapter-kit/contracts.js +237 -0
- package/dist/adapter-kit/errors.d.ts +39 -0
- package/dist/adapter-kit/errors.js +52 -0
- package/dist/adapter-kit/index.d.ts +144 -0
- package/dist/adapter-kit/index.js +178 -0
- package/dist/adapter-kit/semver.d.ts +12 -0
- package/dist/adapter-kit/semver.js +55 -0
- package/dist/agent/capabilities.d.ts +39 -0
- package/dist/agent/capabilities.js +36 -0
- package/dist/agent/create-agent.d.ts +61 -11
- package/dist/agent/create-agent.js +136 -22
- package/dist/agent/index.d.ts +3 -2
- package/dist/agent/index.js +1 -1
- package/dist/agent/middleware/approval.d.ts +26 -0
- package/dist/agent/middleware/approval.js +23 -0
- package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist/agent/middleware/context-editor.d.ts +52 -0
- package/dist/agent/middleware/context-editor.js +66 -0
- package/dist/agent/middleware/filesystem.d.ts +44 -0
- package/dist/agent/middleware/filesystem.js +83 -0
- package/dist/agent/middleware/index.d.ts +19 -0
- package/dist/agent/middleware/index.js +9 -0
- package/dist/agent/middleware/limits.d.ts +42 -0
- package/dist/agent/middleware/limits.js +52 -0
- package/dist/agent/middleware/models.d.ts +48 -0
- package/dist/agent/middleware/models.js +44 -0
- package/dist/agent/middleware/pii.d.ts +67 -0
- package/dist/agent/middleware/pii.js +142 -0
- package/dist/agent/middleware/retry.d.ts +66 -0
- package/dist/agent/middleware/retry.js +59 -0
- package/dist/agent/middleware/shared.d.ts +41 -0
- package/dist/agent/middleware/shared.js +73 -0
- package/dist/agent/middleware/tool-selector.d.ts +64 -0
- package/dist/agent/middleware/tool-selector.js +106 -0
- package/dist/agent/permissions.d.ts +113 -0
- package/dist/agent/permissions.js +180 -0
- package/dist/agent/sandbox.d.ts +151 -0
- package/dist/agent/sandbox.js +257 -0
- package/dist/agent/tool.d.ts +8 -3
- package/dist/agent/tool.js +14 -3
- package/dist/core/secure-stream.js +4 -3
- package/dist/deep-agent/index.d.ts +109 -0
- package/dist/deep-agent/index.js +216 -0
- package/dist/embeddings/adapters.js +2 -1
- package/dist/evals/judge.js +2 -5
- package/dist/evaluate/cache.d.ts +0 -14
- package/dist/evaluate/cache.js +0 -30
- package/dist/evaluate/file-cache.d.ts +15 -0
- package/dist/evaluate/file-cache.js +30 -0
- package/dist/evaluate/index.d.ts +2 -1
- package/dist/evaluate/index.js +2 -1
- package/dist/evaluate/review.js +2 -2
- package/dist/evaluate/run.js +2 -2
- package/dist/evaluate/version.js +3 -3
- package/dist/graph/functional.d.ts +9 -0
- package/dist/graph/functional.js +20 -3
- package/dist/graph/graph.js +27 -6
- package/dist/graph/lint.d.ts +4 -1
- package/dist/graph/lint.js +35 -0
- package/dist/graph/node-cache.js +3 -3
- package/dist/hallucination/rag.d.ts +4 -1
- package/dist/hallucination/rag.js +6 -1
- package/dist/images/asset-stores.js +2 -3
- package/dist/images/asset-support.js +2 -2
- package/dist/images/assets.js +1 -2
- package/dist/images/evals.js +4 -4
- package/dist/insights/proposals.js +1 -2
- package/dist/insights/regressions.d.ts +26 -2
- package/dist/insights/regressions.js +129 -3
- package/dist/loaders/git.js +8 -1
- package/dist/loaders/web.js +7 -1
- package/dist/mcp/client.d.ts +14 -0
- package/dist/mcp/client.js +59 -1
- package/dist/mcp/registry.d.ts +6 -0
- package/dist/mcp/registry.js +7 -0
- package/dist/postgres/fulltext.d.ts +67 -0
- package/dist/postgres/fulltext.js +91 -0
- package/dist/prompts/file.js +28 -11
- package/dist/protocols/a2a.d.ts +235 -0
- package/dist/protocols/a2a.js +386 -0
- package/dist/protocols/acp.d.ts +87 -0
- package/dist/protocols/acp.js +322 -0
- package/dist/protocols/ag-ui.d.ts +120 -0
- package/dist/protocols/ag-ui.js +180 -0
- package/dist/protocols/shared.d.ts +80 -0
- package/dist/protocols/shared.js +49 -0
- package/dist/providers/anthropic.js +1 -0
- package/dist/providers/cohere.js +2 -2
- package/dist/providers/google.js +2 -11
- package/dist/providers/openai.js +1 -0
- package/dist/rag/elasticsearch.d.ts +71 -0
- package/dist/rag/elasticsearch.js +130 -0
- package/dist/rag/file-ingestion.js +15 -9
- package/dist/rag/pipeline.d.ts +140 -0
- package/dist/rag/pipeline.js +187 -0
- package/dist/rag/rerankers.d.ts +92 -0
- package/dist/rag/rerankers.js +97 -0
- package/dist/rag/retrievers.d.ts +12 -1
- package/dist/runtime/detect.d.ts +17 -0
- package/dist/runtime/detect.js +16 -0
- package/dist/runtime/index.d.ts +19 -0
- package/dist/runtime/index.js +7 -0
- package/dist/security/injection-detector.d.ts +2 -0
- package/dist/security/injection-detector.js +7 -1
- package/dist/security/input-guard.js +5 -2
- package/dist/security/output-guard.js +8 -4
- package/dist/security/pii-detector.js +22 -6
- package/dist/security/prompt-hardening.js +6 -2
- package/dist/security/semantic-injection-classifier.d.ts +2 -1
- package/dist/security/semantic-injection-classifier.js +31 -35
- package/dist/security/upload-scanner.d.ts +8 -0
- package/dist/security/upload-scanner.js +51 -9
- package/dist/server/assistant.d.ts +5 -1
- package/dist/server/assistant.js +4 -2
- package/dist/server/auth.d.ts +250 -0
- package/dist/server/auth.js +392 -0
- package/dist/server/deployments.d.ts +63 -1
- package/dist/server/deployments.js +75 -2
- package/dist/server/runs.d.ts +9 -0
- package/dist/server/runs.js +20 -0
- package/dist/server/server.js +5 -0
- package/dist/store/memory.d.ts +10 -0
- package/dist/store/memory.js +10 -0
- package/dist/testing/record.js +2 -2
- package/dist/tracing/stores.js +2 -1
- package/dist/tracing/tracer.js +2 -2
- package/dist/types/config.d.ts +20 -0
- package/dist/types/graph.d.ts +38 -0
- package/dist/types/messages.d.ts +36 -2
- package/dist/types/principal.d.ts +21 -0
- package/dist/types/principal.js +1 -0
- package/dist/types/server.d.ts +8 -9
- package/dist/utils/base64.d.ts +4 -0
- package/dist/utils/base64.js +21 -0
- package/dist/utils/checksums.d.ts +22 -0
- package/dist/utils/checksums.js +39 -0
- package/dist/utils/ids.d.ts +8 -0
- package/dist/utils/ids.js +26 -2
- package/dist/utils/sha256.d.ts +22 -0
- package/dist/utils/sha256.js +120 -0
- package/dist/utils/tenant.d.ts +11 -0
- package/dist/utils/tenant.js +14 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
- package/dist-cjs/adapter-kit/contracts.js +243 -0
- package/dist-cjs/adapter-kit/errors.d.ts +39 -0
- package/dist-cjs/adapter-kit/errors.js +57 -0
- package/dist-cjs/adapter-kit/index.d.ts +144 -0
- package/dist-cjs/adapter-kit/index.js +194 -0
- package/dist-cjs/adapter-kit/semver.d.ts +12 -0
- package/dist-cjs/adapter-kit/semver.js +59 -0
- package/dist-cjs/agent/capabilities.d.ts +39 -0
- package/dist-cjs/agent/capabilities.js +41 -0
- package/dist-cjs/agent/create-agent.d.ts +61 -11
- package/dist-cjs/agent/create-agent.js +136 -22
- package/dist-cjs/agent/index.d.ts +3 -2
- package/dist-cjs/agent/index.js +4 -4
- package/dist-cjs/agent/middleware/approval.d.ts +26 -0
- package/dist-cjs/agent/middleware/approval.js +26 -0
- package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
- package/dist-cjs/agent/middleware/context-editor.js +70 -0
- package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
- package/dist-cjs/agent/middleware/filesystem.js +86 -0
- package/dist-cjs/agent/middleware/index.d.ts +19 -0
- package/dist-cjs/agent/middleware/index.js +28 -0
- package/dist-cjs/agent/middleware/limits.d.ts +42 -0
- package/dist-cjs/agent/middleware/limits.js +57 -0
- package/dist-cjs/agent/middleware/models.d.ts +48 -0
- package/dist-cjs/agent/middleware/models.js +48 -0
- package/dist-cjs/agent/middleware/pii.d.ts +67 -0
- package/dist-cjs/agent/middleware/pii.js +147 -0
- package/dist-cjs/agent/middleware/retry.d.ts +66 -0
- package/dist-cjs/agent/middleware/retry.js +63 -0
- package/dist-cjs/agent/middleware/shared.d.ts +41 -0
- package/dist-cjs/agent/middleware/shared.js +83 -0
- package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
- package/dist-cjs/agent/middleware/tool-selector.js +109 -0
- package/dist-cjs/agent/permissions.d.ts +113 -0
- package/dist-cjs/agent/permissions.js +189 -0
- package/dist-cjs/agent/sandbox.d.ts +151 -0
- package/dist-cjs/agent/sandbox.js +297 -0
- package/dist-cjs/agent/tool.d.ts +8 -3
- package/dist-cjs/agent/tool.js +14 -3
- package/dist-cjs/core/secure-stream.js +4 -3
- package/dist-cjs/deep-agent/index.d.ts +109 -0
- package/dist-cjs/deep-agent/index.js +219 -0
- package/dist-cjs/embeddings/adapters.js +2 -1
- package/dist-cjs/evals/judge.js +2 -5
- package/dist-cjs/evaluate/cache.d.ts +0 -14
- package/dist-cjs/evaluate/cache.js +1 -65
- package/dist-cjs/evaluate/file-cache.d.ts +15 -0
- package/dist-cjs/evaluate/file-cache.js +67 -0
- package/dist-cjs/evaluate/index.d.ts +2 -1
- package/dist-cjs/evaluate/index.js +3 -2
- package/dist-cjs/evaluate/review.js +2 -2
- package/dist-cjs/evaluate/run.js +2 -2
- package/dist-cjs/evaluate/version.js +3 -3
- package/dist-cjs/graph/functional.d.ts +9 -0
- package/dist-cjs/graph/functional.js +20 -3
- package/dist-cjs/graph/graph.js +27 -6
- package/dist-cjs/graph/lint.d.ts +4 -1
- package/dist-cjs/graph/lint.js +35 -0
- package/dist-cjs/graph/node-cache.js +3 -3
- package/dist-cjs/hallucination/rag.d.ts +4 -1
- package/dist-cjs/hallucination/rag.js +6 -1
- package/dist-cjs/images/asset-stores.js +2 -3
- package/dist-cjs/images/asset-support.js +2 -2
- package/dist-cjs/images/assets.js +1 -2
- package/dist-cjs/images/evals.js +4 -4
- package/dist-cjs/insights/proposals.js +1 -2
- package/dist-cjs/insights/regressions.d.ts +26 -2
- package/dist-cjs/insights/regressions.js +129 -3
- package/dist-cjs/loaders/git.js +8 -1
- package/dist-cjs/loaders/web.js +7 -1
- package/dist-cjs/mcp/client.d.ts +14 -0
- package/dist-cjs/mcp/client.js +60 -1
- package/dist-cjs/mcp/registry.d.ts +6 -0
- package/dist-cjs/mcp/registry.js +7 -0
- package/dist-cjs/postgres/fulltext.d.ts +67 -0
- package/dist-cjs/postgres/fulltext.js +129 -0
- package/dist-cjs/prompts/file.js +28 -11
- package/dist-cjs/protocols/a2a.d.ts +235 -0
- package/dist-cjs/protocols/a2a.js +393 -0
- package/dist-cjs/protocols/acp.d.ts +87 -0
- package/dist-cjs/protocols/acp.js +326 -0
- package/dist-cjs/protocols/ag-ui.d.ts +120 -0
- package/dist-cjs/protocols/ag-ui.js +186 -0
- package/dist-cjs/protocols/shared.d.ts +80 -0
- package/dist-cjs/protocols/shared.js +59 -0
- package/dist-cjs/providers/anthropic.js +1 -0
- package/dist-cjs/providers/cohere.js +2 -2
- package/dist-cjs/providers/google.js +3 -12
- package/dist-cjs/providers/openai.js +1 -0
- package/dist-cjs/rag/elasticsearch.d.ts +71 -0
- package/dist-cjs/rag/elasticsearch.js +135 -0
- package/dist-cjs/rag/file-ingestion.js +14 -8
- package/dist-cjs/rag/pipeline.d.ts +140 -0
- package/dist-cjs/rag/pipeline.js +191 -0
- package/dist-cjs/rag/rerankers.d.ts +92 -0
- package/dist-cjs/rag/rerankers.js +107 -0
- package/dist-cjs/rag/retrievers.d.ts +12 -1
- package/dist-cjs/runtime/detect.d.ts +17 -0
- package/dist-cjs/runtime/detect.js +19 -0
- package/dist-cjs/runtime/index.d.ts +19 -0
- package/dist-cjs/runtime/index.js +31 -0
- package/dist-cjs/security/injection-detector.d.ts +2 -0
- package/dist-cjs/security/injection-detector.js +7 -1
- package/dist-cjs/security/input-guard.js +5 -2
- package/dist-cjs/security/output-guard.js +8 -4
- package/dist-cjs/security/pii-detector.js +22 -6
- package/dist-cjs/security/prompt-hardening.js +6 -2
- package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
- package/dist-cjs/security/semantic-injection-classifier.js +31 -35
- package/dist-cjs/security/upload-scanner.d.ts +8 -0
- package/dist-cjs/security/upload-scanner.js +53 -9
- package/dist-cjs/server/assistant.d.ts +5 -1
- package/dist-cjs/server/assistant.js +4 -2
- package/dist-cjs/server/auth.d.ts +250 -0
- package/dist-cjs/server/auth.js +402 -0
- package/dist-cjs/server/deployments.d.ts +63 -1
- package/dist-cjs/server/deployments.js +75 -2
- package/dist-cjs/server/runs.d.ts +9 -0
- package/dist-cjs/server/runs.js +20 -0
- package/dist-cjs/server/server.js +5 -0
- package/dist-cjs/store/memory.d.ts +10 -0
- package/dist-cjs/store/memory.js +10 -0
- package/dist-cjs/testing/record.js +2 -2
- package/dist-cjs/tracing/stores.js +2 -1
- package/dist-cjs/tracing/tracer.js +2 -2
- package/dist-cjs/types/config.d.ts +20 -0
- package/dist-cjs/types/graph.d.ts +38 -0
- package/dist-cjs/types/messages.d.ts +36 -2
- package/dist-cjs/types/principal.d.ts +21 -0
- package/dist-cjs/types/principal.js +2 -0
- package/dist-cjs/types/server.d.ts +8 -9
- package/dist-cjs/utils/base64.d.ts +4 -0
- package/dist-cjs/utils/base64.js +25 -0
- package/dist-cjs/utils/checksums.d.ts +22 -0
- package/dist-cjs/utils/checksums.js +46 -0
- package/dist-cjs/utils/ids.d.ts +8 -0
- package/dist-cjs/utils/ids.js +28 -2
- package/dist-cjs/utils/sha256.d.ts +22 -0
- package/dist-cjs/utils/sha256.js +125 -0
- package/dist-cjs/utils/tenant.d.ts +11 -0
- package/dist-cjs/utils/tenant.js +15 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +7 -3
- package/package.json +193 -6
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createIngestionPipeline = createIngestionPipeline;
|
|
4
|
+
exports.ingestionExecutor = ingestionExecutor;
|
|
5
|
+
const ingestion_js_1 = require("./ingestion.js");
|
|
6
|
+
function createIngestionPipeline(options) {
|
|
7
|
+
const batchSize = options.batchSize ?? 64;
|
|
8
|
+
if (!(Number.isInteger(batchSize) && batchSize > 0))
|
|
9
|
+
throw new RangeError('batchSize must be a positive integer');
|
|
10
|
+
const namespace = ['nexus', 'ingestion', options.name, 'documents'];
|
|
11
|
+
const keywords = options.keywords ?? [];
|
|
12
|
+
const chunkingVersion = stableJson(options.chunking ?? {});
|
|
13
|
+
const read = async (id) => (await options.manifest.get(namespace, id))?.value;
|
|
14
|
+
const removeChunks = async (ids) => {
|
|
15
|
+
if (ids.length === 0)
|
|
16
|
+
return;
|
|
17
|
+
await Promise.all([options.vectors.delete(ids), ...keywords.map((index) => index.delete(ids))]);
|
|
18
|
+
};
|
|
19
|
+
const remove = async (ids) => {
|
|
20
|
+
let chunks = 0;
|
|
21
|
+
for (const id of ids) {
|
|
22
|
+
const recorded = await read(id);
|
|
23
|
+
if (!recorded)
|
|
24
|
+
continue;
|
|
25
|
+
const chunkIds = Object.keys(recorded.chunks);
|
|
26
|
+
await removeChunks(chunkIds);
|
|
27
|
+
await options.manifest.delete(namespace, id);
|
|
28
|
+
chunks += chunkIds.length;
|
|
29
|
+
}
|
|
30
|
+
return chunks;
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
document: read,
|
|
34
|
+
async remove(ids) {
|
|
35
|
+
await remove(ids);
|
|
36
|
+
return ids.length;
|
|
37
|
+
},
|
|
38
|
+
async run(source, runOptions = {}) {
|
|
39
|
+
const report = {
|
|
40
|
+
documents: 0,
|
|
41
|
+
unchanged: 0,
|
|
42
|
+
added: 0,
|
|
43
|
+
updated: 0,
|
|
44
|
+
deleted: 0,
|
|
45
|
+
chunksEmbedded: 0,
|
|
46
|
+
chunksKept: 0,
|
|
47
|
+
chunksDeleted: 0,
|
|
48
|
+
resumed: 0,
|
|
49
|
+
};
|
|
50
|
+
const previous = runOptions.operation?.previousHeartbeat;
|
|
51
|
+
const skip = previous?.pipeline === options.name ? previous.documents : 0;
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
let batch = [];
|
|
54
|
+
const flush = async () => {
|
|
55
|
+
if (batch.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
const current = batch;
|
|
58
|
+
batch = [];
|
|
59
|
+
const versions = await Promise.all(current.map(({ document }) => hashText(stableJson([document.text, document.source ?? null, document.metadata ?? null]))));
|
|
60
|
+
const recorded = await Promise.all(current.map(({ id }) => read(id)));
|
|
61
|
+
const writes = [];
|
|
62
|
+
const stale = [];
|
|
63
|
+
const toEmbed = [];
|
|
64
|
+
for (const [index, { document, id }] of current.entries()) {
|
|
65
|
+
const version = versions[index];
|
|
66
|
+
const before = recorded[index];
|
|
67
|
+
if (before &&
|
|
68
|
+
before.version === version &&
|
|
69
|
+
before.chunking === chunkingVersion &&
|
|
70
|
+
before.model === options.embeddingModel) {
|
|
71
|
+
report.unchanged += 1;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (before)
|
|
75
|
+
report.updated += 1;
|
|
76
|
+
else
|
|
77
|
+
report.added += 1;
|
|
78
|
+
const split = (0, ingestion_js_1.ingestDocuments)([{ ...document, id }], options.chunking).chunks;
|
|
79
|
+
const hashes = await Promise.all(split.map((chunk) => hashText(stableJson([chunk.content, document.metadata ?? null]))));
|
|
80
|
+
const used = new Map();
|
|
81
|
+
const chunks = split.map((chunk, position) => {
|
|
82
|
+
const base = `${id}#${hashes[position].slice(0, 12)}`;
|
|
83
|
+
const count = (used.get(base) ?? 0) + 1;
|
|
84
|
+
used.set(base, count);
|
|
85
|
+
return { ...chunk, id: count === 1 ? base : `${base}-${count}` };
|
|
86
|
+
});
|
|
87
|
+
const reusable = before && before.model === options.embeddingModel ? before.chunks : {};
|
|
88
|
+
for (const [position, chunk] of chunks.entries()) {
|
|
89
|
+
if (reusable[chunk.id] === hashes[position])
|
|
90
|
+
report.chunksKept += 1;
|
|
91
|
+
else
|
|
92
|
+
toEmbed.push(chunk);
|
|
93
|
+
}
|
|
94
|
+
const kept = new Set(chunks.map((chunk) => chunk.id));
|
|
95
|
+
if (before)
|
|
96
|
+
stale.push(...Object.keys(before.chunks).filter((chunkId) => !kept.has(chunkId) || !reusable[chunkId]));
|
|
97
|
+
writes.push({ id, version, chunks, hashes });
|
|
98
|
+
}
|
|
99
|
+
if (toEmbed.length > 0) {
|
|
100
|
+
const vectors = await options.embed(toEmbed.map((chunk) => chunk.content));
|
|
101
|
+
if (vectors.length !== toEmbed.length) {
|
|
102
|
+
throw new RangeError(`The embedding function returned ${vectors.length} vectors for ${toEmbed.length} texts`);
|
|
103
|
+
}
|
|
104
|
+
await Promise.all([
|
|
105
|
+
options.vectors.add(toEmbed.map((chunk, index) => ({ ...chunk, embedding: vectors[index] }))),
|
|
106
|
+
...keywords.map((index) => index.add(toEmbed)),
|
|
107
|
+
]);
|
|
108
|
+
report.chunksEmbedded += toEmbed.length;
|
|
109
|
+
}
|
|
110
|
+
const embeddedIds = new Set(toEmbed.map((chunk) => chunk.id));
|
|
111
|
+
const removable = stale.filter((chunkId) => !embeddedIds.has(chunkId));
|
|
112
|
+
await removeChunks(removable);
|
|
113
|
+
report.chunksDeleted += removable.length;
|
|
114
|
+
const updatedAt = new Date().toISOString();
|
|
115
|
+
await Promise.all(writes.map(({ id, version, chunks, hashes }) => options.manifest.put(namespace, id, {
|
|
116
|
+
id,
|
|
117
|
+
version,
|
|
118
|
+
chunking: chunkingVersion,
|
|
119
|
+
model: options.embeddingModel,
|
|
120
|
+
chunks: Object.fromEntries(chunks.map((chunk, index) => [chunk.id, hashes[index]])),
|
|
121
|
+
updatedAt,
|
|
122
|
+
})));
|
|
123
|
+
await runOptions.operation?.heartbeat({
|
|
124
|
+
pipeline: options.name,
|
|
125
|
+
documents: report.documents,
|
|
126
|
+
});
|
|
127
|
+
runOptions.onProgress?.({ ...report });
|
|
128
|
+
};
|
|
129
|
+
const sources = isSourceList(source) ? source : [source];
|
|
130
|
+
for (const loader of sources) {
|
|
131
|
+
for await (const document of loader) {
|
|
132
|
+
runOptions.signal?.throwIfAborted();
|
|
133
|
+
const id = document.id ?? document.source;
|
|
134
|
+
if (!id)
|
|
135
|
+
throw new TypeError('Durable ingestion needs a document id, or a source to use as one');
|
|
136
|
+
seen.add(id);
|
|
137
|
+
report.documents += 1;
|
|
138
|
+
if (report.documents <= skip) {
|
|
139
|
+
report.resumed += 1;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
batch.push({ document, id });
|
|
143
|
+
if (batch.length >= batchSize)
|
|
144
|
+
await flush();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
runOptions.signal?.throwIfAborted();
|
|
148
|
+
await flush();
|
|
149
|
+
if (runOptions.deleteMissing !== false) {
|
|
150
|
+
const missing = [];
|
|
151
|
+
for (let offset = 0;; offset += 500) {
|
|
152
|
+
const page = await options.manifest.search(namespace, { limit: 500, offset });
|
|
153
|
+
for (const item of page)
|
|
154
|
+
if (!seen.has(item.key))
|
|
155
|
+
missing.push(item.key);
|
|
156
|
+
if (page.length < 500)
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
for (const id of missing) {
|
|
160
|
+
runOptions.signal?.throwIfAborted();
|
|
161
|
+
report.chunksDeleted += await remove([id]);
|
|
162
|
+
report.deleted += 1;
|
|
163
|
+
}
|
|
164
|
+
if (missing.length > 0)
|
|
165
|
+
runOptions.onProgress?.({ ...report });
|
|
166
|
+
}
|
|
167
|
+
return report;
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function ingestionExecutor(pipeline, source, options = {}) {
|
|
172
|
+
return (context) => pipeline.run(source(), { ...options, signal: context.signal, operation: context });
|
|
173
|
+
}
|
|
174
|
+
async function hashText(text) {
|
|
175
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));
|
|
176
|
+
let hex = '';
|
|
177
|
+
for (const byte of new Uint8Array(digest).subarray(0, 16))
|
|
178
|
+
hex += byte.toString(16).padStart(2, '0');
|
|
179
|
+
return hex;
|
|
180
|
+
}
|
|
181
|
+
function stableJson(value) {
|
|
182
|
+
return JSON.stringify(value, (_key, entry) => entry && typeof entry === 'object' && !Array.isArray(entry)
|
|
183
|
+
? Object.fromEntries(Object.entries(entry).sort(([a], [b]) => a.localeCompare(b)))
|
|
184
|
+
: entry);
|
|
185
|
+
}
|
|
186
|
+
function isSourceList(value) {
|
|
187
|
+
if (!Array.isArray(value))
|
|
188
|
+
return false;
|
|
189
|
+
const first = value[0];
|
|
190
|
+
return first !== undefined && typeof first === 'object' && first !== null && !('text' in first);
|
|
191
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Reranker } from './retrievers.js';
|
|
2
|
+
/** Raised when a rerank API answers with an error. */
|
|
3
|
+
export declare class RerankerError extends Error {
|
|
4
|
+
/** The HTTP status the API answered with. */
|
|
5
|
+
readonly status: number;
|
|
6
|
+
/** The API's response body, as text. */
|
|
7
|
+
readonly body?: string | undefined;
|
|
8
|
+
constructor(message: string,
|
|
9
|
+
/** The HTTP status the API answered with. */
|
|
10
|
+
status: number,
|
|
11
|
+
/** The API's response body, as text. */
|
|
12
|
+
body?: string | undefined);
|
|
13
|
+
}
|
|
14
|
+
/** What every HTTP reranker takes. */
|
|
15
|
+
export interface RerankerHttpOptions {
|
|
16
|
+
/** Replaces the global `fetch`, for a proxy, retries, or tests. */
|
|
17
|
+
fetch?: typeof globalThis.fetch;
|
|
18
|
+
/** Headers added to every request. */
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
/** Aborts a request that takes longer, in milliseconds. Defaults to 30 seconds. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** Characters of each chunk sent. Defaults to 4,000. */
|
|
23
|
+
maxChunkCharacters?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Options for a hosted rerank API. */
|
|
26
|
+
export interface HostedRerankerOptions extends RerankerHttpOptions {
|
|
27
|
+
/** The API key. */
|
|
28
|
+
apiKey: string;
|
|
29
|
+
/** The rerank model, such as `rerank-v3.5`. */
|
|
30
|
+
model: string;
|
|
31
|
+
/** Replaces the API's endpoint, for a regional or proxied one. */
|
|
32
|
+
url?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Options for `httpReranker()`: any rerank API, described by its request and its response. */
|
|
35
|
+
export interface HttpRerankerOptions extends RerankerHttpOptions {
|
|
36
|
+
/** The endpoint. */
|
|
37
|
+
url: string;
|
|
38
|
+
/** The request body for a query and its passages. */
|
|
39
|
+
body: (query: string, passages: string[]) => unknown;
|
|
40
|
+
/** Reads the response: each passage's index and score. Passages it leaves out score 0. */
|
|
41
|
+
scores: (response: unknown) => ReadonlyArray<{
|
|
42
|
+
index: number;
|
|
43
|
+
score: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A reranker over any HTTP rerank API: you describe the request body and how to read the scores,
|
|
48
|
+
* and it handles the rest — truncation, the timeout, cancellation, errors, and scores put back in
|
|
49
|
+
* the order of the chunks.
|
|
50
|
+
*/
|
|
51
|
+
export declare function httpReranker(options: HttpRerankerOptions): Reranker;
|
|
52
|
+
/** Cohere's rerank API (`/v2/rerank`), such as `rerank-v3.5`. */
|
|
53
|
+
export declare function cohereReranker(options: HostedRerankerOptions): Reranker;
|
|
54
|
+
/** Voyage AI's rerank API (`/v1/rerank`), such as `rerank-2`. */
|
|
55
|
+
export declare function voyageReranker(options: HostedRerankerOptions): Reranker;
|
|
56
|
+
/** Jina AI's rerank API (`/v1/rerank`), such as `jina-reranker-v2-base-multilingual`. */
|
|
57
|
+
export declare function jinaReranker(options: HostedRerankerOptions): Reranker;
|
|
58
|
+
/** Options for `teiReranker()`. */
|
|
59
|
+
export interface TeiRerankerOptions extends RerankerHttpOptions {
|
|
60
|
+
/** The server, such as `http://localhost:8080`. */
|
|
61
|
+
url: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A cross-encoder served by Hugging Face's text-embeddings-inference, on your own machine or
|
|
65
|
+
* cluster: `docker run ... --model-id BAAI/bge-reranker-base`, then `teiReranker({ url })`. The
|
|
66
|
+
* scores are the model's own, from 0 to 1.
|
|
67
|
+
*/
|
|
68
|
+
export declare function teiReranker(options: TeiRerankerOptions): Reranker;
|
|
69
|
+
/**
|
|
70
|
+
* A cross-encoder run in process: one score per (query, passage) pair, higher meaning more relevant.
|
|
71
|
+
* A model from an ONNX runtime or a transformers library fits it in a few lines, so the package
|
|
72
|
+
* depends on neither.
|
|
73
|
+
*/
|
|
74
|
+
export interface CrossEncoder {
|
|
75
|
+
/** Scores each pair, in order. */
|
|
76
|
+
score(pairs: ReadonlyArray<readonly [query: string, passage: string]>): Promise<readonly number[]> | readonly number[];
|
|
77
|
+
}
|
|
78
|
+
/** Options for `crossEncoderReranker()`. */
|
|
79
|
+
export interface CrossEncoderRerankerOptions {
|
|
80
|
+
/** Pairs scored in one call. Defaults to 32. */
|
|
81
|
+
batchSize?: number;
|
|
82
|
+
/** Characters of each chunk scored. Defaults to 2,000. */
|
|
83
|
+
maxChunkCharacters?: number;
|
|
84
|
+
/** Maps raw logits to 0–1 with a sigmoid, for a model that returns logits. Defaults to false. */
|
|
85
|
+
sigmoid?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A reranker over a cross-encoder you run in process: no network call, no per-query price, and the
|
|
89
|
+
* passages never leave the machine. Pairs are scored in batches, and the run's signal is checked
|
|
90
|
+
* between batches.
|
|
91
|
+
*/
|
|
92
|
+
export declare function crossEncoderReranker(model: CrossEncoder, options?: CrossEncoderRerankerOptions): Reranker;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RerankerError = void 0;
|
|
4
|
+
exports.httpReranker = httpReranker;
|
|
5
|
+
exports.cohereReranker = cohereReranker;
|
|
6
|
+
exports.voyageReranker = voyageReranker;
|
|
7
|
+
exports.jinaReranker = jinaReranker;
|
|
8
|
+
exports.teiReranker = teiReranker;
|
|
9
|
+
exports.crossEncoderReranker = crossEncoderReranker;
|
|
10
|
+
const signals_js_1 = require("../utils/signals.js");
|
|
11
|
+
class RerankerError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
body;
|
|
14
|
+
constructor(message, status, body) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.body = body;
|
|
18
|
+
this.name = 'RerankerError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.RerankerError = RerankerError;
|
|
22
|
+
function httpReranker(options) {
|
|
23
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
24
|
+
const limit = options.maxChunkCharacters ?? 4_000;
|
|
25
|
+
return async (query, chunks, signal) => {
|
|
26
|
+
if (chunks.length === 0)
|
|
27
|
+
return [];
|
|
28
|
+
const passages = chunks.map((chunk) => chunk.content.slice(0, limit));
|
|
29
|
+
const linked = (0, signals_js_1.linkSignals)([AbortSignal.timeout(options.timeoutMs ?? 30_000), signal]);
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetchImpl(options.url, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: { 'content-type': 'application/json', ...options.headers },
|
|
34
|
+
body: JSON.stringify(options.body(query, passages)),
|
|
35
|
+
signal: linked.signal,
|
|
36
|
+
});
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
if (!response.ok)
|
|
39
|
+
throw new RerankerError(`The rerank API answered ${response.status}`, response.status, text);
|
|
40
|
+
const scores = new Array(chunks.length).fill(0);
|
|
41
|
+
for (const { index, score } of options.scores(JSON.parse(text))) {
|
|
42
|
+
if (Number.isInteger(index) && index >= 0 && index < scores.length)
|
|
43
|
+
scores[index] = Number(score);
|
|
44
|
+
}
|
|
45
|
+
return scores;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
linked.dispose();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const bearer = (options) => ({ authorization: `Bearer ${options.apiKey}`, ...options.headers });
|
|
53
|
+
const readScored = (list) => (list ?? []).map((item) => ({ index: item.index, score: item.relevance_score }));
|
|
54
|
+
function cohereReranker(options) {
|
|
55
|
+
return httpReranker({
|
|
56
|
+
...options,
|
|
57
|
+
url: options.url ?? 'https://api.cohere.com/v2/rerank',
|
|
58
|
+
headers: bearer(options),
|
|
59
|
+
body: (query, documents) => ({ model: options.model, query, documents, top_n: documents.length }),
|
|
60
|
+
scores: (response) => readScored(response.results),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function voyageReranker(options) {
|
|
64
|
+
return httpReranker({
|
|
65
|
+
...options,
|
|
66
|
+
url: options.url ?? 'https://api.voyageai.com/v1/rerank',
|
|
67
|
+
headers: bearer(options),
|
|
68
|
+
body: (query, documents) => ({ model: options.model, query, documents, top_k: documents.length }),
|
|
69
|
+
scores: (response) => readScored(response.data),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function jinaReranker(options) {
|
|
73
|
+
return httpReranker({
|
|
74
|
+
...options,
|
|
75
|
+
url: options.url ?? 'https://api.jina.ai/v1/rerank',
|
|
76
|
+
headers: bearer(options),
|
|
77
|
+
body: (query, documents) => ({ model: options.model, query, documents, top_n: documents.length }),
|
|
78
|
+
scores: (response) => readScored(response.results),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function teiReranker(options) {
|
|
82
|
+
return httpReranker({
|
|
83
|
+
...options,
|
|
84
|
+
url: `${options.url.replace(/\/+$/, '')}/rerank`,
|
|
85
|
+
body: (query, texts) => ({ query, texts, raw_scores: false, truncate: true }),
|
|
86
|
+
scores: (response) => response,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function crossEncoderReranker(model, options = {}) {
|
|
90
|
+
const batchSize = Math.max(1, options.batchSize ?? 32);
|
|
91
|
+
const limit = options.maxChunkCharacters ?? 2_000;
|
|
92
|
+
return async (query, chunks, signal) => {
|
|
93
|
+
const pairs = chunks.map((chunk) => [query, chunk.content.slice(0, limit)]);
|
|
94
|
+
const scores = [];
|
|
95
|
+
for (let start = 0; start < pairs.length; start += batchSize) {
|
|
96
|
+
signal?.throwIfAborted();
|
|
97
|
+
const batch = pairs.slice(start, start + batchSize);
|
|
98
|
+
const result = await model.score(batch);
|
|
99
|
+
if (result.length !== batch.length) {
|
|
100
|
+
throw new RangeError(`The cross-encoder returned ${result.length} scores for ${batch.length} pairs`);
|
|
101
|
+
}
|
|
102
|
+
for (const raw of result)
|
|
103
|
+
scores.push(options.sigmoid ? 1 / (1 + Math.exp(-raw)) : raw);
|
|
104
|
+
}
|
|
105
|
+
return scores;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -19,6 +19,17 @@ export interface Retriever {
|
|
|
19
19
|
/** The chunks that best answer a query, best first. */
|
|
20
20
|
retrieve(query: string, options?: RetrieveOptions): Promise<VectorSearchResult[]>;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Keyword search that holds its own chunks: an in-memory BM25 `KeywordIndex`, Postgres full-text
|
|
24
|
+
* search, a search engine. It is a `Retriever`, so `hybridRetriever()` fuses it with vector search,
|
|
25
|
+
* and it takes writes, so ingestion keeps it in step with a vector store.
|
|
26
|
+
*/
|
|
27
|
+
export interface SparseRetriever extends Retriever {
|
|
28
|
+
/** Adds chunks, or replaces those whose id exists. */
|
|
29
|
+
add(chunks: readonly RagChunk[]): Promise<void> | void;
|
|
30
|
+
/** Removes chunks by id. An id that is not held is ignored. */
|
|
31
|
+
delete(ids: readonly string[]): Promise<void> | void;
|
|
32
|
+
}
|
|
22
33
|
/** A client that runs one completion — a `NexusAI` instance, or anything with the same `complete()`. */
|
|
23
34
|
export interface RetrievalModelClient {
|
|
24
35
|
/** Runs one completion and returns its text. */
|
|
@@ -47,7 +58,7 @@ export declare function tokenize(text: string): string[];
|
|
|
47
58
|
* codes, error numbers, names — that keyword search finds; `hybridRetriever()` fuses the two.
|
|
48
59
|
* Adding a chunk whose id exists replaces it, as in a vector store.
|
|
49
60
|
*/
|
|
50
|
-
export declare class KeywordIndex implements
|
|
61
|
+
export declare class KeywordIndex implements SparseRetriever {
|
|
51
62
|
private readonly chunks;
|
|
52
63
|
private readonly postings;
|
|
53
64
|
private totalLength;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** A JavaScript runtime this package recognizes. */
|
|
2
|
+
export type RuntimeName = 'node' | 'deno' | 'bun' | 'workerd' | 'edge-light' | 'browser' | 'unknown';
|
|
3
|
+
/** Which runtime the code is running on. */
|
|
4
|
+
export interface RuntimeInfo {
|
|
5
|
+
/**
|
|
6
|
+
* `node`, `deno`, `bun`, `workerd` for Cloudflare Workers, `edge-light` for Vercel's edge
|
|
7
|
+
* runtime, `browser` for a page or a web worker, or `unknown`.
|
|
8
|
+
*/
|
|
9
|
+
name: RuntimeName;
|
|
10
|
+
/** The runtime's version, where it reports one. */
|
|
11
|
+
version?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Which runtime the code is running on, from what each one reliably exposes. Deno and Bun are
|
|
15
|
+
* checked before Node.js, since both also present Node's `process`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runtimeInfo(): RuntimeInfo;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runtimeInfo = runtimeInfo;
|
|
4
|
+
function runtimeInfo() {
|
|
5
|
+
const host = globalThis;
|
|
6
|
+
if (host.Deno?.version?.deno)
|
|
7
|
+
return { name: 'deno', version: host.Deno.version.deno };
|
|
8
|
+
if (host.Bun?.version)
|
|
9
|
+
return { name: 'bun', version: host.Bun.version };
|
|
10
|
+
if (typeof host.EdgeRuntime === 'string')
|
|
11
|
+
return { name: 'edge-light' };
|
|
12
|
+
if (host.navigator?.userAgent === 'Cloudflare-Workers')
|
|
13
|
+
return { name: 'workerd' };
|
|
14
|
+
if (host.process?.versions?.node)
|
|
15
|
+
return { name: 'node', version: host.process.versions.node };
|
|
16
|
+
if (host.document !== undefined || typeof host.importScripts === 'function')
|
|
17
|
+
return { name: 'browser' };
|
|
18
|
+
return { name: 'unknown' };
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The portable kernel: the contracts every model call and tool call is built on, for any JavaScript
|
|
3
|
+
* runtime. That means Node.js, Deno, Bun, edge runtimes such as Cloudflare Workers and Vercel's,
|
|
4
|
+
* and browsers.
|
|
5
|
+
*
|
|
6
|
+
* Nothing here, nor anything it imports, uses `fs`, `net`, `child_process`, Node's `crypto`, or
|
|
7
|
+
* Node's globals, and CI runs its tests on each runtime. The provider adapters, graphs, agents, and
|
|
8
|
+
* protocols are portable too, on their own entry points.
|
|
9
|
+
*/
|
|
10
|
+
export type { AssetContent, AudioContent, BinaryBuffer, CacheHint, CompletionRequest, ContentPart, ImageContent, Message, MessageRole, PromptCacheConfig, ReasoningConfig, TextContent, ToolCallResult, ToolChoice, ToolContext, ToolDefinition, ToolOutput, VideoContent, } from '../types/messages.js';
|
|
11
|
+
export type { CacheOutcome, NexusResponse, NexusStream, ResponseCost, ResponseMeta, StreamChunk, TokenUsage, ToolCall, } from '../types/response.js';
|
|
12
|
+
export type { ProviderCallContext } from '../types/lifecycle.js';
|
|
13
|
+
export { BaseProvider, type ProviderInfo } from '../providers/base.js';
|
|
14
|
+
export { categorizeProviderError, createProviderHttpError, isAbortError, isRetryableProviderError, NexusProviderError, type NexusProviderErrorCategory, type NexusProviderErrorOptions, toNexusProviderError, } from '../providers/errors.js';
|
|
15
|
+
export { isToolOutput, ToolExecutor, tool, toolMessageContent, toolOutput } from '../agent/tool.js';
|
|
16
|
+
export { capabilitiesOf, isSensitiveCapability, type ParsedCapability, parseCapability, } from '../agent/capabilities.js';
|
|
17
|
+
export { type NegotiateOptions, type NegotiationResult, NexusCapabilityError, negotiateCompletionRequest, } from '../capabilities/negotiate.js';
|
|
18
|
+
export { collectStream, createTextStream, mapStream } from '../core/streaming.js';
|
|
19
|
+
export { type RuntimeInfo, type RuntimeName, runtimeInfo } from './detect.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runtimeInfo = exports.mapStream = exports.createTextStream = exports.collectStream = exports.negotiateCompletionRequest = exports.NexusCapabilityError = exports.parseCapability = exports.isSensitiveCapability = exports.capabilitiesOf = exports.toolOutput = exports.toolMessageContent = exports.tool = exports.ToolExecutor = exports.isToolOutput = exports.toNexusProviderError = exports.NexusProviderError = exports.isRetryableProviderError = exports.isAbortError = exports.createProviderHttpError = exports.categorizeProviderError = exports.BaseProvider = void 0;
|
|
4
|
+
var base_js_1 = require("../providers/base.js");
|
|
5
|
+
Object.defineProperty(exports, "BaseProvider", { enumerable: true, get: function () { return base_js_1.BaseProvider; } });
|
|
6
|
+
var errors_js_1 = require("../providers/errors.js");
|
|
7
|
+
Object.defineProperty(exports, "categorizeProviderError", { enumerable: true, get: function () { return errors_js_1.categorizeProviderError; } });
|
|
8
|
+
Object.defineProperty(exports, "createProviderHttpError", { enumerable: true, get: function () { return errors_js_1.createProviderHttpError; } });
|
|
9
|
+
Object.defineProperty(exports, "isAbortError", { enumerable: true, get: function () { return errors_js_1.isAbortError; } });
|
|
10
|
+
Object.defineProperty(exports, "isRetryableProviderError", { enumerable: true, get: function () { return errors_js_1.isRetryableProviderError; } });
|
|
11
|
+
Object.defineProperty(exports, "NexusProviderError", { enumerable: true, get: function () { return errors_js_1.NexusProviderError; } });
|
|
12
|
+
Object.defineProperty(exports, "toNexusProviderError", { enumerable: true, get: function () { return errors_js_1.toNexusProviderError; } });
|
|
13
|
+
var tool_js_1 = require("../agent/tool.js");
|
|
14
|
+
Object.defineProperty(exports, "isToolOutput", { enumerable: true, get: function () { return tool_js_1.isToolOutput; } });
|
|
15
|
+
Object.defineProperty(exports, "ToolExecutor", { enumerable: true, get: function () { return tool_js_1.ToolExecutor; } });
|
|
16
|
+
Object.defineProperty(exports, "tool", { enumerable: true, get: function () { return tool_js_1.tool; } });
|
|
17
|
+
Object.defineProperty(exports, "toolMessageContent", { enumerable: true, get: function () { return tool_js_1.toolMessageContent; } });
|
|
18
|
+
Object.defineProperty(exports, "toolOutput", { enumerable: true, get: function () { return tool_js_1.toolOutput; } });
|
|
19
|
+
var capabilities_js_1 = require("../agent/capabilities.js");
|
|
20
|
+
Object.defineProperty(exports, "capabilitiesOf", { enumerable: true, get: function () { return capabilities_js_1.capabilitiesOf; } });
|
|
21
|
+
Object.defineProperty(exports, "isSensitiveCapability", { enumerable: true, get: function () { return capabilities_js_1.isSensitiveCapability; } });
|
|
22
|
+
Object.defineProperty(exports, "parseCapability", { enumerable: true, get: function () { return capabilities_js_1.parseCapability; } });
|
|
23
|
+
var negotiate_js_1 = require("../capabilities/negotiate.js");
|
|
24
|
+
Object.defineProperty(exports, "NexusCapabilityError", { enumerable: true, get: function () { return negotiate_js_1.NexusCapabilityError; } });
|
|
25
|
+
Object.defineProperty(exports, "negotiateCompletionRequest", { enumerable: true, get: function () { return negotiate_js_1.negotiateCompletionRequest; } });
|
|
26
|
+
var streaming_js_1 = require("../core/streaming.js");
|
|
27
|
+
Object.defineProperty(exports, "collectStream", { enumerable: true, get: function () { return streaming_js_1.collectStream; } });
|
|
28
|
+
Object.defineProperty(exports, "createTextStream", { enumerable: true, get: function () { return streaming_js_1.createTextStream; } });
|
|
29
|
+
Object.defineProperty(exports, "mapStream", { enumerable: true, get: function () { return streaming_js_1.mapStream; } });
|
|
30
|
+
var detect_js_1 = require("./detect.js");
|
|
31
|
+
Object.defineProperty(exports, "runtimeInfo", { enumerable: true, get: function () { return detect_js_1.runtimeInfo; } });
|
|
@@ -7,5 +7,7 @@ export declare class InjectionDetector {
|
|
|
7
7
|
/** Returns the request with injection markers defused. */
|
|
8
8
|
neutralize(request: CompletionRequest): CompletionRequest;
|
|
9
9
|
private extractText;
|
|
10
|
+
/** Defuses what `detect()` finds. Text with nothing to defuse is returned exactly as it was. */
|
|
10
11
|
private escapeInstructionLikeText;
|
|
12
|
+
private escapeNormalized;
|
|
11
13
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.InjectionDetector = void 0;
|
|
4
|
+
const checksums_js_1 = require("../utils/checksums.js");
|
|
4
5
|
const DEFAULT_PATTERNS = [
|
|
5
6
|
{
|
|
6
7
|
pattern: /ignore\s+(all\s+)?previous\s+instructions/i,
|
|
@@ -41,7 +42,7 @@ class InjectionDetector {
|
|
|
41
42
|
];
|
|
42
43
|
const findings = [];
|
|
43
44
|
request.messages.forEach((message, index) => {
|
|
44
|
-
for (const text of this.extractText(message)) {
|
|
45
|
+
for (const text of this.extractText(message).map(checksums_js_1.normalizeForDetection)) {
|
|
45
46
|
patterns.forEach(({ pattern, severity, label }) => {
|
|
46
47
|
const match = text.match(pattern);
|
|
47
48
|
if (!match)
|
|
@@ -75,6 +76,11 @@ class InjectionDetector {
|
|
|
75
76
|
return message.content.filter((part) => part.type === 'text').map((part) => part.text);
|
|
76
77
|
}
|
|
77
78
|
escapeInstructionLikeText(text) {
|
|
79
|
+
const normalized = (0, checksums_js_1.normalizeForDetection)(text);
|
|
80
|
+
const escaped = this.escapeNormalized(normalized);
|
|
81
|
+
return escaped === normalized ? text : escaped;
|
|
82
|
+
}
|
|
83
|
+
escapeNormalized(text) {
|
|
78
84
|
return text
|
|
79
85
|
.replace(/ignore\s+(all\s+)?previous\s+instructions/gi, '[neutralized instruction override]')
|
|
80
86
|
.replace(/disregard\s+(all\s+)?(prior|previous)\s+instructions/gi, '[neutralized instruction override]')
|
|
@@ -21,7 +21,7 @@ const SECRET_PATTERNS = [
|
|
|
21
21
|
];
|
|
22
22
|
const SUSPICIOUS_URL_PATTERNS = [
|
|
23
23
|
{
|
|
24
|
-
pattern: /https?:\/\/(?:localhost|127
|
|
24
|
+
pattern: /https?:\/\/(?:localhost|127(?:\.\d{1,3}){3}|0\.0\.0\.0|\[::1?\]|169\.254\.169\.254|metadata\.google\.internal|100\.100\.100\.200)(?:[:/]|$)/gi,
|
|
25
25
|
label: 'local or metadata URL',
|
|
26
26
|
},
|
|
27
27
|
{ pattern: /https?:\/\/[^\s]*\.(?:onion)(?:[:/]|$)/gi, label: 'onion URL' },
|
|
@@ -100,7 +100,7 @@ class InputGuard {
|
|
|
100
100
|
let output = text;
|
|
101
101
|
patterns.forEach(({ pattern, label, severity }) => {
|
|
102
102
|
output = output.replace(pattern, (value) => {
|
|
103
|
-
findings.push({ type, severity, message: `Detected ${label}`, path, value });
|
|
103
|
+
findings.push({ type, severity, message: `Detected ${label}`, path, value: previewSecret(value) });
|
|
104
104
|
return action === 'mask' ? '[REDACTED]' : value;
|
|
105
105
|
});
|
|
106
106
|
});
|
|
@@ -130,3 +130,6 @@ class InputGuard {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
exports.InputGuard = InputGuard;
|
|
133
|
+
function previewSecret(value) {
|
|
134
|
+
return `${value.slice(0, 4)}… (${value.length} characters)`;
|
|
135
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OutputGuard = void 0;
|
|
4
4
|
exports.redactSensitiveText = redactSensitiveText;
|
|
5
|
+
const checksums_js_1 = require("../utils/checksums.js");
|
|
5
6
|
const OUTPUT_SECRET_PATTERNS = [
|
|
6
7
|
{
|
|
7
8
|
pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
@@ -16,17 +17,18 @@ const OUTPUT_SECRET_PATTERNS = [
|
|
|
16
17
|
{ pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g, label: 'google api key', severity: 'critical' },
|
|
17
18
|
{ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, label: 'jwt token', severity: 'high' },
|
|
18
19
|
{ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, label: 'email address', severity: 'high' },
|
|
20
|
+
{ pattern: /\b(?:\d[ -]*?){13,19}\b/g, label: 'possible credit card', severity: 'high', valid: checksums_js_1.luhn },
|
|
19
21
|
{
|
|
20
22
|
pattern: /(?:\+?\d{1,3}[\s.-]?)?(?:\(?\d{2,4}\)?[\s.-]?)?\d{3,4}[\s.-]?\d{4}\b/g,
|
|
21
23
|
label: 'phone number',
|
|
22
24
|
severity: 'high',
|
|
25
|
+
valid: checksums_js_1.phoneNumber,
|
|
23
26
|
},
|
|
24
27
|
{
|
|
25
28
|
pattern: /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g,
|
|
26
29
|
label: 'ip address',
|
|
27
30
|
severity: 'high',
|
|
28
31
|
},
|
|
29
|
-
{ pattern: /\b(?:\d[ -]*?){13,19}\b/g, label: 'possible credit card', severity: 'high' },
|
|
30
32
|
{
|
|
31
33
|
pattern: /(?:api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|secret|password|token)\s*[:=]\s*(?:['"][^'"\r\n]+['"]|[^\s,;]{8,})/gi,
|
|
32
34
|
label: 'secret assignment',
|
|
@@ -37,8 +39,8 @@ const OUTPUT_SECRET_PATTERNS = [
|
|
|
37
39
|
const CONNECTION_STRING_PATTERN = /\b(?:postgres|postgresql|mysql|mongodb|redis|mssql):\/\/[^\s'"<>]+/gi;
|
|
38
40
|
function redactSensitiveText(value) {
|
|
39
41
|
let redacted = value;
|
|
40
|
-
for (const { pattern } of OUTPUT_SECRET_PATTERNS) {
|
|
41
|
-
redacted = redacted.replace(pattern, '[REDACTED]');
|
|
42
|
+
for (const { pattern, valid } of OUTPUT_SECRET_PATTERNS) {
|
|
43
|
+
redacted = redacted.replace(pattern, (match) => (valid && !valid(match) ? match : '[REDACTED]'));
|
|
42
44
|
}
|
|
43
45
|
return redacted.replace(CONNECTION_STRING_PATTERN, '[REDACTED_CONNECTION_STRING]');
|
|
44
46
|
}
|
|
@@ -68,9 +70,11 @@ class OutputGuard {
|
|
|
68
70
|
}
|
|
69
71
|
const shouldRedact = config.output?.piiRedaction !== false;
|
|
70
72
|
if (shouldRedact) {
|
|
71
|
-
for (const { pattern, label, severity } of OUTPUT_SECRET_PATTERNS) {
|
|
73
|
+
for (const { pattern, label, severity, valid } of OUTPUT_SECRET_PATTERNS) {
|
|
72
74
|
for (const target of textTargets) {
|
|
73
75
|
target.value = target.value.replace(pattern, (value) => {
|
|
76
|
+
if (valid && !valid(value))
|
|
77
|
+
return value;
|
|
74
78
|
findings.push({
|
|
75
79
|
type: 'pii',
|
|
76
80
|
severity,
|