@nylorun/runtime 0.25.0-beta → 0.27.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 (326) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +26 -16
  3. package/dist/adapters/execution/restate.d.ts +15 -18
  4. package/dist/adapters/execution/restate.js +31 -27
  5. package/dist/adapters/replication/pgoutput.d.ts +1 -1
  6. package/dist/adapters/replication/pgoutput.js +3 -2
  7. package/dist/adapters/sandbox/local.js +8 -5
  8. package/dist/adapters/sandbox/virtual.js +2 -2
  9. package/dist/adapters/streams/s2.d.ts +1 -1
  10. package/dist/adapters/streams/s2.js +18 -14
  11. package/dist/api/a2a/card.js +1 -1
  12. package/dist/api/a2a/endpoint.js +3 -4
  13. package/dist/api/a2a/protocol.js +4 -0
  14. package/dist/api/a2a/routes.d.ts +2 -6
  15. package/dist/api/a2a/routes.js +12 -18
  16. package/dist/api/ag-ui/endpoint.js +2 -3
  17. package/dist/api/ag-ui/routes.js +4 -4
  18. package/dist/api/components.d.ts +326 -45
  19. package/dist/api/components.js +5 -1
  20. package/dist/api/gate/keys.js +7 -4
  21. package/dist/api/gate/routes.d.ts +6 -2
  22. package/dist/api/gate/routes.js +15 -11
  23. package/dist/api/http/app.d.ts +2 -2
  24. package/dist/api/http/app.js +2 -2
  25. package/dist/api/http/body.d.ts +15 -3
  26. package/dist/api/http/body.js +15 -7
  27. package/dist/api/http/define.d.ts +1 -1
  28. package/dist/api/http/respond.js +7 -3
  29. package/dist/api/http/routes/access.js +5 -4
  30. package/dist/api/http/routes/artifacts.js +19 -7
  31. package/dist/api/http/routes/coverage.js +1 -1
  32. package/dist/api/http/routes/environment-manifests.js +1 -1
  33. package/dist/api/http/routes/environments.js +6 -6
  34. package/dist/api/http/routes/files.d.ts +2 -1
  35. package/dist/api/http/routes/files.js +31 -5
  36. package/dist/api/http/routes/mcp.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +35 -0
  38. package/dist/api/http/routes/reads.js +50 -22
  39. package/dist/api/http/routes/sessions.js +12 -11
  40. package/dist/api/http/routes/tenant.d.ts +2 -2
  41. package/dist/api/http/routes/tenant.js +69 -16
  42. package/dist/api/inbound-files.d.ts +3 -9
  43. package/dist/api/inbound-files.js +18 -24
  44. package/dist/api/openapi.js +6 -2
  45. package/dist/artifacts/disk.d.ts +0 -4
  46. package/dist/artifacts/disk.js +77 -73
  47. package/dist/artifacts/files.d.ts +13 -4
  48. package/dist/artifacts/files.js +27 -9
  49. package/dist/artifacts/folder-upload.d.ts +75 -0
  50. package/dist/artifacts/folder-upload.js +263 -0
  51. package/dist/artifacts/folders.d.ts +14 -8
  52. package/dist/artifacts/folders.js +19 -11
  53. package/dist/artifacts/links.js +7 -6
  54. package/dist/artifacts/parts.d.ts +9 -14
  55. package/dist/artifacts/parts.js +11 -9
  56. package/dist/artifacts/pipe.d.ts +15 -0
  57. package/dist/artifacts/pipe.js +71 -0
  58. package/dist/artifacts/quota.d.ts +36 -0
  59. package/dist/artifacts/quota.js +44 -0
  60. package/dist/artifacts/service.d.ts +25 -1
  61. package/dist/artifacts/service.js +70 -25
  62. package/dist/blob/fs.js +2 -1
  63. package/dist/blob/s3.d.ts +6 -2
  64. package/dist/blob/s3.js +9 -7
  65. package/dist/blob/types.d.ts +1 -1
  66. package/dist/blob/types.js +1 -1
  67. package/dist/core/limits.d.ts +11 -35
  68. package/dist/core/limits.js +5 -73
  69. package/dist/core/sandbox-routes.js +0 -1
  70. package/dist/core/turn-manifest.d.ts +1 -2
  71. package/dist/core/turn-manifest.js +4 -28
  72. package/dist/execution/memory.d.ts +8 -5
  73. package/dist/execution/memory.js +8 -6
  74. package/dist/execution/types.d.ts +1 -6
  75. package/dist/execution/types.js +0 -5
  76. package/dist/gates/contract.d.ts +3 -2
  77. package/dist/gates/contract.js +1 -1
  78. package/dist/gates/egress-credentials.js +4 -3
  79. package/dist/gates/egress.d.ts +9 -9
  80. package/dist/gates/egress.js +19 -19
  81. package/dist/gates/fixture-model.js +13 -2
  82. package/dist/gates/handler.d.ts +1 -3
  83. package/dist/gates/handler.js +4 -3
  84. package/dist/gates/http-client.d.ts +4 -3
  85. package/dist/gates/http-client.js +5 -7
  86. package/dist/gates/http-tool.d.ts +24 -4
  87. package/dist/gates/http-tool.js +53 -17
  88. package/dist/gates/in-process.d.ts +2 -1
  89. package/dist/gates/in-process.js +17 -9
  90. package/dist/gates/inflight.d.ts +2 -2
  91. package/dist/gates/inflight.js +3 -2
  92. package/dist/gates/journal.js +5 -7
  93. package/dist/gates/mcp-handler.d.ts +4 -3
  94. package/dist/gates/mcp-handler.js +15 -5
  95. package/dist/gates/model-gate.d.ts +11 -2
  96. package/dist/gates/policy.d.ts +28 -5
  97. package/dist/gates/policy.js +60 -17
  98. package/dist/gates/provider-gate.d.ts +22 -0
  99. package/dist/gates/provider-gate.js +30 -0
  100. package/dist/gates/tool-calls.d.ts +1 -3
  101. package/dist/gates/tool-calls.js +2 -3
  102. package/dist/gates/tool-client.d.ts +1 -1
  103. package/dist/gates/tool-client.js +5 -6
  104. package/dist/gates/tool-contract.d.ts +8 -5
  105. package/dist/gates/tool-contract.js +1 -1
  106. package/dist/gates/tool-gate.d.ts +18 -7
  107. package/dist/gates/tool-gate.js +49 -7
  108. package/dist/harness/calls.d.ts +27 -25
  109. package/dist/harness/calls.js +40 -46
  110. package/dist/harness/executors.d.ts +10 -3
  111. package/dist/harness/executors.js +53 -23
  112. package/dist/harness/main.js +2 -2
  113. package/dist/harness/pod.d.ts +0 -2
  114. package/dist/harness/pod.js +7 -6
  115. package/dist/harness/run-tokens.d.ts +4 -1
  116. package/dist/harness/run-tokens.js +6 -2
  117. package/dist/harness/service.d.ts +17 -14
  118. package/dist/harness/service.js +21 -35
  119. package/dist/harness/setup.d.ts +0 -2
  120. package/dist/harness/setup.js +4 -9
  121. package/dist/harness/ws-client.d.ts +2 -2
  122. package/dist/harness/ws-client.js +7 -5
  123. package/dist/harness/ws-port.d.ts +3 -4
  124. package/dist/harness/ws-port.js +3 -4
  125. package/dist/harness-api/artifacts.d.ts +20 -0
  126. package/dist/harness-api/artifacts.js +478 -0
  127. package/dist/harness-api/in-process.d.ts +2 -2
  128. package/dist/harness-api/in-process.js +18 -16
  129. package/dist/harness-api/record.d.ts +10 -9
  130. package/dist/harness-api/record.js +102 -157
  131. package/dist/harness-api/renew.d.ts +1 -1
  132. package/dist/harness-api/renew.js +2 -2
  133. package/dist/harness-api/server.d.ts +1 -16
  134. package/dist/harness-api/server.js +82 -73
  135. package/dist/harness-api/start.d.ts +20 -2
  136. package/dist/harness-api/start.js +57 -18
  137. package/dist/harness-api/tool-journal.d.ts +15 -10
  138. package/dist/harness-api/tool-journal.js +26 -24
  139. package/dist/harness-api/workspace.d.ts +0 -4
  140. package/dist/harness-api/workspace.js +2 -45
  141. package/dist/harness-api/ws-server.js +10 -10
  142. package/dist/host/create-host.js +2 -3
  143. package/dist/host/execution.js +2 -3
  144. package/dist/host/gates.d.ts +2 -9
  145. package/dist/host/gates.js +15 -12
  146. package/dist/host/http.d.ts +0 -2
  147. package/dist/host/http.js +0 -27
  148. package/dist/host/main.js +10 -3
  149. package/dist/host/operate.js +12 -3
  150. package/dist/host/stack-config.d.ts +11 -0
  151. package/dist/host/stack-config.js +21 -1
  152. package/dist/index.d.ts +0 -1
  153. package/dist/index.js +0 -1
  154. package/dist/infra/index.js +2 -1
  155. package/dist/infra/readiness.d.ts +0 -2
  156. package/dist/infra/readiness.js +4 -3
  157. package/dist/keys/client.d.ts +1 -0
  158. package/dist/keys/client.js +2 -3
  159. package/dist/keys/contract.d.ts +1 -3
  160. package/dist/keys/contract.js +1 -3
  161. package/dist/keys/egress-ca.d.ts +0 -2
  162. package/dist/keys/egress-ca.js +2 -3
  163. package/dist/keys/keys.js +6 -1
  164. package/dist/keys/x509.d.ts +8 -4
  165. package/dist/keys/x509.js +14 -5
  166. package/dist/management-openapi.json +1961 -994
  167. package/dist/mcp/connect.d.ts +35 -26
  168. package/dist/mcp/connect.js +112 -141
  169. package/dist/mcp/pool.d.ts +27 -22
  170. package/dist/mcp/pool.js +34 -48
  171. package/dist/mcp/preview.d.ts +13 -6
  172. package/dist/mcp/preview.js +37 -20
  173. package/dist/mcp/snapshot.d.ts +35 -86
  174. package/dist/mcp/snapshot.js +93 -135
  175. package/dist/model/auth-store.js +5 -3
  176. package/dist/model/capabilities.d.ts +1 -1
  177. package/dist/model/catalog.d.ts +9 -36
  178. package/dist/model/catalog.js +6 -33
  179. package/dist/model/descriptor.d.ts +51 -0
  180. package/dist/model/descriptor.js +83 -0
  181. package/dist/model/models.d.ts +0 -3
  182. package/dist/model/models.js +3 -5
  183. package/dist/model/native-blocks.js +1 -1
  184. package/dist/model/pi-model.d.ts +16 -5
  185. package/dist/model/pi-model.js +86 -105
  186. package/dist/openapi.json +866 -1218
  187. package/dist/reads/cursor.js +3 -1
  188. package/dist/record/append.d.ts +34 -2
  189. package/dist/record/append.js +50 -1
  190. package/dist/record/environment.d.ts +2 -0
  191. package/dist/record/environment.js +2 -0
  192. package/dist/record/index.d.ts +1 -1
  193. package/dist/record/index.js +1 -1
  194. package/dist/redact.d.ts +1 -1
  195. package/dist/redact.js +3 -2
  196. package/dist/sandbox/egress-token.d.ts +3 -3
  197. package/dist/sandbox/egress-token.js +3 -3
  198. package/dist/sandbox/join.js +3 -2
  199. package/dist/sandbox/manager.d.ts +41 -41
  200. package/dist/sandbox/manager.js +112 -44
  201. package/dist/sandbox/placement.d.ts +1 -1
  202. package/dist/sandbox/placement.js +5 -8
  203. package/dist/sandbox/pods/client.d.ts +1 -10
  204. package/dist/sandbox/pods/client.js +8 -4
  205. package/dist/sandbox/pods/lifecycle.d.ts +19 -11
  206. package/dist/sandbox/pods/lifecycle.js +45 -23
  207. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  208. package/dist/sandbox/pods/network-gate.js +7 -7
  209. package/dist/sandbox/pods/reconcile.js +4 -1
  210. package/dist/sandbox/pods/spec.js +8 -5
  211. package/dist/sandbox/policy.d.ts +7 -1
  212. package/dist/sandbox/policy.js +10 -3
  213. package/dist/sandbox/resolve.d.ts +8 -3
  214. package/dist/sandbox/resolve.js +12 -2
  215. package/dist/sandbox/setup.d.ts +3 -1
  216. package/dist/sandbox/setup.js +34 -11
  217. package/dist/sandbox/share.d.ts +5 -14
  218. package/dist/sandbox/share.js +5 -34
  219. package/dist/sandbox/skills.d.ts +7 -13
  220. package/dist/sandbox/skills.js +16 -31
  221. package/dist/sandbox/tenant-config.d.ts +2 -2
  222. package/dist/sandbox/tenant-config.js +4 -3
  223. package/dist/sandbox/tools.d.ts +7 -1
  224. package/dist/sandbox/tools.js +26 -26
  225. package/dist/sandbox/types.d.ts +10 -3
  226. package/dist/sandbox/types.js +9 -0
  227. package/dist/store/postgres/connect.d.ts +6 -5
  228. package/dist/store/postgres/connect.js +14 -11
  229. package/dist/store/postgres/control.js +13 -13
  230. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  231. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  232. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  233. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  234. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  235. package/dist/store/postgres/migrate.js +1 -1
  236. package/dist/store/postgres/reads.js +10 -2
  237. package/dist/store/postgres/schema.d.ts +19 -0
  238. package/dist/store/postgres/schema.js +7 -0
  239. package/dist/store/postgres/store.js +55 -26
  240. package/dist/store/types.d.ts +17 -7
  241. package/dist/streams/relay/core.d.ts +3 -3
  242. package/dist/streams/relay/core.js +9 -10
  243. package/dist/tenant/advance.js +21 -31
  244. package/dist/tenant/commands.js +9 -16
  245. package/dist/tenant/context.d.ts +14 -17
  246. package/dist/tenant/definition-files.d.ts +25 -4
  247. package/dist/tenant/definition-files.js +85 -47
  248. package/dist/tenant/effects.js +9 -36
  249. package/dist/tenant/environment-manifests.js +9 -1
  250. package/dist/tenant/environments.d.ts +0 -2
  251. package/dist/tenant/environments.js +54 -30
  252. package/dist/tenant/ephemeral.d.ts +1 -1
  253. package/dist/tenant/ephemeral.js +2 -1
  254. package/dist/tenant/history.d.ts +1 -1
  255. package/dist/tenant/history.js +1 -1
  256. package/dist/tenant/host-token.d.ts +3 -3
  257. package/dist/tenant/host-token.js +12 -11
  258. package/dist/tenant/http.d.ts +5 -8
  259. package/dist/tenant/http.js +9 -0
  260. package/dist/tenant/identity-file.d.ts +24 -1
  261. package/dist/tenant/identity-file.js +35 -7
  262. package/dist/tenant/issuers.d.ts +5 -5
  263. package/dist/tenant/issuers.js +31 -23
  264. package/dist/tenant/jwt.d.ts +8 -2
  265. package/dist/tenant/jwt.js +10 -2
  266. package/dist/tenant/limits.d.ts +90 -0
  267. package/dist/tenant/limits.js +175 -0
  268. package/dist/tenant/module.d.ts +4 -1
  269. package/dist/tenant/module.js +9 -8
  270. package/dist/tenant/outbound.d.ts +17 -19
  271. package/dist/tenant/outbound.js +18 -21
  272. package/dist/tenant/run-grants.js +3 -2
  273. package/dist/tenant/run-token.d.ts +0 -4
  274. package/dist/tenant/run-token.js +6 -11
  275. package/dist/tenant/runtime-limits.d.ts +24 -0
  276. package/dist/tenant/runtime-limits.js +27 -0
  277. package/dist/tenant/runtime.js +30 -43
  278. package/dist/tenant/session-streams.js +7 -6
  279. package/dist/tenant/session.d.ts +1 -1
  280. package/dist/tenant/session.js +1 -1
  281. package/dist/tenant/sessions.js +32 -29
  282. package/dist/tenant/signing-keys.d.ts +1 -1
  283. package/dist/tenant/signing-keys.js +5 -6
  284. package/dist/tenant/status.js +4 -3
  285. package/dist/tenant/streams.js +18 -20
  286. package/dist/tenant/sweep.d.ts +1 -21
  287. package/dist/tenant/sweep.js +39 -48
  288. package/dist/tenant/transcript.d.ts +70 -23
  289. package/dist/tenant/transcript.js +28 -22
  290. package/dist/tenant/types.d.ts +11 -16
  291. package/dist/tenant/worker.d.ts +2 -10
  292. package/dist/tenant/worker.js +5 -14
  293. package/dist/traces/ids.js +5 -1
  294. package/dist/traces/map.d.ts +9 -6
  295. package/dist/traces/map.js +9 -11
  296. package/dist/traces/otlp.d.ts +3 -3
  297. package/dist/traces/otlp.js +5 -3
  298. package/dist/traces/projector.d.ts +9 -4
  299. package/dist/traces/projector.js +13 -7
  300. package/dist/traces/read.js +6 -1
  301. package/dist/tuning.d.ts +383 -0
  302. package/dist/tuning.js +658 -0
  303. package/dist/vault/headers.js +4 -2
  304. package/dist/version.d.ts +1 -1
  305. package/dist/version.js +1 -1
  306. package/package.json +3 -3
  307. package/dist/artifacts/export.d.ts +0 -8
  308. package/dist/artifacts/export.js +0 -204
  309. package/dist/artifacts/media-types.d.ts +0 -30
  310. package/dist/artifacts/media-types.js +0 -137
  311. package/dist/artifacts/workspace.d.ts +0 -60
  312. package/dist/artifacts/workspace.js +0 -45
  313. package/dist/media.d.ts +0 -10
  314. package/dist/media.js +0 -58
  315. package/dist/sandbox/runtime-tools.d.ts +0 -9
  316. package/dist/sandbox/runtime-tools.js +0 -16
  317. package/dist/sandbox/session-sandbox.d.ts +0 -16
  318. package/dist/sandbox/session-sandbox.js +0 -77
  319. package/dist/tenant/artifact-tool.d.ts +0 -13
  320. package/dist/tenant/artifact-tool.js +0 -243
  321. package/dist/tenant/skill-tool.d.ts +0 -20
  322. package/dist/tenant/skill-tool.js +0 -67
  323. package/dist/tenant/tool-results.d.ts +0 -57
  324. package/dist/tenant/tool-results.js +0 -298
  325. package/dist/tenant/tool-search.d.ts +0 -39
  326. package/dist/tenant/tool-search.js +0 -103
