nexus-ai-pro 2.20.2 → 2.22.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 (233) hide show
  1. package/API_STABILITY.md +93 -0
  2. package/CHANGELOG.md +208 -1
  3. package/README.md +11 -6
  4. package/SECURITY.md +16 -2
  5. package/dist/adapter-kit/gather.d.ts +76 -0
  6. package/dist/adapter-kit/gather.js +147 -0
  7. package/dist/adapter-kit/index.d.ts +1 -0
  8. package/dist/adapter-kit/index.js +1 -0
  9. package/dist/adapter-kit/record.d.ts +3 -1
  10. package/dist/adapter-kit/record.js +20 -5
  11. package/dist/agent/openapi.d.ts +94 -8
  12. package/dist/agent/openapi.js +528 -32
  13. package/dist/cli/adapters.d.ts +4 -3
  14. package/dist/cli/adapters.js +71 -2
  15. package/dist/cli/doctor.js +3 -0
  16. package/dist/cli.js +3 -1
  17. package/dist/core/config-builder.d.ts +1 -0
  18. package/dist/core/config-builder.js +1 -0
  19. package/dist/core/defaults.d.ts +34 -0
  20. package/dist/core/defaults.js +39 -0
  21. package/dist/core/secure-stream.js +4 -1
  22. package/dist/decisions/completion.d.ts +10 -7
  23. package/dist/decisions/completion.js +82 -8
  24. package/dist/decisions/core.d.ts +88 -43
  25. package/dist/decisions/core.js +115 -28
  26. package/dist/decisions/errors.d.ts +25 -0
  27. package/dist/decisions/errors.js +12 -0
  28. package/dist/decisions/index.d.ts +6 -5
  29. package/dist/decisions/index.js +2 -1
  30. package/dist/decisions/integrations.d.ts +14 -5
  31. package/dist/decisions/integrations.js +16 -1
  32. package/dist/decisions/mock.d.ts +5 -3
  33. package/dist/decisions/mock.js +4 -1
  34. package/dist/decisions/pictures.d.ts +43 -0
  35. package/dist/decisions/pictures.js +103 -0
  36. package/dist/decisions/typesafe.d.ts +33 -5
  37. package/dist/decisions/typesafe.js +121 -8
  38. package/dist/doctor/index.d.ts +6 -9
  39. package/dist/doctor/index.js +41 -0
  40. package/dist/embeddings/adapters.d.ts +1 -1
  41. package/dist/embeddings/adapters.js +3 -3
  42. package/dist/embeddings/batch-google.d.ts +27 -8
  43. package/dist/embeddings/batch-google.js +112 -36
  44. package/dist/embeddings/batch-mock.js +1 -1
  45. package/dist/embeddings/batch.js +10 -4
  46. package/dist/embeddings/engine.js +14 -13
  47. package/dist/embeddings/index.d.ts +1 -1
  48. package/dist/embeddings/inputs.d.ts +26 -3
  49. package/dist/embeddings/inputs.js +84 -5
  50. package/dist/embeddings/mock.js +1 -1
  51. package/dist/embeddings/models.js +39 -4
  52. package/dist/loaders/csv.d.ts +23 -1
  53. package/dist/loaders/csv.js +125 -72
  54. package/dist/loaders/index.d.ts +7 -2
  55. package/dist/loaders/input.d.ts +6 -0
  56. package/dist/loaders/input.js +42 -1
  57. package/dist/loaders/json.d.ts +14 -3
  58. package/dist/loaders/json.js +171 -20
  59. package/dist/loaders/web.d.ts +55 -0
  60. package/dist/loaders/web.js +20 -1
  61. package/dist/mcp/client.d.ts +54 -5
  62. package/dist/mcp/client.js +171 -20
  63. package/dist/mcp/http.d.ts +45 -0
  64. package/dist/mcp/http.js +233 -0
  65. package/dist/mcp/index.d.ts +4 -2
  66. package/dist/mcp/index.js +3 -1
  67. package/dist/mcp/protocol.d.ts +107 -0
  68. package/dist/mcp/sampling.d.ts +41 -0
  69. package/dist/mcp/sampling.js +52 -0
  70. package/dist/mcp/server.d.ts +96 -14
  71. package/dist/mcp/server.js +274 -59
  72. package/dist/models/data.js +20 -1
  73. package/dist/operations/adapters.d.ts +1 -0
  74. package/dist/operations/adapters.js +3 -1
  75. package/dist/protocols/a2a-grpc.js +10 -3
  76. package/dist/protocols/a2a-signing.d.ts +65 -0
  77. package/dist/protocols/a2a-signing.js +133 -0
  78. package/dist/protocols/a2a.d.ts +14 -0
  79. package/dist/protocols/a2a.js +60 -5
  80. package/dist/providers/cohere.js +16 -2
  81. package/dist/providers/google.js +7 -1
  82. package/dist/providers/ollama.d.ts +1 -1
  83. package/dist/providers/ollama.js +35 -27
  84. package/dist/providers/openai.js +19 -2
  85. package/dist/rag/pinecone.d.ts +63 -2
  86. package/dist/rag/pinecone.js +80 -0
  87. package/dist/realtime/conversation.js +18 -0
  88. package/dist/realtime/gemini-live.transport.d.ts +28 -3
  89. package/dist/realtime/gemini-live.transport.js +170 -32
  90. package/dist/realtime/session.d.ts +2 -0
  91. package/dist/realtime/session.js +19 -4
  92. package/dist/realtime/types.d.ts +35 -3
  93. package/dist/security/output-guard.js +4 -1
  94. package/dist/server/auth.d.ts +263 -2
  95. package/dist/server/auth.js +432 -5
  96. package/dist/server/deployments.d.ts +3 -2
  97. package/dist/server/deployments.js +6 -1
  98. package/dist/telephony/index.d.ts +1 -1
  99. package/dist/telephony/manager.d.ts +8 -1
  100. package/dist/telephony/manager.js +13 -0
  101. package/dist/telephony/providers/telnyx.d.ts +18 -5
  102. package/dist/telephony/providers/telnyx.js +44 -7
  103. package/dist/telephony/providers/twilio.d.ts +3 -1
  104. package/dist/telephony/providers/twilio.js +16 -0
  105. package/dist/tracing/stores.d.ts +2 -0
  106. package/dist/tracing/stores.js +1 -0
  107. package/dist/tracing/tracer.d.ts +5 -2
  108. package/dist/tracing/tracer.js +6 -3
  109. package/dist/types/embeddings.d.ts +47 -5
  110. package/dist/types/messages.d.ts +7 -0
  111. package/dist/types/principal.d.ts +1 -1
  112. package/dist/types/response.d.ts +5 -0
  113. package/dist/types/telephony.d.ts +38 -0
  114. package/dist/types/tracing.d.ts +6 -0
  115. package/dist/version.d.ts +1 -1
  116. package/dist/version.js +1 -1
  117. package/dist/voice/providers/google.d.ts +44 -0
  118. package/dist/voice/providers/google.js +215 -0
  119. package/dist-cjs/adapter-kit/gather.d.ts +76 -0
  120. package/dist-cjs/adapter-kit/gather.js +152 -0
  121. package/dist-cjs/adapter-kit/index.d.ts +1 -0
  122. package/dist-cjs/adapter-kit/index.js +4 -1
  123. package/dist-cjs/adapter-kit/record.d.ts +3 -1
  124. package/dist-cjs/adapter-kit/record.js +20 -5
  125. package/dist-cjs/agent/openapi.d.ts +94 -8
  126. package/dist-cjs/agent/openapi.js +530 -32
  127. package/dist-cjs/cli/adapters.d.ts +4 -3
  128. package/dist-cjs/cli/adapters.js +70 -1
  129. package/dist-cjs/cli/doctor.js +3 -0
  130. package/dist-cjs/core/config-builder.d.ts +1 -0
  131. package/dist-cjs/core/config-builder.js +5 -1
  132. package/dist-cjs/core/defaults.d.ts +34 -0
  133. package/dist-cjs/core/defaults.js +44 -0
  134. package/dist-cjs/core/secure-stream.js +4 -1
  135. package/dist-cjs/decisions/completion.d.ts +10 -7
  136. package/dist-cjs/decisions/completion.js +81 -7
  137. package/dist-cjs/decisions/core.d.ts +88 -43
  138. package/dist-cjs/decisions/core.js +134 -46
  139. package/dist-cjs/decisions/errors.d.ts +25 -0
  140. package/dist-cjs/decisions/errors.js +16 -0
  141. package/dist-cjs/decisions/index.d.ts +6 -5
  142. package/dist-cjs/decisions/index.js +5 -1
  143. package/dist-cjs/decisions/integrations.d.ts +14 -5
  144. package/dist-cjs/decisions/integrations.js +16 -1
  145. package/dist-cjs/decisions/mock.d.ts +5 -3
  146. package/dist-cjs/decisions/mock.js +4 -1
  147. package/dist-cjs/decisions/pictures.d.ts +43 -0
  148. package/dist-cjs/decisions/pictures.js +109 -0
  149. package/dist-cjs/decisions/typesafe.d.ts +33 -5
  150. package/dist-cjs/decisions/typesafe.js +126 -13
  151. package/dist-cjs/doctor/index.d.ts +6 -9
  152. package/dist-cjs/doctor/index.js +41 -0
  153. package/dist-cjs/embeddings/adapters.d.ts +1 -1
  154. package/dist-cjs/embeddings/adapters.js +3 -3
  155. package/dist-cjs/embeddings/batch-google.d.ts +27 -8
  156. package/dist-cjs/embeddings/batch-google.js +112 -36
  157. package/dist-cjs/embeddings/batch-mock.js +1 -1
  158. package/dist-cjs/embeddings/batch.js +9 -3
  159. package/dist-cjs/embeddings/engine.js +13 -12
  160. package/dist-cjs/embeddings/index.d.ts +1 -1
  161. package/dist-cjs/embeddings/inputs.d.ts +26 -3
  162. package/dist-cjs/embeddings/inputs.js +89 -5
  163. package/dist-cjs/embeddings/mock.js +1 -1
  164. package/dist-cjs/embeddings/models.js +39 -4
  165. package/dist-cjs/loaders/csv.d.ts +23 -1
  166. package/dist-cjs/loaders/csv.js +126 -71
  167. package/dist-cjs/loaders/index.d.ts +7 -2
  168. package/dist-cjs/loaders/input.d.ts +6 -0
  169. package/dist-cjs/loaders/input.js +43 -1
  170. package/dist-cjs/loaders/json.d.ts +14 -3
  171. package/dist-cjs/loaders/json.js +170 -19
  172. package/dist-cjs/loaders/web.d.ts +55 -0
  173. package/dist-cjs/loaders/web.js +21 -1
  174. package/dist-cjs/mcp/client.d.ts +54 -5
  175. package/dist-cjs/mcp/client.js +171 -20
  176. package/dist-cjs/mcp/http.d.ts +45 -0
  177. package/dist-cjs/mcp/http.js +236 -0
  178. package/dist-cjs/mcp/index.d.ts +4 -2
  179. package/dist-cjs/mcp/index.js +5 -1
  180. package/dist-cjs/mcp/protocol.d.ts +107 -0
  181. package/dist-cjs/mcp/sampling.d.ts +41 -0
  182. package/dist-cjs/mcp/sampling.js +55 -0
  183. package/dist-cjs/mcp/server.d.ts +96 -14
  184. package/dist-cjs/mcp/server.js +273 -58
  185. package/dist-cjs/models/data.js +20 -1
  186. package/dist-cjs/operations/adapters.d.ts +1 -0
  187. package/dist-cjs/operations/adapters.js +3 -1
  188. package/dist-cjs/protocols/a2a-grpc.js +10 -3
  189. package/dist-cjs/protocols/a2a-signing.d.ts +65 -0
  190. package/dist-cjs/protocols/a2a-signing.js +140 -0
  191. package/dist-cjs/protocols/a2a.d.ts +14 -0
  192. package/dist-cjs/protocols/a2a.js +65 -6
  193. package/dist-cjs/providers/cohere.js +16 -2
  194. package/dist-cjs/providers/google.js +7 -1
  195. package/dist-cjs/providers/ollama.d.ts +1 -1
  196. package/dist-cjs/providers/ollama.js +35 -27
  197. package/dist-cjs/providers/openai.js +19 -2
  198. package/dist-cjs/rag/pinecone.d.ts +63 -2
  199. package/dist-cjs/rag/pinecone.js +81 -0
  200. package/dist-cjs/realtime/conversation.js +18 -0
  201. package/dist-cjs/realtime/gemini-live.transport.d.ts +28 -3
  202. package/dist-cjs/realtime/gemini-live.transport.js +170 -32
  203. package/dist-cjs/realtime/session.d.ts +2 -0
  204. package/dist-cjs/realtime/session.js +19 -4
  205. package/dist-cjs/realtime/types.d.ts +35 -3
  206. package/dist-cjs/security/output-guard.js +4 -1
  207. package/dist-cjs/server/auth.d.ts +263 -2
  208. package/dist-cjs/server/auth.js +437 -5
  209. package/dist-cjs/server/deployments.d.ts +3 -2
  210. package/dist-cjs/server/deployments.js +6 -1
  211. package/dist-cjs/telephony/index.d.ts +1 -1
  212. package/dist-cjs/telephony/manager.d.ts +8 -1
  213. package/dist-cjs/telephony/manager.js +13 -0
  214. package/dist-cjs/telephony/providers/telnyx.d.ts +18 -5
  215. package/dist-cjs/telephony/providers/telnyx.js +44 -7
  216. package/dist-cjs/telephony/providers/twilio.d.ts +3 -1
  217. package/dist-cjs/telephony/providers/twilio.js +16 -0
  218. package/dist-cjs/tracing/stores.d.ts +2 -0
  219. package/dist-cjs/tracing/stores.js +1 -0
  220. package/dist-cjs/tracing/tracer.d.ts +5 -2
  221. package/dist-cjs/tracing/tracer.js +6 -3
  222. package/dist-cjs/types/embeddings.d.ts +47 -5
  223. package/dist-cjs/types/messages.d.ts +7 -0
  224. package/dist-cjs/types/principal.d.ts +1 -1
  225. package/dist-cjs/types/response.d.ts +5 -0
  226. package/dist-cjs/types/telephony.d.ts +38 -0
  227. package/dist-cjs/types/tracing.d.ts +6 -0
  228. package/dist-cjs/version.d.ts +1 -1
  229. package/dist-cjs/version.js +1 -1
  230. package/dist-cjs/voice/providers/google.d.ts +44 -0
  231. package/dist-cjs/voice/providers/google.js +252 -0
  232. package/llms.txt +1 -1
  233. package/package.json +17 -1
