@nylorun/runtime 0.15.2-beta → 0.17.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 +134 -0
- package/README.md +68 -29
- package/dist/adapters/execution/restate.d.ts +9 -1
- package/dist/adapters/execution/restate.js +64 -5
- package/dist/adapters/sandbox/local.d.ts +10 -0
- package/dist/adapters/sandbox/local.js +195 -0
- package/dist/adapters/sandbox/virtual.js +36 -1
- package/dist/admin-openapi.json +341 -7
- package/dist/api/a2a/tasks.js +6 -1
- package/dist/api/ag-ui/endpoint.js +1 -2
- package/dist/api/ag-ui/history.js +6 -1
- package/dist/api/ag-ui/routes.d.ts +1 -1
- package/dist/api/ag-ui/routes.js +15 -13
- package/dist/api/components.d.ts +2667 -711
- package/dist/api/components.js +24 -11
- package/dist/api/gate/keys.d.ts +4 -3
- package/dist/api/gate/routes.d.ts +19 -3
- package/dist/api/gate/routes.js +162 -53
- package/dist/api/http/app.js +6 -0
- package/dist/api/http/define.d.ts +19 -12
- package/dist/api/http/define.js +13 -27
- package/dist/api/http/routes/access.d.ts +3 -19
- package/dist/api/http/routes/access.js +12 -172
- package/dist/api/http/routes/actions.js +0 -1
- package/dist/api/http/routes/artifacts.d.ts +11 -0
- package/dist/api/http/routes/artifacts.js +516 -0
- package/dist/api/http/routes/me.d.ts +13 -0
- package/dist/api/http/routes/me.js +45 -0
- package/dist/api/http/routes/sandboxes.d.ts +9 -0
- package/dist/api/http/routes/sandboxes.js +150 -0
- package/dist/api/http/routes/sessions.js +9 -13
- package/dist/api/http/routes/tenant.js +36 -2
- package/dist/api/http/routes/vaults.d.ts +12 -3
- package/dist/api/http/routes/vaults.js +122 -54
- package/dist/api/openapi.js +4 -10
- package/dist/artifacts/config.d.ts +23 -0
- package/dist/artifacts/config.js +27 -0
- package/dist/artifacts/export.d.ts +8 -0
- package/dist/artifacts/export.js +205 -0
- package/dist/artifacts/files.d.ts +36 -0
- package/dist/artifacts/files.js +45 -0
- package/dist/artifacts/folders.d.ts +60 -0
- package/dist/artifacts/folders.js +209 -0
- package/dist/artifacts/links.d.ts +28 -0
- package/dist/artifacts/links.js +92 -0
- package/dist/artifacts/media-types.d.ts +13 -0
- package/dist/artifacts/media-types.js +80 -0
- package/dist/artifacts/parts.d.ts +34 -0
- package/dist/artifacts/parts.js +38 -0
- package/dist/artifacts/service.d.ts +74 -0
- package/dist/artifacts/service.js +301 -0
- package/dist/artifacts/workspace.d.ts +60 -0
- package/dist/artifacts/workspace.js +57 -0
- package/dist/artifacts/zip.d.ts +10 -0
- package/dist/artifacts/zip.js +126 -0
- package/dist/blob/fs.d.ts +13 -0
- package/dist/blob/fs.js +194 -0
- package/dist/blob/index.d.ts +9 -0
- package/dist/blob/index.js +9 -0
- package/dist/blob/s3.d.ts +42 -0
- package/dist/blob/s3.js +366 -0
- package/dist/blob/types.d.ts +108 -0
- package/dist/blob/types.js +85 -0
- package/dist/core/sandbox-routes.d.ts +2 -2
- package/dist/core/sandbox-routes.js +4 -3
- package/dist/execution/memory.d.ts +11 -1
- package/dist/execution/memory.js +77 -3
- package/dist/execution/types.d.ts +54 -1
- package/dist/execution/types.js +31 -0
- package/dist/gates/contract.d.ts +14 -13
- package/dist/gates/contract.js +13 -12
- package/dist/gates/egress.d.ts +72 -0
- package/dist/gates/egress.js +319 -0
- package/dist/gates/handler.d.ts +3 -0
- package/dist/gates/handler.js +3 -2
- package/dist/gates/http-client.d.ts +3 -2
- package/dist/gates/http-client.js +17 -6
- package/dist/gates/in-process.d.ts +13 -1
- package/dist/gates/in-process.js +11 -0
- package/dist/gates/inflight.d.ts +23 -4
- package/dist/gates/inflight.js +18 -4
- package/dist/gates/mcp-handler.js +5 -1
- package/dist/gates/model-gate.d.ts +6 -2
- package/dist/gates/tenant-vaults.d.ts +18 -9
- package/dist/gates/tenant-vaults.js +17 -12
- package/dist/gates/tool-calls.d.ts +9 -5
- package/dist/gates/tool-calls.js +6 -6
- package/dist/gates/tool-client.d.ts +9 -2
- package/dist/gates/tool-client.js +56 -22
- package/dist/gates/tool-contract.d.ts +12 -7
- package/dist/gates/tool-contract.js +10 -4
- package/dist/gates/tool-gate.d.ts +1 -0
- package/dist/harness/calls.d.ts +52 -0
- package/dist/harness/calls.js +113 -0
- package/dist/harness/executors.d.ts +22 -0
- package/dist/harness/executors.js +96 -0
- package/dist/harness/main.d.ts +6 -0
- package/dist/harness/main.js +103 -0
- package/dist/harness/pod.d.ts +21 -0
- package/dist/harness/pod.js +119 -0
- package/dist/harness/run-tokens.d.ts +18 -0
- package/dist/harness/run-tokens.js +15 -0
- package/dist/harness/service.d.ts +46 -0
- package/dist/harness/service.js +179 -0
- package/dist/harness/ws-client.d.ts +43 -0
- package/dist/harness/ws-client.js +122 -0
- package/dist/harness/ws-port.d.ts +22 -0
- package/dist/harness/ws-port.js +78 -0
- package/dist/harness-api/in-process.d.ts +24 -0
- package/dist/harness-api/in-process.js +119 -0
- package/dist/harness-api/record.d.ts +44 -0
- package/dist/harness-api/record.js +250 -0
- package/dist/harness-api/renew.d.ts +37 -0
- package/dist/harness-api/renew.js +77 -0
- package/dist/harness-api/server.d.ts +104 -0
- package/dist/harness-api/server.js +408 -0
- package/dist/harness-api/start.d.ts +36 -0
- package/dist/harness-api/start.js +118 -0
- package/dist/harness-api/workspace.d.ts +53 -0
- package/dist/harness-api/workspace.js +201 -0
- package/dist/harness-api/ws-server.d.ts +34 -0
- package/dist/harness-api/ws-server.js +227 -0
- package/dist/host/admin-api.d.ts +12 -5
- package/dist/host/admin-api.js +72 -6
- package/dist/host/app.d.ts +0 -1
- package/dist/host/app.js +35 -24
- package/dist/host/config-for.d.ts +4 -0
- package/dist/host/config-for.js +2 -0
- package/dist/host/config.d.ts +0 -6
- package/dist/host/create-host.d.ts +3 -10
- package/dist/host/create-host.js +4 -1
- package/dist/host/gates.d.ts +27 -2
- package/dist/host/gates.js +52 -2
- package/dist/host/http.d.ts +4 -1
- package/dist/host/http.js +6 -1
- package/dist/host/main.js +167 -37
- package/dist/host/stack-config.d.ts +112 -9
- package/dist/host/stack-config.js +222 -20
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/keys/client.js +9 -1
- package/dist/keys/keys.d.ts +12 -2
- package/dist/keys/keys.js +8 -0
- package/dist/mcp/connect.js +2 -1
- package/dist/mcp/pool.d.ts +1 -0
- package/dist/mcp/pool.js +1 -0
- package/dist/mcp/snapshot.d.ts +8 -1
- package/dist/mcp/snapshot.js +28 -0
- package/dist/media.d.ts +5 -24
- package/dist/media.js +5 -0
- package/dist/model/pi-model.d.ts +6 -2
- package/dist/model/pi-model.js +39 -22
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +1 -1
- package/dist/openapi.json +14084 -9426
- package/dist/record/index.d.ts +1 -0
- package/dist/record/index.js +1 -0
- package/dist/record/sandbox.d.ts +25 -0
- package/dist/record/sandbox.js +43 -0
- package/dist/sandbox/capability.d.ts +3 -0
- package/dist/sandbox/capability.js +10 -0
- package/dist/sandbox/egress-token.d.ts +26 -0
- package/dist/sandbox/egress-token.js +8 -0
- package/dist/sandbox/join.d.ts +37 -0
- package/dist/sandbox/join.js +165 -0
- package/dist/sandbox/manager.d.ts +99 -34
- package/dist/sandbox/manager.js +216 -84
- package/dist/sandbox/placement.d.ts +27 -0
- package/dist/sandbox/placement.js +44 -0
- package/dist/sandbox/pods/client.d.ts +90 -0
- package/dist/sandbox/pods/client.js +79 -0
- package/dist/sandbox/pods/lifecycle.d.ts +91 -0
- package/dist/sandbox/pods/lifecycle.js +178 -0
- package/dist/sandbox/pods/name.d.ts +1 -0
- package/dist/sandbox/pods/name.js +28 -0
- package/dist/sandbox/pods/network-gate.d.ts +25 -0
- package/dist/sandbox/pods/network-gate.js +71 -0
- package/dist/sandbox/pods/reconcile.d.ts +11 -0
- package/dist/sandbox/pods/reconcile.js +152 -0
- package/dist/sandbox/pods/spec.d.ts +45 -0
- package/dist/sandbox/pods/spec.js +62 -0
- package/dist/sandbox/records.d.ts +29 -0
- package/dist/sandbox/records.js +79 -0
- package/dist/sandbox/resolve.d.ts +5 -0
- package/dist/sandbox/resolve.js +3 -2
- package/dist/sandbox/session-sandbox.js +7 -5
- package/dist/sandbox/share.d.ts +11 -0
- package/dist/sandbox/share.js +10 -0
- package/dist/sandbox/tenant-config.d.ts +16 -1
- package/dist/sandbox/tenant-config.js +9 -0
- package/dist/sandbox/types.d.ts +29 -1
- package/dist/sandbox/types.js +13 -1
- package/dist/store/postgres/drizzle/0004_sandbox_resources.sql +20 -0
- package/dist/store/postgres/drizzle/0005_artifacts.sql +27 -0
- package/dist/store/postgres/drizzle/0006_folder_artifacts.sql +12 -0
- package/dist/store/postgres/drizzle/0007_sandbox_pods.sql +14 -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/meta/_journal.json +49 -0
- package/dist/store/postgres/record-writer.d.ts +6 -1
- package/dist/store/postgres/record-writer.js +20 -2
- package/dist/store/postgres/schema.d.ts +1168 -250
- package/dist/store/postgres/schema.js +174 -28
- package/dist/store/postgres/store.js +339 -103
- package/dist/store/postgres/tenant.d.ts +1 -1
- package/dist/store/postgres/tenant.js +2 -2
- package/dist/store/types.d.ts +163 -32
- package/dist/streams/control.d.ts +2 -5
- package/dist/streams/control.js +5 -8
- package/dist/streams/types.d.ts +8 -8
- package/dist/streams/types.js +1 -1
- package/dist/tenant/advance.js +218 -160
- package/dist/tenant/artifact-tool.d.ts +5 -0
- package/dist/tenant/artifact-tool.js +119 -0
- package/dist/tenant/auth.d.ts +13 -9
- package/dist/tenant/auth.js +25 -32
- package/dist/tenant/commands.js +31 -10
- package/dist/tenant/context.d.ts +83 -26
- package/dist/tenant/delivery-token.js +4 -4
- package/dist/tenant/effects.d.ts +24 -14
- package/dist/tenant/effects.js +10 -398
- package/dist/tenant/ephemeral.d.ts +14 -2
- package/dist/tenant/ephemeral.js +8 -1
- package/dist/tenant/history.d.ts +13 -23
- package/dist/tenant/history.js +14 -62
- package/dist/tenant/host-token.d.ts +92 -0
- package/dist/tenant/host-token.js +179 -0
- 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.js +1 -0
- package/dist/tenant/operator-keys.d.ts +18 -0
- package/dist/tenant/operator-keys.js +55 -0
- package/dist/tenant/outbound.d.ts +30 -0
- package/dist/tenant/outbound.js +142 -10
- package/dist/tenant/paths.d.ts +2 -1
- package/dist/tenant/paths.js +3 -1
- package/dist/tenant/principals.d.ts +2 -11
- package/dist/tenant/principals.js +10 -30
- package/dist/tenant/reset.d.ts +5 -2
- package/dist/tenant/reset.js +22 -2
- package/dist/tenant/run-grants.d.ts +49 -0
- package/dist/tenant/run-grants.js +62 -0
- package/dist/tenant/run-token.d.ts +70 -0
- package/dist/tenant/run-token.js +138 -0
- package/dist/tenant/runtime.d.ts +42 -0
- package/dist/tenant/runtime.js +134 -33
- package/dist/tenant/sandboxes.d.ts +119 -0
- package/dist/tenant/sandboxes.js +551 -0
- package/dist/tenant/session-streams.d.ts +6 -10
- package/dist/tenant/session-streams.js +9 -27
- package/dist/tenant/session.d.ts +2 -13
- package/dist/tenant/session.js +4 -28
- package/dist/tenant/sessions.d.ts +6 -1
- package/dist/tenant/sessions.js +29 -2
- package/dist/tenant/signing-keys.d.ts +2 -1
- package/dist/tenant/signing-keys.js +2 -2
- package/dist/tenant/status.d.ts +8 -3
- package/dist/tenant/status.js +5 -0
- package/dist/tenant/streams.d.ts +8 -2
- package/dist/tenant/streams.js +25 -6
- package/dist/tenant/sweep.js +37 -4
- package/dist/tenant/types.d.ts +39 -1
- package/dist/tenant/worker.d.ts +11 -5
- package/dist/tenant/worker.js +12 -6
- 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 +54 -0
- package/dist/vault/sources.js +185 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -10
- package/dist/adapters/media.d.ts +0 -24
- package/dist/adapters/media.js +0 -111
- package/dist/host/cors.d.ts +0 -27
- package/dist/host/cors.js +0 -64
- package/dist/tenant/access-policy.d.ts +0 -30
- package/dist/tenant/access-policy.js +0 -52
- package/dist/tenant/browser.d.ts +0 -15
- package/dist/tenant/browser.js +0 -39
- 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 -180
package/dist/tenant/session.d.ts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers over the session document: turn-manifest pins and variants, state rebasing,
|
|
3
|
-
* the tools of an MCP snapshot, and the event target of an Action. No store access and no I/O.
|
|
4
|
-
*
|
|
5
|
-
* Later waves: stable; the async store conversion (Wave 1 / A) does not change these.
|
|
6
|
-
*/
|
|
7
|
-
import { type DurableSessionTool, type HostEffect } from "@nylorun/harness/run";
|
|
8
1
|
import type { Action } from "@nylorun/core/contracts";
|
|
9
|
-
import {
|
|
2
|
+
import type { AgentManifest, JsonValue } from "@nylorun/core/define";
|
|
10
3
|
import { type TurnManifestStore } from "../core/turn-manifest.js";
|
|
11
|
-
import type { McpSnapshot, McpToolRecord } from "../mcp/snapshot.js";
|
|
12
4
|
import type { Session } from "./context.js";
|
|
13
|
-
export
|
|
5
|
+
export { manifestFor, sessionToolsOf } from "../mcp/snapshot.js";
|
|
14
6
|
/** Turn-manifest store backed by the session's `variants` map (no schema change). */
|
|
15
7
|
export declare function variantStore(session: Session): TurnManifestStore;
|
|
16
8
|
/** Manifest this turn's checkpoint is pinned to (session pin or a stored variant). */
|
|
@@ -18,9 +10,6 @@ export declare function turnManifestOf(session: Session): AgentManifest;
|
|
|
18
10
|
/** Wrap a linked agent turn so the Loop learns the turn's manifest (SD-I4 pin stays on session). */
|
|
19
11
|
export declare function linkedAgentOutput(session: Session, output: JsonValue | undefined): JsonValue;
|
|
20
12
|
export declare function rebaseSessionState(session: Session, turnHash: string): void;
|
|
21
|
-
export declare function mcpToolOf(session: Session, request: Pick<HostEffect, "agent" | "capabilityId" | "toolName">): McpToolRecord | undefined;
|
|
22
|
-
/** The manifest of the agent an effect belongs to: the root, or an agent it uses as a tool. */
|
|
23
|
-
export declare function manifestFor(manifest: AgentManifest, agent: HostEffect["agent"]): AgentManifest | undefined;
|
|
24
13
|
export declare function pinnedTool(manifest: AgentManifest, capabilityId?: string, toolName?: string): import("@nylorun/core/define").ToolManifest | undefined;
|
|
25
14
|
/** What an action runs, for events: a tool name, or a hook point and its capabilities. */
|
|
26
15
|
export declare function actionTarget(action: Action): {
|
package/dist/tenant/session.js
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure helpers over the session document: turn-manifest pins and variants, state rebasing,
|
|
3
|
-
*
|
|
3
|
+
* and the event target of an Action. No store access and no I/O. (The tools of an MCP snapshot
|
|
4
|
+
* live in `mcp/snapshot.ts`.)
|
|
4
5
|
*
|
|
5
6
|
* Later waves: stable; the async store conversion (Wave 1 / A) does not change these.
|
|
6
7
|
*/
|
|
7
|
-
import { agentTurnValue
|
|
8
|
-
import { delegateManifest, } from "@nylorun/core/define";
|
|
8
|
+
import { agentTurnValue } from "@nylorun/harness/run";
|
|
9
9
|
import { isWorkflowManifest } from "../core/flow-host.js";
|
|
10
10
|
import { allowedManifestHashes, rebaseTurnState, } from "../core/turn-manifest.js";
|
|
11
|
-
export
|
|
12
|
-
if (!snapshot?.mcpTools.length)
|
|
13
|
-
return undefined;
|
|
14
|
-
return snapshot.mcpTools.map((tool) => ({
|
|
15
|
-
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
16
|
-
capabilityId: tool.capabilityId,
|
|
17
|
-
name: tool.name,
|
|
18
|
-
...(tool.description === undefined
|
|
19
|
-
? {}
|
|
20
|
-
: { description: tool.description }),
|
|
21
|
-
inputSchema: tool.inputSchema,
|
|
22
|
-
...(tool.outputSchema === undefined
|
|
23
|
-
? {}
|
|
24
|
-
: { outputSchema: tool.outputSchema }),
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
11
|
+
export { manifestFor, sessionToolsOf } from "../mcp/snapshot.js";
|
|
27
12
|
/** Turn-manifest store backed by the session's `variants` map (no schema change). */
|
|
28
13
|
export function variantStore(session) {
|
|
29
14
|
return {
|
|
@@ -61,15 +46,6 @@ export function rebaseSessionState(session, turnHash) {
|
|
|
61
46
|
if (next !== session.state)
|
|
62
47
|
session.state = next;
|
|
63
48
|
}
|
|
64
|
-
export function mcpToolOf(session, request) {
|
|
65
|
-
return session.mcpSnapshot?.mcpTools.find((tool) => tool.agentId === request.agent?.id &&
|
|
66
|
-
tool.capabilityId === request.capabilityId &&
|
|
67
|
-
tool.name === request.toolName);
|
|
68
|
-
}
|
|
69
|
-
/** The manifest of the agent an effect belongs to: the root, or an agent it uses as a tool. */
|
|
70
|
-
export function manifestFor(manifest, agent) {
|
|
71
|
-
return agent ? delegateManifest(manifest, agent.id) : manifest;
|
|
72
|
-
}
|
|
73
49
|
export function pinnedTool(manifest, capabilityId, toolName) {
|
|
74
50
|
const capability = manifest.capabilities.find((item) => item.id === capabilityId);
|
|
75
51
|
return capability?.tools?.find((tool) => tool.name === toolName);
|
|
@@ -15,7 +15,7 @@ export declare function listDefinitions(ctx: TenantContext): Promise<{
|
|
|
15
15
|
}[];
|
|
16
16
|
}>;
|
|
17
17
|
/**
|
|
18
|
-
* The agents a
|
|
18
|
+
* The agents a token caller may see: id, name and description only, never
|
|
19
19
|
* instructions, tools or MCP servers, and only the agents allowed.
|
|
20
20
|
*/
|
|
21
21
|
export declare function listAgentsPublic(ctx: TenantContext, agents: ReadonlySet<string> | "*"): Promise<{
|
|
@@ -59,6 +59,11 @@ export declare function putSession(ctx: TenantContext, id: string, body: PutSess
|
|
|
59
59
|
* session): an existing one must have the same owner and agent, or it is a 404.
|
|
60
60
|
*/
|
|
61
61
|
createOnly?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* The sandboxes the caller reaches (a token caller's grants, `sandboxGrantsOf`); undefined
|
|
64
|
+
* reaches every one. `sandbox: { id }` must name one of them.
|
|
65
|
+
*/
|
|
66
|
+
sandboxGrants?: readonly string[];
|
|
62
67
|
}): Promise<Session>;
|
|
63
68
|
/** The session resource returned by `GET`/`PUT /v1/sessions/:id`. */
|
|
64
69
|
export declare function sessionView(t: Tx, s: Session): Promise<unknown>;
|
package/dist/tenant/sessions.js
CHANGED
|
@@ -12,6 +12,8 @@ import { resolveSandbox } from "../sandbox/resolve.js";
|
|
|
12
12
|
import { withSandboxCapability } from "../sandbox/session-sandbox.js";
|
|
13
13
|
import { declaredSandboxes, sandboxSpecOf, sessionSandboxSpec, } from "../sandbox/share.js";
|
|
14
14
|
import { effectiveSandboxConfig, readSandboxConfig } from "../sandbox/tenant-config.js";
|
|
15
|
+
import { checkPlacement, requirePods } from "../sandbox/placement.js";
|
|
16
|
+
import { attachSandbox, recordAttachment } from "./sandboxes.js";
|
|
15
17
|
import { sandboxLookup, } from "./context.js";
|
|
16
18
|
import { fail } from "./http.js";
|
|
17
19
|
/** A session's creation identity: everything but the request id and vault attachments. */
|
|
@@ -31,7 +33,7 @@ export async function listDefinitions(ctx) {
|
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
|
-
* The agents a
|
|
36
|
+
* The agents a token caller may see: id, name and description only, never
|
|
35
37
|
* instructions, tools or MCP servers, and only the agents allowed.
|
|
36
38
|
*/
|
|
37
39
|
export async function listAgentsPublic(ctx, agents) {
|
|
@@ -133,7 +135,14 @@ export async function putSession(ctx, id, body, access, options = {}) {
|
|
|
133
135
|
}
|
|
134
136
|
const definition = (await t.get("definitions", body.agentId)) ??
|
|
135
137
|
fail(404, "Definition not found");
|
|
136
|
-
const sandbox = await sessionSandbox(t, body, definition, {
|
|
138
|
+
const sandbox = await sessionSandbox(t, body, definition, {
|
|
139
|
+
opaque,
|
|
140
|
+
...(options.sandboxGrants === undefined ? {} : { grants: options.sandboxGrants }),
|
|
141
|
+
});
|
|
142
|
+
// Placement (D38): where this session's harness may run, decided now and kept.
|
|
143
|
+
checkPlacement(await readSandboxConfig(t), sandbox.kind);
|
|
144
|
+
if (sandbox.kind === "pod")
|
|
145
|
+
await requirePods(ctx.pods, `Sandbox ${sandbox.sandboxId}`);
|
|
137
146
|
const created = {
|
|
138
147
|
id,
|
|
139
148
|
agentId: body.agentId,
|
|
@@ -151,23 +160,40 @@ export async function putSession(ctx, id, body, access, options = {}) {
|
|
|
151
160
|
...(sandbox.sandboxOwnerId !== undefined
|
|
152
161
|
? { sandboxOwnerId: sandbox.sandboxOwnerId }
|
|
153
162
|
: {}),
|
|
163
|
+
...(sandbox.sandboxId !== undefined ? { sandboxId: sandbox.sandboxId } : {}),
|
|
154
164
|
...(sandbox.spec !== undefined
|
|
155
165
|
? { sandbox: sandbox.spec, sandboxSource: sandbox.source }
|
|
156
166
|
: {}),
|
|
157
167
|
};
|
|
158
168
|
await t.put("sessions", id, created);
|
|
159
169
|
await ctx.vault.recordAttachment(t, id, vaultIds);
|
|
170
|
+
if (sandbox.sandboxId !== undefined)
|
|
171
|
+
await recordAttachment(t, id, sandbox.sandboxId);
|
|
160
172
|
return created;
|
|
161
173
|
});
|
|
162
174
|
}
|
|
163
175
|
function isShare(value) {
|
|
164
176
|
return typeof value === "object" && value !== null && "session" in value;
|
|
165
177
|
}
|
|
178
|
+
function isAttach(value) {
|
|
179
|
+
return typeof value === "object" && value !== null && "id" in value;
|
|
180
|
+
}
|
|
166
181
|
/** Decide a new session's sandbox and, for an agent, the manifest that carries its tools. */
|
|
167
182
|
async function sessionSandbox(t, body, definition, options) {
|
|
168
183
|
const request = body.sandbox;
|
|
169
184
|
const declared = sandboxSpecOf(definition.manifest) !== undefined;
|
|
185
|
+
if (isAttach(request)) {
|
|
186
|
+
if (declared)
|
|
187
|
+
fail(400, `'${body.agentId}' declares its own sandbox with .sandbox(). Remove it from the agent to attach a sandbox.`);
|
|
188
|
+
const sandbox = await attachSandbox(t, request.id, options.grants);
|
|
189
|
+
// A pod's storage and lifecycle are the resource's, not the agent's sandbox manifest.
|
|
190
|
+
const { storage: _storage, lifecycle: _lifecycle, ...spec } = sandbox.spec;
|
|
191
|
+
return { ...(await pin(definition, spec, "sandbox")), sandboxId: sandbox.id, kind: sandbox.kind };
|
|
192
|
+
}
|
|
170
193
|
if (isShare(request)) {
|
|
194
|
+
const owner = await t.get("sessions", request.session);
|
|
195
|
+
if (owner?.sandboxId !== undefined && (!options.opaque || owner.ownerUserId === body.ownerUserId))
|
|
196
|
+
fail(400, `Session ${request.session} is attached to sandbox ${owner.sandboxId}: open this session with sandbox: { id: "${owner.sandboxId}" }.`);
|
|
171
197
|
const lookup = await sandboxLookup(t, request.session);
|
|
172
198
|
const sandboxOwnerId = validateSandboxAttach(body, definition.manifest, lookup, {
|
|
173
199
|
opaque: options.opaque,
|
|
@@ -240,6 +266,7 @@ export async function sessionView(t, s) {
|
|
|
240
266
|
vaultIds: s.vaultIds ?? [],
|
|
241
267
|
credentialSelections: s.credentialSelections ?? [],
|
|
242
268
|
sandboxOwnerId: s.sandboxOwnerId ?? null,
|
|
269
|
+
...(s.sandboxId === undefined ? {} : { sandboxId: s.sandboxId }),
|
|
243
270
|
sandbox: s.sandbox ?? null,
|
|
244
271
|
...(s.sandboxSource ? { sandboxSource: s.sandboxSource } : {}),
|
|
245
272
|
mcpSnapshot: s.mcpSnapshot ?? null,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tenant signing keys for
|
|
2
|
+
* Tenant signing keys for the tokens the Runtime signs itself (delivery tokens, capability
|
|
3
|
+
* links, run and host tokens). Each key is an ES256 pair: the public JWK is stored
|
|
3
4
|
* as is, the PKCS#8 private key is sealed with the vault KEK (AAD bound to the Tenant and key
|
|
4
5
|
* id), like a credential.
|
|
5
6
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tenant signing keys for
|
|
2
|
+
* Tenant signing keys for the tokens the Runtime signs itself (delivery tokens, capability
|
|
3
|
+
* links, run and host tokens). Each key is an ES256 pair: the public JWK is stored
|
|
3
4
|
* as is, the PKCS#8 private key is sealed with the vault KEK (AAD bound to the Tenant and key
|
|
4
5
|
* id), like a credential.
|
|
5
6
|
*
|
|
@@ -117,7 +118,6 @@ export class SigningKeys {
|
|
|
117
118
|
const [previous] = await t.signingKeys(["previous"]);
|
|
118
119
|
if (previous && !force) {
|
|
119
120
|
const retired = Date.parse(previous.retiredAt ?? previous.createdAt);
|
|
120
|
-
// Delivery tokens may outlive the policy's subject tokens.
|
|
121
121
|
const longest = Math.max(maxTtlSeconds, DELIVERY_TOKEN_MAX_TTL_SECONDS);
|
|
122
122
|
const until = retired + longest * 1000 + ROTATION_GRACE_MS;
|
|
123
123
|
if (until > now.getTime())
|
package/dist/tenant/status.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { SeedTenantConfigRequest, SeedTenantConfigResponse, TenantStatus } from "@nylorun/core/contracts";
|
|
1
|
+
import type { HarnessStatus, SeedTenantConfigRequest, SeedTenantConfigResponse, TenantStatus } from "@nylorun/core/contracts";
|
|
2
2
|
import type { SessionStore } from "../store/types.js";
|
|
3
3
|
import type { VaultService } from "../vault/service.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { WorkspacePort } from "../harness-api/workspace.js";
|
|
5
5
|
import type { TenantConfig } from "./types.js";
|
|
6
|
+
import type { TenantContext } from "./context.js";
|
|
6
7
|
import type { TenantEnvelope } from "@nylorun/core/contracts";
|
|
7
8
|
import type { StuckInvocation } from "../execution/types.js";
|
|
8
9
|
import type { StreamsStatus } from "./streams.js";
|
|
@@ -12,7 +13,7 @@ export interface TenantStatusContext {
|
|
|
12
13
|
config: TenantConfig;
|
|
13
14
|
store: SessionStore;
|
|
14
15
|
vault: VaultService;
|
|
15
|
-
sandbox:
|
|
16
|
+
sandbox: Pick<WorkspacePort, "report">;
|
|
16
17
|
closing: boolean;
|
|
17
18
|
modelConfigured: boolean;
|
|
18
19
|
/**
|
|
@@ -22,7 +23,11 @@ export interface TenantStatusContext {
|
|
|
22
23
|
stuckInvocations?: () => Promise<StuckInvocation[]>;
|
|
23
24
|
/** This Tenant's Durable Streams status (`streamsStatus` in `streams.ts`). */
|
|
24
25
|
streamsStatus?: () => Promise<StreamsStatus>;
|
|
26
|
+
/** The Tenant's harnesses (F6.2). */
|
|
27
|
+
harness?: HarnessStatus;
|
|
25
28
|
}
|
|
29
|
+
/** The Tenant's harnesses, for its status and the Host's. */
|
|
30
|
+
export declare function harnessStatusOf(ctx: Pick<TenantContext, "mcp" | "harness">): HarnessStatus;
|
|
26
31
|
/** Build `TenantStatusSchema` with secrets redacted (A14). */
|
|
27
32
|
export declare function buildTenantStatus(ctx: TenantStatusContext): Promise<TenantStatus>;
|
|
28
33
|
/**
|
package/dist/tenant/status.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { FIXTURE_MODEL_SETTING, seedFixtureModel } from "./model-setting.js";
|
|
2
2
|
import { SANDBOX_CONFIG_SETTING, writeSandboxConfig } from "../sandbox/tenant-config.js";
|
|
3
|
+
/** The Tenant's harnesses, for its status and the Host's. */
|
|
4
|
+
export function harnessStatusOf(ctx) {
|
|
5
|
+
return { mode: ctx.mcp ? "in-process" : "remote", ...ctx.harness.status() };
|
|
6
|
+
}
|
|
3
7
|
/** How long status waits for the execution to list stuck invocations. */
|
|
4
8
|
const STUCK_TIMEOUT_MS = 5000;
|
|
5
9
|
/** `TenantStatus.execution`: paused and backing-off invocations, or why they are unknown. */
|
|
@@ -84,6 +88,7 @@ export async function buildTenantStatus(ctx) {
|
|
|
84
88
|
backend: sandboxReport.backend,
|
|
85
89
|
retained: counts.sandboxes,
|
|
86
90
|
},
|
|
91
|
+
...(ctx.harness ? { harness: ctx.harness } : {}),
|
|
87
92
|
...(execution ? { execution } : {}),
|
|
88
93
|
...(streams ? { streams } : {}),
|
|
89
94
|
};
|
package/dist/tenant/streams.d.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* - **Readers.** History and session SSE read the session's stream
|
|
18
18
|
* (`tenant/session-streams.ts`). This module runs one `tenant/control` reader per Tenant per
|
|
19
19
|
* process, on the current basin, which calls `ctx.abortLocal` for each `session.cancel`,
|
|
20
|
-
*
|
|
21
|
-
* each `
|
|
20
|
+
* passes an Action's outcome to a run held here for each `action.resolved` (F6.2), and
|
|
21
|
+
* checks the session streams for each `sessions.reset`.
|
|
22
22
|
*
|
|
23
23
|
* The caller passes the streams: the Host's S2 streams, or `MemoryStreams` for tests and a
|
|
24
24
|
* local development Host (not durable).
|
|
@@ -78,6 +78,12 @@ export declare function wireStreams(ctx: TenantContext, options: WireStreamsOpti
|
|
|
78
78
|
* effect), so failures are logged, never thrown.
|
|
79
79
|
*/
|
|
80
80
|
export declare function signalSessionCancel(ctx: TenantContext, sessionId: string, turnId: string | null): void;
|
|
81
|
+
/**
|
|
82
|
+
* Appends `action.resolved` for an Action whose outcome just committed, so a run held for it
|
|
83
|
+
* by a harness of another process goes on (F6.2). Call it from `t.afterCommit`. A lost signal
|
|
84
|
+
* costs the hold's time only: the segment then ends as waiting and resumes by replay.
|
|
85
|
+
*/
|
|
86
|
+
export declare function signalActionOutcome(ctx: TenantContext, sessionId: string, actionId: string): void;
|
|
81
87
|
/**
|
|
82
88
|
* After a reset deleted the Tenant's sessions and moved it to a new basin generation: tells
|
|
83
89
|
* every process with the Tenant open (`sessions.reset` on the old basin's control stream),
|
package/dist/tenant/streams.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { basinOf } from "../streams/basin.js";
|
|
2
|
-
import { signalCancel, signalSessionsReset } from "../streams/control.js";
|
|
2
|
+
import { signalActionResolved, signalCancel, signalSessionsReset } from "../streams/control.js";
|
|
3
3
|
import { createStreamRelay, } from "../streams/relay/core.js";
|
|
4
4
|
import { CONTROL_STREAM, } from "../streams/types.js";
|
|
5
|
-
import { checkSessionStreams, currentBasin,
|
|
5
|
+
import { checkSessionStreams, currentBasin, sleep, } from "./session-streams.js";
|
|
6
6
|
const RETRY_MIN_MS = 100;
|
|
7
7
|
const RETRY_MAX_MS = 2000;
|
|
8
8
|
/** The longest wait between two `ensureTenant` attempts while the basin is being repaired. */
|
|
@@ -59,12 +59,12 @@ export async function wireStreams(ctx, options) {
|
|
|
59
59
|
const signal = body;
|
|
60
60
|
if (signal?.type === "session.cancel" && typeof signal.sessionId === "string")
|
|
61
61
|
ctx.abortLocal(signal.sessionId, typeof signal.turnId === "string" ? signal.turnId : undefined);
|
|
62
|
+
else if (signal?.type === "action.resolved" &&
|
|
63
|
+
typeof signal.sessionId === "string" &&
|
|
64
|
+
typeof signal.actionId === "string")
|
|
65
|
+
void resolveHeld(ctx, signal.sessionId, signal.actionId).catch(report("held run resolution failed"));
|
|
62
66
|
else if (signal?.type === "sessions.reset")
|
|
63
67
|
void checkSessionStreams(ctx).catch(report("session stream check failed"));
|
|
64
|
-
else if (signal?.type === "subject.revoked" &&
|
|
65
|
-
typeof signal.subject === "string" &&
|
|
66
|
-
typeof signal.epoch === "number")
|
|
67
|
-
endSubjectStreams(ctx.sessionStreams, signal.subject, signal.epoch);
|
|
68
68
|
}, report("control stream read failed; retrying"));
|
|
69
69
|
// Signals appended from here on reach this process.
|
|
70
70
|
await startControl();
|
|
@@ -171,6 +171,25 @@ export function signalSessionCancel(ctx, sessionId, turnId) {
|
|
|
171
171
|
message: messageOf(error),
|
|
172
172
|
}));
|
|
173
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Appends `action.resolved` for an Action whose outcome just committed, so a run held for it
|
|
176
|
+
* by a harness of another process goes on (F6.2). Call it from `t.afterCommit`. A lost signal
|
|
177
|
+
* costs the hold's time only: the segment then ends as waiting and resumes by replay.
|
|
178
|
+
*/
|
|
179
|
+
export function signalActionOutcome(ctx, sessionId, actionId) {
|
|
180
|
+
const streams = ctx.sessionStreams.wiring?.streams;
|
|
181
|
+
if (!streams)
|
|
182
|
+
return;
|
|
183
|
+
void signalActionResolved(streams, currentBasin(ctx), sessionId, actionId).catch((error) => ctx.config.logger.warn("action resolved signal failed", { sessionId, message: messageOf(error) }));
|
|
184
|
+
}
|
|
185
|
+
/** Passes an Action's recorded outcome to the run of `sessionId` held here, if any. */
|
|
186
|
+
async function resolveHeld(ctx, sessionId, actionId) {
|
|
187
|
+
if (!ctx.harness.holds(sessionId))
|
|
188
|
+
return;
|
|
189
|
+
const effect = await ctx.store.tx((t) => t.get("effects", actionId));
|
|
190
|
+
if (effect?.status === "completed" && effect.outcome)
|
|
191
|
+
ctx.harness.resolved(sessionId, actionId, effect.outcome);
|
|
192
|
+
}
|
|
174
193
|
/**
|
|
175
194
|
* After a reset deleted the Tenant's sessions and moved it to a new basin generation: tells
|
|
176
195
|
* every process with the Tenant open (`sessions.reset` on the old basin's control stream),
|
package/dist/tenant/sweep.js
CHANGED
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* 2. **Orphaned sessions.** `running` or `runnable` sessions with no owner, or an owner whose
|
|
10
10
|
* lease expired, are woken with reason `recover`: a wake lost between commit and send, or
|
|
11
11
|
* a Worker that died mid-advance (the advance then takes over, §11.4).
|
|
12
|
-
* 3. **Sandboxes.** Idle sandboxes are stopped, records of compute
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* 3. **Sandboxes.** Idle sandboxes are stopped, records of compute no longer held are marked
|
|
13
|
+
* stopped, and sandboxes whose session or sandbox resource is gone are removed, by the
|
|
14
|
+
* workspace capability (`harness-api/workspace.ts`: here, or in the harness that serves
|
|
15
|
+
* workspaces). Idle MCP connections of the in-process harness are closed.
|
|
15
16
|
* 4. **Hooks.** Callbacks registered with `ctx.onSweep`.
|
|
16
17
|
* 5. **Deliveries.** Deliveries to Action endpoints whose deadline passed without an answer are
|
|
17
18
|
* lost (`delivery.ts` `loseAction`), and pending Actions of agents with an endpoint are sent
|
|
@@ -38,9 +39,12 @@ export async function sweep(ctx) {
|
|
|
38
39
|
() => ctx.sandbox.sweep({
|
|
39
40
|
now: now.getTime(),
|
|
40
41
|
sessionExists: async (id) => !!(await ctx.store.tx((t) => t.get("sessions", id))),
|
|
42
|
+
sandboxExists: async (id) => (await ctx.store.tx((t) => t.sandboxResource(id))) !== undefined,
|
|
41
43
|
}),
|
|
42
44
|
],
|
|
43
|
-
["
|
|
45
|
+
["pods", () => reconcileStalePods(ctx, now)],
|
|
46
|
+
// A harness elsewhere closes its own idle MCP connections.
|
|
47
|
+
["mcp", async () => ctx.mcp?.sweep()],
|
|
44
48
|
...[...ctx.sweepHooks].map((hook) => ["hook", hook]),
|
|
45
49
|
];
|
|
46
50
|
let failure;
|
|
@@ -80,3 +84,32 @@ export async function wakeOrphanedSessions(ctx, now = new Date()) {
|
|
|
80
84
|
await ctx.wake(id, { reason: "recover" });
|
|
81
85
|
return ids;
|
|
82
86
|
}
|
|
87
|
+
/** A pod sandbox in a change this long without a reconcile gets one from the sweep. */
|
|
88
|
+
const STALE_POD_MS = 60_000;
|
|
89
|
+
/**
|
|
90
|
+
* Pod sandboxes (F7.2) whose change has not finished (creating, deleting, an old volume still
|
|
91
|
+
* to delete, an expiry not yet recorded), or that should run but whose engine is not connected,
|
|
92
|
+
* and whose row has not moved for a minute: a reconcile the `Sandbox` object lost (a send
|
|
93
|
+
* dropped while the Tenant closed) is sent again, and a pod or volume deleted outside the
|
|
94
|
+
* Runtime is found.
|
|
95
|
+
*/
|
|
96
|
+
async function reconcileStalePods(ctx, now) {
|
|
97
|
+
if (!ctx.pods)
|
|
98
|
+
return;
|
|
99
|
+
const stale = await ctx.store.tx(async (t) => (await t.listSandboxResources()).filter((row) => {
|
|
100
|
+
const pod = row.pod;
|
|
101
|
+
if (!pod || now.getTime() - Date.parse(row.updatedAt) < STALE_POD_MS)
|
|
102
|
+
return false;
|
|
103
|
+
const expiring = pod.expiresAt !== undefined && Date.parse(pod.expiresAt) <= now.getTime() && pod.observed !== "expired";
|
|
104
|
+
return (pod.desired === "deleted" ||
|
|
105
|
+
pod.retiring !== undefined ||
|
|
106
|
+
pod.observed === "creating" ||
|
|
107
|
+
pod.observed === "deleting" ||
|
|
108
|
+
expiring ||
|
|
109
|
+
(pod.desired === "suspended" && pod.observed !== "suspended") ||
|
|
110
|
+
// Running, but its engine is not connected: the pod may be gone, or its volume.
|
|
111
|
+
(pod.desired === "running" && pod.observed === "running" && !ctx.harness.hosting(row.id)));
|
|
112
|
+
}));
|
|
113
|
+
for (const row of stale)
|
|
114
|
+
await ctx.sandboxSignal(row.id, { kind: "reconcile" });
|
|
115
|
+
}
|
package/dist/tenant/types.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import type { HostAggregate, HostTenant, TenantEnvelope } from "@nylorun/core/contracts";
|
|
2
|
+
import type { HarnessStatus, HostAggregate, HostTenant, TenantEnvelope } from "@nylorun/core/contracts";
|
|
3
|
+
import type { HarnessChannel } from "@nylorun/core/harness-api";
|
|
3
4
|
import type { FlowLimits } from "../core/limits.js";
|
|
5
|
+
import type { HarnessPeer } from "../harness-api/server.js";
|
|
6
|
+
import type { HostAuthority } from "../sandbox/join.js";
|
|
4
7
|
import type { SessionStore } from "../store/types.js";
|
|
5
8
|
import type { SandboxBackend } from "../sandbox/types.js";
|
|
6
9
|
import type { TenantCause } from "./cause.js";
|
|
10
|
+
import type { OperatorKeys } from "./operator-keys.js";
|
|
7
11
|
import type { OutboundPolicy } from "./outbound.js";
|
|
12
|
+
import type { ResolverConfig } from "../vault/sources.js";
|
|
8
13
|
import type { TenantWorker } from "./worker.js";
|
|
9
14
|
export type TenantMode = "shared" | "ephemeral" | "test";
|
|
10
15
|
/**
|
|
@@ -19,6 +24,8 @@ export interface TenantPaths {
|
|
|
19
24
|
tmp: string;
|
|
20
25
|
sandboxes: string;
|
|
21
26
|
pluginData: string;
|
|
27
|
+
/** The `fs` BlobStore's directory, used when the Host has no Object store. */
|
|
28
|
+
blobs: string;
|
|
22
29
|
logs: string;
|
|
23
30
|
log: string;
|
|
24
31
|
}
|
|
@@ -48,6 +55,12 @@ export interface TenantConfig {
|
|
|
48
55
|
steps?: number;
|
|
49
56
|
ms?: number;
|
|
50
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* How long a run waits in its lease for a pending Action's outcome (F6.2): the turn goes on
|
|
60
|
+
* at once when the Action endpoint answers in time, and ends its segment as waiting (resumed
|
|
61
|
+
* by replay) otherwise. Default 5 minutes; 0 never waits.
|
|
62
|
+
*/
|
|
63
|
+
actionHoldMs?: number;
|
|
51
64
|
/** Retries and timeouts for model calls (Model Calls §5, §6). Defaults in `piModel`. */
|
|
52
65
|
modelCall?: import("../gates/model-gate.js").ModelCallSettings;
|
|
53
66
|
childEnv: Readonly<Record<string, string>>;
|
|
@@ -70,6 +83,23 @@ export interface TenantConfig {
|
|
|
70
83
|
*/
|
|
71
84
|
flowEnv?: Readonly<Record<string, string | undefined>>;
|
|
72
85
|
vaultFetch?: typeof fetch;
|
|
86
|
+
/**
|
|
87
|
+
* The trusted issuers whose JWTs the Tenant API accepts (Host feature `trusted-issuers`), from
|
|
88
|
+
* the identity file (`NYLORUN_IDENTITY_FILE`). Absent: none.
|
|
89
|
+
*/
|
|
90
|
+
issuers?: import("./issuers.js").TrustedIssuers;
|
|
91
|
+
/**
|
|
92
|
+
* The operator's credential resolver (F9 C1), asked for a person's MCP credential when the
|
|
93
|
+
* session's vaults hold none. Used where this process authorizes MCP calls itself (embedding,
|
|
94
|
+
* the ephemeral Runtime, tests); the gateway takes `NYLORUN_RESOLVER_*` instead.
|
|
95
|
+
*/
|
|
96
|
+
resolver?: ResolverConfig;
|
|
97
|
+
/**
|
|
98
|
+
* The URL browsers reach this Tenant API at (`NYLORUN_PUBLIC_URL`, no trailing slash): the
|
|
99
|
+
* base of the MCP OAuth callback, `<publicUrl>/v1/oauth/callback` (F9 C2). Absent (embedding,
|
|
100
|
+
* tests): the origin the start request was sent to.
|
|
101
|
+
*/
|
|
102
|
+
publicUrl?: string;
|
|
73
103
|
logger: Logger;
|
|
74
104
|
}
|
|
75
105
|
export interface Logger {
|
|
@@ -84,6 +114,8 @@ export interface TenantSummary {
|
|
|
84
114
|
inFlightDeliveries: number;
|
|
85
115
|
pendingActions: number;
|
|
86
116
|
uncertainEffects: number;
|
|
117
|
+
/** The Tenant's harnesses (F6.2). */
|
|
118
|
+
harness?: HarnessStatus;
|
|
87
119
|
}
|
|
88
120
|
/**
|
|
89
121
|
* The Node request and response behind a `Request`, for what still writes to Node directly
|
|
@@ -107,6 +139,12 @@ export interface TenantHandle {
|
|
|
107
139
|
/** Stop scheduling; wait for or cancel active turns. */
|
|
108
140
|
drain(activeWork: "drain" | "cancel", timeoutMs?: number): Promise<void>;
|
|
109
141
|
close(): Promise<void>;
|
|
142
|
+
/** Serves a harness's Harness API channel until it closes. Returns a function that detaches it. */
|
|
143
|
+
attachHarness?(channel: HarnessChannel, peer: HarnessPeer): () => void;
|
|
144
|
+
/** The Tenant's side of a sandbox pod's join (F7.2); absent without sandbox pods. */
|
|
145
|
+
hostAuthority?(): HostAuthority | undefined;
|
|
146
|
+
/** The Tenant's operator keys, for the Admin API (`/v1/admin/keys`, F9 I1). */
|
|
147
|
+
operatorKeys?(): OperatorKeys;
|
|
110
148
|
}
|
|
111
149
|
/**
|
|
112
150
|
* The Host's Tenant for a request: open, or unavailable. `cause` says why when opening it
|
package/dist/tenant/worker.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AbortReason } from "@nylorun/core/harness-api";
|
|
2
|
+
import { RunAbort } from "@nylorun/harness/api";
|
|
3
|
+
import type { AdvanceResult, DeliverResult, SandboxResult, SandboxTrigger, DurableExecution, StuckInvocation, WorkerHandlers } from "../execution/types.js";
|
|
2
4
|
import type { Logger } from "./types.js";
|
|
3
5
|
/** This process's Worker id: the `owner` it writes on the sessions it advances (§10.6). */
|
|
4
6
|
export declare const WORKER_ID: string;
|
|
@@ -11,10 +13,12 @@ export declare const DEFAULT_ADVANCE_DEADLINE_MS: number;
|
|
|
11
13
|
/** Default time an aborted advance has to settle before it is abandoned. */
|
|
12
14
|
export declare const DEFAULT_ADVANCE_GRACE_MS = 30000;
|
|
13
15
|
/** Why an advance's signal aborted (see "Abort reasons" above). */
|
|
14
|
-
export type AdvanceAbortKind =
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
export type AdvanceAbortKind = AbortReason;
|
|
17
|
+
/**
|
|
18
|
+
* The abort reason of an advance. A harness run's reason (`RunAbort`) has the same kinds, so the
|
|
19
|
+
* executors read either one.
|
|
20
|
+
*/
|
|
21
|
+
export declare class AdvanceAbort extends RunAbort {
|
|
18
22
|
readonly name: string;
|
|
19
23
|
constructor(kind: AdvanceAbortKind, message: string, options?: {
|
|
20
24
|
cause?: unknown;
|
|
@@ -40,6 +44,8 @@ export interface TenantWorker {
|
|
|
40
44
|
sweep(): Promise<void>;
|
|
41
45
|
/** Delivers one Action to its endpoint. Only infrastructure errors throw. */
|
|
42
46
|
deliver?(actionId: string, signal: AbortSignal): Promise<DeliverResult>;
|
|
47
|
+
/** Reconciles one pod sandbox (F7.2). Only infrastructure errors throw. */
|
|
48
|
+
sandbox?(sandboxId: string, trigger: SandboxTrigger, signal: AbortSignal): Promise<SandboxResult>;
|
|
43
49
|
}
|
|
44
50
|
export interface TenantWorkersOptions {
|
|
45
51
|
/** Finds or opens a Tenant that is not registered on this process. */
|
package/dist/tenant/worker.js
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
*/
|
|
52
52
|
import { hostname } from "node:os";
|
|
53
53
|
import { randomBytes } from "node:crypto";
|
|
54
|
+
import { RunAbort } from "@nylorun/harness/api";
|
|
54
55
|
/** This process's Worker id: the `owner` it writes on the sessions it advances (§10.6). */
|
|
55
56
|
export const WORKER_ID = `worker-${hostname()}-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
56
57
|
/**
|
|
@@ -61,13 +62,14 @@ export const WORKER_ID = `worker-${hostname()}-${process.pid}-${randomBytes(4).t
|
|
|
61
62
|
export const DEFAULT_ADVANCE_DEADLINE_MS = 50 * 60_000;
|
|
62
63
|
/** Default time an aborted advance has to settle before it is abandoned. */
|
|
63
64
|
export const DEFAULT_ADVANCE_GRACE_MS = 30_000;
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The abort reason of an advance. A harness run's reason (`RunAbort`) has the same kinds, so the
|
|
67
|
+
* executors read either one.
|
|
68
|
+
*/
|
|
69
|
+
export class AdvanceAbort extends RunAbort {
|
|
67
70
|
name = "AdvanceAbort";
|
|
68
71
|
constructor(kind, message, options) {
|
|
69
|
-
super(message, options);
|
|
70
|
-
this.kind = kind;
|
|
72
|
+
super(kind, message, options);
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
/** The abort reason of an advance that ran past its deadline. */
|
|
@@ -87,7 +89,7 @@ export class AdvanceDeadlineError extends AdvanceAbort {
|
|
|
87
89
|
export function abortKind(signal) {
|
|
88
90
|
if (!signal.aborted)
|
|
89
91
|
return undefined;
|
|
90
|
-
return signal.reason instanceof
|
|
92
|
+
return signal.reason instanceof RunAbort ? signal.reason.kind : "shutdown";
|
|
91
93
|
}
|
|
92
94
|
/** The execution's abort reason as a `shutdown`, keeping its message. */
|
|
93
95
|
function shutdownOf(reason) {
|
|
@@ -138,6 +140,10 @@ export class TenantWorkers {
|
|
|
138
140
|
const worker = await this.find(tenantId);
|
|
139
141
|
return worker?.deliver ? worker.deliver(actionId, signal) : DONE;
|
|
140
142
|
},
|
|
143
|
+
sandbox: async (tenantId, sandboxId, trigger, signal) => {
|
|
144
|
+
const worker = await this.find(tenantId);
|
|
145
|
+
return worker?.sandbox ? worker.sandbox(sandboxId, trigger, signal) : {};
|
|
146
|
+
},
|
|
141
147
|
};
|
|
142
148
|
/** Runs one advance under the deadline and the grace period (see the module comment). */
|
|
143
149
|
async bounded(tenantId, sessionId, worker, signal) {
|
package/dist/vault/crypto.d.ts
CHANGED
|
@@ -10,3 +10,10 @@ export declare function encryptSecret(kek: Buffer, aad: Buffer, plaintext: Buffe
|
|
|
10
10
|
kekId: string;
|
|
11
11
|
};
|
|
12
12
|
export declare function decryptSecret(kek: Buffer, aad: Buffer, nonce: Buffer, ciphertext: Buffer, wrappedDek: Buffer, expectedKekId: string): Buffer;
|
|
13
|
+
/**
|
|
14
|
+
* `encryptSecret` in one column: nonce, wrapped DEK, then ciphertext and tag. For rows that
|
|
15
|
+
* seal several short secrets under one `kekId` (`oauth_pending`, F9 C2).
|
|
16
|
+
*/
|
|
17
|
+
export declare function sealBytes(kek: Buffer, aad: Buffer, plaintext: Buffer): Buffer;
|
|
18
|
+
/** Opens what `sealBytes` sealed; `VaultCryptoError` when it cannot. */
|
|
19
|
+
export declare function openBytes(kek: Buffer, aad: Buffer, sealed: Buffer, expectedKekId: string): Buffer;
|
package/dist/vault/crypto.js
CHANGED
|
@@ -78,3 +78,19 @@ function unwrapKey(kek, wrapped) {
|
|
|
78
78
|
throw new VaultCryptoError();
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
/** The wrapped DEK's length: its nonce, the 32-byte key and the tag. */
|
|
82
|
+
const WRAPPED_DEK_BYTES = IV_BYTES + 32 + TAG_BYTES;
|
|
83
|
+
/**
|
|
84
|
+
* `encryptSecret` in one column: nonce, wrapped DEK, then ciphertext and tag. For rows that
|
|
85
|
+
* seal several short secrets under one `kekId` (`oauth_pending`, F9 C2).
|
|
86
|
+
*/
|
|
87
|
+
export function sealBytes(kek, aad, plaintext) {
|
|
88
|
+
const sealed = encryptSecret(kek, aad, plaintext);
|
|
89
|
+
return Buffer.concat([sealed.nonce, sealed.wrappedDek, sealed.ciphertext]);
|
|
90
|
+
}
|
|
91
|
+
/** Opens what `sealBytes` sealed; `VaultCryptoError` when it cannot. */
|
|
92
|
+
export function openBytes(kek, aad, sealed, expectedKekId) {
|
|
93
|
+
if (sealed.length < IV_BYTES + WRAPPED_DEK_BYTES + TAG_BYTES)
|
|
94
|
+
throw new VaultCryptoError();
|
|
95
|
+
return decryptSecret(kek, aad, sealed.subarray(0, IV_BYTES), sealed.subarray(IV_BYTES + WRAPPED_DEK_BYTES), sealed.subarray(IV_BYTES, IV_BYTES + WRAPPED_DEK_BYTES), expectedKekId);
|
|
96
|
+
}
|