@@ -1,22 +1,33 @@
1
1
  import { type Credential } from "@earendil-works/pi-ai";
2
2
  import type { RuntimeModelAdapter } from "../contracts.js";
3
+ import { type RuntimeLimits } from "@nylorun/core/contracts";
3
4
  import { type FileResolver } from "../artifacts/files.js";
4
5
  import type { HostModelSecret } from "../vault/service.js";
5
6
  export interface ModelPreview {
6
7
  readonly invocationId: string;
7
8
  readonly text: string;
8
9
  }
9
- /** Retry, timeout and cache settings for one model call (Model Calls §5, §6). */
10
+ /**
11
+ * Retry and timeout settings for one model call (Model Calls §5, §6): the Tenant's Runtime limits
12
+ * `model*` (`modelCallSettingsOf`), which the gates read for each call. Each left out is that
13
+ * limit's default.
14
+ */
10
15
  export interface ModelCallSettings {
11
- /** Attempts per call, including the first. Default 3. */
16
+ /** Attempts per call, including the first (`modelAttempts`). */
12
17
  readonly attempts?: number;
13
- /** Base delay between attempts; doubles each time, with jitter. Default 2 s. */
18
+ /** Base delay between attempts; doubles each time, with jitter (`modelRetryDelayMs`). */
14
19
  readonly retryBaseDelayMs?: number;
15
- /** A stream with no event for this long is aborted and retried. Default 300 s. */
20
+ /** A stream with no event for this long is aborted and retried (`modelIdleMs`). */
16
21
  readonly idleTimeoutMs?: number;
17
- /** Whole-request timeout passed to the provider SDK. Default 600 s. */
22
+ /** Whole-request timeout passed to the provider SDK (`modelRequestMs`). */
18
23
  readonly requestTimeoutMs?: number;
24
+ /** Retries the provider SDK makes within one attempt (`modelProviderRetries`). */
25
+ readonly providerRetries?: number;
26
+ /** The longest delay the provider SDK waits before one of them (`modelRetryDelayMaxMs`). */
27
+ readonly maxRetryDelayMs?: number;
19
28
  }
