@nylorun/runtime 0.16.0-beta → 0.19.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 (256) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/README.md +105 -63
  3. package/dist/adapters/execution/restate.d.ts +1 -3
  4. package/dist/adapters/execution/restate.js +4 -31
  5. package/dist/adapters/replication/pgoutput.js +1 -1
  6. package/dist/adapters/sandbox/local.js +1 -1
  7. package/dist/adapters/sandbox/virtual.js +1 -1
  8. package/dist/api/ag-ui/endpoint.js +1 -2
  9. package/dist/api/ag-ui/routes.d.ts +1 -1
  10. package/dist/api/ag-ui/routes.js +11 -13
  11. package/dist/api/ag-ui/translate.d.ts +1 -3
  12. package/dist/api/ag-ui/translate.js +2 -17
  13. package/dist/api/components.d.ts +4783 -2588
  14. package/dist/api/components.js +19 -21
  15. package/dist/api/gate/routes.d.ts +7 -8
  16. package/dist/api/gate/routes.js +58 -52
  17. package/dist/api/http/app.d.ts +6 -0
  18. package/dist/api/http/app.js +20 -14
  19. package/dist/api/http/define.d.ts +22 -13
  20. package/dist/api/http/define.js +43 -39
  21. package/dist/api/http/routes/access.d.ts +3 -19
  22. package/dist/api/http/routes/access.js +25 -184
  23. package/dist/api/http/routes/artifacts.js +3 -7
  24. package/dist/api/http/routes/files.d.ts +8 -0
  25. package/dist/api/http/routes/files.js +77 -0
  26. package/dist/api/http/routes/keys.d.ts +10 -0
  27. package/dist/api/http/routes/keys.js +64 -0
  28. package/dist/api/http/routes/me.d.ts +13 -0
  29. package/dist/api/http/routes/me.js +48 -0
  30. package/dist/api/http/routes/reads.d.ts +66 -0
  31. package/dist/api/http/routes/reads.js +127 -0
  32. package/dist/api/http/routes/sandboxes.d.ts +2 -2
  33. package/dist/api/http/routes/sandboxes.js +8 -7
  34. package/dist/api/http/routes/sessions.js +31 -20
  35. package/dist/api/http/routes/tenant.js +15 -18
  36. package/dist/api/http/routes/vaults.d.ts +13 -3
  37. package/dist/api/http/routes/vaults.js +136 -64
  38. package/dist/api/openapi.d.ts +10 -11
  39. package/dist/api/openapi.js +292 -68
  40. package/dist/artifacts/links.js +6 -6
  41. package/dist/artifacts/service.js +1 -1
  42. package/dist/core/flow-host.d.ts +44 -23
  43. package/dist/core/flow-host.js +88 -106
  44. package/dist/core/provider.d.ts +4 -1
  45. package/dist/core/provider.js +6 -1
  46. package/dist/core/sandbox-routes.d.ts +0 -9
  47. package/dist/core/sandbox-routes.js +2 -21
  48. package/dist/execution/memory.d.ts +4 -5
  49. package/dist/execution/memory.js +11 -25
  50. package/dist/execution/types.d.ts +3 -33
  51. package/dist/execution/types.js +2 -12
  52. package/dist/gates/http-tool.d.ts +86 -0
  53. package/dist/gates/http-tool.js +197 -0
  54. package/dist/gates/mcp-handler.d.ts +3 -0
  55. package/dist/gates/mcp-handler.js +6 -4
  56. package/dist/gates/meter.d.ts +2 -2
  57. package/dist/gates/meter.js +6 -0
  58. package/dist/gates/tenant-vaults.d.ts +19 -14
  59. package/dist/gates/tenant-vaults.js +17 -12
  60. package/dist/gates/tool-calls.d.ts +7 -15
  61. package/dist/gates/tool-calls.js +4 -4
  62. package/dist/gates/tool-client.d.ts +1 -1
  63. package/dist/gates/tool-client.js +23 -47
  64. package/dist/gates/tool-contract.d.ts +27 -39
  65. package/dist/gates/tool-contract.js +30 -21
  66. package/dist/gates/tool-gate.d.ts +25 -24
  67. package/dist/gates/tool-gate.js +11 -7
  68. package/dist/harness/calls.d.ts +10 -4
  69. package/dist/harness/calls.js +15 -6
  70. package/dist/harness/executors.d.ts +1 -1
  71. package/dist/harness/executors.js +17 -12
  72. package/dist/harness/main.d.ts +2 -2
  73. package/dist/harness/main.js +5 -13
  74. package/dist/harness/service.d.ts +4 -5
  75. package/dist/harness/service.js +9 -2
  76. package/dist/harness-api/in-process.js +2 -2
  77. package/dist/harness-api/record.d.ts +6 -3
  78. package/dist/harness-api/record.js +65 -72
  79. package/dist/harness-api/server.d.ts +1 -9
  80. package/dist/harness-api/server.js +19 -17
  81. package/dist/harness-api/start.js +1 -5
  82. package/dist/harness-api/workspace.js +2 -3
  83. package/dist/harness-api/ws-server.js +1 -1
  84. package/dist/host/app.d.ts +2 -10
  85. package/dist/host/app.js +24 -44
  86. package/dist/host/config-for.d.ts +5 -5
  87. package/dist/host/config-for.js +2 -3
  88. package/dist/host/config.d.ts +0 -11
  89. package/dist/host/create-host.d.ts +3 -40
  90. package/dist/host/create-host.js +18 -91
  91. package/dist/host/environment.d.ts +0 -3
  92. package/dist/host/environment.js +0 -9
  93. package/dist/host/gates.d.ts +4 -1
  94. package/dist/host/gates.js +9 -3
  95. package/dist/host/http.d.ts +1 -2
  96. package/dist/host/http.js +1 -7
  97. package/dist/host/index.d.ts +2 -2
  98. package/dist/host/index.js +2 -2
  99. package/dist/host/main.js +55 -40
  100. package/dist/host/operate.d.ts +29 -0
  101. package/dist/host/operate.js +223 -0
  102. package/dist/host/stack-config.d.ts +23 -20
  103. package/dist/host/stack-config.js +28 -44
  104. package/dist/index.d.ts +4 -1
  105. package/dist/index.js +3 -1
  106. package/dist/infra/workers.d.ts +1 -1
  107. package/dist/keys/client.js +9 -1
  108. package/dist/keys/keys.d.ts +12 -2
  109. package/dist/keys/keys.js +10 -2
  110. package/dist/management-openapi.json +6491 -0
  111. package/dist/mcp/connect.d.ts +8 -4
  112. package/dist/mcp/connect.js +8 -27
  113. package/dist/mcp/pool.d.ts +6 -9
  114. package/dist/mcp/pool.js +8 -18
  115. package/dist/mcp/snapshot.d.ts +8 -2
  116. package/dist/mcp/snapshot.js +29 -2
  117. package/dist/media.d.ts +1 -1
  118. package/dist/media.js +1 -1
  119. package/dist/model/pi-model.js +3 -0
  120. package/dist/openapi.json +6228 -15615
  121. package/dist/reads/access.d.ts +4 -0
  122. package/dist/reads/access.js +12 -0
  123. package/dist/reads/cursor.d.ts +11 -0
  124. package/dist/reads/cursor.js +80 -0
  125. package/dist/reads/types.d.ts +25 -0
  126. package/dist/reads/types.js +1 -0
  127. package/dist/sandbox/manager.d.ts +16 -0
  128. package/dist/sandbox/manager.js +40 -0
  129. package/dist/sandbox/session-sandbox.js +13 -3
  130. package/dist/sandbox/skills.d.ts +13 -0
  131. package/dist/sandbox/skills.js +46 -0
  132. package/dist/sandbox/types.d.ts +2 -2
  133. package/dist/store/ownership.d.ts +9 -0
  134. package/dist/store/ownership.js +13 -0
  135. package/dist/store/postgres/connect.d.ts +2 -0
  136. package/dist/store/postgres/connect.js +29 -0
  137. package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
  138. package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
  139. package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
  140. package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
  141. package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
  142. package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
  143. package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
  144. package/dist/store/postgres/drizzle/meta/_journal.json +49 -0
  145. package/dist/store/postgres/reads.d.ts +3 -0
  146. package/dist/store/postgres/reads.js +272 -0
  147. package/dist/store/postgres/schema.d.ts +423 -594
  148. package/dist/store/postgres/schema.js +84 -101
  149. package/dist/store/postgres/store.js +96 -248
  150. package/dist/store/postgres/tenant.d.ts +34 -2
  151. package/dist/store/postgres/tenant.js +16 -32
  152. package/dist/store/types.d.ts +44 -119
  153. package/dist/store/types.js +0 -1
  154. package/dist/streams/control.d.ts +0 -7
  155. package/dist/streams/control.js +0 -13
  156. package/dist/streams/types.d.ts +2 -20
  157. package/dist/streams/types.js +1 -1
  158. package/dist/tenant/advance.d.ts +3 -3
  159. package/dist/tenant/advance.js +10 -7
  160. package/dist/tenant/auth.d.ts +24 -15
  161. package/dist/tenant/auth.js +44 -55
  162. package/dist/tenant/cause.d.ts +3 -3
  163. package/dist/tenant/cause.js +1 -1
  164. package/dist/tenant/commands.d.ts +2 -18
  165. package/dist/tenant/commands.js +38 -179
  166. package/dist/tenant/context.d.ts +33 -45
  167. package/dist/tenant/definition-files.d.ts +24 -0
  168. package/dist/tenant/definition-files.js +103 -0
  169. package/dist/tenant/effects.d.ts +20 -18
  170. package/dist/tenant/effects.js +59 -133
  171. package/dist/tenant/ephemeral.d.ts +21 -8
  172. package/dist/tenant/ephemeral.js +11 -12
  173. package/dist/tenant/host-token.js +5 -5
  174. package/dist/tenant/identity-file.d.ts +75 -0
  175. package/dist/tenant/identity-file.js +202 -0
  176. package/dist/tenant/issuers.d.ts +48 -0
  177. package/dist/tenant/issuers.js +311 -0
  178. package/dist/tenant/jwt.js +3 -3
  179. package/dist/tenant/module.d.ts +1 -4
  180. package/dist/tenant/module.js +3 -12
  181. package/dist/tenant/operator-keys.d.ts +44 -0
  182. package/dist/tenant/operator-keys.js +87 -0
  183. package/dist/tenant/outbound.d.ts +40 -1
  184. package/dist/tenant/outbound.js +167 -18
  185. package/dist/tenant/paths.d.ts +1 -1
  186. package/dist/tenant/paths.js +1 -3
  187. package/dist/tenant/principals.d.ts +13 -13
  188. package/dist/tenant/principals.js +35 -36
  189. package/dist/tenant/run-token.js +6 -6
  190. package/dist/tenant/runtime.d.ts +5 -3
  191. package/dist/tenant/runtime.js +29 -15
  192. package/dist/tenant/sandboxes.d.ts +9 -6
  193. package/dist/tenant/sandboxes.js +16 -5
  194. package/dist/tenant/scheduler.d.ts +1 -5
  195. package/dist/tenant/scheduler.js +2 -5
  196. package/dist/tenant/session-streams.d.ts +8 -11
  197. package/dist/tenant/session-streams.js +48 -32
  198. package/dist/tenant/session.d.ts +8 -41
  199. package/dist/tenant/session.js +0 -37
  200. package/dist/tenant/sessions.d.ts +2 -2
  201. package/dist/tenant/sessions.js +17 -9
  202. package/dist/tenant/signing-keys.d.ts +3 -2
  203. package/dist/tenant/signing-keys.js +5 -5
  204. package/dist/tenant/skill-tool.d.ts +20 -0
  205. package/dist/tenant/skill-tool.js +72 -0
  206. package/dist/tenant/slim.d.ts +1 -1
  207. package/dist/tenant/slim.js +1 -1
  208. package/dist/tenant/status.js +7 -12
  209. package/dist/tenant/store-pg.d.ts +15 -0
  210. package/dist/tenant/store-pg.js +7 -3
  211. package/dist/tenant/streams.d.ts +3 -11
  212. package/dist/tenant/streams.js +2 -29
  213. package/dist/tenant/sweep.js +0 -5
  214. package/dist/tenant/transcript.d.ts +2 -2
  215. package/dist/tenant/transcript.js +2 -2
  216. package/dist/tenant/types.d.ts +33 -19
  217. package/dist/tenant/worker.d.ts +1 -3
  218. package/dist/tenant/worker.js +3 -8
  219. package/dist/vault/crypto.d.ts +7 -0
  220. package/dist/vault/crypto.js +16 -0
  221. package/dist/vault/oauth.d.ts +55 -0
  222. package/dist/vault/oauth.js +186 -0
  223. package/dist/vault/service.d.ts +54 -7
  224. package/dist/vault/service.js +261 -17
  225. package/dist/vault/sources.d.ts +60 -0
  226. package/dist/vault/sources.js +196 -0
  227. package/dist/version.d.ts +1 -1
  228. package/dist/version.js +1 -1
  229. package/package.json +5 -4
  230. package/dist/admin-openapi.json +0 -604
  231. package/dist/api/http/routes/actions.d.ts +0 -8
  232. package/dist/api/http/routes/actions.js +0 -76
  233. package/dist/api/http/routes/endpoints.d.ts +0 -7
  234. package/dist/api/http/routes/endpoints.js +0 -100
  235. package/dist/host/admin-api.d.ts +0 -23
  236. package/dist/host/admin-api.js +0 -74
  237. package/dist/host/cors.d.ts +0 -27
  238. package/dist/host/cors.js +0 -64
  239. package/dist/mcp/stdio.d.ts +0 -17
  240. package/dist/mcp/stdio.js +0 -82
  241. package/dist/tenant/access-policy.d.ts +0 -30
  242. package/dist/tenant/access-policy.js +0 -52
  243. package/dist/tenant/actions.d.ts +0 -14
  244. package/dist/tenant/actions.js +0 -29
  245. package/dist/tenant/browser.d.ts +0 -15
  246. package/dist/tenant/browser.js +0 -39
  247. package/dist/tenant/delivery-token.d.ts +0 -41
  248. package/dist/tenant/delivery-token.js +0 -104
  249. package/dist/tenant/delivery.d.ts +0 -62
  250. package/dist/tenant/delivery.js +0 -481
  251. package/dist/tenant/endpoints.d.ts +0 -67
  252. package/dist/tenant/endpoints.js +0 -90
  253. package/dist/tenant/subject-limits.d.ts +0 -16
  254. package/dist/tenant/subject-limits.js +0 -45
  255. package/dist/tenant/tokens.d.ts +0 -14
  256. package/dist/tenant/tokens.js +0 -187
