@nylorun/runtime 0.22.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 (273) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +95 -30
  3. package/dist/adapters/execution/restate.d.ts +15 -3
  4. package/dist/adapters/execution/restate.js +78 -84
  5. package/dist/adapters/replication/pgoutput.d.ts +2 -0
  6. package/dist/adapters/replication/pgoutput.js +6 -3
  7. package/dist/adapters/sandbox/local.d.ts +9 -1
  8. package/dist/adapters/sandbox/local.js +16 -4
  9. package/dist/adapters/sandbox/virtual.js +4 -1
  10. package/dist/adapters/streams/s2.js +1 -13
  11. package/dist/api/a2a/endpoint.js +5 -1
  12. package/dist/api/a2a/routes.d.ts +2 -0
  13. package/dist/api/a2a/routes.js +2 -2
  14. package/dist/api/a2a/tasks.js +2 -1
  15. package/dist/api/ag-ui/endpoint.js +4 -2
  16. package/dist/api/ag-ui/history.js +4 -1
  17. package/dist/api/ag-ui/routes.d.ts +4 -1
  18. package/dist/api/ag-ui/routes.js +11 -10
  19. package/dist/api/ag-ui/translate.js +3 -2
  20. package/dist/api/components.d.ts +4953 -1204
  21. package/dist/api/components.js +25 -16
  22. package/dist/api/gate/keys.js +7 -1
  23. package/dist/api/gate/routes.d.ts +6 -0
  24. package/dist/api/gate/routes.js +39 -3
  25. package/dist/api/http/app.js +4 -4
  26. package/dist/api/http/define.d.ts +6 -0
  27. package/dist/api/http/define.js +13 -1
  28. package/dist/api/http/respond.js +1 -1
  29. package/dist/api/http/routes/artifacts.js +35 -8
  30. package/dist/api/http/routes/coverage.d.ts +9 -0
  31. package/dist/api/http/routes/coverage.js +34 -0
  32. package/dist/api/http/routes/environments.d.ts +11 -0
  33. package/dist/api/http/routes/environments.js +283 -0
  34. package/dist/api/http/routes/mcp.d.ts +1 -1
  35. package/dist/api/http/routes/mcp.js +2 -2
  36. package/dist/api/http/routes/me.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +45 -9
  38. package/dist/api/http/routes/reads.js +54 -5
  39. package/dist/api/http/routes/sessions.js +11 -6
  40. package/dist/api/http/routes/tenant.js +32 -10
  41. package/dist/api/openapi.js +31 -26
  42. package/dist/artifacts/disk.d.ts +59 -0
  43. package/dist/artifacts/disk.js +563 -0
  44. package/dist/artifacts/export.d.ts +1 -1
  45. package/dist/artifacts/export.js +2 -3
  46. package/dist/artifacts/folders.d.ts +6 -3
  47. package/dist/artifacts/folders.js +17 -12
  48. package/dist/artifacts/service.d.ts +2 -1
  49. package/dist/artifacts/service.js +6 -1
  50. package/dist/artifacts/workspace.d.ts +1 -1
  51. package/dist/artifacts/workspace.js +1 -1
  52. package/dist/core/flow-host.d.ts +3 -22
  53. package/dist/core/flow-host.js +11 -37
  54. package/dist/core/provider.d.ts +3 -2
  55. package/dist/core/provider.js +5 -37
  56. package/dist/core/sandbox-routes.d.ts +2 -21
  57. package/dist/core/sandbox-routes.js +10 -43
  58. package/dist/execution/types.d.ts +11 -9
  59. package/dist/execution/types.js +5 -7
  60. package/dist/gates/contract.d.ts +3 -24
  61. package/dist/gates/contract.js +1 -36
  62. package/dist/gates/egress-credentials.d.ts +9 -0
  63. package/dist/gates/egress-credentials.js +11 -4
  64. package/dist/gates/egress.d.ts +28 -1
  65. package/dist/gates/egress.js +115 -10
  66. package/dist/gates/fixture-model.d.ts +28 -0
  67. package/dist/gates/fixture-model.js +92 -0
  68. package/dist/gates/handler.js +4 -4
  69. package/dist/gates/http-tool.d.ts +6 -2
  70. package/dist/gates/http-tool.js +3 -5
  71. package/dist/gates/in-process.d.ts +5 -0
  72. package/dist/gates/in-process.js +12 -1
  73. package/dist/gates/journal.d.ts +30 -0
  74. package/dist/gates/journal.js +52 -0
  75. package/dist/gates/mcp-handler.d.ts +4 -0
  76. package/dist/gates/mcp-handler.js +5 -1
  77. package/dist/gates/meter.d.ts +10 -2
  78. package/dist/gates/meter.js +20 -5
  79. package/dist/gates/policy.d.ts +49 -0
  80. package/dist/gates/policy.js +134 -0
  81. package/dist/gates/tenant-vaults.d.ts +7 -7
  82. package/dist/gates/tenant-vaults.js +5 -5
  83. package/dist/gates/tool-calls.d.ts +10 -5
  84. package/dist/gates/tool-calls.js +28 -10
  85. package/dist/gates/tool-client.d.ts +8 -0
  86. package/dist/gates/tool-client.js +27 -11
  87. package/dist/gates/tool-contract.d.ts +10 -68
  88. package/dist/gates/tool-contract.js +1 -85
  89. package/dist/gates/tool-gate.d.ts +1 -1
  90. package/dist/harness/calls.d.ts +12 -5
  91. package/dist/harness/calls.js +21 -12
  92. package/dist/harness/executors.js +12 -9
  93. package/dist/harness/main.js +18 -5
  94. package/dist/harness/pod.d.ts +34 -0
  95. package/dist/harness/pod.js +41 -0
  96. package/dist/harness/service.d.ts +0 -1
  97. package/dist/harness/service.js +13 -5
  98. package/dist/harness/setup.d.ts +27 -0
  99. package/dist/harness/setup.js +150 -0
  100. package/dist/harness/ws-client.js +3 -2
  101. package/dist/harness/ws-port.d.ts +1 -5
  102. package/dist/harness/ws-port.js +1 -5
  103. package/dist/harness-api/in-process.js +3 -3
  104. package/dist/harness-api/record.d.ts +15 -1
  105. package/dist/harness-api/record.js +174 -44
  106. package/dist/harness-api/redact.d.ts +3 -0
  107. package/dist/harness-api/redact.js +45 -0
  108. package/dist/harness-api/server.d.ts +8 -0
  109. package/dist/harness-api/server.js +93 -13
  110. package/dist/harness-api/start.d.ts +10 -3
  111. package/dist/harness-api/start.js +40 -10
  112. package/dist/harness-api/tool-journal.d.ts +96 -0
  113. package/dist/harness-api/tool-journal.js +102 -0
  114. package/dist/harness-api/tool-status.d.ts +13 -0
  115. package/dist/harness-api/tool-status.js +26 -0
  116. package/dist/harness-api/workspace.d.ts +6 -3
  117. package/dist/harness-api/workspace.js +27 -11
  118. package/dist/harness-api/ws-server.d.ts +11 -3
  119. package/dist/harness-api/ws-server.js +94 -15
  120. package/dist/host/execution.d.ts +8 -6
  121. package/dist/host/gates.d.ts +2 -1
  122. package/dist/host/gates.js +10 -2
  123. package/dist/host/main.js +64 -21
  124. package/dist/host/operate.js +67 -5
  125. package/dist/host/stack-config.d.ts +70 -0
  126. package/dist/host/stack-config.js +67 -1
  127. package/dist/infra/execution.d.ts +18 -1
  128. package/dist/infra/execution.js +28 -4
  129. package/dist/infra/index.d.ts +1 -1
  130. package/dist/keys/client.js +4 -1
  131. package/dist/keys/keys.d.ts +5 -5
  132. package/dist/keys/keys.js +5 -7
  133. package/dist/management-openapi.json +999 -2211
  134. package/dist/mcp/connect.d.ts +25 -20
  135. package/dist/mcp/connect.js +29 -30
  136. package/dist/mcp/pool.d.ts +2 -2
  137. package/dist/mcp/pool.js +1 -1
  138. package/dist/mcp/preview.d.ts +3 -3
  139. package/dist/mcp/preview.js +10 -20
  140. package/dist/mcp/snapshot.d.ts +5 -11
  141. package/dist/mcp/snapshot.js +6 -7
  142. package/dist/model/pi-model.js +8 -0
  143. package/dist/openapi.json +6955 -2526
  144. package/dist/reads/types.d.ts +16 -3
  145. package/dist/record/append.d.ts +3 -1
  146. package/dist/record/append.js +1 -0
  147. package/dist/record/envelope.d.ts +5 -1
  148. package/dist/record/envelope.js +7 -3
  149. package/dist/record/environment.d.ts +25 -0
  150. package/dist/record/environment.js +44 -0
  151. package/dist/record/index.d.ts +1 -1
  152. package/dist/record/index.js +1 -1
  153. package/dist/retry.d.ts +21 -0
  154. package/dist/retry.js +34 -0
  155. package/dist/sandbox/disk-fs.d.ts +95 -0
  156. package/dist/sandbox/disk-fs.js +206 -0
  157. package/dist/sandbox/join.d.ts +17 -18
  158. package/dist/sandbox/join.js +46 -20
  159. package/dist/sandbox/manager.d.ts +16 -8
  160. package/dist/sandbox/manager.js +33 -17
  161. package/dist/sandbox/placement.d.ts +13 -12
  162. package/dist/sandbox/placement.js +29 -12
  163. package/dist/sandbox/pods/client.d.ts +6 -1
  164. package/dist/sandbox/pods/lifecycle.d.ts +28 -27
  165. package/dist/sandbox/pods/lifecycle.js +14 -26
  166. package/dist/sandbox/pods/reconcile.js +21 -15
  167. package/dist/sandbox/pods/spec.d.ts +15 -13
  168. package/dist/sandbox/pods/spec.js +16 -20
  169. package/dist/sandbox/records.d.ts +2 -2
  170. package/dist/sandbox/records.js +3 -3
  171. package/dist/sandbox/resolve.d.ts +35 -27
  172. package/dist/sandbox/resolve.js +66 -41
  173. package/dist/sandbox/session-sandbox.js +1 -1
  174. package/dist/sandbox/setup.d.ts +61 -0
  175. package/dist/sandbox/setup.js +123 -0
  176. package/dist/sandbox/share.d.ts +11 -21
  177. package/dist/sandbox/share.js +12 -41
  178. package/dist/sandbox/tenant-config.d.ts +12 -14
  179. package/dist/sandbox/tenant-config.js +12 -17
  180. package/dist/sandbox/types.d.ts +6 -0
  181. package/dist/store/postgres/control.js +8 -1
  182. package/dist/store/postgres/drizzle/0018_wake_outbox_retries.sql +3 -0
  183. package/dist/store/postgres/drizzle/0019_sandbox_signal_outbox.sql +18 -0
  184. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  185. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  186. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  187. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  188. package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
  189. package/dist/store/postgres/reads.js +117 -17
  190. package/dist/store/postgres/record-writer.d.ts +4 -4
  191. package/dist/store/postgres/record-writer.js +9 -9
  192. package/dist/store/postgres/record.d.ts +7 -0
  193. package/dist/store/postgres/record.js +23 -2
  194. package/dist/store/postgres/schema.d.ts +1213 -779
  195. package/dist/store/postgres/schema.js +136 -76
  196. package/dist/store/postgres/store.js +283 -117
  197. package/dist/store/types.d.ts +206 -88
  198. package/dist/streams/relay/core.js +1 -13
  199. package/dist/tenant/advance.d.ts +7 -6
  200. package/dist/tenant/advance.js +121 -49
  201. package/dist/tenant/artifact-tool.js +3 -4
  202. package/dist/tenant/commands.d.ts +15 -2
  203. package/dist/tenant/commands.js +94 -14
  204. package/dist/tenant/context.d.ts +67 -33
  205. package/dist/tenant/context.js +4 -15
  206. package/dist/tenant/control.d.ts +8 -1
  207. package/dist/tenant/control.js +2 -0
  208. package/dist/tenant/effects.d.ts +1 -1
  209. package/dist/tenant/effects.js +37 -20
  210. package/dist/tenant/environments.d.ts +167 -0
  211. package/dist/tenant/environments.js +823 -0
  212. package/dist/tenant/harness-tokens.d.ts +21 -0
  213. package/dist/tenant/harness-tokens.js +61 -0
  214. package/dist/tenant/host-token.d.ts +1 -1
  215. package/dist/tenant/host-token.js +2 -4
  216. package/dist/tenant/identity-file.d.ts +4 -4
  217. package/dist/tenant/identity-file.js +17 -8
  218. package/dist/tenant/issuers.js +3 -3
  219. package/dist/tenant/principals.js +1 -1
  220. package/dist/tenant/reset.js +3 -3
  221. package/dist/tenant/runtime.d.ts +6 -1
  222. package/dist/tenant/runtime.js +30 -4
  223. package/dist/tenant/scheduler.d.ts +7 -0
  224. package/dist/tenant/scheduler.js +38 -7
  225. package/dist/tenant/session-streams.d.ts +0 -1
  226. package/dist/tenant/session-streams.js +1 -13
  227. package/dist/tenant/session.d.ts +3 -3
  228. package/dist/tenant/session.js +0 -14
  229. package/dist/tenant/sessions.d.ts +13 -13
  230. package/dist/tenant/sessions.js +61 -138
  231. package/dist/tenant/signing-keys.d.ts +5 -1
  232. package/dist/tenant/status.js +3 -3
  233. package/dist/tenant/streams.js +14 -29
  234. package/dist/tenant/sweep.d.ts +39 -12
  235. package/dist/tenant/sweep.js +168 -63
  236. package/dist/tenant/transcript.d.ts +210 -20
  237. package/dist/tenant/transcript.js +62 -11
  238. package/dist/tenant/turn-timing.d.ts +43 -0
  239. package/dist/tenant/turn-timing.js +39 -0
  240. package/dist/tenant/types.d.ts +2 -0
  241. package/dist/traces/ids.d.ts +42 -0
  242. package/dist/traces/ids.js +85 -0
  243. package/dist/traces/key.d.ts +6 -0
  244. package/dist/traces/key.js +37 -0
  245. package/dist/traces/map.d.ts +113 -0
  246. package/dist/traces/map.js +317 -0
  247. package/dist/traces/options.d.ts +7 -0
  248. package/dist/traces/options.js +1 -0
  249. package/dist/traces/otlp.d.ts +42 -0
  250. package/dist/traces/otlp.js +84 -0
  251. package/dist/traces/projector.d.ts +58 -0
  252. package/dist/traces/projector.js +200 -0
  253. package/dist/traces/read.d.ts +23 -0
  254. package/dist/traces/read.js +16 -0
  255. package/dist/vault/coverage.d.ts +26 -0
  256. package/dist/vault/coverage.js +64 -0
  257. package/dist/vault/error.d.ts +11 -1
  258. package/dist/vault/error.js +5 -1
  259. package/dist/vault/service.d.ts +47 -48
  260. package/dist/vault/service.js +321 -382
  261. package/dist/vault/sources.d.ts +5 -16
  262. package/dist/vault/sources.js +6 -8
  263. package/dist/version.d.ts +1 -1
  264. package/dist/version.js +2 -2
  265. package/package.json +14 -13
  266. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  267. package/dist/api/http/routes/sandboxes.js +0 -153
  268. package/dist/api/http/routes/vaults.d.ts +0 -10
  269. package/dist/api/http/routes/vaults.js +0 -139
  270. package/dist/record/sandbox.d.ts +0 -25
  271. package/dist/record/sandbox.js +0 -43
  272. package/dist/tenant/sandboxes.d.ts +0 -122
  273. package/dist/tenant/sandboxes.js +0 -562
