@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
@@ -17,22 +17,20 @@ export type HttpOutcome = {
17
17
  readonly kind: "failed";
18
18
  readonly code: string;
19
19
  readonly message: string;
20
- /** With `credential_rejected`: the tool's `credential`, and the scope of its vault. */
20
+ /** With `credential_rejected`: the tool's `credential`. */
21
21
  readonly server?: string;
22
- readonly vault?: "installation" | "user";
23
22
  /** How many credential values the message had in it, replaced (R2b C8). */
24
23
  readonly redacted?: number;
25
24
  };
26
25
  /**
27
26
  * One declared HTTP tool of a session: what the gate finds it by in the pinned manifest. An
28
- * agent's HTTP tool is named by its capability and tool; a flow's HTTP stage or HTTP verifier
27
+ * agent's HTTP tool is named by its agent and tool name; a flow's HTTP stage or HTTP verifier
29
28
  * by its stage key.
30
29
  */
31
30
  export type HttpToolRef = {
32
31
  readonly sessionId: string;
33
32
  /** The agent used as a tool that declares it; absent for the session's root agent. */
34
33
  readonly agentId?: string;
35
- readonly capabilityId: string;
36
34
  readonly toolName: string;
37
35
  } | {
38
36
  readonly sessionId: string;
@@ -45,6 +43,11 @@ export interface HttpToolCall {
45
43
  readonly effectId: string;
46
44
  readonly turnId: string;
47
45
  readonly input: unknown;
46
+ /**
47
+ * The call's span context (R7.4), sent as `traceparent`: in the gates service, from the call's
48
+ * journal row (`gates/journal.ts`); in process, from core's `execute` answer. Never a body field.
49
+ */
50
+ readonly traceparent?: string;
48
51
  }
49
52
  /** What `runHttpTool` reads of the Tenant: the session's pinned manifest and its credentials. */
50
53
  export interface HttpToolTenant {
@@ -53,8 +56,8 @@ export interface HttpToolTenant {
53
56
  } | undefined>;
54
57
  authorize(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
55
58
  }
56
- /** The HTTP tool `capabilityId`/`toolName` of the agent `agentId` (the root when absent). */
57
- export declare function declaredHttpTool(manifest: unknown, agentId: string | undefined, capabilityId: string | undefined, toolName: string | undefined): (ToolManifest & {
59
+ /** The HTTP tool `toolName` of the agent `agentId` (the root when absent). */
60
+ export declare function declaredHttpTool(manifest: unknown, agentId: string | undefined, toolName: string | undefined): (ToolManifest & {
58
61
  readonly http: HttpToolTarget;
59
62
  }) | undefined;
60
63
  /** The HTTP stage or HTTP verifier at stage key `stage` of a flow session's `manifest`. */
@@ -20,15 +20,13 @@ import { MAX_RESPONSE_BYTES, post } from "../tenant/outbound.js";
20
20
  import { credentialSecrets } from "../vault/headers.js";
21
21
  /** How much of a failed answer's body the model sees. */
22
22
  export const HTTP_ERROR_BODY_CHARS = 2_000;
23
- /** The HTTP tool `capabilityId`/`toolName` of the agent `agentId` (the root when absent). */
24
- export function declaredHttpTool(manifest, agentId, capabilityId, toolName) {
23
+ /** The HTTP tool `toolName` of the agent `agentId` (the root when absent). */
24
+ export function declaredHttpTool(manifest, agentId, toolName) {
25
25
  const root = manifest;
26
- if (!root || !Array.isArray(root.capabilities))
26
+ if (!root || typeof root !== "object" || isWorkflowManifest(root) || toolName === undefined)
27
27
  return undefined;
28
28
  const agent = agentId === undefined ? root : delegateManifest(root, agentId);
29
- const tool = agent?.capabilities
30
- .find((capability) => capability.id === capabilityId)
31
- ?.tools?.find((item) => item.name === toolName);
29
+ const tool = agent?.tools?.find((item) => item.name === toolName);
32
30
  return tool?.http ? tool : undefined;
33
31
  }
34
32
  /** The HTTP stage or HTTP verifier at stage key `stage` of a flow session's `manifest`. */
@@ -44,18 +42,17 @@ export function declaredFlowHttp(manifest, stage) {
44
42
  export function isHttpToolCall(manifest, request) {
45
43
  if (request.kind !== "tool")
46
44
  return false;
47
- if (request.capabilityId === undefined)
45
+ if (request.toolName === undefined)
48
46
  return declaredFlowHttp(manifest, request.key) !== undefined;
49
- return declaredHttpTool(manifest, request.agent?.id, request.capabilityId, request.toolName) !== undefined;
47
+ return declaredHttpTool(manifest, request.agent?.id, request.toolName) !== undefined;
50
48
  }
51
49
  /** The ref a `tool` effect calls the Tool Gate with: its agent's HTTP tool, or its flow stage. */
52
50
  export function httpToolRefOf(request) {
53
- if (request.capabilityId === undefined)
51
+ if (request.toolName === undefined)
54
52
  return { sessionId: request.sessionId, stage: request.key };
55
53
  return {
56
54
  sessionId: request.sessionId,
57
55
  ...(request.agent ? { agentId: request.agent.id } : {}),
58
- capabilityId: request.capabilityId,
59
56
  toolName: request.toolName,
60
57
  };
61
58
  }
@@ -138,7 +135,6 @@ export async function runHttpTool(tenant, call, options) {
138
135
  ...("stage" in tool
139
136
  ? { stage: tool.stage }
140
137
  : {
141
- capability: tool.capabilityId,
142
138
  tool: tool.toolName,
143
139
  ...(tool.agentId === undefined ? {} : { agent: tool.agentId }),
144
140
  }),
@@ -154,7 +150,7 @@ export async function runHttpTool(tenant, call, options) {
154
150
  const agentId = "stage" in tool ? undefined : tool.agentId;
155
151
  const declared = "stage" in tool
156
152
  ? declaredFlowHttp(session.manifest, tool.stage)
157
- : declaredHttpTool(session.manifest, agentId, tool.capabilityId, tool.toolName);
153
+ : declaredHttpTool(session.manifest, agentId, tool.toolName);
158
154
  if (!declared)
159
155
  return failed("http.undeclared", "stage" in tool
160
156
  ? `'${tool.stage}' is not an HTTP stage of the flow`
@@ -163,7 +159,6 @@ export async function runHttpTool(tenant, call, options) {
163
159
  let credential = {};
164
160
  let identity;
165
161
  let target = http;
166
- let vault;
167
162
  if (http.credential !== undefined) {
168
163
  const authorized = await tenant.authorize(tool.sessionId, {
169
164
  url: http.url,
@@ -172,10 +167,9 @@ export async function runHttpTool(tenant, call, options) {
172
167
  if (authorized.status === "refused")
173
168
  return failed("http.credential", `The credential '${http.credential}' was refused: ${authorized.reason}`);
174
169
  if (authorized.status === "unauthenticated")
175
- return failed("http.credential", `The session's vaults hold no credential '${http.credential}' for ${authorized.url}`);
170
+ return failed("http.credential", `The session's Environment holds no credential '${http.credential}' for ${authorized.url}`);
176
171
  credential = authorized.headers;
177
172
  identity = authorized.identity;
178
- vault = authorized.vault;
179
173
  // A credential with `via` sends the call there, a gateway (R2b C2, Q9).
180
174
  if (authorized.via !== undefined)
181
175
  target = { ...http, url: authorized.via };
@@ -187,6 +181,7 @@ export async function runHttpTool(tenant, call, options) {
187
181
  [TURN_ID_HEADER]: call.turnId,
188
182
  [AGENT_ID_HEADER]: agentId ?? session.manifest.id,
189
183
  "idempotency-key": call.effectId,
184
+ ...(call.traceparent ? { traceparent: call.traceparent } : {}),
190
185
  },
191
186
  policy: options.policy,
192
187
  signal: options.signal,
@@ -199,9 +194,8 @@ export async function runHttpTool(tenant, call, options) {
199
194
  return outcome;
200
195
  return {
201
196
  ...outcome,
202
- message: `The service answered HTTP 401: it rejected the credential '${http.credential}' from the ${vault} vault`,
197
+ message: `The service answered HTTP 401: it rejected the credential '${http.credential}' from the session's Environment`,
203
198
  server: http.credential,
204
- ...(vault === undefined ? {} : { vault }),
205
199
  };
206
200
  }
207
201
  }
@@ -41,4 +41,9 @@ export declare function tenantModelGate(options: {
41
41
  /** The Tenant's Object store, for the files a prompt names. */
42
42
  readonly blobs?: BlobStore;
43
43
  }): ModelGate;
44
+ /**
45
+ * Serves one call: from the fixture model while the Tenant's `model.fixture` setting is on (R4.3),
46
+ * else from `gate`. Read per call, so a seed applies to the next call.
47
+ */
48
+ export declare function serve(store: SessionStore, gate: ModelGate, request: ModelGateRequest, signal: AbortSignal): ReturnType<ModelGate["call"]>;
44
49
  export declare function inProcessModelGate(options: InProcessModelGateOptions): ModelGate;
@@ -2,6 +2,8 @@ import { piModel } from "../model/pi-model.js";
2
2
  import { HostModelVault } from "../vault/host-model.js";
3
3
  import { createMeter } from "./meter.js";
4
4
  import { artifactFiles } from "../artifacts/files.js";
5
+ import { usesFixtureModel } from "../tenant/model-setting.js";
6
+ import { fixtureAnswer } from "./fixture-model.js";
5
7
  /**
6
8
  * The files a call names, from the Tenant's artifacts, limited to the call's session: in
7
9
  * process, the loop's own request; over HTTP, the session of the run token's claims, which the
@@ -26,9 +28,18 @@ export function tenantModelGate(options) {
26
28
  });
27
29
  const meter = createMeter({ logger: options.logger });
28
30
  return {
29
- call: (request, signal) => meter.call(options.store, request, () => gate.call(request, signal)),
31
+ call: (request, signal) => meter.call(options.store, request, () => serve(options.store, gate, request, signal)),
30
32
  };
31
33
  }
34
+ /**
35
+ * Serves one call: from the fixture model while the Tenant's `model.fixture` setting is on (R4.3),
36
+ * else from `gate`. Read per call, so a seed applies to the next call.
37
+ */
38
+ export async function serve(store, gate, request, signal) {
39
+ if (await store.tx((t) => usesFixtureModel(t)))
40
+ return fixtureAnswer(request.call, signal);
41
+ return gate.call(request, signal);
42
+ }
32
43
  export function inProcessModelGate(options) {
33
44
  return {
34
45
  async call(request, signal) {
@@ -0,0 +1,30 @@
1
+ import type { McpServerRef } from "../mcp/pool.js";
2
+ import type { RunClaims } from "../tenant/run-token.js";
3
+ import { type HttpToolRef } from "./http-tool.js";
4
+ import type { TenantVaults } from "./tenant-vaults.js";
5
+ /** The call a run asks the gate to send, as the gate's body names it. */
6
+ export type GateToolCall = {
7
+ readonly kind: "mcp";
8
+ readonly server: McpServerRef;
9
+ readonly name: string;
10
+ readonly arguments: Record<string, unknown>;
11
+ } | {
12
+ readonly kind: "http";
13
+ readonly tool: HttpToolRef;
14
+ readonly input: unknown;
15
+ };
16
+ /**
17
+ * What the journal says of a call: why it does not hold it, or that it does, with the call's span
18
+ * context from the row (R7.4), which the gate sends on as `traceparent`.
19
+ */
20
+ export type JournalAnswer = {
21
+ readonly refused: string;
22
+ } | {
23
+ readonly refused?: undefined;
24
+ readonly traceparent?: string;
25
+ };
26
+ export interface JournalCheck {
27
+ /** Whether the journal holds `call` under `key` for the run `claims` names. */
28
+ check(tenantId: string | undefined, claims: RunClaims, key: string, call: GateToolCall): Promise<JournalAnswer>;
29
+ }
30
+ export declare function journalCheck(vaults: TenantVaults): JournalCheck;
@@ -0,0 +1,51 @@
1
+ import { mcpToolOf } from "../mcp/snapshot.js";
2
+ import { canonical } from "../store/canonical.js";
3
+ import { httpToolRefOf } from "./http-tool.js";
4
+ export function journalCheck(vaults) {
5
+ return {
6
+ async check(tenantId, claims, key, call) {
7
+ const store = (await vaults.open(tenantId)).store;
8
+ const refusal = await store.tx(async (t) => {
9
+ const row = await t.get("effects", key);
10
+ if (!row)
11
+ return `No journaled call has the effect id '${key}'`;
12
+ const request = row.request;
13
+ if (request.kind !== "tool" ||
14
+ request.sessionId !== claims.sessionId ||
15
+ request.turnId !== claims.turnId)
16
+ return `Effect '${key}' is not a tool call of this run's turn`;
17
+ if (row.status !== "invoking")
18
+ return `Effect '${key}' is not running: it is ${row.status}`;
19
+ if (call.kind === "http") {
20
+ const { sessionId: _journaled, ...expected } = httpToolRefOf(request);
21
+ const { sessionId: _named, ...named } = call.tool;
22
+ if (canonical(expected) !== canonical(named))
23
+ return `Effect '${key}' calls another tool`;
24
+ if (canonical(request.input ?? null) !== canonical(call.input ?? null))
25
+ return `Effect '${key}' was journaled with other input`;
26
+ return held(row);
27
+ }
28
+ const session = await t.get("sessions", request.sessionId);
29
+ const tool = mcpToolOf(session?.mcpSnapshot, request);
30
+ if (!tool ||
31
+ tool.serverName !== call.server.serverName ||
32
+ (tool.agentId ?? null) !== (call.server.agentId ?? null) ||
33
+ tool.serverToolName !== call.name)
34
+ return `Effect '${key}' calls another tool`;
35
+ if (canonical(argumentsOf(request.input)) !== canonical(call.arguments))
36
+ return `Effect '${key}' was journaled with other input`;
37
+ return held(row);
38
+ });
39
+ return typeof refusal === "string" ? { refused: refusal } : refusal;
40
+ },
41
+ };
42
+ }
43
+ /** A journaled call: its span context, when core recorded one (R7.4). */
44
+ function held(row) {
45
+ const traceparent = row.traceparent;
46
+ return typeof traceparent === "string" ? { traceparent } : {};
47
+ }
48
+ /** A tool call's input as `tools/call` arguments (`callMcpTool`): an object, else none. */
49
+ function argumentsOf(input) {
50
+ return input && typeof input === "object" && !Array.isArray(input) ? input : {};
51
+ }
@@ -29,6 +29,10 @@ export interface McpHandler {
29
29
  effectId: string;
30
30
  name: string;
31
31
  arguments: Record<string, unknown>;
32
+ /** The call's `Idempotency-Key`, sent to the server in `_meta` (R5.5). */
33
+ key?: string;
34
+ /** The call's span context from its journal row (R7.4), sent as `traceparent`. */
35
+ traceparent?: string;
32
36
  }, signal: AbortSignal): Promise<McpAnswer<Record<string, unknown>>>;
33
37
  close(server: McpServerRef): Promise<void>;
34
38
  /** Closes connections idle past the timeout. */
@@ -13,13 +13,13 @@ export function createMcpHandler(options) {
13
13
  const open = options.open ?? openMcpServer;
14
14
  const live = new Map();
15
15
  const opening = new Map();
16
- const keyOf = (server) => JSON.stringify([server.sessionId, server.agentId ?? null, server.capabilityId, server.serverName]);
16
+ const keyOf = (server) => JSON.stringify([server.sessionId, server.agentId ?? null, server.serverName]);
17
17
  /** The declared remote server, from the session's pinned manifest. */
18
18
  async function declared(vault, server) {
19
19
  const session = await vault.session(server.sessionId);
20
20
  if (!session)
21
21
  throw new NotDeclared(`Session ${server.sessionId} not found`);
22
- const found = findServer(session.manifest, server.agentId, server.capabilityId, server.serverName);
22
+ const found = findServer(session.manifest, server.agentId, server.serverName);
23
23
  if (!found)
24
24
  throw new NotDeclared(`MCP server '${server.serverName}' is not declared`);
25
25
  return found.server;
@@ -114,7 +114,11 @@ export function createMcpHandler(options) {
114
114
  async function callOnce(tenantId, request, signal) {
115
115
  for (let attempt = 0;; attempt += 1) {
116
116
  try {
117
- return await use(tenantId, request.server, (entry) => entry.connection.client.callTool({ name: request.name, arguments: request.arguments }, { signal }), true);
117
+ return await use(tenantId, request.server, (entry) => entry.connection.client.callTool({ name: request.name, arguments: request.arguments }, {
118
+ signal,
119
+ ...(request.key === undefined ? {} : { key: request.key }),
120
+ ...(request.traceparent === undefined ? {} : { traceparent: request.traceparent }),
121
+ }), true);
118
122
  }
119
123
  catch (error) {
120
124
  if (!(error instanceof McpConnectionStale) || attempt > 0)
@@ -170,7 +174,6 @@ export function createMcpHandler(options) {
170
174
  function fields(server) {
171
175
  return {
172
176
  session: server.sessionId,
173
- capability: server.capabilityId,
174
177
  server: server.serverName,
175
178
  ...(server.agentId === undefined ? {} : { agent: server.agentId }),
176
179
  };
@@ -13,8 +13,16 @@ export interface MeterOptions {
13
13
  readonly now?: () => Date;
14
14
  }
15
15
  export declare function createMeter(options: MeterOptions): Meter;
16
- /** The ledger row of an outcome that carries usage; undefined for a failure. */
17
- export declare function usageRow(request: ModelGateRequest, outcome: ModelGateOutcome, at: Date): ModelUsageWrite | undefined;
16
+ /** When the gate sent a call and when its answer was complete (R7.1). */
17
+ export interface CallTiming {
18
+ readonly startedAt: Date;
19
+ readonly endedAt: Date;
20
+ }
21
+ /**
22
+ * The ledger row of an outcome that carries usage; undefined for a failure. With `timing`, the
23
+ * row keeps the gate's start and end of the call, and the adapter's `firstTokenMs`.
24
+ */
25
+ export declare function usageRow(request: ModelGateRequest, outcome: ModelGateOutcome, at: Date, timing?: CallTiming): ModelUsageWrite | undefined;
18
26
  /**
19
27
  * Why the budget refuses another call, or undefined. Calls in flight count at the scope's
20
28
  * average cost per recorded call.
@@ -8,6 +8,10 @@
8
8
  * the scope at the scope's average cost per call, reaches its limit. Calls in a turn run one
9
9
  * at a time, so a scope overspends by at most one call per concurrent call. The refusal is a
10
10
  * `budget_exhausted` failure, which the turn does not retry.
11
+ *
12
+ * The row also keeps the gate's own timing of the call (R7.1): `now()` before the call and
13
+ * after it, and the adapter's `firstTokenMs` (`evidence.extras`), so a model call's span is
14
+ * what the gate measured.
11
15
  */
12
16
  import { randomUUID } from "node:crypto";
13
17
  import { failure } from "../model/classify.js";
@@ -60,8 +64,9 @@ export function createMeter(options) {
60
64
  for (const key of keys)
61
65
  inflight.set(key, (inflight.get(key) ?? 0) + 1);
62
66
  try {
67
+ const startedAt = now();
63
68
  const outcome = await call();
64
- await record(store, request, outcome);
69
+ await record(store, request, outcome, { startedAt, endedAt: now() });
65
70
  return outcome;
66
71
  }
67
72
  finally {
@@ -75,8 +80,8 @@ export function createMeter(options) {
75
80
  }
76
81
  },
77
82
  };
78
- async function record(store, request, outcome) {
79
- const row = usageRow(request, outcome, now());
83
+ async function record(store, request, outcome, timing) {
84
+ const row = usageRow(request, outcome, timing.endedAt, timing);
80
85
  if (!row)
81
86
  return;
82
87
  try {
@@ -96,11 +101,15 @@ export function createMeter(options) {
96
101
  }
97
102
  }
98
103
  }
99
- /** The ledger row of an outcome that carries usage; undefined for a failure. */
100
- export function usageRow(request, outcome, at) {
104
+ /**
105
+ * The ledger row of an outcome that carries usage; undefined for a failure. With `timing`, the
106
+ * row keeps the gate's start and end of the call, and the adapter's `firstTokenMs`.
107
+ */
108
+ export function usageRow(request, outcome, at, timing) {
101
109
  if (typeof outcome !== "object" || "kind" in outcome || !outcome.usage)
102
110
  return undefined;
103
111
  const usage = outcome.usage;
112
+ const firstTokenMs = outcome.evidence?.extras?.firstTokenMs;
104
113
  const producer = producerOf(outcome);
105
114
  const inputTokens = count(usage.inputTokens);
106
115
  const outputTokens = count(usage.outputTokens);
@@ -126,6 +135,12 @@ export function usageRow(request, outcome, at) {
126
135
  reasoningTokens: count(usage.reasoningTokens),
127
136
  costUsd: Number.isFinite(usage.costUsd) && usage.costUsd > 0 ? usage.costUsd : 0,
128
137
  createdAt: at.toISOString(),
138
+ ...(timing
139
+ ? { startedAt: timing.startedAt.toISOString(), endedAt: timing.endedAt.toISOString() }
140
+ : {}),
141
+ ...(typeof firstTokenMs === "number" && Number.isFinite(firstTokenMs) && firstTokenMs >= 0
142
+ ? { firstTokenMs: Math.round(firstTokenMs) }
143
+ : {}),
129
144
  };
130
145
  }
131
146
  /** The ledger rows a budget counts for `request`; undefined when it doesn't apply. */
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Tool policy (R5 step 2). Every MCP and HTTP tool call of every harness is decided as core
3
+ * journals it (`harness-api/record.ts`), which each harness must do before it sends a call:
4
+ * `deny` fails it to the model as `tool.denied`, unsent; `ask` holds the row until a person
5
+ * approves it on the session; `allow` runs it. The Tool Gate sends only a call the journal holds
6
+ * running (`journal.ts`), so a held or refused call cannot run, whichever harness asks.
7
+ *
8
+ * The rules are the Tenant's (`PUT /v1/tenant/tool-policy`, the `tool-policy` setting). A rule
9
+ * matches a call when every selector it sets matches; the strictest matching rule decides
10
+ * (`deny`, then `ask`, then `allow`), and a call no rule matches runs. A session pinned to a
11
+ * manifest saved before R5 step 2 keeps its `approval: "always"` as an ask: no session loses
12
+ * the approval it was opened with. A policy that cannot be read refuses every call.
13
+ */
14
+ import { type ToolPolicy, type ToolRule } from "@nylorun/core/contracts";
15
+ import type { AgentManifest } from "@nylorun/core/define";
16
+ import type { HostEffect } from "@nylorun/harness/run";
17
+ import { type McpSnapshot } from "../mcp/snapshot.js";
18
+ import type { Tx } from "../store/types.js";
19
+ import type { Session } from "../tenant/context.js";
20
+ /** The `tenant_settings` key the policy is kept under. */
21
+ export declare const TOOL_POLICY_SETTING = "tool-policy";
22
+ /** A call as the policy reads it. */
23
+ export interface PolicyCall {
24
+ readonly kind: "mcp" | "http";
25
+ /** The name the model calls the tool by; a flow stage's key. */
26
+ readonly tool: string;
27
+ /** The MCP server's name. */
28
+ readonly server?: string;
29
+ /** The agent that calls it. */
30
+ readonly agent: string;
31
+ readonly input: unknown;
32
+ }
33
+ export type PolicyDecision = {
34
+ readonly effect: "allow";
35
+ } | {
36
+ readonly effect: "ask" | "deny";
37
+ readonly reason: string;
38
+ };
39
+ /** The Tenant's policy; an empty one when none was set. Throws when it cannot be read. */
40
+ export declare function readToolPolicy(t: Pick<Tx, "getSetting">): Promise<ToolPolicy>;
41
+ /**
42
+ * The decision for `request`, a session's MCP or HTTP tool call: the Tenant's rules and the
43
+ * pinned manifest's `approval`. A policy that cannot be read refuses the call.
44
+ */
45
+ export declare function toolCallDecision(t: Pick<Tx, "getSetting">, s: Session, request: HostEffect, kind: "mcp" | "http"): Promise<PolicyDecision>;
46
+ /** Decides `call` by `rules`; `pinnedAsk` adds the ask of a manifest saved before R5 step 2. */
47
+ export declare function decide(rules: readonly ToolRule[], call: PolicyCall, pinnedAsk?: boolean): PolicyDecision;
48
+ /** The pinned `approval: "always"` of the call's tool, in a manifest saved before R5 step 2. */
49
+ export declare function pinnedApproval(manifest: AgentManifest, snapshot: McpSnapshot | undefined, request: HostEffect): boolean;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Tool policy (R5 step 2). Every MCP and HTTP tool call of every harness is decided as core
3
+ * journals it (`harness-api/record.ts`), which each harness must do before it sends a call:
4
+ * `deny` fails it to the model as `tool.denied`, unsent; `ask` holds the row until a person
5
+ * approves it on the session; `allow` runs it. The Tool Gate sends only a call the journal holds
6
+ * running (`journal.ts`), so a held or refused call cannot run, whichever harness asks.
7
+ *
8
+ * The rules are the Tenant's (`PUT /v1/tenant/tool-policy`, the `tool-policy` setting). A rule
9
+ * matches a call when every selector it sets matches; the strictest matching rule decides
10
+ * (`deny`, then `ask`, then `allow`), and a call no rule matches runs. A session pinned to a
11
+ * manifest saved before R5 step 2 keeps its `approval: "always"` as an ask: no session loses
12
+ * the approval it was opened with. A policy that cannot be read refuses every call.
13
+ */
14
+ import { ToolPolicySchema } from "@nylorun/core/contracts";
15
+ import { mcpToolOf, mcpToolSettingsIn } from "../mcp/snapshot.js";
16
+ import { canonical } from "../store/canonical.js";
17
+ import { turnManifestOf } from "../tenant/session.js";
18
+ import { declaredHttpTool } from "./http-tool.js";
19
+ /** The `tenant_settings` key the policy is kept under. */
20
+ export const TOOL_POLICY_SETTING = "tool-policy";
21
+ const STRICTNESS = { allow: 0, ask: 1, deny: 2 };
22
+ /** The Tenant's policy; an empty one when none was set. Throws when it cannot be read. */
23
+ export async function readToolPolicy(t) {
24
+ const raw = await t.getSetting(TOOL_POLICY_SETTING);
25
+ if (raw === undefined)
26
+ return { rules: [], updatedAt: null };
27
+ return ToolPolicySchema.parse(JSON.parse(raw));
28
+ }
29
+ /**
30
+ * The decision for `request`, a session's MCP or HTTP tool call: the Tenant's rules and the
31
+ * pinned manifest's `approval`. A policy that cannot be read refuses the call.
32
+ */
33
+ export async function toolCallDecision(t, s, request, kind) {
34
+ let rules;
35
+ try {
36
+ rules = (await readToolPolicy(t)).rules;
37
+ }
38
+ catch {
39
+ return { effect: "deny", reason: "The Tenant's tool policy could not be read, so no tool call runs" };
40
+ }
41
+ const mcp = mcpToolOf(s.mcpSnapshot, request);
42
+ return decide(rules, {
43
+ kind,
44
+ tool: request.toolName ?? request.key ?? request.effectId,
45
+ ...(mcp ? { server: mcp.serverName } : {}),
46
+ agent: request.agent?.id ?? s.agentId,
47
+ input: request.input,
48
+ }, pinnedApproval(turnManifestOf(s), s.mcpSnapshot, request));
49
+ }
50
+ /** Decides `call` by `rules`; `pinnedAsk` adds the ask of a manifest saved before R5 step 2. */
51
+ export function decide(rules, call, pinnedAsk = false) {
52
+ let strictest = pinnedAsk
53
+ ? { effect: "ask", reason: `The agent's manifest asks for approval of '${call.tool}'` }
54
+ : undefined;
55
+ for (const rule of rules) {
56
+ if (!matches(rule, call))
57
+ continue;
58
+ if (strictest && STRICTNESS[rule.effect] <= STRICTNESS[strictest.effect])
59
+ continue;
60
+ strictest = { effect: rule.effect, reason: rule.reason ?? describe(rule) };
61
+ }
62
+ if (!strictest || strictest.effect === "allow")
63
+ return { effect: "allow" };
64
+ return { effect: strictest.effect, reason: strictest.reason };
65
+ }
66
+ /** The pinned `approval: "always"` of the call's tool, in a manifest saved before R5 step 2. */
67
+ export function pinnedApproval(manifest, snapshot, request) {
68
+ const mcp = mcpToolOf(snapshot, request);
69
+ if (mcp)
70
+ return mcpToolSettingsIn(manifest, mcp).approval === "always";
71
+ return declaredHttpTool(manifest, request.agent?.id, request.toolName)?.approval === "always";
72
+ }
73
+ function matches(rule, call) {
74
+ if (rule.kind !== undefined && rule.kind !== call.kind)
75
+ return false;
76
+ if (rule.tool !== undefined && !glob(rule.tool).test(call.tool))
77
+ return false;
78
+ if (rule.server !== undefined && (call.server === undefined || !glob(rule.server).test(call.server)))
79
+ return false;
80
+ if (rule.agent !== undefined && !glob(rule.agent).test(call.agent))
81
+ return false;
82
+ for (const [path, condition] of Object.entries(rule.when ?? {}))
83
+ if (!holds(condition, fieldOf(call.input, path)))
84
+ return false;
85
+ return true;
86
+ }
87
+ /** A pattern where `*` is any run of characters, matched whole. */
88
+ function glob(pattern) {
89
+ return new RegExp(`^${pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`);
90
+ }
91
+ /** The value at a dotted `path` of `input`, or undefined. */
92
+ function fieldOf(input, path) {
93
+ let value = input;
94
+ for (const key of path.split(".")) {
95
+ if (!value || typeof value !== "object")
96
+ return undefined;
97
+ value = value[key];
98
+ }
99
+ return value;
100
+ }
101
+ function holds(condition, value) {
102
+ if (value === undefined)
103
+ return false;
104
+ if (condition.equals !== undefined && canonical(value) !== canonical(condition.equals))
105
+ return false;
106
+ if (condition.in !== undefined && !condition.in.some((item) => canonical(item) === canonical(value)))
107
+ return false;
108
+ const number = typeof value === "number" ? value : undefined;
109
+ if (condition.gt !== undefined && !(number !== undefined && number > condition.gt))
110
+ return false;
111
+ if (condition.gte !== undefined && !(number !== undefined && number >= condition.gte))
112
+ return false;
113
+ if (condition.lt !== undefined && !(number !== undefined && number < condition.lt))
114
+ return false;
115
+ if (condition.lte !== undefined && !(number !== undefined && number <= condition.lte))
116
+ return false;
117
+ if (condition.matches !== undefined && !(typeof value === "string" && new RegExp(condition.matches).test(value)))
118
+ return false;
119
+ return true;
120
+ }
121
+ /** What a rule with no `reason` says. */
122
+ function describe(rule) {
123
+ const what = [
124
+ rule.tool === undefined ? undefined : `tool '${rule.tool}'`,
125
+ rule.server === undefined ? undefined : `server '${rule.server}'`,
126
+ rule.kind === undefined ? undefined : `${rule.kind} tools`,
127
+ rule.agent === undefined ? undefined : `agent '${rule.agent}'`,
128
+ rule.when === undefined ? undefined : `input ${Object.keys(rule.when).join(", ")}`,
129
+ ].filter((part) => part !== undefined);
130
+ const verb = rule.effect === "deny" ? "refuses" : "asks a person about";
131
+ return `The Tenant's tool policy ${verb} ${what.length === 0 ? "every tool call" : `calls of ${what.join(", ")}`}`;
132
+ }
@@ -29,15 +29,15 @@ export interface TenantVault {
29
29
  session(sessionId: string): Promise<Session | undefined>;
30
30
  /**
31
31
  * The authorization of one request to a session's remote MCP server or HTTP tool: its
32
- * credential from the session's attached vaults.
32
+ * credential from the vault of the session's Environment.
33
33
  */
34
34
  authorizeMcp(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
35
35
  /** Vault writes and token signing with the Tenant's vault key (the keys service, F4.2). */
36
36
  keys(): Keys;
37
- /** Whether a live session on the sandbox has an `environment_secret` bound to `host` (R2c). */
38
- environmentSecretBound(sandboxId: string, host: string): Promise<boolean>;
37
+ /** Whether the Environment's vault has an `environment_secret` bound to `host` (R2c). */
38
+ environmentSecretBound(environmentId: string, host: string): Promise<boolean>;
39
39
  /** The header an `environment_secret` sets on one request to `host` (R2c). */
40
- releaseEnvironmentSecret(sandboxId: string, host: string): ReturnType<VaultService["releaseEnvironmentSecret"]>;
40
+ releaseEnvironmentSecret(environmentId: string, host: string): ReturnType<VaultService["releaseEnvironmentSecret"]>;
41
41
  }
42
42
  export interface TenantVaults {
43
43
  /**
@@ -60,8 +60,8 @@ export interface TenantVaultsOptions {
60
60
  }
61
61
  export declare function createTenantVaults(options: TenantVaultsOptions): TenantVaults;
62
62
  /**
63
- * A request to a session's remote MCP server, authorized from the session's attached vaults:
64
- * what the loop does in its own process (`tenant/effects.ts` `authorize`). The vaults come from
65
- * the session row.
63
+ * A request to a session's remote MCP server, authorized from its Environment's vault: what the
64
+ * loop does in its own process (`tenant/effects.ts` `authorize`). The Environment comes from the
65
+ * session row.
66
66
  */
67
67
  export declare function authorizeSessionMcp(vault: Pick<VaultService, "authorize">, session: (sessionId: string) => Promise<Session | undefined>, sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
@@ -52,8 +52,8 @@ export function createTenantVaults(options) {
52
52
  session,
53
53
  authorizeMcp: (sessionId, request) => authorizeSessionMcp(credentials, session, sessionId, request),
54
54
  keys: () => keys,
55
- environmentSecretBound: (sandboxId, host) => credentials.environmentSecretBound(sandboxId, host),
56
- releaseEnvironmentSecret: (sandboxId, host) => credentials.releaseEnvironmentSecret({ sandboxId, host }),
55
+ environmentSecretBound: (environmentId, host) => credentials.environmentSecretBound(environmentId, host),
56
+ releaseEnvironmentSecret: (environmentId, host) => credentials.releaseEnvironmentSecret({ environmentId, host }),
57
57
  };
58
58
  }
59
59
  /** The database's Tenant, once its schema is at this build's version. */
@@ -81,9 +81,9 @@ export function createTenantVaults(options) {
81
81
  };
82
82
  }
83
83
  /**
84
- * A request to a session's remote MCP server, authorized from the session's attached vaults:
85
- * what the loop does in its own process (`tenant/effects.ts` `authorize`). The vaults come from
86
- * the session row.
84
+ * A request to a session's remote MCP server, authorized from its Environment's vault: what the
85
+ * loop does in its own process (`tenant/effects.ts` `authorize`). The Environment comes from the
86
+ * session row.
87
87
  */
88
88
  export async function authorizeSessionMcp(vault, session, sessionId, request) {
89
89
  const found = await session(sessionId);