nexus-ai-pro 2.1.0 → 2.3.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 (300) hide show
  1. package/API_STABILITY.md +77 -0
  2. package/CHANGELOG.md +286 -1
  3. package/README.md +19 -9
  4. package/dist/agent/capabilities.d.ts +39 -0
  5. package/dist/agent/capabilities.js +36 -0
  6. package/dist/agent/create-agent.d.ts +61 -11
  7. package/dist/agent/create-agent.js +136 -22
  8. package/dist/agent/index.d.ts +3 -2
  9. package/dist/agent/index.js +1 -1
  10. package/dist/agent/middleware/approval.d.ts +26 -0
  11. package/dist/agent/middleware/approval.js +23 -0
  12. package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  13. package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
  14. package/dist/agent/middleware/context-editor.d.ts +52 -0
  15. package/dist/agent/middleware/context-editor.js +66 -0
  16. package/dist/agent/middleware/filesystem.d.ts +44 -0
  17. package/dist/agent/middleware/filesystem.js +83 -0
  18. package/dist/agent/middleware/index.d.ts +19 -0
  19. package/dist/agent/middleware/index.js +9 -0
  20. package/dist/agent/middleware/limits.d.ts +42 -0
  21. package/dist/agent/middleware/limits.js +52 -0
  22. package/dist/agent/middleware/models.d.ts +48 -0
  23. package/dist/agent/middleware/models.js +44 -0
  24. package/dist/agent/middleware/pii.d.ts +67 -0
  25. package/dist/agent/middleware/pii.js +173 -0
  26. package/dist/agent/middleware/retry.d.ts +66 -0
  27. package/dist/agent/middleware/retry.js +59 -0
  28. package/dist/agent/middleware/shared.d.ts +41 -0
  29. package/dist/agent/middleware/shared.js +73 -0
  30. package/dist/agent/middleware/tool-selector.d.ts +64 -0
  31. package/dist/agent/middleware/tool-selector.js +106 -0
  32. package/dist/agent/permissions.d.ts +113 -0
  33. package/dist/agent/permissions.js +180 -0
  34. package/dist/agent/sandbox.d.ts +151 -0
  35. package/dist/agent/sandbox.js +257 -0
  36. package/dist/agent/tool.d.ts +8 -3
  37. package/dist/agent/tool.js +14 -3
  38. package/dist/cache/memory-cache.d.ts +8 -0
  39. package/dist/cache/memory-cache.js +9 -0
  40. package/dist/cache/semantic-cache.d.ts +6 -3
  41. package/dist/cache/semantic-cache.js +5 -2
  42. package/dist/cli/args.js +3 -0
  43. package/dist/cli/db.d.ts +16 -4
  44. package/dist/cli/db.js +169 -22
  45. package/dist/cli/doctor.d.ts +17 -0
  46. package/dist/cli/doctor.js +53 -0
  47. package/dist/cli.js +12 -1
  48. package/dist/core/lifecycle.js +11 -6
  49. package/dist/core/nexus.d.ts +6 -0
  50. package/dist/core/nexus.js +103 -22
  51. package/dist/deep-agent/index.d.ts +109 -0
  52. package/dist/deep-agent/index.js +216 -0
  53. package/dist/doctor/index.d.ts +90 -0
  54. package/dist/doctor/index.js +237 -0
  55. package/dist/graph/functional.d.ts +89 -7
  56. package/dist/graph/functional.js +156 -36
  57. package/dist/graph/graph.d.ts +15 -5
  58. package/dist/graph/graph.js +95 -22
  59. package/dist/graph/lint.d.ts +4 -1
  60. package/dist/graph/lint.js +35 -0
  61. package/dist/images/evals.js +4 -2
  62. package/dist/operations/adapters.d.ts +52 -4
  63. package/dist/operations/adapters.js +194 -7
  64. package/dist/operations/runner.js +1 -0
  65. package/dist/operations/stats.js +8 -1
  66. package/dist/operations/store.d.ts +5 -1
  67. package/dist/operations/store.js +7 -0
  68. package/dist/ops/circuit-breaker.d.ts +52 -0
  69. package/dist/ops/circuit-breaker.js +4 -0
  70. package/dist/ops/circuit-store.d.ts +13 -2
  71. package/dist/ops/circuit-store.js +68 -0
  72. package/dist/ops/circuit-sync.d.ts +17 -0
  73. package/dist/ops/circuit-sync.js +86 -0
  74. package/dist/ops/rate-limiter.d.ts +2 -0
  75. package/dist/ops/rate-limiter.js +2 -0
  76. package/dist/postgres/circuits.d.ts +21 -4
  77. package/dist/postgres/circuits.js +65 -4
  78. package/dist/postgres/client.d.ts +0 -2
  79. package/dist/postgres/client.js +0 -4
  80. package/dist/postgres/evaluate.d.ts +16 -4
  81. package/dist/postgres/evaluate.js +17 -5
  82. package/dist/postgres/fulltext.d.ts +67 -0
  83. package/dist/postgres/fulltext.js +91 -0
  84. package/dist/postgres/index.d.ts +32 -11
  85. package/dist/postgres/index.js +65 -31
  86. package/dist/postgres/migrations.d.ts +60 -0
  87. package/dist/postgres/migrations.js +121 -0
  88. package/dist/postgres/operations.d.ts +14 -3
  89. package/dist/postgres/operations.js +45 -15
  90. package/dist/postgres/prompts.d.ts +11 -2
  91. package/dist/postgres/prompts.js +14 -3
  92. package/dist/postgres/rollups.d.ts +35 -0
  93. package/dist/postgres/rollups.js +129 -0
  94. package/dist/postgres/store.d.ts +11 -2
  95. package/dist/postgres/store.js +14 -3
  96. package/dist/postgres/traces.d.ts +13 -3
  97. package/dist/postgres/traces.js +25 -3
  98. package/dist/postgres/vectors.d.ts +11 -2
  99. package/dist/postgres/vectors.js +14 -3
  100. package/dist/rag/elasticsearch.d.ts +71 -0
  101. package/dist/rag/elasticsearch.js +130 -0
  102. package/dist/rag/pipeline.d.ts +140 -0
  103. package/dist/rag/pipeline.js +187 -0
  104. package/dist/rag/rerankers.d.ts +92 -0
  105. package/dist/rag/rerankers.js +97 -0
  106. package/dist/rag/retrievers.d.ts +12 -1
  107. package/dist/router/auto-router.d.ts +34 -7
  108. package/dist/router/auto-router.js +201 -68
  109. package/dist/server/assistant.d.ts +9 -1
  110. package/dist/server/assistant.js +10 -0
  111. package/dist/server/auth.d.ts +250 -0
  112. package/dist/server/auth.js +392 -0
  113. package/dist/server/errors.d.ts +12 -0
  114. package/dist/server/errors.js +7 -0
  115. package/dist/server/index.d.ts +1 -1
  116. package/dist/server/index.js +1 -1
  117. package/dist/server/runs.d.ts +24 -1
  118. package/dist/server/runs.js +90 -14
  119. package/dist/sqlite/index.d.ts +16 -3
  120. package/dist/sqlite/index.js +16 -3
  121. package/dist/sqlite/migrations.d.ts +26 -0
  122. package/dist/sqlite/migrations.js +63 -0
  123. package/dist/sqlite/operations.d.ts +14 -3
  124. package/dist/sqlite/operations.js +42 -15
  125. package/dist/sqlite/store.d.ts +9 -2
  126. package/dist/sqlite/store.js +13 -3
  127. package/dist/sqlite/vectors.d.ts +11 -2
  128. package/dist/sqlite/vectors.js +13 -2
  129. package/dist/store/memory.d.ts +10 -0
  130. package/dist/store/memory.js +10 -0
  131. package/dist/store/tenant.d.ts +12 -0
  132. package/dist/store/tenant.js +29 -0
  133. package/dist/tenancy/index.d.ts +113 -0
  134. package/dist/tenancy/index.js +233 -0
  135. package/dist/tracing/instrument.js +2 -0
  136. package/dist/types/config.d.ts +24 -2
  137. package/dist/types/graph.d.ts +49 -1
  138. package/dist/types/messages.d.ts +41 -2
  139. package/dist/types/operations.d.ts +6 -0
  140. package/dist/types/principal.d.ts +21 -0
  141. package/dist/types/principal.js +1 -0
  142. package/dist/types/response.d.ts +20 -0
  143. package/dist/types/server.d.ts +12 -10
  144. package/dist/utils/schema-migrations.d.ts +97 -0
  145. package/dist/utils/schema-migrations.js +91 -0
  146. package/dist/utils/signals.d.ts +17 -0
  147. package/dist/utils/signals.js +26 -0
  148. package/dist/utils/tenant.d.ts +25 -0
  149. package/dist/utils/tenant.js +30 -0
  150. package/dist/version.d.ts +1 -1
  151. package/dist/version.js +1 -1
  152. package/dist-cjs/agent/capabilities.d.ts +39 -0
  153. package/dist-cjs/agent/capabilities.js +41 -0
  154. package/dist-cjs/agent/create-agent.d.ts +61 -11
  155. package/dist-cjs/agent/create-agent.js +136 -22
  156. package/dist-cjs/agent/index.d.ts +3 -2
  157. package/dist-cjs/agent/index.js +4 -4
  158. package/dist-cjs/agent/middleware/approval.d.ts +26 -0
  159. package/dist-cjs/agent/middleware/approval.js +26 -0
  160. package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  161. package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
  162. package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
  163. package/dist-cjs/agent/middleware/context-editor.js +70 -0
  164. package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
  165. package/dist-cjs/agent/middleware/filesystem.js +86 -0
  166. package/dist-cjs/agent/middleware/index.d.ts +19 -0
  167. package/dist-cjs/agent/middleware/index.js +28 -0
  168. package/dist-cjs/agent/middleware/limits.d.ts +42 -0
  169. package/dist-cjs/agent/middleware/limits.js +57 -0
  170. package/dist-cjs/agent/middleware/models.d.ts +48 -0
  171. package/dist-cjs/agent/middleware/models.js +48 -0
  172. package/dist-cjs/agent/middleware/pii.d.ts +67 -0
  173. package/dist-cjs/agent/middleware/pii.js +178 -0
  174. package/dist-cjs/agent/middleware/retry.d.ts +66 -0
  175. package/dist-cjs/agent/middleware/retry.js +63 -0
  176. package/dist-cjs/agent/middleware/shared.d.ts +41 -0
  177. package/dist-cjs/agent/middleware/shared.js +83 -0
  178. package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
  179. package/dist-cjs/agent/middleware/tool-selector.js +109 -0
  180. package/dist-cjs/agent/permissions.d.ts +113 -0
  181. package/dist-cjs/agent/permissions.js +189 -0
  182. package/dist-cjs/agent/sandbox.d.ts +151 -0
  183. package/dist-cjs/agent/sandbox.js +297 -0
  184. package/dist-cjs/agent/tool.d.ts +8 -3
  185. package/dist-cjs/agent/tool.js +14 -3
  186. package/dist-cjs/cache/memory-cache.d.ts +8 -0
  187. package/dist-cjs/cache/memory-cache.js +9 -0
  188. package/dist-cjs/cache/semantic-cache.d.ts +6 -3
  189. package/dist-cjs/cache/semantic-cache.js +5 -2
  190. package/dist-cjs/cli/args.js +3 -0
  191. package/dist-cjs/cli/db.d.ts +16 -4
  192. package/dist-cjs/cli/db.js +200 -20
  193. package/dist-cjs/cli/doctor.d.ts +17 -0
  194. package/dist-cjs/cli/doctor.js +56 -0
  195. package/dist-cjs/core/lifecycle.js +11 -6
  196. package/dist-cjs/core/nexus.d.ts +6 -0
  197. package/dist-cjs/core/nexus.js +103 -22
  198. package/dist-cjs/deep-agent/index.d.ts +109 -0
  199. package/dist-cjs/deep-agent/index.js +219 -0
  200. package/dist-cjs/doctor/index.d.ts +90 -0
  201. package/dist-cjs/doctor/index.js +276 -0
  202. package/dist-cjs/graph/functional.d.ts +89 -7
  203. package/dist-cjs/graph/functional.js +155 -35
  204. package/dist-cjs/graph/graph.d.ts +15 -5
  205. package/dist-cjs/graph/graph.js +95 -22
  206. package/dist-cjs/graph/lint.d.ts +4 -1
  207. package/dist-cjs/graph/lint.js +35 -0
  208. package/dist-cjs/images/evals.js +4 -2
  209. package/dist-cjs/operations/adapters.d.ts +52 -4
  210. package/dist-cjs/operations/adapters.js +193 -6
  211. package/dist-cjs/operations/runner.js +1 -0
  212. package/dist-cjs/operations/stats.js +8 -1
  213. package/dist-cjs/operations/store.d.ts +5 -1
  214. package/dist-cjs/operations/store.js +7 -0
  215. package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
  216. package/dist-cjs/ops/circuit-breaker.js +4 -0
  217. package/dist-cjs/ops/circuit-store.d.ts +13 -2
  218. package/dist-cjs/ops/circuit-store.js +68 -0
  219. package/dist-cjs/ops/circuit-sync.d.ts +17 -0
  220. package/dist-cjs/ops/circuit-sync.js +87 -0
  221. package/dist-cjs/ops/rate-limiter.d.ts +2 -0
  222. package/dist-cjs/ops/rate-limiter.js +2 -0
  223. package/dist-cjs/postgres/circuits.d.ts +21 -4
  224. package/dist-cjs/postgres/circuits.js +98 -3
  225. package/dist-cjs/postgres/client.d.ts +0 -2
  226. package/dist-cjs/postgres/client.js +0 -5
  227. package/dist-cjs/postgres/evaluate.d.ts +16 -4
  228. package/dist-cjs/postgres/evaluate.js +50 -4
  229. package/dist-cjs/postgres/fulltext.d.ts +67 -0
  230. package/dist-cjs/postgres/fulltext.js +129 -0
  231. package/dist-cjs/postgres/index.d.ts +32 -11
  232. package/dist-cjs/postgres/index.js +69 -20
  233. package/dist-cjs/postgres/migrations.d.ts +60 -0
  234. package/dist-cjs/postgres/migrations.js +129 -0
  235. package/dist-cjs/postgres/operations.d.ts +14 -3
  236. package/dist-cjs/postgres/operations.js +78 -14
  237. package/dist-cjs/postgres/prompts.d.ts +11 -2
  238. package/dist-cjs/postgres/prompts.js +47 -2
  239. package/dist-cjs/postgres/rollups.d.ts +35 -0
  240. package/dist-cjs/postgres/rollups.js +168 -0
  241. package/dist-cjs/postgres/store.d.ts +11 -2
  242. package/dist-cjs/postgres/store.js +47 -2
  243. package/dist-cjs/postgres/traces.d.ts +13 -3
  244. package/dist-cjs/postgres/traces.js +58 -2
  245. package/dist-cjs/postgres/vectors.d.ts +11 -2
  246. package/dist-cjs/postgres/vectors.js +47 -2
  247. package/dist-cjs/rag/elasticsearch.d.ts +71 -0
  248. package/dist-cjs/rag/elasticsearch.js +135 -0
  249. package/dist-cjs/rag/pipeline.d.ts +140 -0
  250. package/dist-cjs/rag/pipeline.js +191 -0
  251. package/dist-cjs/rag/rerankers.d.ts +92 -0
  252. package/dist-cjs/rag/rerankers.js +107 -0
  253. package/dist-cjs/rag/retrievers.d.ts +12 -1
  254. package/dist-cjs/router/auto-router.d.ts +34 -7
  255. package/dist-cjs/router/auto-router.js +201 -68
  256. package/dist-cjs/server/assistant.d.ts +9 -1
  257. package/dist-cjs/server/assistant.js +10 -0
  258. package/dist-cjs/server/auth.d.ts +250 -0
  259. package/dist-cjs/server/auth.js +402 -0
  260. package/dist-cjs/server/errors.d.ts +12 -0
  261. package/dist-cjs/server/errors.js +9 -1
  262. package/dist-cjs/server/index.d.ts +1 -1
  263. package/dist-cjs/server/index.js +2 -1
  264. package/dist-cjs/server/runs.d.ts +24 -1
  265. package/dist-cjs/server/runs.js +89 -13
  266. package/dist-cjs/sqlite/index.d.ts +16 -3
  267. package/dist-cjs/sqlite/index.js +31 -10
  268. package/dist-cjs/sqlite/migrations.d.ts +26 -0
  269. package/dist-cjs/sqlite/migrations.js +71 -0
  270. package/dist-cjs/sqlite/operations.d.ts +14 -3
  271. package/dist-cjs/sqlite/operations.js +76 -15
  272. package/dist-cjs/sqlite/store.d.ts +9 -2
  273. package/dist-cjs/sqlite/store.js +47 -3
  274. package/dist-cjs/sqlite/vectors.d.ts +11 -2
  275. package/dist-cjs/sqlite/vectors.js +47 -2
  276. package/dist-cjs/store/memory.d.ts +10 -0
  277. package/dist-cjs/store/memory.js +10 -0
  278. package/dist-cjs/store/tenant.d.ts +12 -0
  279. package/dist-cjs/store/tenant.js +33 -0
  280. package/dist-cjs/tenancy/index.d.ts +113 -0
  281. package/dist-cjs/tenancy/index.js +250 -0
  282. package/dist-cjs/tracing/instrument.js +2 -0
  283. package/dist-cjs/types/config.d.ts +24 -2
  284. package/dist-cjs/types/graph.d.ts +49 -1
  285. package/dist-cjs/types/messages.d.ts +41 -2
  286. package/dist-cjs/types/operations.d.ts +6 -0
  287. package/dist-cjs/types/principal.d.ts +21 -0
  288. package/dist-cjs/types/principal.js +2 -0
  289. package/dist-cjs/types/response.d.ts +20 -0
  290. package/dist-cjs/types/server.d.ts +12 -10
  291. package/dist-cjs/utils/schema-migrations.d.ts +97 -0
  292. package/dist-cjs/utils/schema-migrations.js +102 -0
  293. package/dist-cjs/utils/signals.d.ts +17 -0
  294. package/dist-cjs/utils/signals.js +29 -0
  295. package/dist-cjs/utils/tenant.d.ts +25 -0
  296. package/dist-cjs/utils/tenant.js +37 -0
  297. package/dist-cjs/version.d.ts +1 -1
  298. package/dist-cjs/version.js +1 -1
  299. package/llms.txt +6 -4
  300. package/package.json +188 -2