@@ -66,7 +66,8 @@ export declare function folderContent(ctx: TenantContext, id: string, version: n
66
66
  /**
67
67
  * Deletes the artifact and every version: its rows and, for a session's artifact, its
68
68
  * `artifact.deleted` event in one transaction, then its bytes. A folder's content-addressed
69
- * files go too, those no other version names, under the quota lock (`folders.ts`).
69
+ * files go too, those no other version names, under the quota lock (`folders.ts`). An
70
+ * Environment's disk artifact is refused: its own routes delete its versions.
70
71
  */
71
72
  export declare function deleteArtifact(ctx: TenantContext, id: string, access: ArtifactAccess): Promise<{
72
73
  artifactId: string;
@@ -52,6 +52,7 @@ export function artifactView(row, versions) {
52
52
  name: row.name,
53
53
  contentType: row.contentType,
54
54
  ...(row.sessionId === null ? {} : { sessionId: row.sessionId }),
55
+ ...(row.environmentId === null ? {} : { environmentId: row.environmentId }),
55
56
  latestVersion: row.latestVersion,
56
57
  ...(row.labelsJson === null ? {} : { labels: JSON.parse(row.labelsJson) }),
57
58
  createdAt: row.createdAt,
@@ -194,6 +195,7 @@ export async function uploadArtifact(ctx, input, body, access, signal) {
194
195
  : JSON.stringify(input.labels),
195
196
  createdAt,
196
197
  updatedAt: createdAt,
198
+ environmentId: null,
197
199
  };
198
200
  version = {
199
201
  artifactId,
@@ -278,11 +280,14 @@ export async function folderContent(ctx, id, version, access) {
278
280
  /**
279
281
  * Deletes the artifact and every version: its rows and, for a session's artifact, its
280
282
  * `artifact.deleted` event in one transaction, then its bytes. A folder's content-addressed
281
- * files go too, those no other version names, under the quota lock (`folders.ts`).
283
+ * files go too, those no other version names, under the quota lock (`folders.ts`). An
284
+ * Environment's disk artifact is refused: its own routes delete its versions.
282
285
  */
283
286
  export async function deleteArtifact(ctx, id, access) {
284
287
  const keys = await ctx.store.tx(async (t) => {
285
288
  const found = await readableArtifact(t, id, access);
289
+ if (found.environmentId !== null)
290
+ fail(409, `Artifact ${id} holds Environment ${found.environmentId}'s disk versions: delete them with DELETE /v1/environments/{id}/versions/{version}, or delete the Environment.`);
286
291
  if (found.sessionId !== null)
287
292
  await lockedSession(t, found.sessionId, access);
288
293
  if (found.kind === "folder")
@@ -22,7 +22,7 @@ export interface WorkspaceSession {
22
22
  /** The session that owns the workspace (one hop through `sandboxOwnerId`). */
23
23
  readonly ownerId: string;
24
24
  /** The sandbox resource the workspace belongs to, when the owner is attached to one. */
25
- readonly sandboxId?: string;
25
+ readonly environmentId?: string;
26
26
  /** The session's pinned manifest: its sandbox capability carries the sandbox's spec. */
27
27
  readonly manifest: AgentManifest;
28
28
  }
@@ -24,7 +24,7 @@ export function sandboxWorkspaceReader(sandbox) {
24
24
  id: session.ownerId,
25
25
  activeTurnId: session.turnId,
26
26
  manifest: session.manifest,
27
- ...(session.sandboxId === undefined ? {} : { sandboxId: session.sandboxId }),
27
+ ...(session.environmentId === undefined ? {} : { environmentId: session.environmentId }),
28
28
  },
29
29
  };
30
30
  };
@@ -1,8 +1,8 @@
1
1
  import type { EffectOutcome } from "@nylorun/core/contracts";
2
2
  import { type JsonValue, type WorkflowManifest } from "@nylorun/core/define";
3
3
  import type { HostEffect } from "@nylorun/harness/run";
4
- import type { EffectDoc, Tx, WakeDelivery } from "../store/types.js";
5
- import { type FlowLimits } from "./limits.js";
4
+ import type { EffectDoc, Tx } from "../store/types.js";
5
+ import { type TurnTiming } from "../tenant/turn-timing.js";
6
6
  /** Deterministic agent session id: derive(workflowSessionId, path, …parts). */
7
7
  export declare function deriveSessionId(workflowSessionId: string, path: string, ...parts: string[]): string;
8
8
  /**
@@ -60,15 +60,13 @@ export type FlowHostSession = {
60
60
  status: string;
61
61
  activeTurnId: string | null;
62
62
  info?: any;
63
- vaultIds?: readonly string[];
64
- credentialSelections?: readonly unknown[];
65
63
  creation?: unknown;
66
64
  waits?: unknown;
67
65
  lastOutput?: JsonValue;
68
66
  /** The turn `status`, `lastOutput` and `error` describe, once it ended. */
69
67
  lastTurnId?: string;
70
68
  error?: string;
71
- };
69
+ } & TurnTiming;
72
70
  /** How a linked agent turn ended. */
73
71
  export type LinkedTurnEnd = {
74
72
  readonly turnId: string;
@@ -97,8 +95,6 @@ export type CascadeCancelPlan = {
97
95
  /** Linked agent sessions to cancel, deepest path first. */
98
96
  readonly agentSessionIds: string[];
99
97
  };
100
- /** Wakes a session after commit (`DurableExecution.wake` through the Tenant context). */
101
- type Schedule = WakeDelivery;
102
98
  /** Effect documents as the flow host writes them. */
103
99
  export type FlowEffect = EffectDoc & {
104
100
  agentSessionId?: string;
@@ -135,7 +131,6 @@ export declare function wakeLinkedWorkflow(input: {
135
131
  readonly failed?: boolean;
136
132
  readonly cancelled?: boolean;
137
133
  readonly error?: string;
138
- readonly schedule: Schedule;
139
134
  }): Promise<void>;
140
135
  /** Pending workflow `agent` effects with a linked session, for the Tenant sweep. */
141
136
  export declare function pendingAgentEffects(t: Tx): Promise<FlowEffect[]>;
@@ -152,7 +147,6 @@ export declare function pendingAgentEffects(t: Tx): Promise<FlowEffect[]>;
152
147
  export declare function reconcilePendingAgentEffect(input: {
153
148
  readonly t: Tx;
154
149
  readonly effectId: string;
155
- readonly schedule: Schedule;
156
150
  }): Promise<boolean>;
157
151
  /** Path depth for deepest-first cancel ordering. */
158
152
  export declare function pathDepth(path: string): number;
@@ -222,16 +216,3 @@ export declare function foreignInteractionConflict(input: {
222
216
  message: string;
223
217
  ownerSessionId: string;
224
218
  } | undefined>;
225
- /**
226
- * When concurrency slots free, re-enter the workflow so queued effects can start
227
- * (status stays `queued` until the effect resolver dispatches them). Returns
228
- * true when a wake was scheduled.
229
- */
230
- export declare function wakeForQueuedEffects(input: {
231
- readonly t: Tx;
232
- readonly workflowSessionId: string;
233
- readonly turnId: string;
234
- readonly limits: FlowLimits;
235
- readonly schedule: Schedule;
236
- }): Promise<boolean>;
237
- export {};
@@ -4,8 +4,8 @@
4
4
  * Every function that touches state takes the caller's transaction `t: Tx` and
5
5
  * is async. Nothing here publishes, notifies or schedules directly (seam rule
6
6
  * 1): events go through `t.event(...)`, which the store publishes after
7
- * commit, and wakes go through `t.wake(id, wake, schedule)`, the wake outbox, which
8
- * calls `schedule` after commit. Functions
7
+ * commit, and wakes go through `t.wake(id, wake)`, the wake outbox, which the store
8
+ * delivers after commit. Functions
9
9
  * that rewrite a session-scoped document lock that session first with
10
10
  * `t.lockSession` (a no-op when the caller already holds it).
11
11
  *
@@ -24,9 +24,9 @@
24
24
  * - `linkedMessageKey(request): string`
25
25
  * - `linkedTurnOf(t, agentSessionId, request): Promise<string | undefined>`
26
26
  * - `linkedTurnEnd(t, effect, agent): Promise<LinkedTurnEnd | undefined>`
27
- * - `wakeLinkedWorkflow({ t, agentSessionId, turnId, output?, failed?, cancelled?, error?, schedule }): Promise<void>`
27
+ * - `wakeLinkedWorkflow({ t, agentSessionId, turnId, output?, failed?, cancelled?, error? }): Promise<void>`
28
28
  * - `pendingAgentEffects(t): Promise<FlowEffect[]>`
29
- * - `reconcilePendingAgentEffect({ t, effectId, schedule }): Promise<boolean>`
29
+ * - `reconcilePendingAgentEffect({ t, effectId }): Promise<boolean>`
30
30
  * - `planCancelCascade({ t, workflowSessionId, turnId }): Promise<CascadeCancelPlan>`
31
31
  * - `cancelSiblingWork({ t, workflowSessionId, turnId, siblingPaths?, cancelEffectIds? }): Promise<CancelSiblingResult>`
32
32
  * - `cancelQueuedEffects({ t, workflowSessionId, turnId }): Promise<string[]>`
@@ -34,11 +34,10 @@
34
34
  * - `flowInteractionOf(waits, workflowSessionId, interactionId): { id; kind } | undefined`
35
35
  * - `findInteractionOwner({ t, workflowSessionId, interactionId }): Promise<{ sessionId; path } | undefined>`
36
36
  * - `foreignInteractionConflict({ t, workflowSessionId, interactionId }): Promise<{ status: 409; message; ownerSessionId } | undefined>`
37
- * - `wakeForQueuedEffects({ t, workflowSessionId, turnId, limits, schedule }): Promise<boolean>`
38
37
  *
39
- * `schedule` (a `WakeDelivery`, the Tenant's `ctx.wake`) runs after commit, never inside `t`;
40
- * `wake` carries the reason (`linked`, `flow`) and, where the cause has one, a dedupe key. A
41
- * wake `schedule` does not deliver stays in the outbox for the Tenant sweep (`Tx.wake`).
38
+ * A wake carries the reason (`linked`, `flow`) and, where the cause has one, a dedupe key.
39
+ * The store delivers it after commit, never inside `t`; one it does not deliver stays in the
40
+ * outbox for the Tenant sweep (`Tx.wake`).
42
41
  *
43
42
  * An `agent` effect settles only from the linked turn it started. A linked agent session is
44
43
  * reused by every iteration of a Loop (its id derives from the workflow and the path), so its
@@ -51,7 +50,7 @@
51
50
  */
52
51
  import { createHash } from "node:crypto";
53
52
  import { isVerdict } from "@nylorun/core/define";
54
- import { mayDispatchMore } from "./limits.js";
53
+ import { resumeTurn } from "../tenant/turn-timing.js";
55
54
  /** Deterministic agent session id: derive(workflowSessionId, path, …parts). */
56
55
  export function deriveSessionId(workflowSessionId, path, ...parts) {
57
56
  const digest = createHash("sha256")
@@ -140,10 +139,6 @@ export async function recordVerdict(t, request, value) {
140
139
  ...(verdict.data !== undefined ? { data: verdict.data } : {}),
141
140
  });
142
141
  }
143
- /** Writes the wake to the outbox; `schedule` delivers it after commit. */
144
- function requestWake(t, schedule, id, wake) {
145
- return t.wake(id, wake, schedule);
146
- }
147
142
  /** Active agent turns + tool nodes in flight for one workflow turn. */
148
143
  export async function countActiveFlowWork(t, workflowSessionId, turnId) {
149
144
  const effects = await t.effectsForTurn(workflowSessionId, turnId);
@@ -243,9 +238,11 @@ export async function wakeLinkedWorkflow(input) {
243
238
  }
244
239
  : { value: input.output ?? null };
245
240
  await settleAgentEffect({ t, effect, outcome });
241
+ // A workflow paused on its agents' interactions goes on (R7.1, `tenant/turn-timing.ts`).
242
+ resumeTurn(workflow);
246
243
  workflow.status = "runnable";
247
244
  await t.put("sessions", workflow.id, workflow);
248
- await requestWake(t, input.schedule, workflow.id, {
245
+ await t.wake(workflow.id, {
249
246
  reason: "linked",
250
247
  dedupeKey: `linked:${link.turnId}:${link.effectId}`,
251
248
  });
@@ -288,7 +285,6 @@ export async function reconcilePendingAgentEffect(input) {
288
285
  : end.status === "failed"
289
286
  ? { failed: true, error: end.error }
290
287
  : { cancelled: true, error: end.error }),
291
- schedule: input.schedule,
292
288
  });
293
289
  return true;
294
290
  }
@@ -457,25 +453,3 @@ export async function foreignInteractionConflict(input) {
457
453
  ownerSessionId: owner.sessionId,
458
454
  };
459
455
  }
460
- /**
461
- * When concurrency slots free, re-enter the workflow so queued effects can start
462
- * (status stays `queued` until the effect resolver dispatches them). Returns
463
- * true when a wake was scheduled.
464
- */
465
- export async function wakeForQueuedEffects(input) {
466
- const { t } = input;
467
- if (!mayDispatchMore(await countActiveFlowWork(t, input.workflowSessionId, input.turnId), input.limits))
468
- return false;
469
- const queued = await t.effectsForTurn(input.workflowSessionId, input.turnId, ["queued"]);
470
- if (queued.length === 0)
471
- return false;
472
- const workflow = await t.lockSession(input.workflowSessionId);
473
- if (workflow && workflow.status === "waiting") {
474
- workflow.status = "runnable";
475
- await t.put("sessions", workflow.id, workflow);
476
- }
477
- await requestWake(t, input.schedule, input.workflowSessionId, {
478
- reason: "flow",
479
- });
480
- return true;
481
- }
@@ -9,7 +9,8 @@ export declare function gatewayModel(options: {
9
9
  model: string;
10
10
  }): ModelProvider;
11
11
  /**
12
- * Deterministic release fixture; still uses the real engine. With a `lookup_order` tool it looks
13
- * up the demo order through it once; without one it answers in text.
12
+ * The fixture model as a provider (`model: {kind: "fixture"}` in the Runtime's config). The Tenant
13
+ * setting `model.fixture` is answered by the Model Gate instead (R4.3); both answer the same way
14
+ * (`gates/fixture-model.ts`).
14
15
  */
15
16
  export declare function toolFixtureModel(): ModelProvider;
@@ -1,3 +1,4 @@
1
+ import { fixtureAnswer } from "../gates/fixture-model.js";
1
2
  export function scriptedModel(output = "Scripted development response") {
2
3
  return async () => ({ output: [{ type: "text", text: output }] });
3
4
  }
@@ -23,43 +24,10 @@ export function gatewayModel(options) {
23
24
  };
24
25
  }
25
26
  /**
26
- * Deterministic release fixture; still uses the real engine. With a `lookup_order` tool it looks
27
- * up the demo order through it once; without one it answers in text.
27
+ * The fixture model as a provider (`model: {kind: "fixture"}` in the Runtime's config). The Tenant
28
+ * setting `model.fixture` is answered by the Model Gate instead (R4.3); both answer the same way
29
+ * (`gates/fixture-model.ts`).
28
30
  */
29
31
  export function toolFixtureModel() {
30
- return async (effect) => {
31
- const call = effect.input;
32
- const last = call.prompt.at(-1);
33
- if (last?.kind === "tool-result")
34
- return {
35
- output: [
36
- {
37
- type: "text",
38
- text: `Order lookup complete: ${last.content.map((part) => (part.type === "text" ? part.text : JSON.stringify(part))).join(" ")}`,
39
- },
40
- ],
41
- };
42
- const users = call.prompt.filter((p) => p.kind === "message" && p.role === "user");
43
- if (users.length > 1)
44
- return {
45
- output: [
46
- {
47
- type: "text",
48
- text: `I remember: ${users[0].content.map((part) => (part.type === "text" ? part.text : "")).join(" ")}`,
49
- },
50
- ],
51
- };
52
- if (!call.tools.some((tool) => tool.name === "lookup_order"))
53
- return { output: [{ type: "text", text: "Hello from the fixture model." }] };
54
- return {
55
- output: [
56
- {
57
- type: "tool-call",
58
- id: "fixture_lookup",
59
- name: "lookup_order",
60
- args: { orderId: "demo-123" },
61
- },
62
- ],
63
- };
64
- };
32
+ return async (effect, signal) => fixtureAnswer(effect.input, signal);
65
33
  }
@@ -9,32 +9,13 @@ export type SandboxRouteSession = SessionSandboxRef & {
9
9
  readonly activeTurnId: string | null;
10
10
  readonly status: string;
11
11
  };
12
- /** A session together with a synchronous lookup over its sandbox owner chain. */
13
- export type SandboxRouteRead = {
14
- readonly session: SandboxRouteSession;
15
- readonly lookup: (id: string) => SessionSandboxRef | undefined;
16
- };
17
12
  export type SandboxRouteDeps = {
18
13
  readonly sandbox: Pick<WorkspacePort, "run">;
19
- /** The session (rejects when missing) and its owner chain, read in one transaction. */
20
- readonly session: (id: string) => Promise<SandboxRouteRead>;
14
+ /** The session; rejects when it is missing. */
15
+ readonly session: (id: string) => Promise<SandboxRouteSession>;
21
16
  };
22
17
  /**
23
18
  * `POST /v1/sessions/:id/sandbox/:tool` — application principal.
24
19
  * Refused while the session has an active agent turn.
25
20
  */
26
21
  export declare function handleSessionSandboxTool(deps: SandboxRouteDeps, sessionId: string, toolName: string, body: unknown, signal: AbortSignal): Promise<SandboxToolOutcome>;
27
- /**
28
- * Validate PutSession.sandbox attach: same owner, and a sandbox to share. A session whose
29
- * definition declares a sandbox must declare the owner's spec; one whose definition declares
30
- * none inherits it (`inherit`).
31
- */
32
- export declare function validateSandboxAttach(body: {
33
- ownerUserId: string;
34
- sandbox?: unknown;
35
- }, newManifest: SessionSandboxRef["manifest"], lookup: (id: string) => SessionSandboxRef | undefined,
36
- /** Acting for a subject: another owner's session is the 404 of a missing one. */
37
- options?: {
38
- opaque?: boolean;
39
- inherit?: boolean;
40
- }): string | undefined;
@@ -1,9 +1,9 @@
1
1
  /**
2
- * HTTP handlers for the session-scoped sandbox tool route (`POST /v1/sessions/:id/sandbox/:tool`)
3
- * and the sandbox attach check of `PUT /v1/sessions/:id`.
2
+ * HTTP handlers for the session-scoped sandbox tool route (`POST /v1/sessions/:id/sandbox/:tool`):
3
+ * a tool run in the sandbox of the session's Environment.
4
4
  */
5
5
  import { isSandboxToolName, } from "@nylorun/core/define";
6
- import { capabilityForSandbox, owningSandboxSessionId, sandboxSpecOf, sandboxWorkspaceOf, sandboxSpecsEqual, sessionSandboxSpec, } from "../sandbox/share.js";
6
+ import { capabilityForSandbox, sandboxWorkspaceOf, sessionSandboxSpec, } from "../sandbox/share.js";
7
7
  export class SandboxRouteError extends Error {
8
8
  status;
9
9
  constructor(status, message) {
@@ -12,10 +12,8 @@ export class SandboxRouteError extends Error {
12
12
  this.name = "SandboxRouteError";
13
13
  }
14
14
  }
15
- function resolveSpec(session, lookup) {
16
- const ownerId = owningSandboxSessionId(session, lookup);
17
- const owner = ownerId === session.id ? session : lookup(ownerId) ?? session;
18
- const spec = sessionSandboxSpec(owner) ?? sessionSandboxSpec(session);
15
+ function resolveSpec(session) {
16
+ const spec = sessionSandboxSpec(session);
19
17
  if (!spec)
20
18
  throw new SandboxRouteError(404, "Session has no sandbox");
21
19
  return spec;
@@ -34,14 +32,14 @@ function toolInput(body) {
34
32
  const { requestId: _r, ...input } = record;
35
33
  return input;
36
34
  }
37
- async function runTool(deps, { session, lookup }, tool, input, signal) {
38
- const spec = resolveSpec(session, lookup);
39
- const workspace = sandboxWorkspaceOf(session, lookup);
35
+ async function runTool(deps, session, tool, input, signal) {
36
+ const spec = resolveSpec(session);
37
+ const workspace = sandboxWorkspaceOf(session);
40
38
  const ref = {
41
39
  id: workspace.ownerId,
42
40
  activeTurnId: session.activeTurnId,
43
41
  manifest: session.manifest,
44
- ...(workspace.sandboxId === undefined ? {} : { sandboxId: workspace.sandboxId }),
42
+ ...(workspace.environmentId === undefined ? {} : { environmentId: workspace.environmentId }),
45
43
  };
46
44
  return deps.sandbox.run(ref, capabilityForSandbox(spec), tool, input, signal);
47
45
  }
@@ -51,39 +49,8 @@ async function runTool(deps, { session, lookup }, tool, input, signal) {
51
49
  */
52
50
  export async function handleSessionSandboxTool(deps, sessionId, toolName, body, signal) {
53
51
  const read = await deps.session(sessionId);
54
- if (read.session.activeTurnId)
52
+ if (read.activeTurnId)
55
53
  throw new SandboxRouteError(409, "Sandbox is unavailable while the session has an active agent turn");
56
54
  const tool = parseTool(toolName);
57
55
  return runTool(deps, read, tool, toolInput(body), signal);
58
56
  }
59
- /**
60
- * Validate PutSession.sandbox attach: same owner, and a sandbox to share. A session whose
61
- * definition declares a sandbox must declare the owner's spec; one whose definition declares
62
- * none inherits it (`inherit`).
63
- */
64
- export function validateSandboxAttach(body, newManifest, lookup,
65
- /** Acting for a subject: another owner's session is the 404 of a missing one. */
66
- options = {}) {
67
- const request = body.sandbox;
68
- const share = typeof request === "object" && request !== null && "session" in request
69
- ? String(request.session)
70
- : undefined;
71
- if (!share)
72
- return undefined;
73
- const owner = lookup(share);
74
- if (!owner || (options.opaque && owner.ownerUserId !== body.ownerUserId))
75
- throw new SandboxRouteError(404, "Sandbox session not found");
76
- if (owner.ownerUserId !== body.ownerUserId)
77
- throw new SandboxRouteError(403, "Sandbox session must belong to the same owner");
78
- const ownerSpec = sessionSandboxSpec(owner);
79
- if (!ownerSpec)
80
- throw new SandboxRouteError(400, "Target session has no sandbox to share");
81
- if (options.inherit)
82
- return owningSandboxSessionId(owner, lookup);
83
- const newSpec = sandboxSpecOf(newManifest);
84
- if (!newSpec)
85
- throw new SandboxRouteError(400, "Session must declare a sandbox identical to the shared session");
86
- if (!sandboxSpecsEqual(ownerSpec, newSpec))
87
- throw new SandboxRouteError(409, "Sandbox specs must be identical to share a sandbox");
88
- return owningSandboxSessionId(owner, lookup);
89
- }
@@ -1,12 +1,10 @@
1
1
  /**
2
2
  * The Durable Session Execution seam (architecture §12.3).
3
3
  *
4
- * Durable Session Execution decides when to look at a session again. It never
5
- * holds session state: every outcome lives in the Session Store, and `advance`
6
- * is safe to repeat (§10.1). It owns the schedule (wake delivery, one advance per
7
- * key, retries, timers, the sweep chain); the Session Store owns the record (the
8
- * checkpoint, the effect journal, the events) and the wake outbox that bridges the
9
- * two. Restate is the supported implementation
4
+ * Durable Session Execution decides when to look at a session again; `advance` is
5
+ * safe to repeat (§10.1). What it owns and what the Session Store owns is said once,
6
+ * under "Durable Session Execution" in `src/CONTEXT.md`. Restate is the supported
7
+ * implementation
10
8
  * (`adapters/execution/restate.ts`); `execution/memory.ts` is the in-process
11
9
  * implementation used by unit tests, `startEphemeralRuntime` and a Host
12
10
  * started without Restate endpoints.
@@ -19,7 +17,7 @@
19
17
  * - **At least once after a wake.** When `wake` resolves, an `advance` for the
20
18
  * key will start after that point (possibly merged with other wakes). Wakes
21
19
  * are sent after the Session Store transaction that caused them commits, never
22
- * inside it: the transaction writes the wake to its outbox (`Tx.wake`), and the
20
+ * inside it: the transaction writes the wake to the outbox (`Tx.wake`), and the
23
21
  * wake is sent after commit, or by the Tenant sweep when that send failed or a
24
22
  * crash cut it off. A wake may therefore be sent more than once, always with the
25
23
  * same `dedupeKey`.
@@ -74,10 +72,14 @@ export type AdvanceResult = {
74
72
  retryAfterMs: number;
75
73
  };
76
74
  /** A pod sandbox's timers. */
77
- export type SandboxTimer = "idle" | "ttl";
78
- /** What `DurableExecution.sandbox` asks for: a reconcile, or a timer. */
75
+ export type SandboxTimer = "idle";
76
+ /**
77
+ * What `DurableExecution.sandbox` asks for: a reconcile, or a timer. A reconcile's `key` merges
78
+ * repeated sends of one request (the outbox's `signal:<row id>`); without one, every send runs.
79
+ */
79
80
  export type SandboxSignal = {
80
81
  kind: "reconcile";
82
+ key?: string;
81
83
  } | {
82
84
  kind: "arm";
83
85
  timer: SandboxTimer;
@@ -1,12 +1,10 @@
1
1
  /**
2
2
  * The Durable Session Execution seam (architecture §12.3).
3
3
  *
4
- * Durable Session Execution decides when to look at a session again. It never
5
- * holds session state: every outcome lives in the Session Store, and `advance`
6
- * is safe to repeat (§10.1). It owns the schedule (wake delivery, one advance per
7
- * key, retries, timers, the sweep chain); the Session Store owns the record (the
8
- * checkpoint, the effect journal, the events) and the wake outbox that bridges the
9
- * two. Restate is the supported implementation
4
+ * Durable Session Execution decides when to look at a session again; `advance` is
5
+ * safe to repeat (§10.1). What it owns and what the Session Store owns is said once,
6
+ * under "Durable Session Execution" in `src/CONTEXT.md`. Restate is the supported
7
+ * implementation
10
8
  * (`adapters/execution/restate.ts`); `execution/memory.ts` is the in-process
11
9
  * implementation used by unit tests, `startEphemeralRuntime` and a Host
12
10
  * started without Restate endpoints.
@@ -19,7 +17,7 @@
19
17
  * - **At least once after a wake.** When `wake` resolves, an `advance` for the
20
18
  * key will start after that point (possibly merged with other wakes). Wakes
21
19
  * are sent after the Session Store transaction that caused them commits, never
22
- * inside it: the transaction writes the wake to its outbox (`Tx.wake`), and the
20
+ * inside it: the transaction writes the wake to the outbox (`Tx.wake`), and the
23
21
  * wake is sent after commit, or by the Tenant sweep when that send failed or a
24
22
  * crash cut it off. A wake may therefore be sent more than once, always with the
25
23
  * same `dedupeKey`.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The gates service's wire format (blueprint §15, P1.1). Internal: only the loop calls it, so
3
- * it is not in the published OpenAPI documents.
2
+ * The gates service's wire format (blueprint §15, P1.1). Not in the published OpenAPI documents:
3
+ * harnesses call it, and its bodies are published in `@nylorun/core/harness-api` (R4.5).
4
4
  *
5
5
  * `POST /nylorun/v1/model-calls`
6
6
  * - Headers: `Authorization: Bearer <run token>` (F5, `tenant/run-token.ts`), `Idempotency-Key:
@@ -22,31 +22,10 @@
22
22
  * whether or not it exists, `403 gate_forbidden` when it is another session's call. The token
23
23
  * need not be live: a user cancel makes it stale, and still stops its call.
24
24
  */
25
- import { z } from "zod";
26
25
  import type { ModelGateOutcome } from "./model-gate.js";
27
- export declare const MODEL_CALLS_PATH = "/nylorun/v1/model-calls";
26
+ export { MAX_MODEL_CALL_BYTES, MODEL_CALLS_PATH, ModelCallBodySchema, type GateErrorBody, type ModelCallBody, } from "@nylorun/core/harness-api";
28
27
  /** Header naming the Tenant a call is for: optional, and checked against the gate's Tenant. */
29
28
  export declare const TENANT_HEADER = "nylorun-tenant";
30
- /** Largest request body the gate reads: one `ModelCall` with its whole projected history. */
31
- export declare const MAX_MODEL_CALL_BYTES: number;
32
- export declare const ModelCallBodySchema: z.ZodObject<{
33
- effectId: z.ZodString;
34
- invocationId: z.ZodString;
35
- call: z.ZodObject<{
36
- prompt: z.ZodArray<z.ZodUnknown>;
37
- tools: z.ZodArray<z.ZodObject<{
38
- name: z.ZodString;
39
- }, z.core.$loose>>;
40
- }, z.core.$loose>;
41
- }, z.core.$strict>;
42
- export type ModelCallBody = z.infer<typeof ModelCallBodySchema>;
43
29
  export interface ModelCallResponse {
44
30
  readonly outcome: ModelGateOutcome;
45
31
  }
46
- /** An error answer: `{error: {code, message}}`. */
47
- export interface GateErrorBody {
48
- readonly error: {
49
- readonly code: "gate_unauthorized" | "gate_forbidden" | "run_stale" | "invalid_request" | "gate_conflict";
50
- readonly message: string;
51
- };
52
- }
@@ -1,38 +1,3 @@
1
- /**
2
- * The gates service's wire format (blueprint §15, P1.1). Internal: only the loop calls it, so
3
- * it is not in the published OpenAPI documents.
4
- *
5
- * `POST /nylorun/v1/model-calls`
6
- * - Headers: `Authorization: Bearer <run token>` (F5, `tenant/run-token.ts`), `Idempotency-Key:
7
- * <effect id>`, and optionally `Nylorun-Tenant: <tenant id>`, which must then be the token's
8
- * Tenant. Core's credential (`NYLORUN_GATES_TOKEN`) is refused: only a run calls the model.
9
- * - Body: `ModelCallBody`, at most `MAX_MODEL_CALL_BYTES`. It names no session, turn or agent:
10
- * the gate takes them from the token, and refuses a body that names them.
11
- * - `200 {outcome}` once the call has finished, whether the outcome is a candidate or a
12
- * failure; nothing is sent while it runs. `401 gate_unauthorized` for a missing, malformed,
13
- * expired or foreign token, `409 run_stale` when the token's lease is no longer the
14
- * session's (a cancel, a new turn, a takeover), `400 invalid_request` for a malformed Tenant
15
- * header or a bad body, `409 gate_conflict` when a different request already runs under the
16
- * same key.
17
- * - With an `Idempotency-Key` the call outlives its client (P1.2): a re-send with the same key
18
- * and body joins it, or gets its outcome for 30 minutes. Without one, a client that goes
19
- * away aborts the call.
20
- *
21
- * `POST /nylorun/v1/model-calls/{key}/cancel` (same headers): aborts the keyed call; `204`
22
- * whether or not it exists, `403 gate_forbidden` when it is another session's call. The token
23
- * need not be live: a user cancel makes it stale, and still stops its call.
24
- */
25
- import { z } from "zod";
26
- export const MODEL_CALLS_PATH = "/nylorun/v1/model-calls";
1
+ export { MAX_MODEL_CALL_BYTES, MODEL_CALLS_PATH, ModelCallBodySchema, } from "@nylorun/core/harness-api";
27
2
  /** Header naming the Tenant a call is for: optional, and checked against the gate's Tenant. */
28
3
  export const TENANT_HEADER = "nylorun-tenant";
29
- /** Largest request body the gate reads: one `ModelCall` with its whole projected history. */
30
- export const MAX_MODEL_CALL_BYTES = 32 * 1024 * 1024;
31
- export const ModelCallBodySchema = z.strictObject({
32
- effectId: z.string().min(1),
33
- invocationId: z.string().min(1),
34
- call: z.looseObject({
35
- prompt: z.array(z.unknown()),
36
- tools: z.array(z.looseObject({ name: z.string() })),
37
- }),
38
- });
@@ -27,6 +27,13 @@ export interface EgressCredentials {
27
27
  /** Tests: the port requests are sent to, instead of 443. */
28
28
  readonly upstreamPort?: number;
29
29
  }
30
+ /** The answer to a CONNECT the gate serves; a tunnel's byte counts leave it out. */
31
+ export declare const CONNECT_ESTABLISHED = "HTTP/1.1 200 Connection Established\r\n\r\n";
32
+ /** What one terminated connection did so far (R7.3): requests read, credentials set (ids). */
33
+ export interface EgressTally {
34
+ requests: number;
35
+ readonly credentialIds: Set<string>;
36
+ }
30
37
  export interface CredentialTerminator {
31
38
  /**
32
39
  * Answers the CONNECT, terminates TLS for `host` and serves its requests, sending each to one
@@ -39,6 +46,8 @@ export interface CredentialTerminator {
39
46
  address: string;
40
47
  family: number;
41
48
  }[];
49
+ /** Counted as requests are served, read when the connection closes. */
50
+ readonly tally: EgressTally;
42
51
  }): Promise<void>;
43
52
  }
44
53
  export declare function credentialTerminator(options: {