@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.
- package/CHANGELOG.md +207 -0
- package/README.md +105 -63
- package/dist/adapters/execution/restate.d.ts +1 -3
- package/dist/adapters/execution/restate.js +4 -31
- package/dist/adapters/replication/pgoutput.js +1 -1
- package/dist/adapters/sandbox/local.js +1 -1
- package/dist/adapters/sandbox/virtual.js +1 -1
- package/dist/api/ag-ui/endpoint.js +1 -2
- package/dist/api/ag-ui/routes.d.ts +1 -1
- package/dist/api/ag-ui/routes.js +11 -13
- package/dist/api/ag-ui/translate.d.ts +1 -3
- package/dist/api/ag-ui/translate.js +2 -17
- package/dist/api/components.d.ts +4783 -2588
- package/dist/api/components.js +19 -21
- package/dist/api/gate/routes.d.ts +7 -8
- package/dist/api/gate/routes.js +58 -52
- package/dist/api/http/app.d.ts +6 -0
- package/dist/api/http/app.js +20 -14
- package/dist/api/http/define.d.ts +22 -13
- package/dist/api/http/define.js +43 -39
- package/dist/api/http/routes/access.d.ts +3 -19
- package/dist/api/http/routes/access.js +25 -184
- package/dist/api/http/routes/artifacts.js +3 -7
- package/dist/api/http/routes/files.d.ts +8 -0
- package/dist/api/http/routes/files.js +77 -0
- package/dist/api/http/routes/keys.d.ts +10 -0
- package/dist/api/http/routes/keys.js +64 -0
- package/dist/api/http/routes/me.d.ts +13 -0
- package/dist/api/http/routes/me.js +48 -0
- package/dist/api/http/routes/reads.d.ts +66 -0
- package/dist/api/http/routes/reads.js +127 -0
- package/dist/api/http/routes/sandboxes.d.ts +2 -2
- package/dist/api/http/routes/sandboxes.js +8 -7
- package/dist/api/http/routes/sessions.js +31 -20
- package/dist/api/http/routes/tenant.js +15 -18
- package/dist/api/http/routes/vaults.d.ts +13 -3
- package/dist/api/http/routes/vaults.js +136 -64
- package/dist/api/openapi.d.ts +10 -11
- package/dist/api/openapi.js +292 -68
- package/dist/artifacts/links.js +6 -6
- package/dist/artifacts/service.js +1 -1
- package/dist/core/flow-host.d.ts +44 -23
- package/dist/core/flow-host.js +88 -106
- package/dist/core/provider.d.ts +4 -1
- package/dist/core/provider.js +6 -1
- package/dist/core/sandbox-routes.d.ts +0 -9
- package/dist/core/sandbox-routes.js +2 -21
- package/dist/execution/memory.d.ts +4 -5
- package/dist/execution/memory.js +11 -25
- package/dist/execution/types.d.ts +3 -33
- package/dist/execution/types.js +2 -12
- package/dist/gates/http-tool.d.ts +86 -0
- package/dist/gates/http-tool.js +197 -0
- package/dist/gates/mcp-handler.d.ts +3 -0
- package/dist/gates/mcp-handler.js +6 -4
- package/dist/gates/meter.d.ts +2 -2
- package/dist/gates/meter.js +6 -0
- package/dist/gates/tenant-vaults.d.ts +19 -14
- package/dist/gates/tenant-vaults.js +17 -12
- package/dist/gates/tool-calls.d.ts +7 -15
- package/dist/gates/tool-calls.js +4 -4
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +23 -47
- package/dist/gates/tool-contract.d.ts +27 -39
- package/dist/gates/tool-contract.js +30 -21
- package/dist/gates/tool-gate.d.ts +25 -24
- package/dist/gates/tool-gate.js +11 -7
- package/dist/harness/calls.d.ts +10 -4
- package/dist/harness/calls.js +15 -6
- package/dist/harness/executors.d.ts +1 -1
- package/dist/harness/executors.js +17 -12
- package/dist/harness/main.d.ts +2 -2
- package/dist/harness/main.js +5 -13
- package/dist/harness/service.d.ts +4 -5
- package/dist/harness/service.js +9 -2
- package/dist/harness-api/in-process.js +2 -2
- package/dist/harness-api/record.d.ts +6 -3
- package/dist/harness-api/record.js +65 -72
- package/dist/harness-api/server.d.ts +1 -9
- package/dist/harness-api/server.js +19 -17
- package/dist/harness-api/start.js +1 -5
- package/dist/harness-api/workspace.js +2 -3
- package/dist/harness-api/ws-server.js +1 -1
- package/dist/host/app.d.ts +2 -10
- package/dist/host/app.js +24 -44
- package/dist/host/config-for.d.ts +5 -5
- package/dist/host/config-for.js +2 -3
- package/dist/host/config.d.ts +0 -11
- package/dist/host/create-host.d.ts +3 -40
- package/dist/host/create-host.js +18 -91
- package/dist/host/environment.d.ts +0 -3
- package/dist/host/environment.js +0 -9
- package/dist/host/gates.d.ts +4 -1
- package/dist/host/gates.js +9 -3
- package/dist/host/http.d.ts +1 -2
- package/dist/host/http.js +1 -7
- package/dist/host/index.d.ts +2 -2
- package/dist/host/index.js +2 -2
- package/dist/host/main.js +55 -40
- package/dist/host/operate.d.ts +29 -0
- package/dist/host/operate.js +223 -0
- package/dist/host/stack-config.d.ts +23 -20
- package/dist/host/stack-config.js +28 -44
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/infra/workers.d.ts +1 -1
- package/dist/keys/client.js +9 -1
- package/dist/keys/keys.d.ts +12 -2
- package/dist/keys/keys.js +10 -2
- package/dist/management-openapi.json +6491 -0
- package/dist/mcp/connect.d.ts +8 -4
- package/dist/mcp/connect.js +8 -27
- package/dist/mcp/pool.d.ts +6 -9
- package/dist/mcp/pool.js +8 -18
- package/dist/mcp/snapshot.d.ts +8 -2
- package/dist/mcp/snapshot.js +29 -2
- package/dist/media.d.ts +1 -1
- package/dist/media.js +1 -1
- package/dist/model/pi-model.js +3 -0
- package/dist/openapi.json +6228 -15615
- package/dist/reads/access.d.ts +4 -0
- package/dist/reads/access.js +12 -0
- package/dist/reads/cursor.d.ts +11 -0
- package/dist/reads/cursor.js +80 -0
- package/dist/reads/types.d.ts +25 -0
- package/dist/reads/types.js +1 -0
- package/dist/sandbox/manager.d.ts +16 -0
- package/dist/sandbox/manager.js +40 -0
- package/dist/sandbox/session-sandbox.js +13 -3
- package/dist/sandbox/skills.d.ts +13 -0
- package/dist/sandbox/skills.js +46 -0
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/store/ownership.d.ts +9 -0
- package/dist/store/ownership.js +13 -0
- package/dist/store/postgres/connect.d.ts +2 -0
- package/dist/store/postgres/connect.js +29 -0
- package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
- package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
- package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
- package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
- package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
- package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
- package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +49 -0
- package/dist/store/postgres/reads.d.ts +3 -0
- package/dist/store/postgres/reads.js +272 -0
- package/dist/store/postgres/schema.d.ts +423 -594
- package/dist/store/postgres/schema.js +84 -101
- package/dist/store/postgres/store.js +96 -248
- package/dist/store/postgres/tenant.d.ts +34 -2
- package/dist/store/postgres/tenant.js +16 -32
- package/dist/store/types.d.ts +44 -119
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +0 -7
- package/dist/streams/control.js +0 -13
- package/dist/streams/types.d.ts +2 -20
- package/dist/streams/types.js +1 -1
- package/dist/tenant/advance.d.ts +3 -3
- package/dist/tenant/advance.js +10 -7
- package/dist/tenant/auth.d.ts +24 -15
- package/dist/tenant/auth.js +44 -55
- package/dist/tenant/cause.d.ts +3 -3
- package/dist/tenant/cause.js +1 -1
- package/dist/tenant/commands.d.ts +2 -18
- package/dist/tenant/commands.js +38 -179
- package/dist/tenant/context.d.ts +33 -45
- package/dist/tenant/definition-files.d.ts +24 -0
- package/dist/tenant/definition-files.js +103 -0
- package/dist/tenant/effects.d.ts +20 -18
- package/dist/tenant/effects.js +59 -133
- package/dist/tenant/ephemeral.d.ts +21 -8
- package/dist/tenant/ephemeral.js +11 -12
- package/dist/tenant/host-token.js +5 -5
- package/dist/tenant/identity-file.d.ts +75 -0
- package/dist/tenant/identity-file.js +202 -0
- package/dist/tenant/issuers.d.ts +48 -0
- package/dist/tenant/issuers.js +311 -0
- package/dist/tenant/jwt.js +3 -3
- package/dist/tenant/module.d.ts +1 -4
- package/dist/tenant/module.js +3 -12
- package/dist/tenant/operator-keys.d.ts +44 -0
- package/dist/tenant/operator-keys.js +87 -0
- package/dist/tenant/outbound.d.ts +40 -1
- package/dist/tenant/outbound.js +167 -18
- package/dist/tenant/paths.d.ts +1 -1
- package/dist/tenant/paths.js +1 -3
- package/dist/tenant/principals.d.ts +13 -13
- package/dist/tenant/principals.js +35 -36
- package/dist/tenant/run-token.js +6 -6
- package/dist/tenant/runtime.d.ts +5 -3
- package/dist/tenant/runtime.js +29 -15
- package/dist/tenant/sandboxes.d.ts +9 -6
- package/dist/tenant/sandboxes.js +16 -5
- package/dist/tenant/scheduler.d.ts +1 -5
- package/dist/tenant/scheduler.js +2 -5
- package/dist/tenant/session-streams.d.ts +8 -11
- package/dist/tenant/session-streams.js +48 -32
- package/dist/tenant/session.d.ts +8 -41
- package/dist/tenant/session.js +0 -37
- package/dist/tenant/sessions.d.ts +2 -2
- package/dist/tenant/sessions.js +17 -9
- package/dist/tenant/signing-keys.d.ts +3 -2
- package/dist/tenant/signing-keys.js +5 -5
- package/dist/tenant/skill-tool.d.ts +20 -0
- package/dist/tenant/skill-tool.js +72 -0
- package/dist/tenant/slim.d.ts +1 -1
- package/dist/tenant/slim.js +1 -1
- package/dist/tenant/status.js +7 -12
- package/dist/tenant/store-pg.d.ts +15 -0
- package/dist/tenant/store-pg.js +7 -3
- package/dist/tenant/streams.d.ts +3 -11
- package/dist/tenant/streams.js +2 -29
- package/dist/tenant/sweep.js +0 -5
- package/dist/tenant/transcript.d.ts +2 -2
- package/dist/tenant/transcript.js +2 -2
- package/dist/tenant/types.d.ts +33 -19
- package/dist/tenant/worker.d.ts +1 -3
- package/dist/tenant/worker.js +3 -8
- package/dist/vault/crypto.d.ts +7 -0
- package/dist/vault/crypto.js +16 -0
- package/dist/vault/oauth.d.ts +55 -0
- package/dist/vault/oauth.js +186 -0
- package/dist/vault/service.d.ts +54 -7
- package/dist/vault/service.js +261 -17
- package/dist/vault/sources.d.ts +60 -0
- package/dist/vault/sources.js +196 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -4
- package/dist/admin-openapi.json +0 -604
- package/dist/api/http/routes/actions.d.ts +0 -8
- package/dist/api/http/routes/actions.js +0 -76
- package/dist/api/http/routes/endpoints.d.ts +0 -7
- package/dist/api/http/routes/endpoints.js +0 -100
- package/dist/host/admin-api.d.ts +0 -23
- package/dist/host/admin-api.js +0 -74
- package/dist/host/cors.d.ts +0 -27
- package/dist/host/cors.js +0 -64
- package/dist/mcp/stdio.d.ts +0 -17
- package/dist/mcp/stdio.js +0 -82
- package/dist/tenant/access-policy.d.ts +0 -30
- package/dist/tenant/access-policy.js +0 -52
- package/dist/tenant/actions.d.ts +0 -14
- package/dist/tenant/actions.js +0 -29
- package/dist/tenant/browser.d.ts +0 -15
- package/dist/tenant/browser.js +0 -39
- package/dist/tenant/delivery-token.d.ts +0 -41
- package/dist/tenant/delivery-token.js +0 -104
- package/dist/tenant/delivery.d.ts +0 -62
- package/dist/tenant/delivery.js +0 -481
- package/dist/tenant/endpoints.d.ts +0 -67
- package/dist/tenant/endpoints.js +0 -90
- package/dist/tenant/subject-limits.d.ts +0 -16
- package/dist/tenant/subject-limits.js +0 -45
- package/dist/tenant/tokens.d.ts +0 -14
- 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
|
|
5
|
-
* the ids its
|
|
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
|
|
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:
|
|
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,
|
|
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"
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
20
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
3
|
-
*
|
|
4
|
-
*
|
|
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;
|