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.
Files changed (298) hide show
  1. package/API_STABILITY.md +225 -0
  2. package/CHANGELOG.md +325 -1
  3. package/README.md +20 -6
  4. package/SECURITY.md +140 -0
  5. package/dist/adapter-kit/contracts.d.ts +87 -0
  6. package/dist/adapter-kit/contracts.js +237 -0
  7. package/dist/adapter-kit/errors.d.ts +39 -0
  8. package/dist/adapter-kit/errors.js +52 -0
  9. package/dist/adapter-kit/index.d.ts +144 -0
  10. package/dist/adapter-kit/index.js +178 -0
  11. package/dist/adapter-kit/semver.d.ts +12 -0
  12. package/dist/adapter-kit/semver.js +55 -0
  13. package/dist/agent/capabilities.d.ts +39 -0
  14. package/dist/agent/capabilities.js +36 -0
  15. package/dist/agent/create-agent.d.ts +61 -11
  16. package/dist/agent/create-agent.js +136 -22
  17. package/dist/agent/index.d.ts +3 -2
  18. package/dist/agent/index.js +1 -1
  19. package/dist/agent/middleware/approval.d.ts +26 -0
  20. package/dist/agent/middleware/approval.js +23 -0
  21. package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  22. package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
  23. package/dist/agent/middleware/context-editor.d.ts +52 -0
  24. package/dist/agent/middleware/context-editor.js +66 -0
  25. package/dist/agent/middleware/filesystem.d.ts +44 -0
  26. package/dist/agent/middleware/filesystem.js +83 -0
  27. package/dist/agent/middleware/index.d.ts +19 -0
  28. package/dist/agent/middleware/index.js +9 -0
  29. package/dist/agent/middleware/limits.d.ts +42 -0
  30. package/dist/agent/middleware/limits.js +52 -0
  31. package/dist/agent/middleware/models.d.ts +48 -0
  32. package/dist/agent/middleware/models.js +44 -0
  33. package/dist/agent/middleware/pii.d.ts +67 -0
  34. package/dist/agent/middleware/pii.js +142 -0
  35. package/dist/agent/middleware/retry.d.ts +66 -0
  36. package/dist/agent/middleware/retry.js +59 -0
  37. package/dist/agent/middleware/shared.d.ts +41 -0
  38. package/dist/agent/middleware/shared.js +73 -0
  39. package/dist/agent/middleware/tool-selector.d.ts +64 -0
  40. package/dist/agent/middleware/tool-selector.js +106 -0
  41. package/dist/agent/permissions.d.ts +113 -0
  42. package/dist/agent/permissions.js +180 -0
  43. package/dist/agent/sandbox.d.ts +151 -0
  44. package/dist/agent/sandbox.js +257 -0
  45. package/dist/agent/tool.d.ts +8 -3
  46. package/dist/agent/tool.js +14 -3
  47. package/dist/core/secure-stream.js +4 -3
  48. package/dist/deep-agent/index.d.ts +109 -0
  49. package/dist/deep-agent/index.js +216 -0
  50. package/dist/embeddings/adapters.js +2 -1
  51. package/dist/evals/judge.js +2 -5
  52. package/dist/evaluate/cache.d.ts +0 -14
  53. package/dist/evaluate/cache.js +0 -30
  54. package/dist/evaluate/file-cache.d.ts +15 -0
  55. package/dist/evaluate/file-cache.js +30 -0
  56. package/dist/evaluate/index.d.ts +2 -1
  57. package/dist/evaluate/index.js +2 -1
  58. package/dist/evaluate/review.js +2 -2
  59. package/dist/evaluate/run.js +2 -2
  60. package/dist/evaluate/version.js +3 -3
  61. package/dist/graph/functional.d.ts +9 -0
  62. package/dist/graph/functional.js +20 -3
  63. package/dist/graph/graph.js +27 -6
  64. package/dist/graph/lint.d.ts +4 -1
  65. package/dist/graph/lint.js +35 -0
  66. package/dist/graph/node-cache.js +3 -3
  67. package/dist/hallucination/rag.d.ts +4 -1
  68. package/dist/hallucination/rag.js +6 -1
  69. package/dist/images/asset-stores.js +2 -3
  70. package/dist/images/asset-support.js +2 -2
  71. package/dist/images/assets.js +1 -2
  72. package/dist/images/evals.js +4 -4
  73. package/dist/insights/proposals.js +1 -2
  74. package/dist/insights/regressions.d.ts +26 -2
  75. package/dist/insights/regressions.js +129 -3
  76. package/dist/loaders/git.js +8 -1
  77. package/dist/loaders/web.js +7 -1
  78. package/dist/mcp/client.d.ts +14 -0
  79. package/dist/mcp/client.js +59 -1
  80. package/dist/mcp/registry.d.ts +6 -0
  81. package/dist/mcp/registry.js +7 -0
  82. package/dist/postgres/fulltext.d.ts +67 -0
  83. package/dist/postgres/fulltext.js +91 -0
  84. package/dist/prompts/file.js +28 -11
  85. package/dist/protocols/a2a.d.ts +235 -0
  86. package/dist/protocols/a2a.js +386 -0
  87. package/dist/protocols/acp.d.ts +87 -0
  88. package/dist/protocols/acp.js +322 -0
  89. package/dist/protocols/ag-ui.d.ts +120 -0
  90. package/dist/protocols/ag-ui.js +180 -0
  91. package/dist/protocols/shared.d.ts +80 -0
  92. package/dist/protocols/shared.js +49 -0
  93. package/dist/providers/anthropic.js +1 -0
  94. package/dist/providers/cohere.js +2 -2
  95. package/dist/providers/google.js +2 -11
  96. package/dist/providers/openai.js +1 -0
  97. package/dist/rag/elasticsearch.d.ts +71 -0
  98. package/dist/rag/elasticsearch.js +130 -0
  99. package/dist/rag/file-ingestion.js +15 -9
  100. package/dist/rag/pipeline.d.ts +140 -0
  101. package/dist/rag/pipeline.js +187 -0
  102. package/dist/rag/rerankers.d.ts +92 -0
  103. package/dist/rag/rerankers.js +97 -0
  104. package/dist/rag/retrievers.d.ts +12 -1
  105. package/dist/runtime/detect.d.ts +17 -0
  106. package/dist/runtime/detect.js +16 -0
  107. package/dist/runtime/index.d.ts +19 -0
  108. package/dist/runtime/index.js +7 -0
  109. package/dist/security/injection-detector.d.ts +2 -0
  110. package/dist/security/injection-detector.js +7 -1
  111. package/dist/security/input-guard.js +5 -2
  112. package/dist/security/output-guard.js +8 -4
  113. package/dist/security/pii-detector.js +22 -6
  114. package/dist/security/prompt-hardening.js +6 -2
  115. package/dist/security/semantic-injection-classifier.d.ts +2 -1
  116. package/dist/security/semantic-injection-classifier.js +31 -35
  117. package/dist/security/upload-scanner.d.ts +8 -0
  118. package/dist/security/upload-scanner.js +51 -9
  119. package/dist/server/assistant.d.ts +5 -1
  120. package/dist/server/assistant.js +4 -2
  121. package/dist/server/auth.d.ts +250 -0
  122. package/dist/server/auth.js +392 -0
  123. package/dist/server/deployments.d.ts +63 -1
  124. package/dist/server/deployments.js +75 -2
  125. package/dist/server/runs.d.ts +9 -0
  126. package/dist/server/runs.js +20 -0
  127. package/dist/server/server.js +5 -0
  128. package/dist/store/memory.d.ts +10 -0
  129. package/dist/store/memory.js +10 -0
  130. package/dist/testing/record.js +2 -2
  131. package/dist/tracing/stores.js +2 -1
  132. package/dist/tracing/tracer.js +2 -2
  133. package/dist/types/config.d.ts +20 -0
  134. package/dist/types/graph.d.ts +38 -0
  135. package/dist/types/messages.d.ts +36 -2
  136. package/dist/types/principal.d.ts +21 -0
  137. package/dist/types/principal.js +1 -0
  138. package/dist/types/server.d.ts +8 -9
  139. package/dist/utils/base64.d.ts +4 -0
  140. package/dist/utils/base64.js +21 -0
  141. package/dist/utils/checksums.d.ts +22 -0
  142. package/dist/utils/checksums.js +39 -0
  143. package/dist/utils/ids.d.ts +8 -0
  144. package/dist/utils/ids.js +26 -2
  145. package/dist/utils/sha256.d.ts +22 -0
  146. package/dist/utils/sha256.js +120 -0
  147. package/dist/utils/tenant.d.ts +11 -0
  148. package/dist/utils/tenant.js +14 -0
  149. package/dist/version.d.ts +1 -1
  150. package/dist/version.js +1 -1
  151. package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
  152. package/dist-cjs/adapter-kit/contracts.js +243 -0
  153. package/dist-cjs/adapter-kit/errors.d.ts +39 -0
  154. package/dist-cjs/adapter-kit/errors.js +57 -0
  155. package/dist-cjs/adapter-kit/index.d.ts +144 -0
  156. package/dist-cjs/adapter-kit/index.js +194 -0
  157. package/dist-cjs/adapter-kit/semver.d.ts +12 -0
  158. package/dist-cjs/adapter-kit/semver.js +59 -0
  159. package/dist-cjs/agent/capabilities.d.ts +39 -0
  160. package/dist-cjs/agent/capabilities.js +41 -0
  161. package/dist-cjs/agent/create-agent.d.ts +61 -11
  162. package/dist-cjs/agent/create-agent.js +136 -22
  163. package/dist-cjs/agent/index.d.ts +3 -2
  164. package/dist-cjs/agent/index.js +4 -4
  165. package/dist-cjs/agent/middleware/approval.d.ts +26 -0
  166. package/dist-cjs/agent/middleware/approval.js +26 -0
  167. package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  168. package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
  169. package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
  170. package/dist-cjs/agent/middleware/context-editor.js +70 -0
  171. package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
  172. package/dist-cjs/agent/middleware/filesystem.js +86 -0
  173. package/dist-cjs/agent/middleware/index.d.ts +19 -0
  174. package/dist-cjs/agent/middleware/index.js +28 -0
  175. package/dist-cjs/agent/middleware/limits.d.ts +42 -0
  176. package/dist-cjs/agent/middleware/limits.js +57 -0
  177. package/dist-cjs/agent/middleware/models.d.ts +48 -0
  178. package/dist-cjs/agent/middleware/models.js +48 -0
  179. package/dist-cjs/agent/middleware/pii.d.ts +67 -0
  180. package/dist-cjs/agent/middleware/pii.js +147 -0
  181. package/dist-cjs/agent/middleware/retry.d.ts +66 -0
  182. package/dist-cjs/agent/middleware/retry.js +63 -0
  183. package/dist-cjs/agent/middleware/shared.d.ts +41 -0
  184. package/dist-cjs/agent/middleware/shared.js +83 -0
  185. package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
  186. package/dist-cjs/agent/middleware/tool-selector.js +109 -0
  187. package/dist-cjs/agent/permissions.d.ts +113 -0
  188. package/dist-cjs/agent/permissions.js +189 -0
  189. package/dist-cjs/agent/sandbox.d.ts +151 -0
  190. package/dist-cjs/agent/sandbox.js +297 -0
  191. package/dist-cjs/agent/tool.d.ts +8 -3
  192. package/dist-cjs/agent/tool.js +14 -3
  193. package/dist-cjs/core/secure-stream.js +4 -3
  194. package/dist-cjs/deep-agent/index.d.ts +109 -0
  195. package/dist-cjs/deep-agent/index.js +219 -0
  196. package/dist-cjs/embeddings/adapters.js +2 -1
  197. package/dist-cjs/evals/judge.js +2 -5
  198. package/dist-cjs/evaluate/cache.d.ts +0 -14
  199. package/dist-cjs/evaluate/cache.js +1 -65
  200. package/dist-cjs/evaluate/file-cache.d.ts +15 -0
  201. package/dist-cjs/evaluate/file-cache.js +67 -0
  202. package/dist-cjs/evaluate/index.d.ts +2 -1
  203. package/dist-cjs/evaluate/index.js +3 -2
  204. package/dist-cjs/evaluate/review.js +2 -2
  205. package/dist-cjs/evaluate/run.js +2 -2
  206. package/dist-cjs/evaluate/version.js +3 -3
  207. package/dist-cjs/graph/functional.d.ts +9 -0
  208. package/dist-cjs/graph/functional.js +20 -3
  209. package/dist-cjs/graph/graph.js +27 -6
  210. package/dist-cjs/graph/lint.d.ts +4 -1
  211. package/dist-cjs/graph/lint.js +35 -0
  212. package/dist-cjs/graph/node-cache.js +3 -3
  213. package/dist-cjs/hallucination/rag.d.ts +4 -1
  214. package/dist-cjs/hallucination/rag.js +6 -1
  215. package/dist-cjs/images/asset-stores.js +2 -3
  216. package/dist-cjs/images/asset-support.js +2 -2
  217. package/dist-cjs/images/assets.js +1 -2
  218. package/dist-cjs/images/evals.js +4 -4
  219. package/dist-cjs/insights/proposals.js +1 -2
  220. package/dist-cjs/insights/regressions.d.ts +26 -2
  221. package/dist-cjs/insights/regressions.js +129 -3
  222. package/dist-cjs/loaders/git.js +8 -1
  223. package/dist-cjs/loaders/web.js +7 -1
  224. package/dist-cjs/mcp/client.d.ts +14 -0
  225. package/dist-cjs/mcp/client.js +60 -1
  226. package/dist-cjs/mcp/registry.d.ts +6 -0
  227. package/dist-cjs/mcp/registry.js +7 -0
  228. package/dist-cjs/postgres/fulltext.d.ts +67 -0
  229. package/dist-cjs/postgres/fulltext.js +129 -0
  230. package/dist-cjs/prompts/file.js +28 -11
  231. package/dist-cjs/protocols/a2a.d.ts +235 -0
  232. package/dist-cjs/protocols/a2a.js +393 -0
  233. package/dist-cjs/protocols/acp.d.ts +87 -0
  234. package/dist-cjs/protocols/acp.js +326 -0
  235. package/dist-cjs/protocols/ag-ui.d.ts +120 -0
  236. package/dist-cjs/protocols/ag-ui.js +186 -0
  237. package/dist-cjs/protocols/shared.d.ts +80 -0
  238. package/dist-cjs/protocols/shared.js +59 -0
  239. package/dist-cjs/providers/anthropic.js +1 -0
  240. package/dist-cjs/providers/cohere.js +2 -2
  241. package/dist-cjs/providers/google.js +3 -12
  242. package/dist-cjs/providers/openai.js +1 -0
  243. package/dist-cjs/rag/elasticsearch.d.ts +71 -0
  244. package/dist-cjs/rag/elasticsearch.js +135 -0
  245. package/dist-cjs/rag/file-ingestion.js +14 -8
  246. package/dist-cjs/rag/pipeline.d.ts +140 -0
  247. package/dist-cjs/rag/pipeline.js +191 -0
  248. package/dist-cjs/rag/rerankers.d.ts +92 -0
  249. package/dist-cjs/rag/rerankers.js +107 -0
  250. package/dist-cjs/rag/retrievers.d.ts +12 -1
  251. package/dist-cjs/runtime/detect.d.ts +17 -0
  252. package/dist-cjs/runtime/detect.js +19 -0
  253. package/dist-cjs/runtime/index.d.ts +19 -0
  254. package/dist-cjs/runtime/index.js +31 -0
  255. package/dist-cjs/security/injection-detector.d.ts +2 -0
  256. package/dist-cjs/security/injection-detector.js +7 -1
  257. package/dist-cjs/security/input-guard.js +5 -2
  258. package/dist-cjs/security/output-guard.js +8 -4
  259. package/dist-cjs/security/pii-detector.js +22 -6
  260. package/dist-cjs/security/prompt-hardening.js +6 -2
  261. package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
  262. package/dist-cjs/security/semantic-injection-classifier.js +31 -35
  263. package/dist-cjs/security/upload-scanner.d.ts +8 -0
  264. package/dist-cjs/security/upload-scanner.js +53 -9
  265. package/dist-cjs/server/assistant.d.ts +5 -1
  266. package/dist-cjs/server/assistant.js +4 -2
  267. package/dist-cjs/server/auth.d.ts +250 -0
  268. package/dist-cjs/server/auth.js +402 -0
  269. package/dist-cjs/server/deployments.d.ts +63 -1
  270. package/dist-cjs/server/deployments.js +75 -2
  271. package/dist-cjs/server/runs.d.ts +9 -0
  272. package/dist-cjs/server/runs.js +20 -0
  273. package/dist-cjs/server/server.js +5 -0
  274. package/dist-cjs/store/memory.d.ts +10 -0
  275. package/dist-cjs/store/memory.js +10 -0
  276. package/dist-cjs/testing/record.js +2 -2
  277. package/dist-cjs/tracing/stores.js +2 -1
  278. package/dist-cjs/tracing/tracer.js +2 -2
  279. package/dist-cjs/types/config.d.ts +20 -0
  280. package/dist-cjs/types/graph.d.ts +38 -0
  281. package/dist-cjs/types/messages.d.ts +36 -2
  282. package/dist-cjs/types/principal.d.ts +21 -0
  283. package/dist-cjs/types/principal.js +2 -0
  284. package/dist-cjs/types/server.d.ts +8 -9
  285. package/dist-cjs/utils/base64.d.ts +4 -0
  286. package/dist-cjs/utils/base64.js +25 -0
  287. package/dist-cjs/utils/checksums.d.ts +22 -0
  288. package/dist-cjs/utils/checksums.js +46 -0
  289. package/dist-cjs/utils/ids.d.ts +8 -0
  290. package/dist-cjs/utils/ids.js +28 -2
  291. package/dist-cjs/utils/sha256.d.ts +22 -0
  292. package/dist-cjs/utils/sha256.js +125 -0
  293. package/dist-cjs/utils/tenant.d.ts +11 -0
  294. package/dist-cjs/utils/tenant.js +15 -0
  295. package/dist-cjs/version.d.ts +1 -1
  296. package/dist-cjs/version.js +1 -1
  297. package/llms.txt +7 -3
  298. package/package.json +193 -6