@@ -1,10 +1,11 @@
1
1
  import { type ParsedArgs } from './args.js';
2
2
  /**
3
- * `nexus adapters verify <module>[#export]`, `nexus adapters page <records...>`, and
4
- * `nexus adapters recheck <records...>`.
3
+ * `nexus adapters verify <module>[#export]`, `nexus adapters page <records...>`,
4
+ * `nexus adapters recheck <records...>`, and `nexus adapters gather --out <dir>`.
5
5
  *
6
6
  * `verify` runs an adapter's contract suite and benchmark from its own repository and writes its
7
7
  * compatibility record; `page` lists records on one page; `recheck` verifies each listed adapter again
8
- * against the installed release and fails when one that passed no longer does.
8
+ * against the installed release and fails when one that passed no longer does; `gather` fetches the
9
+ * records adapters' own CI publishes, checked and never run, into a directory a page lists.
9
10
  */
10
11
  export declare function runAdaptersCommand(subcommand: string | undefined, args: ParsedArgs): Promise<void>;
@@ -1,8 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
2
+ import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import { createRequire } from 'node:module';
4
4
  import path from 'node:path';
5
5
  import { pathToFileURL } from 'node:url';
6
+ import { ADAPTER_KEYWORD, gatherRecords } from '../adapter-kit/gather.js';
6
7
  import { compareRecords, compatibilityPage, readCompatibilityRecord, recordStatus, } from '../adapter-kit/record.js';
