@nylorun/runtime 0.23.0-beta → 0.24.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 (252) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/README.md +79 -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/endpoint.js +5 -1
  10. package/dist/api/a2a/routes.d.ts +2 -0
  11. package/dist/api/a2a/routes.js +2 -2
  12. package/dist/api/a2a/tasks.js +2 -1
  13. package/dist/api/ag-ui/endpoint.js +4 -2
  14. package/dist/api/ag-ui/history.js +4 -1
  15. package/dist/api/ag-ui/routes.d.ts +4 -1
  16. package/dist/api/ag-ui/routes.js +11 -10
  17. package/dist/api/ag-ui/translate.js +3 -2
  18. package/dist/api/components.d.ts +4953 -1204
  19. package/dist/api/components.js +25 -16
  20. package/dist/api/gate/keys.js +7 -1
  21. package/dist/api/gate/routes.d.ts +6 -0
  22. package/dist/api/gate/routes.js +39 -3
  23. package/dist/api/http/app.js +4 -4
  24. package/dist/api/http/define.d.ts +6 -0
  25. package/dist/api/http/define.js +13 -1
  26. package/dist/api/http/respond.js +1 -1
  27. package/dist/api/http/routes/artifacts.js +35 -8
  28. package/dist/api/http/routes/coverage.d.ts +9 -0
  29. package/dist/api/http/routes/coverage.js +34 -0
  30. package/dist/api/http/routes/environments.d.ts +11 -0
  31. package/dist/api/http/routes/environments.js +283 -0
  32. package/dist/api/http/routes/mcp.d.ts +1 -1
  33. package/dist/api/http/routes/mcp.js +2 -2
  34. package/dist/api/http/routes/me.js +1 -1
  35. package/dist/api/http/routes/reads.d.ts +45 -9
  36. package/dist/api/http/routes/reads.js +54 -5
  37. package/dist/api/http/routes/sessions.js +11 -6
  38. package/dist/api/http/routes/tenant.js +32 -10
  39. package/dist/api/openapi.js +31 -26
  40. package/dist/artifacts/disk.d.ts +59 -0
  41. package/dist/artifacts/disk.js +563 -0
  42. package/dist/artifacts/export.d.ts +1 -1
  43. package/dist/artifacts/export.js +2 -3
  44. package/dist/artifacts/folders.d.ts +6 -3
  45. package/dist/artifacts/folders.js +17 -12
  46. package/dist/artifacts/service.d.ts +2 -1
  47. package/dist/artifacts/service.js +6 -1
  48. package/dist/artifacts/workspace.d.ts +1 -1
  49. package/dist/artifacts/workspace.js +1 -1
  50. package/dist/core/flow-host.d.ts +2 -3
  51. package/dist/core/flow-host.js +3 -0
  52. package/dist/core/provider.d.ts +3 -2
  53. package/dist/core/provider.js +5 -37
  54. package/dist/core/sandbox-routes.d.ts +2 -21
  55. package/dist/core/sandbox-routes.js +10 -43
  56. package/dist/execution/types.d.ts +1 -1
  57. package/dist/gates/contract.d.ts +3 -24
  58. package/dist/gates/contract.js +1 -36
  59. package/dist/gates/egress-credentials.d.ts +9 -0
  60. package/dist/gates/egress-credentials.js +11 -4
  61. package/dist/gates/egress.d.ts +28 -1
  62. package/dist/gates/egress.js +115 -10
  63. package/dist/gates/fixture-model.d.ts +28 -0
  64. package/dist/gates/fixture-model.js +92 -0
  65. package/dist/gates/handler.js +4 -4
  66. package/dist/gates/http-tool.d.ts +6 -2
  67. package/dist/gates/http-tool.js +3 -5
  68. package/dist/gates/in-process.d.ts +5 -0
  69. package/dist/gates/in-process.js +12 -1
  70. package/dist/gates/journal.d.ts +30 -0
  71. package/dist/gates/journal.js +52 -0
  72. package/dist/gates/mcp-handler.d.ts +4 -0
  73. package/dist/gates/mcp-handler.js +5 -1
  74. package/dist/gates/meter.d.ts +10 -2
  75. package/dist/gates/meter.js +20 -5
  76. package/dist/gates/policy.d.ts +49 -0
  77. package/dist/gates/policy.js +134 -0
  78. package/dist/gates/tenant-vaults.d.ts +7 -7
  79. package/dist/gates/tenant-vaults.js +5 -5
  80. package/dist/gates/tool-calls.d.ts +10 -5
  81. package/dist/gates/tool-calls.js +28 -10
  82. package/dist/gates/tool-client.d.ts +8 -0
  83. package/dist/gates/tool-client.js +27 -11
  84. package/dist/gates/tool-contract.d.ts +10 -68
  85. package/dist/gates/tool-contract.js +1 -85
  86. package/dist/gates/tool-gate.d.ts +1 -1
  87. package/dist/harness/calls.d.ts +12 -5
  88. package/dist/harness/calls.js +21 -12
  89. package/dist/harness/executors.js +12 -9
  90. package/dist/harness/main.js +18 -5
  91. package/dist/harness/pod.d.ts +34 -0
  92. package/dist/harness/pod.js +41 -0
  93. package/dist/harness/service.d.ts +0 -1
  94. package/dist/harness/service.js +13 -5
  95. package/dist/harness/setup.d.ts +27 -0
  96. package/dist/harness/setup.js +150 -0
  97. package/dist/harness/ws-client.js +3 -2
  98. package/dist/harness/ws-port.d.ts +1 -5
  99. package/dist/harness/ws-port.js +1 -5
  100. package/dist/harness-api/in-process.js +3 -3
  101. package/dist/harness-api/record.d.ts +15 -1
  102. package/dist/harness-api/record.js +174 -44
  103. package/dist/harness-api/redact.d.ts +3 -0
  104. package/dist/harness-api/redact.js +45 -0
  105. package/dist/harness-api/server.d.ts +8 -0
  106. package/dist/harness-api/server.js +93 -13
  107. package/dist/harness-api/start.d.ts +10 -3
  108. package/dist/harness-api/start.js +40 -10
  109. package/dist/harness-api/tool-journal.d.ts +96 -0
  110. package/dist/harness-api/tool-journal.js +102 -0
  111. package/dist/harness-api/tool-status.d.ts +13 -0
  112. package/dist/harness-api/tool-status.js +26 -0
  113. package/dist/harness-api/workspace.d.ts +6 -3
  114. package/dist/harness-api/workspace.js +27 -11
  115. package/dist/harness-api/ws-server.d.ts +11 -3
  116. package/dist/harness-api/ws-server.js +94 -15
  117. package/dist/host/gates.d.ts +2 -1
  118. package/dist/host/gates.js +10 -2
  119. package/dist/host/main.js +54 -5
  120. package/dist/host/operate.js +67 -5
  121. package/dist/host/stack-config.d.ts +64 -0
  122. package/dist/host/stack-config.js +61 -1
  123. package/dist/keys/client.js +4 -1
  124. package/dist/keys/keys.d.ts +5 -5
  125. package/dist/keys/keys.js +5 -7
  126. package/dist/management-openapi.json +999 -2211
  127. package/dist/mcp/connect.d.ts +25 -20
  128. package/dist/mcp/connect.js +29 -30
  129. package/dist/mcp/pool.d.ts +2 -2
  130. package/dist/mcp/pool.js +1 -1
  131. package/dist/mcp/preview.d.ts +3 -3
  132. package/dist/mcp/preview.js +10 -20
  133. package/dist/mcp/snapshot.d.ts +5 -11
  134. package/dist/mcp/snapshot.js +6 -7
  135. package/dist/model/pi-model.js +8 -0
  136. package/dist/openapi.json +6955 -2526
  137. package/dist/reads/types.d.ts +16 -3
  138. package/dist/record/append.d.ts +3 -1
  139. package/dist/record/append.js +1 -0
  140. package/dist/record/envelope.d.ts +5 -1
  141. package/dist/record/envelope.js +7 -3
  142. package/dist/record/environment.d.ts +25 -0
  143. package/dist/record/environment.js +44 -0
  144. package/dist/record/index.d.ts +1 -1
  145. package/dist/record/index.js +1 -1
  146. package/dist/sandbox/disk-fs.d.ts +95 -0
  147. package/dist/sandbox/disk-fs.js +206 -0
  148. package/dist/sandbox/join.d.ts +17 -18
  149. package/dist/sandbox/join.js +42 -15
  150. package/dist/sandbox/manager.d.ts +16 -8
  151. package/dist/sandbox/manager.js +33 -17
  152. package/dist/sandbox/placement.d.ts +13 -12
  153. package/dist/sandbox/placement.js +29 -12
  154. package/dist/sandbox/pods/client.d.ts +6 -1
  155. package/dist/sandbox/pods/lifecycle.d.ts +28 -27
  156. package/dist/sandbox/pods/lifecycle.js +14 -26
  157. package/dist/sandbox/pods/reconcile.js +18 -12
  158. package/dist/sandbox/pods/spec.d.ts +15 -13
  159. package/dist/sandbox/pods/spec.js +16 -20
  160. package/dist/sandbox/records.d.ts +2 -2
  161. package/dist/sandbox/records.js +3 -3
  162. package/dist/sandbox/resolve.d.ts +35 -27
  163. package/dist/sandbox/resolve.js +66 -41
  164. package/dist/sandbox/session-sandbox.js +1 -1
  165. package/dist/sandbox/setup.d.ts +61 -0
  166. package/dist/sandbox/setup.js +123 -0
  167. package/dist/sandbox/share.d.ts +11 -21
  168. package/dist/sandbox/share.js +12 -41
  169. package/dist/sandbox/tenant-config.d.ts +12 -14
  170. package/dist/sandbox/tenant-config.js +12 -17
  171. package/dist/sandbox/types.d.ts +6 -0
  172. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  173. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  174. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  175. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  176. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  177. package/dist/store/postgres/reads.js +117 -17
  178. package/dist/store/postgres/record-writer.d.ts +4 -4
  179. package/dist/store/postgres/record-writer.js +9 -9
  180. package/dist/store/postgres/record.d.ts +7 -0
  181. package/dist/store/postgres/record.js +23 -2
  182. package/dist/store/postgres/schema.d.ts +934 -761
  183. package/dist/store/postgres/schema.js +102 -73
  184. package/dist/store/postgres/store.js +119 -66
  185. package/dist/store/types.d.ts +109 -48
  186. package/dist/tenant/advance.d.ts +7 -6
  187. package/dist/tenant/advance.js +115 -42
  188. package/dist/tenant/artifact-tool.js +3 -4
  189. package/dist/tenant/commands.d.ts +15 -2
  190. package/dist/tenant/commands.js +93 -12
  191. package/dist/tenant/context.d.ts +48 -22
  192. package/dist/tenant/context.js +4 -15
  193. package/dist/tenant/effects.d.ts +1 -1
  194. package/dist/tenant/effects.js +37 -20
  195. package/dist/tenant/environments.d.ts +167 -0
  196. package/dist/tenant/environments.js +823 -0
  197. package/dist/tenant/harness-tokens.d.ts +21 -0
  198. package/dist/tenant/harness-tokens.js +61 -0
  199. package/dist/tenant/host-token.d.ts +1 -1
  200. package/dist/tenant/host-token.js +2 -4
  201. package/dist/tenant/identity-file.d.ts +4 -4
  202. package/dist/tenant/identity-file.js +17 -8
  203. package/dist/tenant/issuers.js +3 -3
  204. package/dist/tenant/principals.js +1 -1
  205. package/dist/tenant/reset.js +3 -3
  206. package/dist/tenant/runtime.d.ts +6 -1
  207. package/dist/tenant/runtime.js +7 -1
  208. package/dist/tenant/session.d.ts +3 -3
  209. package/dist/tenant/session.js +0 -14
  210. package/dist/tenant/sessions.d.ts +13 -13
  211. package/dist/tenant/sessions.js +61 -138
  212. package/dist/tenant/signing-keys.d.ts +5 -1
  213. package/dist/tenant/status.js +3 -3
  214. package/dist/tenant/sweep.js +12 -6
  215. package/dist/tenant/transcript.d.ts +210 -20
  216. package/dist/tenant/transcript.js +62 -11
  217. package/dist/tenant/turn-timing.d.ts +43 -0
  218. package/dist/tenant/turn-timing.js +39 -0
  219. package/dist/tenant/types.d.ts +2 -0
  220. package/dist/traces/ids.d.ts +42 -0
  221. package/dist/traces/ids.js +85 -0
  222. package/dist/traces/key.d.ts +6 -0
  223. package/dist/traces/key.js +37 -0
  224. package/dist/traces/map.d.ts +113 -0
  225. package/dist/traces/map.js +317 -0
  226. package/dist/traces/options.d.ts +7 -0
  227. package/dist/traces/options.js +1 -0
  228. package/dist/traces/otlp.d.ts +42 -0
  229. package/dist/traces/otlp.js +84 -0
  230. package/dist/traces/projector.d.ts +58 -0
  231. package/dist/traces/projector.js +200 -0
  232. package/dist/traces/read.d.ts +23 -0
  233. package/dist/traces/read.js +16 -0
  234. package/dist/vault/coverage.d.ts +26 -0
  235. package/dist/vault/coverage.js +64 -0
  236. package/dist/vault/error.d.ts +11 -1
  237. package/dist/vault/error.js +5 -1
  238. package/dist/vault/service.d.ts +47 -48
  239. package/dist/vault/service.js +321 -382
  240. package/dist/vault/sources.d.ts +5 -16
  241. package/dist/vault/sources.js +6 -8
  242. package/dist/version.d.ts +1 -1
  243. package/dist/version.js +2 -2
  244. package/package.json +13 -13
  245. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  246. package/dist/api/http/routes/sandboxes.js +0 -153
  247. package/dist/api/http/routes/vaults.d.ts +0 -10
  248. package/dist/api/http/routes/vaults.js +0 -139
  249. package/dist/record/sandbox.d.ts +0 -25
  250. package/dist/record/sandbox.js +0 -43
  251. package/dist/tenant/sandboxes.d.ts +0 -123
  252. package/dist/tenant/sandboxes.js +0 -566