@@ -0,0 +1,80 @@
1
+ /**
2
+ * What the protocol adapters share: the part of a compiled graph they drive, and the conversions
3
+ * between a protocol's messages and the graph's. Structural, so a protocol entry point never imports
4
+ * the graph runtime: an agent or any compiled graph already satisfies it.
5
+ */
6
+ import type { GraphEvent } from '../types/graph.js';
7
+ import type { Message } from '../types/messages.js';
8
+ import type { Principal } from '../types/principal.js';
9
+ /** Run options a protocol passes to a graph. */
10
+ export interface ProtocolRunOptions {
11
+ /** The thread: a protocol's conversation, context, or session id. */
12
+ threadId?: string;
13
+ /** Stops the run, as a protocol's cancel does. */
14
+ signal?: AbortSignal;
15
+ /** Receives the run's fine-grained events, which the protocol translates as they happen. */
16
+ onEvent?: (event: GraphEvent) => void;
17
+ /** Who the run is for, when the protocol's transport authenticated the caller. */
18
+ principal?: Principal;
19
+ }
20
+ /** A question a paused run is waiting on. */
21
+ export interface ProtocolInterrupt {
22
+ /** Its id, which an answer names. */
23
+ id: string;
24
+ /** The question. */
25
+ reason?: string;
26
+ /** What it is about, such as the tool call awaiting approval. */
27
+ payload?: unknown;
28
+ }
29
+ /** What a run ended with. */
30
+ export interface ProtocolResult {
31
+ /** The thread it ran on. */
32
+ threadId: string;
33
+ /** `completed`, `awaiting_input`, or another graph status. */
34
+ status: string;
35
+ /** The state it ended with. */
36
+ state: unknown;
37
+ /** Every question a paused run asked. */
38
+ interrupts?: ProtocolInterrupt[];
39
+ }
40
+ /** The part of a compiled graph the protocols drive. An agent from `createAgent()` satisfies it. */
41
+ export interface ProtocolGraph {
42
+ /** Runs an input. */
43
+ invoke(input: unknown, options?: ProtocolRunOptions): Promise<ProtocolResult>;
44
+ /** Answers a paused run's questions, by interrupt id, and continues it. */
45
+ resumeInterruptsWith(threadId: string, answers: Record<string, unknown>, options?: ProtocolRunOptions): Promise<ProtocolResult>;
46
+ }
47
+ /** A role a protocol message may carry. */
48
+ export type ProtocolRole = 'user' | 'assistant' | 'system' | 'developer' | 'tool' | 'agent';
49
+ /** The final answer a run produced: an agent's `answer`, or its state as JSON. */
50
+ export declare function answerOf(state: unknown): string;
51
+ /**
52
+ * The conversation a finished run leaves, to send whole with the next turn: an agent's transcript,
53
+ * tool calls and results included, or the fallback for a graph that keeps none.
54
+ */
55
+ export declare function transcriptOf(state: unknown, fallback: Message[]): Message[];
56
+ /** The text of a protocol message's content, for content as a string or a list of text parts. */
57
+ export declare function textOf(content: unknown): string;
58
+ /** A Nexus message from a protocol role and text. A developer message is a system message. */
59
+ export declare function messageOf(role: ProtocolRole, content: string, extra?: Partial<Message>): Message;
60
+ /** A short random id, for runs and messages the protocol creates. */
61
+ export declare function protocolId(prefix: string): string;
62
+ /** Whether an interrupt asks to approve a tool call, as an agent's approvals do. */
63
+ export declare function toolCallOf(interrupt: ProtocolInterrupt): {
64
+ id: string;
65
+ name: string;
66
+ args: unknown;
67
+ } | undefined;
68
+ /**
69
+ * Words answering an approval, as an approval: yes, ok, allow, approve, or confirm allows, and
70
+ * anything else refuses, with the words as the reason. A protocol that carries only text needs this
71
+ * so that "no" never reads as consent.
72
+ */
73
+ export declare function approvalOf(text: string): {
74
+ approved: true;
75
+ } | {
76
+ approved: false;
77
+ reason?: string;
78
+ };
79
+ /** A value as the text a protocol's text field carries. */
80
+ export declare function asText(value: unknown): string;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.answerOf = answerOf;
4
+ exports.transcriptOf = transcriptOf;
5
+ exports.textOf = textOf;
6
+ exports.messageOf = messageOf;
7
+ exports.protocolId = protocolId;
8
+ exports.toolCallOf = toolCallOf;
9
+ exports.approvalOf = approvalOf;
10
+ exports.asText = asText;
11
+ function answerOf(state) {
12
+ const answer = state?.answer;
13
+ if (typeof answer === 'string')
14
+ return answer;
15
+ return state === undefined ? '' : JSON.stringify(state);
16
+ }
17
+ function transcriptOf(state, fallback) {
18
+ const messages = state?.messages;
19
+ return Array.isArray(messages) && messages.every((item) => typeof item?.role === 'string')
20
+ ? messages
21
+ : fallback;
22
+ }
23
+ function textOf(content) {
24
+ if (typeof content === 'string')
25
+ return content;
26
+ if (Array.isArray(content)) {
27
+ return content
28
+ .map((part) => {
29
+ const value = part;
30
+ return typeof value.text === 'string' ? value.text : '';
31
+ })
32
+ .filter(Boolean)
33
+ .join('\n');
34
+ }
35
+ return '';
36
+ }
37
+ function messageOf(role, content, extra = {}) {
38
+ const mapped = role === 'developer' ? 'system' : role === 'agent' ? 'assistant' : role;
39
+ return { role: mapped, content, ...extra };
40
+ }
41
+ function protocolId(prefix) {
42
+ return `${prefix}-${globalThis.crypto.randomUUID()}`;
43
+ }
44
+ function toolCallOf(interrupt) {
45
+ const payload = interrupt.payload;
46
+ return typeof payload?.id === 'string' && typeof payload.name === 'string'
47
+ ? { id: payload.id, name: payload.name, args: payload.args }
48
+ : undefined;
49
+ }
50
+ function approvalOf(text) {
51
+ const words = text.trim();
52
+ if (/^(y|yes|ok|okay|allow|allowed|approve|approved|confirm|confirmed|go ahead)\b/i.test(words)) {
53
+ return { approved: true };
54
+ }
55
+ return { approved: false, ...(words ? { reason: words } : {}) };
56
+ }
57
+ function asText(value) {
58
+ return typeof value === 'string' ? value : JSON.stringify(value ?? null);
59
+ }
@@ -199,6 +199,7 @@ class AnthropicProvider extends base_js_1.BaseProvider {
199
199
  this.client = new Anthropic({
200
200
  apiKey: this.config.apiKey,
201
201
  baseURL: this.config.baseUrl,
202
+ ...(this.config.fetch ? { fetch: this.config.fetch } : {}),
202
203
  });
203
204
  }
