@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,13 +1,16 @@
1
1
  /**
2
2
  * The Record seam of the Harness API: core's journal for the effects of a run. `recordIntent`
3
3
  * journals an effect before anything runs it, and either answers it from the journal, hands it
4
- * to core's own executors (flow work, delegation, `save_artifact`, `read_artifact`,
5
- * `tool_search` and the skill tools), tells the harness to `execute` it (model calls; MCP, HTTP
6
- * and sandbox tools, a deferred MCP tool's `tool_call` among them, whose effect names the tool;
7
- * a flow's HTTP stages and HTTP verifiers), or fails a tool the Runtime cannot run (one that
8
- * would run the developer's code, R2 M6). `recordOutcome` records what the harness's call returned, an
9
- * agent's MCP or HTTP tool result first shaped to fit (R2b C11). Both run under the advance's
10
- * lease: every write is epoch-checked (`ownedSession`), and a lost epoch writes nothing.
4
+ * to core's own executors (flow work, delegation), tells the harness to `execute` it (model calls;
5
+ * a flow's HTTP stages and HTTP verifiers; an agent's tool call whose target the session has,
6
+ * protocol 15: an `mcp` tool it discovered and the turn leaves enabled, by `{server, tool}`; an
7
+ * `http` tool it declares; a `sandbox` operation when it has a sandbox; a `harness` tool), or
8
+ * fails a tool call it cannot run (no target, as an author's code tool has, R2 M6, or one the
9
+ * session lacks). A tool call is routed by its target, never by its name. `recordOutcome`
10
+ * records what the harness's call returned, as the harness reported it: one record entry, refused
11
+ * past the Runtime limit `recordEntryBytes` (`limit_exceeded`), since the harness shapes tool
12
+ * results, not core. Both run under the advance's lease: every write is epoch-checked
13
+ * (`ownedSession`), and a lost epoch writes nothing.
11
14
  *
12
15
  * Rows store the request's hash (`requestHash`) and, for a model call, the request without its
13
16
  * prompt: drift is a hash compare. Rows written before carry their full request, hashed on read.
@@ -19,26 +22,24 @@
19
22
  * harness and the Tool Gate reads with the row.
20
23
  */
21
24
  import { HarnessApiError, effectRequestHash, isEffectIdOf, } from "@nylorun/core/harness-api";
25
+ import { isSandboxToolName } from "@nylorun/core/define";
22
26
  import { isFlowEffect, isFlowToolEffect, recordVerdict, settleAgentEffect, } from "../core/flow-host.js";
23
- import { sandboxToolSpec } from "../sandbox/runtime-tools.js";
24
- import { abortOn, isReadArtifactCall, isSaveArtifactCall } from "../harness/calls.js";
25
- import { callReadArtifact, callSaveArtifact } from "../tenant/artifact-tool.js";
26
- import { mayNeedShaping, resultBytes, resultLimit, shapeToolOutcome, } from "../tenant/tool-results.js";
27
- import { callSkillTool, isSkillToolCall } from "../tenant/skill-tool.js";
28
- import { callToolSearch, isToolSearchCall } from "../tenant/tool-search.js";
27
+ import { sessionSandboxSpec } from "../sandbox/share.js";
28
+ import { readRuntimeLimits } from "../tenant/runtime-limits.js";
29
+ import { checkEntry } from "../record/index.js";
29
30
  import { turnManifestOf } from "../tenant/session.js";
30
- import { isOwnershipLost } from "../store/ownership.js";
31
- import { manifestFor, mcpToolOf } from "../mcp/snapshot.js";
31
+ import { isEnabledIn, manifestFor, mcpToolIn } from "../mcp/snapshot.js";
32
+ import { findServer } from "../mcp/pool.js";
32
33
  import { isHttpToolCall } from "../gates/http-tool.js";
33
34
  import { ownedSession } from "../tenant/context.js";
34
35
  import { isGateToolEffect, linkedOutcome, recoversModelCalls, recoversToolCalls, resolveNewFlowEffect, } from "../tenant/effects.js";
35
- import { assistantMessage, contextCompacted, modelFailed, modelOutcomeFacts, } from "../tenant/transcript.js";
36
+ import { assistantMessage, modelFailed, modelOutcomeFacts, } from "../tenant/transcript.js";
36
37
  import { abortKind } from "../tenant/worker.js";
37
38
  import { redactTokens } from "./redact.js";
38
39
  import { toolCallDecision } from "../gates/policy.js";