@@ -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,134 @@
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
+ if (request.capabilityId === undefined)
72
+ return false;
73
+ return declaredHttpTool(manifest, request.agent?.id, request.capabilityId, request.toolName)?.approval === "always";
74
+ }
75
+ function matches(rule, call) {
76
+ if (rule.kind !== undefined && rule.kind !== call.kind)
77
+ return false;
78
+ if (rule.tool !== undefined && !glob(rule.tool).test(call.tool))
79
+ return false;
80
+ if (rule.server !== undefined && (call.server === undefined || !glob(rule.server).test(call.server)))
81
+ return false;
82
+ if (rule.agent !== undefined && !glob(rule.agent).test(call.agent))
83
+ return false;
84
+ for (const [path, condition] of Object.entries(rule.when ?? {}))
85
+ if (!holds(condition, fieldOf(call.input, path)))
86
+ return false;
87
+ return true;
88
+ }
89
+ /** A pattern where `*` is any run of characters, matched whole. */
90
+ function glob(pattern) {
91
+ return new RegExp(`^${pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`);
92
+ }
93
+ /** The value at a dotted `path` of `input`, or undefined. */
94
+ function fieldOf(input, path) {
95
+ let value = input;
96
+ for (const key of path.split(".")) {
97
+ if (!value || typeof value !== "object")
98
+ return undefined;
99
+ value = value[key];
100
+ }
101
+ return value;
102
+ }
103
+ function holds(condition, value) {
104
+ if (value === undefined)
105
+ return false;
106
+ if (condition.equals !== undefined && canonical(value) !== canonical(condition.equals))
107
+ return false;
108
+ if (condition.in !== undefined && !condition.in.some((item) => canonical(item) === canonical(value)))
109
+ return false;
110
+ const number = typeof value === "number" ? value : undefined;
111
+ if (condition.gt !== undefined && !(number !== undefined && number > condition.gt))
112
+ return false;
113
+ if (condition.gte !== undefined && !(number !== undefined && number >= condition.gte))
114
+ return false;
115
+ if (condition.lt !== undefined && !(number !== undefined && number < condition.lt))
116
+ return false;
117
+ if (condition.lte !== undefined && !(number !== undefined && number <= condition.lte))
118
+ return false;
119
+ if (condition.matches !== undefined && !(typeof value === "string" && new RegExp(condition.matches).test(value)))
120
+ return false;
121
+ return true;
122
+ }
123
+ /** What a rule with no `reason` says. */
124
+ function describe(rule) {
125
+ const what = [
126
+ rule.tool === undefined ? undefined : `tool '${rule.tool}'`,
127
+ rule.server === undefined ? undefined : `server '${rule.server}'`,
128
+ rule.kind === undefined ? undefined : `${rule.kind} tools`,
129
+ rule.agent === undefined ? undefined : `agent '${rule.agent}'`,
130
+ rule.when === undefined ? undefined : `input ${Object.keys(rule.when).join(", ")}`,
131
+ ].filter((part) => part !== undefined);
132
+ const verb = rule.effect === "deny" ? "refuses" : "asks a person about";
133
+ return `The Tenant's tool policy ${verb} ${what.length === 0 ? "every tool call" : `calls of ${what.join(", ")}`}`;
134
+ }
@@ -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);
@@ -8,10 +8,14 @@
8
8
  * - The gate also writes a `tool_crossings` row before it calls the server, and the answer
