@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
@@ -40,10 +40,13 @@
40
40
  * already recorded; an `ownership.lost` leaves the session to the advance that owns it.
41
41
  */
42
42
  import { runDurable, runFlowDurable, } from "@nylorun/harness/run";
43
+ import { redactTokens } from "../harness-api/redact.js";
43
44
  import { aggregateWaits, cancelSiblingWork, isWorkflowManifest, wakeLinkedWorkflow, } from "../core/flow-host.js";
44
45
  import { isOwnershipLost, ownedTx } from "../store/ownership.js";
45
46
  import { endAdvance } from "./scheduler.js";
46
- import { isGateToolEffect, recoversModelCalls, recoversToolCalls } from "./effects.js";
47
+ import { harnessOf } from "./context.js";
48
+ import { HARNESS_ID } from "../sandbox/placement.js";
49
+ import { recoversModelCalls } from "./effects.js";
47
50
  import { slimModelEffects } from "./slim.js";
48
51
  import { applyUpdates, leanState, transcriptShadow, withTranscript, } from "./history.js";
49
52
  import { command } from "./commands.js";
@@ -51,7 +54,8 @@ import { exportOutputs } from "../artifacts/export.js";
51
54
  import { dropRunGrant, grantRun } from "./run-grants.js";
52
55
  import { startHeartbeat } from "../harness-api/renew.js";
53
56
  import { buildTurnStart, startSegment } from "../harness-api/start.js";
54
- import { touchPodSandbox } from "./sandboxes.js";
57
+ import { touchEnvironment } from "./environments.js";
58
+ import { endTurn, pauseTurn } from "./turn-timing.js";
55
59
  import { AdvanceAbort, AdvanceDeadlineError, abortKind, } from "./worker.js";
56
60
  /**
57
61
  * A segment stopped by a `shutdown` or `ownership.lost` abort before it settled. Never
@@ -179,28 +183,27 @@ export async function advance(ctx, id, signal) {
179
183
  sessionId: id,
180
184
  message: error instanceof Error ? error.message : String(error),
181
185
  }));
182
- // A pod sandbox's idle time counts from the end of each segment (D34).
183
- if (placed.pod !== undefined)
184
- await touchPodSandbox(ctx, placed.pod);
186
+ // The Environment was used: `deleteAfter`, and a pod's idle time (D34), count from the end
187
+ // of each segment.
188
+ if (placed.environment !== undefined)
189
+ await touchEnvironment(ctx, placed.environment);
185
190
  }
186
191
  return result;
187
192
  }
188
- /** Why a pod sandbox cannot run a segment, or undefined when it can. */
193
+ /** Why a pod Environment cannot run a segment, or undefined when it can. */
189
194
  function podUnusable(ctx, segment) {
190
195
  const pod = segment.pod;
191
196
  if (!pod)
192
197
  return undefined;
193
198
  if (!ctx.pods)
194
- return `Sandbox ${pod.id} is a pod sandbox, and this Runtime has no sandbox pods`;
199
+ return `Environment ${pod.id} is a pod Environment, and this Runtime has no sandbox pods`;
195
200
  const state = pod.pod;
196
201
  if (state.desired === "deleted")
197
- return `Sandbox ${pod.id} was deleted`;
202
+ return `Environment ${pod.id} was deleted`;
198
203
  if (state.observed === "lost")
199
- return `Sandbox ${pod.id} was lost: ${state.reason ?? "its volume is gone"}`;
200
- if (state.observed === "expired")
201
- return `Sandbox ${pod.id} expired`;
204
+ return `Environment ${pod.id} was lost: ${state.reason ?? "its volume is gone"}`;
202
205
  if (state.observed === "failed")
203
- return `Sandbox ${pod.id} failed to start: ${state.reason ?? "its pod was not ready"}`;
206
+ return `Environment ${pod.id} failed to start: ${state.reason ?? "its pod was not ready"}`;
204
207
  return undefined;
205
208
  }