7
8
  import { CliUsageError, flagBool, isRecord, printTable, stringFlag, writeJson } from './args.js';
8
9
  export async function runAdaptersCommand(subcommand, args) {
@@ -12,7 +13,75 @@ export async function runAdaptersCommand(subcommand, args) {
12
13
  return page(args);
13
14
  if (subcommand === 'recheck')
14
15
  return recheck(args);
15
- throw new CliUsageError(`Unknown adapters command "${subcommand ?? ''}". Use verify, page, or recheck.`);
16
+ if (subcommand === 'gather')
17
+ return gather(args);
18
+ throw new CliUsageError(`Unknown adapters command "${subcommand ?? ''}". Use verify, page, recheck, or gather.`);
19
+ }
20
+ async function gather(args) {
21
+ const out = stringFlag(args.flags, 'out');
22
+ if (!out)
23
+ throw new CliUsageError('nexus adapters gather needs --out <dir>: where the gathered records are kept.');
24
+ const sourcesFile = stringFlag(args.flags, 'sources');
25
+ const sources = sourcesFile ? sourcesFrom(await readFile(sourcesFile, 'utf8'), sourcesFile) : [];
26
+ const previous = [];
27
+ for (const name of existsSync(out) ? (await readdir(out)).sort() : []) {
28
+ if (!name.endsWith('.json'))
29
+ continue;
30
+ try {
31
+ previous.push({ file: name, record: readCompatibilityRecord(await readFile(path.join(out, name), 'utf8')) });
32
+ }
33
+ catch {
34
+ }
35
+ }
36
+ const registry = stringFlag(args.flags, 'registry');
37
+ const report = await gatherRecords({
38
+ keyword: args.flags.keyword === false ? false : (stringFlag(args.flags, 'keyword') ?? ADAPTER_KEYWORD),
39
+ sources,
40
+ previous: previous.map((entry) => entry.record),
41
+ ...(registry ? { registry } : {}),
42
+ });
43
+ await mkdir(out, { recursive: true });
44
+ const written = new Set();
45
+ for (const source of report.sources) {
46
+ if (!source.record)
47
+ continue;
48
+ const file = `${slug(source.package)}.json`;
49
+ written.add(file);
50
+ await writeFile(path.join(out, file), `${JSON.stringify(source.record, null, 2)}
51
+ `);
52
+ }
53
+ for (const { file } of previous)
54
+ if (!written.has(file))
55
+ await rm(path.join(out, file));
56
+ if (flagBool(args.flags, 'json')) {
57
+ writeJson(report);
58
+ return;
59
+ }
60
+ printTable(report.sources.map((source) => ({
61
+ package: source.package,
62
+ outcome: source.outcome,
63
+ state: source.record ? recordStatus(source.record).state : '',
64
+ detail: source.detail ?? '',
65
+ })), ['package', 'outcome', 'state', 'detail']);
66
+ console.log('');
67
+ console.log(`${report.records.length} records listed in ${out}, of ${report.sources.length} packages.`);
68
+ }
69
+ function sourcesFrom(text, file) {
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(text);
73
+ }
74
+ catch (error) {
75
+ throw new CliUsageError(`${file} is not JSON: ${error.message}`);
76
+ }
77
+ if (!Array.isArray(parsed))
78
+ throw new CliUsageError(`${file} must list sources: [{ "package": ..., "url": ... }]`);
79
+ return parsed.map((item, index) => {
80
+ if (!isRecord(item) || typeof item.package !== 'string' || typeof item.url !== 'string') {
81
+ throw new CliUsageError(`${file}, source ${index + 1}: each source names its "package" and its record's "url"`);
82
+ }
83
+ return { package: item.package, url: item.url };
84
+ });
16
85
  }
