@nylorun/runtime 0.23.0-beta → 0.25.0-beta

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 (301) hide show
  1. package/CHANGELOG.md +252 -0
  2. package/README.md +87 -28
  3. package/dist/adapters/execution/restate.js +1 -1
  4. package/dist/adapters/replication/pgoutput.d.ts +2 -0
  5. package/dist/adapters/replication/pgoutput.js +6 -3
  6. package/dist/adapters/sandbox/local.d.ts +9 -1
  7. package/dist/adapters/sandbox/local.js +16 -4
  8. package/dist/adapters/sandbox/virtual.js +4 -1
  9. package/dist/api/a2a/card.d.ts +11 -1
  10. package/dist/api/a2a/card.js +26 -2
  11. package/dist/api/a2a/endpoint.js +9 -3
  12. package/dist/api/a2a/protocol.d.ts +25 -2
  13. package/dist/api/a2a/protocol.js +20 -3
  14. package/dist/api/a2a/routes.d.ts +6 -1
  15. package/dist/api/a2a/routes.js +67 -9
  16. package/dist/api/a2a/tasks.js +2 -1
  17. package/dist/api/ag-ui/endpoint.js +8 -3
  18. package/dist/api/ag-ui/history.d.ts +6 -1
  19. package/dist/api/ag-ui/history.js +39 -3
  20. package/dist/api/ag-ui/routes.d.ts +4 -1
  21. package/dist/api/ag-ui/routes.js +63 -20
  22. package/dist/api/ag-ui/translate.js +3 -2
  23. package/dist/api/components.d.ts +5327 -1091
  24. package/dist/api/components.js +40 -16
  25. package/dist/api/gate/keys.d.ts +2 -1
  26. package/dist/api/gate/keys.js +14 -1
  27. package/dist/api/gate/routes.d.ts +6 -0
  28. package/dist/api/gate/routes.js +52 -3
  29. package/dist/api/http/app.js +17 -6
  30. package/dist/api/http/body.d.ts +3 -3
  31. package/dist/api/http/body.js +7 -7
  32. package/dist/api/http/define.d.ts +6 -0
  33. package/dist/api/http/define.js +13 -1
  34. package/dist/api/http/respond.js +1 -1
  35. package/dist/api/http/routes/artifacts.js +35 -8
  36. package/dist/api/http/routes/coverage.d.ts +9 -0
  37. package/dist/api/http/routes/coverage.js +34 -0
  38. package/dist/api/http/routes/environment-manifests.d.ts +9 -0
  39. package/dist/api/http/routes/environment-manifests.js +118 -0
  40. package/dist/api/http/routes/environments.d.ts +11 -0
  41. package/dist/api/http/routes/environments.js +332 -0
  42. package/dist/api/http/routes/mcp.d.ts +1 -1
  43. package/dist/api/http/routes/mcp.js +2 -2
  44. package/dist/api/http/routes/me.js +1 -1
  45. package/dist/api/http/routes/reads.d.ts +94 -9
  46. package/dist/api/http/routes/reads.js +54 -5
  47. package/dist/api/http/routes/sessions.js +24 -9
  48. package/dist/api/http/routes/tenant.js +65 -15
  49. package/dist/api/inbound-files.d.ts +65 -0
  50. package/dist/api/inbound-files.js +115 -0
  51. package/dist/api/openapi.js +48 -28
  52. package/dist/artifacts/disk.d.ts +59 -0
  53. package/dist/artifacts/disk.js +569 -0
  54. package/dist/artifacts/export.d.ts +1 -1
  55. package/dist/artifacts/export.js +2 -3
  56. package/dist/artifacts/folders.d.ts +6 -3
  57. package/dist/artifacts/folders.js +17 -12
  58. package/dist/artifacts/media-types.d.ts +15 -0
  59. package/dist/artifacts/media-types.js +56 -4
  60. package/dist/artifacts/parts.d.ts +12 -3
  61. package/dist/artifacts/parts.js +15 -4
  62. package/dist/artifacts/service.d.ts +2 -1
  63. package/dist/artifacts/service.js +6 -1
  64. package/dist/artifacts/workspace.d.ts +5 -5
  65. package/dist/artifacts/workspace.js +7 -19
  66. package/dist/core/flow-host.d.ts +3 -4
  67. package/dist/core/flow-host.js +5 -2
  68. package/dist/core/provider.d.ts +3 -2
  69. package/dist/core/provider.js +5 -37
  70. package/dist/core/sandbox-routes.d.ts +2 -21
  71. package/dist/core/sandbox-routes.js +11 -44
  72. package/dist/core/turn-manifest.d.ts +3 -2
  73. package/dist/core/turn-manifest.js +29 -5
  74. package/dist/execution/types.d.ts +1 -1
  75. package/dist/gates/contract.d.ts +3 -24
  76. package/dist/gates/contract.js +1 -36
  77. package/dist/gates/egress-credentials.d.ts +9 -0
  78. package/dist/gates/egress-credentials.js +11 -4
  79. package/dist/gates/egress.d.ts +22 -1
  80. package/dist/gates/egress.js +116 -10
  81. package/dist/gates/fixture-model.d.ts +28 -0
  82. package/dist/gates/fixture-model.js +92 -0
  83. package/dist/gates/handler.js +4 -4
  84. package/dist/gates/http-tool.d.ts +9 -6
  85. package/dist/gates/http-tool.js +11 -17
  86. package/dist/gates/in-process.d.ts +5 -0
  87. package/dist/gates/in-process.js +12 -1
  88. package/dist/gates/journal.d.ts +30 -0
  89. package/dist/gates/journal.js +51 -0
  90. package/dist/gates/mcp-handler.d.ts +4 -0
  91. package/dist/gates/mcp-handler.js +7 -4
  92. package/dist/gates/meter.d.ts +10 -2
  93. package/dist/gates/meter.js +20 -5
  94. package/dist/gates/policy.d.ts +49 -0
  95. package/dist/gates/policy.js +132 -0
  96. package/dist/gates/tenant-vaults.d.ts +7 -7
  97. package/dist/gates/tenant-vaults.js +5 -5
  98. package/dist/gates/tool-calls.d.ts +10 -5
  99. package/dist/gates/tool-calls.js +28 -10
  100. package/dist/gates/tool-client.d.ts +8 -0
  101. package/dist/gates/tool-client.js +27 -11
  102. package/dist/gates/tool-contract.d.ts +11 -69
  103. package/dist/gates/tool-contract.js +1 -85
  104. package/dist/gates/tool-gate.d.ts +1 -1
  105. package/dist/harness/calls.d.ts +14 -7
  106. package/dist/harness/calls.js +30 -24
  107. package/dist/harness/executors.js +12 -9
  108. package/dist/harness/main.js +24 -5
  109. package/dist/harness/pod.d.ts +39 -0
  110. package/dist/harness/pod.js +46 -0
  111. package/dist/harness/service.d.ts +0 -1
  112. package/dist/harness/service.js +17 -9
  113. package/dist/harness/setup.d.ts +29 -0
  114. package/dist/harness/setup.js +174 -0
  115. package/dist/harness/ws-client.js +3 -2
  116. package/dist/harness/ws-port.d.ts +1 -5
  117. package/dist/harness/ws-port.js +1 -5
  118. package/dist/harness-api/in-process.js +3 -3
  119. package/dist/harness-api/record.d.ts +15 -1
  120. package/dist/harness-api/record.js +180 -50
  121. package/dist/harness-api/redact.d.ts +3 -0
  122. package/dist/harness-api/redact.js +45 -0
  123. package/dist/harness-api/server.d.ts +35 -4
  124. package/dist/harness-api/server.js +130 -17
  125. package/dist/harness-api/start.d.ts +10 -3
  126. package/dist/harness-api/start.js +40 -10
  127. package/dist/harness-api/tool-journal.d.ts +105 -0
  128. package/dist/harness-api/tool-journal.js +132 -0
  129. package/dist/harness-api/tool-status.d.ts +13 -0
  130. package/dist/harness-api/tool-status.js +26 -0
  131. package/dist/harness-api/workspace.d.ts +12 -7
  132. package/dist/harness-api/workspace.js +47 -25
  133. package/dist/harness-api/ws-server.d.ts +11 -3
  134. package/dist/harness-api/ws-server.js +94 -15
  135. package/dist/host/gates.d.ts +2 -1
  136. package/dist/host/gates.js +10 -2
  137. package/dist/host/main.js +54 -5
  138. package/dist/host/operate.js +67 -5
  139. package/dist/host/stack-config.d.ts +64 -0
  140. package/dist/host/stack-config.js +62 -2
  141. package/dist/keys/client.js +4 -1
  142. package/dist/keys/keys.d.ts +13 -6
  143. package/dist/keys/keys.js +9 -8
  144. package/dist/management-openapi.json +1264 -2171
  145. package/dist/mcp/connect.d.ts +26 -22
  146. package/dist/mcp/connect.js +30 -34
  147. package/dist/mcp/pool.d.ts +3 -6
  148. package/dist/mcp/pool.js +14 -28
  149. package/dist/mcp/preview.d.ts +3 -3
  150. package/dist/mcp/preview.js +10 -21
  151. package/dist/mcp/snapshot.d.ts +16 -24
  152. package/dist/mcp/snapshot.js +33 -45
  153. package/dist/model/capabilities.d.ts +26 -0
  154. package/dist/model/capabilities.js +44 -0
  155. package/dist/model/catalog.d.ts +34 -3
  156. package/dist/model/catalog.js +38 -2
  157. package/dist/model/models.d.ts +2 -0
  158. package/dist/model/models.js +1 -1
  159. package/dist/model/native-blocks.d.ts +35 -0
  160. package/dist/model/native-blocks.js +170 -0
  161. package/dist/model/pi-model.js +114 -94
  162. package/dist/openapi.json +12822 -3943
  163. package/dist/reads/types.d.ts +16 -3
  164. package/dist/record/append.d.ts +3 -1
  165. package/dist/record/append.js +1 -0
  166. package/dist/record/envelope.d.ts +5 -1
  167. package/dist/record/envelope.js +7 -3
  168. package/dist/record/environment.d.ts +25 -0
  169. package/dist/record/environment.js +44 -0
  170. package/dist/record/index.d.ts +1 -1
  171. package/dist/record/index.js +1 -1
  172. package/dist/sandbox/disk-fs.d.ts +95 -0
  173. package/dist/sandbox/disk-fs.js +206 -0
  174. package/dist/sandbox/join.d.ts +17 -18
  175. package/dist/sandbox/join.js +46 -15
  176. package/dist/sandbox/manager.d.ts +47 -21
  177. package/dist/sandbox/manager.js +162 -107
  178. package/dist/sandbox/placement.d.ts +14 -13
  179. package/dist/sandbox/placement.js +31 -14
  180. package/dist/sandbox/pods/client.d.ts +9 -2
  181. package/dist/sandbox/pods/lifecycle.d.ts +36 -27
  182. package/dist/sandbox/pods/lifecycle.js +29 -26
  183. package/dist/sandbox/pods/reconcile.js +18 -12
  184. package/dist/sandbox/pods/spec.d.ts +15 -13
  185. package/dist/sandbox/pods/spec.js +17 -20
  186. package/dist/sandbox/records.d.ts +2 -2
  187. package/dist/sandbox/records.js +3 -3
  188. package/dist/sandbox/resolve.d.ts +37 -27
  189. package/dist/sandbox/resolve.js +73 -41
  190. package/dist/sandbox/runtime-tools.d.ts +9 -0
  191. package/dist/sandbox/runtime-tools.js +16 -0
  192. package/dist/sandbox/session-sandbox.d.ts +4 -6
  193. package/dist/sandbox/session-sandbox.js +47 -94
  194. package/dist/sandbox/setup.d.ts +64 -0
  195. package/dist/sandbox/setup.js +160 -0
  196. package/dist/sandbox/share.d.ts +21 -26
  197. package/dist/sandbox/share.js +36 -63
  198. package/dist/sandbox/skills.d.ts +8 -6
  199. package/dist/sandbox/skills.js +24 -23
  200. package/dist/sandbox/tenant-config.d.ts +12 -14
  201. package/dist/sandbox/tenant-config.js +12 -17
  202. package/dist/sandbox/types.d.ts +6 -0
  203. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  204. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  205. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  206. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  207. package/dist/store/postgres/drizzle/0024_flat_manifest.sql +7 -0
  208. package/dist/store/postgres/drizzle/0025_environment_manifests.sql +14 -0
  209. package/dist/store/postgres/drizzle/0026_environment_forks.sql +1 -0
  210. package/dist/store/postgres/drizzle/0027_environment_image_digest.sql +1 -0
  211. package/dist/store/postgres/drizzle/meta/_journal.json +56 -0
  212. package/dist/store/postgres/reads.js +123 -17
  213. package/dist/store/postgres/record-writer.d.ts +4 -4
  214. package/dist/store/postgres/record-writer.js +9 -9
  215. package/dist/store/postgres/record.d.ts +7 -0
  216. package/dist/store/postgres/record.js +23 -2
  217. package/dist/store/postgres/schema.d.ts +1126 -736
  218. package/dist/store/postgres/schema.js +136 -73
  219. package/dist/store/postgres/store.js +195 -67
  220. package/dist/store/types.d.ts +165 -50
  221. package/dist/tenant/advance.d.ts +7 -6
  222. package/dist/tenant/advance.js +118 -42
  223. package/dist/tenant/artifact-tool.d.ts +1 -1
  224. package/dist/tenant/artifact-tool.js +46 -26
  225. package/dist/tenant/commands.d.ts +15 -2
  226. package/dist/tenant/commands.js +115 -14
  227. package/dist/tenant/context.d.ts +56 -24
  228. package/dist/tenant/context.js +4 -15
  229. package/dist/tenant/effects.d.ts +1 -1
  230. package/dist/tenant/effects.js +44 -29
  231. package/dist/tenant/environment-manifests.d.ts +57 -0
  232. package/dist/tenant/environment-manifests.js +200 -0
  233. package/dist/tenant/environments.d.ts +174 -0
  234. package/dist/tenant/environments.js +1044 -0
  235. package/dist/tenant/harness-tokens.d.ts +21 -0
  236. package/dist/tenant/harness-tokens.js +61 -0
  237. package/dist/tenant/host-token.d.ts +1 -1
  238. package/dist/tenant/host-token.js +2 -4
  239. package/dist/tenant/identity-file.d.ts +4 -4
  240. package/dist/tenant/identity-file.js +17 -8
  241. package/dist/tenant/issuers.js +3 -3
  242. package/dist/tenant/model-setting.d.ts +14 -0
  243. package/dist/tenant/model-setting.js +51 -0
  244. package/dist/tenant/principals.js +1 -1
  245. package/dist/tenant/reset.js +3 -3
  246. package/dist/tenant/runtime.d.ts +6 -1
  247. package/dist/tenant/runtime.js +7 -1
  248. package/dist/tenant/session.d.ts +3 -3
  249. package/dist/tenant/session.js +0 -14
  250. package/dist/tenant/sessions.d.ts +18 -14
  251. package/dist/tenant/sessions.js +99 -143
  252. package/dist/tenant/signing-keys.d.ts +5 -1
  253. package/dist/tenant/skill-tool.d.ts +3 -3
  254. package/dist/tenant/skill-tool.js +6 -11
  255. package/dist/tenant/status.js +3 -3
  256. package/dist/tenant/sweep.js +13 -7
  257. package/dist/tenant/tool-results.js +1 -1
  258. package/dist/tenant/tool-search.d.ts +7 -11
  259. package/dist/tenant/tool-search.js +8 -5
  260. package/dist/tenant/transcript.d.ts +245 -36
  261. package/dist/tenant/transcript.js +64 -12
  262. package/dist/tenant/turn-timing.d.ts +43 -0
  263. package/dist/tenant/turn-timing.js +39 -0
  264. package/dist/tenant/types.d.ts +2 -0
  265. package/dist/traces/ids.d.ts +41 -0
  266. package/dist/traces/ids.js +81 -0
  267. package/dist/traces/key.d.ts +6 -0
  268. package/dist/traces/key.js +37 -0
  269. package/dist/traces/map.d.ts +113 -0
  270. package/dist/traces/map.js +289 -0
  271. package/dist/traces/options.d.ts +7 -0
  272. package/dist/traces/options.js +1 -0
  273. package/dist/traces/otlp.d.ts +42 -0
  274. package/dist/traces/otlp.js +84 -0
  275. package/dist/traces/projector.d.ts +58 -0
  276. package/dist/traces/projector.js +200 -0
  277. package/dist/traces/read.d.ts +23 -0
  278. package/dist/traces/read.js +16 -0
  279. package/dist/vault/coverage.d.ts +26 -0
  280. package/dist/vault/coverage.js +62 -0
  281. package/dist/vault/error.d.ts +11 -1
  282. package/dist/vault/error.js +5 -1
  283. package/dist/vault/host-model.d.ts +4 -1
  284. package/dist/vault/host-model.js +10 -2
  285. package/dist/vault/service.d.ts +92 -48
  286. package/dist/vault/service.js +462 -376
  287. package/dist/vault/sources.d.ts +5 -16
  288. package/dist/vault/sources.js +6 -8
  289. package/dist/version.d.ts +1 -1
  290. package/dist/version.js +2 -2
  291. package/package.json +13 -13
  292. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  293. package/dist/api/http/routes/sandboxes.js +0 -153
  294. package/dist/api/http/routes/vaults.d.ts +0 -10
  295. package/dist/api/http/routes/vaults.js +0 -139
  296. package/dist/record/sandbox.d.ts +0 -25
  297. package/dist/record/sandbox.js +0 -43
  298. package/dist/sandbox/capability.d.ts +0 -3
  299. package/dist/sandbox/capability.js +0 -10
  300. package/dist/tenant/sandboxes.d.ts +0 -123
  301. package/dist/tenant/sandboxes.js +0 -566
