@nylorun/runtime 0.26.0-beta → 0.28.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 (379) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +18 -14
  3. package/dist/adapters/execution/restate.d.ts +15 -18
  4. package/dist/adapters/execution/restate.js +31 -27
  5. package/dist/adapters/replication/pgoutput.d.ts +1 -1
  6. package/dist/adapters/replication/pgoutput.js +3 -2
  7. package/dist/adapters/sandbox/local.js +8 -5
  8. package/dist/adapters/sandbox/virtual.js +2 -2
  9. package/dist/adapters/streams/s2.d.ts +1 -1
  10. package/dist/adapters/streams/s2.js +18 -14
  11. package/dist/api/a2a/card.d.ts +4 -3
  12. package/dist/api/a2a/card.js +4 -4
  13. package/dist/api/a2a/endpoint.js +3 -4
  14. package/dist/api/a2a/protocol.js +4 -0
  15. package/dist/api/a2a/routes.d.ts +2 -6
  16. package/dist/api/a2a/routes.js +30 -25
  17. package/dist/api/ag-ui/endpoint.js +2 -3
  18. package/dist/api/ag-ui/routes.js +8 -11
  19. package/dist/api/components.d.ts +6789 -3405
  20. package/dist/api/components.js +43 -30
  21. package/dist/api/gate/keys.js +7 -4
  22. package/dist/api/gate/routes.d.ts +6 -2
  23. package/dist/api/gate/routes.js +15 -11
  24. package/dist/api/http/app.js +9 -4
  25. package/dist/api/http/body.d.ts +17 -3
  26. package/dist/api/http/body.js +35 -7
  27. package/dist/api/http/respond.js +6 -2
  28. package/dist/api/http/routes/access.js +5 -4
  29. package/dist/api/http/routes/agent-manifests.d.ts +10 -0
  30. package/dist/api/http/routes/agent-manifests.js +135 -0
  31. package/dist/api/http/routes/agents.d.ts +11 -0
  32. package/dist/api/http/routes/agents.js +418 -0
  33. package/dist/api/http/routes/artifacts.js +21 -9
  34. package/dist/api/http/routes/coverage.d.ts +4 -4
  35. package/dist/api/http/routes/coverage.js +13 -7
  36. package/dist/api/http/routes/files.d.ts +2 -1
  37. package/dist/api/http/routes/files.js +31 -5
  38. package/dist/api/http/routes/mcp.js +3 -3
  39. package/dist/api/http/routes/me.d.ts +1 -1
  40. package/dist/api/http/routes/me.js +1 -2
  41. package/dist/api/http/routes/memories.d.ts +9 -0
  42. package/dist/api/http/routes/memories.js +152 -0
  43. package/dist/api/http/routes/reads.d.ts +10 -111
  44. package/dist/api/http/routes/reads.js +51 -29
  45. package/dist/api/http/routes/sessions.js +26 -54
  46. package/dist/api/http/routes/tenant.d.ts +2 -2
  47. package/dist/api/http/routes/tenant.js +77 -24
  48. package/dist/api/http/routes/triggers.d.ts +10 -0
  49. package/dist/api/http/routes/triggers.js +180 -0
  50. package/dist/api/inbound-files.d.ts +3 -9
  51. package/dist/api/inbound-files.js +18 -24
  52. package/dist/api/openapi.js +73 -47
  53. package/dist/artifacts/disk.d.ts +10 -14
  54. package/dist/artifacts/disk.js +99 -95
  55. package/dist/artifacts/files.d.ts +13 -4
  56. package/dist/artifacts/files.js +27 -9
  57. package/dist/artifacts/folder-upload.d.ts +75 -0
  58. package/dist/artifacts/folder-upload.js +263 -0
  59. package/dist/artifacts/folders.d.ts +14 -8
  60. package/dist/artifacts/folders.js +19 -11
  61. package/dist/artifacts/links.js +7 -6
  62. package/dist/artifacts/parts.d.ts +9 -14
  63. package/dist/artifacts/parts.js +11 -9
  64. package/dist/artifacts/pipe.d.ts +15 -0
  65. package/dist/artifacts/pipe.js +71 -0
  66. package/dist/artifacts/quota.d.ts +36 -0
  67. package/dist/artifacts/quota.js +44 -0
  68. package/dist/artifacts/service.d.ts +25 -1
  69. package/dist/artifacts/service.js +72 -27
  70. package/dist/blob/fs.js +2 -1
  71. package/dist/blob/s3.d.ts +6 -2
  72. package/dist/blob/s3.js +9 -7
  73. package/dist/blob/types.d.ts +1 -1
  74. package/dist/blob/types.js +1 -1
  75. package/dist/core/flow-host.d.ts +0 -1
  76. package/dist/core/limits.d.ts +11 -35
  77. package/dist/core/limits.js +5 -73
  78. package/dist/core/sandbox-routes.js +0 -1
  79. package/dist/core/turn-manifest.d.ts +1 -2
  80. package/dist/core/turn-manifest.js +4 -28
  81. package/dist/execution/memory.d.ts +8 -5
  82. package/dist/execution/memory.js +8 -6
  83. package/dist/execution/types.d.ts +1 -6
  84. package/dist/execution/types.js +0 -5
  85. package/dist/gates/contract.d.ts +3 -2
  86. package/dist/gates/contract.js +1 -1
  87. package/dist/gates/egress-credentials.js +5 -4
  88. package/dist/gates/egress.d.ts +13 -13
  89. package/dist/gates/egress.js +19 -21
  90. package/dist/gates/fixture-model.js +13 -2
  91. package/dist/gates/handler.d.ts +1 -3
  92. package/dist/gates/handler.js +4 -3
  93. package/dist/gates/http-client.d.ts +4 -3
  94. package/dist/gates/http-client.js +5 -7
  95. package/dist/gates/http-tool.d.ts +24 -4
  96. package/dist/gates/http-tool.js +41 -13
  97. package/dist/gates/in-process.d.ts +2 -1
  98. package/dist/gates/in-process.js +17 -9
  99. package/dist/gates/inflight.d.ts +2 -2
  100. package/dist/gates/inflight.js +3 -2
  101. package/dist/gates/journal.js +5 -7
  102. package/dist/gates/mcp-handler.d.ts +4 -3
  103. package/dist/gates/mcp-handler.js +15 -5
  104. package/dist/gates/meter.js +6 -5
  105. package/dist/gates/model-gate.d.ts +11 -2
  106. package/dist/gates/policy.d.ts +34 -6
  107. package/dist/gates/policy.js +66 -18
  108. package/dist/gates/provider-gate.d.ts +22 -0
  109. package/dist/gates/provider-gate.js +30 -0
  110. package/dist/gates/tool-calls.d.ts +1 -3
  111. package/dist/gates/tool-calls.js +2 -3
  112. package/dist/gates/tool-client.d.ts +1 -1
  113. package/dist/gates/tool-client.js +5 -6
  114. package/dist/gates/tool-contract.d.ts +8 -5
  115. package/dist/gates/tool-contract.js +1 -1
  116. package/dist/gates/tool-gate.d.ts +18 -7
  117. package/dist/gates/tool-gate.js +49 -7
  118. package/dist/harness/calls.d.ts +27 -25
  119. package/dist/harness/calls.js +40 -46
  120. package/dist/harness/executors.d.ts +10 -3
  121. package/dist/harness/executors.js +53 -23
  122. package/dist/harness/main.js +2 -2
  123. package/dist/harness/pod.d.ts +2 -2
  124. package/dist/harness/pod.js +7 -6
  125. package/dist/harness/run-tokens.d.ts +4 -1
  126. package/dist/harness/run-tokens.js +6 -2
  127. package/dist/harness/service.d.ts +17 -14
  128. package/dist/harness/service.js +21 -35
  129. package/dist/harness/setup.d.ts +0 -2
  130. package/dist/harness/setup.js +21 -15
  131. package/dist/harness/ws-client.d.ts +2 -2
  132. package/dist/harness/ws-client.js +7 -5
  133. package/dist/harness/ws-port.d.ts +3 -4
  134. package/dist/harness/ws-port.js +3 -4
  135. package/dist/harness-api/artifacts.d.ts +20 -0
  136. package/dist/harness-api/artifacts.js +478 -0
  137. package/dist/harness-api/in-process.d.ts +2 -2
  138. package/dist/harness-api/in-process.js +18 -16
  139. package/dist/harness-api/record.d.ts +10 -9
  140. package/dist/harness-api/record.js +113 -157
  141. package/dist/harness-api/renew.d.ts +1 -1
  142. package/dist/harness-api/renew.js +2 -2
  143. package/dist/harness-api/server.d.ts +1 -16
  144. package/dist/harness-api/server.js +86 -73
  145. package/dist/harness-api/start.d.ts +27 -5
  146. package/dist/harness-api/start.js +102 -19
  147. package/dist/harness-api/tool-journal.d.ts +15 -10
  148. package/dist/harness-api/tool-journal.js +26 -24
  149. package/dist/harness-api/workspace.d.ts +0 -4
  150. package/dist/harness-api/workspace.js +4 -47
  151. package/dist/harness-api/ws-server.js +10 -10
  152. package/dist/host/create-host.js +2 -3
  153. package/dist/host/execution.js +2 -3
  154. package/dist/host/gates.d.ts +2 -9
  155. package/dist/host/gates.js +15 -12
  156. package/dist/host/http.d.ts +2 -4
  157. package/dist/host/http.js +5 -29
  158. package/dist/host/main.js +10 -3
  159. package/dist/host/operate.js +12 -3
  160. package/dist/host/stack-config.d.ts +11 -0
  161. package/dist/host/stack-config.js +21 -1
  162. package/dist/index.d.ts +0 -1
  163. package/dist/index.js +0 -1
  164. package/dist/infra/index.js +2 -1
  165. package/dist/infra/readiness.d.ts +0 -2
  166. package/dist/infra/readiness.js +4 -3
  167. package/dist/keys/client.d.ts +1 -0
  168. package/dist/keys/client.js +2 -3
  169. package/dist/keys/contract.d.ts +0 -2
  170. package/dist/keys/contract.js +0 -2
  171. package/dist/keys/egress-ca.d.ts +0 -2
  172. package/dist/keys/egress-ca.js +2 -3
  173. package/dist/keys/keys.d.ts +15 -4
  174. package/dist/keys/keys.js +22 -1
  175. package/dist/keys/webhooks.d.ts +25 -0
  176. package/dist/keys/webhooks.js +62 -0
  177. package/dist/keys/x509.d.ts +8 -4
  178. package/dist/keys/x509.js +14 -5
  179. package/dist/management-openapi.json +3043 -1954
  180. package/dist/mcp/connect.d.ts +35 -26
  181. package/dist/mcp/connect.js +112 -141
  182. package/dist/mcp/pool.d.ts +27 -22
  183. package/dist/mcp/pool.js +34 -48
  184. package/dist/mcp/preview.d.ts +12 -5
  185. package/dist/mcp/preview.js +43 -26
  186. package/dist/mcp/snapshot.d.ts +35 -86
  187. package/dist/mcp/snapshot.js +93 -135
  188. package/dist/memory/service.d.ts +97 -0
  189. package/dist/memory/service.js +335 -0
  190. package/dist/memory/tool.d.ts +36 -0
  191. package/dist/memory/tool.js +84 -0
  192. package/dist/model/auth-store.js +5 -3
  193. package/dist/model/capabilities.d.ts +1 -1
  194. package/dist/model/catalog.d.ts +9 -36
  195. package/dist/model/catalog.js +6 -33
  196. package/dist/model/descriptor.d.ts +51 -0
  197. package/dist/model/descriptor.js +83 -0
  198. package/dist/model/models.d.ts +0 -3
  199. package/dist/model/models.js +3 -5
  200. package/dist/model/native-blocks.js +1 -1
  201. package/dist/model/pi-model.d.ts +16 -5
  202. package/dist/model/pi-model.js +86 -105
  203. package/dist/openapi.json +13653 -9986
  204. package/dist/reads/cursor.js +3 -1
  205. package/dist/reads/types.d.ts +3 -3
  206. package/dist/record/agent.d.ts +27 -0
  207. package/dist/record/agent.js +46 -0
  208. package/dist/record/append.d.ts +34 -2
  209. package/dist/record/append.js +50 -1
  210. package/dist/record/index.d.ts +2 -2
  211. package/dist/record/index.js +2 -2
  212. package/dist/redact.d.ts +1 -1
  213. package/dist/redact.js +3 -2
  214. package/dist/sandbox/egress-token.d.ts +3 -3
  215. package/dist/sandbox/egress-token.js +3 -3
  216. package/dist/sandbox/join.js +8 -7
  217. package/dist/sandbox/manager.d.ts +41 -41
  218. package/dist/sandbox/manager.js +112 -44
  219. package/dist/sandbox/placement.d.ts +2 -2
  220. package/dist/sandbox/placement.js +7 -10
  221. package/dist/sandbox/pods/client.d.ts +1 -10
  222. package/dist/sandbox/pods/client.js +8 -4
  223. package/dist/sandbox/pods/lifecycle.d.ts +25 -17
  224. package/dist/sandbox/pods/lifecycle.js +51 -29
  225. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  226. package/dist/sandbox/pods/network-gate.js +7 -7
  227. package/dist/sandbox/pods/reconcile.js +14 -3
  228. package/dist/sandbox/pods/spec.js +8 -5
  229. package/dist/sandbox/policy.d.ts +7 -1
  230. package/dist/sandbox/policy.js +10 -3
  231. package/dist/sandbox/resolve.d.ts +11 -6
  232. package/dist/sandbox/resolve.js +19 -9
  233. package/dist/sandbox/setup.d.ts +6 -2
  234. package/dist/sandbox/setup.js +47 -14
  235. package/dist/sandbox/share.d.ts +5 -14
  236. package/dist/sandbox/share.js +5 -34
  237. package/dist/sandbox/skills.d.ts +7 -13
  238. package/dist/sandbox/skills.js +16 -31
  239. package/dist/sandbox/tenant-config.d.ts +2 -2
  240. package/dist/sandbox/tenant-config.js +4 -3
  241. package/dist/sandbox/tools.d.ts +7 -1
  242. package/dist/sandbox/tools.js +26 -26
  243. package/dist/sandbox/types.d.ts +10 -3
  244. package/dist/sandbox/types.js +9 -0
  245. package/dist/store/postgres/connect.d.ts +6 -5
  246. package/dist/store/postgres/connect.js +14 -11
  247. package/dist/store/postgres/control.js +13 -13
  248. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  249. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  250. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  251. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  252. package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
  253. package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
  254. package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
  255. package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
  256. package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
  257. package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
  258. package/dist/store/postgres/reads.js +17 -82
  259. package/dist/store/postgres/record-writer.d.ts +4 -4
  260. package/dist/store/postgres/record-writer.js +9 -9
  261. package/dist/store/postgres/schema.d.ts +1382 -151
  262. package/dist/store/postgres/schema.js +191 -37
  263. package/dist/store/postgres/store.js +384 -105
  264. package/dist/store/postgres/tenant.js +1 -2
  265. package/dist/store/types.d.ts +172 -57
  266. package/dist/store/types.js +0 -1
  267. package/dist/streams/relay/core.d.ts +3 -3
  268. package/dist/streams/relay/core.js +9 -10
  269. package/dist/tenant/advance.js +41 -39
  270. package/dist/tenant/agent-manifests.d.ts +72 -0
  271. package/dist/tenant/agent-manifests.js +359 -0
  272. package/dist/tenant/agent-versions.d.ts +126 -0
  273. package/dist/tenant/agent-versions.js +389 -0
  274. package/dist/tenant/agents.d.ts +230 -0
  275. package/dist/tenant/{environments.js → agents.js} +493 -328
  276. package/dist/tenant/auth.d.ts +1 -1
  277. package/dist/tenant/auth.js +3 -3
  278. package/dist/tenant/commands.d.ts +63 -2
  279. package/dist/tenant/commands.js +240 -90
  280. package/dist/tenant/context.d.ts +57 -33
  281. package/dist/tenant/context.js +6 -2
  282. package/dist/tenant/definition-files.d.ts +25 -4
  283. package/dist/tenant/definition-files.js +85 -47
  284. package/dist/tenant/effects.js +15 -51
  285. package/dist/tenant/ephemeral.js +2 -1
  286. package/dist/tenant/history.d.ts +1 -1
  287. package/dist/tenant/history.js +1 -1
  288. package/dist/tenant/host-token.d.ts +3 -3
  289. package/dist/tenant/host-token.js +11 -10
  290. package/dist/tenant/http.d.ts +5 -8
  291. package/dist/tenant/http.js +9 -0
  292. package/dist/tenant/identity-file.d.ts +29 -6
  293. package/dist/tenant/identity-file.js +45 -15
  294. package/dist/tenant/issuers.d.ts +5 -5
  295. package/dist/tenant/issuers.js +34 -26
  296. package/dist/tenant/jwt.d.ts +8 -2
  297. package/dist/tenant/jwt.js +10 -2
  298. package/dist/tenant/limits.d.ts +87 -0
  299. package/dist/tenant/limits.js +172 -0
  300. package/dist/tenant/memories.d.ts +38 -0
  301. package/dist/tenant/memories.js +119 -0
  302. package/dist/tenant/module.d.ts +4 -1
  303. package/dist/tenant/module.js +9 -8
  304. package/dist/tenant/outbound.d.ts +11 -14
  305. package/dist/tenant/outbound.js +10 -15
  306. package/dist/tenant/reset.js +2 -2
  307. package/dist/tenant/run-grants.d.ts +6 -0
  308. package/dist/tenant/run-grants.js +7 -2
  309. package/dist/tenant/run-token.d.ts +0 -4
  310. package/dist/tenant/run-token.js +6 -11
  311. package/dist/tenant/runtime-limits.d.ts +24 -0
  312. package/dist/tenant/runtime-limits.js +27 -0
  313. package/dist/tenant/runtime.d.ts +1 -1
  314. package/dist/tenant/runtime.js +38 -43
  315. package/dist/tenant/session-streams.js +7 -6
  316. package/dist/tenant/session.d.ts +1 -1
  317. package/dist/tenant/session.js +1 -1
  318. package/dist/tenant/sessions.d.ts +28 -45
  319. package/dist/tenant/sessions.js +89 -209
  320. package/dist/tenant/signing-keys.d.ts +1 -1
  321. package/dist/tenant/signing-keys.js +5 -6
  322. package/dist/tenant/status.js +10 -10
  323. package/dist/tenant/streams.js +18 -20
  324. package/dist/tenant/sweep.d.ts +1 -21
  325. package/dist/tenant/sweep.js +51 -51
  326. package/dist/tenant/transcript.d.ts +70 -23
  327. package/dist/tenant/transcript.js +28 -22
  328. package/dist/tenant/triggers.d.ts +143 -0
  329. package/dist/tenant/triggers.js +715 -0
  330. package/dist/tenant/turn-timing.d.ts +11 -1
  331. package/dist/tenant/turn-timing.js +3 -1
  332. package/dist/tenant/types.d.ts +9 -14
  333. package/dist/tenant/worker.d.ts +4 -10
  334. package/dist/tenant/worker.js +10 -14
  335. package/dist/traces/ids.js +5 -1
  336. package/dist/traces/map.d.ts +10 -7
  337. package/dist/traces/map.js +16 -12
  338. package/dist/traces/otlp.d.ts +3 -3
  339. package/dist/traces/otlp.js +5 -3
  340. package/dist/traces/projector.d.ts +9 -4
  341. package/dist/traces/projector.js +13 -7
  342. package/dist/traces/read.js +6 -1
  343. package/dist/tuning.d.ts +390 -0
  344. package/dist/tuning.js +677 -0
  345. package/dist/vault/headers.js +4 -2
  346. package/dist/vault/service.d.ts +13 -8
  347. package/dist/vault/service.js +61 -17
  348. package/dist/version.d.ts +1 -1
  349. package/dist/version.js +1 -1
  350. package/package.json +13 -12
  351. package/dist/api/http/routes/environment-manifests.d.ts +0 -9
  352. package/dist/api/http/routes/environment-manifests.js +0 -118
  353. package/dist/api/http/routes/environments.d.ts +0 -11
  354. package/dist/api/http/routes/environments.js +0 -332
  355. package/dist/artifacts/export.d.ts +0 -8
  356. package/dist/artifacts/export.js +0 -204
  357. package/dist/artifacts/media-types.d.ts +0 -30
  358. package/dist/artifacts/media-types.js +0 -137
  359. package/dist/artifacts/workspace.d.ts +0 -60
  360. package/dist/artifacts/workspace.js +0 -45
  361. package/dist/media.d.ts +0 -10
  362. package/dist/media.js +0 -58
  363. package/dist/record/environment.d.ts +0 -25
  364. package/dist/record/environment.js +0 -44
  365. package/dist/sandbox/runtime-tools.d.ts +0 -9
  366. package/dist/sandbox/runtime-tools.js +0 -16
  367. package/dist/sandbox/session-sandbox.d.ts +0 -16
  368. package/dist/sandbox/session-sandbox.js +0 -77
  369. package/dist/tenant/artifact-tool.d.ts +0 -13
  370. package/dist/tenant/artifact-tool.js +0 -243
  371. package/dist/tenant/environment-manifests.d.ts +0 -57
  372. package/dist/tenant/environment-manifests.js +0 -200
  373. package/dist/tenant/environments.d.ts +0 -174
  374. package/dist/tenant/skill-tool.d.ts +0 -20
  375. package/dist/tenant/skill-tool.js +0 -67
  376. package/dist/tenant/tool-results.d.ts +0 -57
  377. package/dist/tenant/tool-results.js +0 -298
  378. package/dist/tenant/tool-search.d.ts +0 -39
  379. package/dist/tenant/tool-search.js +0 -103