206
209
  /**
@@ -229,7 +232,6 @@ async function takeOwnership(ctx, id) {
229
232
  if (taken.takeover)
230
233
  await takeOver(t, s, {
231
234
  recoversModelCalls: recoversModelCalls(ctx),
232
- recoversToolCalls: recoversToolCalls(ctx),
233
235
  });
234
236
  if (!s.checkpoint || !["running", "runnable"].includes(s.status)) {
235
237
  await t.releaseOwnership(id, lease.owner, lease.epoch);
@@ -243,10 +245,12 @@ async function takeOwnership(ctx, id) {
243
245
  /**
244
246
  * Takeover (§10.5 step 2, §11.4), inside the transaction that took ownership: the dead
245
247
  * owner's `invoking` effects become `uncertain` and are never invoked again, except model
246
- * calls when the gate recovers them (P1.2) and remote MCP and HTTP tool calls when the Tool
247
- * Gate does (F4.1 G3): those stay `invoking`, and the replay re-sends them. When one belongs
248
- * to the active turn, the session becomes `uncertain` too, with an `effect.uncertain` event.
249
- * Mutates and writes `s`. Returns the effect ids it marked.
248
+ * calls when the gate recovers them (P1.2) and every tool call (R5.3): those stay `invoking`,
249
+ * and the replay decides. It re-sends a model call, and an MCP or HTTP tool call with the same
250
+ * key; a sandbox or core-served tool call fails to the model as `tool.interrupted`
251
+ * (`harness-api/tool-journal.ts`). When an effect marked `uncertain` belongs to the active
252
+ * turn, the session becomes `uncertain` too, with an `effect.uncertain` event. Mutates and
253
+ * writes `s`. Returns the effect ids it marked.
250
254
  */