29
+ /** The model call settings the Runtime limits `limits` set. */
30
+ export declare function modelCallSettingsOf(limits: RuntimeLimits): Required<ModelCallSettings>;
20
31
  export interface PiModelOptions {
21
32
  readonly root?: string;
22
33
  readonly onPreview?: (preview: ModelPreview) => void;
@@ -1,13 +1,25 @@
1
- import { parseJsonWithRepair, retryAssistantCall, } from "@earendil-works/pi-ai";
1
+ import { retryAssistantCall, } from "@earendil-works/pi-ai";
2
+ import { RUNTIME_LIMIT_DEFAULTS } from "@nylorun/core/contracts";
2
3
  import { mediaReferenceOf } from "@nylorun/core/define";
3
4
  import { FileUnavailableError, } from "../artifacts/files.js";
4
- import { essence, inputLabel, isModelImage, isText, modelInputOf, unreadableReason, } from "../artifacts/media-types.js";
5
+ import { essence, inputLabel, isModelImage, modelInputOf } from "@nylorun/core/contracts";
5
6
  import { scrub } from "../redact.js";
6
- import { capabilitiesOf } from "./capabilities.js";
7
7
  import { classifyAssistantError, failure } from "./classify.js";
8
+ import { addedInputsOf, describeModel, selectionOf } from "./descriptor.js";
8
9
  import { modelsFor } from "./models.js";
9
10
  import { NativeBlocks, nativeKindsOf } from "./native-blocks.js";
10
11
  import { projectSecrets } from "./settings.js";
12
+ /** The model call settings the Runtime limits `limits` set. */
13
+ export function modelCallSettingsOf(limits) {
14
+ return {
15
+ attempts: limits.modelAttempts,
16
+ retryBaseDelayMs: limits.modelRetryDelayMs,
17
+ idleTimeoutMs: limits.modelIdleMs,
18
+ requestTimeoutMs: limits.modelRequestMs,
19
+ providerRetries: limits.modelProviderRetries,
20
+ maxRetryDelayMs: limits.modelRetryDelayMaxMs,
21
+ };
22
+ }
11
23
  const OPENAI_COMPATIBLE_REASONING = /^reasoning(_content|_text)?$/;
12
24
  const emptyUsage = () => ({
13
25
  input: 0,
@@ -31,12 +43,7 @@ const noAmbientAuth = {
31
43
  * A provider failure is returned as a failure outcome; only an abort is thrown.
32
44
  */
33
45
  export function piModel(options = {}) {
34
- const settings = {
35
- attempts: Math.max(1, options.settings?.attempts ?? 3),
36
- retryBaseDelayMs: options.settings?.retryBaseDelayMs ?? 2_000,
37
- idleTimeoutMs: options.settings?.idleTimeoutMs ?? 300_000,
38
- requestTimeoutMs: options.settings?.requestTimeoutMs ?? 600_000,
39
- };
46
+ const settings = { ...modelCallSettingsOf(RUNTIME_LIMIT_DEFAULTS), ...options.settings };
40
47
  return async (call, context) => {
41
48
  context.signal.throwIfAborted();
42
49
  // The first stream event's time, from here (R7.1): the meter keeps it on the usage row.
@@ -54,25 +61,15 @@ export function piModel(options = {}) {
54
61
  ...outcome,
55
62
  message: String(scrub(outcome.message, secrets)),
56
63
  });
57
- const requested = call.model?.id;
58
- const selection = {
59
- provider: stored.provider,
60
- model: requested?.startsWith(`${stored.provider}/`)
61
- ? requested.slice(stored.provider.length + 1)
62
- : (requested ?? stored.model),
63
- ...(stored.baseUrl
64
- ? { custom: { baseUrl: stored.baseUrl, ...(stored.settings ?? {}) } }
65
- : {}),
66
- };
64
+ const selection = selectionOf(stored, call.model?.id);
67
65
  const registry = modelsFor(selection, hostCredentialStore(stored, options.writeHostCredential), { environment: false, authContext: noAmbientAuth });
68
66
  const found = registry.getModel(selection.provider, selection.model);
69
67
  if (!found)
70
68
  return failure("invalid_request", "Unknown model. Run nylorun tenant models set.", false);
71
- // What the model reads (protocol 12): its catalog's inputs and the operator's additions.
72
- // pi-ai drops an image the model's `input` lacks, so the call's model carries an added one.
73
- const { inputs } = capabilitiesOf(found, selection.custom
74
- ? stored.settings?.inputs
75
- : stored.addedInputs?.[`${selection.provider}/${selection.model}`]);
69
+ // What the model reads (protocol 12), as `turn.start.model` says: its catalog's inputs and
70
+ // the operator's additions. pi-ai drops an image the model's `input` lacks, so the call's
71
+ // model carries an added one.
72
+ const { inputs } = describeModel(found, addedInputsOf(selection, stored, stored.addedInputs ?? {}));
76
73
  const selected = {
77
74
  ...found,
78
75
  input: inputs.includes("image") ? ["text", "image"] : ["text"],
@@ -80,7 +77,10 @@ export function piModel(options = {}) {
80
77
  // PDFs, Office documents, audio and video go in the API's own blocks: pi-ai builds the
81
78
  // request with a marker in each one's place and `onPayload` swaps it for the bytes.
82
79
  const natives = new NativeBlocks();
80
+ // A part the provider cannot take fails the call before anything is sent: never retried.
83
81
  const request = await buildContext(call, selected, inputs, options, natives);
82
+ if ("failed" in request)
83
+ return redacted(request.failed);
84
84
  // Publish only portable references, never the materialized provider image bytes.
85
85
  context.reportPreparedCall?.({
86
86
  adapter: "runtime.pi-ai",
@@ -96,8 +96,8 @@ export function piModel(options = {}) {
96
96
  sessionId: call.executionId,
97
97
  cacheRetention: "short",
98
98
  timeoutMs: settings.requestTimeoutMs,
99
- maxRetries: 2,
100
- maxRetryDelayMs: 60_000,
99
+ maxRetries: settings.providerRetries,
100
+ maxRetryDelayMs: settings.maxRetryDelayMs,
101
101
  ...(natives.size > 0
102
102
  ? {
103
103
  // A file that cannot be carried stops the request before it is sent.
@@ -211,21 +211,6 @@ export function piModel(options = {}) {
211
211
  ...metadataFor(selected, part.thoughtSignature),
212
212
  });
213
213
  }
214
- if (call.outputSchema &&
215
- !output.some((part) => part.type === "tool-call")) {
216
- const text = output
217
- .filter((part) => part.type === "text")
218
- .map((part) => part.text)
219
- .join("");
220
- let value;
221
- try {
222
- value = parseJsonWithRepair(withoutCodeFence(text));
223
- }
224
- catch (error) {
225
- return failure("invalid_output", `The model's final answer is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, false);
226
- }
227
- output.splice(0, output.length, { type: "json", value });
228
- }
229
214
  return {
230
215
  output,
231
216
  finishReason: response.stopReason === "toolUse"
@@ -255,28 +240,56 @@ export function piModel(options = {}) {
255
240
  };
256
241
  };
257
242
  }
243
+ /** A part the call cannot carry: the call fails with it, before anything is sent. */
244
+ class Unsendable extends Error {
245
+ outcome;
246
+ constructor(outcome) {
247
+ super(outcome.message);
248
+ this.outcome = outcome;
249
+ }
250
+ }
251
+ /**
252
+ * The provider context for `call`. The gate decides nothing about files (protocol 15): a media
253
+ * part names an artifact version the session may read, which it fetches (`options.files`) and
254
+ * translates, an image to an image block and a PDF, Office document, audio or video to a marker
255
+ * for its native block (`natives`). A part the model or its API cannot take is refused
256
+ * (`unsupported_input`): a text file (the harness inlines text), a kind the model does not read.
257
+ * A file it cannot fetch is `invalid_request`. Bytes go only into the provider request built
258
+ * here, never into the transcript or the journal.
259
+ */
258
260
  async function buildContext(call, selected, inputs, options, natives) {
261
+ try {
262
+ return await translate(call, selected, inputs, options, natives);
263
+ }
264
+ catch (error) {
265
+ if (error instanceof Unsendable)
266
+ return { failed: error.outcome };
267
+ throw error;
268
+ }
269
+ }
270
+ async function translate(call, selected, inputs, options, natives) {
259
271
  const messages = [];
260
272
  const instructions = [];
261
- /**
262
- * A file a media part names (protocol 6, 12), as the model gets it: a text file as a `<file>`
263
- * block, an image the model reads as an image, a PDF, Office document, audio or video the model
264
- * reads as a marker for its native block (`natives`), and anything else, or a file that cannot
265
- * be read, as a note. Its bytes go only into the provider request built here, never into the
266
- * transcript or the journal, and no file fails the call here.
267
- */
268
- const materialize = async (part) => {
269
- let name = mediaReferenceOf(part.reference)?.name ?? "file";
270
- let type = essence(part.mediaType);
271
- const note = (why) => ({
272
- type: "text",
273
- text: `(File ${JSON.stringify(name)}, ${type}, is not shown: ${why}.)`,
274
- });
275
- const refused = unreadableReason(part.mediaType, inputs);
276
- if (refused)
277
- return note(refused);
273
+ const unsupported = (message) => new Unsendable(failure("unsupported_input", message, false));
274
+ /** Why the model cannot take a file of `type`, or undefined when it can. */
275
+ const refusal = (name, type) => {
276
+ const kind = modelInputOf(type);
277
+ const file = `File ${JSON.stringify(name)} (${essence(type)})`;
278
+ if (kind === "text")
279
+ return unsupported(`${file} is text: send its contents as a text part.`);
280
+ if (kind === undefined ||
281
+ !inputs.includes(kind) ||
282
+ (kind === "image" && !isModelImage(type)) ||
283
+ (kind !== "image" && !(isNativeKind(kind) && nativeKindsOf(selected.api).includes(kind))))
284
+ return unsupported(`This model (${selected.provider}/${selected.id}) does not read ${kind === "image" && inputs.includes("image") ? "this image format" : inputLabel(kind)}: ${file}.`);
285
+ return undefined;
286
+ };
287
+ const toProvider = async (part) => {
288
+ const declared = refusal(mediaReferenceOf(part.reference)?.name ?? "file", part.mediaType);
289
+ if (declared)
290
+ throw declared;
278
291
  if (!options.files)
279
- return note("files are unavailable");
292
+ throw new Unsendable(failure("invalid_request", "This gate cannot fetch files: no artifact store.", false));
280
293
  let file;
281
294
  try {
282
295
  file = await options.files(part.reference);
@@ -284,37 +297,21 @@ async function buildContext(call, selected, inputs, options, natives) {
284
297
  catch (error) {
285
298
  if (!(error instanceof FileUnavailableError))
286
299
  throw error;
287
- return note(error.message.replace(/\.$/u, ""));
300
+ throw new Unsendable(failure("invalid_request", error.message, false));
288
301
  }
289
- name = file.name;
290
- type = essence(file.mediaType);
291
- const stored = unreadableReason(file.mediaType, inputs);
302
+ const stored = refusal(file.name, file.mediaType);
292
303
  if (stored)
293
- return note(stored);
304
+ throw stored;
305
+ const type = essence(file.mediaType);
306
+ const data = Buffer.from(file.bytes).toString("base64");
294
307
  if (isModelImage(file.mediaType))
295
- return { type: "image", data: Buffer.from(file.bytes).toString("base64"), mimeType: type };
296
- if (isText(file.mediaType)) {
297
- let text;
298
- try {
299
- text = new TextDecoder("utf-8", { fatal: true }).decode(file.bytes);
300
- }
301
- catch {
302
- return note("it is not UTF-8 text");
303
- }
304
- return {
305
- type: "text",
306
- text: `<file name=${JSON.stringify(file.name)} type=${JSON.stringify(type)}>\n${text}\n</file>`,
307
- };
308
- }
309
- const kind = modelInputOf(file.mediaType);
310
- if (isNativeKind(kind) && nativeKindsOf(selected.api).includes(kind))
311
- return natives.add({
312
- kind,
313
- mediaType: type,
314
- name: file.name,
315
- data: Buffer.from(file.bytes).toString("base64"),
316
- });
317
- return note(`the Runtime does not send ${inputLabel(kind)} to this model's API (${selected.api})`);
308
+ return { type: "image", data, mimeType: type };
309
+ return natives.add({
310
+ kind: modelInputOf(file.mediaType),
311
+ mediaType: type,
312
+ name: file.name,
313
+ data,
314
+ });
318
315
  };
319
316
  const content = async (parts) => {
320
317
  const result = [];
@@ -322,7 +319,7 @@ async function buildContext(call, selected, inputs, options, natives) {
322
319
  if (part.type === "text")
323
320
  result.push({ type: "text", text: part.text });
324
321
  else if (part.type === "media")
325
- result.push(await materialize(part));
322
+ result.push(await toProvider(part));
326
323
  }
327
324
  return result;
328
325
  };
@@ -355,18 +352,7 @@ async function buildContext(call, selected, inputs, options, natives) {
355
352
  description: tool.description ?? "",
356
353
  parameters: { ...tool.inputSchema },
357
354
  }));
358
- return {
359
- systemPrompt: [
360
- ...instructions,
361
- ...(call.outputSchema
362
- ? [
363
- `Return the final answer as JSON matching this schema: ${JSON.stringify(call.outputSchema)}. Use tools when needed before returning the final JSON.`,
364
- ]
365
- : []),
366
- ].join("\n"),
367
- messages,
368
- tools,
369
- };
355
+ return { systemPrompt: instructions.join("\n"), messages, tools };
370
356
  }
371
357
  const isNativeKind = (input) => input === "pdf" || input === "office" || input === "audio" || input === "video";
372
358
  /**
@@ -456,11 +442,6 @@ function metadataFor(selected, signature, redacted) {
456
442
  },
457
443
  };
458
444
  }
459
- /** Open-weight models often wrap a JSON answer in a Markdown code fence. */
460
- function withoutCodeFence(text) {
461
- const fenced = /^\s*```[a-zA-Z]*\s*\n([\s\S]*?)\n?```\s*$/.exec(text);
462
- return fenced ? fenced[1] : text;
463
- }
464
445
  function usageOf(usage) {
465
446
  return {
466
447
  inputTokens: usage.input,