204
205
  return this.client;
@@ -17,7 +17,7 @@ class CohereProvider extends base_js_1.BaseProvider {
17
17
  try {
18
18
  this.throwIfAborted(request);
19
19
  const startTime = Date.now();
20
- const response = await fetch(`${this.baseUrl()}/v2/chat`, {
20
+ const response = await (this.config.fetch ?? fetch)(`${this.baseUrl()}/v2/chat`, {
21
21
  method: 'POST',
22
22
  headers: {
23
23
  Authorization: `Bearer ${this.config.apiKey}`,
@@ -81,7 +81,7 @@ class CohereProvider extends base_js_1.BaseProvider {
81
81
  }, request.signal);
82
82
  }
83
83
  async healthCheck() {
84
- const response = await fetch(`${this.baseUrl()}/v2/models`, {
84
+ const response = await (this.config.fetch ?? fetch)(`${this.baseUrl()}/v2/models`, {
85
85
  headers: { Authorization: `Bearer ${this.config.apiKey}` },
86
86
  });
87
87
  return response.ok;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GoogleProvider = void 0;
4
+ const base64_js_1 = require("../utils/base64.js");
4
5
  const base_js_1 = require("./base.js");
5
6
  const usage_js_1 = require("../core/usage.js");
6
7
  const ids_js_1 = require("../utils/ids.js");
@@ -122,7 +123,7 @@ class GoogleProvider extends base_js_1.BaseProvider {
122
123
  const endpoint = stream ? 'streamGenerateContent' : 'generateContent';
123
124
  const url = `${this.baseUrl()}/models/${encodeURIComponent(model)}:${endpoint}?key=${encodeURIComponent(this.config.apiKey)}${stream ? '&alt=sse' : ''}`;
124
125
  const body = JSON.stringify(this.createBody(request));
125
- const response = await fetch(url, {
126
+ const response = await (this.config.fetch ?? fetch)(url, {
126
127
  method: 'POST',
127
128
  headers: { 'content-type': 'application/json' },
128
129
  body,
@@ -277,7 +278,7 @@ class GoogleProvider extends base_js_1.BaseProvider {
277
278
  return (content?.parts || [])
278
279
  .filter((part) => part.inlineData?.data && !part.thought)
279
280
  .map((part) => ({
280
- location: { kind: 'bytes', data: decodeBase64(part.inlineData?.data ?? '') },
281
+ location: { kind: 'bytes', data: (0, base64_js_1.decodeBase64)(part.inlineData?.data ?? '') },
281
282
  mimeType: part.inlineData?.mimeType || 'application/octet-stream',
282
283
  }));
283
284
  }
@@ -314,13 +315,3 @@ class GoogleProvider extends base_js_1.BaseProvider {
314
315
  }
315
316
  }
316
317
  exports.GoogleProvider = GoogleProvider;
317
- function decodeBase64(data) {
318
- const buffer = globalThis.Buffer;
319
- if (buffer)
320
- return new Uint8Array(buffer.from(data, 'base64'));
321
- const binary = atob(data);
322
- const bytes = new Uint8Array(binary.length);
323
- for (let index = 0; index < binary.length; index += 1)
324
- bytes[index] = binary.charCodeAt(index);
325
- return bytes;
326
- }
@@ -61,6 +61,7 @@ class OpenAIProvider extends base_js_1.BaseProvider {
61
61
  organization: this.config.organization,
62
62
  defaultHeaders: this.config.defaultHeaders,
63
63
  defaultQuery: this.config.defaultQuery,
64
+ ...(this.config.fetch ? { fetch: this.config.fetch } : {}),
64
65
  });
65
66
  }
66
67
  return this.client;
@@ -0,0 +1,71 @@
1
+ import type { RagChunk } from '../hallucination/rag.js';
2
+ import type { VectorSearchResult } from '../hallucination/retrieval.js';
3
+ import type { RetrieveOptions, SparseRetriever } from './retrievers.js';
4
+ /** Options for the Elasticsearch keyword index. */
5
+ export interface ElasticsearchKeywordIndexOptions {
6
+ /** The cluster, such as `http://localhost:9200`, an Elastic Cloud endpoint, or an OpenSearch domain. */
7
+ url: string;
8
+ /** The index chunks are kept in. */
9
+ index: string;
10
+ /** Sent as `Authorization: ApiKey <key>`. */
11
+ apiKey?: string;
12
+ /** Sent as basic authentication, for a cluster with users instead of API keys. */
13
+ username?: string;
14
+ /** The password for `username`. */
15
+ password?: string;
16
+ /** The analyzer `migrate()` gives the `content` field, such as `english`. Defaults to the cluster's standard one. */
17
+ analyzer?: string;
18
+ /**
19
+ * When a write becomes searchable. `wait_for` (the default) returns once a search would see it, as
20
+ * the vector stores do; `false` returns sooner and leaves it to the index's refresh interval.
21
+ */
22
+ refresh?: 'wait_for' | boolean;
23
+ /** Replaces the global `fetch`, for a proxy, retries, signed requests, or tests. */
24
+ fetch?: typeof globalThis.fetch;
25
+ /** Headers added to every request. */
26
+ headers?: Record<string, string>;
27
+ /** Aborts a request that takes longer, in milliseconds. Defaults to 30 seconds. */
28
+ timeoutMs?: number;
29
+ }
30
+ /** Raised when the cluster answers with an error. */
31
+ export declare class ElasticsearchError extends Error {
32
+ /** The HTTP status the cluster answered with. */
33
+ readonly status: number;
34
+ /** The cluster's response body, as text. */
35
+ readonly body?: string | undefined;
36
+ constructor(message: string,
37
+ /** The HTTP status the cluster answered with. */
38
+ status: number,
39
+ /** The cluster's response body, as text. */
40
+ body?: string | undefined);
41
+ }
42
+ /**
43
+ * Keyword search in Elasticsearch or OpenSearch, through their REST API.
44
+ *
45
+ * No client library is a dependency: the index speaks HTTP through `fetch`, as the vector stores do,
46
+ * so it runs wherever `fetch` does. Scores are the engine's own BM25, ranked in the cluster, so only
47
+ * the best matches come back. A `SparseRetriever`, so `hybridRetriever()` fuses it with any vector
48
+ * store. A metadata filter is an exact match on top-level fields, as in the vector stores.
49
+ */
50
+ export declare class ElasticsearchKeywordIndex implements SparseRetriever {
51
+ private readonly options;
52
+ private readonly base;
53
+ private readonly fetchImpl;
54
+ constructor(options: ElasticsearchKeywordIndexOptions);
55
+ /**
56
+ * Creates the index unless it exists: `content` as analyzed text, `id` and `source` as keywords,
57
+ * and every string under `metadata` as a keyword, so a filter matches it exactly. Never runs
58
+ * implicitly.
59
+ */
60
+ migrate(): Promise<void>;
61
+ /** Adds chunks, or replaces those whose id exists, in one bulk request. */
62
+ add(chunks: readonly RagChunk[]): Promise<void>;
63
+ /** Removes chunks by id. An id that is not held is ignored. */
64
+ delete(ids: readonly string[]): Promise<void>;
65
+ /** The chunks that best match a query's terms, best first, scored by the engine's BM25. */
66
+ retrieve(query: string, options?: RetrieveOptions): Promise<VectorSearchResult[]>;
67
+ /** Chunks held. */
68
+ size(): Promise<number>;
69
+ private bulk;
70
+ private request;
71
+ }
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ElasticsearchKeywordIndex = exports.ElasticsearchError = void 0;
4
+ const signals_js_1 = require("../utils/signals.js");
5
+ class ElasticsearchError extends Error {
6
+ status;
7
+ body;
8
+ constructor(message, status, body) {
9
+ super(message);
10
+ this.status = status;
11
+ this.body = body;
12
+ this.name = 'ElasticsearchError';
13
+ }
14
+ }
15
+ exports.ElasticsearchError = ElasticsearchError;
16
+ class ElasticsearchKeywordIndex {
17
+ options;
18
+ base;
19
+ fetchImpl;
20
+ constructor(options) {
21
+ this.options = options;
22
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(options.index)) {
23
+ throw new RangeError(`"${options.index}" is not a valid index name: use lowercase letters, digits, ".", "_", and "-"`);
24
+ }
25
+ this.base = options.url.replace(/\/+$/, '');
26
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
27
+ }
28
+ async migrate() {
29
+ const existing = await this.request('HEAD', `/${this.options.index}`, undefined, { allowNotFound: true });
30
+ if (existing !== undefined)
31
+ return;
32
+ await this.request('PUT', `/${this.options.index}`, {
33
+ mappings: {
34
+ dynamic_templates: [
35
+ {
36
+ metadata_strings: { path_match: 'metadata.*', match_mapping_type: 'string', mapping: { type: 'keyword' } },
37
+ },
38
+ ],
39
+ properties: {
40
+ id: { type: 'keyword' },
41
+ content: { type: 'text', ...(this.options.analyzer ? { analyzer: this.options.analyzer } : {}) },
42
+ source: { type: 'keyword' },
43
+ metadata: { type: 'object', dynamic: true },
44
+ },
45
+ },
46
+ });
47
+ }
48
+ async add(chunks) {
49
+ if (chunks.length === 0)
50
+ return;
51
+ const lines = chunks.flatMap((chunk) => [
52
+ { index: { _index: this.options.index, _id: chunk.id } },
53
+ { id: chunk.id, content: chunk.content, source: chunk.source ?? null, metadata: chunk.metadata ?? null },
54
+ ]);
55
+ await this.bulk(lines);
56
+ }
57
+ async delete(ids) {
58
+ if (ids.length === 0)
59
+ return;
60
+ await this.bulk(ids.map((id) => ({ delete: { _index: this.options.index, _id: id } })));
61
+ }
62
+ async retrieve(query, options = {}) {
63
+ const filter = Object.entries(options.filter ?? {});
64
+ const clauses = [];
65
+ for (const [key, value] of filter) {
66
+ if (value === null)
67
+ clauses.push({ bool: { must_not: { exists: { field: `metadata.${key}` } } } });
68
+ else if (['string', 'number', 'boolean'].includes(typeof value))
69
+ clauses.push({ term: { [`metadata.${key}`]: value } });
70
+ else
71
+ return [];
72
+ }
73
+ const result = (await this.request('POST', `/${this.options.index}/_search`, {
74
+ size: options.topK ?? 5,
75
+ query: { bool: { must: [{ match: { content: { query, operator: 'or' } } }], filter: clauses } },
76
+ _source: ['id', 'content', 'source', 'metadata'],
77
+ }, { signal: options.signal }));
78
+ return (result.hits?.hits ?? []).map((hit) => ({
79
+ id: hit._source?.id ?? hit._id,
80
+ content: hit._source?.content ?? '',
81
+ ...(hit._source?.source ? { source: hit._source.source } : {}),
82
+ ...(hit._source?.metadata ? { metadata: hit._source.metadata } : {}),
83
+ score: hit._score ?? 0,
84
+ }));
85
+ }
86
+ async size() {
87
+ const result = (await this.request('GET', `/${this.options.index}/_count`));
88
+ return result.count ?? 0;
89
+ }
90
+ async bulk(lines) {
91
+ const refresh = this.options.refresh ?? 'wait_for';
92
+ const result = (await this.request('POST', `/_bulk${refresh === false ? '' : `?refresh=${refresh === true ? 'true' : 'wait_for'}`}`, `${lines.map((line) => JSON.stringify(line)).join('\n')}\n`));
93
+ if (!result.errors)
94
+ return;
95
+ const failed = (result.items ?? [])
96
+ .map((item) => Object.entries(item)[0])
97
+ .map((entry) => (entry?.[1]?.error && !(entry[0] === 'delete' && entry[1].status === 404) ? entry[1] : undefined))
98
+ .find((outcome) => outcome !== undefined);
99
+ if (failed)
100
+ throw new ElasticsearchError(`A bulk write failed: ${failed.error?.reason ?? 'unknown reason'}`, failed.status ?? 500);
101
+ }
102
+ async request(method, path, body, options = {}) {
103
+ const timeout = AbortSignal.timeout(this.options.timeoutMs ?? 30_000);
104
+ const linked = (0, signals_js_1.linkSignals)([timeout, options.signal]);
105
+ const ndjson = typeof body === 'string';
106
+ const authorization = this.options.apiKey
107
+ ? `ApiKey ${this.options.apiKey}`
108
+ : this.options.username !== undefined
109
+ ? `Basic ${btoa(`${this.options.username}:${this.options.password ?? ''}`)}`
110
+ : undefined;
111
+ try {
112
+ const response = await this.fetchImpl(`${this.base}${path}`, {
113
+ method,
114
+ headers: {
115
+ ...(body === undefined ? {} : { 'content-type': ndjson ? 'application/x-ndjson' : 'application/json' }),
116
+ ...(authorization ? { authorization } : {}),
117
+ ...this.options.headers,
118
+ },
119
+ ...(body === undefined ? {} : { body: ndjson ? body : JSON.stringify(body) }),
120
+ signal: linked.signal,
121
+ });
122
+ if (response.status === 404 && options.allowNotFound)
123
+ return undefined;
124
+ const text = method === 'HEAD' ? '' : await response.text();
125
+ if (!response.ok) {
126
+ throw new ElasticsearchError(`Elasticsearch ${method} ${path} failed with ${response.status}`, response.status, text);
127
+ }
128
+ return text ? JSON.parse(text) : {};
129
+ }
130
+ finally {
131
+ linked.dispose();
132
+ }
133
+ }
134
+ }
135
+ exports.ElasticsearchKeywordIndex = ElasticsearchKeywordIndex;
@@ -15,19 +15,30 @@ async function ingestFilesAfterScan(files, options = {}) {
15
15
  for (const file of files) {
16
16
  const extractor = options.extractors?.find((item) => item.supports(file));
17
17
  if (extractor) {
18
+ const text = await extractor.extract(file);
19
+ if (options.scan?.scanTextContent !== false) {
20
+ const extracted = (0, upload_scanner_js_1.scanUploads)([{ name: file.name, mimeType: 'text/plain', content: text }], {
21
+ ...(options.scan?.forbiddenPatterns ? { forbiddenPatterns: options.scan.forbiddenPatterns } : {}),
22
+ blockedExtensions: [],
23
+ });
24
+ if (!extracted.ok) {
25
+ throw new Error(`Upload scan failed on extracted text: ${extracted.findings.map((finding) => `${finding.fileName}: ${finding.message}`).join('; ')}`);
26
+ }
27
+ }
18
28
  documents.push({
19
29
  id: file.name,
20
30
  source: file.name,
21
- text: await extractor.extract(file),
31
+ text,
22
32
  metadata: { mimeType: file.mimeType, sizeBytes: file.sizeBytes },
23
33
  });
24
34
  continue;
25
35
  }
26
- if (typeof file.content === 'string' && isTextMime(file.mimeType)) {
36
+ const text = (0, upload_scanner_js_1.isTextual)(file.mimeType) ? (0, upload_scanner_js_1.textOf)(file.content, file.mimeType) : undefined;
37
+ if (text !== undefined) {
27
38
  documents.push({
28
39
  id: file.name,
29
40
  source: file.name,
30
- text: file.content,
41
+ text,
31
42
  metadata: { mimeType: file.mimeType, sizeBytes: file.sizeBytes },
32
43
  });
33
44
  continue;
@@ -53,8 +64,3 @@ function createOcrExtractor(ocrImage, mimeTypes = ['image/png', 'image/jpeg', 'i
53
64
  extract: ocrImage,
54
65
  };
55
66
  }
56
- function isTextMime(mimeType) {
57
- if (!mimeType)
58
- return true;
59
- return (mimeType.startsWith('text/') || ['application/json', 'application/xml', 'application/markdown'].includes(mimeType));
60
- }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Durable ingestion: keeps a vector store, and any keyword indexes beside it, in step with a corpus
3
+ * that changes.
4
+ *
5
+ * A manifest in any `Store` records, for every document, the version of its content, the chunking,
6
+ * the embedding model, and each chunk's version. A run reads the corpus, embeds only the chunks that
7
+ * are new or changed, deletes the chunks a document no longer has and the documents the corpus no
8
+ * longer has, and writes every change idempotently, before the manifest records it. A run that dies
9
+ * halfway leaves a manifest that says exactly what finished; the next run skips it, and inside a
10
+ * durable operation it starts at the document it stopped on.
11
+ */
12
+ import type { EmbeddingProvider, VectorStore } from '../hallucination/retrieval.js';
13
+ import type { OperationContext, OperationExecutor } from '../types/operations.js';
14
+ import type { Store } from '../types/store.js';
15
+ import { type DocumentSource, type IngestionOptions } from './ingestion.js';
16
+ import type { SparseRetriever } from './retrievers.js';
17
+ /** A source of documents: a loader, an array, or any iterable, read once per run. */
18
+ export type IngestionSource = AsyncIterable<DocumentSource> | Iterable<DocumentSource>;
19
+ /** Options for `createIngestionPipeline()`. */
20
+ export interface IngestionPipelineOptions {
21
+ /** Names the pipeline. Its manifest is kept under this name, so two pipelines never share one. */
22
+ name: string;
23
+ /**
24
+ * Where the manifest is kept: any `Store`, so it lives in memory, Redis, Postgres, or SQLite. It
25
+ * holds one item per document, and a `MemoryStore` keeps 10,000 items unless given a larger
26
+ * `maxItems`: a manifest that forgets a document re-embeds it.
27
+ */
28
+ manifest: Store;
29
+ /** The vector store chunks are embedded into. */
30
+ vectors: VectorStore;
31
+ /**
32
+ * Keyword indexes kept in step, such as an `ElasticsearchKeywordIndex`. A `PostgresKeywordIndex`
33
+ * that is `shared` needs no place here: the vector store's writes already reach it.
34
+ */
35
+ keywords?: readonly SparseRetriever[];
36
+ /** Embeds chunk texts. Called only for chunks that are new or changed. */
37
+ embed: EmbeddingProvider;
38
+ /** The embedding model, recorded with every document: a different one re-embeds everything. */
39
+ embeddingModel: string;
40
+ /** How documents are split. Different options re-chunk every document. */
41
+ chunking?: IngestionOptions;
42
+ /** Documents read, embedded, and recorded together. Defaults to 64. */
43
+ batchSize?: number;
44
+ }
45
+ /** Options for one run. */
46
+ export interface IngestionRunOptions {
47
+ /** Stops the run between batches. Everything recorded so far stays recorded. */
48
+ signal?: AbortSignal;
49
+ /**
50
+ * Removes the documents the manifest has and this run did not see, with their chunks. Defaults to
51
+ * true: the source is the whole corpus. Turn it off when a run loads only part of it.
52
+ */
53
+ deleteMissing?: boolean;
54
+ /** Called after each batch, with the run so far. */
55
+ onProgress?: (progress: IngestionReport) => void;
56
+ /**
57
+ * The durable operation the run belongs to, when it runs inside an `OperationRunner`. After every
58
+ * batch the run records how far it got with `heartbeat()`, and a later attempt reads it from
59
+ * `previousHeartbeat` and skips straight past what was finished.
60
+ */
61
+ operation?: Pick<OperationContext, 'heartbeat' | 'previousHeartbeat'>;
62
+ }
63
+ /** What a run did. */
64
+ export interface IngestionReport {
65
+ /** Documents read. */
66
+ documents: number;
67
+ /** Documents whose content, chunking, and model were all as recorded, so nothing was done. */
68
+ unchanged: number;
69
+ /** Documents the manifest did not have. */
70
+ added: number;
71
+ /** Documents that changed. */
72
+ updated: number;
73
+ /** Documents removed because the corpus no longer has them. */
74
+ deleted: number;
75
+ /** Chunks embedded and written. */
76
+ chunksEmbedded: number;
77
+ /** Chunks of changed documents whose text did not change, so their embedding was kept. */
78
+ chunksKept: number;
79
+ /** Chunks removed. */
80
+ chunksDeleted: number;
81
+ /** Documents a resumed run skipped because an earlier attempt had finished them. */
82
+ resumed: number;
83
+ }
84
+ /** What the manifest records for one document. */
85
+ export interface IngestedDocument {
86
+ /** The document's id. */
87
+ id: string;
88
+ /** A hash of its text, source, and metadata. */
89
+ version: string;
90
+ /** A hash of the chunking options it was split with. */
91
+ chunking: string;
92
+ /** The embedding model its chunks were embedded with. */
93
+ model: string;
94
+ /** Each chunk's id, with the hash of its text: the chunk's version. */
95
+ chunks: Record<string, string>;
96
+ /** When it was last written, as ISO-8601. */
97
+ updatedAt: string;
98
+ }
99
+ /** A pipeline: run it as often as the corpus changes. */
100
+ export interface IngestionPipeline {
101
+ /** Brings the stores in step with the documents a source yields. */
102
+ run(source: IngestionSource | readonly IngestionSource[], options?: IngestionRunOptions): Promise<IngestionReport>;
103
+ /** What the manifest records for a document, or `undefined`. */
104
+ document(id: string): Promise<IngestedDocument | undefined>;
105
+ /** Removes documents by id, with their chunks, from every store and the manifest. */
106
+ remove(ids: readonly string[]): Promise<number>;
107
+ }
108
+ /**
109
+ * Builds a pipeline that keeps the stores in step with a corpus, re-embedding only what changed.
110
+ *
111
+ * Documents need an `id` (or a `source`), which is what ties a document to its earlier version. A
112
+ * chunk's id is its document's id and a hash of its text, so a chunk whose text did not change keeps
113
+ * its embedding wherever it moved. Chunks are cut by length, within each Markdown section when
114
+ * `splitOnMarkdownHeadings` is on, so an edit re-embeds the chunks of its section from the edit on,
115
+ * and a section added or removed leaves the others alone. One run at a time per pipeline; an
116
+ * operation's idempotency key enforces that across workers.
117
+ *
118
+ * ```ts
119
+ * const pipeline = createIngestionPipeline({
120
+ * name: 'help-center',
121
+ * manifest: store,
122
+ * vectors: new PostgresVectorStore(pool, { dimensions: 1536 }),
123
+ * embed,
124
+ * embeddingModel: 'text-embedding-3-small',
125
+ * });
126
+ * const report = await pipeline.run(loadDirectory('./docs'));
127
+ * ```
128
+ */
129
+ export declare function createIngestionPipeline(options: IngestionPipelineOptions): IngestionPipeline;
130
+ /**
131
+ * The pipeline as the executor of a durable operation: submit it to an `OperationRunner`, and an
132
+ * attempt that dies halfway is recovered by another worker, which starts at the document the first
133
+ * one stopped on.
134
+ *
135
+ * ```ts
136
+ * const runner = new OperationRunner({ store: new RedisOperationStore(redis), retry: { maxAttempts: 5 } });
137
+ * await runner.submit(ingestionExecutor(pipeline, () => loadDirectory('./docs')), { idempotencyKey: 'help-center' });
138
+ * ```
139
+ */
140
+ export declare function ingestionExecutor(pipeline: IngestionPipeline, source: () => IngestionSource | readonly IngestionSource[], options?: Omit<IngestionRunOptions, 'operation' | 'signal'>): OperationExecutor<IngestionReport>;