@@ -0,0 +1,151 @@
1
+ /**
2
+ * The sandbox contract: where an agent's commands and code run, and where its files live.
3
+ *
4
+ * The package ships the contract and a conformance suite, not the isolation: a container, a VM, a
5
+ * remote interpreter, or a hosted sandbox fills the `Sandbox` interface. `processSandbox()` is a
6
+ * reference for development, which runs commands as ordinary processes; it says so in its
7
+ * `isolation`, and is never a security boundary.
8
+ */
9
+ import type { ToolDefinition } from '../types/messages.js';
10
+ /** Options for one command. */
11
+ export interface SandboxExecOptions {
12
+ /** The working directory, inside the sandbox. Defaults to its root. */
13
+ cwd?: string;
14
+ /** Environment variables for the command. The host's own are never passed through. */
15
+ env?: Record<string, string>;
16
+ /** Sent to the command's standard input. */
17
+ stdin?: string;
18
+ /** Stops the command after this many milliseconds. */
19
+ timeoutMs?: number;
20
+ /** Stops the command when aborted. */
21
+ signal?: AbortSignal;
22
+ }
23
+ /** What a command did. */
24
+ export interface SandboxExecResult {
25
+ /** Its exit code, or `null` when it was stopped. */
26
+ exitCode: number | null;
27
+ /** What it wrote to standard output. */
28
+ stdout: string;
29
+ /** What it wrote to standard error. */
30
+ stderr: string;
31
+ /** True when it was stopped by its timeout. */
32
+ timedOut: boolean;
33
+ /** How long it ran, in milliseconds. */
34
+ durationMs: number;
35
+ }
36
+ /** What a sandbox claims to isolate. The conformance suite checks the claims it can observe. */
37
+ export interface SandboxIsolation {
38
+ /** Commands cannot read or write the host's files. */
39
+ filesystem: boolean;
40
+ /** Commands cannot reach the network, or only what the sandbox allows. */
41
+ network: boolean;
42
+ /** Commands cannot see or signal the host's processes. */
43
+ processes: boolean;
44
+ }
45
+ /** Where an agent's commands, code, and files go. The application provides one. */
46
+ export interface Sandbox {
47
+ /** Names the sandbox in reports. */
48
+ readonly name: string;
49
+ /** What it isolates. */
50
+ readonly isolation: SandboxIsolation;
51
+ /** Runs a command line in the sandbox. */
52
+ exec(command: string, options?: SandboxExecOptions): Promise<SandboxExecResult>;
53
+ /** Reads a file, by a path inside the sandbox. */
54
+ readFile(file: string): Promise<string>;
55
+ /** Writes a file, creating its directory. */
56
+ writeFile(file: string, content: string): Promise<void>;
57
+ /** Lists a directory's entries. */
58
+ listFiles?(directory?: string): Promise<string[]>;
59
+ /** Removes a file or directory. */
60
+ removeFile?(file: string): Promise<void>;
61
+ /** Releases the sandbox. */
62
+ dispose?(): Promise<void>;
63
+ }
64
+ /** Raised when a path would leave the sandbox. */
65
+ export declare class SandboxPathError extends Error {
66
+ /** The path that was refused. */
67
+ readonly path: string;
68
+ /** Always `SANDBOX_PATH`. */
69
+ readonly code = "SANDBOX_PATH";
70
+ constructor(
71
+ /** The path that was refused. */
72
+ path: string);
73
+ }
74
+ /** Options for `processSandbox()`. */
75
+ export interface ProcessSandboxOptions {
76
+ /** The directory commands run in and files live under. Created when missing. */
77
+ root: string;
78
+ /** Environment variables every command gets, on top of a minimal `PATH`. */
79
+ env?: Record<string, string>;
80
+ /** Timeout for a command that sets none. Defaults to 30 seconds. */
81
+ timeoutMs?: number;
82
+ /** Output kept per stream, in bytes. Defaults to 1 MB. */
83
+ maxOutputBytes?: number;
84
+ }
85
+ /**
86
+ * A sandbox that runs commands as ordinary processes in a directory, for development and tests only.
87
+ *
88
+ * Its file methods refuse any path outside `root`, and commands get only `PATH` and the variables
89
+ * given, never the host's environment. But a command it runs can read and write anything the user
90
+ * can, and reach any host: `isolation` says so. Use a container or a VM in production.
91
+ */
92
+ export declare function processSandbox(options: ProcessSandboxOptions): Sandbox;
93
+ /** Options for `sandboxTools()`. */
94
+ export interface SandboxToolsOptions {
95
+ /** Offer `run_command`. Defaults to true. */
96
+ shell?: boolean;
97
+ /** Offer `read_file`, `write_file`, and `list_files`. Defaults to true. */
98
+ files?: boolean;
99
+ /**
100
+ * The path a permission policy sees the sandbox's files under. Defaults to `/workspace`, so a
101
+ * policy granting `/workspace/**` grants the sandbox.
102
+ */
103
+ mount?: string;
104
+ /** Timeout for each command. */
105
+ timeoutMs?: number;
106
+ }
107
+ /**
108
+ * Tools that act through a sandbox, each declaring exactly what one call does: `run_command`
109
+ * declares `shell:<the command>`, and the file tools declare `filesystem:read` or
110
+ * `filesystem:write` with the path under `mount`. A permission policy can therefore grant a
111
+ * workspace, a few commands, and nothing else.
112
+ */
113
+ export declare function sandboxTools(sandbox: Sandbox, options?: SandboxToolsOptions): ToolDefinition[];
114
+ /** One conformance check's result. */
115
+ export interface SandboxConformanceCheck {
116
+ /** What was checked. */
117
+ name: string;
118
+ /** Whether it held. */
119
+ ok: boolean;
120
+ /** What went wrong, when it did not. */
121
+ detail?: string;
122
+ }
123
+ /** What `runSandboxConformance()` found. */
124
+ export interface SandboxConformanceReport {
125
+ /** The sandbox's name. */
126
+ sandbox: string;
127
+ /** True when every check held. */
128
+ passed: boolean;
129
+ /** Every check, in order. */
130
+ checks: SandboxConformanceCheck[];
131
+ }
132
+ /** Options for `runSandboxConformance()`. */
133
+ export interface SandboxConformanceOptions {
134
+ /** The command that runs JavaScript inside the sandbox. Defaults to `node`. */
135
+ node?: string;
136
+ /** The timeout the timeout check sets, in milliseconds. Defaults to 300. */
137
+ timeoutMs?: number;
138
+ }
139
+ /**
140
+ * Checks a sandbox against the contract, without a test framework, so any sandbox — a container
141
+ * runner, a VM, a hosted service — can prove itself the same way:
142
+ *
143
+ * - commands report their output, errors, and exit code, take standard input and the variables
144
+ * given, and never see the host's environment;
145
+ * - a command past its timeout is stopped and reported as timed out;
146
+ * - commands run where the files are, and files round-trip;
147
+ * - a path outside the sandbox is refused.
148
+ *
149
+ * Every check runs on a fresh sandbox from `create`, which is disposed afterwards.
150
+ */
151
+ export declare function runSandboxConformance(create: () => Sandbox | Promise<Sandbox>, options?: SandboxConformanceOptions): Promise<SandboxConformanceReport>;
@@ -0,0 +1,257 @@
1
+ export class SandboxPathError extends Error {
2
+ path;
3
+ code = 'SANDBOX_PATH';
4
+ constructor(path) {
5
+ super(`"${path}" is outside the sandbox`);
6
+ this.path = path;
7
+ this.name = 'SandboxPathError';
8
+ }
9
+ }
10
+ export function processSandbox(options) {
11
+ const ready = (async () => {
12
+ const [{ spawn }, fs, { dirname, isAbsolute, relative, resolve }] = await Promise.all([
13
+ import('node:child_process'),
14
+ import('node:fs/promises'),
15
+ import('node:path'),
16
+ ]);
17
+ const root = resolve(options.root);
18
+ await fs.mkdir(root, { recursive: true });
19
+ const inside = (file) => {
20
+ const resolved = resolve(root, file);
21
+ const fromRoot = relative(root, resolved);
22
+ if (fromRoot.startsWith('..') || isAbsolute(fromRoot))
23
+ throw new SandboxPathError(file);
24
+ return resolved;
25
+ };
26
+ return { spawn, fs, dirname, root, inside };
27
+ })();
28
+ ready.catch(() => undefined);
29
+ const maxOutput = options.maxOutputBytes ?? 1_048_576;
30
+ return {
31
+ name: 'process (development only)',
32
+ isolation: { filesystem: false, network: false, processes: false },
33
+ async exec(command, execOptions = {}) {
34
+ const { spawn, root, inside } = await ready;
35
+ const started = Date.now();
36
+ const cwd = inside(execOptions.cwd ?? '.');
37
+ const env = {
38
+ PATH: process.env.PATH ?? '',
39
+ ...(process.platform === 'win32' && process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {}),
40
+ HOME: root,
41
+ ...options.env,
42
+ ...execOptions.env,
43
+ };
44
+ return new Promise((resolve) => {
45
+ const child = spawn(command, {
46
+ cwd,
47
+ env,
48
+ shell: true,
49
+ windowsHide: true,
50
+ detached: process.platform !== 'win32',
51
+ });
52
+ let stdout = '';
53
+ let stderr = '';
54
+ let timedOut = false;
55
+ let settled = false;
56
+ const keep = (current, chunk) => current.length >= maxOutput ? current : (current + chunk.toString('utf8')).slice(0, maxOutput);
57
+ child.stdout?.on('data', (chunk) => {
58
+ stdout = keep(stdout, chunk);
59
+ });
60
+ child.stderr?.on('data', (chunk) => {
61
+ stderr = keep(stderr, chunk);
62
+ });
63
+ const stop = () => killTree(spawn, child.pid);
64
+ const limit = execOptions.timeoutMs ?? options.timeoutMs ?? 30_000;
65
+ const timer = setTimeout(() => {
66
+ timedOut = true;
67
+ stop();
68
+ }, limit);
69
+ const onAbort = () => stop();
70
+ execOptions.signal?.addEventListener('abort', onAbort, { once: true });
71
+ const finish = (exitCode) => {
72
+ if (settled)
73
+ return;
74
+ settled = true;
75
+ clearTimeout(timer);
76
+ execOptions.signal?.removeEventListener('abort', onAbort);
77
+ resolve({ exitCode: timedOut ? null : exitCode, stdout, stderr, timedOut, durationMs: Date.now() - started });
78
+ };
79
+ child.on('error', (error) => {
80
+ stderr += error.message;
81
+ finish(null);
82
+ });
83
+ child.on('close', (code) => finish(code));
84
+ if (execOptions.stdin !== undefined)
85
+ child.stdin?.end(execOptions.stdin);
86
+ else
87
+ child.stdin?.end();
88
+ });
89
+ },
90
+ async readFile(file) {
91
+ const { fs, inside } = await ready;
92
+ return fs.readFile(inside(file), 'utf8');
93
+ },
94
+ async writeFile(file, content) {
95
+ const { fs, dirname, inside } = await ready;
96
+ const target = inside(file);
97
+ await fs.mkdir(dirname(target), { recursive: true });
98
+ await fs.writeFile(target, content, 'utf8');
99
+ },
100
+ async listFiles(directory = '.') {
101
+ const { fs, inside } = await ready;
102
+ return (await fs.readdir(inside(directory))).sort();
103
+ },
104
+ async removeFile(file) {
105
+ const { fs, root, inside } = await ready;
106
+ const target = inside(file);
107
+ if (target === root)
108
+ throw new SandboxPathError(file);
109
+ await fs.rm(target, { recursive: true, force: true });
110
+ },
111
+ };
112
+ }
113
+ function killTree(spawn, pid) {
114
+ if (pid === undefined)
115
+ return;
116
+ try {
117
+ if (process.platform === 'win32') {
118
+ spawn('taskkill', ['/pid', String(pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
119
+ }
120
+ else {
121
+ process.kill(-pid, 'SIGKILL');
122
+ }
123
+ }
124
+ catch {
125
+ }
126
+ }
127
+ export function sandboxTools(sandbox, options = {}) {
128
+ const mount = (options.mount ?? '/workspace').replace(/\/+$/, '');
129
+ const local = (file) => {
130
+ const unified = String(file ?? '.').replace(/\\/g, '/');
131
+ const relative = unified === mount ? '.' : unified.startsWith(`${mount}/`) ? unified.slice(mount.length + 1) : unified;
132
+ return relative.replace(/^\/+/, '') || '.';
133
+ };
134
+ const mounted = (file) => `${mount}/${local(file)}`;
135
+ const tools = [];
136
+ if (options.shell !== false) {
137
+ tools.push({
138
+ name: 'run_command',
139
+ description: 'Runs a shell command in the sandbox and returns its exit code and output.',
140
+ parameters: {
141
+ type: 'object',
142
+ properties: { command: { type: 'string', description: 'The command line to run' } },
143
+ required: ['command'],
144
+ },
145
+ capabilities: (args) => [`shell:${String(args.command ?? '')}`],
146
+ execute: async (args) => {
147
+ const result = await sandbox.exec(String(args.command ?? ''), {
148
+ ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
149
+ });
150
+ return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, timedOut: result.timedOut };
151
+ },
152
+ });
153
+ }
154
+ if (options.files !== false) {
155
+ tools.push({
156
+ name: 'read_file',
157
+ description: `Reads a text file in the sandbox. Paths are relative to ${mount}.`,
158
+ parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
159
+ capabilities: (args) => [`filesystem:read:${mounted(args.path)}`],
160
+ execute: async (args) => sandbox.readFile(local(args.path)),
161
+ }, {
162
+ name: 'write_file',
163
+ description: `Writes a text file in the sandbox, creating its directory. Paths are relative to ${mount}.`,
164
+ parameters: {
165
+ type: 'object',
166
+ properties: { path: { type: 'string' }, content: { type: 'string' } },
167
+ required: ['path', 'content'],
168
+ },
169
+ capabilities: (args) => [`filesystem:write:${mounted(args.path)}`],
170
+ execute: async (args) => {
171
+ await sandbox.writeFile(local(args.path), String(args.content ?? ''));
172
+ return { written: local(args.path) };
173
+ },
174
+ });
175
+ if (sandbox.listFiles) {
176
+ const list = sandbox.listFiles.bind(sandbox);
177
+ tools.push({
178
+ name: 'list_files',
179
+ description: 'Lists the entries of a directory in the sandbox.',
180
+ parameters: { type: 'object', properties: { path: { type: 'string' } } },
181
+ capabilities: (args) => [`filesystem:read:${mounted(args.path)}`],
182
+ execute: async (args) => list(local(args.path)),
183
+ });
184
+ }
185
+ }
186
+ return tools;
187
+ }
188
+ export async function runSandboxConformance(create, options = {}) {
189
+ const node = options.node ?? 'node';
190
+ const js = (code) => `${node} -e "${code}"`;
191
+ const checks = [];
192
+ let name = 'sandbox';
193
+ const check = async (label, run) => {
194
+ const sandbox = await create();
195
+ name = sandbox.name;
196
+ try {
197
+ const problem = await run(sandbox);
198
+ checks.push(problem ? { name: label, ok: false, detail: problem } : { name: label, ok: true });
199
+ }
200
+ catch (error) {
201
+ checks.push({ name: label, ok: false, detail: error instanceof Error ? error.message : String(error) });
202
+ }
203
+ finally {
204
+ await sandbox.dispose?.();
205
+ }
206
+ };
207
+ const expect = (actual, expected, what) => actual === expected ? undefined : `${what}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`;
208
+ await check('a command reports its output and a zero exit code', async (sandbox) => {
209
+ const result = await sandbox.exec(js("process.stdout.write('ok')"));
210
+ return expect(result.stdout, 'ok', 'stdout') ?? expect(result.exitCode, 0, 'exit code');
211
+ });
212
+ await check('a failing command reports its exit code and its errors', async (sandbox) => {
213
+ const result = await sandbox.exec(js("process.stderr.write('bad'); process.exit(3)"));
214
+ return expect(result.exitCode, 3, 'exit code') ?? expect(result.stderr, 'bad', 'stderr');
215
+ });
216
+ await check('a command reads its standard input', async (sandbox) => {
217
+ const result = await sandbox.exec(js('process.stdin.pipe(process.stdout)'), { stdin: 'piped' });
218
+ return expect(result.stdout, 'piped', 'stdout');
219
+ });
220
+ await check('a command gets the variables it is given, and none of the host', async (sandbox) => {
221
+ process.env.NEXUS_SANDBOX_HOST_SECRET = 'host-only';
222
+ try {
223
+ const result = await sandbox.exec(js("process.stdout.write((process.env.NEXUS_SANDBOX_GIVEN || '') + '|' + (process.env.NEXUS_SANDBOX_HOST_SECRET || ''))"), { env: { NEXUS_SANDBOX_GIVEN: 'given' } });
224
+ return expect(result.stdout, 'given|', 'environment');
225
+ }
226
+ finally {
227
+ delete process.env.NEXUS_SANDBOX_HOST_SECRET;
228
+ }
229
+ });
230
+ await check('a command past its timeout is stopped', async (sandbox) => {
231
+ const limit = options.timeoutMs ?? 300;
232
+ const result = await sandbox.exec(js('setTimeout(() => {}, 20000)'), { timeoutMs: limit });
233
+ if (!result.timedOut)
234
+ return 'the command was not reported as timed out';
235
+ if (result.durationMs > limit + 5_000)
236
+ return `the command ran ${result.durationMs} ms past a ${limit} ms timeout`;
237
+ return undefined;
238
+ });
239
+ await check('files round-trip, and commands run where they are', async (sandbox) => {
240
+ await sandbox.writeFile('conformance/note.txt', 'here');
241
+ const read = await sandbox.readFile('conformance/note.txt');
242
+ const seen = await sandbox.exec(js("process.stdout.write(require('fs').readFileSync('conformance/note.txt', 'utf8'))"));
243
+ const listed = sandbox.listFiles ? await sandbox.listFiles('conformance') : ['note.txt'];
244
+ return (expect(read, 'here', 'readFile') ??
245
+ expect(seen.stdout, 'here', 'a command reading the file') ??
246
+ (listed.includes('note.txt') ? undefined : `listFiles did not list note.txt: ${JSON.stringify(listed)}`));
247
+ });
248
+ await check('a path outside the sandbox is refused', async (sandbox) => {
249
+ for (const attempt of ['../outside.txt', 'conformance/../../outside.txt']) {
250
+ const refused = await sandbox.writeFile(attempt, 'escaped').then(() => false, () => true);
251
+ if (!refused)
252
+ return `writeFile("${attempt}") was allowed`;
253
+ }
254
+ return undefined;
255
+ });
256
+ return { sandbox: name, passed: checks.every((item) => item.ok), checks };
257
+ }
@@ -1,11 +1,14 @@
1
- import type { ContentPart, Message, ToolDefinition, ToolOutput } from '../types/messages.js';
1
+ import type { ContentPart, Message, ToolContext, ToolDefinition, ToolOutput } from '../types/messages.js';
2
2
  import type { ToolExecutionResult } from '../types/agent.js';
3
3
  /** Defines a tool the model can call, typing its arguments. */
4
4
  export declare function tool<TArgs extends Record<string, unknown> = Record<string, unknown>>(definition: {
5
5
  name: string;
6
6
  description: string;
7
7
  parameters: Record<string, unknown>;
8
- execute: (args: TArgs) => Promise<unknown> | unknown;
8
+ /** Runs the tool. In an agent, `context` says who the run is for and carries its signal and store. */
9
+ execute: (args: TArgs, context: ToolContext) => Promise<unknown> | unknown;
10
+ /** What the tool does to the world, for a permission policy; see `ToolDefinition.capabilities`. */
11
+ capabilities?: readonly string[] | ((args: TArgs) => readonly string[]);
9
12
  }): ToolDefinition;
10
13
  /**
11
14
  * Builds a tool's result as content: text, images, and asset references, in order. A string becomes
@@ -28,8 +31,10 @@ export declare class ToolExecutor {
28
31
  register(toolDefinition: ToolDefinition): this;
29
32
  /** Whether a tool is registered. */
30
33
  has(name: string): boolean;
34
+ /** A registered tool, or `undefined`. */
35
+ get(name: string): ToolDefinition | undefined;
31
36
  /** Every registered tool. */
32
37
  list(): ToolDefinition[];
33
38
  /** Runs a tool. An unknown name or a thrown error comes back as a failed result. */
34
- execute(name: string, args: Record<string, unknown>): Promise<ToolExecutionResult>;
39
+ execute(name: string, args: Record<string, unknown>, context?: ToolContext): Promise<ToolExecutionResult>;
35
40
  }
@@ -1,9 +1,17 @@
1
1
  export function tool(definition) {
2
+ const capabilities = definition.capabilities;
2
3
  return {
3
4
  name: definition.name,
4
5
  description: definition.description,
5
6
  parameters: definition.parameters,
6
- execute: async (args) => definition.execute(args),
7
+ execute: async (args, context) => definition.execute(args, context ?? {}),
8
+ ...(capabilities === undefined
9
+ ? {}
10
+ : {
11
+ capabilities: typeof capabilities === 'function'
12
+ ? (args) => capabilities(args)
13
+ : capabilities,
14
+ }),
7
15
  };
8
16
  }
9
17
  export function toolOutput(...content) {
@@ -37,10 +45,13 @@ export class ToolExecutor {
37
45
  has(name) {
38
46
  return this.tools.has(name);
39
47
  }
48
+ get(name) {
49
+ return this.tools.get(name);
50
+ }
40
51
  list() {
41
52
  return [...this.tools.values()];
42
53
  }
43
- async execute(name, args) {
54
+ async execute(name, args, context) {
44
55
  const toolDefinition = this.tools.get(name);
45
56
  if (!toolDefinition) {
46
57
  return { ok: false, error: `Tool "${name}" is not registered` };
@@ -49,7 +60,7 @@ export class ToolExecutor {
49
60
  return { ok: false, error: `Tool "${name}" does not have an execute function` };
50
61
  }
51
62
  try {
52
- const result = await toolDefinition.execute(args);
63
+ const result = await toolDefinition.execute(args, context);
53
64
  return { ok: true, result };
54
65
  }
55
66
  catch (err) {
@@ -15,6 +15,14 @@ export declare class MemoryCache<T> {
15
15
  constructor(maxEntries?: number);
16
16
  /** Returns a value, or `undefined` for a miss or an expired entry. A hit counts as recent use. */
17
17
  get(key: string): T | undefined;
18
+ /**
19
+ * Like `get()`, also saying whether an entry was there but had expired, which a caller can report
20
+ * as stale rather than missing. The expired entry is removed either way.
21
+ */
22
+ lookup(key: string): {
23
+ value?: T;
24
+ stale: boolean;
25
+ };
18
26
  /** Stores a value. Defaults to 5 minutes. */
19
27
  set(key: string, value: T, ttlSeconds?: number): void;
20
28
  /** Removes every entry. */
@@ -16,6 +16,15 @@ export class MemoryCache {
16
16
  this.entries.set(key, entry);
17
17
  return entry.value;
18
18
  }
19
+ lookup(key) {
20
+ const entry = this.entries.get(key);
21
+ if (entry && entry.expiresAt < Date.now()) {
22
+ this.entries.delete(key);
23
+ return { stale: true };
24
+ }
25
+ const value = this.get(key);
26
+ return value === undefined ? { stale: false } : { value, stale: false };
27
+ }
19
28
  set(key, value, ttlSeconds = 300) {
20
29
  if (this.entries.size >= this.maxEntries) {
21
30
  const oldest = this.entries.keys().next().value;
@@ -25,10 +25,13 @@ export declare class SemanticCache {
25
25
  constructor(options?: SemanticCacheOptions);
26
26
  /**
27
27
  * The cached response for the most similar earlier request above the threshold, or `undefined`.
28
+ * Only entries stored under the same `scope` are compared: the client passes the request's tenant
29
+ * and cache namespace, so one tenant is never answered from another's entry however alike the
30
+ * questions are.
28
31
  */
29
- get(request: CompletionRequest): Promise<NexusResponse | undefined>;
30
- /** Caches a response under its request's embedding. */
31
- set(key: string, request: CompletionRequest, response: NexusResponse): Promise<void>;
32
+ get(request: CompletionRequest, scope?: string): Promise<NexusResponse | undefined>;
33
+ /** Caches a response under its request's embedding, in a `scope` lookups must match. */
34
+ set(key: string, request: CompletionRequest, response: NexusResponse, scope?: string): Promise<void>;
32
35
  /** Removes every entry. */
33
36
  clear(): void;
34
37
  }
@@ -7,7 +7,7 @@ export class SemanticCache {
7
7
  this.options = options;
8
8
  this.embed = options.embed || createHashEmbeddings;
9
9
  }
10
- async get(request) {
10
+ async get(request, scope = '') {
11
11
  if (!this.options.enabled)
12
12
  return undefined;
13
13
  const text = requestToSearchText(request);
@@ -17,6 +17,8 @@ export class SemanticCache {
17
17
  let best;
18
18
  this.entries = this.entries.filter((entry) => entry.expiresAt > now);
19
19
  for (const entry of this.entries) {
20
+ if (entry.scope !== scope)
21
+ continue;
20
22
  const score = cosineSimilarity(embedding, entry.embedding);
21
23
  if (score >= threshold && (!best || score > best.score)) {
22
24
  best = { entry, score };
@@ -37,7 +39,7 @@ export class SemanticCache {
37
39
  },
38
40
  };
39
41
  }
40
- async set(key, request, response) {
42
+ async set(key, request, response, scope = '') {
41
43
  if (!this.options.enabled)
42
44
  return;
43
45
  const text = requestToSearchText(request);
@@ -47,6 +49,7 @@ export class SemanticCache {
47
49
  this.entries.shift();
48
50
  this.entries.push({
49
51
  key,
52
+ scope,
50
53
  text,
51
54
  embedding,
52
55
  response,
package/dist/cli/args.js CHANGED
@@ -5,12 +5,15 @@ export const BOOLEAN_FLAGS = new Set([
5
5
  'allow-dataset-mismatch',
6
6
  'check',
7
7
  'densify',
8
+ 'dry-run',
8
9
  'fail',
9
10
  'fail-on-regression',
10
11
  'help',
11
12
  'json',
12
13
  'print',
14
+ 'record',
13
15
  'reveal-values',
16
+ 'strict',
14
17
  'write',
15
18
  ]);
16
19
  export function parseArgs(args) {
package/dist/cli/db.d.ts CHANGED
@@ -1,8 +1,20 @@
1
1
  import { type ParsedArgs } from './args.js';
2
2
  /**
3
- * `nexus db sql` prints the Postgres schema for the chosen adapters.
3
+ * `nexus db sql | status | migrate`.
4
4
  *
5
- * Printed rather than applied, so the CLI needs no database driver and the schema goes through
6
- * whatever review and migration tooling the application already has: `nexus db sql | psql "$URL"`.
5
+ * `sql` prints the Postgres schema, so the CLI needs no driver and the schema can go through the
6
+ * application's own review and tooling: `nexus db sql | psql "$URL"`. `status` and `migrate` reach
7
+ * the database through a module the application writes, which exports its own client:
8
+ *
9
+ * ```js
10
+ * // db.mjs
11
+ * import pg from 'pg';
12
+ * export const client = new pg.Pool({ connectionString: process.env.DATABASE_URL });
13
+ * export const close = () => client.end();
14
+ * ```
15
+ *
16
+ * The module may also export `dialect` (`postgres` or `sqlite`), a `transaction` function (see
17
+ * `PostgresMigrateOptions`), and `migrations`, a list replacing the bundled ones, for stores on
18
+ * custom tables.
7
19
  */
8
- export declare function runDbCommand(subcommand: string | undefined, { flags }: ParsedArgs): Promise<void>;
20
+ export declare function runDbCommand(subcommand: string | undefined, args: ParsedArgs): Promise<void>;