39
40
  import { traceKey } from "../traces/key.js";
40
41
  import { effectSpanOf, traceparentOf, turnTraceIdOf } from "../traces/ids.js";
41
- import { MAX_TOOL_ATTEMPTS, firstSend, interrupted, settleToolRow, toolKindOf, } from "./tool-journal.js";
42
+ import { firstSend, interrupted, settleToolRow, toolKindOf, } from "./tool-journal.js";
42
43
  const TURN_CANCELLED = () => new HarnessApiError("turn_cancelled", "Turn cancelled");
43
44
  /** The hash a journal row's request was recorded with. */
44
45
  export function hashOf(row) {
@@ -137,36 +138,20 @@ export async function recordIntent(scope, effect, requestHash) {
137
138
  const agentManifest = manifestFor(s.manifest, request.agent);
138
139
  if (!agentManifest)
139
140
  throw new Error(`Agent '${request.agent?.id ?? ""}' is not used as a tool`);
140
- const executed = request.kind === "model" ||
141
- (request.kind === "tool" &&
142
- // An MCP tool the turn's manifest disables is not one (R2b C9).
143
- (mcpToolOf(s.mcpSnapshot, request, turnManifestOf(s)) !== undefined ||
144
- // HTTP tools (R2 M3) cross the Tool Gate.
145
- isHttpToolCall(s.manifest, request) ||
146
- sandboxToolSpec(agentManifest, request.toolName) !== undefined ||
147
- // `save_artifact` (F8.1) runs beside the sandbox tools, as do `read_artifact` (R2b
148
- // C11) and the skill tools (R2 M4).
149
- isSaveArtifactCall(agentManifest, request) ||
150
- isReadArtifactCall(agentManifest, request) ||
151
- isSkillToolCall(agentManifest, request) ||
152
- // `tool_search` (R2b C10) reads the session's pinned MCP snapshot: core serves it.
153
- isToolSearchCall(s.mcpSnapshot, request) ||
154
- // A tool the run's harness declared and runs itself (R4.2, Q3), by its name: the turn
155
- // refuses a native tool whose name another tool of the agent's has (`harness-api/server.ts`).
156
- (request.toolName !== undefined && scope.nativeTools?.has(request.toolName) === true)));
157
- if (!executed) {
158
- // A tool that would run the developer's code: refused at save (a turn's manifest only
159
- // removes tools), so this is a backstop, as is an MCP tool the turn disabled, which the
160
- // model was never offered. The model sees the failure; nothing runs.
161
- const disabled = request.kind === "tool" && mcpToolOf(s.mcpSnapshot, request) !== undefined;
141
+ const refused = request.kind === "tool" ? refusal(s, request) : undefined;
142
+ if (refused) {
143
+ // A call with no target (an author's code tool, refused at save, so a backstop), or one
144
+ // whose target the session lacks: an MCP tool it never discovered or the turn disables, an
145
+ // HTTP tool its agent does not declare, a sandbox call without a sandbox. The model sees
146
+ // the failure; nothing runs.
162
147
  const row = {
163
148
  request,
164
149
  requestHash,
165
150
  status: "invoking",
166
- ...toolKindOf(s, agentManifest, request, scope.nativeTools),
151
+ ...toolKindOf(s, agentManifest, request),
167
152
  attempts: 0,
168
153
  };
169
- const outcome = await settleToolRow(t, s, row, disabled ? disabledTool(request) : unrunnableTool(request), { writer: coreIn(scope) });
154
+ const outcome = await settleToolRow(t, s, row, refused, { writer: coreIn(scope) });
170
155
  return { status: "completed", outcome };
171
156
  }
172
157
  const row = {
@@ -175,7 +160,7 @@ export async function recordIntent(scope, effect, requestHash) {
175
160
  requestHash,
176
161
  status: "invoking",
177
162
  ...(request.kind === "tool"
178
- ? { ...firstSend(toolKindOf(s, agentManifest, request, scope.nativeTools)), ...traced() }
163
+ ? { ...firstSend(toolKindOf(s, agentManifest, request)), ...traced() }
179
164
  : { startedAt: new Date().toISOString() }),
180
165
  };
181
166
  // The Tenant's tool policy decides an MCP or HTTP tool call as it is journaled (R5 step 2).
@@ -185,28 +170,8 @@ export async function recordIntent(scope, effect, requestHash) {
185
170
  return decided;
186
171
  }
187
172
  await t.put("effects", request.effectId, row);
188
- // `save_artifact` writes the Tenant's artifacts: core runs it, reading the file through the
189
- // workspace feature, wherever the sandbox is.
190
- if (request.kind === "tool" && isSaveArtifactCall(agentManifest, request))
191
- return "save";
192
- // `read_artifact` reads the session's artifacts: core serves it, with or without a sandbox.
193
- if (isReadArtifactCall(agentManifest, request))
194
- return "read";
195
- // The skill tools read the agent's definition files: core serves them.
196
- if (isSkillToolCall(agentManifest, request))
197
- return { skill: agentManifest };
198
- if (isToolSearchCall(s.mcpSnapshot, request))
199
- return "search";
200
173
  return { status: "execute", ...(row.traceparent ? { traceparent: row.traceparent } : {}) };
201
174
  });
202
- if (answer === "save")
203
- return runInCore(scope, request, () => callSaveArtifact(scope.ctx, request, scope.signal));
204
- if (answer === "read")
205
- return runInCore(scope, request, () => callReadArtifact(scope.ctx, request, scope.signal));
206
- if (answer === "search")
207
- return runInCore(scope, request, () => callToolSearch(scope.ctx, request));
208
- if (typeof answer === "object" && "skill" in answer)
209
- return runInCore(scope, request, () => callSkillTool(scope.ctx, answer.skill, request, scope.signal));
210
175
  if (answer !== "flow")
211
176
  return answer;
212
177
  if (!isFlowEffect(request))
@@ -214,27 +179,50 @@ export async function recordIntent(scope, effect, requestHash) {
214
179
  return resolveNewFlowEffect(ctx, request, signal, lease);
215
180
  }
216
181
  /**
217
- * Runs a tool core serves (`save_artifact`, F8.1; `read_artifact`, R2b C11; `tool_search`, R2b
218
- * C10; the skill tools, R2 M4) for the run and records its outcome, as a harness would record a
219
- * call it ran: the run gets
220
- * the outcome as the intent's answer.
182
+ * Why an agent's tool call cannot run, by what it names as its target (protocol 15), or
183
+ * undefined when it can:
184
+ *
185
+ * - `mcp`: the tool `{server, tool}` of an MCP server its agent declares, as the session
186
+ * discovered it (`tool.unknown` otherwise), left enabled by the turn's manifest (R2b C9,
187
+ * `tool.disabled`);
188
+ * - `http`: an HTTP tool its agent declares under that name (R2 M3), which crosses the Tool Gate;
189
+ * - `sandbox`: one of the sandbox operations, in a session with a sandbox;
190
+ * - `harness`: a tool the harness runs in its own process; its egress gate is its only control.
191
+ *
192
+ * No target, and anything else, runs nothing (`tool.unavailable`).
221
193
  */
222
- async function runInCore(scope, request, call) {
223
- // Core ran the call: its outcome is what core saw, not the harness's claim (R4.9).
224
- const writer = coreIn(scope);
225
- const core = { ctx: scope.ctx, lease: scope.lease, signal: scope.signal, ...(writer ? { writer } : {}) };
226
- let value;
227
- try {
228
- value = await call();
229
- }
230
- catch (error) {
231
- if (isOwnershipLost(error))
232
- throw error;
233
- return recordOutcome(core, request.effectId, {
234
- error: error instanceof Error ? error.message : String(error),
235
- });
194
+ function refusal(s, request) {
195
+ const target = request.target;
196
+ switch (target?.kind) {
197
+ case "mcp": {
198
+ const agentId = request.agent?.id;
199
+ if (!findServer(s.manifest, agentId, target.server) || !mcpToolIn(s.mcpSnapshot, agentId, target.server, target.tool))
200
+ return {
201
+ kind: "failed",
202
+ code: "tool.unknown",
203
+ message: `Tool '${target.tool}' of MCP server '${target.server}' was not discovered for this session.`,
204
+ };
205
+ if (!isEnabledIn(turnManifestOf(s), agentId, target.server, target.tool))
206
+ return {
207
+ kind: "failed",
208
+ code: "tool.disabled",
209
+ message: `Tool '${target.tool}' is disabled by its MCP server's tools settings.`,
210
+ };
211
+ return undefined;
212
+ }
213
+ case "http":
214
+ return isHttpToolCall(s.manifest, request) ? undefined : unrunnableTool(request);
215
+ case "sandbox":
216
+ return sessionSandboxSpec(s) !== undefined &&
217
+ request.toolName !== undefined &&
218
+ isSandboxToolName(request.toolName)
219
+ ? undefined
220
+ : unrunnableTool(request);
221
+ case "harness":
222
+ return undefined;
223
+ default:
224
+ return unrunnableTool(request);
236
225
  }
237
- return recordOutcome(core, request.effectId, { value });
238
226
  }
239
227
  /** Core's own write during the scope's run (R4.9): core saw it, in that run. */
240
228
  function coreIn(scope) {
@@ -244,9 +232,9 @@ function coreIn(scope) {
244
232
  /**
245
233
  * The answer to a tool row still `invoking` (R5.3, R5.4): a crash, a shutdown or a lost gate
246
234
  * hop interrupted its send. An MCP or HTTP call (it crosses the Tool Gate, which hands the tool
247
- * its key) is sent again, one more attempt, until `MAX_TOOL_ATTEMPTS`; then it fails to the
248
- * model. Any other call (sandbox, core-served) has no key to dedupe on: it fails to the model
249
- * now, and is not run again (Q2).
235
+ * its key) is sent again, one more attempt, until the Tenant's `toolAttempts`; then it fails to the
236
+ * model. Any other call (sandbox, harness, core-served) has no key to dedupe on: it fails to the
237
+ * model now, and is not run again (Q2).
250
238
  */
251
239
  async function resend(t, s, row, rejoin) {
252
240
  if (!isGateToolEffect(s, row.request)) {
@@ -254,8 +242,9 @@ async function resend(t, s, row, rejoin) {
254
242
  return { status: "completed", outcome };
255
243
  }
256
244
  const attempts = (row.attempts ?? 1) + 1;
257
- if (attempts > MAX_TOOL_ATTEMPTS) {
258
- const outcome = await settleToolRow(t, s, row, interrupted(`The call was interrupted ${MAX_TOOL_ATTEMPTS} times and may have run.`));
245
+ const { toolAttempts } = await readRuntimeLimits(t);
246
+ if (attempts > toolAttempts) {
247
+ const outcome = await settleToolRow(t, s, row, interrupted(`The call was interrupted ${toolAttempts} times and may have run (the Runtime limit toolAttempts).`));
259
248
  return { status: "completed", outcome };
260
249
  }
261
250
  row.attempts = attempts;
@@ -295,16 +284,24 @@ async function bypolicy(t, s, row, kind) {
295
284
  await t.put("effects", row.request.effectId, row);
296
285
  return { status: "pending" };
297
286
  }
298
- /** The failed outcome of an MCP tool the turn's manifest disables (R2b C9). */
299
- function disabledTool(request) {
300
- return {
301
- kind: "failed",
302
- code: "tool.disabled",
303
- message: `Tool '${request.toolName ?? ""}' is disabled by its MCP server's tools settings.`,
304
- };
305
- }
306
- /** The failed outcome of a tool the Runtime cannot run: it would run the developer's code. */
287
+ /**
288
+ * The failed outcome of a tool call the Runtime cannot run: one that names no target (an author's
289
+ * code tool: the Runtime runs agents from their manifests alone), or one whose target the session
290
+ * lacks (a sandbox call without a sandbox).
291
+ */
307
292
  function unrunnableTool(request) {
293
+ if (request.target?.kind === "sandbox")
294
+ return {
295
+ kind: "failed",
296
+ code: "tool.unavailable",
297
+ message: `Tool '${request.toolName ?? ""}' runs in a sandbox, and this session has none.`,
298
+ };
299
+ if (request.target !== undefined)
300
+ return {
301
+ kind: "failed",
302
+ code: "tool.unavailable",
303
+ message: `Tool '${request.toolName ?? ""}' is not one this session can run (${request.target.kind}).`,
304
+ };
308
305
  return {
309
306
  kind: "failed",
310
307
  code: "tool.unavailable",
@@ -347,12 +344,16 @@ export async function recordOutcome(scope, effectId, reported) {
347
344
  });
348
345
  }
349
346
  // What a harness reports never carries the Runtime's own tokens into the record (R4).
350
- const value = await shaped(scope, effectId, redactTokens(result.value));
347
+ const value = redactTokens(result.value);
351
348
  return ctx.store.tx(async (t) => {
352
349
  const s = await t.assertEpoch(lease.sessionId, lease.epoch);
353
350
  const effect = await t.get("effects", effectId);
354
351
  if (!effect)
355
352
  throw new HarnessApiError("invalid", `Effect ${effectId} was never journaled`);
353
+ // An entry of the record (S0): past the Tenant's `recordEntryBytes`, `limit_exceeded`. The
354
+ // harness shapes what it reports to fit, storing the rest with `artifact.write`; the row
355
+ // stays as it was.
356
+ checkEntry(`The outcome of effect ${effectId} (store the rest with artifact.write)`, value, (await readRuntimeLimits(t)).recordEntryBytes);
356
357
  const { request } = effect;
357
358
  if (s.status === "cancelled" || s.activeTurnId !== request.turnId || abortKind(signal) === "cancel")
358
359
  throw TURN_CANCELLED();
@@ -360,7 +361,7 @@ export async function recordOutcome(scope, effectId, reported) {
360
361
  // Settled already: a re-send after a lost hop collected what the first send recorded.
361
362
  if (effect.status === "completed")
362
363
  return { status: "completed", outcome: effect.outcome };
363
- // What the call returned is the harness's claim, or, for a tool core ran, core's (R4.9).
364
+ // What the call returned is the harness's claim (R4.9).
364
365
  const outcome = await settleToolRow(t, s, effect, value, { writer: scope.writer });
365
366
  if (request.context?.role === "verify-http")
366
367
  await recordVerdict(t, request, value);
@@ -372,80 +373,24 @@ export async function recordOutcome(scope, effectId, reported) {
372
373
  await t.put("effects", effectId, effect);
373
374
  const model = request.kind === "model";
374
375
  const failed = model ? modelFailed(request, value) : undefined;
375
- // A summary call is never an assistant message; it publishes context.compacted, `partial`
376
- // unless it is the last.
377
- const summarizing = model && Boolean(request.context.compaction);
378
- const compacted = summarizing && !failed ? contextCompacted(request, value) : undefined;
376
+ // A summary call is never an assistant message: the compaction's transcript edit names it,
377
+ // and `context.compacted` is written with that edit (Q4).
378
+ const summarizing = model && request.purpose === "summary";
379
379
  const transcript = failed || summarizing || !model ? undefined : assistantMessage(request, value);
380
380
  if (request.context?.role === "verify-http")
381
381
  await recordVerdict(t, request, value);
382
382
  const writer = scope.writer;
383
383
  // The call's span (R7.1): the journal's window, and the meter's row for it when a gate ran it.
384
- const facts = failed || compacted || transcript
384
+ const facts = failed || transcript
385
385
  ? modelOutcomeFacts(effectId, effect, await t.modelUsageOf(effectId))
386
386
  : undefined;
387
387
  if (failed)
388
388
  await t.event(s.id, request.turnId, "model.failed", { ...failed, ...facts }, writer);
389
- if (compacted)
390
- await t.event(s.id, request.turnId, "context.compacted", { ...compacted, ...facts }, writer);
391
389
  if (transcript)
392
390
  await t.event(s.id, request.turnId, "message.assistant", { ...transcript, ...facts }, writer);
393
391
  return { status: "completed", outcome: effect.outcome };
394
392
  });
395
393
  }
396
- /**
397
- * The outcome of an agent's remote MCP or HTTP tool call, shaped to fit before anything records
398
- * it (R2b C11, `tenant/tool-results.ts`): what does not fit becomes the session's artifacts. Any
399
- * other outcome, and one a cancel will discard, as it is. Only a cancel stops the upload: after a
400
- * shutdown or a deadline the outcome is still recorded, so the next advance replays it.
401
- *
402
- * Its cap is what its step's budget has left (`resultLimit`): a step's results are one transcript
403
- * entry. Parallel results are counted under the session lock, each reserving its share on its
404
- * row (`resultBytes`) before it is shaped, so two never spend the same room.
405
- */
406
- async function shaped(scope, effectId, value) {
407
- const { ctx, lease, signal } = scope;
408
- if (abortKind(signal) === "cancel" || !mayNeedShaping(value))
409
- return value;
410
- const call = await ctx.store.tx(async (t) => {
411
- const s = await t.lockSession(lease.sessionId);
412
- const effect = await t.get("effects", effectId);
413
- if (!s || !effect || effect.status === "completed")
414
- return undefined;
415
- const request = effect.request;
416
- // An agent's tool; a flow's HTTP stage or verifier names no tool, and its output is data.
417
- if (request.kind !== "tool" || request.toolName === undefined)
418
- return undefined;
419
- const mcp = mcpToolOf(s.mcpSnapshot, request) !== undefined;
420
- if (!mcp && !isHttpToolCall(s.manifest, request))
421
- return undefined;
422
- const step = stepOf(request);
423
- let used = 0;
424
- if (step !== undefined)
425
- for (const other of await t.effectsForTurn(s.id, request.turnId, [
426
- "invoking",
427
- "completed",
428
- ])) {
429
- if (other.request.effectId === effectId || stepOf(other.request) !== step)
430
- continue;
431
- used +=
432
- other.resultBytes ??
433
- (other.status === "completed" ? resultBytes(other.outcome?.value) : 0);
434
- }
435
- const limit = resultLimit(used);
436
- effect.resultBytes = Math.min(resultBytes(value), limit);
437
- await t.put("effects", effectId, effect);
438
- return { request, session: { id: s.id, ownerUserId: s.ownerUserId }, mcp, limit };
439
- });
440
- return call ? shapeToolOutcome(ctx, call, value, abortOn(signal, ["cancel"])) : value;
441
- }
442
- /** The model step a tool call answers, and whose agent: its results share one transcript entry. */
443
- function stepOf(request) {
444
- if (request.kind !== "tool")
445
- return undefined;
446
- const stepId = request.context?.stepId;
447
- return typeof stepId === "string" ? JSON.stringify([request.agent?.id ?? null, stepId]) : undefined;
448
- }
449
394
  /** What a journal row keeps of a request: a model call without its prompt. */
450
395
  function storedRequest(request) {
451
396
  if (request.kind !== "model" || request.input === undefined)
@@ -18,7 +18,7 @@ export type Renewal = {
18
18
  } | {
19
19
  ok: false;
20
20
  };
21
- /** How often a lease is renewed: a third of its length. */
21
+ /** How often a lease is renewed: a third of its length, and at least 1 ms, a timer's least delay. */
22
22
  export declare function renewEveryMs(ctx: TenantContext): number;
23
23
  /** The run token the advance of `lease` holds, as a run grant carries it. */
24
24
  export declare function tokenOf(ctx: TenantContext, lease: Lease): {
@@ -1,8 +1,8 @@
1
1
  import { dropRunGrant, renewRunGrant } from "../tenant/run-grants.js";
2
2
  import { AdvanceAbort } from "../tenant/worker.js";
3
- /** How often a lease is renewed: a third of its length. */
3
+ /** How often a lease is renewed: a third of its length, and at least 1 ms, a timer's least delay. */
4
4
  export function renewEveryMs(ctx) {
5
- return Math.max(10, Math.floor(ctx.ownerLeaseMs / 3));
5
+ return Math.max(1, Math.floor(ctx.ownerLeaseMs / 3));
6
6
  }
7
7
  /** The run token the advance of `lease` holds, as a run grant carries it. */
8
8
  export function tokenOf(ctx, lease) {
@@ -59,14 +59,6 @@ export type RunEnd = {
59
59
  /** No harness took the run within `offerWaitMs`. */
60
60
  | {
61
61
  readonly kind: "unavailable";
62
- }
63
- /**
64
- * The harness that would take the run cannot: a tool it runs itself (`features.tools`) has the
65
- * name of a tool of the turn's agents. The turn fails with `message`.
66
- */
67
- | {
68
- readonly kind: "refused";
69
- readonly message: string;
70
62
  };
71
63
  export interface HarnessApiServer {
72
64
  /** Serves a harness's channel until it closes. Returns a function that detaches it. */
@@ -112,16 +104,9 @@ export declare class NoWorkspaceHarness extends Error {
112
104
  constructor(pod?: string | undefined);
113
105
  }
114
106
  export interface HarnessApiServerOptions {
107
+ /** How long an offered run waits for a harness. Default `harnessApi.offerWaitMs` (Host tuning). */
115
108
  offerWaitMs?: number;
116
109
  /** The sandbox backend preference a harness that serves workspaces selects with. */
117
110
  sandboxPreference?: string;
118
111
  }
119
112
  export declare function createHarnessApiServer(ctx: TenantContext, options?: HarnessApiServerOptions): HarnessApiServer;
120
- /**
121
- * The tools a harness runs itself (`features.tools`) whose names the turn already uses: a tool of
122
- * the turn's agents (their own and the Runtime's `runtime` tools), a session tool, `tool_search` or
123
- * `tool_call` (session tools a later step may get). Tools are known by name alone, so a native
124
- * tool must not take one (Harness API 3); `hello` already refuses a name with `__`, which names an
125
- * MCP server's tools. A flow turn runs no native tools.
126
- */
127
- export declare function nativeToolClashes(tools: ReadonlySet<string> | undefined, start: TurnStart): string[];