251
255
  export async function takeOver(t, s, options = {}) {
252
256
  const marked = [];
@@ -254,7 +258,7 @@ export async function takeOver(t, s, options = {}) {
254
258
  // Still running at the gate, or finished there: the replay re-sends it (P1.2).
255
259
  if (options.recoversModelCalls && effect.request.kind === "model")
256
260
  continue;
257
- if (options.recoversToolCalls && isGateToolEffect(s, effect.request))
261
+ if (effect.request.kind === "tool")
258
262
  continue;
259
263
  effect.status = "uncertain";
260
264
  effect.error ??= "The Worker running this effect stopped before its outcome was recorded";
@@ -279,13 +283,16 @@ async function runRemoteSegment(ctx, lease, started, controller, run, onTaken, p
279
283
  let end;
280
284
  try {
281
285
  const segment = await startSegment(ctx, lease, { harness: true });
282
- // A pod sandbox's segment runs in its pod (F7.2), or fails when the pod cannot run it.
286
+ // A pod Environment's segment runs in its pod (F7.2), or fails when the pod cannot run it.
283
287
  const unusable = podUnusable(ctx, segment);
284
288
  if (unusable)
285
289
  throw new Error(unusable);
290
+ // A new volume being seeded from a disk version takes no turn until it holds it (R2d step 3).
291
+ if (segment.pod?.seeding)
292
+ throw new HarnessUnavailable();
286
293
  const pod = segment.pod?.id;
287
- if (pod !== undefined)
288
- placed.pod = pod;
294
+ if (started.environmentId !== undefined)
295
+ placed.environment = started.environmentId;
289
296
  end = await ctx.harness.offer({
290
297
  lease,
291
298
  start: buildTurnStart(ctx, segment),
@@ -293,6 +300,8 @@ async function runRemoteSegment(ctx, lease, started, controller, run, onTaken, p
293
300
  transcript: segment.transcript,
294
301
  run,
295
302
  onTaken,
303
+ // Only a connection of the session's harness takes its runs (R4.1).
304
+ harness: harnessOf(segment.current),
296
305
  ...(pod === undefined ? {} : { pod, waitMs: POD_OFFER_WAIT_MS }),
297
306
  });
298
307
  if (end.kind === "unavailable") {
@@ -304,13 +313,17 @@ async function runRemoteSegment(ctx, lease, started, controller, run, onTaken, p
304
313
  // Aborted before a harness took it: settled as an abort of the engine would be.
305
314
  if (end.kind === "aborted")
306
315
  throw signal.reason;
316
+ // The harness's own tools take names of this turn's tools (Harness API 3): the turn fails.
317
+ if (end.kind === "refused")
318
+ throw new Error(end.message);
307
319
  // Checked before the abort: whatever core's signal says, the run's effects are unaccounted for.
308
320
  if (end.kind === "released" && end.reason === "connection.lost")
309
321
  throw new RunLost();
310
322
  stopIfLeaving(signal);
311
323
  if (end.kind === "released")
312
324
  throw new SegmentStopped(end.reason === "ownership.lost" ? "ownership.lost" : "shutdown");
313
- const { output } = end;
325
+ // A harness's output never carries the Runtime's own tokens into the record (R4).
326
+ const output = redactTokens(end.output);
314
327
  if (output.thrown)
315
328
  throw thrownError(output.thrown);
316
329
  const result = resultOf(output, segment);
@@ -320,7 +333,9 @@ async function runRemoteSegment(ctx, lease, started, controller, run, onTaken, p
320
333
  await settleFailure(ctx, lease, started, signal.reason);
321
334
  else {
322
335
  const recorded = result.status === "yielded" || result.status === "completed" || result.status === "paused";
323
- cursor = await settleTurn(ctx, lease, started, result, recorded ? output.transcript ?? [] : [], signal, segment.cursor);
336
+ cursor = await settleTurn(ctx, lease, started, result, recorded ? output.transcript ?? [] : [], signal, segment.cursor,
337
+ // The turn's end and its transcript are what the harness reported (R4.9).
338
+ { source: { kind: "harness", id: harnessOf(started) }, evidence: "claimed", runId: output.runId });
324
339
  }
325
340
  end.reply(cursor === undefined ? {} : { cursor });
326
341
  }
@@ -361,7 +376,8 @@ function resultOf(output, segment) {
361
376
  effectIds: output.effectIds ?? [],
362
377
  };
363
378
  let state = output.state;
364
- if (state !== undefined && transcriptShadow())
379
+ // Our engine's transcript shadow (tests); another harness's state is stored as sent (R4.2).
380
+ if (state !== undefined && transcriptShadow() && ours(current))
365
381
  state = withTranscript(state, applyUpdates(segment.transcript, output.transcript ?? []));
366
382
  const checkpoint = state === undefined || isWorkflowManifest(current.manifest)
367
383
  ? current.checkpoint
@@ -408,8 +424,8 @@ async function wakeWorkflowOf(ctx, t, agent, turnId, type, payload) {
408
424
  * the turn has committed and while the advance still holds the lease. The harness hears back
409
425
  * after the export. The export never throws.
410
426
  */
411
- async function settleTurn(ctx, lease, s, result, updates, signal, cursor) {
412
- const kept = await settle(ctx, lease, s, result, updates, cursor);
427
+ async function settleTurn(ctx, lease, s, result, updates, signal, cursor, writer) {
428
+ const kept = await settle(ctx, lease, s, result, updates, cursor, writer);
413
429
  if (result.status === "completed")
414
430
  await exportOutputs(ctx, lease.sessionId, s.activeTurnId, signal);
415
431
  return kept;
@@ -419,7 +435,9 @@ async function settleTurn(ctx, lease, s, result, updates, signal, cursor) {
419
435
  * cursor of the transcript it recorded (`cursor` when it recorded no edit), or undefined when
420
436
  * the transcript was not kept.
421
437
  */
422
- async function settle(ctx, lease, s, result, updates = [], cursor) {
438
+ async function settle(ctx, lease, s, result, updates = [], cursor,
439
+ /** Who reported the result (R4.9): the run's harness. */
440
+ writer) {
423
441
  const id = lease.sessionId;
424
442
  let kept;
425
443
  const siblingCancelIds = await ownedTx(ctx.store, id, lease.epoch, async (t, current) => {
@@ -430,11 +448,24 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
430
448
  // A result can arrive during concurrent action persistence. Preserve the runnable marker.
431
449
  const resumeRequested = current.status === "runnable";
432
450
  if (result.status === "waiting" || result.status === "uncertain") {
451
+ // Tool calls the Tool Gate held for approval (R5 step 2): the turn pauses for a person.
452
+ const held = resumeRequested ? [] : await heldCalls(t, result.effectIds);
453
+ if (held.length > 0) {
454
+ current.status = "paused";
455
+ pauseTurn(current);
456
+ current.waits = held;
457
+ const payload = { interactions: held };
458
+ await t.event(id, s.activeTurnId, "turn.paused", payload);
459
+ await t.put("sessions", id, current);
460
+ await wakeWorkflowOf(ctx, t, current, s.activeTurnId, "turn.paused", payload);
461
+ return siblings;
462
+ }
433
463
  const linkedWaits = isWorkflowManifest(current.manifest)
434
464
  ? await aggregateWaits({ t, workflowSessionId: id })
435
465
  : [];
436
466
  if (linkedWaits.length > 0 && !resumeRequested) {
437
467
  current.status = "paused";
468
+ pauseTurn(current);
438
469
  current.waits = linkedWaits;
439
470
  }
440
471
  else {
@@ -456,8 +487,8 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
456
487
  if (result.status === "yielded") {
457
488
  // The turn goes on in a new segment: same turn, next checkpoint, woken right away.
458
489
  const finished = result.checkpoint;
459
- kept = (await recordTranscript(t, id, s.activeTurnId, current, updates)) ?? cursor;
460
- current.state = leanState(result.result.state);
490
+ kept = (await recordTranscript(t, id, s.activeTurnId, current, updates, writer)) ?? cursor;
491
+ current.state = leanOf(current, result.result.state);
461
492
  current.checkpoint = {
462
493
  ...finished,
463
494
  segment: finished.segment + 1,
@@ -478,16 +509,16 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
478
509
  const flow = isWorkflowManifest(current.manifest);
479
510
  if (!flow) {
480
511
  if (result.status !== "failed") {
481
- const last = await recordTranscript(t, id, s.activeTurnId, current, updates);
512
+ const last = await recordTranscript(t, id, s.activeTurnId, current, updates, writer);
482
513
  if (result.status !== "cancelled")
483
514
  kept = last ?? cursor;
484
515
  }
485
516
  current.state =
486
517
  result.status === "failed"
487
518
  ? current.turnStartState
488
- : leanState(result.result.state);
519
+ : leanOf(current, result.result.state);
489
520
  }
490
- current.checkpoint = leanCheckpoint(result.checkpoint);
521
+ current.checkpoint = leanCheckpoint(current, result.checkpoint);
491
522
  current.status = result.status;
492
523
  current.waits =
493
524
  result.status === "paused" ? result.result.pending : undefined;
@@ -508,7 +539,11 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
508
539
  });
509
540
  siblings.push(...cancelResult.agentSessionIds);
510
541
  }
511
- if (result.status !== "paused") {
542
+ // The turn's span (R7.1): a pause starts its wait; an end, its duration and totals.
543
+ const ended = result.status === "paused" ? undefined : await endTurn(t, current, s.activeTurnId);
544
+ if (result.status === "paused")
545
+ pauseTurn(current);
546
+ else {
512
547
  current.activeTurnId = null;
513
548
  if (s.activeTurnId)
514
549
  current.lastTurnId = s.activeTurnId;
@@ -516,7 +551,7 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
516
551
  }
517
552
  const type = `turn.${result.status}`;
518
553
  const payload = result.status === "completed"
519
- ? { output: result.result.output }
554
+ ? { output: result.result.output, ...ended }
520
555
  : result.status === "paused"
521
556
  ? {
522
557
  interactions: (result.result.pending ?? []).map((call) => ({
@@ -539,9 +574,10 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
539
574
  ? { path: result.result.error.path }
540
575
  : {}),
541
576
  },
577
+ ...ended,
542
578
  }
543
- : {};
544
- await t.event(id, s.activeTurnId, type, payload);
579
+ : { ...ended };
580
+ await t.event(id, s.activeTurnId, type, payload, writer);
545
581
  await t.put("sessions", id, current);
546
582
  await wakeWorkflowOf(ctx, t, current, s.activeTurnId, type, payload);
547
583
  return siblings;
@@ -564,6 +600,37 @@ async function settle(ctx, lease, s, result, updates = [], cursor) {
564
600
  }
565
601
  return kept;
566
602
  }
603
+ /**
604
+ * The approvals a waiting segment's held tool calls ask for (R5 step 2), as `turn.paused`
605
+ * interactions: each one's id is the call's effect id, which `approve` answers.
606
+ */
607
+ async function heldCalls(t, effectIds) {
608
+ const held = [];
609
+ for (const effectId of effectIds) {
610
+ const row = await t.get("effects", effectId);
611
+ if (row?.status !== "held")
612
+ continue;
613
+ const request = row.request;
614
+ const context = (request.context ?? {});
615
+ const toolName = request.toolName ?? request.key ?? effectId;
616
+ const reason = row.hold?.reason ?? "The Tenant's tool policy asks a person about this call";
617
+ held.push({
618
+ invocationId: typeof context.invocationId === "string" ? context.invocationId : effectId,
619
+ ...(typeof context.callId === "string" ? { callId: context.callId } : {}),
620
+ interaction: {
621
+ id: effectId,
622
+ kind: "approval",
623
+ prompt: `Approve ${toolName}?`,
624
+ reason,
625
+ ...(row.server === undefined ? {} : { server: row.server }),
626
+ input: request.input ?? null,
627
+ },
628
+ status: "interaction",
629
+ toolName,
630
+ });
631
+ }
632
+ return held;
633
+ }
567
634
  /** A segment threw: fail the turn, restore the turn-start state, wake a linked workflow. */
568
635
  async function settleFailure(ctx, lease, s, error) {
569
636
  const id = lease.sessionId;
@@ -574,7 +641,7 @@ async function settleFailure(ctx, lease, s, error) {
574
641
  current.status = "failed";
575
642
  current.state = current.turnStartState;
576
643
  current.error = error instanceof Error ? error.message : String(error);
577
- const payload = { message: current.error };
644
+ const payload = { message: current.error, ...(await endTurn(t, current, current.activeTurnId)) };
578
645
  await t.event(id, current.activeTurnId, "turn.failed", payload);
579
646
  await slimModelEffects(t, id, current.activeTurnId);
580
647
  if (current.activeTurnId)
@@ -584,21 +651,30 @@ async function settleFailure(ctx, lease, s, error) {
584
651
  await wakeWorkflowOf(ctx, t, current, s.activeTurnId, "turn.failed", payload);
585
652
  });
586
653
  }
587
- /** `checkpoint` with its engine state stored lean (`history.ts`). */
588
- function leanCheckpoint(checkpoint) {
654
+ /** Whether the session runs our harness, whose state core may store lean (R4.2). */
655
+ const ours = (session) => harnessOf(session) === HARNESS_ID;
656
+ /**
657
+ * `state` as the session row stores it: our engine's without its transcript (`history.ts`);
658
+ * another harness's as it sent it, never read (R4.2).
659
+ */
660
+ function leanOf(session, state) {
661
+ return ours(session) ? leanState(state) : state;
662
+ }
663
+ /** `checkpoint` with its engine state stored lean (`leanOf`). */
664
+ function leanCheckpoint(session, checkpoint) {
589
665
  const state = checkpoint?.state;
590
666
  if (!checkpoint || !state)
591
667
  return checkpoint;
592
- return { ...checkpoint, state: leanState(state) };
668
+ return { ...checkpoint, state: leanOf(session, state) };
593
669
  }
594
670
  /**
595
671
  * Appends a segment's transcript edits; a snapshot moves `history.snapshot`. Returns the seq of
596
672
  * the last edit, if any.
597
673
  */
598
- async function recordTranscript(t, id, turnId, s, updates) {
674
+ async function recordTranscript(t, id, turnId, s, updates, writer) {
599
675
  let last;
600
676
  for (const update of updates) {
601
- const event = await t.event(id, turnId, "transcript.updated", update);
677
+ const event = await t.event(id, turnId, "transcript.updated", update, writer);
602
678
  if (update.keep === 0)
603
679
  s.history = { from: s.history?.from ?? event.seq, snapshot: event.seq };
604
680
  last = event.seq;
@@ -4,7 +4,7 @@ import type { HostEffect } from "@nylorun/harness/run";
4
4
  import { type TenantContext } from "./context.js";
5
5
  export { isReadArtifactCall, isSaveArtifactCall } from "../harness/calls.js";
6
6
  export declare function callSaveArtifact(ctx: TenantContext, request: HostEffect, signal: AbortSignal): Promise<SandboxToolOutcome>;
7
- /** What `read_artifact` returns: a page of a text artifact, or an image as a file. */
7
+ /** What `read_artifact` returns: a page of a text artifact, or a file the model reads. */
8
8
  interface ReadOutcome {
9
9
  readonly kind: "completed";
10
10
  readonly output: Record<string, unknown>;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Our engine's built-in artifact tools, of the `nylorun.artifacts` capability
3
- * (`sandbox/session-sandbox.ts`). The Runtime runs them itself, like the sandbox tools.
2
+ * Our engine's built-in artifact tools, which the Runtime pins in a session agent's `runtime`
3
+ * block (`sandbox/session-sandbox.ts`). The Runtime runs them itself, like the sandbox tools.
4
4
  *
5
5
  * `save_artifact` (F8.1), which comes with a session's sandbox, saves a file as an artifact of
6
6
  * its session:
@@ -9,25 +9,28 @@
9
9
  * The version commits with `artifact.created` (or `artifact.version.created`) on the session's
10
10
  * stream, carrying the turn and the tool call.
11
11
  *
12
- * `read_artifact` (R2b C11, Q24), which an agent with a remote MCP server or an HTTP tool has,
13
- * reads a text artifact of its own session in pages of at most 32 KiB, from a byte `offset`:
14
- * how the model reads a tool result the Runtime stored because it was too large to show
15
- * (`tool-results.ts`). An image is shown to a model that reads images, as a file of the result.
16
- * It reaches only the session's own artifacts, never a Tenant-wide one or another session's.
12
+ * `read_artifact` (R2b C11, Q24; every agent since protocol 12) reads a text artifact in pages of
13
+ * at most 32 KiB, from a byte `offset`: how the model reads a tool result the Runtime stored
14
+ * because it was too large to show (`tool-results.ts`), and a file of an earlier turn again, by
15
+ * its reference line. Another file is shown as a file of the result when the Tenant's model reads
16
+ * its type, and refused (`artifact.unsupported`) when it does not. It reaches the session's own
17
+ * artifacts and the Tenant-wide ones its messages named (`Session.fileArtifacts`), never another
18
+ * session's. `version` picks a version; the latest by default.
17
19
  *
18
20
  * Expected problems (no such file, too large, the Tenant's total reached, not text) are failed
19
21
  * outcomes the model sees; only infrastructure errors throw.
20
22
  */
21
23
  import { basename } from "node:path";
22
- import { READ_ARTIFACT_MAX_BYTES, SANDBOX_CAPABILITY_ID, } from "@nylorun/core/define";
24
+ import { READ_ARTIFACT_MAX_BYTES } from "@nylorun/core/define";
23
25
  import { isArtifactId } from "@nylorun/core/compatibility";
24
26
  import { readArtifactLimits } from "../artifacts/config.js";
25
- import { MODEL_FILE_MAX_BYTES, contentTypeFor, essence, isModelImage, isText, normalizeContentType, } from "../artifacts/media-types.js";
27
+ import { contentTypeFor, essence, isText, normalizeContentType, unreadableReason, } from "../artifacts/media-types.js";
28
+ import { currentModelInputs } from "../model/catalog.js";
26
29
  import { uploadArtifact } from "../artifacts/service.js";
27
30
  import { DEFAULT_CONTENT_TYPE } from "../blob/index.js";
28
- import { sandboxCapabilityOf } from "../sandbox/manager.js";
31
+ import { sandboxToolSpec } from "../sandbox/manager.js";
29
32
  import { sandboxWorkspaceOf } from "../sandbox/share.js";
30
- import { sandboxLookup, sessionOf } from "./context.js";
33
+ import { sessionOf } from "./context.js";
31
34
  import { HttpError } from "./http.js";
32
35
  import { manifestFor } from "./session.js";
33
36
  import { jsonBytes, utf8Boundary } from "./tool-results.js";
@@ -47,13 +50,12 @@ export async function callSaveArtifact(ctx, request, signal) {
47
50
  const artifactId = typeof input.artifactId === "string" ? input.artifactId : undefined;
48
51
  const { s, workspace, limits, ours } = await ctx.store.tx(async (t) => {
49
52
  const s = await sessionOf(t, request.sessionId);
50
- const lookup = await sandboxLookup(t, s.id);
51
53
  // A new version only of an artifact of this session.
52
54
  const ours = artifactId === undefined ||
53
55
  (isArtifactId(artifactId) && (await t.artifact(artifactId))?.sessionId === s.id);
54
56
  return {
55
57
  s,
56
- workspace: sandboxWorkspaceOf(s, lookup),
58
+ workspace: sandboxWorkspaceOf(s),
57
59
  limits: await readArtifactLimits(t),
58
60
  ours,
59
61
  };
@@ -63,15 +65,15 @@ export async function callSaveArtifact(ctx, request, signal) {
63
65
  let bytes;
64
66
  let fileName;
65
67
  if (path !== undefined) {
66
- const capability = sandboxCapabilityOf(manifestFor(s.manifest, request.agent), SANDBOX_CAPABILITY_ID, "read");
67
- if (!capability)
68
+ const spec = sandboxToolSpec(manifestFor(s.manifest, request.agent), "read");
69
+ if (!spec)
68
70
  return failed("artifact.no_sandbox", "This session has no sandbox to read a path from.");
69
71
  const read = await ctx.sandbox.readBytes({
70
72
  id: workspace.ownerId,
71
73
  activeTurnId: s.activeTurnId,
72
74
  manifest: s.manifest,
73
- ...(workspace.sandboxId === undefined ? {} : { sandboxId: workspace.sandboxId }),
74
- }, capability, path, limits.fileBytes, signal);
75
+ ...(workspace.environmentId === undefined ? {} : { environmentId: workspace.environmentId }),
76
+ }, spec, path, limits.fileBytes, signal);
75
77
  if (read.kind === "failed")
76
78
  return read;
77
79
  if (read.kind === "missing")
@@ -129,9 +131,12 @@ export async function callSaveArtifact(ctx, request, signal) {
129
131
  export async function callReadArtifact(ctx, request, signal) {
130
132
  const input = (request.input ?? {});
131
133
  const artifactId = typeof input.artifactId === "string" ? input.artifactId : "";
134
+ const wanted = input.version;
132
135
  const offset = input.offset ?? 0;
133
136
  const length = input.length ?? READ_ARTIFACT_MAX_BYTES;
134
137
  if (artifactId === "" ||
138
+ (wanted !== undefined &&
139
+ (typeof wanted !== "number" || !Number.isSafeInteger(wanted) || wanted < 1)) ||
135
140
  typeof offset !== "number" ||
136
141
  !Number.isSafeInteger(offset) ||
137
142
  offset < 0 ||
@@ -139,20 +144,25 @@ export async function callReadArtifact(ctx, request, signal) {
139
144
  !Number.isSafeInteger(length) ||
140
145
  length < 1 ||
141
146
  length > READ_ARTIFACT_MAX_BYTES)
142
- return failed("artifact.invalid_input", `Give artifactId, an offset of 0 or more, and a length of 1 to ${READ_ARTIFACT_MAX_BYTES}.`);
147
+ return failed("artifact.invalid_input", `Give artifactId, a version of 1 or more if any, an offset of 0 or more, and a length of 1 to ${READ_ARTIFACT_MAX_BYTES}.`);
143
148
  const found = await ctx.store.tx(async (t) => {
144
149
  const s = await sessionOf(t, request.sessionId);
145
150
  if (!isArtifactId(artifactId))
146
151
  return undefined;
147
- // Only an artifact of this session: never a Tenant-wide one, nor another session's.
152
+ // An artifact of this session, or a Tenant-wide one its messages named; never another
153
+ // session's.
148
154
  const artifact = await t.artifact(artifactId);
149
- if (!artifact || artifact.sessionId !== s.id)
155
+ if (!artifact ||
156
+ (artifact.sessionId !== s.id &&
157
+ !(artifact.sessionId === null && s.fileArtifacts?.includes(artifact.id))))
150
158
  return undefined;
151
- const version = await t.artifactVersion(artifact.id, artifact.latestVersion);
152
- return version ? { artifact, version } : undefined;
159
+ const version = await t.artifactVersion(artifact.id, typeof wanted === "number" ? wanted : artifact.latestVersion);
160
+ return { artifact, version };
153
161
  });
154
162
  if (!found)
155
163
  return failed("artifact.not_found", `Artifact ${artifactId} is not an artifact of this session.`);
164
+ if (!found.version)
165
+ return failed("artifact.not_found", `Artifact ${artifactId} has no version ${String(wanted)}.`);
156
166
  const { artifact, version } = found;
157
167
  const about = {
158
168
  artifactId: artifact.id,
@@ -163,19 +173,29 @@ export async function callReadArtifact(ctx, request, signal) {
163
173
  };
164
174
  if (artifact.kind !== "file")
165
175
  return failed("artifact.not_text", `${artifact.name} is a folder; read_artifact reads a file.`);
166
- if (isModelImage(version.contentType) && version.size <= MODEL_FILE_MAX_BYTES)
176
+ if (!isText(version.contentType)) {
177
+ // A file the Tenant's model reads goes to it beside the result (protocol 12). Without a
178
+ // model in the vault (a test's fake provider), text and images.
179
+ const inputs = (await currentModelInputs(ctx)) ?? ["text", "image"];
180
+ const reason = unreadableReason(version.contentType, inputs);
181
+ if (reason)
182
+ return failed("artifact.unsupported", `${artifact.name} is ${essence(version.contentType)}: ${reason}.`);
167
183
  return {
168
184
  kind: "completed",
169
185
  output: about,
170
186
  files: [
171
187
  {
172
188
  mediaType: version.contentType,
173
- reference: { artifactId: artifact.id, version: version.version, name: artifact.name },
189
+ reference: {
190
+ artifactId: artifact.id,
191
+ version: version.version,
192
+ name: artifact.name,
193
+ size: version.size,
194
+ },
174
195
  },
175
196
  ],
176
197
  };
177
- if (!isText(version.contentType))
178
- return failed("artifact.not_text", `${artifact.name} is ${essence(version.contentType)}, not text; read_artifact reads text and shows images.`);
198
+ }
179
199
  if (offset > version.size || (offset === version.size && version.size > 0))
180
200
  return failed("artifact.invalid_input", `Offset ${offset} is past the end of ${artifact.name}, which is ${version.size} bytes.`);
181
201
  const end = Math.min(offset + length, version.size);
@@ -1,3 +1,16 @@
1
- import type { SessionCommand } from "@nylorun/core/contracts";
1
+ import type { EventWriter, SessionCommand } from "@nylorun/core/contracts";
2
2
  import { type AuthScope, type TenantContext } from "./context.js";
3
- export declare function command(ctx: TenantContext, id: string, input: SessionCommand, scope: AuthScope): Promise<unknown>;
3
+ import type { TraceContext } from "../traces/ids.js";
4
+ export declare function command(ctx: TenantContext, id: string, input: SessionCommand, scope: AuthScope,
5
+ /**
6
+ * The W3C trace context the request carried (R7.4): a `message` records it on its
7
+ * `command.message`, and the turn joins the caller's trace. Not part of the command's identity.
8
+ */
9
+ options?: {
10
+ trace?: TraceContext;
11
+ }): Promise<unknown>;
12
+ /**
13
+ * Who sent a command (R4.9), from its credential and never its body: a client over the Runtime
14
+ * API (an application, or a person through it or a token), or core itself.
15
+ */
16
+ export declare function commandWriter(scope: AuthScope): EventWriter;