@nylorun/runtime 0.22.0-beta → 0.24.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +161 -0
- package/README.md +95 -30
- package/dist/adapters/execution/restate.d.ts +15 -3
- package/dist/adapters/execution/restate.js +78 -84
- package/dist/adapters/replication/pgoutput.d.ts +2 -0
- package/dist/adapters/replication/pgoutput.js +6 -3
- package/dist/adapters/sandbox/local.d.ts +9 -1
- package/dist/adapters/sandbox/local.js +16 -4
- package/dist/adapters/sandbox/virtual.js +4 -1
- package/dist/adapters/streams/s2.js +1 -13
- package/dist/api/a2a/endpoint.js +5 -1
- package/dist/api/a2a/routes.d.ts +2 -0
- package/dist/api/a2a/routes.js +2 -2
- package/dist/api/a2a/tasks.js +2 -1
- package/dist/api/ag-ui/endpoint.js +4 -2
- package/dist/api/ag-ui/history.js +4 -1
- package/dist/api/ag-ui/routes.d.ts +4 -1
- package/dist/api/ag-ui/routes.js +11 -10
- package/dist/api/ag-ui/translate.js +3 -2
- package/dist/api/components.d.ts +4953 -1204
- package/dist/api/components.js +25 -16
- package/dist/api/gate/keys.js +7 -1
- package/dist/api/gate/routes.d.ts +6 -0
- package/dist/api/gate/routes.js +39 -3
- package/dist/api/http/app.js +4 -4
- package/dist/api/http/define.d.ts +6 -0
- package/dist/api/http/define.js +13 -1
- package/dist/api/http/respond.js +1 -1
- package/dist/api/http/routes/artifacts.js +35 -8
- package/dist/api/http/routes/coverage.d.ts +9 -0
- package/dist/api/http/routes/coverage.js +34 -0
- package/dist/api/http/routes/environments.d.ts +11 -0
- package/dist/api/http/routes/environments.js +283 -0
- package/dist/api/http/routes/mcp.d.ts +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- package/dist/api/http/routes/me.js +1 -1
- package/dist/api/http/routes/reads.d.ts +45 -9
- package/dist/api/http/routes/reads.js +54 -5
- package/dist/api/http/routes/sessions.js +11 -6
- package/dist/api/http/routes/tenant.js +32 -10
- package/dist/api/openapi.js +31 -26
- package/dist/artifacts/disk.d.ts +59 -0
- package/dist/artifacts/disk.js +563 -0
- package/dist/artifacts/export.d.ts +1 -1
- package/dist/artifacts/export.js +2 -3
- package/dist/artifacts/folders.d.ts +6 -3
- package/dist/artifacts/folders.js +17 -12
- package/dist/artifacts/service.d.ts +2 -1
- package/dist/artifacts/service.js +6 -1
- package/dist/artifacts/workspace.d.ts +1 -1
- package/dist/artifacts/workspace.js +1 -1
- package/dist/core/flow-host.d.ts +3 -22
- package/dist/core/flow-host.js +11 -37
- package/dist/core/provider.d.ts +3 -2
- package/dist/core/provider.js +5 -37
- package/dist/core/sandbox-routes.d.ts +2 -21
- package/dist/core/sandbox-routes.js +10 -43
- package/dist/execution/types.d.ts +11 -9
- package/dist/execution/types.js +5 -7
- package/dist/gates/contract.d.ts +3 -24
- package/dist/gates/contract.js +1 -36
- package/dist/gates/egress-credentials.d.ts +9 -0
- package/dist/gates/egress-credentials.js +11 -4
- package/dist/gates/egress.d.ts +28 -1
- package/dist/gates/egress.js +115 -10
- package/dist/gates/fixture-model.d.ts +28 -0
- package/dist/gates/fixture-model.js +92 -0
- package/dist/gates/handler.js +4 -4
- package/dist/gates/http-tool.d.ts +6 -2
- package/dist/gates/http-tool.js +3 -5
- package/dist/gates/in-process.d.ts +5 -0
- package/dist/gates/in-process.js +12 -1
- package/dist/gates/journal.d.ts +30 -0
- package/dist/gates/journal.js +52 -0
- package/dist/gates/mcp-handler.d.ts +4 -0
- package/dist/gates/mcp-handler.js +5 -1
- package/dist/gates/meter.d.ts +10 -2
- package/dist/gates/meter.js +20 -5
- package/dist/gates/policy.d.ts +49 -0
- package/dist/gates/policy.js +134 -0
- package/dist/gates/tenant-vaults.d.ts +7 -7
- package/dist/gates/tenant-vaults.js +5 -5
- package/dist/gates/tool-calls.d.ts +10 -5
- package/dist/gates/tool-calls.js +28 -10
- package/dist/gates/tool-client.d.ts +8 -0
- package/dist/gates/tool-client.js +27 -11
- package/dist/gates/tool-contract.d.ts +10 -68
- package/dist/gates/tool-contract.js +1 -85
- package/dist/gates/tool-gate.d.ts +1 -1
- package/dist/harness/calls.d.ts +12 -5
- package/dist/harness/calls.js +21 -12
- package/dist/harness/executors.js +12 -9
- package/dist/harness/main.js +18 -5
- package/dist/harness/pod.d.ts +34 -0
- package/dist/harness/pod.js +41 -0
- package/dist/harness/service.d.ts +0 -1
- package/dist/harness/service.js +13 -5
- package/dist/harness/setup.d.ts +27 -0
- package/dist/harness/setup.js +150 -0
- package/dist/harness/ws-client.js +3 -2
- package/dist/harness/ws-port.d.ts +1 -5
- package/dist/harness/ws-port.js +1 -5
- package/dist/harness-api/in-process.js +3 -3
- package/dist/harness-api/record.d.ts +15 -1
- package/dist/harness-api/record.js +174 -44
- package/dist/harness-api/redact.d.ts +3 -0
- package/dist/harness-api/redact.js +45 -0
- package/dist/harness-api/server.d.ts +8 -0
- package/dist/harness-api/server.js +93 -13
- package/dist/harness-api/start.d.ts +10 -3
- package/dist/harness-api/start.js +40 -10
- package/dist/harness-api/tool-journal.d.ts +96 -0
- package/dist/harness-api/tool-journal.js +102 -0
- package/dist/harness-api/tool-status.d.ts +13 -0
- package/dist/harness-api/tool-status.js +26 -0
- package/dist/harness-api/workspace.d.ts +6 -3
- package/dist/harness-api/workspace.js +27 -11
- package/dist/harness-api/ws-server.d.ts +11 -3
- package/dist/harness-api/ws-server.js +94 -15
- package/dist/host/execution.d.ts +8 -6
- package/dist/host/gates.d.ts +2 -1
- package/dist/host/gates.js +10 -2
- package/dist/host/main.js +64 -21
- package/dist/host/operate.js +67 -5
- package/dist/host/stack-config.d.ts +70 -0
- package/dist/host/stack-config.js +67 -1
- package/dist/infra/execution.d.ts +18 -1
- package/dist/infra/execution.js +28 -4
- package/dist/infra/index.d.ts +1 -1
- package/dist/keys/client.js +4 -1
- package/dist/keys/keys.d.ts +5 -5
- package/dist/keys/keys.js +5 -7
- package/dist/management-openapi.json +999 -2211
- package/dist/mcp/connect.d.ts +25 -20
- package/dist/mcp/connect.js +29 -30
- package/dist/mcp/pool.d.ts +2 -2
- package/dist/mcp/pool.js +1 -1
- package/dist/mcp/preview.d.ts +3 -3
- package/dist/mcp/preview.js +10 -20
- package/dist/mcp/snapshot.d.ts +5 -11
- package/dist/mcp/snapshot.js +6 -7
- package/dist/model/pi-model.js +8 -0
- package/dist/openapi.json +6955 -2526
- package/dist/reads/types.d.ts +16 -3
- package/dist/record/append.d.ts +3 -1
- package/dist/record/append.js +1 -0
- package/dist/record/envelope.d.ts +5 -1
- package/dist/record/envelope.js +7 -3
- package/dist/record/environment.d.ts +25 -0
- package/dist/record/environment.js +44 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/retry.d.ts +21 -0
- package/dist/retry.js +34 -0
- package/dist/sandbox/disk-fs.d.ts +95 -0
- package/dist/sandbox/disk-fs.js +206 -0
- package/dist/sandbox/join.d.ts +17 -18
- package/dist/sandbox/join.js +46 -20
- package/dist/sandbox/manager.d.ts +16 -8
- package/dist/sandbox/manager.js +33 -17
- package/dist/sandbox/placement.d.ts +13 -12
- package/dist/sandbox/placement.js +29 -12
- package/dist/sandbox/pods/client.d.ts +6 -1
- package/dist/sandbox/pods/lifecycle.d.ts +28 -27
- package/dist/sandbox/pods/lifecycle.js +14 -26
- package/dist/sandbox/pods/reconcile.js +21 -15
- package/dist/sandbox/pods/spec.d.ts +15 -13
- package/dist/sandbox/pods/spec.js +16 -20
- package/dist/sandbox/records.d.ts +2 -2
- package/dist/sandbox/records.js +3 -3
- package/dist/sandbox/resolve.d.ts +35 -27
- package/dist/sandbox/resolve.js +66 -41
- package/dist/sandbox/session-sandbox.js +1 -1
- package/dist/sandbox/setup.d.ts +61 -0
- package/dist/sandbox/setup.js +123 -0
- package/dist/sandbox/share.d.ts +11 -21
- package/dist/sandbox/share.js +12 -41
- package/dist/sandbox/tenant-config.d.ts +12 -14
- package/dist/sandbox/tenant-config.js +12 -17
- package/dist/sandbox/types.d.ts +6 -0
- package/dist/store/postgres/control.js +8 -1
- package/dist/store/postgres/drizzle/0018_wake_outbox_retries.sql +3 -0
- package/dist/store/postgres/drizzle/0019_sandbox_signal_outbox.sql +18 -0
- package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
- package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
- package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
- package/dist/store/postgres/reads.js +117 -17
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/record.d.ts +7 -0
- package/dist/store/postgres/record.js +23 -2
- package/dist/store/postgres/schema.d.ts +1213 -779
- package/dist/store/postgres/schema.js +136 -76
- package/dist/store/postgres/store.js +283 -117
- package/dist/store/types.d.ts +206 -88
- package/dist/streams/relay/core.js +1 -13
- package/dist/tenant/advance.d.ts +7 -6
- package/dist/tenant/advance.js +121 -49
- package/dist/tenant/artifact-tool.js +3 -4
- package/dist/tenant/commands.d.ts +15 -2
- package/dist/tenant/commands.js +94 -14
- package/dist/tenant/context.d.ts +67 -33
- package/dist/tenant/context.js +4 -15
- package/dist/tenant/control.d.ts +8 -1
- package/dist/tenant/control.js +2 -0
- package/dist/tenant/effects.d.ts +1 -1
- package/dist/tenant/effects.js +37 -20
- package/dist/tenant/environments.d.ts +167 -0
- package/dist/tenant/environments.js +823 -0
- package/dist/tenant/harness-tokens.d.ts +21 -0
- package/dist/tenant/harness-tokens.js +61 -0
- package/dist/tenant/host-token.d.ts +1 -1
- package/dist/tenant/host-token.js +2 -4
- package/dist/tenant/identity-file.d.ts +4 -4
- package/dist/tenant/identity-file.js +17 -8
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/principals.js +1 -1
- package/dist/tenant/reset.js +3 -3
- package/dist/tenant/runtime.d.ts +6 -1
- package/dist/tenant/runtime.js +30 -4
- package/dist/tenant/scheduler.d.ts +7 -0
- package/dist/tenant/scheduler.js +38 -7
- package/dist/tenant/session-streams.d.ts +0 -1
- package/dist/tenant/session-streams.js +1 -13
- package/dist/tenant/session.d.ts +3 -3
- package/dist/tenant/session.js +0 -14
- package/dist/tenant/sessions.d.ts +13 -13
- package/dist/tenant/sessions.js +61 -138
- package/dist/tenant/signing-keys.d.ts +5 -1
- package/dist/tenant/status.js +3 -3
- package/dist/tenant/streams.js +14 -29
- package/dist/tenant/sweep.d.ts +39 -12
- package/dist/tenant/sweep.js +168 -63
- package/dist/tenant/transcript.d.ts +210 -20
- package/dist/tenant/transcript.js +62 -11
- package/dist/tenant/turn-timing.d.ts +43 -0
- package/dist/tenant/turn-timing.js +39 -0
- package/dist/tenant/types.d.ts +2 -0
- package/dist/traces/ids.d.ts +42 -0
- package/dist/traces/ids.js +85 -0
- package/dist/traces/key.d.ts +6 -0
- package/dist/traces/key.js +37 -0
- package/dist/traces/map.d.ts +113 -0
- package/dist/traces/map.js +317 -0
- package/dist/traces/options.d.ts +7 -0
- package/dist/traces/options.js +1 -0
- package/dist/traces/otlp.d.ts +42 -0
- package/dist/traces/otlp.js +84 -0
- package/dist/traces/projector.d.ts +58 -0
- package/dist/traces/projector.js +200 -0
- package/dist/traces/read.d.ts +23 -0
- package/dist/traces/read.js +16 -0
- package/dist/vault/coverage.d.ts +26 -0
- package/dist/vault/coverage.js +64 -0
- package/dist/vault/error.d.ts +11 -1
- package/dist/vault/error.js +5 -1
- package/dist/vault/service.d.ts +47 -48
- package/dist/vault/service.js +321 -382
- package/dist/vault/sources.d.ts +5 -16
- package/dist/vault/sources.js +6 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/package.json +14 -13
- package/dist/api/http/routes/sandboxes.d.ts +0 -9
- package/dist/api/http/routes/sandboxes.js +0 -153
- package/dist/api/http/routes/vaults.d.ts +0 -10
- package/dist/api/http/routes/vaults.js +0 -139
- package/dist/record/sandbox.d.ts +0 -25
- package/dist/record/sandbox.js +0 -43
- package/dist/tenant/sandboxes.d.ts +0 -122
- package/dist/tenant/sandboxes.js +0 -562
package/dist/tenant/sessions.js
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session and definition resources: `PUT/GET /v1/agents`, `PUT /v1/sessions/:id` (create or
|
|
3
|
-
*
|
|
4
|
-
* queries; `PUT /v1/sessions/:id` runs in one transaction with the
|
|
2
|
+
* Session and definition resources: `PUT/GET /v1/agents`, `PUT /v1/sessions/:id` (create, or
|
|
3
|
+
* find an identical one), the session list and the session view. Reads use the store's typed
|
|
4
|
+
* queries; `PUT /v1/sessions/:id` runs in one transaction with the Environment's checks.
|
|
5
5
|
*/
|
|
6
6
|
import { DefinitionDocumentSchema, definitionFilesOf, } from "@nylorun/core/contracts";
|
|
7
7
|
import { hashManifest } from "@nylorun/core/compatibility";
|
|
8
|
-
import { codeToolRefusal } from "@nylorun/core/define";
|
|
8
|
+
import { approvalRefusal, codeToolRefusal } from "@nylorun/core/define";
|
|
9
9
|
import { aggregateWaits, isWorkflowManifest } from "../core/flow-host.js";
|
|
10
10
|
import { canonical } from "../store/canonical.js";
|
|
11
|
-
import {
|
|
12
|
-
import { resolveSandbox } from "../sandbox/resolve.js";
|
|
11
|
+
import { sandboxSpecOf } from "../sandbox/resolve.js";
|
|
13
12
|
import { withPlatformTools, withSandboxCapability } from "../sandbox/session-sandbox.js";
|
|
14
|
-
import { declaredSandboxes
|
|
13
|
+
import { declaredSandboxes } from "../sandbox/share.js";
|
|
15
14
|
import { effectiveSandboxConfig, readSandboxConfig } from "../sandbox/tenant-config.js";
|
|
16
|
-
import { checkPlacement, requirePods } from "../sandbox/placement.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
15
|
+
import { HARNESS_ID, checkPlacement, requirePods } from "../sandbox/placement.js";
|
|
16
|
+
import { openEnvironment, recordAttachment } from "./environments.js";
|
|
17
|
+
import { harnessOf, } from "./context.js";
|
|
19
18
|
import { fail } from "./http.js";
|
|
20
19
|
import { missingDefinitionFiles } from "./definition-files.js";
|
|
21
|
-
/** A session's creation identity: everything but the request id
|
|
20
|
+
/** A session's creation identity: everything but the request id. */
|
|
22
21
|
const sessionIdentity = (value) => {
|
|
23
|
-
const { requestId: _requestId,
|
|
22
|
+
const { requestId: _requestId, ...body } = value ?? {};
|
|
24
23
|
return canonical(body);
|
|
25
24
|
};
|
|
26
25
|
export async function listDefinitions(ctx) {
|
|
@@ -56,13 +55,17 @@ export async function putDefinition(ctx, agentId, body) {
|
|
|
56
55
|
if (body.manifest.id !== agentId)
|
|
57
56
|
fail(400, "Agent id mismatch");
|
|
58
57
|
DefinitionDocumentSchema.parse(body.manifest);
|
|
59
|
-
// The Runtime runs
|
|
58
|
+
// The Runtime runs agents from their manifests alone (track R2): no tool may need it.
|
|
60
59
|
const code = codeToolRefusal(body.manifest);
|
|
61
60
|
if (code)
|
|
62
61
|
fail(400, code);
|
|
62
|
+
// Tool approvals are the Tenant's tool policy, decided at the Tool Gate (R5 step 2).
|
|
63
|
+
const approval = approvalRefusal(body.manifest);
|
|
64
|
+
if (approval)
|
|
65
|
+
fail(400, approval);
|
|
63
66
|
const declared = declaredSandboxes(body.manifest);
|
|
64
67
|
if (declared.length > 0)
|
|
65
|
-
fail(400, `${declared.join(", ")} declares a sandbox. Agents no longer declare one: remove .sandbox()
|
|
68
|
+
fail(400, `${declared.join(", ")} declares a sandbox. Agents no longer declare one: remove .sandbox(), create an Environment with the sandbox (PUT /v1/environments/{id}) and open the session on it, createSession({ environment }). See MIGRATION.md.`);
|
|
66
69
|
const definition = {
|
|
67
70
|
...body,
|
|
68
71
|
manifestHash: hashManifest(body.manifest),
|
|
@@ -104,18 +107,16 @@ export async function listSessions(ctx, agentId, access) {
|
|
|
104
107
|
};
|
|
105
108
|
}
|
|
106
109
|
/**
|
|
107
|
-
* Create a session from its definition, or
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
110
|
+
* Create a session from its definition, or find an identical one. `access`, when the request
|
|
111
|
+
* acts for a subject, must be the session's owner: another owner's session id is the 404 of a
|
|
112
|
+
* missing one (not the 409 of a mismatch), and so is an Environment whose owner guard names
|
|
113
|
+
* another owner.
|
|
111
114
|
*
|
|
112
|
-
* The session's
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
+
* The session's Environment is fixed for its life: its sandbox (kinds `virtual` and `pod`) is
|
|
116
|
+
* the session's, with the spec of the Environment's applied recipe pinned now, and its vault
|
|
117
|
+
* holds the session's credentials. Without one the session has no sandbox and no credentials.
|
|
115
118
|
*/
|
|
116
119
|
export async function putSession(ctx, id, body, access, options = {}) {
|
|
117
|
-
const vaultIds = body.vaultIds ?? [];
|
|
118
|
-
const credentialSelections = body.credentialSelections ?? [];
|
|
119
120
|
const opaque = access !== undefined;
|
|
120
121
|
if (opaque && body.ownerUserId !== access.owner)
|
|
121
122
|
fail(403, "ownerUserId must be the subject");
|
|
@@ -135,35 +136,34 @@ export async function putSession(ctx, id, body, access, options = {}) {
|
|
|
135
136
|
fail(404, "Session not found");
|
|
136
137
|
return prior;
|
|
137
138
|
}
|
|
138
|
-
await ctx.vault.assertAttachment(t, body.ownerUserId, vaultIds, credentialSelections, { opaque });
|
|
139
|
-
const environment = await ctx.vault.sessionEnvironment(t, vaultIds);
|
|
140
|
-
if (environment.conflict)
|
|
141
|
-
fail(409, environment.conflict, { code: "credential_conflict" });
|
|
142
139
|
if (prior) {
|
|
143
|
-
if (prior.sandboxId !== undefined)
|
|
144
|
-
await assertSandboxSecrets(ctx, t, prior.sandboxId, id, body.ownerUserId, environment.secretHosts);
|
|
145
140
|
if (sessionIdentity(prior.creation) !== sessionIdentity(body))
|
|
146
141
|
fail(409, "Session already exists with different creation parameters");
|
|
147
|
-
prior.vaultIds = vaultIds;
|
|
148
|
-
prior.credentialSelections = credentialSelections;
|
|
149
|
-
prior.creation = body;
|
|
150
|
-
await t.put("sessions", id, prior);
|
|
151
|
-
await ctx.vault.recordAttachment(t, id, vaultIds);
|
|
152
142
|
return prior;
|
|
153
143
|
}
|
|
154
144
|
const definition = (await t.get("definitions", body.agentId)) ??
|
|
155
145
|
fail(404, "Definition not found");
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
146
|
+
const environment = body.environment === undefined
|
|
147
|
+
? undefined
|
|
148
|
+
: await openEnvironment(t, body.environment, {
|
|
149
|
+
...(options.environmentGrants === undefined ? {} : { grants: options.environmentGrants }),
|
|
150
|
+
ownerUserId: body.ownerUserId,
|
|
151
|
+
opaque,
|
|
152
|
+
});
|
|
153
|
+
const config = await readSandboxConfig(t);
|
|
154
|
+
// The harness the session runs for its life (R4.1): its Environment's, else ours. A flow
|
|
155
|
+
// runs in core and our engine, whatever its Environment names.
|
|
156
|
+
const harness = isWorkflowManifest(definition.manifest) || environment === undefined
|
|
157
|
+
? HARNESS_ID
|
|
158
|
+
: environment.applied.harness;
|
|
159
|
+
// Placement (D38, D52): where this session's harness may run, decided now and kept.
|
|
160
|
+
checkPlacement(config, environment?.kind, harness);
|
|
161
|
+
if (environment?.kind === "pod")
|
|
162
|
+
await requirePods(ctx.pods, `Environment ${environment.id}`);
|
|
163
|
+
const spec = environment === undefined || environment.kind === "none"
|
|
164
|
+
? undefined
|
|
165
|
+
: sandboxSpecOf(environment.applied, effectiveSandboxConfig(config).limits.idle);
|
|
166
|
+
const pinned = spec === undefined ? platformTools(definition) : pin(definition, spec);
|
|
167
167
|
const created = {
|
|
168
168
|
id,
|
|
169
169
|
agentId: body.agentId,
|
|
@@ -176,90 +176,18 @@ export async function putSession(ctx, id, body, access, options = {}) {
|
|
|
176
176
|
status: "idle",
|
|
177
177
|
activeTurnId: null,
|
|
178
178
|
creation: body,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
...(sandbox.sandboxId !== undefined ? { sandboxId: sandbox.sandboxId } : {}),
|
|
185
|
-
...(sandbox.spec !== undefined
|
|
186
|
-
? { sandbox: sandbox.spec, sandboxSource: sandbox.source }
|
|
187
|
-
: {}),
|
|
179
|
+
harness,
|
|
180
|
+
...(environment === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { environmentId: environment.id, environmentVersion: environment.version }),
|
|
183
|
+
...(spec === undefined ? {} : { sandbox: spec }),
|
|
188
184
|
};
|
|
189
185
|
await t.put("sessions", id, created);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
await recordAttachment(t, id, sandbox.sandboxId);
|
|
186
|
+
if (environment !== undefined)
|
|
187
|
+
await recordAttachment(t, id, environment.id);
|
|
193
188
|
return created;
|
|
194
189
|
});
|
|
195
190
|
}
|
|
196
|
-
/**
|
|
197
|
-
* A sandbox's shell secrets must be unambiguous (R2c, D50): egress-gate sets the header from the
|
|
198
|
-
* one `environment_secret` bound to a host among the sessions using the sandbox. So while any of
|
|
199
|
-
* them has secrets, they all have one owner, and no host is bound to two credentials.
|
|
200
|
-
*/
|
|
201
|
-
async function assertSandboxSecrets(ctx, t, sandboxId, sessionId, ownerUserId, secretHosts) {
|
|
202
|
-
const conflict = (message) => fail(409, message, { code: "credential_conflict" });
|
|
203
|
-
const mine = Object.keys(secretHosts).length > 0;
|
|
204
|
-
for (const other of await t.sessionsOnSandbox(sandboxId)) {
|
|
205
|
-
if (other.id === sessionId)
|
|
206
|
-
continue;
|
|
207
|
-
const theirs = (await ctx.vault.sessionEnvironment(t, other.vaultIds ?? [])).secretHosts;
|
|
208
|
-
if (other.ownerUserId !== ownerUserId && (mine || Object.keys(theirs).length > 0))
|
|
209
|
-
conflict(`Sandbox ${sandboxId} serves environment_secret credentials of one owner at a time`);
|
|
210
|
-
for (const [host, credentialId] of Object.entries(secretHosts))
|
|
211
|
-
if (theirs[host] !== undefined && theirs[host] !== credentialId)
|
|
212
|
-
conflict(`Two environment_secret credentials would be bound to ${host} in sandbox ${sandboxId}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function isShare(value) {
|
|
216
|
-
return typeof value === "object" && value !== null && "session" in value;
|
|
217
|
-
}
|
|
218
|
-
function isAttach(value) {
|
|
219
|
-
return typeof value === "object" && value !== null && "id" in value;
|
|
220
|
-
}
|
|
221
|
-
/** Decide a new session's sandbox and, for an agent, the manifest that carries its tools. */
|
|
222
|
-
async function sessionSandbox(t, body, definition, options) {
|
|
223
|
-
const request = body.sandbox;
|
|
224
|
-
const declared = sandboxSpecOf(definition.manifest) !== undefined;
|
|
225
|
-
if (isAttach(request)) {
|
|
226
|
-
if (declared)
|
|
227
|
-
fail(400, `'${body.agentId}' declares its own sandbox with .sandbox(). Remove it from the agent to attach a sandbox.`);
|
|
228
|
-
const sandbox = await attachSandbox(t, request.id, options.grants);
|
|
229
|
-
// A pod's storage and lifecycle are the resource's, not the agent's sandbox manifest.
|
|
230
|
-
const { storage: _storage, lifecycle: _lifecycle, ...spec } = sandbox.spec;
|
|
231
|
-
return { ...(await pin(definition, spec, "sandbox")), sandboxId: sandbox.id, kind: sandbox.kind };
|
|
232
|
-
}
|
|
233
|
-
if (isShare(request)) {
|
|
234
|
-
const owner = await t.get("sessions", request.session);
|
|
235
|
-
if (owner?.sandboxId !== undefined && (!options.opaque || owner.ownerUserId === body.ownerUserId))
|
|
236
|
-
fail(400, `Session ${request.session} is attached to sandbox ${owner.sandboxId}: open this session with sandbox: { id: "${owner.sandboxId}" }.`);
|
|
237
|
-
const lookup = await sandboxLookup(t, request.session);
|
|
238
|
-
const sandboxOwnerId = validateSandboxAttach(body, definition.manifest, lookup, {
|
|
239
|
-
opaque: options.opaque,
|
|
240
|
-
inherit: !declared,
|
|
241
|
-
});
|
|
242
|
-
if (declared || sandboxOwnerId === undefined)
|
|
243
|
-
return { sandboxOwnerId };
|
|
244
|
-
const spec = sessionSandboxSpec(lookup(request.session));
|
|
245
|
-
return { ...(await pin(definition, spec, "shared")), sandboxOwnerId };
|
|
246
|
-
}
|
|
247
|
-
if (declared) {
|
|
248
|
-
if (request !== undefined)
|
|
249
|
-
fail(400, `'${body.agentId}' declares its own sandbox with .sandbox(). Remove it from the agent to choose the sandbox when opening the session, or omit sandbox here.`);
|
|
250
|
-
return {};
|
|
251
|
-
}
|
|
252
|
-
const resolved = resolveSandbox({
|
|
253
|
-
request,
|
|
254
|
-
config: effectiveSandboxConfig(await readSandboxConfig(t)),
|
|
255
|
-
actingForSubject: options.opaque,
|
|
256
|
-
});
|
|
257
|
-
if (resolved.kind === "none")
|
|
258
|
-
return {};
|
|
259
|
-
if (resolved.kind === "error")
|
|
260
|
-
return fail(resolved.status, resolved.errors.join(" "));
|
|
261
|
-
return pin(definition, resolved.spec, resolved.source);
|
|
262
|
-
}
|
|
263
191
|
/**
|
|
264
192
|
* The manifest a session without a sandbox pins: its agents' `read_artifact` (R2b C11), when
|
|
265
193
|
* one has a remote MCP server or an HTTP tool; nothing otherwise, and for a workflow.
|
|
@@ -274,19 +202,15 @@ function platformTools(definition) {
|
|
|
274
202
|
return fail(400, pinned.message);
|
|
275
203
|
return { manifest: pinned.manifest, manifestHash: pinned.manifestHash };
|
|
276
204
|
}
|
|
277
|
-
|
|
205
|
+
/** The manifest a session with a sandbox pins: the definition with the sandbox capability. */
|
|
206
|
+
function pin(definition, spec) {
|
|
278
207
|
// A workflow's manifest stays as registered: its agents inherit the sandbox when they start.
|
|
279
208
|
if (isWorkflowManifest(definition.manifest))
|
|
280
|
-
return {
|
|
209
|
+
return {};
|
|
281
210
|
const sandboxed = withSandboxCapability(definition.manifest, spec);
|
|
282
211
|
if (!sandboxed.ok)
|
|
283
212
|
return fail(400, sandboxed.message);
|
|
284
|
-
return {
|
|
285
|
-
spec,
|
|
286
|
-
source,
|
|
287
|
-
manifest: sandboxed.manifest,
|
|
288
|
-
manifestHash: sandboxed.manifestHash,
|
|
289
|
-
};
|
|
213
|
+
return { manifest: sandboxed.manifest, manifestHash: sandboxed.manifestHash };
|
|
290
214
|
}
|
|
291
215
|
/** The session resource returned by `GET`/`PUT /v1/sessions/:id`. */
|
|
292
216
|
export async function sessionView(t, s) {
|
|
@@ -315,12 +239,11 @@ export async function sessionView(t, s) {
|
|
|
315
239
|
implementationVersion: s.implementationVersion,
|
|
316
240
|
status: s.status,
|
|
317
241
|
activeTurnId: s.activeTurnId,
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
...(s.sandboxSource ? { sandboxSource: s.sandboxSource } : {}),
|
|
242
|
+
environment: s.environmentId === undefined
|
|
243
|
+
? null
|
|
244
|
+
: { id: s.environmentId, version: s.environmentVersion ?? 1 },
|
|
245
|
+
harness: harnessOf(s),
|
|
246
|
+
...(s.closedReason === undefined ? {} : { closedReason: s.closedReason }),
|
|
324
247
|
mcpSnapshot: s.mcpSnapshot ?? null,
|
|
325
248
|
mcpDiagnostics: s.mcpDiagnostics ?? [],
|
|
326
249
|
waits,
|
|
@@ -19,7 +19,11 @@ import type { SigningKeyRow, Tx } from "../store/types.js";
|
|
|
19
19
|
type KeyLike = Awaited<ReturnType<typeof importJWK>>;
|
|
20
20
|
export interface SigningKeysOptions {
|
|
21
21
|
tenantId: string;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* The vault KEK. The gateway's keys service only reads the file `nylorun start` wrote
|
|
24
|
+
* (`gates/tenant-vaults.ts`); an in-process Runtime may create it on first use. Resolve it
|
|
25
|
+
* before opening a transaction.
|
|
26
|
+
*/
|
|
23
27
|
kek(): Buffer;
|
|
24
28
|
}
|
|
25
29
|
export interface CurrentKey {
|
package/dist/tenant/status.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FIXTURE_MODEL_SETTING, seedFixtureModel } from "./model-setting.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ENVIRONMENTS_CONFIG_SETTING, writeSandboxConfig } from "../sandbox/tenant-config.js";
|
|
3
3
|
/** The Tenant's harnesses, for its status and the Host's. */
|
|
4
4
|
export function harnessStatusOf(ctx) {
|
|
5
5
|
return { mode: ctx.mcp ? "in-process" : "remote", ...ctx.harness.status() };
|
|
@@ -108,12 +108,12 @@ export async function seedTenantConfig(ctx, body) {
|
|
|
108
108
|
const sandboxConfig = body.sandbox?.config;
|
|
109
109
|
if (sandboxConfig) {
|
|
110
110
|
const inserted = await ctx.store.tx(async (t) => {
|
|
111
|
-
if ((await t.getSetting(
|
|
111
|
+
if ((await t.getSetting(ENVIRONMENTS_CONFIG_SETTING)) !== undefined)
|
|
112
112
|
return false;
|
|
113
113
|
await writeSandboxConfig(t, sandboxConfig);
|
|
114
114
|
return true;
|
|
115
115
|
});
|
|
116
|
-
(inserted ? applied : kept).push(
|
|
116
|
+
(inserted ? applied : kept).push(ENVIRONMENTS_CONFIG_SETTING);
|
|
117
117
|
}
|
|
118
118
|
if (body.fixtureModel) {
|
|
119
119
|
const inserted = await ctx.store.tx((t) => seedFixtureModel(t));
|
package/dist/tenant/streams.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { basinOf } from "../streams/basin.js";
|
|
2
2
|
import { createStreamRelay, } from "../streams/relay/core.js";
|
|
3
|
-
import {
|
|
3
|
+
import { retry } from "../retry.js";
|
|
4
|
+
import { checkSessionStreams, currentBasin } from "./session-streams.js";
|
|
4
5
|
const RETRY_MIN_MS = 100;
|
|
5
6
|
const RETRY_MAX_MS = 2000;
|
|
6
7
|
/** The longest wait between two `ensureTenant` attempts while the basin is being repaired. */
|
|
@@ -92,7 +93,11 @@ export async function wireStreams(ctx, options) {
|
|
|
92
93
|
/** Deletes a retired generation's basin, then forgets it. A failure retries on next open. */
|
|
93
94
|
async function deleteRetired(generation) {
|
|
94
95
|
try {
|
|
95
|
-
await
|
|
96
|
+
await retry(() => streams.deleteTenant(basinOf(tenantId, generation)), {
|
|
97
|
+
minMs: RETRY_MIN_MS,
|
|
98
|
+
maxMs: RETRY_MAX_MS,
|
|
99
|
+
attempts: 5,
|
|
100
|
+
});
|
|
96
101
|
await store.tx((t) => t.forgetRetiredGeneration(generation));
|
|
97
102
|
logger.info("retired stream basin deleted", { generation });
|
|
98
103
|
}
|
|
@@ -176,21 +181,6 @@ async function probe(streams, timeoutMs) {
|
|
|
176
181
|
export async function closeStreams(ctx) {
|
|
177
182
|
await ctx.sessionStreams.wiring?.close();
|
|
178
183
|
}
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
async function withRetries(task, attempts) {
|
|
181
|
-
let delay = RETRY_MIN_MS;
|
|
182
|
-
for (let attempt = 1;; attempt += 1) {
|
|
183
|
-
try {
|
|
184
|
-
return await task();
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
if (attempt >= attempts)
|
|
188
|
-
throw error;
|
|
189
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
190
|
-
delay = Math.min(delay * 2, RETRY_MAX_MS);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
184
|
function basinKeeper(streams, basinKey, signal, onError) {
|
|
195
185
|
let ready = false;
|
|
196
186
|
let readyFor;
|
|
@@ -230,20 +220,15 @@ function basinKeeper(streams, basinKey, signal, onError) {
|
|
|
230
220
|
repairing = true;
|
|
231
221
|
ready = false;
|
|
232
222
|
void (async () => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
223
|
+
await retry(ensure, {
|
|
224
|
+
minMs: RETRY_MIN_MS,
|
|
225
|
+
maxMs: BASIN_RETRY_MAX_MS,
|
|
226
|
+
signal,
|
|
227
|
+
onError: (error) => {
|
|
241
228
|
if (failures === 1 || failures % 10 === 0)
|
|
242
229
|
onError(error);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
delay = Math.min(delay * 2, BASIN_RETRY_MAX_MS);
|
|
246
|
-
}
|
|
230
|
+
},
|
|
231
|
+
}).then(() => (repairedAt = Date.now()), () => undefined);
|
|
247
232
|
repairing = false;
|
|
248
233
|
})();
|
|
249
234
|
},
|
package/dist/tenant/sweep.d.ts
CHANGED
|
@@ -1,20 +1,47 @@
|
|
|
1
|
+
import type { PendingRequest } from "../store/types.js";
|
|
1
2
|
import type { TenantContext } from "./context.js";
|
|
2
3
|
/**
|
|
3
|
-
* How old
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* How old an outbox row is before the sweep delivers it, unless its delivery after commit
|
|
5
|
+
* already failed (then it is due at once): longer than that delivery can take with its
|
|
6
|
+
* retries, so the sweep does not send again, and double the load on, an execution that is
|
|
7
|
+
* only slow. Sending one twice is harmless either way (the idempotency key).
|
|
8
|
+
*/
|
|
9
|
+
export declare const OUTBOX_GRACE_MS = 30000;
|
|
10
|
+
/**
|
|
11
|
+
* How often the orphan scan, and the stale pod sandbox check, run after the first pass of a
|
|
12
|
+
* Tenant opened on this process.
|
|
6
13
|
*/
|
|
7
|
-
export declare const WAKE_GRACE_MS = 2000;
|
|
8
|
-
/** How often the orphan scan runs after the first pass of a Tenant opened on this process. */
|
|
9
14
|
export declare const ORPHAN_SCAN_MS = 60000;
|
|
15
|
+
/**
|
|
16
|
+
* How long one pass may spend draining the outbox, and paging through orphaned sessions.
|
|
17
|
+
* A backlog is worked off over several passes rather than holding one (and with it the
|
|
18
|
+
* Tenant's sweep object in the execution) for long.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DRAIN_BUDGET_MS = 2000;
|
|
10
21
|
export declare function sweep(ctx: TenantContext): Promise<void>;
|
|
22
|
+
/** Failed deliveries after which the sweep parks an outbox row (`Tx.failOutbox`). */
|
|
23
|
+
export declare const MAX_OUTBOX_ATTEMPTS = 10;
|
|
11
24
|
/**
|
|
12
|
-
* Deliver the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
25
|
+
* Deliver the outbox rows that are due (`Tx.pendingOutbox`: older than `graceMs`, or whose
|
|
26
|
+
* delivery failed), oldest first, batch after batch until none is left or `budgetMs` is
|
|
27
|
+
* spent, and delete the delivered ones. Each row is delivered on its own: one that fails
|
|
28
|
+
* (Restate refuses it, or a Runtime that does not know a wake's reason) is tried again later,
|
|
29
|
+
* with backoff, so it never holds back the rows behind it, and after `MAX_OUTBOX_ATTEMPTS`
|
|
30
|
+
* failures it is parked: logged once, at error, and kept in the outbox for an operator. A
|
|
31
|
+
* parked wake's session is still re-woken by the orphan scan while it is runnable, and a
|
|
32
|
+
* parked sandbox signal's pod by the stale pod check. A row the Tenant declines (it is
|
|
33
|
+
* closing) stays as it is, and ends the drain. Never throws for a delivery; returns the rows
|
|
34
|
+
* delivered.
|
|
15
35
|
*/
|
|
16
|
-
export declare function
|
|
36
|
+
export declare function deliverOutbox(ctx: Pick<TenantContext, "store" | "send" | "config">, graceMs?: number, budgetMs?: number): Promise<PendingRequest[]>;
|
|
17
37
|
/** Settle pending workflow `agent` effects whose linked turn already finished. */
|
|
18
|
-
export declare function reconcileLinkedAgents(ctx: Pick<TenantContext, "store"
|
|
19
|
-
/**
|
|
20
|
-
|
|
38
|
+
export declare function reconcileLinkedAgents(ctx: Pick<TenantContext, "store">): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Wake `running`/`runnable` sessions without a live owner, page after page from the first id
|
|
41
|
+
* after `after`, until none is left or `budgetMs` is spent. Returns the ids woken, and the last
|
|
42
|
+
* id when the budget ran out first (`next`, where the next scan goes on).
|
|
43
|
+
*/
|
|
44
|
+
export declare function wakeOrphanedSessions(ctx: Pick<TenantContext, "store" | "wake">, now?: Date, after?: string, budgetMs?: number): Promise<{
|
|
45
|
+
woken: string[];
|
|
46
|
+
next?: string;
|
|
47
|
+
}>;
|