@@ -48,6 +48,6 @@ export declare function accessOf(scope: AuthScope): SessionAccess | undefined;
48
48
  export declare function ownerOf(scope: AuthScope): string | undefined;
49
49
  /** The application principal behind the request, acting for a subject or not. */
50
50
  export declare function requirePrincipal(scope: AuthScope): string;
51
- /** True when a token caller may reach `agentId` (its issuer's allowlist). Other callers always may. */
51
+ /** True when a token caller may reach Agent `agentId` (its grants). Other callers always may. */
52
52
  export declare function mayUseAgent(scope: AuthScope, agentId: string): boolean;
53
53
  export declare function requireApplication(scope: AuthScope): string;
@@ -1,5 +1,5 @@
1
1
  import { SCOPES_HEADER, SUBJECT_HEADER, } from "@nylorun/core/compatibility";
2
- import { parseSubjectHeaders, TOKEN_SCOPES } from "@nylorun/core/contracts";
2
+ import { agentGranted, parseSubjectHeaders, TOKEN_SCOPES } from "@nylorun/core/contracts";
3
3
  import { hashToken } from "../core/bearer.js";
4
4
  import { fail } from "./http.js";
5
5
  import { verifyIssuerToken } from "./issuers.js";
@@ -168,9 +168,9 @@ export function requirePrincipal(scope) {
168
168
  return scope.principalId;
169
169
  return fail(403, "Application credential required");
170
170
  }