17
86
  async function verify(args) {
18
87
  const [target] = args.positionals;
@@ -28,6 +28,9 @@ export async function runDoctorCommand({ flags }) {
28
28
  const maxQueueAgeMs = numberFlag(flags, 'max-queue-age-ms');
29
29
  if (maxQueueAgeMs !== undefined)
30
30
  options.maxQueueAgeMs = maxQueueAgeMs;
31
+ const upgrade = stringFlag(flags, 'upgrade');
32
+ if (upgrade !== undefined)
33
+ options.upgrade = upgrade;
31
34
  let report;
32
35
  try {
33
36
  report = await diagnose(options);
package/dist/cli.js CHANGED
@@ -437,7 +437,7 @@ Usage:
437
437
  nexus db status --client <db.mjs> [--adapters ...] [--dialect postgres|sqlite] [--table t] [--check] [--json]
438
438
  nexus db migrate --client <db.mjs> [--dry-run] [--adapters ...] [--dialect postgres|sqlite] [--table t] [--json]
439
439
  nexus migrate [paths...] [--write] [--check] [--json]
440
- nexus doctor [--module doctor.mjs] [--replicas n] [--max-queue-age-ms n] [--strict] [--json]
440
+ nexus doctor [--module doctor.mjs] [--replicas n] [--max-queue-age-ms n] [--upgrade 3.0] [--strict] [--json]
441
441
  nexus graph lint <module>[#export] [--deployed] [--strict] [--ignore CODE,CODE] [--json]
442
442
  nexus deploy status [assistant] --url <server> [--token t] [--json]
443
443
  nexus deploy canary <assistant> <revision> <percent> --url <server> [--reason r] [--version n]
@@ -446,6 +446,7 @@ Usage:
446
446
  nexus adapters verify <module>[#export] [--out record.json] [--package name@version] [--repository url] [--verify spec] [--json]
447
447
  nexus adapters page <records|dir...> [--out page.md] [--html] [--title t] [--intro text] [--expect page.md] [--check] [--strict]
448
448
  nexus adapters recheck <records|dir...> [--out dir] [--update] [--json]
449
+ nexus adapters gather --out <dir> [--keyword nexus-ai-pro-adapter | --no-keyword] [--sources sources.json] [--registry url] [--json]
449
450
  nexus policy check <policy.json> [--base <policy.json>|git:<ref>] [--accept path,path] [--json]
450
451
  nexus optimize [request.json|prompt.txt] [--model gpt-5-mini] [--max-input-tokens 4000] [--densify] [--json]
451
452
 
@@ -461,6 +462,7 @@ Database modules for "db status" and "db migrate" export { client }, and optiona
461
462
  "graph lint" reads a compiled graph's shape and exits 1 on an error finding, or on a warning with --strict.
462
463
  "adapters verify" exits 1 when the adapter does not pass; its module exports { adapter, config, options, record, close }.
463
464
  "adapters recheck" verifies each listed adapter again and exits 1 when one that passed no longer does; "adapters page --check" exits 1 on a failing or incompatible record, --strict on a stale one too.
465
+ "adapters gather" fetches the records adapters' own CI publishes, found on npm by keyword or listed in --sources, keeps only those sealed and naming their package, and never runs them.
464
466
  "policy check" exits 1 when the policy is not valid, or, against --base, when a change that widens what a tenant may reach is not acknowledged.`);
465
467
  }
466
468
  main().catch((error) => {
@@ -95,3 +95,4 @@ export declare function createNexusConfig(seed?: Partial<NexusAIConfig>): NexusC
95
95
  * Identity helper for users who prefer object literals but want type inference and validation in editors.
96
96
  */
97
97
  export declare function defineNexusConfig(config: NexusAIConfig): NexusAIConfig;
98
+ export { DEFAULT_CHANGES, type DefaultChange, type NexusDefaults, nexusDefaults, useDefaults } from './defaults.js';
@@ -148,3 +148,4 @@ export function createNexusConfig(seed = {}) {
148
148
  export function defineNexusConfig(config) {
149
149
  return config;
150
150
  }
151
+ export { DEFAULT_CHANGES, nexusDefaults, useDefaults } from './defaults.js';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Which release's defaults apply where an option is left out: 2.x's, or a preview of 3.0's. 3.0 turns on
3
+ * three settings that 2.x keeps opt-in, because turning them on changes what an upgrade does; the
4
+ * preview turns on all three at once, so an application can run as 3.0 will before it upgrades. An
5
+ * option set explicitly always wins, under either.
6
+ */
7
+ /** A release whose defaults apply: `2.x`, the default, or `3.0`, a preview of the next major release's. */
8
+ export type NexusDefaults = '2.x' | '3.0';
9
+ /** One default that 3.0 changes: what it is, what 2.x does, what 3.0 does, and what to do first. */
10
+ export interface DefaultChange {
11
+ /** The setting, as its option is named. */
12
+ setting: string;
13
+ /** Where it is set. */
14
+ module: string;
15
+ /** What 2.x does when the option is left out. */
16
+ today: string;
17
+ /** What 3.0, and the preview, does when it is left out. */
18
+ next: string;
19
+ /** What to do before turning it on, if anything. */
20
+ before: string;
21
+ }
22
+ /** The defaults 3.0 changes, each turned on by the preview. */
23
+ export declare const DEFAULT_CHANGES: readonly DefaultChange[];
24
+ /**
25
+ * Chooses which release's defaults apply to everything created after it: `3.0` previews the next
26
+ * major release's, `2.x` returns to today's. Call it before creating a tracer, an operation store, or
27
+ * a deployment: each reads the defaults when it is created. Overrides `NEXUS_DEFAULTS`.
28
+ */
29
+ export declare function useDefaults(release: NexusDefaults): void;
30
+ /**
31
+ * The defaults in effect: as `useDefaults()` chose, else the `NEXUS_DEFAULTS` environment variable,
32
+ * `2.x` or `3.0`, else `2.x`. Throws for any other value of the variable, rather than ignore a typo.
33
+ */
34
+ export declare function nexusDefaults(): NexusDefaults;
@@ -0,0 +1,39 @@
1
+ export const DEFAULT_CHANGES = [
2
+ {
3
+ setting: 'Tracer incremental',
4
+ module: 'nexus-ai-pro/tracing',
5
+ today: 'false: a trace is written when its root run finishes',
6
+ next: 'true on a durable trace store: each run is written as it starts and again as it ends, so a run in a process that dies is not lost',
7
+ before: 'Nothing; close what a process that died left open with closeAbandonedRuns().',
8
+ },
9
+ {
10
+ setting: 'RedisOperationStore index',
11
+ module: 'nexus-ai-pro/operations',
12
+ today: 'false: finding queued work reads every record',
13
+ next: 'true where the client has zadd, zrem, and zrangebyscore: queued and leased work is kept in sorted sets',
14
+ before: 'Run 2.2 or later on every worker sharing the prefix, and call reindex() once.',
15
+ },
16
+ {
17
+ setting: 'Deployments bucketStrategy',
18
+ module: 'nexus-ai-pro/server',
19
+ today: 'fnv1a-v1 for a deployment first recorded',
20
+ next: 'hash-v2 for a deployment first recorded, which spreads sequential thread ids evenly; a recorded deployment keeps its own',
21
+ before: 'Nothing; a deployment already recorded keeps the strategy it records, and bucketing() changes one.',
22
+ },
23
+ ];
24
+ let chosen;
25
+ export function useDefaults(release) {
26
+ chosen = checked(release, 'useDefaults()');
27
+ }
28
+ export function nexusDefaults() {
29
+ if (chosen)
30
+ return chosen;
31
+ const variable = globalThis.process?.env
32
+ ?.NEXUS_DEFAULTS;
33
+ return variable === undefined || variable === '' ? '2.x' : checked(variable, 'NEXUS_DEFAULTS');
34
+ }
35
+ function checked(release, where) {
36
+ if (release === '2.x' || release === '3.0')
37
+ return release;
38
+ throw new Error(`${where} takes 2.x or 3.0, not ${JSON.stringify(release)}`);
39
+ }
@@ -50,6 +50,7 @@ export function protectStreamOutput(stream, security, requestSignal) {
50
50
  const protectedOutput = security.protectOutput(toPseudoResponse(bufferedText, bufferedToolCalls, sourceMeta));
51
51
  security.assertOutputSafe(protectedOutput);
52
52
  const safeText = protectedOutput.value.content;
53
+ const textChanged = safeText !== bufferedText;
53
54
  const safeToolCalls = protectedOutput.value.toolCalls || [];
54
55
  const applied = protectedOutput.value.meta.guardrailsApplied;
55
56
  const textIndexes = chunks
@@ -69,9 +70,11 @@ export function protectStreamOutput(stream, security, requestSignal) {
69
70
  safeOffset += length;
70
71
  if (!content)
71
72
  continue;
73
+ const { logprobs, ...text } = chunk;
72
74
  yield {
73
- ...chunk,
75
+ ...text,
74
76
  content,
77
+ ...(logprobs && !textChanged ? { logprobs } : {}),
75
78
  meta: withGuardrails(chunk.meta, applied),
76
79
  };
77
80
  continue;
@@ -1,8 +1,3 @@
1
- /**
2
- * Decisions from any completion model: each question asked on its own, answered with a one-token code,
3
- * and its probabilities read from the code token's log probabilities, or counted over samples where
4
- * the model reports none.
5
- */
6
1
  import type { CompletionRequest } from '../types/messages.js';
7
2
  import type { NexusResponse } from '../types/response.js';
8
3
  import { type DecisionProvider } from './core.js';
@@ -25,8 +20,14 @@ export interface CompletionDecisionsOptions {
25
20
  * a model that reports none or refuses to, which it then asks by sampling from the start.
26
21
  */
27
22
  method?: CompletionDecisionMethod;
28
- /** Calls per question, when sampling. Defaults to 8. */
23
+ /** Calls per question, when sampling, and per box. Defaults to 8. */
29
24
  samples?: number;
25
+ /**
26
+ * Whether the model reads pictures, so a decision about the pictures a state holds, or a box, may
27
+ * be asked of it. Defaults to `true`; set `false` for a text-only model, which such a decision then
28
+ * passes over to a fallback.
29
+ */
30
+ images?: boolean;
30
31
  /** The temperature samples are drawn at. Defaults to 1. */
31
32
  temperature?: number;
32
33
  /** Questions asked at once. Defaults to 8. */
@@ -41,7 +42,9 @@ export interface CompletionDecisionsOptions {
41
42
  * Its probabilities are read from the log probabilities of that token's alternatives where the model
42
43
  * reports them, the way TypeSafe AI's Jev reads its own; elsewhere the question is asked `samples`
43
44
  * times, and each answer counted. Sampled probabilities come in steps of one sample, and cost as many
44
- * calls.
45
+ * calls. The pictures a state holds are shown to the model after the state, each labelled by its
46
+ * path; a box is asked `samples` times, and is the median of each edge, its confidence how far the
47
+ * samples overlap it.
45
48
  */
46
49
  export declare function completionDecisions(client: DecisionCompletionClient, options: CompletionDecisionsOptions): DecisionProvider & {
47
50
  readonly sampledModels: ReadonlySet<string>;
@@ -1,14 +1,17 @@
1
- import { DecisionError, } from './core.js';
1
+ import { boxOverlap, DecisionError, } from './core.js';
2
+ import { replacePictures, statePictures } from './pictures.js';
2
3
  const LOGPROB_OPTIONS = 20;
3
4
  export function completionDecisions(client, options) {
4
5
  const method = options.method ?? 'auto';
6
+ const images = options.images !== false;
5
7
  const sampled = new Set();
6
8
  const info = {
7
9
  name: options.name ?? options.model,
8
10
  capabilities: {
9
- types: ['yes-no', 'choice', 'score'],
11
+ types: images ? ['yes-no', 'choice', 'score', 'box'] : ['yes-no', 'choice', 'score'],
10
12
  maxOptions: 255,
11
13
  maxLevels: 10,
14
+ images,
12
15
  defaultModel: options.model,
13
16
  },
14
17
  chargedByItsCalls: true,
@@ -21,7 +24,9 @@ export function completionDecisions(client, options) {
21
24
  messages: [
22
25
  {
23
26
  role: 'system',
24
- content: 'You answer one question about a state with exactly one of the codes given, and nothing else.',
27
+ content: question.type === 'box'
28
+ ? 'You answer where something is in a picture with exactly four numbers, and nothing else.'
29
+ : 'You answer one question about a state with exactly one of the codes given, and nothing else.',
25
30
  },
26
31
  { role: 'user', content: prompt },
27
32
  ],
@@ -29,6 +34,8 @@ export function completionDecisions(client, options) {
29
34
  ...(context.tenantId !== undefined ? { tenantId: context.tenantId } : {}),
30
35
  ...extra,
31
36
  });
37
+ if (question.type === 'box')
38
+ return askBox(request, model, usage);
32
39
  const readable = codes.length <= LOGPROB_OPTIONS && codes.every((code) => code.length === 1);
33
40
  if (method !== 'sampling' && readable && !sampled.has(model)) {
34
41
  let response;
@@ -64,6 +71,28 @@ export function completionDecisions(client, options) {
64
71
  }
65
72
  return distributionOf(question, codes, weights);
66
73
  };
74
+ const askBox = async (request, model, usage) => {
75
+ if (method === 'logprobs') {
76
+ throw new DecisionError('A box is read from several samples of the answer, not from log probabilities', 'DECISION_UNSUPPORTED');
77
+ }
78
+ const count = Math.max(1, Math.floor(options.samples ?? 8));
79
+ const readings = await Promise.all(Array.from({ length: count }, () => client.complete(request({ temperature: options.temperature ?? 1, maxTokens: 32 })).then((response) => {
80
+ usage.add(response);
81
+ return boxIn(response.content);
82
+ })));
83
+ const boxes = readings.filter((box) => box !== undefined);
84
+ if (boxes.length === 0) {
85
+ throw new DecisionError(`"${model}" answered none of ${count} samples with a box`, 'DECISION_FAILED');
86
+ }
87
+ const box = {
88
+ top: median(boxes.map((item) => item.top)),
89
+ bottom: median(boxes.map((item) => item.bottom)),
90
+ left: median(boxes.map((item) => item.left)),
91
+ right: median(boxes.map((item) => item.right)),
92
+ };
93
+ const agreement = boxes.reduce((sum, item) => sum + boxOverlap(item, box), 0) / count;
94
+ return { type: 'box', box, confidence: agreement };
95
+ };
67
96
  return {
68
97
  info,
69
98
  sampledModels: sampled,
@@ -112,6 +141,8 @@ class Totals {
112
141
  }
113
142
  }
114
143
  function codesOf(question) {
144
+ if (question.type === 'box')
145
+ return [];
115
146
  if (question.type === 'yes-no')
116
147
  return ['Y', 'N'];
117
148
  if (question.type === 'score')
@@ -121,10 +152,37 @@ function codesOf(question) {
121
152
  : question.options.map((_, index) => String(index + 1));
122
153
  }
123
154
  function promptOf(state, question, codes, instructions) {
124
- const text = typeof state === 'string' ? state : JSON.stringify(state, null, 2);
125
- const lines = [`State:\n${text}`, ''];
155
+ const pictures = typeof state === 'string' ? [] : statePictures(state);
156
+ const shown = pictures.length ? replacePictures(state, (picture) => `[picture \`${picture.path}\`]`) : state;
157
+ const text = typeof shown === 'string' ? shown : JSON.stringify(shown, null, 2);
158
+ const asked = questionLines(question, codes, instructions).join('\n');
159
+ if (pictures.length === 0)
160
+ return `State:\n${text}\n\n${asked}`;
161
+ return [
162
+ { type: 'text', text: `State:\n${text}\n\nThe pictures it holds, by path:` },
163
+ ...pictures.flatMap((picture) => [
164
+ { type: 'text', text: `\`${picture.path}\`:` },
165
+ imagePart(picture.part),
166
+ ]),
167
+ { type: 'text', text: asked },
168
+ ];
169
+ }
170
+ function imagePart(part) {
171
+ if (part.type === 'image')
172
+ return part;
173
+ if (part.type === 'asset')
174
+ return { type: 'image', source: { asset: part.asset } };
175
+ return part;
176
+ }
177
+ function questionLines(question, codes, instructions) {
178
+ const lines = [];
126
179
  if (instructions)
127
180
  lines.push(instructions, '');
181
+ if (question.type === 'box') {
182
+ lines.push(`Find: ${question.question}`);
183
+ lines.push('', "Answer with four numbers from 0 to 1, separated by spaces: the top, bottom, left, and right edges of a box around it, each a fraction of the picture's height or width from its top-left corner.");
184
+ return lines;
185
+ }
128
186
  if (question.type === 'yes-no') {
129
187
  lines.push(`Question: ${question.question ?? 'Is the statement true of the state?'}`);
130
188
  lines.push(`Y: yes${question.criteria?.yes ? `, ${question.criteria.yes}` : ''}`);
@@ -143,16 +201,30 @@ function promptOf(state, question, codes, instructions) {
143
201
  });
144
202
  }
145
203
  lines.push('', `Answer with one code: ${codes.join(', ')}.`);
146
- return lines.join('\n');
204
+ return lines;
205
+ }
206
+ function boxIn(content) {
207
+ const numbers = (content.match(/-?\d*\.?\d+/g) ?? []).slice(0, 4).map(Number);
208
+ if (numbers.length < 4 || numbers.some((value) => !(value >= 0 && value <= 1)))
209
+ return undefined;
210
+ const [top, bottom, left, right] = numbers;
211
+ return top <= bottom && left <= right ? { top, bottom, left, right } : undefined;
212
+ }
213
+ function median(values) {
214
+ const sorted = [...values].sort((a, b) => a - b);
215
+ const middle = Math.floor(sorted.length / 2);
216
+ return sorted.length % 2
217
+ ? sorted[middle]
218
+ : (sorted[middle - 1] + sorted[middle]) / 2;
147
219
  }
148
220
  function codeOf(token) {
149
221
  return token.replace(/^[\s"'`*(]+|[\s"'`*).:,]+$/g, '').toUpperCase();
150
222
  }
151
223
  function weightsFrom(logprobs, codes) {
152
224
  const first = logprobs?.find((item) => item.token.trim() !== '');
153
- if (!first)
225
+ if (!first?.top?.length)
154
226
  return undefined;
155
- const candidates = first.top?.length ? first.top : [{ token: first.token, logprob: first.logprob }];
227
+ const candidates = first.top;
156
228
  const weights = codes.map(() => 0);
157
229
  let found = false;
158
230
  for (const candidate of candidates) {
@@ -184,6 +256,8 @@ function distributionOf(question, codes, weights) {
184
256
  return { type: 'yes-no', yes: share(0) };
185
257
  if (question.type === 'score')
186
258
  return { type: 'score', probabilities: codes.map((_, index) => share(index)) };
259
+ if (question.type === 'box')
260
+ throw new DecisionError('A box has no codes to weigh', 'DECISION_INVALID');
187
261
  return {
188
262
  type: 'choice',
189
263
  probabilities: Object.fromEntries(question.options.map((option, index) => [option.name, share(index)])),