@nylorun/runtime 0.12.0-beta → 0.14.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 +361 -0
- package/README.md +69 -15
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +38 -8
- package/dist/adapters/replication/pgoutput.d.ts +16 -0
- package/dist/adapters/replication/pgoutput.js +169 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +1071 -0
- package/dist/api/a2a/card.d.ts +15 -0
- package/dist/api/a2a/card.js +24 -0
- package/dist/api/a2a/endpoint.d.ts +8 -0
- package/dist/api/a2a/endpoint.js +81 -0
- package/dist/api/a2a/protocol.d.ts +144 -0
- package/dist/api/a2a/protocol.js +188 -0
- package/dist/api/a2a/routes.d.ts +19 -0
- package/dist/api/a2a/routes.js +289 -0
- package/dist/api/a2a/tasks.d.ts +50 -0
- package/dist/api/a2a/tasks.js +186 -0
- package/dist/api/ag-ui/endpoint.d.ts +8 -0
- package/dist/api/ag-ui/endpoint.js +126 -0
- package/dist/api/ag-ui/history.d.ts +7 -0
- package/dist/api/ag-ui/history.js +60 -0
- package/dist/api/ag-ui/routes.d.ts +32 -0
- package/dist/api/ag-ui/routes.js +305 -0
- package/dist/api/ag-ui/session-id.d.ts +1 -0
- package/dist/api/ag-ui/session-id.js +12 -0
- package/dist/api/ag-ui/sse.d.ts +9 -0
- package/dist/api/ag-ui/sse.js +7 -0
- package/dist/api/ag-ui/translate.d.ts +38 -0
- package/dist/api/ag-ui/translate.js +222 -0
- package/dist/api/components.d.ts +7825 -0
- package/dist/api/components.js +83 -0
- package/dist/api/http/app.d.ts +18 -0
- package/dist/api/http/app.js +58 -0
- package/dist/api/http/body.d.ts +4 -0
- package/dist/api/http/body.js +37 -0
- package/dist/api/http/define.d.ts +39 -0
- package/dist/api/http/define.js +130 -0
- package/dist/api/http/respond.d.ts +9 -0
- package/dist/api/http/respond.js +44 -0
- package/dist/api/http/routes/access.d.ts +24 -0
- package/dist/api/http/routes/access.js +259 -0
- package/dist/api/http/routes/actions.d.ts +8 -0
- package/dist/api/http/routes/actions.js +76 -0
- package/dist/api/http/routes/endpoints.d.ts +7 -0
- package/dist/api/http/routes/endpoints.js +100 -0
- package/dist/api/http/routes/sessions.d.ts +3 -0
- package/dist/api/http/routes/sessions.js +205 -0
- package/dist/api/http/routes/tenant.d.ts +10 -0
- package/dist/api/http/routes/tenant.js +172 -0
- package/dist/api/http/routes/vaults.d.ts +8 -0
- package/dist/api/http/routes/vaults.js +167 -0
- package/dist/api/openapi.d.ts +19 -0
- package/dist/api/openapi.js +138 -0
- package/dist/api/route.d.ts +12 -0
- package/dist/api/route.js +4 -0
- package/dist/core/bearer.d.ts +8 -0
- package/dist/core/bearer.js +11 -0
- package/dist/core/flow-host.d.ts +7 -15
- package/dist/core/flow-host.js +19 -41
- package/dist/core/sandbox-routes.d.ts +12 -7
- package/dist/core/sandbox-routes.js +24 -25
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +33 -13
- package/dist/execution/types.d.ts +38 -6
- package/dist/execution/types.js +12 -3
- package/dist/host/admin-api.d.ts +22 -0
- package/dist/host/admin-api.js +178 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +185 -0
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- package/dist/host/config.d.ts +11 -0
- package/dist/host/cors.d.ts +27 -0
- package/dist/host/cors.js +64 -0
- package/dist/host/create-host.d.ts +31 -3
- package/dist/host/create-host.js +168 -327
- package/dist/host/http.d.ts +14 -3
- package/dist/host/http.js +64 -23
- package/dist/host/main.js +55 -4
- package/dist/host/stack-config.d.ts +20 -0
- package/dist/host/stack-config.js +55 -0
- package/dist/mcp/pool.d.ts +19 -1
- package/dist/mcp/pool.js +60 -10
- package/dist/model/catalog.d.ts +2 -10
- package/dist/model/classify.d.ts +13 -0
- package/dist/model/classify.js +61 -0
- package/dist/model/models.d.ts +10 -1
- package/dist/model/models.js +14 -6
- package/dist/model/pi-model.d.ts +17 -2
- package/dist/model/pi-model.js +366 -222
- package/dist/openapi.json +25696 -0
- package/dist/sandbox/manager.d.ts +13 -1
- package/dist/sandbox/manager.js +8 -11
- package/dist/sandbox/resolve.d.ts +33 -0
- package/dist/sandbox/resolve.js +81 -0
- package/dist/sandbox/session-sandbox.d.ts +11 -0
- package/dist/sandbox/session-sandbox.js +51 -0
- package/dist/sandbox/share.d.ts +14 -2
- package/dist/sandbox/share.js +33 -3
- package/dist/sandbox/tenant-config.d.ts +37 -0
- package/dist/sandbox/tenant-config.js +51 -0
- package/dist/sandbox/tools.d.ts +7 -9
- package/dist/sandbox/tools.js +11 -3
- package/dist/sandbox/types.d.ts +8 -2
- package/dist/store/event.d.ts +19 -0
- package/dist/store/event.js +49 -0
- package/dist/store/memory.d.ts +20 -8
- package/dist/store/memory.js +265 -100
- package/dist/store/postgres/connect.d.ts +13 -0
- package/dist/store/postgres/connect.js +5 -0
- package/dist/store/postgres/migrations/003_subject_tokens.d.ts +8 -0
- package/dist/store/postgres/migrations/003_subject_tokens.js +42 -0
- package/dist/store/postgres/migrations/004_publishable_keys.d.ts +6 -0
- package/dist/store/postgres/migrations/004_publishable_keys.js +18 -0
- package/dist/store/postgres/migrations/005_action_endpoints.d.ts +6 -0
- package/dist/store/postgres/migrations/005_action_endpoints.js +32 -0
- package/dist/store/postgres/migrations/006_drop_executors.d.ts +11 -0
- package/dist/store/postgres/migrations/006_drop_executors.js +32 -0
- package/dist/store/postgres/migrations/007_event_time.d.ts +7 -0
- package/dist/store/postgres/migrations/007_event_time.js +14 -0
- package/dist/store/postgres/migrations/008_durable_streams.d.ts +13 -0
- package/dist/store/postgres/migrations/008_durable_streams.js +35 -0
- package/dist/store/postgres/migrations/index.d.ts +5 -1
- package/dist/store/postgres/migrations/index.js +22 -2
- package/dist/store/postgres/migrations/shared/index.d.ts +31 -0
- package/dist/store/postgres/migrations/shared/index.js +92 -0
- package/dist/store/postgres/record.d.ts +11 -0
- package/dist/store/postgres/record.js +64 -0
- package/dist/store/postgres/store.js +337 -95
- package/dist/store/postgres/tenants.js +14 -0
- package/dist/store/types.d.ts +176 -81
- package/dist/streams/basin.d.ts +18 -1
- package/dist/streams/basin.js +34 -2
- package/dist/streams/control.d.ts +18 -0
- package/dist/streams/control.js +29 -0
- package/dist/streams/memory.d.ts +3 -1
- package/dist/streams/memory.js +14 -8
- package/dist/streams/relay/core.d.ts +35 -0
- package/dist/streams/relay/core.js +366 -0
- package/dist/streams/relay/memory.d.ts +69 -0
- package/dist/streams/relay/memory.js +176 -0
- package/dist/streams/relay/types.d.ts +77 -0
- package/dist/streams/relay/types.js +7 -0
- package/dist/streams/types.d.ts +66 -36
- package/dist/streams/types.js +18 -53
- package/dist/tenant/access-policy.d.ts +30 -0
- package/dist/tenant/access-policy.js +52 -0
- package/dist/tenant/actions.d.ts +10 -149
- package/dist/tenant/actions.js +5 -167
- package/dist/tenant/advance.js +32 -0
- package/dist/tenant/auth.d.ts +19 -17
- package/dist/tenant/auth.js +90 -93
- package/dist/tenant/browser.d.ts +15 -0
- package/dist/tenant/browser.js +39 -0
- package/dist/tenant/commands.d.ts +18 -2
- package/dist/tenant/commands.js +121 -129
- package/dist/tenant/context.d.ts +75 -18
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +105 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +476 -0
- package/dist/tenant/effects.d.ts +5 -0
- package/dist/tenant/effects.js +138 -48
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +6 -0
- package/dist/tenant/ephemeral.js +60 -36
- package/dist/tenant/http.d.ts +8 -9
- package/dist/tenant/http.js +13 -27
- package/dist/tenant/jwt.d.ts +16 -0
- package/dist/tenant/jwt.js +50 -0
- package/dist/tenant/module.d.ts +3 -0
- package/dist/tenant/module.js +6 -12
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/pool.d.ts +0 -2
- package/dist/tenant/pool.js +0 -18
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +10 -20
- package/dist/tenant/runtime.js +59 -42
- package/dist/tenant/scheduler.d.ts +7 -2
- package/dist/tenant/scheduler.js +7 -3
- package/dist/tenant/session-streams.d.ts +125 -0
- package/dist/tenant/session-streams.js +399 -0
- package/dist/tenant/sessions.d.ts +28 -4
- package/dist/tenant/sessions.js +118 -24
- package/dist/tenant/signing-keys.d.ts +55 -0
- package/dist/tenant/signing-keys.js +172 -0
- package/dist/tenant/slim.d.ts +8 -0
- package/dist/tenant/slim.js +21 -0
- package/dist/tenant/status.d.ts +0 -3
- package/dist/tenant/status.js +19 -12
- package/dist/tenant/streams.d.ts +60 -67
- package/dist/tenant/streams.js +140 -208
- package/dist/tenant/subject-limits.d.ts +16 -0
- package/dist/tenant/subject-limits.js +45 -0
- package/dist/tenant/sweep.d.ts +1 -10
- package/dist/tenant/sweep.js +29 -69
- package/dist/tenant/tokens.d.ts +14 -0
- package/dist/tenant/tokens.js +178 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +29 -7
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +9 -3
- package/dist/vault/service.d.ts +18 -2
- package/dist/vault/service.js +57 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +20 -8
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- package/dist/tenant/routes-tenant.d.ts +0 -12
- package/dist/tenant/routes-tenant.js +0 -137
- package/dist/tenant/routes.d.ts +0 -9
- package/dist/tenant/routes.js +0 -151
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Tenant's own settings (`/v1/tenant/**`): its status, reset, a first configuration, the
|
|
3
|
+
* model it calls and the sandboxes its sessions get.
|
|
4
|
+
*
|
|
5
|
+
* A reset that deletes sessions moves the Tenant to a new basin generation, so a session
|
|
6
|
+
* created again starts in an empty basin; the old basin is deleted after a grace period.
|
|
7
|
+
*/
|
|
8
|
+
import type { OpenAPIHono } from "@hono/zod-openapi";
|
|
9
|
+
import type { TenantEnv } from "../app.js";
|
|
10
|
+
export declare function tenantRoutes(api: OpenAPIHono<TenantEnv>): void;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PutHostModelRequestSchema, PutTenantSandboxRequestSchema, ResetTenantRequestSchema, SeedTenantConfigRequestSchema, SelectHostModelRequestSchema, } from "@nylorun/core/contracts";
|
|
3
|
+
import { HostModelCatalog, HostModelView, ListProvidersResponse, PutHostModelRequest, PutTenantSandboxRequest, ResetTenantRequest, ResetTenantResponse, SeedTenantConfigRequest, SeedTenantConfigResponse, SelectHostModelRequest, TenantSandboxView, TenantStatus, } from "../../components.js";
|
|
4
|
+
import { hostModelCatalog } from "../../../model/catalog.js";
|
|
5
|
+
import { sandboxConfigErrors } from "../../../sandbox/resolve.js";
|
|
6
|
+
import { effectiveSandboxConfig, readSandboxConfig, writeSandboxConfig, } from "../../../sandbox/tenant-config.js";
|
|
7
|
+
import { fail } from "../../../tenant/http.js";
|
|
8
|
+
import { clearObservers } from "../../../tenant/session-streams.js";
|
|
9
|
+
import { usesFixtureModel } from "../../../tenant/model-setting.js";
|
|
10
|
+
import { resetTenant } from "../../../tenant/reset.js";
|
|
11
|
+
import { clearWork, drain } from "../../../tenant/scheduler.js";
|
|
12
|
+
import { buildTenantStatus, seedTenantConfig } from "../../../tenant/status.js";
|
|
13
|
+
import { tenantReset, streamsStatus, } from "../../../tenant/streams.js";
|
|
14
|
+
import { readJson } from "../body.js";
|
|
15
|
+
import { pathSegments, tenantRoute } from "../define.js";
|
|
16
|
+
import { jsonResponse } from "../respond.js";
|
|
17
|
+
const settings = (scopes) => ({
|
|
18
|
+
credentials: scopes === "never" ? ["application"] : ["application", "subject"],
|
|
19
|
+
scopes,
|
|
20
|
+
});
|
|
21
|
+
const SETTINGS = settings(["tenant:settings"]);
|
|
22
|
+
const json = (schema, description) => ({
|
|
23
|
+
description,
|
|
24
|
+
content: { "application/json": { schema } },
|
|
25
|
+
});
|
|
26
|
+
const body = (schema) => ({
|
|
27
|
+
required: true,
|
|
28
|
+
content: { "application/json": { schema } },
|
|
29
|
+
});
|
|
30
|
+
/** `GET /v1/tenant/sandbox`: the backend report and the configuration with defaults applied. */
|
|
31
|
+
async function sandboxView(ctx) {
|
|
32
|
+
const config = await ctx.store.tx((t) => readSandboxConfig(t));
|
|
33
|
+
return { ...(await ctx.sandbox.report()), config: effectiveSandboxConfig(config) };
|
|
34
|
+
}
|
|
35
|
+
export function tenantRoutes(api) {
|
|
36
|
+
tenantRoute(api, SETTINGS, {
|
|
37
|
+
method: "get",
|
|
38
|
+
path: "/v1/tenant",
|
|
39
|
+
tags: ["Tenant"],
|
|
40
|
+
summary: "Get the Tenant's status",
|
|
41
|
+
responses: { 200: json(TenantStatus, "Readiness, model, sandboxes, streams and counts") },
|
|
42
|
+
}, async (c) => {
|
|
43
|
+
const ctx = c.env.tenant;
|
|
44
|
+
const { vault } = ctx;
|
|
45
|
+
return jsonResponse(200, await buildTenantStatus({
|
|
46
|
+
envelope: ctx.envelope,
|
|
47
|
+
config: ctx.config,
|
|
48
|
+
store: ctx.store,
|
|
49
|
+
vault,
|
|
50
|
+
sandbox: ctx.sandbox,
|
|
51
|
+
closing: ctx.closing || ctx.closed,
|
|
52
|
+
modelConfigured: !ctx.useVaultModel ||
|
|
53
|
+
(await ctx.store.tx((t) => usesFixtureModel(t))) ||
|
|
54
|
+
(await vault.getHostModel()).configured,
|
|
55
|
+
...(ctx.stuckInvocations ? { stuckInvocations: ctx.stuckInvocations } : {}),
|
|
56
|
+
streamsStatus: () => streamsStatus(ctx),
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
tenantRoute(api, settings("never"), {
|
|
60
|
+
method: "post",
|
|
61
|
+
path: "/v1/tenant/reset",
|
|
62
|
+
tags: ["Tenant"],
|
|
63
|
+
summary: "Reset the Tenant",
|
|
64
|
+
description: "Deletes its sessions, its sandboxes, or both, after draining or cancelling the turns in progress. The Tenant stays open.",
|
|
65
|
+
request: { body: body(ResetTenantRequest) },
|
|
66
|
+
responses: { 200: json(ResetTenantResponse, "Reset") },
|
|
67
|
+
}, async (c) => {
|
|
68
|
+
const ctx = c.env.tenant;
|
|
69
|
+
const request = ResetTenantRequestSchema.parse(await readJson(c.req.raw));
|
|
70
|
+
await drain(ctx, request.activeWork, 30_000);
|
|
71
|
+
// A sessions reset moves the Tenant to a new basin generation, so a session created
|
|
72
|
+
// again with the same id, during or after the reset, starts in an empty basin at 0.
|
|
73
|
+
const sessionsReset = request.scope !== "sandboxes";
|
|
74
|
+
await resetTenant({
|
|
75
|
+
store: ctx.store,
|
|
76
|
+
sandbox: ctx.sandbox,
|
|
77
|
+
paths: ctx.config.paths,
|
|
78
|
+
clearSessionState: () => {
|
|
79
|
+
clearWork(ctx);
|
|
80
|
+
clearObservers(ctx.sessionStreams);
|
|
81
|
+
},
|
|
82
|
+
}, request.scope);
|
|
83
|
+
if (sessionsReset)
|
|
84
|
+
await tenantReset(ctx);
|
|
85
|
+
// Reset leaves the Tenant open for new work.
|
|
86
|
+
ctx.closing = false;
|
|
87
|
+
return jsonResponse(200, { ok: true });
|
|
88
|
+
});
|
|
89
|
+
tenantRoute(api, settings("never"), {
|
|
90
|
+
method: "put",
|
|
91
|
+
path: "/v1/tenant/config/seed",
|
|
92
|
+
tags: ["Tenant"],
|
|
93
|
+
summary: "Seed the Tenant's configuration",
|
|
94
|
+
description: "Sets each setting the Tenant does not have yet; keeps the ones it has.",
|
|
95
|
+
request: { body: body(SeedTenantConfigRequest) },
|
|
96
|
+
responses: { 200: json(SeedTenantConfigResponse, "What was set, and what was kept") },
|
|
97
|
+
}, async (c) => {
|
|
98
|
+
const ctx = c.env.tenant;
|
|
99
|
+
const request = SeedTenantConfigRequestSchema.parse(await readJson(c.req.raw));
|
|
100
|
+
return jsonResponse(200, await seedTenantConfig({ store: ctx.store, vault: ctx.vault }, request));
|
|
101
|
+
});
|
|
102
|
+
tenantRoute(api, settings(["tenant:settings", "agents:write"]), {
|
|
103
|
+
method: "get",
|
|
104
|
+
path: "/v1/tenant/models",
|
|
105
|
+
tags: ["Tenant"],
|
|
106
|
+
summary: "List model providers and models",
|
|
107
|
+
responses: { 200: json(HostModelCatalog, "Provider and model names") },
|
|
108
|
+
}, async (c) => {
|
|
109
|
+
c.env.tenant;
|
|
110
|
+
return jsonResponse(200, hostModelCatalog());
|
|
111
|
+
});
|
|
112
|
+
tenantRoute(api, SETTINGS, {
|
|
113
|
+
method: "get",
|
|
114
|
+
path: "/v1/tenant/sandbox",
|
|
115
|
+
tags: ["Tenant"],
|
|
116
|
+
summary: "Get the Tenant's sandbox configuration",
|
|
117
|
+
responses: { 200: json(TenantSandboxView, "The backend in use and the configuration in force") },
|
|
118
|
+
}, async (c) => jsonResponse(200, await sandboxView(c.env.tenant)));
|
|
119
|
+
tenantRoute(api, SETTINGS, {
|
|
120
|
+
method: "put",
|
|
121
|
+
path: "/v1/tenant/sandbox",
|
|
122
|
+
tags: ["Tenant"],
|
|
123
|
+
summary: "Set the Tenant's sandbox configuration",
|
|
124
|
+
description: "What a session gets when it names no sandbox, and the limits every session's sandbox must fit.",
|
|
125
|
+
request: { body: body(PutTenantSandboxRequest) },
|
|
126
|
+
responses: { 200: json(TenantSandboxView, "The configuration now in force") },
|
|
127
|
+
}, async (c) => {
|
|
128
|
+
const ctx = c.env.tenant;
|
|
129
|
+
const { requestId: _requestId, ...config } = PutTenantSandboxRequestSchema.parse(await readJson(c.req.raw));
|
|
130
|
+
const errors = sandboxConfigErrors(effectiveSandboxConfig(config));
|
|
131
|
+
if (errors.length > 0)
|
|
132
|
+
fail(400, errors.join(" "));
|
|
133
|
+
await ctx.store.tx((t) => writeSandboxConfig(t, config));
|
|
134
|
+
return jsonResponse(200, await sandboxView(ctx));
|
|
135
|
+
});
|
|
136
|
+
tenantRoute(api, settings(["tenant:settings", "agents:write"]), {
|
|
137
|
+
method: "get",
|
|
138
|
+
path: "/v1/tenant/providers",
|
|
139
|
+
tags: ["Tenant"],
|
|
140
|
+
summary: "List the model providers the Tenant has credentials for",
|
|
141
|
+
responses: { 200: json(ListProvidersResponse, "The providers") },
|
|
142
|
+
}, async (c) => jsonResponse(200, await (c.env.tenant).vault.listHostProviders()));
|
|
143
|
+
tenantRoute(api, SETTINGS, {
|
|
144
|
+
method: "get",
|
|
145
|
+
path: "/v1/tenant/model",
|
|
146
|
+
tags: ["Tenant"],
|
|
147
|
+
summary: "Get the Tenant's model",
|
|
148
|
+
responses: { 200: json(HostModelView, "The model its sessions call") },
|
|
149
|
+
}, async (c) => jsonResponse(200, await (c.env.tenant).vault.getHostModel()));
|
|
150
|
+
tenantRoute(api, SETTINGS, {
|
|
151
|
+
method: "put",
|
|
152
|
+
path: "/v1/tenant/model",
|
|
153
|
+
tags: ["Tenant"],
|
|
154
|
+
summary: "Set a model provider's credentials",
|
|
155
|
+
request: { body: body(PutHostModelRequest) },
|
|
156
|
+
responses: { 200: json(HostModelView, "The model now in force") },
|
|
157
|
+
}, async (c) => {
|
|
158
|
+
const ctx = c.env.tenant;
|
|
159
|
+
return jsonResponse(200, await ctx.vault.putHostModel(PutHostModelRequestSchema.parse(await readJson(c.req.raw))));
|
|
160
|
+
});
|
|
161
|
+
tenantRoute(api, SETTINGS, {
|
|
162
|
+
method: "put",
|
|
163
|
+
path: "/v1/tenant/model/selection",
|
|
164
|
+
tags: ["Tenant"],
|
|
165
|
+
summary: "Choose the provider and model",
|
|
166
|
+
request: { body: body(SelectHostModelRequest) },
|
|
167
|
+
responses: { 200: json(HostModelView, "The model now in force") },
|
|
168
|
+
}, async (c) => {
|
|
169
|
+
const ctx = c.env.tenant;
|
|
170
|
+
return jsonResponse(200, await ctx.vault.selectHostModel(SelectHostModelRequestSchema.parse(await readJson(c.req.raw))));
|
|
171
|
+
});
|
|
172
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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.
|
|
5
|
+
*/
|
|
6
|
+
import type { OpenAPIHono } from "@hono/zod-openapi";
|
|
7
|
+
import type { TenantEnv } from "../app.js";
|
|
8
|
+
export declare function vaultRoutes(api: OpenAPIHono<TenantEnv>): void;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CreateCredentialRequestSchema, CreateVaultRequestSchema, RotateCredentialRequestSchema, } from "@nylorun/core/contracts";
|
|
3
|
+
import { CreateCredentialRequest, CreateVaultRequest, CredentialInfo, DeletedResponse, ListCredentialsResponse, ListVaultsResponse, RotateCredentialRequest, VaultInfo, } from "../../components.js";
|
|
4
|
+
import { ownerOf } from "../../../tenant/auth.js";
|
|
5
|
+
import { fail } from "../../../tenant/http.js";
|
|
6
|
+
import { readJson } from "../body.js";
|
|
7
|
+
import { pathSegments, tenantRoute } from "../define.js";
|
|
8
|
+
import { jsonResponse } from "../respond.js";
|
|
9
|
+
const OWN_VAULTS = {
|
|
10
|
+
credentials: ["application", "subject", "token"],
|
|
11
|
+
scopes: ["vaults:own"],
|
|
12
|
+
browser: true,
|
|
13
|
+
};
|
|
14
|
+
const json = (schema, description) => ({
|
|
15
|
+
description,
|
|
16
|
+
content: { "application/json": { schema } },
|
|
17
|
+
});
|
|
18
|
+
const body = (schema) => ({
|
|
19
|
+
required: true,
|
|
20
|
+
content: { "application/json": { schema } },
|
|
21
|
+
});
|
|
22
|
+
const vaultId = z.object({ vaultId: z.string() });
|
|
23
|
+
const credentialId = vaultId.extend({ credentialId: z.string() });
|
|
24
|
+
/**
|
|
25
|
+
* Who a vault route acts for: the subject, or no one in particular (an application key). Below
|
|
26
|
+
* a vault, the vault must be the subject's before anything else.
|
|
27
|
+
*/
|
|
28
|
+
async function ownerFor(c) {
|
|
29
|
+
const { vault } = c.env.tenant;
|
|
30
|
+
const owner = ownerOf(c.get("scope"));
|
|
31
|
+
const id = c.req.param("vaultId");
|
|
32
|
+
if (id !== undefined && owner !== undefined)
|
|
33
|
+
await vault.assertOwner(id, owner);
|
|
34
|
+
return owner;
|
|
35
|
+
}
|
|
36
|
+
export function vaultRoutes(api) {
|
|
37
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
38
|
+
method: "post",
|
|
39
|
+
path: "/v1/vaults",
|
|
40
|
+
tags: ["Vaults"],
|
|
41
|
+
summary: "Create a vault",
|
|
42
|
+
request: { body: body(CreateVaultRequest) },
|
|
43
|
+
responses: {
|
|
44
|
+
200: json(VaultInfo, "The vault"),
|
|
45
|
+
409: { description: "The idempotency key was used for another vault" },
|
|
46
|
+
},
|
|
47
|
+
}, async (c) => {
|
|
48
|
+
const owner = await ownerFor(c);
|
|
49
|
+
const request = CreateVaultRequestSchema.parse(await readJson(c.req.raw));
|
|
50
|
+
if (owner !== undefined && request.ownerUserId !== owner)
|
|
51
|
+
fail(403, "ownerUserId must be the subject");
|
|
52
|
+
return jsonResponse(200, await c.env.tenant.vault.createVault(request));
|
|
53
|
+
});
|
|
54
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
55
|
+
method: "get",
|
|
56
|
+
path: "/v1/vaults",
|
|
57
|
+
tags: ["Vaults"],
|
|
58
|
+
summary: "List a person's vaults",
|
|
59
|
+
request: {
|
|
60
|
+
query: z.object({
|
|
61
|
+
ownerUserId: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.meta({ description: "Whose vaults; required with an application key acting for no one" }),
|
|
65
|
+
}),
|
|
66
|
+
},
|
|
67
|
+
responses: { 200: json(ListVaultsResponse, "The vaults") },
|
|
68
|
+
}, async (c) => {
|
|
69
|
+
const owner = await ownerFor(c);
|
|
70
|
+
const ownerUserId = c.req.query("ownerUserId") ?? owner ?? fail(400, "ownerUserId is required");
|
|
71
|
+
if (owner !== undefined && ownerUserId !== owner)
|
|
72
|
+
fail(403, "ownerUserId must be the subject");
|
|
73
|
+
return jsonResponse(200, { vaults: await c.env.tenant.vault.listVaults(ownerUserId) });
|
|
74
|
+
});
|
|
75
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
76
|
+
method: "get",
|
|
77
|
+
path: "/v1/vaults/{vaultId}",
|
|
78
|
+
tags: ["Vaults"],
|
|
79
|
+
summary: "Get a vault",
|
|
80
|
+
request: { params: vaultId },
|
|
81
|
+
responses: { 200: json(VaultInfo, "The vault") },
|
|
82
|
+
}, async (c) => {
|
|
83
|
+
await ownerFor(c);
|
|
84
|
+
return jsonResponse(200, await c.env.tenant.vault.getVault(c.req.param("vaultId")));
|
|
85
|
+
});
|
|
86
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
87
|
+
method: "delete",
|
|
88
|
+
path: "/v1/vaults/{vaultId}",
|
|
89
|
+
tags: ["Vaults"],
|
|
90
|
+
summary: "Delete a vault and its credentials",
|
|
91
|
+
request: { params: vaultId },
|
|
92
|
+
responses: { 200: json(DeletedResponse, "Deleted") },
|
|
93
|
+
}, async (c) => {
|
|
94
|
+
await ownerFor(c);
|
|
95
|
+
return jsonResponse(200, await c.env.tenant.vault.deleteVault(c.req.param("vaultId")));
|
|
96
|
+
});
|
|
97
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
98
|
+
method: "post",
|
|
99
|
+
path: "/v1/vaults/{vaultId}/credentials",
|
|
100
|
+
tags: ["Vaults"],
|
|
101
|
+
summary: "Add a credential",
|
|
102
|
+
description: "A bearer token or OAuth tokens, bound to the URLs a tool may send them to. A subject token cannot store OAuth refresh credentials.",
|
|
103
|
+
request: { params: vaultId, body: body(CreateCredentialRequest) },
|
|
104
|
+
responses: { 200: json(CredentialInfo, "The credential, without its secret") },
|
|
105
|
+
}, async (c) => {
|
|
106
|
+
await ownerFor(c);
|
|
107
|
+
const request = CreateCredentialRequestSchema.parse(await readJson(c.req.raw));
|
|
108
|
+
// The Runtime calls a refresh credential's token endpoint itself: never for a browser.
|
|
109
|
+
if (c.get("scope").kind === "token" &&
|
|
110
|
+
request.auth.type === "oauth" &&
|
|
111
|
+
request.auth.refresh)
|
|
112
|
+
fail(403, "A subject token cannot store OAuth refresh credentials", {
|
|
113
|
+
code: "scope_required",
|
|
114
|
+
});
|
|
115
|
+
return jsonResponse(200, await c.env.tenant.vault.createCredential(c.req.param("vaultId"), request));
|
|
116
|
+
});
|
|
117
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
118
|
+
method: "get",
|
|
119
|
+
path: "/v1/vaults/{vaultId}/credentials",
|
|
120
|
+
tags: ["Vaults"],
|
|
121
|
+
summary: "List a vault's credentials",
|
|
122
|
+
request: { params: vaultId },
|
|
123
|
+
responses: { 200: json(ListCredentialsResponse, "The credentials, without their secrets") },
|
|
124
|
+
}, async (c) => {
|
|
125
|
+
await ownerFor(c);
|
|
126
|
+
return jsonResponse(200, {
|
|
127
|
+
credentials: await c.env.tenant.vault.listCredentials(c.req.param("vaultId")),
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
131
|
+
method: "get",
|
|
132
|
+
path: "/v1/vaults/{vaultId}/credentials/{credentialId}",
|
|
133
|
+
tags: ["Vaults"],
|
|
134
|
+
summary: "Get a credential",
|
|
135
|
+
request: { params: credentialId },
|
|
136
|
+
responses: { 200: json(CredentialInfo, "The credential, without its secret") },
|
|
137
|
+
}, async (c) => {
|
|
138
|
+
await ownerFor(c);
|
|
139
|
+
return jsonResponse(200, await c.env.tenant.vault.getCredential(c.req.param("vaultId"), c.req.param("credentialId")));
|
|
140
|
+
});
|
|
141
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
142
|
+
method: "post",
|
|
143
|
+
path: "/v1/vaults/{vaultId}/credentials/{credentialId}",
|
|
144
|
+
tags: ["Vaults"],
|
|
145
|
+
summary: "Rotate a credential's secret",
|
|
146
|
+
request: { params: credentialId, body: body(RotateCredentialRequest) },
|
|
147
|
+
responses: {
|
|
148
|
+
200: json(CredentialInfo, "The credential"),
|
|
149
|
+
409: { description: "A credential's type cannot change" },
|
|
150
|
+
},
|
|
151
|
+
}, async (c) => {
|
|
152
|
+
await ownerFor(c);
|
|
153
|
+
const request = RotateCredentialRequestSchema.parse(await readJson(c.req.raw));
|
|
154
|
+
return jsonResponse(200, await c.env.tenant.vault.rotateCredential(c.req.param("vaultId"), c.req.param("credentialId"), request));
|
|
155
|
+
});
|
|
156
|
+
tenantRoute(api, OWN_VAULTS, {
|
|
157
|
+
method: "delete",
|
|
158
|
+
path: "/v1/vaults/{vaultId}/credentials/{credentialId}",
|
|
159
|
+
tags: ["Vaults"],
|
|
160
|
+
summary: "Delete a credential",
|
|
161
|
+
request: { params: credentialId },
|
|
162
|
+
responses: { 200: json(DeletedResponse, "Deleted") },
|
|
163
|
+
}, async (c) => {
|
|
164
|
+
await ownerFor(c);
|
|
165
|
+
return jsonResponse(200, await c.env.tenant.vault.deleteCredential(c.req.param("vaultId"), c.req.param("credentialId")));
|
|
166
|
+
});
|
|
167
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Runtime's OpenAPI 3.2 documents, generated from the routes as declared for serving:
|
|
3
|
+
*
|
|
4
|
+
* - the Tenant API (`tenantDocument`): every Tenant route, with `/health`, `/ready` and
|
|
5
|
+
* `/openapi.json`. Served at `GET /openapi.json`, packed as `@nylorun/runtime/openapi.json`
|
|
6
|
+
* and attached to each release.
|
|
7
|
+
* - the Admin API (`adminDocument`): served at `GET /v1/admin/openapi.json` behind the admin
|
|
8
|
+
* key, packed as `@nylorun/runtime/admin-openapi.json`.
|
|
9
|
+
*
|
|
10
|
+
* Event streams are `text/event-stream` with an `itemSchema` (OpenAPI 3.2). Nothing here reads
|
|
11
|
+
* the environment: the same Runtime makes the same document.
|
|
12
|
+
*/
|
|
13
|
+
import { OpenApiGeneratorV32 } from "@asteasolutions/zod-to-openapi";
|
|
14
|
+
type OpenApiDocument = ReturnType<OpenApiGeneratorV32["generateDocument"]>;
|
|
15
|
+
/** The Tenant API's document. */
|
|
16
|
+
export declare function tenantDocument(): OpenApiDocument;
|
|
17
|
+
/** The Admin API's document. */
|
|
18
|
+
export declare function adminDocument(): OpenApiDocument;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Runtime's OpenAPI 3.2 documents, generated from the routes as declared for serving:
|
|
3
|
+
*
|
|
4
|
+
* - the Tenant API (`tenantDocument`): every Tenant route, with `/health`, `/ready` and
|
|
5
|
+
* `/openapi.json`. Served at `GET /openapi.json`, packed as `@nylorun/runtime/openapi.json`
|
|
6
|
+
* and attached to each release.
|
|
7
|
+
* - the Admin API (`adminDocument`): served at `GET /v1/admin/openapi.json` behind the admin
|
|
8
|
+
* key, packed as `@nylorun/runtime/admin-openapi.json`.
|
|
9
|
+
*
|
|
10
|
+
* Event streams are `text/event-stream` with an `itemSchema` (OpenAPI 3.2). Nothing here reads
|
|
11
|
+
* the environment: the same Runtime makes the same document.
|
|
12
|
+
*/
|
|
13
|
+
import { OpenAPIRegistry, OpenApiGeneratorV32, } from "@asteasolutions/zod-to-openapi";
|
|
14
|
+
import { HOST_PROTOCOL } from "@nylorun/core/compatibility";
|
|
15
|
+
import { HealthResponseSchema, ReadyResponseSchema } from "@nylorun/core/contracts";
|
|
16
|
+
import { createAdminApi } from "../host/admin-api.js";
|
|
17
|
+
import { RUNTIME_VERSION } from "../version.js";
|
|
18
|
+
import { tenantApi } from "./http/app.js";
|
|
19
|
+
const PROTOCOL = {
|
|
20
|
+
min: HOST_PROTOCOL.min,
|
|
21
|
+
max: HOST_PROTOCOL.max,
|
|
22
|
+
features: [...HOST_PROTOCOL.features],
|
|
23
|
+
};
|
|
24
|
+
const bearer = (description, bearerFormat) => ({
|
|
25
|
+
type: "http",
|
|
26
|
+
scheme: "bearer",
|
|
27
|
+
...(bearerFormat ? { bearerFormat } : {}),
|
|
28
|
+
description,
|
|
29
|
+
});
|
|
30
|
+
/** `/health`, `/ready` and `/openapi.json`: what every listener answers without a key. */
|
|
31
|
+
function hostRoutes() {
|
|
32
|
+
const registry = new OpenAPIRegistry();
|
|
33
|
+
const route = (config) => registry.registerPath(config);
|
|
34
|
+
route({
|
|
35
|
+
method: "get",
|
|
36
|
+
path: "/health",
|
|
37
|
+
tags: ["Host"],
|
|
38
|
+
summary: "Check the Runtime",
|
|
39
|
+
description: "The protocol range and features a client checks first. Refuses an `Origin`.",
|
|
40
|
+
responses: {
|
|
41
|
+
200: {
|
|
42
|
+
description: "The Runtime is up",
|
|
43
|
+
content: { "application/json": { schema: HealthResponseSchema.meta({ id: "Health" }) } },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
route({
|
|
48
|
+
method: "get",
|
|
49
|
+
path: "/ready",
|
|
50
|
+
tags: ["Host"],
|
|
51
|
+
summary: "Check the Runtime is ready",
|
|
52
|
+
description: "The listeners, Tenant discovery, Postgres, Restate and S2.",
|
|
53
|
+
responses: {
|
|
54
|
+
200: {
|
|
55
|
+
description: "Ready",
|
|
56
|
+
content: { "application/json": { schema: ReadyResponseSchema.meta({ id: "Ready" }) } },
|
|
57
|
+
},
|
|
58
|
+
503: { description: "Not ready: `checks` says what is not" },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
route({
|
|
62
|
+
method: "get",
|
|
63
|
+
path: "/openapi.json",
|
|
64
|
+
tags: ["Host"],
|
|
65
|
+
summary: "Get this document",
|
|
66
|
+
responses: { 200: { description: "The Tenant API's OpenAPI document" } },
|
|
67
|
+
});
|
|
68
|
+
return registry;
|
|
69
|
+
}
|
|
70
|
+
function tenantSchemes(registry) {
|
|
71
|
+
registry.registerComponent("securitySchemes", "applicationKey", bearer("An application key of the Tenant, or a key derived from the admin key. With `Nylorun-Subject` and `Nylorun-Scopes`, it acts for that person, narrowed to those scopes."));
|
|
72
|
+
registry.registerComponent("securitySchemes", "subjectToken", bearer("A subject token (`POST /v1/tokens`): one person and one role of the access policy, for at most 15 minutes.", "JWT"));
|
|
73
|
+
registry.registerComponent("securitySchemes", "deliveryToken", bearer("The delivery token the Runtime signs each Action delivery with (`Nylorun-Signature`), sent back by the Action endpoint. It reaches only that Action's heartbeat, result and sandbox routes, while that delivery is current, for at most 15 minutes.", "JWT"));
|
|
74
|
+
registry.registerComponent("securitySchemes", "publishableKey", {
|
|
75
|
+
type: "apiKey",
|
|
76
|
+
in: "header",
|
|
77
|
+
name: "Nylorun-Key",
|
|
78
|
+
description: "A publishable key, sent by a browser page on one of its origins. Public by design: it names the Tenant and grants the access policy's anonymous scopes.",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
let tenant;
|
|
82
|
+
let admin;
|
|
83
|
+
/** The Tenant API's document. */
|
|
84
|
+
export function tenantDocument() {
|
|
85
|
+
if (tenant)
|
|
86
|
+
return tenant;
|
|
87
|
+
const host = hostRoutes();
|
|
88
|
+
tenantSchemes(host);
|
|
89
|
+
tenant = new OpenApiGeneratorV32([
|
|
90
|
+
...host.definitions,
|
|
91
|
+
...tenantApi().openAPIRegistry.definitions,
|
|
92
|
+
]).generateDocument({
|
|
93
|
+
openapi: "3.2.0",
|
|
94
|
+
info: {
|
|
95
|
+
title: "Nylorun Runtime: Tenant API",
|
|
96
|
+
version: RUNTIME_VERSION,
|
|
97
|
+
description: "Agents, sessions and their events, vaults, access and the Tenant's settings, for one Tenant (`Nylorun-Tenant`). Every request sends `Nylorun-Protocol`. Who may call each operation is its `security` and its `x-nylorun-credentials`, `x-nylorun-scopes` (the subject scopes that reach it) and `x-nylorun-browser` fields.",
|
|
98
|
+
"x-nylorun-protocol": PROTOCOL,
|
|
99
|
+
},
|
|
100
|
+
servers: [
|
|
101
|
+
{
|
|
102
|
+
url: "{origin}",
|
|
103
|
+
description: "A Runtime: the local stack (`nylorun start`), or where yours runs",
|
|
104
|
+
variables: { origin: { default: "http://localhost:8787" } },
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
return tenant;
|
|
109
|
+
}
|
|
110
|
+
/** The Admin API's document. */
|
|
111
|
+
export function adminDocument() {
|
|
112
|
+
if (admin)
|
|
113
|
+
return admin;
|
|
114
|
+
// Only the routes' declarations are read: nothing is served from this app.
|
|
115
|
+
const api = createAdminApi({
|
|
116
|
+
module: undefined,
|
|
117
|
+
status: () => Promise.reject(new Error("Not served")),
|
|
118
|
+
shutdown: () => { },
|
|
119
|
+
});
|
|
120
|
+
api.openAPIRegistry.registerComponent("securitySchemes", "adminKey", bearer("The Host's admin key (`host-credentials.json`). Never a Tenant bearer."));
|
|
121
|
+
admin = new OpenApiGeneratorV32(api.openAPIRegistry.definitions).generateDocument({
|
|
122
|
+
openapi: "3.2.0",
|
|
123
|
+
info: {
|
|
124
|
+
title: "Nylorun Runtime: Admin API",
|
|
125
|
+
version: RUNTIME_VERSION,
|
|
126
|
+
description: "Tenants and the Host's status, with the admin key. Served on the operator listener when the Host has one; the public listener answers these routes with the opaque 404.",
|
|
127
|
+
"x-nylorun-protocol": PROTOCOL,
|
|
128
|
+
},
|
|
129
|
+
servers: [
|
|
130
|
+
{
|
|
131
|
+
url: "{origin}",
|
|
132
|
+
description: "The operator listener: the local stack's, or where yours runs",
|
|
133
|
+
variables: { origin: { default: "http://localhost:8788" } },
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
return admin;
|
|
138
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the Runtime declares an HTTP route: once, for both serving and the OpenAPI document.
|
|
3
|
+
*
|
|
4
|
+
* `app.openapi(route, handler)` from `@hono/zod-openapi` also validates the path, query and
|
|
5
|
+
* body before the handler runs, answering failures its own way. The Runtime's handlers parse
|
|
6
|
+
* where they always have (after the lookups and checks a rejection must come after), so their
|
|
7
|
+
* rejections keep their order and messages; `serveRoute` registers the route and serves it
|
|
8
|
+
* with no validators of its own.
|
|
9
|
+
*/
|
|
10
|
+
import type { OpenAPIHono, RouteConfig } from "@hono/zod-openapi";
|
|
11
|
+
import type { Env, Handler } from "hono";
|
|
12
|
+
export declare function serveRoute<E extends Env>(app: OpenAPIHono<E>, route: RouteConfig, handler: Handler<E>): void;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application keys are 256-bit CSPRNG values and the wire schema requires at least 16
|
|
3
|
+
* characters, so an unsalted digest is adequate to keep them out of the Session Store at rest.
|
|
4
|
+
* This is not a password KDF and must not be used for one.
|
|
5
|
+
*/
|
|
6
|
+
export declare const hashToken: (token: string) => string;
|
|
7
|
+
/** Mint a 32-byte hex application key for tests and CLI bootstrap. */
|
|
8
|
+
export declare function mintBearerToken(): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Application keys are 256-bit CSPRNG values and the wire schema requires at least 16
|
|
4
|
+
* characters, so an unsalted digest is adequate to keep them out of the Session Store at rest.
|
|
5
|
+
* This is not a password KDF and must not be used for one.
|
|
6
|
+
*/
|
|
7
|
+
export const hashToken = (token) => createHash("sha256").update(token, "utf8").digest("hex");
|
|
8
|
+
/** Mint a 32-byte hex application key for tests and CLI bootstrap. */
|
|
9
|
+
export function mintBearerToken() {
|
|
10
|
+
return randomBytes(32).toString("hex");
|
|
11
|
+
}
|
package/dist/core/flow-host.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ActionOutcome } from "@nylorun/core/contracts";
|
|
2
2
|
import type { JsonValue, WorkflowManifest } from "@nylorun/core/define";
|
|
3
3
|
import type { HostEffect } from "@nylorun/harness/run";
|
|
4
4
|
import type { Wake } from "../execution/types.js";
|
|
@@ -73,7 +73,7 @@ export type CascadeCancelPlan = {
|
|
|
73
73
|
/** Linked agent sessions to cancel, deepest path first. */
|
|
74
74
|
readonly agentSessionIds: string[];
|
|
75
75
|
readonly pendingActionIds: string[];
|
|
76
|
-
readonly
|
|
76
|
+
readonly deliveringActionIds: string[];
|
|
77
77
|
};
|
|
78
78
|
/** Wakes a session after commit (`DurableExecution.wake` through the Tenant context). */
|
|
79
79
|
type Schedule = (sessionId: string, wake: Wake) => void | Promise<void>;
|
|
@@ -83,7 +83,7 @@ export type FlowEffect = EffectDoc & {
|
|
|
83
83
|
outcome?: ActionOutcome;
|
|
84
84
|
error?: string;
|
|
85
85
|
};
|
|
86
|
-
/** Active agent turns + pending/
|
|
86
|
+
/** Active agent turns + pending/delivering actions for one workflow turn. */
|
|
87
87
|
export declare function countActiveFlowWork(t: Tx, workflowSessionId: string, turnId: string): Promise<number>;
|
|
88
88
|
/** The `commands` document key of a session command's idempotency key (`tenant/commands.ts`). */
|
|
89
89
|
export declare function commandKey(sessionId: string, idempotencyKey: string): string;
|
|
@@ -132,20 +132,11 @@ export declare function reconcilePendingAgentEffect(input: {
|
|
|
132
132
|
readonly effectId: string;
|
|
133
133
|
readonly schedule: Schedule;
|
|
134
134
|
}): Promise<boolean>;
|
|
135
|
-
/** Claimed `fn`/`verify` Actions, for the re-offer after a Tenant opens. */
|
|
136
|
-
export declare function claimedFnVerifyActions(t: Tx): Promise<Action[]>;
|
|
137
|
-
/**
|
|
138
|
-
* Re-offer one claimed `fn`/`verify` Action (SD-P7 / WF-C9): they are pure or repeat-safe, so
|
|
139
|
-
* a claim left by an executor of an earlier process is offered again at once rather than
|
|
140
|
-
* after its lease. The next claim bumps the generation, which fences a late result. Locks
|
|
141
|
-
* only the Action's session. Returns true when it re-offered.
|
|
142
|
-
*/
|
|
143
|
-
export declare function reofferFnVerifyClaim(t: Tx, actionId: string): Promise<boolean>;
|
|
144
135
|
/** Path depth for deepest-first cancel ordering. */
|
|
145
136
|
export declare function pathDepth(path: string): number;
|
|
146
137
|
/**
|
|
147
138
|
* Plan a cancel cascade for a workflow session: linked agents deepest first,
|
|
148
|
-
* then pending → cancelled and
|
|
139
|
+
* then pending → cancelled and delivering → uncertain actions (SD-P11 / WF-R53).
|
|
149
140
|
*/
|
|
150
141
|
export declare function planCancelCascade(input: {
|
|
151
142
|
readonly t: Tx;
|
|
@@ -155,7 +146,7 @@ export declare function planCancelCascade(input: {
|
|
|
155
146
|
/**
|
|
156
147
|
* Cancel sibling work under a Parallel/Map parent when one branch fails (PAR-R6).
|
|
157
148
|
* Agent turns listed in `agentSessionIds` are returned for the caller to cancel;
|
|
158
|
-
* pending actions → cancelled,
|
|
149
|
+
* pending actions → cancelled, delivering tool actions → uncertain.
|
|
159
150
|
* When `cancelEffectIds` is provided, those effects are marked cancelled and their
|
|
160
151
|
* paths are included in the sibling path set.
|
|
161
152
|
*/
|
|
@@ -169,7 +160,8 @@ export declare function cancelSiblingWork(input: {
|
|
|
169
160
|
readonly cancelEffectIds?: readonly string[];
|
|
170
161
|
}): Promise<CancelSiblingResult>;
|
|
171
162
|
/**
|
|
172
|
-
* Apply cancel fencing to workflow-session actions (pending→cancelled,
|
|
163
|
+
* Apply cancel fencing to workflow-session actions (pending→cancelled,
|
|
164
|
+
* delivering→uncertain).
|
|
173
165
|
*/
|
|
174
166
|
export declare function fenceWorkflowActions(input: {
|
|
175
167
|
readonly t: Tx;
|