@@ -0,0 +1,84 @@
1
+ /**
2
+ * OTLP/HTTP JSON (R7.2): the encoder of a batch of spans as `resourceSpans`, and the poster that
3
+ * delivers one to the collector's `/v1/traces`. Hand-built JSON, no OpenTelemetry SDK: the spans
4
+ * are mapped from records (`map.ts`), not timed from live code.
5
+ *
6
+ * Delivery never holds the log back: a `429`, a `5xx` or a lost connection is tried again three
7
+ * times with backoff, then the batch is dropped and logged (`trace_export_dropped`); any other
8
+ * answer drops it at once. Either way `post` resolves, and the projector acknowledges the rows,
9
+ * so a dead collector makes Postgres keep no WAL. The trace route (R7.5) re-reads any turn.
10
+ */
11
+ import { RUNTIME_VERSION } from "../version.js";
12
+ import { GENAI_SEMCONV } from "./map.js";
13
+ export const DEFAULT_SERVICE_NAME = "nylorun";
14
+ /** The instrumentation scope of every span. */
15
+ const SCOPE = { name: "@nylorun/runtime", version: RUNTIME_VERSION };
16
+ /** A batch of spans as an OTLP `ExportTraceServiceRequest` in JSON. */
17
+ export function resourceSpans(resource, spans) {
18
+ const attributes = [];
19
+ for (const [key, value] of Object.entries(resource.attributes ?? {}))
20
+ if (key !== "service.name")
21
+ attributes.push({ key, value: { stringValue: value } });
22
+ attributes.unshift({ key: "service.name", value: { stringValue: resource.serviceName } });
23
+ attributes.push({ key: "nylorun.semconv.genai", value: { stringValue: GENAI_SEMCONV } });
24
+ return {
25
+ resourceSpans: [{ resource: { attributes }, scopeSpans: [{ scope: SCOPE, spans: [...spans] }] }],
26
+ };
27
+ }
28
+ export function createOtlpPoster(options) {
29
+ const send = options.fetch ?? fetch;
30
+ const retries = options.retries ?? 3;
31
+ const backoffMs = options.backoffMs ?? 500;
32
+ const timeoutMs = options.timeoutMs ?? 10_000;
33
+ const log = options.log ?? (() => { });
34
+ return async (spans, signal) => {
35
+ if (spans.length === 0)
36
+ return "posted";
37
+ const body = JSON.stringify(resourceSpans(options.resource, spans));
38
+ let reason = "";
39
+ for (let attempt = 0; attempt <= retries; attempt += 1) {
40
+ if (attempt > 0 && !(await pause(backoffMs * 2 ** (attempt - 1), signal)))
41
+ break;
42
+ let status;
43
+ try {
44
+ const response = await send(options.url, {
45
+ method: "POST",
46
+ headers: { ...options.headers, "content-type": "application/json" },
47
+ body,
48
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
49
+ });
50
+ status = response.status;
51
+ await response.body?.cancel().catch(() => undefined);
52
+ }
53
+ catch (error) {
54
+ reason = error instanceof Error ? error.message : String(error);
55
+ if (signal?.aborted)
56
+ break;
57
+ continue;
58
+ }
59
+ if (status >= 200 && status < 300)
60
+ return "posted";
61
+ reason = `HTTP ${status}`;
62
+ if (status !== 429 && status < 500)
63
+ break;
64
+ }
65
+ log("trace_export_dropped", { spans: spans.length, reason });
66
+ return "dropped";
67
+ };
68
+ }
69
+ /** Waits `ms`; false when `signal` aborted first. */
70
+ function pause(ms, signal) {
71
+ if (signal?.aborted)
72
+ return Promise.resolve(false);
73
+ return new Promise((resolve) => {
74
+ const timer = setTimeout(() => {
75
+ signal?.removeEventListener("abort", onAbort);
76
+ resolve(true);
77
+ }, ms);
78
+ const onAbort = () => {
79
+ clearTimeout(timer);
80
+ resolve(false);
81
+ };
82
+ signal?.addEventListener("abort", onAbort, { once: true });
83
+ });
84
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The trace projector (R7.2, Q1): a second reader of the session log, beside the stream relay. It
3
+ * reads committed `session_events` from its own replication slot (`nylorun_trace_export`, on the
4
+ * relay's publication), maps each row to at most one span (`map.ts`), and posts them as OTLP to the
5
+ * operator's collector. It holds no state that changes a span, so a replay of its slot posts the
6
+ * same spans with the same ids. It never reads S2 (D48), and no turn waits for it.
7
+ *
8
+ * - **One process at a time.** The slot is the leader election, as for the relay: every process
9
+ * running core runs a projector, and one reads.
10
+ * - **Batches** of up to 512 spans, or what came within 2 s, posted one at a time
11
+ * (`otlp.ts`: retried, then dropped and logged).
12
+ * - **Acknowledged in commit order** once a transaction's spans are posted or dropped, so a dead
13
+ * collector makes Postgres keep no WAL. A transaction with no span is acknowledged with the
14
+ * rest. While the collector is slow, more than `maxQueuedSpans` waiting are dropped.
15
+ * - **Turns.** A bounded cache keeps each turn's trace (`turnTraceOf`) from its `command.message`;
16
+ * a turn not in it (after a restart mid turn, or one evicted) reads its `command.message` once.
17
+ */
18
+ import type { LiveEvent } from "@nylorun/core/contracts";
19
+ import type { ChangeSource } from "../streams/relay/types.js";
20
+ import type { TraceKey } from "./ids.js";
21
+ import { type TraceContent } from "./map.js";
22
+ import type { OtlpPoster } from "./otlp.js";
23
+ /** The projector's replication slot. */
24
+ export declare const TRACE_SLOT = "nylorun_trace_export";
25
+ export interface TraceProjectorOptions {
26
+ readonly source: ChangeSource;
27
+ readonly post: OtlpPoster;
28
+ /** The Tenant's trace key (`traces/key.ts`). */
29
+ readonly key: () => Promise<TraceKey>;
30
+ /** The `command.message` that started turn `turnId`, or undefined when the log has none. */
31
+ readonly turnMessage: (sessionId: string, turnId: string) => Promise<LiveEvent | undefined>;
32
+ readonly content: TraceContent;
33
+ /** Spans per post. Default 512. */
34
+ readonly maxBatch?: number;
35
+ /** How long a span waits for its batch to fill. Default 2 s. */
36
+ readonly maxDelayMs?: number;
37
+ /** Spans waiting while a post is out, past which the waiting ones are dropped. Default 8192. */
38
+ readonly maxQueuedSpans?: number;
39
+ /** Turns whose trace is kept. Default 1024. */
40
+ readonly turnCacheSize?: number;
41
+ readonly log?: (message: string, fields?: Record<string, unknown>) => void;
42
+ }
43
+ export interface TraceProjector {
44
+ start(): void;
45
+ stop(): Promise<void>;
46
+ /** This process reads the slot, and what it has not acknowledged. */
47
+ status(): {
48
+ active: boolean;
49
+ pendingTxs: number;
50
+ pendingSpans: number;
51
+ confirmed: string | null;
52
+ };
53
+ /** Resolves once everything received is posted or dropped and acknowledged (tests, shutdown). */
54
+ idle(): Promise<void>;
55
+ /** How far the slot is behind, in bytes, when known. */
56
+ lag(): Promise<number | undefined>;
57
+ }
58
+ export declare function createTraceProjector(options: TraceProjectorOptions): TraceProjector;
@@ -0,0 +1,200 @@
1
+ import { spanOf, turnTraceOf } from "./map.js";
2
+ /** The projector's replication slot. */
3
+ export const TRACE_SLOT = "nylorun_trace_export";
4
+ export function createTraceProjector(options) {
5
+ const { source, post } = options;
6
+ const log = options.log ?? (() => { });
7
+ const maxBatch = options.maxBatch ?? 512;
8
+ const maxDelayMs = options.maxDelayMs ?? 2000;
9
+ const maxQueued = options.maxQueuedSpans ?? 8192;
10
+ const cacheSize = options.turnCacheSize ?? 1024;
11
+ const turns = new Map();
12
+ const stopping = new AbortController();
13
+ /** Mapped transactions not yet posted, in commit order. */
14
+ let waiting = [];
15
+ let waitingSpans = 0;
16
+ /** Received transactions still being mapped, in commit order. */
17
+ let mapping = Promise.resolve();
18
+ let mappingCount = 0;
19
+ let posting;
20
+ let timer;
21
+ let active = false;
22
+ let confirmed = null;
23
+ /** Moves on with every loss of the source: work of an earlier activation is discarded. */
24
+ let activation = 0;
25
+ let idleWaiters = [];
26
+ function remember(key, trace) {
27
+ turns.delete(key);
28
+ turns.set(key, trace);
29
+ if (turns.size > cacheSize)
30
+ turns.delete(turns.keys().next().value);
31
+ return trace;
32
+ }
33
+ async function turnOf(row, settings) {
34
+ const turnId = row.turnId;
35
+ const cacheKey = `${row.sessionId}\u0000${turnId}`;
36
+ const ids = { tenantId: row.tenantId, sessionId: row.sessionId, turnId };
37
+ if (row.type === "command.message")
38
+ return remember(cacheKey, turnTraceOf(settings, ids, row));
39
+ const known = turns.get(cacheKey);
40
+ if (known)
41
+ return remember(cacheKey, known);
42
+ return remember(cacheKey, turnTraceOf(settings, ids, await options.turnMessage(row.sessionId, turnId)));
43
+ }
44
+ async function spansOf(tx) {
45
+ const spans = [];
46
+ if (tx.rows.length === 0)
47
+ return spans;
48
+ const settings = { key: await options.key(), content: options.content };
49
+ for (const record of tx.rows) {
50
+ const row = record.body;
51
+ if (!row || typeof row !== "object" || row.turnId == null)
52
+ continue;
53
+ const span = spanOf(row, await turnOf(row, settings), settings);
54
+ if (span)
55
+ spans.push(span);
56
+ }
57
+ return spans;
58
+ }
59
+ function onTx(tx) {
60
+ const of = activation;
61
+ mappingCount += 1;
62
+ mapping = mapping.then(async () => {
63
+ try {
64
+ let spans;
65
+ for (;;) {
66
+ try {
67
+ spans = await spansOf(tx);
68
+ break;
69
+ }
70
+ catch (error) {
71
+ // The key or a turn's message could not be read (Postgres away): try again, in order.
72
+ if (of !== activation || stopping.signal.aborted)
73
+ return;
74
+ log("trace_projector_retry", { message: error instanceof Error ? error.message : String(error) });
75
+ await new Promise((resolve) => setTimeout(resolve, 1000));
76
+ }
77
+ }
78
+ if (of !== activation)
79
+ return;
80
+ waiting.push({ endLsn: tx.endLsn, spans });
81
+ waitingSpans += spans.length;
82
+ if (posting && waitingSpans > maxQueued) {
83
+ // The collector is slower than the log: drop what waits, keep its place to acknowledge.
84
+ log("trace_export_dropped", { spans: waitingSpans, reason: "queue full" });
85
+ for (const pending of waiting)
86
+ pending.spans = [];
87
+ waitingSpans = 0;
88
+ }
89
+ schedule();
90
+ }
91
+ finally {
92
+ mappingCount -= 1;
93
+ settle();
94
+ }
95
+ });
96
+ }
97
+ function schedule() {
98
+ if (posting || waiting.length === 0)
99
+ return;
100
+ // Nothing to post: acknowledge at once.
101
+ if (waitingSpans === 0 || waitingSpans >= maxBatch || stopping.signal.aborted)
102
+ return void flush();
103
+ timer ??= setTimeout(() => {
104
+ timer = undefined;
105
+ flush();
106
+ }, maxDelayMs);
107
+ }
108
+ function flush() {
109
+ if (posting || waiting.length === 0)
110
+ return;
111
+ if (timer)
112
+ clearTimeout(timer);
113
+ timer = undefined;
114
+ const of = activation;
115
+ const batch = waiting;
116
+ waiting = [];
117
+ waitingSpans = 0;
118
+ posting = (async () => {
119
+ const spans = batch.flatMap((tx) => tx.spans);
120
+ // A post a shutdown cut short is not the collector's failure: those rows go again.
121
+ let cut = false;
122
+ for (let at = 0; at < spans.length; at += maxBatch)
123
+ if ((await post(spans.slice(at, at + maxBatch), stopping.signal)) === "dropped" && stopping.signal.aborted)
124
+ cut = true;
125
+ // Posted or dropped: the rows are done. A source lost meanwhile replays them anyway.
126
+ if (of === activation && !cut) {
127
+ confirmed = batch.at(-1).endLsn;
128
+ source.acknowledge(confirmed);
129
+ }
130
+ })().finally(() => {
131
+ posting = undefined;
132
+ schedule();
133
+ settle();
134
+ });
135
+ }
136
+ function settle() {
137
+ if (posting || mappingCount > 0 || waiting.length > 0)
138
+ return;
139
+ const waiters = idleWaiters;
140
+ idleWaiters = [];
141
+ for (const wake of waiters)
142
+ wake();
143
+ }
144
+ return {
145
+ start() {
146
+ source.start({
147
+ onActive({ fresh }) {
148
+ active = true;
149
+ log("trace projector active", { fresh });
150
+ // A fresh slot has nothing to reconcile: it exports from now on.
151
+ if (fresh)
152
+ void source.reconciled().catch(() => undefined);
153
+ },
154
+ onTx,
155
+ onKeepalive(lsn) {
156
+ // Caught up: let the slot move past WAL that holds none of our rows.
157
+ if (posting || mappingCount > 0 || waiting.length > 0)
158
+ return undefined;
159
+ confirmed = lsn;
160
+ return lsn;
161
+ },
162
+ onInactive(error) {
163
+ active = false;
164
+ activation += 1;
165
+ // The slot replays everything not acknowledged; what waits is dropped.
166
+ waiting = [];
167
+ waitingSpans = 0;
168
+ if (timer)
169
+ clearTimeout(timer);
170
+ timer = undefined;
171
+ settle();
172
+ if (error)
173
+ log("trace projector lost its source; retrying", {
174
+ message: error instanceof Error ? error.message : String(error),
175
+ });
176
+ },
177
+ });
178
+ },
179
+ async stop() {
180
+ // Post what is mapped (each post gives up at once), then stop reading.
181
+ stopping.abort();
182
+ await mapping.catch(() => undefined);
183
+ flush();
184
+ await posting?.catch(() => undefined);
185
+ await source.stop();
186
+ },
187
+ status: () => ({
188
+ active,
189
+ pendingTxs: waiting.length + mappingCount,
190
+ pendingSpans: waitingSpans,
191
+ confirmed,
192
+ }),
193
+ idle() {
194
+ if (!posting && mappingCount === 0 && waiting.length === 0)
195
+ return Promise.resolve();
196
+ return new Promise((resolve) => idleWaiters.push(resolve));
197
+ },
198
+ lag: async () => (await source.lag?.()) ?? undefined,
199
+ };
200
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The trace route's spans (R7.5): a turn's log rows mapped by the same `spanOf` the projector
3
+ * posts with, under the installation's resource and content opt-ins, so the route and the
4
+ * exporter agree span for span.
5
+ */
6
+ import type { LiveEvent } from "@nylorun/core/contracts";
7
+ import type { TenantContext } from "../tenant/context.js";
8
+ import { type OtlpSpan } from "./map.js";
9
+ /** Turn `turnId`'s spans as an OTLP `ExportTraceServiceRequest`, from its rows in log order. */
10
+ export declare function turnTrace(ctx: TenantContext, sessionId: string, turnId: string, events: readonly LiveEvent[]): Promise<{
11
+ resourceSpans: {
12
+ resource: {
13
+ attributes: import("./map.js").OtlpKeyValue[];
14
+ };
15
+ scopeSpans: {
16
+ scope: {
17
+ name: string;
18
+ version: string;
19
+ };
20
+ spans: OtlpSpan[];
21
+ }[];
22
+ }[];
23
+ }>;
@@ -0,0 +1,16 @@
1
+ import { traceKey } from "./key.js";
2
+ import { spanOf, turnTraceOf } from "./map.js";
3
+ import { DEFAULT_SERVICE_NAME, resourceSpans } from "./otlp.js";
4
+ /** Turn `turnId`'s spans as an OTLP `ExportTraceServiceRequest`, from its rows in log order. */
5
+ export async function turnTrace(ctx, sessionId, turnId, events) {
6
+ const settings = { key: await traceKey(ctx.store), content: ctx.traces?.content ?? new Set() };
7
+ const message = events.find((event) => event.type === "command.message");
8
+ const turn = turnTraceOf(settings, { tenantId: ctx.store.tenantId, sessionId, turnId }, message);
9
+ const spans = [];
10
+ for (const event of events) {
11
+ const span = spanOf(event, turn, settings);
12
+ if (span)
13
+ spans.push(span);
14
+ }
15
+ return resourceSpans(ctx.traces?.resource ?? { serviceName: DEFAULT_SERVICE_NAME }, spans);
16
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * What an agent asks of a session's vaults (`POST /v1/tenant/credential-coverage`): each remote
3
+ * MCP server and each HTTP tool with a `credential`, of the agent, the agents it uses as tools and
4
+ * the agents of a flow. `VaultService.coverage` decides what a session would send for each, as
5
+ * `authorize` decides it for a call.
6
+ */
7
+ import { type AgentManifest, type WorkflowManifest } from "@nylorun/core/define";
8
+ /** One URL a manifest names that a vault credential may be bound to. */
9
+ export interface CredentialNeed {
10
+ readonly kind: "mcp" | "http";
11
+ /** The declaring agent; absent for the manifest's own agent. */
12
+ readonly agentId?: string;
13
+ /** A flow's HTTP stage or HTTP verifier: its stage key. */
14
+ readonly stage?: string;
15
+ /** The MCP server's name, or the HTTP tool's (or stage's) name. */
16
+ readonly name: string;
17
+ /** What a credential selection names: the server's name, or the HTTP tool's `credential`. */
18
+ readonly serverName: string;
19
+ /** The URL as the manifest names it. */
20
+ readonly url: string;
21
+ }
22
+ /**
23
+ * The manifest's needs: its own agent's, then each agent it uses as a tool, then a flow's
24
+ * stages and agents (nested flow agents under their stage keys). Repeats are dropped.
25
+ */
26
+ export declare function credentialNeeds(manifest: AgentManifest | WorkflowManifest): CredentialNeed[];
@@ -0,0 +1,62 @@
1
+ /**
2
+ * What an agent asks of a session's vaults (`POST /v1/tenant/credential-coverage`): each remote
3
+ * MCP server and each HTTP tool with a `credential`, of the agent, the agents it uses as tools and
4
+ * the agents of a flow. `VaultService.coverage` decides what a session would send for each, as
5
+ * `authorize` decides it for a call.
6
+ */
7
+ import { delegatesOf, flowDelegatesOf, forEachFlowNode, isWorkflowManifest, } from "@nylorun/core/define";
8
+ /**
9
+ * The manifest's needs: its own agent's, then each agent it uses as a tool, then a flow's
10
+ * stages and agents (nested flow agents under their stage keys). Repeats are dropped.
11
+ */
12
+ export function credentialNeeds(manifest) {
13
+ const needs = new Map();
14
+ const add = (need) => {
15
+ const key = JSON.stringify([need.kind, need.agentId, need.stage, need.name, need.serverName, need.url]);
16
+ if (!needs.has(key))
17
+ needs.set(key, need);
18
+ };
19
+ const owner = (agentId) => (agentId === undefined ? {} : { agentId });
20
+ const agent = (manifest, agentId) => {
21
+ for (const tool of manifest.tools ?? [])
22
+ if (tool.http?.credential !== undefined)
23
+ add({
24
+ kind: "http",
25
+ ...owner(agentId),
26
+ name: tool.name,
27
+ serverName: tool.http.credential,
28
+ url: tool.http.url,
29
+ });
30
+ for (const server of Object.values(manifest.mcpServers ?? {}))
31
+ add({ kind: "mcp", ...owner(agentId), name: server.name, serverName: server.name, url: server.url });
32
+ for (const child of delegatesOf(manifest))
33
+ agent(child.manifest, child.manifest.id);
34
+ for (const child of flowDelegatesOf(manifest))
35
+ flow(child.manifest, child.manifest.id);
36
+ };
37
+ const flow = (manifest, prefix) => {
38
+ forEachFlowNode(manifest.root, ({ node, key }) => {
39
+ const http = "http" in node ? node.http : "tool" in node ? node.tool.http : undefined;
40
+ if (http?.credential === undefined)
41
+ return;
42
+ add({
43
+ kind: "http",
44
+ stage: key,
45
+ name: "tool" in node ? node.tool.name : key,
46
+ serverName: http.credential,
47
+ url: http.url,
48
+ });
49
+ }, { prefix });
50
+ for (const [id, embedded] of Object.entries(manifest.agents)) {
51
+ if (isWorkflowManifest(embedded))
52
+ flow(embedded, prefix ? `${prefix}/${id}` : id);
53
+ else
54
+ agent(embedded, id);
55
+ }
56
+ };
57
+ if (isWorkflowManifest(manifest))
58
+ flow(manifest, "");
59
+ else
60
+ agent(manifest);
61
+ return [...needs.values()];
62
+ }
@@ -1,4 +1,14 @@
1
1
  export declare class VaultError extends Error {
2
2
  readonly status: number;
3
- constructor(status: number, message: string);
3
+ /** The rejection's code and details, when the answer carries one (`credential_conflict`). */
4
+ readonly rejection: {
5
+ readonly code?: string;
6
+ readonly details?: Record<string, unknown>;
7
+ };
8
+ constructor(status: number, message: string,
9
+ /** The rejection's code and details, when the answer carries one (`credential_conflict`). */
10
+ rejection?: {
11
+ readonly code?: string;
12
+ readonly details?: Record<string, unknown>;
13
+ });
4
14
  }
@@ -1,7 +1,11 @@
1
1
  export class VaultError extends Error {
2
2
  status;
3
- constructor(status, message) {
3
+ rejection;
4
+ constructor(status, message,
5
+ /** The rejection's code and details, when the answer carries one (`credential_conflict`). */
6
+ rejection = {}) {
4
7
  super(message);
5
8
  this.status = status;
9
+ this.rejection = rejection;
6
10
  }
7
11
  }
@@ -17,7 +17,10 @@ export declare class HostModelVault {
17
17
  private readonly store;
18
18
  private readonly kek;
19
19
  constructor(options: HostModelVaultOptions);
20
- /** Reads the active host model secret. Decrypts after the read commits. */
20
+ /**
21
+ * Reads the active host model secret, with the inputs the operator added to catalog models
22
+ * (protocol 12). Decrypts after the read commits.
23
+ */
21
24
  readHostModel(): Promise<HostModelSecret | undefined>;
22
25
  /** Replaces the active host model's secret, keeping its binding. */
23
26
  updateHostCredential(credential: {
@@ -1,3 +1,4 @@
1
+ import { readAddedInputs } from "../tenant/model-setting.js";
1
2
  import { encryptSecret } from "./crypto.js";
2
3
  import { VaultError } from "./error.js";
3
4
  import { activeHostCredentialRow, HOST_VAULT_ID, hostModelAad, modelBinding, readHostPayload, } from "./service.js";
@@ -8,9 +9,15 @@ export class HostModelVault {
8
9
  this.store = options.store;
9
10
  this.kek = options.kek;
10
11
  }
11
- /** Reads the active host model secret. Decrypts after the read commits. */
12
+ /**
13
+ * Reads the active host model secret, with the inputs the operator added to catalog models
14
+ * (protocol 12). Decrypts after the read commits.
15
+ */
12
16
  async readHostModel() {
13
- const row = await this.store.tx((t) => activeHostCredentialRow(t));
17
+ const { row, addedInputs } = await this.store.tx(async (t) => ({
18
+ row: await activeHostCredentialRow(t),
19
+ addedInputs: await readAddedInputs(t),
20
+ }));
14
21
  if (!row)
15
22
  return undefined;
16
23
  const binding = modelBinding(row);
@@ -27,6 +34,7 @@ export class HostModelVault {
27
34
  model: binding.model,
28
35
  ...(binding.baseUrl ? { baseUrl: binding.baseUrl } : {}),
29
36
  ...(binding.settings ? { settings: binding.settings } : {}),
37
+ addedInputs,
30
38
  authType: binding.authType,
31
39
  credential,
32
40
  };