@@ -0,0 +1,127 @@
1
+ import { z } from "zod";
2
+ import { SESSION_STATUSES } from "@nylorun/core/contracts";
3
+ import { SessionManifestView, SessionUsageTotals, ModelCallsPage, ModelCallExportPage, } from "../../components.js";
4
+ import { readAccess, readStoreOf } from "../../../reads/access.js";
5
+ import { requireApplication, requirePrincipal } from "../../../tenant/auth.js";
6
+ import { fail } from "../../../tenant/http.js";
7
+ import { sandboxGrantsOf } from "../../../tenant/sandboxes.js";
8
+ import { tenantRoute } from "../define.js";
9
+ import { jsonResponse } from "../respond.js";
10
+ export const pageQuery = z.object({
11
+ limit: z.coerce.number().int().min(1).max(200).optional(),
12
+ cursor: z.string().optional(),
13
+ });
14
+ export const sessionPageQuery = pageQuery
15
+ .extend({
16
+ agentId: z.string().optional(),
17
+ status: z.enum(SESSION_STATUSES).optional(),
18
+ sandboxId: z.string().optional(),
19
+ ownerUserId: z.string().optional(),
20
+ })
21
+ .strict();
22
+ export function parseQuery(schema, query) {
23
+ const result = schema.safeParse(query);
24
+ return result.success ? result.data : fail(400, "Invalid read query", { code: "query_invalid" });
25
+ }
26
+ export async function sessionPage(ctx, scope, query) {
27
+ const { limit, cursor, ...filters } = parseQuery(sessionPageQuery, query);
28
+ if (limit === undefined)
29
+ return fail(400, "Pagination requires limit");
30
+ if (filters.ownerUserId !== undefined)
31
+ requireApplication(scope);
32
+ return readStoreOf(ctx).sessions(filters, { limit, cursor }, readAccess(scope));
33
+ }
34
+ export async function sandboxPage(ctx, scope, query, labels) {
35
+ const { limit, cursor } = parseQuery(pageQuery.extend({ label: z.string().optional() }).strict(), query);
36
+ if (limit === undefined)
37
+ return fail(400, "Pagination requires limit");
38
+ return readStoreOf(ctx).sandboxes(labels, { limit, cursor }, readAccess(scope), sandboxGrantsOf(scope));
39
+ }
40
+ export function readRoutes(api) {
41
+ const sessionId = z.object({ sessionId: z.string() });
42
+ const turnQuery = z.object({ turnId: z.string().optional() }).strict();
43
+ tenantRoute(api, { credentials: ["application", "subject"], scopes: ["agents:read", "agents:write"] }, {
44
+ method: "get",
45
+ path: "/v1/sessions/{sessionId}/manifest",
46
+ tags: ["Sessions"],
47
+ summary: "Read the session's pinned manifest",
48
+ request: { params: sessionId },
49
+ responses: {
50
+ 200: {
51
+ description: "The pinned public manifest",
52
+ content: { "application/json": { schema: SessionManifestView } },
53
+ },
54
+ },
55
+ }, async (c) => {
56
+ requirePrincipal(c.get("scope"));
57
+ return jsonResponse(200, await readStoreOf(c.env.tenant).manifest(c.req.param("sessionId"), readAccess(c.get("scope"))));
58
+ });
59
+ for (const kind of ["usage", "calls/model"]) {
60
+ const query = kind === "usage"
61
+ ? turnQuery
62
+ : turnQuery
63
+ .extend({
64
+ limit: z.coerce.number().int().min(1).max(200).default(50),
65
+ cursor: z.string().optional(),
66
+ })
67
+ .strict();
68
+ // Spend is the application's to show: no subject scope reaches it (`tenant:settings`,
69
+ // which used to, is retired in protocol 8).
70
+ tenantRoute(api, { credentials: ["application"], scopes: "never" }, {
71
+ method: "get",
72
+ path: `/v1/sessions/{sessionId}/${kind}`,
73
+ tags: ["Sessions"],
74
+ summary: kind === "usage"
75
+ ? "Read a session's recorded model usage"
76
+ : "Page a session's model calls",
77
+ request: { params: sessionId, query },
78
+ responses: {
79
+ 200: {
80
+ description: "Recorded usage, including duplicate billed calls",
81
+ content: {
82
+ "application/json": {
83
+ schema: kind === "usage" ? SessionUsageTotals : ModelCallsPage,
84
+ },
85
+ },
86
+ },
87
+ },
88
+ }, async (c) => {
89
+ requirePrincipal(c.get("scope"));
90
+ const store = readStoreOf(c.env.tenant), access = readAccess(c.get("scope")), id = c.req.param("sessionId");
91
+ if (kind === "usage") {
92
+ const q = parseQuery(turnQuery, c.req.query());
93
+ return jsonResponse(200, await store.usage(id, q.turnId, access));
94
+ }
95
+ const q = parseQuery(turnQuery
96
+ .extend({
97
+ limit: z.coerce.number().int().min(1).max(200).default(50),
98
+ cursor: z.string().optional(),
99
+ })
100
+ .strict(), c.req.query());
101
+ return jsonResponse(200, await store.modelCalls(id, q.turnId, q, access));
102
+ });
103
+ }
104
+ const exportQuery = z
105
+ .object({
106
+ after: z.string().optional(),
107
+ limit: z.coerce.number().int().min(1).max(1000).default(200),
108
+ })
109
+ .strict();
110
+ // The whole Tenant's ledger is the Management API's (protocol 8): a management key, as itself.
111
+ tenantRoute(api, { credentials: ["management"], scopes: "never" }, {
112
+ method: "get",
113
+ path: "/v1/tenant/calls/model",
114
+ tags: ["Tenant"],
115
+ summary: "Export committed model calls in safe transaction order",
116
+ request: { query: exportQuery },
117
+ responses: {
118
+ 200: {
119
+ description: "An unfiltered resumable ledger page",
120
+ content: { "application/json": { schema: ModelCallExportPage } },
121
+ },
122
+ },
123
+ }, async (c) => {
124
+ const q = parseQuery(exportQuery, c.req.query());
125
+ return jsonResponse(200, await readStoreOf(c.env.tenant).exportModel(q.after, q.limit));
126
+ });
127
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Sandboxes as a resource (`/v1/sandboxes/**`, Host feature `sandboxes`): create or find one by
3
3
  * id, read it, list them by label, read its lifecycle stream, and delete it. Ids may hold `/`,
4
- * sent percent-encoded as one path segment (`team-a%2Fproj-42`). A subject token reaches only
5
- * the ids its `sbx` grants match, and changes them only with `sandboxes:write`.
4
+ * sent percent-encoded as one path segment (`team-a%2Fproj-42`). A token caller reaches only
5
+ * the ids its sandbox grants match, and changes them only with `sandboxes:write`.
6
6
  */
7
7
  import type { OpenAPIHono } from "@hono/zod-openapi";
8
8
  import type { TenantEnv } from "../app.js";
@@ -1,22 +1,21 @@
1
1
  import { z } from "zod";
2
2
  import { isSandboxId, PutSandboxRequestSchema } from "@nylorun/core/contracts";
3
- import { DeleteSandboxResponse, ListSandboxEventsResponse, ListSandboxesResponse, PutSandboxRequest, SandboxView, } from "../../components.js";
3
+ import { DeleteSandboxResponse, ListSandboxEventsResponse, ListSandboxesResponse, SandboxPage, PutSandboxRequest, SandboxView, } from "../../components.js";
4
4
  import { fail } from "../../../tenant/http.js";
5
5
  import { deleteSandbox, getSandbox, listSandboxes, putSandbox, resetSandbox, sandboxEventsOf, stopSandbox, } from "../../../tenant/sandboxes.js";
6
6
  import { readJson } from "../body.js";
7
7
  import { tenantRoute } from "../define.js";
8
+ import { pageQuery, sandboxPage } from "./reads.js";
8
9
  import { jsonResponse } from "../respond.js";
9
10
  /** Reading a sandbox: any caller that may use sessions, or change sandboxes. */
10
11
  const READ = {
11
12
  credentials: ["application", "subject", "token"],
12
13
  scopes: ["sessions:own", "sandboxes:write"],
13
- browser: true,
14
14
  };
15
15
  /** Creating or deleting one. */
16
16
  const WRITE = {
17
17
  credentials: ["application", "subject", "token"],
18
18
  scopes: ["sandboxes:write"],
19
- browser: true,
20
19
  };
21
20
  const json = (schema, description) => ({
22
21
  description,
@@ -50,17 +49,19 @@ export function sandboxRoutes(api) {
50
49
  path: "/v1/sandboxes",
51
50
  tags: ["Sandboxes"],
52
51
  summary: "List sandboxes",
53
- description: "Every sandbox with all the labels asked for. A subject token sees only the sandboxes its grants reach.",
52
+ description: "Every sandbox with all the labels asked for. A token caller sees only the sandboxes its grants reach.",
54
53
  request: {
55
- query: z.object({
54
+ query: pageQuery.extend({
56
55
  label: z
57
56
  .union([z.string(), z.array(z.string())])
58
57
  .optional()
59
58
  .meta({ description: "`key=value`; repeat it to require several labels" }),
60
59
  }),
61
60
  },
62
- responses: { 200: json(ListSandboxesResponse, "The sandboxes") },
63
- }, async (c) => jsonResponse(200, await listSandboxes(c.env.tenant, labelsOf(c.req.queries("label")), c.get("scope"))));
61
+ responses: { 200: json(z.union([ListSandboxesResponse, SandboxPage]), "The sandboxes; limit opts into pagination") },
62
+ }, async (c) => jsonResponse(200, c.req.query("limit") !== undefined
63
+ ? await sandboxPage(c.env.tenant, c.get("scope"), c.req.query(), labelsOf(c.req.queries("label")))
64
+ : await listSandboxes(c.env.tenant, labelsOf(c.req.queries("label")), c.get("scope"))));
64
65
  tenantRoute(api, WRITE, {
65
66
  method: "put",
66
67
  path: "/v1/sandboxes/{sandboxId}",
@@ -1,22 +1,22 @@
1
1
  import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
2
2
  import { z } from "zod";
3
- import { PutAgentRequestSchema, PutSessionRequestSchema } from "@nylorun/core/contracts";
3
+ import { PutAgentRequestSchema, PutSessionRequestSchema, SessionCommandSchema, } from "@nylorun/core/contracts";
4
4
  import { handleSessionSandboxTool, SandboxRouteError } from "../../../core/sandbox-routes.js";
5
- import { ListAgentsResponse, ListPublicAgentsResponse, ListSessionsResponse, SessionEvent, PutAgentRequest, PutAgentResponse, PutSessionRequest, SandboxToolOutcome, SessionItemsResponse, SessionView, StreamClosedFrame, } from "../../components.js";
6
- import { sandboxRouteDeps } from "../../../tenant/actions.js";
5
+ import { AcceptedResponse, ListAgentsResponse, ListPublicAgentsResponse, ListSessionsResponse, SessionPage, HistoryPage, SessionEvent, PutAgentRequest, PutAgentResponse, PutSessionRequest, SandboxToolOutcome, SessionCommand, SessionItemsResponse, SessionView, StreamClosedFrame, } from "../../components.js";
7
6
  import { accessOf, requireApplication } from "../../../tenant/auth.js";
7
+ import { command } from "../../../tenant/commands.js";
8
8
  import { loadSession, sessionOf } from "../../../tenant/context.js";
9
9
  import { fail } from "../../../tenant/http.js";
10
- import { sandboxGrantsOf } from "../../../tenant/sandboxes.js";
11
- import { readHistory, requestCursor, streamSessionEvents } from "../../../tenant/session-streams.js";
10
+ import { sandboxGrantsOf, sandboxRouteDeps } from "../../../tenant/sandboxes.js";
11
+ import { readHistory, requestCursor, streamHolderOf, streamSessionEvents } from "../../../tenant/session-streams.js";
12
12
  import { listAgentsPublic, listDefinitions, listSessions, putDefinition, putSession, sessionView, } from "../../../tenant/sessions.js";
13
13
  import { readJson } from "../body.js";
14
14
  import { tenantRoute } from "../define.js";
15
+ import { pageQuery, sessionPage, sessionPageQuery, parseQuery } from "./reads.js";
15
16
  import { jsonResponse } from "../respond.js";
16
17
  const OWN_SESSIONS = {
17
18
  credentials: ["application", "subject", "token"],
18
19
  scopes: ["sessions:own"],
19
- browser: true,
20
20
  };
21
21
  const json = (schema, description) => ({
22
22
  description,
@@ -46,24 +46,33 @@ async function sessionBelow(ctx, incoming, id, access) {
46
46
  return requestCursor(incoming, urlOf(incoming));
47
47
  }
48
48
  export function sessionRoutes(api) {
49
+ tenantRoute(api, OWN_SESSIONS, {
50
+ method: "post",
51
+ path: "/v1/sessions/{sessionId}/commands",
52
+ tags: ["Sessions"],
53
+ summary: "Send a command to a session",
54
+ description: "A message starts a turn; the others answer what the session waits on, or cancel. Idempotent on the command's `idempotencyKey`.",
55
+ request: { params: sessionId, body: body(SessionCommand) },
56
+ responses: {
57
+ 200: json(AcceptedResponse, "The command, accepted"),
58
+ 409: { description: "A turn is running, or the session does not wait on this" },
59
+ },
60
+ }, async (c) => jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c.req.raw)), c.get("scope"))));
49
61
  tenantRoute(api, {
50
- credentials: ["application", "subject", "token", "publishable"],
62
+ credentials: ["application", "subject", "token"],
51
63
  scopes: ["agents:read", "agents:write"],
52
- browser: true,
53
64
  }, {
54
65
  method: "get",
55
66
  path: "/v1/agents",
56
67
  tags: ["Agents"],
57
68
  summary: "List agents",
58
- description: "With an application key, every definition, manifest included. With a subject token or a publishable key, only the agents it may use, by name.",
69
+ description: "With an application key, every definition, manifest included. With a trusted issuer's token, only the agents it may use, by name.",
59
70
  responses: {
60
71
  200: json(z.union([ListAgentsResponse, ListPublicAgentsResponse]), "The agents"),
61
72
  },
62
73
  }, async (c) => {
63
74
  const scope = c.get("scope");
64
75
  const ctx = c.env.tenant;
65
- if (scope.kind === "publishable")
66
- return jsonResponse(200, await listAgentsPublic(ctx, scope.agents));
67
76
  return jsonResponse(200, scope.kind === "token"
68
77
  ? await listAgentsPublic(ctx, scope.agents)
69
78
  : await listDefinitions(ctx));
@@ -84,10 +93,12 @@ export function sessionRoutes(api) {
84
93
  tags: ["Sessions"],
85
94
  summary: "List sessions",
86
95
  description: "Acting for a person, only theirs.",
87
- request: { query: z.object({ agentId: z.string().optional() }) },
88
- responses: { 200: json(ListSessionsResponse, "The sessions") },
96
+ request: { query: sessionPageQuery },
97
+ responses: { 200: json(z.union([ListSessionsResponse, SessionPage]), "The sessions; limit opts into pagination") },
89
98
  }, async (c) => {
90
99
  const scope = c.get("scope");
100
+ if (c.req.query("limit") !== undefined)
101
+ return jsonResponse(200, await sessionPage(c.env.tenant, scope, c.req.query()));
91
102
  return jsonResponse(200, await listSessions(c.env.tenant, c.req.query("agentId") ?? null, accessOf(scope)));
92
103
  });
93
104
  tenantRoute(api, OWN_SESSIONS, {
@@ -107,7 +118,7 @@ export function sessionRoutes(api) {
107
118
  const request = PutSessionRequestSchema.parse(await readJson(c.req.raw));
108
119
  // Agent code may trust `info`: only an app server sets it.
109
120
  if (scope.kind === "token" && request.info !== undefined)
110
- fail(403, "A subject token cannot set session info", { code: "scope_required" });
121
+ fail(403, "A token caller cannot set session info", { code: "scope_required" });
111
122
  const grants = sandboxGrantsOf(scope);
112
123
  const session = await putSession(ctx, c.req.param("sessionId"), request, accessOf(scope), grants === undefined ? {} : { sandboxGrants: grants });
113
124
  return jsonResponse(200, await ctx.store.tx((t) => sessionView(t, session)));
@@ -133,26 +144,28 @@ export function sessionRoutes(api) {
133
144
  request: {
134
145
  params: sessionId,
135
146
  query: resume.extend({
147
+ limit: pageQuery.shape.limit,
136
148
  agent: z
137
149
  .string()
138
150
  .optional()
139
151
  .meta({ description: "Only the events of this agent (the root agent, or one it uses as a tool)" }),
140
152
  }),
141
153
  },
142
- responses: { 200: json(SessionItemsResponse, "Events up to now, and the cursor to continue") },
154
+ responses: { 200: json(z.union([SessionItemsResponse, HistoryPage]), "Events up to now; limit returns a bounded page and tail") },
143
155
  }, async (c) => {
144
156
  const scope = c.get("scope");
145
157
  const ctx = c.env.tenant;
146
158
  const id = c.req.param("sessionId");
147
159
  const cursor = await sessionBelow(ctx, c.env.incoming, id, accessOf(scope));
148
- return jsonResponse(200, await readHistory(ctx, id, cursor, c.req.query("agent")));
160
+ const { limit } = parseQuery(pageQuery.pick({ limit: true }), { limit: c.req.query("limit") });
161
+ return jsonResponse(200, await readHistory(ctx, id, cursor, c.req.query("agent"), limit));
149
162
  });
150
163
  tenantRoute(api, OWN_SESSIONS, {
151
164
  method: "get",
152
165
  path: "/v1/sessions/{sessionId}/events",
153
166
  tags: ["Sessions"],
154
167
  summary: "Follow a session's events",
155
- description: "Server-sent events, each with its type as `event` and its cursor as `id`, from the cursor on. Events are on the `nylorun.event/2` envelope, typed by `SessionEvent`; a client ignores a type it does not know. A subject token's stream ends with `event: nylorun.closed` when the token expires or the subject is revoked. A `: keepalive` comment every 15 seconds.",
168
+ description: "Server-sent events, each with its type as `event` and its cursor as `id`, from the cursor on. Events are on the `nylorun.event/2` envelope, typed by `SessionEvent`; a client ignores a type it does not know. A trusted issuer's token's stream ends with `event: nylorun.closed` when the token expires. A `: keepalive` comment every 15 seconds.",
156
169
  request: { params: sessionId, query: resume },
157
170
  responses: {
158
171
  200: {
@@ -170,9 +183,7 @@ export function sessionRoutes(api) {
170
183
  const { incoming, outgoing } = c.env;
171
184
  const id = c.req.param("sessionId");
172
185
  const cursor = await sessionBelow(ctx, incoming, id, accessOf(scope));
173
- await streamSessionEvents(ctx, incoming, outgoing, id, cursor, scope.kind === "token"
174
- ? { subject: scope.subject, epoch: scope.epoch, expiresAt: scope.expiresAt }
175
- : undefined);
186
+ await streamSessionEvents(ctx, incoming, outgoing, id, cursor, streamHolderOf(scope));
176
187
  return RESPONSE_ALREADY_SENT;
177
188
  });
178
189
  tenantRoute(api, { credentials: ["application"], scopes: "never" }, {
@@ -16,11 +16,8 @@ import { readJson } from "../body.js";
16
16
  import { pathSegments, tenantRoute } from "../define.js";
17
17
  import { jsonResponse } from "../respond.js";
18
18
  import { rearmPodTimers } from "../../../tenant/sandboxes.js";
19
- const settings = (scopes) => ({
20
- credentials: scopes === "never" ? ["application"] : ["application", "subject"],
21
- scopes,
22
- });
23
- const SETTINGS = settings(["tenant:settings"]);
19
+ /** Every Management API route (`/v1/tenant/*`): a management key, acting as itself (protocol 8). */
20
+ const MANAGEMENT = { credentials: ["management"], scopes: "never" };
24
21
  const json = (schema, description) => ({
25
22
  description,
26
23
  content: { "application/json": { schema } },
@@ -79,7 +76,7 @@ async function clusterOf(ctx) {
79
76
  }
80
77
  }
81
78
  export function tenantRoutes(api) {
82
- tenantRoute(api, SETTINGS, {
79
+ tenantRoute(api, MANAGEMENT, {
83
80
  method: "get",
84
81
  path: "/v1/tenant",
85
82
  tags: ["Tenant"],
@@ -103,7 +100,7 @@ export function tenantRoutes(api) {
103
100
  harness: harnessStatusOf(ctx),
104
101
  }));
105
102
  });
106
- tenantRoute(api, settings("never"), {
103
+ tenantRoute(api, MANAGEMENT, {
107
104
  method: "post",
108
105
  path: "/v1/tenant/reset",
109
106
  tags: ["Tenant"],
@@ -134,7 +131,7 @@ export function tenantRoutes(api) {
134
131
  ctx.closing = false;
135
132
  return jsonResponse(200, { ok: true });
136
133
  });
137
- tenantRoute(api, settings("never"), {
134
+ tenantRoute(api, MANAGEMENT, {
138
135
  method: "put",
139
136
  path: "/v1/tenant/config/seed",
140
137
  tags: ["Tenant"],
@@ -147,7 +144,7 @@ export function tenantRoutes(api) {
147
144
  const request = SeedTenantConfigRequestSchema.parse(await readJson(c.req.raw));
148
145
  return jsonResponse(200, await seedTenantConfig({ store: ctx.store, vault: ctx.vault, keys: ctx.keys }, request));
149
146
  });
150
- tenantRoute(api, settings(["tenant:settings", "agents:write"]), {
147
+ tenantRoute(api, MANAGEMENT, {
151
148
  method: "get",
152
149
  path: "/v1/tenant/models",
153
150
  tags: ["Tenant"],
@@ -157,14 +154,14 @@ export function tenantRoutes(api) {
157
154
  c.env.tenant;
158
155
  return jsonResponse(200, hostModelCatalog());
159
156
  });
160
- tenantRoute(api, SETTINGS, {
157
+ tenantRoute(api, MANAGEMENT, {
161
158
  method: "get",
162
159
  path: "/v1/tenant/sandbox",
163
160
  tags: ["Tenant"],
164
161
  summary: "Get the Tenant's sandbox configuration",
165
162
  responses: { 200: json(TenantSandboxView, "The backend in use and the configuration in force") },
166
163
  }, async (c) => jsonResponse(200, await sandboxView(c.env.tenant)));
167
- tenantRoute(api, SETTINGS, {
164
+ tenantRoute(api, MANAGEMENT, {
168
165
  method: "put",
169
166
  path: "/v1/tenant/sandbox",
170
167
  tags: ["Tenant"],
@@ -183,7 +180,7 @@ export function tenantRoutes(api) {
183
180
  await rearmPodTimers(ctx);
184
181
  return jsonResponse(200, await sandboxView(ctx));
185
182
  });
186
- tenantRoute(api, SETTINGS, {
183
+ tenantRoute(api, MANAGEMENT, {
187
184
  method: "get",
188
185
  path: "/v1/tenant/usage",
189
186
  tags: ["Tenant"],
@@ -212,7 +209,7 @@ export function tenantRoutes(api) {
212
209
  ...totals,
213
210
  });
214
211
  });
215
- tenantRoute(api, SETTINGS, {
212
+ tenantRoute(api, MANAGEMENT, {
216
213
  method: "get",
217
214
  path: "/v1/tenant/budgets",
218
215
  tags: ["Tenant"],
@@ -222,7 +219,7 @@ export function tenantRoutes(api) {
222
219
  const rows = await c.env.tenant.store.tx((t) => t.listModelBudgets());
223
220
  return jsonResponse(200, { budgets: rows.map(budgetView) });
224
221
  });
225
- tenantRoute(api, SETTINGS, {
222
+ tenantRoute(api, MANAGEMENT, {
226
223
  method: "put",
227
224
  path: "/v1/tenant/budgets",
228
225
  tags: ["Tenant"],
@@ -239,21 +236,21 @@ export function tenantRoutes(api) {
239
236
  });
240
237
  return jsonResponse(200, { budgets: rows.map(budgetView) });
241
238
  });
242
- tenantRoute(api, settings(["tenant:settings", "agents:write"]), {
239
+ tenantRoute(api, MANAGEMENT, {
243
240
  method: "get",
244
241
  path: "/v1/tenant/providers",
245
242
  tags: ["Tenant"],
246
243
  summary: "List the model providers the Tenant has credentials for",
247
244
  responses: { 200: json(ListProvidersResponse, "The providers") },
248
245
  }, async (c) => jsonResponse(200, await (c.env.tenant).vault.listHostProviders()));
249
- tenantRoute(api, SETTINGS, {
246
+ tenantRoute(api, MANAGEMENT, {
250
247
  method: "get",
251
248
  path: "/v1/tenant/model",
252
249
  tags: ["Tenant"],
253
250
  summary: "Get the Tenant's model",
254
251
  responses: { 200: json(HostModelView, "The model its sessions call") },
255
252
  }, async (c) => jsonResponse(200, await (c.env.tenant).vault.getHostModel()));
256
- tenantRoute(api, SETTINGS, {
253
+ tenantRoute(api, MANAGEMENT, {
257
254
  method: "put",
258
255
  path: "/v1/tenant/model",
259
256
  tags: ["Tenant"],
@@ -264,7 +261,7 @@ export function tenantRoutes(api) {
264
261
  const ctx = c.env.tenant;
265
262
  return jsonResponse(200, await ctx.keys.putHostModel(PutHostModelRequestSchema.parse(await readJson(c.req.raw))));
266
263
  });
267
- tenantRoute(api, SETTINGS, {
264
+ tenantRoute(api, MANAGEMENT, {
268
265
  method: "put",
269
266
  path: "/v1/tenant/model/selection",
270
267
  tags: ["Tenant"],
@@ -1,8 +1,18 @@
1
1
  /**
2
- * Vaults and their credentials (`/v1/vaults/**`): secrets a session's tools use, owned by one
3
- * person. Acting for a subject reaches only the subject's own vaults, and never on another's
4
- * behalf.
2
+ * Vaults and their credentials (`/v1/tenant/vaults/**`, the Management API, protocol 8): secrets
3
+ * a session's tools use. Only a management key (or Studio's key, acting as itself) reaches these
4
+ * routes; an application key is `403 key_role_mismatch`, and nothing acts for a subject here. The
5
+ * installation's own vaults (`scope: "installation"`) attach to any session (`vaultIds`); a vault
6
+ * of one person (`ownerUserId`) attaches only to that person's sessions. A person's own
7
+ * credentials come from the operator's credential resolver (`vault/sources.ts`), not from these
8
+ * routes.
9
+ *
10
+ * MCP OAuth connect: a management key starts one into an installation vault (`/oauth/start`);
11
+ * the authorization server sends the browser back to the anonymous, unversioned
12
+ * `GET /v1/oauth/callback`. Both only route: the gateway's keys module does the OAuth.
5
13
  */
6
14
  import type { OpenAPIHono } from "@hono/zod-openapi";
7
15
  import type { TenantEnv } from "../app.js";
16
+ /** Where the authorization server sends the browser back. */
17
+ export declare const OAUTH_CALLBACK_PATH = "/v1/oauth/callback";
8
18
  export declare function vaultRoutes(api: OpenAPIHono<TenantEnv>): void;