171
- /** True when a token caller may reach `agentId` (its issuer's allowlist). Other callers always may. */
171
+ /** True when a token caller may reach Agent `agentId` (its grants). Other callers always may. */
172
172
  export function mayUseAgent(scope, agentId) {
173
- return scope.kind !== "token" || scope.agents === "*" || scope.agents.has(agentId);
173
+ return scope.kind !== "token" || scope.agents === "*" || agentGranted(scope.agents, agentId);
174
174
  }
175
175
  export function requireApplication(scope) {
176
176
  if (scope.kind === "application")
@@ -1,5 +1,6 @@
1
- import type { EventWriter, SessionCommand } from "@nylorun/core/contracts";
2
- import { type AuthScope, type TenantContext } from "./context.js";
1
+ import type { EventWriter, MessageDelivery, MessageEventBody, SessionCommand } from "@nylorun/core/contracts";
2
+ import type { Tx } from "../store/types.js";
3
+ import { type AuthScope, type Session, type TenantContext } from "./context.js";
3
4
  import type { TraceContext } from "../traces/ids.js";
4
5
  export declare function command(ctx: TenantContext, id: string, input: SessionCommand, scope: AuthScope,
5
6
  /**
@@ -8,7 +9,67 @@ export declare function command(ctx: TenantContext, id: string, input: SessionCo
8
9
  */
9
10
  options?: {
10
11
  trace?: TraceContext;
12
+ /**
13
+ * What a message to a busy session does when it says nothing (`delivery`): the route's
14
+ * answer to the caller's protocol (`queue` from 17). Core's own messages and the AG-UI and
15
+ * A2A adapters, which stream the run they start, take `reject`.
16
+ */
17
+ delivery?: MessageDelivery;
11
18
  }): Promise<unknown>;
19
+ export declare const takesMessages: (s: Session) => boolean;
20
+ /** A message ready to start a turn: as sent, its engine input and who sent it. */
21
+ export interface TurnMessage {
22
+ command: MessageEventBody;
23
+ /** An agent's message input, or a workflow's input value. */
24
+ input: unknown;
25
+ /** The artifacts its file parts named. */
26
+ fileArtifacts: string[];
27
+ writer: EventWriter;
28
+ /** Set for a message that waited in the session's queue. */
29
+ queuedMessageId?: string;
30
+ trace?: TraceContext;
31
+ /** What sent it when not a client (track T4): a trigger's run, or the session's own wake. */
32
+ cause?: MessageCause;
33
+ }
34
+ /** What sent a message when not a client (`command.message`'s `cause`). */
35
+ export interface MessageCause {
36
+ kind: "trigger" | "self";
37
+ triggerId: string;
38
+ runId: string;
39
+ }
40
+ /** How a message was taken: it started a turn, or waits in the session's queue. */
41
+ export type MessageAnswer = {
42
+ status: "accepted";
43
+ turnId: string;
44
+ cursor: string;
45
+ } | {
46
+ status: "queued";
47
+ queuedMessageId: string;
48
+ position: number;
49
+ cursor: string;
50
+ };
51
+ /**
52
+ * Takes a resolved message on session `s` (locked) in transaction `t`: it starts a turn when the
53
+ * session takes messages and none wait, else it waits in the queue behind them (protocol 17), and
54
+ * an idle session whose queue was left waiting (its Agent was unavailable when the last turn
55
+ * ended) starts a turn with all of them, this one last. Past `sessionQueuedMessages` it is
56
+ * `429 queue_full`. `waiting` is the queue's length, read under the session's lock.
57
+ */
58
+ export declare function acceptMessage(ctx: TenantContext, t: Tx, s: Session, id: string, message: TurnMessage, waiting?: number): Promise<MessageAnswer>;
59
+ /** Who checks a queued message's turn start: core, since the sender's grants were checked when it was queued. */
60
+ export declare const QUEUE_SCOPE: AuthScope;
61
+ /**
62
+ * Starts the next turn of session `s` (locked) from its queue, when it takes messages and messages
63
+ * wait (protocol 17, D76): in the transaction that ended the last turn, or a cancel's. Our engine
64
+ * takes every queued message that carries the same turn manifest as the first, a workflow one.
65
+ * Writes `queue.taken`. Returns nothing when nothing started: an empty queue, or an Agent that
66
+ * cannot take a turn now, whose messages wait for the next message or turn end.
67
+ */
68
+ export declare function startQueuedTurn(ctx: TenantContext, t: Tx, s: Session, id: string): Promise<{
69
+ turnId: string;
70
+ taken: string[];
71
+ cursor: string;
72
+ } | undefined>;
12
73
  /**
13
74
  * Who sent a command (R4.9), from its credential and never its body: a client over the Runtime
14
75
  * API (an application, or a person through it or a token), or core itself.
@@ -14,6 +14,12 @@
14
14
  * same transaction; the engine host sees it before its next effect and before settlement on
15
15
  * any Worker. It then aborts an advance running on this process (`ctx.abortLocal`); every
16
16
  * other process aborts its own on the signal (`control.ts`).
17
+ *
18
+ * A message to a session whose turn runs or waits (protocol 17, D76) is queued
19
+ * (`session_queue`, `message.queued`) unless it asks `delivery: "reject"`, which is
20
+ * `409 session_busy`. A turn that ends takes every queued message in the same transaction
21
+ * (`startQueuedTurn`): client messages and queued ones start a turn through one door,
22
+ * `beginMessageTurn`, which writes each one's `command.message`.
17
23
  */
18
24
  import { randomUUID } from "node:crypto";
19
25
  import { createDurableCheckpoint, createFlowCheckpoint, resumeFlowCheckpoint, } from "@nylorun/harness/run";
@@ -21,14 +27,14 @@ import { commandKey, cancelQueuedEffects, flowInteractionOf, foreignInteractionC
21
27
  import { resolveMessageManifest } from "../core/turn-manifest.js";
22
28
  import { canonical } from "../store/canonical.js";
23
29
  import { lockedSession, } from "./context.js";
24
- import { fail } from "./http.js";
30
+ import { fail, HttpError } from "./http.js";
31
+ import { readRuntimeLimits } from "./runtime-limits.js";
25
32
  import { settleToolRow } from "../harness-api/tool-journal.js";
26
33
  import { accessOf } from "./auth.js";
27
- import { checkEnvironmentTurn } from "./environments.js";
34
+ import { checkAgentTurn } from "./agents.js";
28
35
  import { turnManifestOf, variantStore } from "./session.js";
29
36
  import { slimModelEffects } from "./slim.js";
30
37
  import { resolveMessageParts } from "../artifacts/parts.js";
31
- import { currentModelInputs } from "../model/catalog.js";
32
38
  import { endTurn, resumeTurn, startTurn } from "./turn-timing.js";
33
39
  /** A command's identity for idempotency: everything but the request id. */
34
40
  const semantic = (value) => {
@@ -43,11 +49,7 @@ export async function command(ctx, id, input, scope,
43
49
  options = {}) {
44
50
  const { store } = ctx;
45
51
  const cascadeCancelIds = [];
46
- // What the Tenant's model reads, for a message that names a file (protocol 12): read before
47
- // the session's transaction. Undefined without a model in the vault: nothing is refused.
48
- const inputs = input.type === "message" && "parts" in input && input.parts.some((part) => part.type === "file")
49
- ? await currentModelInputs(ctx)
50
- : undefined;
52
+ let cancelledTurn = null;
51
53
  const response = await store.tx(async (t) => {
52
54
  const s = await lockedSession(t, id, accessOf(scope));
53
55
  const command = input;
@@ -70,8 +72,11 @@ options = {}) {
70
72
  let held;
71
73
  // The turn a `cancel` ended, if one was active.
72
74
  let cancelledTurnId = null;
75
+ /** A message's answer: accepted with its turn, or queued (protocol 17). */
76
+ let answer;
73
77
  if (command.type === "cancel") {
74
78
  cancelledTurnId = s.activeTurnId;
79
+ cancelledTurn = cancelledTurnId;
75
80
  const workflowCancel = isWorkflowManifest(s.manifest);
76
81
  // Linked sessions this one started: a workflow's agents, or flow agents an agent
77
82
  // uses as tools.
@@ -134,85 +139,25 @@ options = {}) {
134
139
  }
135
140
  }
136
141
  else {
137
- // A session opened before protocol 11 (migration 0020) or 13 (migration 0024) keeps its
138
- // history but takes no turn.
142
+ // A session opened before protocol 11 (migration 0020), 13 (migration 0024), 15
143
+ // (migrations 0029, 0030) or 16 (migration 0032) keeps its history but takes no turn.
139
144
  if (s.closedReason !== undefined)
140
- fail(409, s.closedReason === "protocol_11"
141
- ? "This session was opened before protocol 11: open a new session that names an Environment."
142
- : "This session was opened before protocol 13, on a manifest the Runtime no longer runs: register the agent again (manifest 7) and open a new session.", { code: "session_closed" });
145
+ fail(409, s.closedReason === "protocol_16"
146
+ ? "This session was opened before protocol 16, from an agent definition: save the agent as an agent manifest (nylorun migrate v8, then nylorun apply), make an Agent from it and open a new session on it."
147
+ : s.closedReason === "protocol_11"
148
+ ? "This session was opened before protocol 11: open a new session on an Agent."
149
+ : s.closedReason === "protocol_15"
150
+ ? "This session was opened before protocol 15, and its pinned manifest holds a runtime block: our harness now writes the model's instructions and defines its tools itself. Open a new session."
151
+ : "This session was opened before protocol 13, on a manifest the Runtime no longer runs: register the agent again (manifest 7) and open a new session.", { code: "session_closed" });
143
152
  if (command.type === "message") {
144
- if (!["idle", "completed", "failed", "cancelled"].includes(s.status))
145
- fail(409, "Session has active or unresolved work");
153
+ const delivery = command.delivery ?? options.delivery ?? "reject";
154
+ const waiting = await t.countQueuedMessages(id);
155
+ if (!takesMessages(s) && delivery === "reject")
156
+ fail(409, "Session has active or unresolved work", { code: "session_busy" });
146
157
  // Every turn start on a sandbox: it exists, the token's grants reach it, and no other
147
- // session's turn holds it.
148
- await checkEnvironmentTurn(t, s, scope, ctx);
149
- s.turnStartState = s.state;
150
- // A new turn folds from the latest snapshot: a cancel or failure only reverts this turn.
151
- if (s.history?.snapshot !== undefined)
152
- s.history = { ...s.history, from: s.history.snapshot };
153
- s.activeTurnId = randomUUID();
154
- startTurn(s);
155
- if (options.trace)
156
- s.turnTrace = options.trace;
157
- else
158
- delete s.turnTrace;
159
- // File parts name artifacts the caller may read; each is pinned to a version now. An
160
- // agent's model must read each one (a workflow passes them on as data).
161
- const parts = "parts" in command
162
- ? await resolveMessageParts(t, command.parts, id, accessOf(scope), isWorkflowManifest(s.manifest) ? undefined : inputs)
163
- : undefined;
164
- if (parts && parts.refused.length > 0)
165
- fail(422, `The model does not read ${parts.refused
166
- .map((part) => `${part.name} (${part.reason})`)
167
- .join(", ")}.`, { code: "attachment_unsupported", details: { parts: parts.refused } });
168
- // The files the session's messages named: `read_artifact` reads them again (protocol 12).
169
- if (parts) {
170
- const named = parts.pinned.flatMap((part) => (part.type === "file" ? [part.artifactId] : []));
171
- if (named.length > 0)
172
- s.fileArtifacts = [...new Set([...(s.fileArtifacts ?? []), ...named])];
173
- }
174
- const data = "data" in command ? command.data : undefined;
175
- if (isWorkflowManifest(s.manifest)) {
176
- const input = "content" in command
177
- ? command.content
178
- : parts
179
- ? { parts: parts.pinned }
180
- : data;
181
- s.checkpoint = createFlowCheckpoint({
182
- manifest: s.manifest,
183
- sessionId: id,
184
- turnId: s.activeTurnId,
185
- input,
186
- });
187
- }
188
- else {
189
- // Optional message.manifest: validate as variant, pin by hash, apply this turn only.
190
- const resolved = resolveMessageManifest({
191
- pinned: s.manifest,
192
- pinnedHash: s.manifestHash,
193
- messageManifest: command.manifest,
194
- store: variantStore(s),
195
- });
196
- if (!resolved.ok) {
197
- fail(400, resolved.message);
198
- }
199
- else {
200
- s.checkpoint = createDurableCheckpoint({
201
- manifest: resolved.manifest,
202
- sessionId: id,
203
- turnId: s.activeTurnId,
204
- input: "content" in command
205
- ? command.content
206
- : parts
207
- ? { content: parts.engine }
208
- : typeof data === "string"
209
- ? data
210
- : JSON.stringify(data),
211
- state: s.state,
212
- info: s.info,
213
- });
214
- }
215
- }
158
+ // session's turn holds it. A queued message is checked when it is queued, too.
159
+ await checkAgentTurn(t, s, scope, ctx);
160
+ answer = await acceptMessage(ctx, t, s, id, await turnMessage(t, s, id, command, scope, options.trace), waiting);
216
161
  }
217
162
  else if (command.type === "approve" &&
218
163
  (held = await heldCall(t, s, command.interactionId)) !== undefined) {
@@ -285,13 +230,15 @@ options = {}) {
285
230
  segment: (s.checkpoint?.segment ?? 0) + 1,
286
231
  });
287
232
  }
233
+ }
234
+ if (command.type !== "cancel" && command.type !== "message") {
288
235
  // A paused turn goes on: its wait for a person is not its running time (R7.1).
289
236
  resumeTurn(s);
290
237
  s.status = "runnable";
291
238
  s.waits = undefined;
292
239
  // A flow answers each interaction in the same segment, as a held tool call's approval is
293
240
  // answered (R5 step 2): the interaction names the cause.
294
- const cause = command.type !== "message" && (isWorkflowManifest(s.manifest) || held !== undefined)
241
+ const cause = isWorkflowManifest(s.manifest) || held !== undefined
295
242
  ? command.interactionId
296
243
  : s.checkpoint.segment;
297
244
  const commandWake = {
@@ -299,8 +246,11 @@ options = {}) {
299
246
  dedupeKey: `${command.type}:${s.activeTurnId}:${cause}`,
300
247
  };
301
248
  await t.wake(id, commandWake);
302
- event = await t.event(id, s.activeTurnId, `command.${command.type}`, command.type === "message" && options.trace ? { ...command, trace: options.trace } : command, commandWriter(scope));
249
+ event = await t.event(id, s.activeTurnId, `command.${command.type}`, command, commandWriter(scope));
303
250
  }
251
+ // A cancel ends the turn, not the queue: the messages waiting start the next one.
252
+ if (command.type === "cancel")
253
+ await startQueuedTurn(ctx, t, s, id);
304
254
  await t.put("sessions", id, s);
305
255
  // Direct cancel of a linked agent fails that node with agent.cancelled (WF-R54). The agent
306
256
  // session is locked above, before wakeLinkedWorkflow locks its workflow.
@@ -313,16 +263,18 @@ options = {}) {
313
263
  error: "Agent turn was cancelled",
314
264
  });
315
265
  const response = {
316
- status: "accepted",
317
- turnId: s.activeTurnId,
318
- cursor: event.cursor,
266
+ ...(answer ?? {
267
+ status: "accepted",
268
+ turnId: command.type === "cancel" ? null : s.activeTurnId,
269
+ cursor: event.cursor,
270
+ }),
319
271
  requestId: command.requestId,
320
272
  };
321
273
  await t.put("commands", key, { command, response });
322
274
  return response;
323
275
  });