9
9
  * after. A re-send that finds the answer gets it. One that finds a row without an answer,
10
10
  * for a call this gateway is not running, was lost with an earlier gateway (a restart mid
11
- * call): it answers `uncertain`, because the call may have run, and is never run again.
11
+ * call): it is started again under the same key (R5.3), so the tool sees the key twice and
12
+ * a tool that honors it acts once. Core counts the sends and stops at its cap (R5.4).
13
+ * - A re-send that another gateway process started or settled between this one's read and its
14
+ * write answers `{uncertain: true}`: not answered here, so the caller sends it again later.
12
15
  *
13
- * A cancel aborts the call and leaves its row without an answer. Settled rows are deleted a day
14
- * later (`prune`).
16
+ * A cancel aborts the call and settles its row as cancelled: it never runs again under its key.
17
+ * A shutdown leaves the row open, so the next gateway starts it again. Settled rows are deleted
18
+ * a day later (`prune`); a re-send after that runs the call again, which at least once allows.
15
19
  */
16
20
  import type { Logger } from "../tenant/types.js";
17
21
  import { type InflightCallsOptions, type InflightOwner } from "./inflight.js";
@@ -29,8 +33,9 @@ export interface ToolCalls {
29
33
  */
30
34
  run(tenantId: string | undefined, key: string, hash: string, call: (signal: AbortSignal) => Promise<ToolCallAnswer>, owner?: InflightOwner): Promise<ToolCallAnswer>;
31
35
  /**
32
- * Aborts the call under `key`; it is never run again under that key. With `sessionId`, false
33
- * when the running call is another session's (nothing is aborted).
36
+ * Aborts the call under `key` and settles its crossing as cancelled: it is never run again
37
+ * under that key. With `sessionId`, false when the running call is another session's (nothing
38
+ * is aborted).
34
39
  */
35
40
  cancel(key: string, sessionId?: string): boolean;
36
41
  /** Aborts every running call (gateway shutdown). */
@@ -1,11 +1,15 @@
1
1
  import { createInflightCalls, InflightConflict, } from "./inflight.js";
2
2
  /** How long a settled crossing is kept. */
3
3
  export const TOOL_CROSSING_TTL_MS = 24 * 60 * 60_000;
4
- const LOST = {
4
+ const CANCELLED = {
5
+ ok: false,
6
+ error: { message: "The call was cancelled while it ran; it may have reached its server" },
7
+ };
8
+ const ELSEWHERE = {
5
9
  ok: false,
6
10
  error: {
7
11
  uncertain: true,
8
- message: "The gateway stopped while this tool call ran; the call may have reached its server, so it is not run again",
12
+ message: "Another gateway process is running this tool call; send it again",
9
13
  },
10
14
  };
11
15
  export function createToolCalls(options) {
@@ -16,7 +20,7 @@ export function createToolCalls(options) {
16
20
  return {
17
21
  async run(tenantId, key, hash, call, owner) {
18
22
  if (inflight.has(key))
19
- return inflight.run(key, hash, () => Promise.resolve(LOST), owner);
23
+ return inflight.run(key, hash, () => Promise.resolve(ELSEWHERE), owner);
20
24
  const vault = await vaults.open(tenantId);
21
25
  const store = vault.store;
22
26
  const row = await store.tx((t) => t.toolCrossing(key));
@@ -25,16 +29,19 @@ export function createToolCalls(options) {
25
29
  throw new InflightConflict(`A different request already ran under ${key}`);
26
30
  if (row.answer !== null && row.answer !== undefined)
27
31
  return row.answer;
28
- // Started here a moment ago, or lost with an earlier gateway.
32
+ // Started here a moment ago: join it.
29
33
  if (inflight.has(key))
30
- return inflight.run(key, hash, () => Promise.resolve(LOST), owner);
31
- return LOST;
34
+ return inflight.run(key, hash, () => Promise.resolve(ELSEWHERE), owner);
35
+ // Otherwise it was lost with an earlier gateway: it starts again below (R5.3).
32
36
  }
33
37
  return inflight.run(key, hash, async (signal) => {
34
- const started = await store.tx((t) => t.startToolCrossing({ key, hash, startedAt: iso() }));
35
- // Another gateway process wrote it between the read and now: never run it twice.
38
+ const startedAt = iso();
39
+ const started = await store.tx((t) => row
40
+ ? t.restartToolCrossing({ key, hash, startedAt })
41
+ : t.startToolCrossing({ key, hash, startedAt }));
42
+ // Another gateway process started or settled it between the read and now.
36
43
  if (!started)
37
- return LOST;
44
+ return ELSEWHERE;
38
45
  const answer = await call(signal);
39
46
  // A cancel or a shutdown: the call may have reached the server; the row stays open.
40
47
  if (signal.aborted)
@@ -48,7 +55,18 @@ export function createToolCalls(options) {
48
55
  return answer;
49
56
  }, owner);
50
57
  },
51
- cancel: (key, sessionId) => inflight.cancel(key, sessionId),
58
+ cancel(key, sessionId) {
59
+ if (!inflight.cancel(key, sessionId))
60
+ return false;
61
+ void vaults
62
+ .open(undefined)
63
+ .then((vault) => vault.store.tx((t) => t.settleToolCrossing(key, CANCELLED, iso())))
64
+ .catch((error) => logger.warn("tool_crossing_unsettled", {
65
+ effect: key,
66
+ message: error instanceof Error ? error.message : String(error),
67
+ }));
68
+ return true;
69
+ },
52
70
  close: () => inflight.close(),
53
71
  async prune() {
54
72
  try {
@@ -1,5 +1,13 @@
1
1
  import type { ToolGate } from "./tool-gate.js";
2
2
  import type { RunTokens } from "../tenant/run-grants.js";
3
+ /**
4
+ * A keyed call whose answer the hop lost (R5.3): the gate never got it, the connection broke,
5
+ * the gate answered `5xx`, or another gateway process holds the call. Sending it again with the
6
+ * same key is safe: the gate joins the call, answers it from its crossing, or runs it again.
7
+ */
8
+ export declare class GateCallLost extends Error {
9
+ readonly name = "GateCallLost";
10
+ }
3
11
  export interface HttpToolGateOptions {
4
12
  /** The gates service, e.g. `http://gateway:4100` (`NYLORUN_GATES_URL`). */
5
13
  readonly url: string;
@@ -8,10 +8,12 @@
8
8
  * Failures keep the meaning they had in the loop's own process:
9
9
  * - An MCP request that fails throws, so the pool reports a diagnostic. A tool call's coded
10
10
  * failure (R2b C7) throws the `McpCallFailed` the loop's own call would have, which becomes a
11
- * failed outcome the model sees. Only the hop's failures and a call lost with the gateway leave
12
- * it `uncertain`.
13
- * - An HTTP tool call throws only for the hop, or a call lost with the gateway: its own failures
14
- * are outcomes.
11
+ * failed outcome the model sees.
12
+ * - An HTTP tool call throws only for the hop: its own failures are outcomes.
13
+ * - A hop that lost the answer (the gate unreachable, the connection lost, a `5xx`, or a call
14
+ * another gateway process holds) throws `GateCallLost`: sending the call again with the same
15
+ * key joins it, collects its answer, or runs it again (R5.3). A gate's refusal (`4xx`) throws
16
+ * a plain error.
15
17
  *
16
18
  * Credentials (F5): a session's MCP requests and keyed cancels carry its run token, read per
17
19
  * request from the advance's grant, and leave the session out of the body: the gate takes it
@@ -26,6 +28,14 @@ import { McpCallFailed, } from "../mcp/connect.js";
26
28
  import { TENANT_HEADER } from "./contract.js";
27
29
  import { GATE_CLIENT_TIMEOUT_MS } from "./http-client.js";
28
30
  import { HTTP_CALLS_PATH, MCP_CLOSE_PATH, MCP_CONNECT_PATH, MCP_LIST_PATH, TOOL_CALLS_PATH, } from "./tool-contract.js";
31
+ /**
32
+ * A keyed call whose answer the hop lost (R5.3): the gate never got it, the connection broke,
33
+ * the gate answered `5xx`, or another gateway process holds the call. Sending it again with the
34
+ * same key is safe: the gate joins the call, answers it from its crossing, or runs it again.
35
+ */
36
+ export class GateCallLost extends Error {
37
+ name = "GateCallLost";
38
+ }
29
39
  /** How long a close or a cancel may take; neither changes an outcome. */
30
40
  const SHORT_TIMEOUT_MS = 2_000;
31
41
  export function httpToolGate(options) {
@@ -111,8 +121,7 @@ export function httpToolGate(options) {
111
121
  }
112
122
  /**
113
123
  * The answer of an MCP or HTTP tool request, or a throw: a coded MCP failure (`McpCallFailed`),
114
- * or one the pool and `resolveEffect` read as before, the call's fate unknown and the effect
115
- * `uncertain`.
124
+ * a lost hop (`GateCallLost`, sent again), or a gate's refusal.
116
125
  */
117
126
  async function answer(path, scope, request) {
118
127
  const result = await exchange(path, scope.body, {
@@ -123,8 +132,10 @@ export function httpToolGate(options) {
123
132
  if (result.kind === "aborted")
124
133
  throw result.reason ?? new Error("aborted");
125
134
  if (result.kind !== "answer")
126
- throw new Error(result.message);
135
+ throw new GateCallLost(result.message);
127
136
  const parsed = parse(result.text);
137
+ if (result.status >= 500)
138
+ throw new GateCallLost(gateRefusal(result.status, parsed));
128
139
  if (result.status !== 200 || !parsed || !("ok" in parsed))
129
140
  throw new Error(gateRefusal(result.status, parsed));
130
141
  if (parsed.ok)
@@ -176,7 +187,9 @@ export function httpToolGate(options) {
176
187
  async callHttp(call, signal) {
177
188
  const token = options.runTokens?.token(call.tool.sessionId);
178
189
  const { sessionId: _, ...unnamed } = call.tool;
179
- const body = { ...call, tool: token ? unnamed : { ...call.tool } };
190
+ // The gate reads the call's span context from its journal row (R7.4), not from the caller.
191
+ const { traceparent: _traceparent, ...sent } = call;
192
+ const body = { ...sent, tool: token ? unnamed : { ...call.tool } };
180
193
  return answered(HTTP_CALLS_PATH, { body, ...(token ? { bearer: token } : {}) }, { signal, idempotencyKey: call.effectId });
181
194
  },
182
195
  async cancel(request) {
@@ -190,10 +203,13 @@ export function httpToolGate(options) {
190
203
  }
191
204
  /** An MCP failure the gate reported, as the loop's own MCP code would have thrown it. */
192
205
  function mcpError(error) {
193
- // Coded (R2b C7): the model sees it. A call lost with the gateway never is.
194
- if (error.failure && !error.uncertain)
206
+ // Held by another gateway process: send it again (R5.3).
207
+ if (error.uncertain)
208
+ return new GateCallLost(error.message);
209
+ // Coded (R2b C7): the model sees it.
210
+ if (error.failure)
195
211
  return McpCallFailed.from(error.failure);
196
- const thrown = new Error(error.uncertain ? `${error.message} (the call may have run; it is not sent again)` : error.message);
212
+ const thrown = new Error(error.message);
197
213
  if (error.code !== undefined)
198
214
  thrown.code = error.code;
199
215
  if (error.credentialIds !== undefined)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The Tool Gate's wire format (blueprint §12, F4.1): the routes the loop calls on the gates
3
- * service for remote MCP servers and HTTP tools. Internal, like the model call route
4
- * (`contract.ts`): same optional Tenant header, not in the published OpenAPI documents.
3
+ * service for remote MCP servers and HTTP tools. Like the model call route (`contract.ts`): same
4
+ * optional Tenant header, bodies published in `@nylorun/core/harness-api` (R4.5).
5
5
  *
6
6
  * Credentials (F5). A session's MCP requests carry its run token, and the gate takes the
7
7
  * session from it: `server.sessionId` is then absent (or the token's). Core's credential
@@ -25,9 +25,9 @@
25
25
  * answer, not a gate error. A tool call's failure is coded (`error.failure`, R2b C7), and neither
26
26
  * a result nor a failure carries a credential value the gate sent (C8). An answer past 8 MiB is
27
27
  * `mcp.too-large` (C11), as an HTTP tool's is `http.too-large`; core shapes what fits into a
28
- * result the model can take (`tenant/tool-results.ts`). A keyed call whose
29
- * earlier attempt was lost with the gateway answers `{ok: false, error: {uncertain: true}}` and
30
- * is never run again.
28
+ * result the model can take (`tenant/tool-results.ts`). A keyed call whose earlier attempt was
29
+ * lost with the gateway runs again under the same key (R5.3); one another gateway process holds
30
+ * answers `{ok: false, error: {uncertain: true}}`, and the caller sends it again.
31
31
  *
32
32
  * HTTP tools (R2 M3). `POST /nylorun/v1/http-calls` `{tool, effectId, turnId, input}`: one call
33
33
  * of an HTTP tool. The loop names the tool (`{sessionId?, agentId?, capabilityId, toolName}`), or a
@@ -36,67 +36,10 @@
36
36
  * (`gates/http-tool.ts`). A run token's turn is the call's; `turnId` counts only under core's
37
37
  * credential. Keyed like a tool call, and cancelled by the same route. It answers
38
38
  * `200 {ok: true, result}` with the tool outcome (a failed request is a failed outcome the
39
- * model sees), or `{ok: false, error: {uncertain: true}}` for a call lost with the gateway.
39
+ * model sees), or `{ok: false, error: {uncertain: true}}` for a call another gateway process holds.
40
40
  */
41
- import { z } from "zod";
42
41
  import type { McpCallFailure } from "../mcp/connect.js";
43
- export declare const MCP_CONNECT_PATH = "/nylorun/v1/mcp/connect";
44
- export declare const MCP_LIST_PATH = "/nylorun/v1/mcp/list";
45
- export declare const MCP_CLOSE_PATH = "/nylorun/v1/mcp/close";
46
- export declare const TOOL_CALLS_PATH = "/nylorun/v1/tool-calls";
47
- export declare const HTTP_CALLS_PATH = "/nylorun/v1/http-calls";
48
- /** Largest tool call body the gate reads. */
49
- export declare const MAX_TOOL_BODY_BYTES: number;
50
- export declare const McpServerRefSchema: z.ZodObject<{
51
- sessionId: z.ZodOptional<z.ZodString>;
52
- agentId: z.ZodOptional<z.ZodString>;
53
- capabilityId: z.ZodString;
54
- serverName: z.ZodString;
55
- }, z.core.$strip>;
56
- export declare const McpServerBodySchema: z.ZodObject<{
57
- server: z.ZodObject<{
58
- sessionId: z.ZodOptional<z.ZodString>;
59
- agentId: z.ZodOptional<z.ZodString>;
60
- capabilityId: z.ZodString;
61
- serverName: z.ZodString;
62
- }, z.core.$strip>;
63
- }, z.core.$strip>;
64
- export declare const McpListBodySchema: z.ZodObject<{
65
- server: z.ZodObject<{
66
- sessionId: z.ZodOptional<z.ZodString>;
67
- agentId: z.ZodOptional<z.ZodString>;
68
- capabilityId: z.ZodString;
69
- serverName: z.ZodString;
70
- }, z.core.$strip>;
71
- cursor: z.ZodOptional<z.ZodString>;
72
- }, z.core.$strip>;
73
- export declare const ToolCallBodySchema: z.ZodObject<{
74
- server: z.ZodObject<{
75
- sessionId: z.ZodOptional<z.ZodString>;
76
- agentId: z.ZodOptional<z.ZodString>;
77
- capabilityId: z.ZodString;
78
- serverName: z.ZodString;
79
- }, z.core.$strip>;
80
- effectId: z.ZodString;
81
- name: z.ZodString;
82
- arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
83
- }, z.core.$strip>;
84
- export type ToolCallBody = z.infer<typeof ToolCallBodySchema>;
85
- export declare const HttpCallBodySchema: z.ZodObject<{
86
- tool: z.ZodUnion<readonly [z.ZodObject<{
87
- sessionId: z.ZodOptional<z.ZodString>;
88
- agentId: z.ZodOptional<z.ZodString>;
89
- capabilityId: z.ZodString;
90
- toolName: z.ZodString;
91
- }, z.core.$strip>, z.ZodObject<{
92
- sessionId: z.ZodOptional<z.ZodString>;
93
- stage: z.ZodString;
94
- }, z.core.$strip>]>;
95
- effectId: z.ZodString;
96
- turnId: z.ZodString;
97
- input: z.ZodUnknown;
98
- }, z.core.$strip>;
99
- export type HttpCallBody = z.infer<typeof HttpCallBodySchema>;
42
+ export { HTTP_CALLS_PATH, HttpCallBodySchema, MAX_TOOL_BODY_BYTES, MCP_CLOSE_PATH, MCP_CONNECT_PATH, MCP_LIST_PATH, McpListBodySchema, McpServerBodySchema, McpServerRefSchema, TOOL_CALLS_PATH, ToolCallBodySchema, type HttpCallBody, type ToolCallBody, } from "@nylorun/core/harness-api";
100
43
  /** Why an MCP request failed, as the loop's diagnostics read it (`mcp/connect.ts`). */
101
44
  export interface McpGateError {
102
45
  readonly message: string;
@@ -104,13 +47,12 @@ export interface McpGateError {
104
47
  readonly code?: number;
105
48
  /** The vault credentials a refused authorization names. */
106
49
  readonly credentialIds?: readonly string[];
107
- /** The call may have run: its earlier attempt was lost with the gateway. Never re-run. */
50
+ /** Not answered here: another gateway process holds the keyed call. Send it again (R5.3). */
108
51
  readonly uncertain?: boolean;
109
52
  /**
110
53
  * How the request failed, coded (R2b C1, C7): the loop's `McpCallFailed`, which a tool call
111
- * turns into a failed outcome the model sees (`mcp.lost` only for a read-only or idempotent
112
- * tool). `credential_rejected` for a `401`, with the server and the scope of the vault whose
113
- * credential was sent. Its message has no credential value in it (C8).
54
+ * turns into a failed outcome the model sees (`mcp.lost` included). `credential_rejected` for a
55
+ * `401`, with the server. Its message has no credential value in it (C8).
114
56
  */
115
57
  readonly failure?: McpCallFailure;
116
58
  }
@@ -1,85 +1 @@
1
- /**
2
- * The Tool Gate's wire format (blueprint §12, F4.1): the routes the loop calls on the gates
3
- * service for remote MCP servers and HTTP tools. Internal, like the model call route
4
- * (`contract.ts`): same optional Tenant header, not in the published OpenAPI documents.
5
- *
6
- * Credentials (F5). A session's MCP requests carry its run token, and the gate takes the
7
- * session from it: `server.sessionId` is then absent (or the token's). Core's credential
8
- * (`NYLORUN_GATES_TOKEN`) is accepted too, for requests made outside a run, and then
9
- * `server.sessionId` names the session.
10
- *
11
- * Remote MCP. The gate holds the connection and the vault credential; the loop names the
12
- * server, never a URL or a credential, and the gate finds it in the session's pinned manifest.
13
- * - `POST /nylorun/v1/mcp/connect` `{server}`: opens the server's connection (vault
14
- * authorization included), or keeps the open one. Its result is `{instructions}` when the
15
- * server gave instructions at `initialize` (R2b C10, for the note on its deferred tools), else
16
- * `null`.
17
- * - `POST /nylorun/v1/mcp/list` `{server, cursor?}`: one page of `tools/list`.
18
- * - `POST /nylorun/v1/tool-calls` `{server, effectId, name, arguments}`: one
19
- * `tools/call`. With an `Idempotency-Key` (the effect id) the call outlives its client and runs
20
- * once (G3); a re-send joins it or gets its outcome.
21
- * - `POST /nylorun/v1/tool-calls/{key}/cancel`: aborts a keyed call; `204` either way, `403
22
- * gate_forbidden` for another session's call under a run token.
23
- * - `POST /nylorun/v1/mcp/close` `{server}`: closes the server's connection; `204`.
24
- * Each answers `200 {ok: true, result}` or `200 {ok: false, error}`: an MCP failure is an
25
- * answer, not a gate error. A tool call's failure is coded (`error.failure`, R2b C7), and neither
26
- * a result nor a failure carries a credential value the gate sent (C8). An answer past 8 MiB is
27
- * `mcp.too-large` (C11), as an HTTP tool's is `http.too-large`; core shapes what fits into a
28
- * result the model can take (`tenant/tool-results.ts`). A keyed call whose
29
- * earlier attempt was lost with the gateway answers `{ok: false, error: {uncertain: true}}` and
30
- * is never run again.
31
- *
32
- * HTTP tools (R2 M3). `POST /nylorun/v1/http-calls` `{tool, effectId, turnId, input}`: one call
33
- * of an HTTP tool. The loop names the tool (`{sessionId?, agentId?, capabilityId, toolName}`), or a
34
- * flow's HTTP stage or HTTP verifier by its stage key (`{sessionId?, stage}`); the gate finds its
35
- * URL, method and credential in the session's pinned manifest
36
- * (`gates/http-tool.ts`). A run token's turn is the call's; `turnId` counts only under core's
37
- * credential. Keyed like a tool call, and cancelled by the same route. It answers
38
- * `200 {ok: true, result}` with the tool outcome (a failed request is a failed outcome the
39
- * model sees), or `{ok: false, error: {uncertain: true}}` for a call lost with the gateway.
40
- */
41
- import { z } from "zod";
42
- export const MCP_CONNECT_PATH = "/nylorun/v1/mcp/connect";
43
- export const MCP_LIST_PATH = "/nylorun/v1/mcp/list";
44
- export const MCP_CLOSE_PATH = "/nylorun/v1/mcp/close";
45
- export const TOOL_CALLS_PATH = "/nylorun/v1/tool-calls";
46
- export const HTTP_CALLS_PATH = "/nylorun/v1/http-calls";
47
- /** Largest tool call body the gate reads. */
48
- export const MAX_TOOL_BODY_BYTES = 8 * 1024 * 1024;
49
- export const McpServerRefSchema = z.object({
50
- /** Required with core's credential; a run token names the session itself. */
51
- sessionId: z.string().min(1).optional(),
52
- agentId: z.string().min(1).optional(),
53
- capabilityId: z.string().min(1),
54
- serverName: z.string().min(1),
55
- });
56
- export const McpServerBodySchema = z.object({ server: McpServerRefSchema });
57
- export const McpListBodySchema = z.object({
58
- server: McpServerRefSchema,
59
- cursor: z.string().optional(),
60
- });
61
- export const ToolCallBodySchema = z.object({
62
- server: McpServerRefSchema,
63
- effectId: z.string().min(1),
64
- name: z.string().min(1),
65
- arguments: z.record(z.string(), z.unknown()),
66
- });
67
- export const HttpCallBodySchema = z.object({
68
- tool: z.union([
69
- z.object({
70
- /** Required with core's credential; a run token names the session itself. */
71
- sessionId: z.string().min(1).optional(),
72
- agentId: z.string().min(1).optional(),
73
- capabilityId: z.string().min(1),
74
- toolName: z.string().min(1),
75
- }),
76
- z.object({
77
- sessionId: z.string().min(1).optional(),
78
- /** A flow's HTTP stage or HTTP verifier. */
79
- stage: z.string().min(1),
80
- }),
81
- ]),
82
- effectId: z.string().min(1),
83
- turnId: z.string().min(1),
84
- input: z.unknown(),
85
- });
1
+ export { HTTP_CALLS_PATH, HttpCallBodySchema, MAX_TOOL_BODY_BYTES, MCP_CLOSE_PATH, MCP_CONNECT_PATH, MCP_LIST_PATH, McpListBodySchema, McpServerBodySchema, McpServerRefSchema, TOOL_CALLS_PATH, ToolCallBodySchema, } from "@nylorun/core/harness-api";