324
276
  if (input.type === "cancel")
325
- ctx.abortLocal(id);
277
+ ctx.abortLocal(id, cancelledTurn ?? undefined);
326
278
  // Cascade cancel linked agents deepest-first under existing fencing (WF-R53).
327
279
  for (const agentId of cascadeCancelIds) {
328
280
  try {
@@ -339,6 +291,204 @@ options = {}) {
339
291
  }
340
292
  return response;
341
293
  }
294
+ /** Statuses in which a session takes a message as a new turn; in the others a turn runs or waits. */
295
+ const TAKES_MESSAGES = ["idle", "completed", "failed", "cancelled"];
296
+ export const takesMessages = (s) => TAKES_MESSAGES.includes(s.status);
297
+ /**
298
+ * Takes a resolved message on session `s` (locked) in transaction `t`: it starts a turn when the
299
+ * session takes messages and none wait, else it waits in the queue behind them (protocol 17), and
300
+ * an idle session whose queue was left waiting (its Agent was unavailable when the last turn
301
+ * ended) starts a turn with all of them, this one last. Past `sessionQueuedMessages` it is
302
+ * `429 queue_full`. `waiting` is the queue's length, read under the session's lock.
303
+ */
304
+ export async function acceptMessage(ctx, t, s, id, message, waiting) {
305
+ const queued = waiting ?? (await t.countQueuedMessages(id));
306
+ const takes = takesMessages(s);
307
+ if (takes && queued === 0) {
308
+ const event = await beginMessageTurn(t, s, id, [message]);
309
+ return { status: "accepted", turnId: s.activeTurnId, cursor: event.cursor };
310
+ }
311
+ // The turn runs or waits, or earlier messages still wait: this one waits behind them.
312
+ const { sessionQueuedMessages } = await readRuntimeLimits(t);
313
+ if (queued >= sessionQueuedMessages)
314
+ fail(429, `Session ${id} already holds ${queued} queued messages, the Runtime limit sessionQueuedMessages (PUT /v1/tenant/limits); they start a turn when the running one ends.`, { code: "queue_full" });
315
+ const queuedMessageId = randomUUID();
316
+ await t.enqueueMessage({
317
+ id: queuedMessageId,
318
+ sessionId: id,
319
+ command: message.command,
320
+ input: message.input,
321
+ fileArtifacts: message.fileArtifacts,
322
+ writer: message.writer,
323
+ trace: message.trace ?? null,
324
+ cause: message.cause ?? null,
325
+ createdAt: new Date().toISOString(),
326
+ });
327
+ const position = queued + 1;
328
+ const event = await t.event(id, s.activeTurnId, "message.queued", { queuedMessageId, position, requestId: message.command.requestId }, message.writer);
329
+ const started = takes ? await startQueuedTurn(ctx, t, s, id) : undefined;
330
+ if (started?.taken.includes(queuedMessageId))
331
+ return { status: "accepted", turnId: started.turnId, cursor: started.cursor };
332
+ return { status: "queued", queuedMessageId, position, cursor: event.cursor };
333
+ }
334
+ /**
335
+ * Resolves a message for a turn, now or when it leaves the queue: file parts name artifacts the
336
+ * caller may read, each pinned to a version (any file goes since protocol 15: the harness decides
337
+ * how the model sees it, and a workflow passes it on as data), and a turn manifest must be a
338
+ * variant of the pinned agent.
339
+ */
340
+ async function turnMessage(t, s, id, command, scope, trace) {
341
+ const parts = "parts" in command ? await resolveMessageParts(t, command.parts, id, accessOf(scope)) : undefined;
342
+ const fileArtifacts = (parts?.pinned ?? []).flatMap((part) => (part.type === "file" ? [part.artifactId] : []));
343
+ const data = "data" in command ? command.data : undefined;
344
+ let input;
345
+ if (isWorkflowManifest(s.manifest))
346
+ input =
347
+ "content" in command
348
+ ? command.content
349
+ : parts
350
+ ? { parts: parts.pinned }
351
+ : data;
352
+ else {
353
+ const resolved = resolveMessageManifest({
354
+ pinned: s.manifest,
355
+ pinnedHash: s.manifestHash,
356
+ messageManifest: command.manifest,
357
+ store: variantStore(s),
358
+ });
359
+ if (!resolved.ok)
360
+ fail(400, resolved.message);
361
+ input =
362
+ "content" in command
363
+ ? command.content
364
+ : parts
365
+ ? { content: parts.engine }
366
+ : typeof data === "string"
367
+ ? data
368
+ : JSON.stringify(data);
369
+ }
370
+ return { command, input, fileArtifacts, writer: commandWriter(scope), ...(trace ? { trace } : {}) };
371
+ }
372
+ /**
373
+ * Starts a turn on session `s` (locked, taking messages) with `messages`, in order: the one door
374
+ * for a client's message and queued ones (protocol 17). Our engine reads more than one as
375
+ * `{kind: "messages", messages}`, each its own user message; a workflow takes one. The checkpoint
376
+ * is made before the session changes, so a manifest that no longer resolves changes nothing.
377
+ * Writes each message's `command.message` and returns the last.
378
+ */
379
+ async function beginMessageTurn(t, s, id, messages) {
380
+ const turnId = randomUUID();
381
+ const first = messages[0];
382
+ let checkpoint;
383
+ if (isWorkflowManifest(s.manifest))
384
+ checkpoint = createFlowCheckpoint({
385
+ manifest: s.manifest,
386
+ sessionId: id,
387
+ turnId,
388
+ input: first.input,
389
+ });
390
+ else {
391
+ const resolved = resolveMessageManifest({
392
+ pinned: s.manifest,
393
+ pinnedHash: s.manifestHash,
394
+ messageManifest: first.command.manifest,
395
+ store: variantStore(s),
396
+ });
397
+ if (!resolved.ok)
398
+ return fail(400, resolved.message);
399
+ checkpoint = createDurableCheckpoint({
400
+ manifest: resolved.manifest,
401
+ sessionId: id,
402
+ turnId,
403
+ input: (messages.length === 1
404
+ ? first.input
405
+ : { kind: "messages", messages: messages.map((message) => message.input) }),
406
+ state: s.state,
407
+ info: s.info,
408
+ });
409
+ }
410
+ s.turnStartState = s.state;
411
+ // A new turn folds from the latest snapshot: a cancel or failure only reverts this turn.
412
+ if (s.history?.snapshot !== undefined)
413
+ s.history = { ...s.history, from: s.history.snapshot };
414
+ s.activeTurnId = turnId;
415
+ startTurn(s);
416
+ if (first.trace)
417
+ s.turnTrace = first.trace;
418
+ else
419
+ delete s.turnTrace;
420
+ // A trigger's run or a booked wake among the messages started it (track T4).
421
+ const cause = messages.find((message) => message.cause)?.cause;
422
+ if (cause)
423
+ s.turnCause = { kind: cause.kind, triggerId: cause.triggerId, runId: cause.runId };
424
+ else
425
+ delete s.turnCause;
426
+ // The files the session's messages named: `read_artifact` reads them again (protocol 12).
427
+ const named = messages.flatMap((message) => message.fileArtifacts);
428
+ if (named.length > 0)
429
+ s.fileArtifacts = [...new Set([...(s.fileArtifacts ?? []), ...named])];
430
+ s.checkpoint = checkpoint;
431
+ s.status = "runnable";
432
+ s.waits = undefined;
433
+ await t.wake(id, { reason: "message", dedupeKey: `message:${turnId}:${checkpoint.segment}` });
434
+ let event;
435
+ for (const message of messages)
436
+ event = await t.event(id, turnId, "command.message", {
437
+ ...message.command,
438
+ ...(message.queuedMessageId === undefined ? {} : { queuedMessageId: message.queuedMessageId }),
439
+ ...(message.trace ? { trace: message.trace } : {}),
440
+ ...(message.cause ? { cause: message.cause } : {}),
441
+ }, message.writer);
442
+ return event;
443
+ }
444
+ /** Who checks a queued message's turn start: core, since the sender's grants were checked when it was queued. */
445
+ export const QUEUE_SCOPE = { kind: "application", principalId: "queue" };
446
+ /**
447
+ * Starts the next turn of session `s` (locked) from its queue, when it takes messages and messages
448
+ * wait (protocol 17, D76): in the transaction that ended the last turn, or a cancel's. Our engine
449
+ * takes every queued message that carries the same turn manifest as the first, a workflow one.
450
+ * Writes `queue.taken`. Returns nothing when nothing started: an empty queue, or an Agent that
451
+ * cannot take a turn now, whose messages wait for the next message or turn end.
452
+ */
453
+ export async function startQueuedTurn(ctx, t, s, id) {
454
+ if (s.closedReason !== undefined || !takesMessages(s) || s.activeTurnId !== null)
455
+ return undefined;
456
+ const queued = await t.queuedMessages(id);
457
+ if (queued.length === 0)
458
+ return undefined;
459
+ const manifestOf = (row) => canonical(row.command.manifest ?? null);
460
+ const batch = isWorkflowManifest(s.manifest) ? [queued[0]] : [];
461
+ if (batch.length === 0)
462
+ for (const row of queued) {
463
+ if (manifestOf(row) !== manifestOf(queued[0]))
464
+ break;
465
+ batch.push(row);
466
+ }
467
+ try {
468
+ await checkAgentTurn(t, s, QUEUE_SCOPE, ctx);
469
+ await beginMessageTurn(t, s, id, batch.map((row) => ({
470
+ command: row.command,
471
+ input: row.input,
472
+ fileArtifacts: row.fileArtifacts,
473
+ writer: row.writer,
474
+ queuedMessageId: row.id,
475
+ ...(row.trace ? { trace: row.trace } : {}),
476
+ ...(row.cause ? { cause: row.cause } : {}),
477
+ })));
478
+ }
479
+ catch (error) {
480
+ if (error instanceof HttpError)
481
+ return undefined;
482
+ throw error;
483
+ }
484
+ const taken = batch.map((row) => row.id);
485
+ await t.deleteQueuedMessages(taken);
486
+ const event = await t.event(id, s.activeTurnId, "queue.taken", {
487
+ turnId: s.activeTurnId,
488
+ queuedMessageIds: taken,
489
+ });
490
+ return { turnId: s.activeTurnId, taken, cursor: event.cursor };
491
+ }
342
492
  /** Core's own commands (R4.9): the flow host's, and a drain's cancels. */
343
493
  const CORE_PRINCIPALS = { "flow-host": "flow-host", drain: "core" };
344
494
  /**