@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
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
import type { Credential } from "@earendil-works/pi-ai";
|
|
10
10
|
import type { ModelFailureOutcome } from "@nylorun/core/define";
|
|
11
11
|
import type { PostgresClient } from "../store/postgres/connect.js";
|
|
12
|
-
import {
|
|
12
|
+
import { type AuthorizeResult, type HostModelSecret } from "../vault/service.js";
|
|
13
|
+
import { CredentialSources, type McpCredentialRequest, type ResolverConfig } from "../vault/sources.js";
|
|
13
14
|
import type { SessionStore } from "../store/types.js";
|
|
14
15
|
import type { Session } from "../tenant/context.js";
|
|
15
16
|
import { type Keys } from "../keys/keys.js";
|
|
17
|
+
import { type OutboundPolicy } from "../tenant/outbound.js";
|
|
16
18
|
/** The Tenant, as a model call needs it. */
|
|
17
19
|
export interface TenantVault {
|
|
18
20
|
readonly tenantId: string;
|
|
@@ -26,12 +28,14 @@ export interface TenantVault {
|
|
|
26
28
|
/** A session, for the remote MCP servers its pinned manifest declares (F4.1). */
|
|
27
29
|
session(sessionId: string): Promise<Session | undefined>;
|
|
28
30
|
/**
|
|
29
|
-
* The
|
|
30
|
-
*
|
|
31
|
+
* The authorization of one request to a session's remote MCP server: its credential from the
|
|
32
|
+
* session's attached vaults, refreshed when due (F4.1), else from the operator's credential
|
|
33
|
+
* resolver (F9 C1).
|
|
31
34
|
*/
|
|
32
35
|
authorizeMcp(sessionId: string, request: {
|
|
33
36
|
url: string;
|
|
34
37
|
serverName: string;
|
|
38
|
+
agentId?: string;
|
|
35
39
|
}): Promise<AuthorizeResult>;
|
|
36
40
|
/** Vault writes and token signing with the Tenant's vault key (the keys service, F4.2). */
|
|
37
41
|
keys(): Keys;
|
|
@@ -52,13 +56,18 @@ export interface TenantVaultsOptions {
|
|
|
52
56
|
readonly sql: PostgresClient;
|
|
53
57
|
/** The Host root; the gate reads `keys/vault-kek` and `tenant/home` under it. */
|
|
54
58
|
readonly hostRoot: string;
|
|
59
|
+
/** The operator's credential resolver (`NYLORUN_RESOLVER_*`, F9 C1). */
|
|
60
|
+
readonly resolver?: ResolverConfig;
|
|
61
|
+
/**
|
|
62
|
+
* The Host's address policy (`NYLORUN_ENDPOINT_*`): OAuth refresh and MCP OAuth connect call
|
|
63
|
+
* authorization servers through `guardedFetch` under it (F9 C2).
|
|
64
|
+
*/
|
|
65
|
+
readonly delivery?: OutboundPolicy;
|
|
55
66
|
}
|
|
56
67
|
export declare function createTenantVaults(options: TenantVaultsOptions): TenantVaults;
|
|
57
68
|
/**
|
|
58
|
-
* A request to a session's remote MCP server, authorized from the session's attached vaults
|
|
59
|
-
* what the loop
|
|
69
|
+
* A request to a session's remote MCP server, authorized from the session's attached vaults,
|
|
70
|
+
* else the operator's credential resolver: what the loop does in its own process
|
|
71
|
+
* (`tenant/effects.ts` `authorize`). The owner and turn come from the session row.
|
|
60
72
|
*/
|
|
61
|
-
export declare function authorizeSessionMcp(
|
|
62
|
-
url: string;
|
|
63
|
-
serverName: string;
|
|
64
|
-
}): Promise<AuthorizeResult>;
|
|
73
|
+
export declare function authorizeSessionMcp(sources: CredentialSources, session: (sessionId: string) => Promise<Session | undefined>, sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
@@ -8,8 +8,10 @@ import { tenantPaths } from "../tenant/paths.js";
|
|
|
8
8
|
import { readVaultKek } from "../vault/kek.js";
|
|
9
9
|
import { HostModelVault } from "../vault/host-model.js";
|
|
10
10
|
import { VaultService } from "../vault/service.js";
|
|
11
|
+
import { CredentialSources } from "../vault/sources.js";
|
|
11
12
|
import { inProcessKeys } from "../keys/keys.js";
|
|
12
13
|
import { SigningKeys } from "../tenant/signing-keys.js";
|
|
14
|
+
import { guardedFetch } from "../tenant/outbound.js";
|
|
13
15
|
/** A call the gate refuses before the provider is called; it answers with `outcome`. */
|
|
14
16
|
export class GateRefusal extends Error {
|
|
15
17
|
outcome;
|
|
@@ -33,7 +35,15 @@ export function createTenantVaults(options) {
|
|
|
33
35
|
return kek;
|
|
34
36
|
};
|
|
35
37
|
const vault = new HostModelVault({ store, kek: readKek });
|
|
36
|
-
const credentials = new VaultService({
|
|
38
|
+
const credentials = new VaultService({
|
|
39
|
+
store,
|
|
40
|
+
kek: readKek,
|
|
41
|
+
fetch: guardedFetch(options.delivery ?? {}),
|
|
42
|
+
});
|
|
43
|
+
const sources = new CredentialSources({
|
|
44
|
+
vault: credentials,
|
|
45
|
+
...(options.resolver ? { resolver: options.resolver } : {}),
|
|
46
|
+
});
|
|
37
47
|
const keys = inProcessKeys({
|
|
38
48
|
store,
|
|
39
49
|
vault: credentials,
|
|
@@ -48,7 +58,7 @@ export function createTenantVaults(options) {
|
|
|
48
58
|
readHostModel: () => vault.readHostModel(),
|
|
49
59
|
writeHostCredential: (credential) => vault.updateHostCredential(credential),
|
|
50
60
|
session,
|
|
51
|
-
authorizeMcp: (sessionId, request) => authorizeSessionMcp(
|
|
61
|
+
authorizeMcp: (sessionId, request) => authorizeSessionMcp(sources, session, sessionId, request),
|
|
52
62
|
keys: () => keys,
|
|
53
63
|
};
|
|
54
64
|
}
|
|
@@ -77,18 +87,13 @@ export function createTenantVaults(options) {
|
|
|
77
87
|
};
|
|
78
88
|
}
|
|
79
89
|
/**
|
|
80
|
-
* A request to a session's remote MCP server, authorized from the session's attached vaults
|
|
81
|
-
* what the loop
|
|
90
|
+
* A request to a session's remote MCP server, authorized from the session's attached vaults,
|
|
91
|
+
* else the operator's credential resolver: what the loop does in its own process
|
|
92
|
+
* (`tenant/effects.ts` `authorize`). The owner and turn come from the session row.
|
|
82
93
|
*/
|
|
83
|
-
export async function authorizeSessionMcp(
|
|
94
|
+
export async function authorizeSessionMcp(sources, session, sessionId, request) {
|
|
84
95
|
const found = await session(sessionId);
|
|
85
96
|
if (!found)
|
|
86
97
|
throw new Error(`Session ${sessionId} not found`);
|
|
87
|
-
return
|
|
88
|
-
sessionId,
|
|
89
|
-
vaultIds: found.vaultIds ?? [],
|
|
90
|
-
credentialSelections: found.credentialSelections ?? [],
|
|
91
|
-
url: request.url,
|
|
92
|
-
serverName: request.serverName,
|
|
93
|
-
});
|
|
98
|
+
return sources.authorize({ ...found, id: sessionId }, request);
|
|
94
99
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { McpServerRef } from "../mcp/pool.js";
|
|
17
17
|
import type { Logger } from "../tenant/types.js";
|
|
18
|
-
import { type InflightCallsOptions } from "./inflight.js";
|
|
18
|
+
import { type InflightCallsOptions, type InflightOwner } from "./inflight.js";
|
|
19
19
|
import type { McpHandler } from "./mcp-handler.js";
|
|
20
20
|
import type { TenantVaults } from "./tenant-vaults.js";
|
|
21
21
|
import type { McpAnswer } from "./tool-contract.js";
|
|
@@ -31,11 +31,15 @@ export interface ToolCallRequest {
|
|
|
31
31
|
export interface ToolCalls {
|
|
32
32
|
/**
|
|
33
33
|
* Runs, joins or answers the call under `key`. Rejects with `InflightConflict` when a
|
|
34
|
-
* different request already ran under it.
|
|
34
|
+
* different request already ran under it, and as `InflightCalls.run` does for `owner`, the
|
|
35
|
+
* run that sends it (F5).
|
|
35
36
|
*/
|
|
36
|
-
run(tenantId: string | undefined, key: string, hash: string, request: ToolCallRequest): Promise<ToolCallAnswer>;
|
|
37
|
-
/**
|
|
38
|
-
|
|
37
|
+
run(tenantId: string | undefined, key: string, hash: string, request: ToolCallRequest, owner?: InflightOwner): Promise<ToolCallAnswer>;
|
|
38
|
+
/**
|
|
39
|
+
* Aborts the call under `key`; it is never run again under that key. With `sessionId`, false
|
|
40
|
+
* when the running call is another session's (nothing is aborted).
|
|
41
|
+
*/
|
|
42
|
+
cancel(key: string, sessionId?: string): boolean;
|
|
39
43
|
/** Aborts every running call (gateway shutdown). */
|
|
40
44
|
close(): void;
|
|
41
45
|
/** Deletes crossings settled more than `TOOL_CROSSING_TTL_MS` ago. */
|
package/dist/gates/tool-calls.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createInflightCalls, InflightConflict } from "./inflight.js";
|
|
1
|
+
import { createInflightCalls, InflightConflict, } from "./inflight.js";
|
|
2
2
|
/** How long a settled crossing is kept. */
|
|
3
3
|
export const TOOL_CROSSING_TTL_MS = 24 * 60 * 60_000;
|
|
4
4
|
const LOST = {
|
|
@@ -14,9 +14,9 @@ export function createToolCalls(options) {
|
|
|
14
14
|
const inflight = createInflightCalls(options.inflight);
|
|
15
15
|
const iso = () => new Date(now()).toISOString();
|
|
16
16
|
return {
|
|
17
|
-
async run(tenantId, key, hash, request) {
|
|
17
|
+
async run(tenantId, key, hash, request, owner) {
|
|
18
18
|
if (inflight.has(key))
|
|
19
|
-
return inflight.run(key, hash, () => Promise.resolve(LOST));
|
|
19
|
+
return inflight.run(key, hash, () => Promise.resolve(LOST), owner);
|
|
20
20
|
const vault = await vaults.open(tenantId);
|
|
21
21
|
const store = vault.store;
|
|
22
22
|
const row = await store.tx((t) => t.toolCrossing(key));
|
|
@@ -27,7 +27,7 @@ export function createToolCalls(options) {
|
|
|
27
27
|
return row.answer;
|
|
28
28
|
// Started here a moment ago, or lost with an earlier gateway.
|
|
29
29
|
if (inflight.has(key))
|
|
30
|
-
return inflight.run(key, hash, () => Promise.resolve(LOST));
|
|
30
|
+
return inflight.run(key, hash, () => Promise.resolve(LOST), owner);
|
|
31
31
|
return LOST;
|
|
32
32
|
}
|
|
33
33
|
return inflight.run(key, hash, async (signal) => {
|
|
@@ -46,9 +46,9 @@ export function createToolCalls(options) {
|
|
|
46
46
|
message: error instanceof Error ? error.message : String(error),
|
|
47
47
|
}));
|
|
48
48
|
return answer;
|
|
49
|
-
});
|
|
49
|
+
}, owner);
|
|
50
50
|
},
|
|
51
|
-
cancel: (key) => inflight.cancel(key),
|
|
51
|
+
cancel: (key, sessionId) => inflight.cancel(key, sessionId),
|
|
52
52
|
close: () => inflight.close(),
|
|
53
53
|
async prune() {
|
|
54
54
|
try {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { ToolGate } from "./tool-gate.js";
|
|
2
|
+
import type { RunTokens } from "../tenant/run-grants.js";
|
|
2
3
|
export interface HttpToolGateOptions {
|
|
3
4
|
/** The gates service, e.g. `http://gateway:4100` (`NYLORUN_GATES_URL`). */
|
|
4
5
|
readonly url: string;
|
|
5
|
-
/**
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* `NYLORUN_GATES_TOKEN`: core's credential, for deliveries and MCP requests outside a run. A
|
|
8
|
+
* harness has none (F6.2): its requests outside a run carry the session's last run token, or
|
|
9
|
+
* nothing, and the gate refuses them.
|
|
10
|
+
*/
|
|
11
|
+
readonly token?: string;
|
|
12
|
+
/** The run token of each session an advance of this process owns. Without it, core's only. */
|
|
13
|
+
readonly runTokens?: RunTokens;
|
|
7
14
|
/** Sent as `Nylorun-Tenant`; the gate checks it against its database's Tenant. */
|
|
8
15
|
readonly tenantId?: string;
|
|
9
16
|
/** How long an MCP request may stay silent. Default `GATE_CLIENT_TIMEOUT_MS`. */
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* diagnostic and `resolveEffect` marks a call `uncertain`, as before.
|
|
10
10
|
* - A delivery never throws. A hop that failed before the request was sent is `not_sent`
|
|
11
11
|
* (the deliverer sends it again, even a tool); one that failed after is `lost`.
|
|
12
|
+
*
|
|
13
|
+
* Credentials (F5): a session's MCP requests and keyed cancels carry its run token, read per
|
|
14
|
+
* request from the advance's grant, and leave the session out of the body: the gate takes it
|
|
15
|
+
* from the token. Deliveries, pings, and MCP requests made outside a run (closing a pooled
|
|
16
|
+
* connection after the advance ended) carry core's credential, `NYLORUN_GATES_TOKEN`, and name
|
|
17
|
+
* the session in the body.
|
|
12
18
|
*/
|
|
13
19
|
import { randomUUID } from "node:crypto";
|
|
14
20
|
import { request as httpRequest } from "node:http";
|
|
@@ -57,7 +63,9 @@ export function httpToolGate(options) {
|
|
|
57
63
|
const outgoing = send(url, {
|
|
58
64
|
method: "POST",
|
|
59
65
|
headers: {
|
|
60
|
-
|
|
66
|
+
...((request.bearer ?? options.token)
|
|
67
|
+
? { authorization: `Bearer ${request.bearer ?? options.token}` }
|
|
68
|
+
: {}),
|
|
61
69
|
"content-type": "application/json",
|
|
62
70
|
"content-length": payload.byteLength,
|
|
63
71
|
...(options.tenantId ? { [TENANT_HEADER]: options.tenantId } : {}),
|
|
@@ -88,9 +96,24 @@ export function httpToolGate(options) {
|
|
|
88
96
|
outgoing.end(payload);
|
|
89
97
|
});
|
|
90
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* The body and credential of a request for `server`'s session: its run token and no session
|
|
101
|
+
* in the body while an advance holds a grant, core's credential and the session otherwise.
|
|
102
|
+
*/
|
|
103
|
+
function scoped(server, body) {
|
|
104
|
+
const token = options.runTokens?.token(server.sessionId);
|
|
105
|
+
if (!token)
|
|
106
|
+
return { body: body({ ...server }) };
|
|
107
|
+
const { sessionId: _, ...unnamed } = server;
|
|
108
|
+
return { body: body(unnamed), bearer: token };
|
|
109
|
+
}
|
|
91
110
|
/** The result of an MCP request, or a throw the pool and `resolveEffect` read as before. */
|
|
92
|
-
async function mcp(path,
|
|
93
|
-
const result = await exchange(path, body, {
|
|
111
|
+
async function mcp(path, scope, request) {
|
|
112
|
+
const result = await exchange(path, scope.body, {
|
|
113
|
+
...request,
|
|
114
|
+
timeoutMs,
|
|
115
|
+
...(scope.bearer ? { bearer: scope.bearer } : {}),
|
|
116
|
+
});
|
|
94
117
|
if (result.kind === "aborted")
|
|
95
118
|
throw result.reason ?? new Error("aborted");
|
|
96
119
|
if (result.kind !== "answer")
|
|
@@ -102,32 +125,43 @@ export function httpToolGate(options) {
|
|
|
102
125
|
return parsed.result;
|
|
103
126
|
throw mcpError(parsed.error);
|
|
104
127
|
}
|
|
105
|
-
async function fireAndForget(path,
|
|
106
|
-
await exchange(path, body, {
|
|
128
|
+
async function fireAndForget(path, scope) {
|
|
129
|
+
await exchange(path, scope.body, {
|
|
130
|
+
timeoutMs: SHORT_TIMEOUT_MS,
|
|
131
|
+
...(scope.bearer ? { bearer: scope.bearer } : {}),
|
|
132
|
+
});
|
|
107
133
|
}
|
|
108
134
|
return {
|
|
109
135
|
recovers: true,
|
|
110
136
|
async openMcp(server) {
|
|
111
|
-
|
|
137
|
+
// The credential is read per request: the connection outlives the advance that opened it.
|
|
138
|
+
await mcp(MCP_CONNECT_PATH, scoped(server, (named) => ({ server: named })), {});
|
|
112
139
|
const client = {
|
|
113
|
-
listTools: (params, request) => mcp(MCP_LIST_PATH,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
})
|
|
125
|
-
},
|
|
140
|
+
listTools: (params, request) => mcp(MCP_LIST_PATH, scoped(server, (named) => ({
|
|
141
|
+
server: named,
|
|
142
|
+
...(params?.cursor ? { cursor: params.cursor } : {}),
|
|
143
|
+
})), request?.signal ? { signal: request.signal } : {}),
|
|
144
|
+
callTool: (params, request) => mcp(TOOL_CALLS_PATH, scoped(server, (named) => ({
|
|
145
|
+
server: named,
|
|
146
|
+
effectId: request?.key ?? randomUUID(),
|
|
147
|
+
name: params.name,
|
|
148
|
+
arguments: params.arguments,
|
|
149
|
+
})), {
|
|
150
|
+
...(request?.signal ? { signal: request.signal } : {}),
|
|
151
|
+
...(request?.key ? { idempotencyKey: request.key } : {}),
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
client,
|
|
156
|
+
close: () => fireAndForget(MCP_CLOSE_PATH, scoped(server, (named) => ({ server: named }))),
|
|
126
157
|
};
|
|
127
|
-
return { client, close: () => fireAndForget(MCP_CLOSE_PATH, { server }) };
|
|
128
158
|
},
|
|
129
159
|
async cancel(request) {
|
|
130
|
-
|
|
160
|
+
const token = options.runTokens?.token(request.sessionId);
|
|
161
|
+
await fireAndForget(`${TOOL_CALLS_PATH}/${encodeURIComponent(request.effectId)}/cancel`, {
|
|
162
|
+
body: undefined,
|
|
163
|
+
...(token ? { bearer: token } : {}),
|
|
164
|
+
});
|
|
131
165
|
},
|
|
132
166
|
async post(delivery, signal) {
|
|
133
167
|
const result = await exchange(DELIVERIES_PATH, {
|
|
@@ -174,7 +208,7 @@ function mcpError(error) {
|
|
|
174
208
|
function gateRefusal(status, parsed) {
|
|
175
209
|
const message = parsed?.error?.message;
|
|
176
210
|
if (status === 401)
|
|
177
|
-
return
|
|
211
|
+
return `The tool gate refused the credential${typeof message === "string" ? `: ${message}` : ""}`;
|
|
178
212
|
return `Tool gate answered ${status}${typeof message === "string" ? `: ${message}` : ""}`;
|
|
179
213
|
}
|
|
180
214
|
function parse(text) {
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Tool Gate's wire format (blueprint §12, F4.1): the routes the loop calls on the gates
|
|
3
3
|
* service for remote MCP servers and Action deliveries. Internal, like the model call route
|
|
4
|
-
* (`contract.ts`): same
|
|
5
|
-
*
|
|
4
|
+
* (`contract.ts`): same optional Tenant header, not in the published OpenAPI documents.
|
|
5
|
+
*
|
|
6
|
+
* Credentials (F5). A session's MCP requests carry its run token, and the gate takes the
|
|
7
|
+
* session from it: `server.sessionId` is then absent (or the token's). Core's credential
|
|
8
|
+
* (`NYLORUN_GATES_TOKEN`) is accepted too, for requests made outside a run, and then
|
|
9
|
+
* `server.sessionId` names the session. Deliveries accept core's credential only.
|
|
6
10
|
*
|
|
7
11
|
* Remote MCP. The gate holds the connection and the vault credential; the loop names the
|
|
8
12
|
* server, never a URL or a credential, and the gate finds it in the session's pinned manifest.
|
|
@@ -12,7 +16,8 @@
|
|
|
12
16
|
* - `POST /nylorun/v1/tool-calls` `{server, effectId, name, arguments}`: one
|
|
13
17
|
* `tools/call`. With an `Idempotency-Key` (the effect id) the call outlives its client and runs
|
|
14
18
|
* once (G3); a re-send joins it or gets its outcome.
|
|
15
|
-
* - `POST /nylorun/v1/tool-calls/{key}/cancel`: aborts a keyed call; `204` either way
|
|
19
|
+
* - `POST /nylorun/v1/tool-calls/{key}/cancel`: aborts a keyed call; `204` either way, `403
|
|
20
|
+
* gate_forbidden` for another session's call under a run token.
|
|
16
21
|
* - `POST /nylorun/v1/mcp/close` `{server}`: closes the server's connection; `204`.
|
|
17
22
|
* Each answers `200 {ok: true, result}` or `200 {ok: false, error}`: an MCP failure is an
|
|
18
23
|
* answer, not a gate error. A keyed call whose earlier attempt was lost with the gateway answers
|
|
@@ -35,14 +40,14 @@ export declare const MAX_DELIVERY_TIMEOUT_MS: number;
|
|
|
35
40
|
/** The only headers the gate forwards on a delivery, lower-cased. */
|
|
36
41
|
export declare const DELIVERY_HEADERS: ReadonlySet<string>;
|
|
37
42
|
export declare const McpServerRefSchema: z.ZodObject<{
|
|
38
|
-
sessionId: z.ZodString
|
|
43
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
39
44
|
agentId: z.ZodOptional<z.ZodString>;
|
|
40
45
|
capabilityId: z.ZodString;
|
|
41
46
|
serverName: z.ZodString;
|
|
42
47
|
}, z.core.$strip>;
|
|
43
48
|
export declare const McpServerBodySchema: z.ZodObject<{
|
|
44
49
|
server: z.ZodObject<{
|
|
45
|
-
sessionId: z.ZodString
|
|
50
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
46
51
|
agentId: z.ZodOptional<z.ZodString>;
|
|
47
52
|
capabilityId: z.ZodString;
|
|
48
53
|
serverName: z.ZodString;
|
|
@@ -50,7 +55,7 @@ export declare const McpServerBodySchema: z.ZodObject<{
|
|
|
50
55
|
}, z.core.$strip>;
|
|
51
56
|
export declare const McpListBodySchema: z.ZodObject<{
|
|
52
57
|
server: z.ZodObject<{
|
|
53
|
-
sessionId: z.ZodString
|
|
58
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
54
59
|
agentId: z.ZodOptional<z.ZodString>;
|
|
55
60
|
capabilityId: z.ZodString;
|
|
56
61
|
serverName: z.ZodString;
|
|
@@ -59,7 +64,7 @@ export declare const McpListBodySchema: z.ZodObject<{
|
|
|
59
64
|
}, z.core.$strip>;
|
|
60
65
|
export declare const ToolCallBodySchema: z.ZodObject<{
|
|
61
66
|
server: z.ZodObject<{
|
|
62
|
-
sessionId: z.ZodString
|
|
67
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
63
68
|
agentId: z.ZodOptional<z.ZodString>;
|
|
64
69
|
capabilityId: z.ZodString;
|
|
65
70
|
serverName: z.ZodString;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Tool Gate's wire format (blueprint §12, F4.1): the routes the loop calls on the gates
|
|
3
3
|
* service for remote MCP servers and Action deliveries. Internal, like the model call route
|
|
4
|
-
* (`contract.ts`): same
|
|
5
|
-
*
|
|
4
|
+
* (`contract.ts`): same optional Tenant header, not in the published OpenAPI documents.
|
|
5
|
+
*
|
|
6
|
+
* Credentials (F5). A session's MCP requests carry its run token, and the gate takes the
|
|
7
|
+
* session from it: `server.sessionId` is then absent (or the token's). Core's credential
|
|
8
|
+
* (`NYLORUN_GATES_TOKEN`) is accepted too, for requests made outside a run, and then
|
|
9
|
+
* `server.sessionId` names the session. Deliveries accept core's credential only.
|
|
6
10
|
*
|
|
7
11
|
* Remote MCP. The gate holds the connection and the vault credential; the loop names the
|
|
8
12
|
* server, never a URL or a credential, and the gate finds it in the session's pinned manifest.
|
|
@@ -12,7 +16,8 @@
|
|
|
12
16
|
* - `POST /nylorun/v1/tool-calls` `{server, effectId, name, arguments}`: one
|
|
13
17
|
* `tools/call`. With an `Idempotency-Key` (the effect id) the call outlives its client and runs
|
|
14
18
|
* once (G3); a re-send joins it or gets its outcome.
|
|
15
|
-
* - `POST /nylorun/v1/tool-calls/{key}/cancel`: aborts a keyed call; `204` either way
|
|
19
|
+
* - `POST /nylorun/v1/tool-calls/{key}/cancel`: aborts a keyed call; `204` either way, `403
|
|
20
|
+
* gate_forbidden` for another session's call under a run token.
|
|
16
21
|
* - `POST /nylorun/v1/mcp/close` `{server}`: closes the server's connection; `204`.
|
|
17
22
|
* Each answers `200 {ok: true, result}` or `200 {ok: false, error}`: an MCP failure is an
|
|
18
23
|
* answer, not a gate error. A keyed call whose earlier attempt was lost with the gateway answers
|
|
@@ -40,7 +45,8 @@ export const DELIVERY_HEADERS = new Set([
|
|
|
40
45
|
"idempotency-key",
|
|
41
46
|
]);
|
|
42
47
|
export const McpServerRefSchema = z.object({
|
|
43
|
-
|
|
48
|
+
/** Required with core's credential; a run token names the session itself. */
|
|
49
|
+
sessionId: z.string().min(1).optional(),
|
|
44
50
|
agentId: z.string().min(1).optional(),
|
|
45
51
|
capabilityId: z.string().min(1),
|
|
46
52
|
serverName: z.string().min(1),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calls a harness executes, as pure helpers over what routes them: the Model Gate (or the
|
|
3
|
+
* Tenant's provider), the MCP pool, the SandboxManager. No store, no record: the session's
|
|
4
|
+
* routing comes with the run (`TurnStart.routing`).
|
|
5
|
+
*/
|
|
6
|
+
import { type AgentManifest } from "@nylorun/core/define";
|
|
7
|
+
import type { HostEffect } from "@nylorun/harness/run";
|
|
8
|
+
import type { AbortReason } from "@nylorun/core/harness-api";
|
|
9
|
+
import type { ModelProvider } from "../core/provider.js";
|
|
10
|
+
import type { ModelGate } from "../gates/model-gate.js";
|
|
11
|
+
import { type McpPool } from "../mcp/pool.js";
|
|
12
|
+
import { type McpSnapshot } from "../mcp/snapshot.js";
|
|
13
|
+
import type { SandboxManager } from "../sandbox/manager.js";
|
|
14
|
+
/** Where a Tenant's model calls go. */
|
|
15
|
+
export interface ModelRoute {
|
|
16
|
+
readonly tenantId: string;
|
|
17
|
+
readonly modelGate: ModelGate;
|
|
18
|
+
readonly modelProvider: ModelProvider;
|
|
19
|
+
/** True when the model comes from the Tenant vault selection, served by `modelGate`. */
|
|
20
|
+
readonly useVaultModel: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** What a session's tool calls are routed by. */
|
|
23
|
+
export interface ToolRouting {
|
|
24
|
+
readonly rootManifest: AgentManifest;
|
|
25
|
+
readonly pluginRoots: Readonly<Record<string, string>>;
|
|
26
|
+
readonly mcpSnapshot?: McpSnapshot;
|
|
27
|
+
/** The session that owns the tree's sandbox, and the sandbox resource it is attached to. */
|
|
28
|
+
readonly sandboxOwnerId: string;
|
|
29
|
+
readonly sandboxId?: string;
|
|
30
|
+
readonly activeTurnId: string | null;
|
|
31
|
+
/** Who the sandbox call's events are claimed for: the run's id. */
|
|
32
|
+
readonly claim?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Call the model for one effect. A provider failure comes back as a failure outcome
|
|
36
|
+
* (Model Calls §6), whichever provider serves the call; only an abort throws, and the
|
|
37
|
+
* advance decides what the abort means.
|
|
38
|
+
*/
|
|
39
|
+
export declare function invokeModel(route: ModelRoute, request: HostEffect, signal: AbortSignal, model?: ModelProvider): Promise<unknown>;
|
|
40
|
+
/**
|
|
41
|
+
* A signal that follows `signal` only for the abort kinds in `kinds`. An MCP call stops on a
|
|
42
|
+
* cancel; at the Tool Gate it also stops waiting on a shutdown, since the gate keeps the call
|
|
43
|
+
* for the next owner. Otherwise a shutdown or a deadline lets the call finish and records it,
|
|
44
|
+
* so the next advance replays it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function abortOn(signal: AbortSignal, kinds: readonly AbortReason[]): AbortSignal;
|
|
47
|
+
/** True when `request` calls `save_artifact` of the `nylorun.artifacts` capability `manifest` has. */
|
|
48
|
+
export declare function isSaveArtifactCall(manifest: AgentManifest | undefined, request: HostEffect): boolean;
|
|
49
|
+
/** True when `request` calls a tool of a remote (`streamable-http` or `sse`) MCP server. */
|
|
50
|
+
export declare function isRemoteMcpCall(routing: Pick<ToolRouting, "rootManifest" | "mcpSnapshot">, request: HostEffect): boolean;
|
|
51
|
+
export declare function callMcpTool(mcp: McpPool, routing: ToolRouting, request: HostEffect, signal: AbortSignal): Promise<unknown>;
|
|
52
|
+
export declare function callSandboxTool(sandbox: SandboxManager, routing: ToolRouting, request: HostEffect, signal: AbortSignal): Promise<unknown>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calls a harness executes, as pure helpers over what routes them: the Model Gate (or the
|
|
3
|
+
* Tenant's provider), the MCP pool, the SandboxManager. No store, no record: the session's
|
|
4
|
+
* routing comes with the run (`TurnStart.routing`).
|
|
5
|
+
*/
|
|
6
|
+
import { ARTIFACTS_CAPABILITY_ID, SAVE_ARTIFACT_TOOL } from "@nylorun/core/define";
|
|
7
|
+
import { runAbortKind } from "@nylorun/harness/api";
|
|
8
|
+
import { classifyThrown } from "../model/classify.js";
|
|
9
|
+
import { findServer } from "../mcp/pool.js";
|
|
10
|
+
import { manifestFor, mcpToolOf } from "../mcp/snapshot.js";
|
|
11
|
+
import { sandboxCapabilityOf } from "../sandbox/capability.js";
|
|
12
|
+
/**
|
|
13
|
+
* Call the model for one effect. A provider failure comes back as a failure outcome
|
|
14
|
+
* (Model Calls §6), whichever provider serves the call; only an abort throws, and the
|
|
15
|
+
* advance decides what the abort means.
|
|
16
|
+
*/
|
|
17
|
+
export async function invokeModel(route, request, signal, model) {
|
|
18
|
+
try {
|
|
19
|
+
if (model)
|
|
20
|
+
return await model(request, signal);
|
|
21
|
+
if (!route.useVaultModel)
|
|
22
|
+
return await route.modelProvider(request, signal);
|
|
23
|
+
return await route.modelGate.call({
|
|
24
|
+
tenantId: route.tenantId,
|
|
25
|
+
sessionId: request.sessionId,
|
|
26
|
+
turnId: request.turnId,
|
|
27
|
+
agentId: request.agentId,
|
|
28
|
+
effectId: request.effectId,
|
|
29
|
+
invocationId: String(request.context.invocationId),
|
|
30
|
+
call: request.input,
|
|
31
|
+
}, signal);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (signal.aborted) {
|
|
35
|
+
// A call that outlives this process stops only when told to: on a user cancel. After a
|
|
36
|
+
// shutdown or a lost lease the next owner re-sends it and picks up its outcome (P1.2).
|
|
37
|
+
if (!model && route.useVaultModel && runAbortKind(signal) === "cancel" && route.modelGate.cancel)
|
|
38
|
+
await route.modelGate.cancel({
|
|
39
|
+
tenantId: route.tenantId,
|
|
40
|
+
sessionId: request.sessionId,
|
|
41
|
+
effectId: request.effectId,
|
|
42
|
+
});
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
return classifyThrown(error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A signal that follows `signal` only for the abort kinds in `kinds`. An MCP call stops on a
|
|
50
|
+
* cancel; at the Tool Gate it also stops waiting on a shutdown, since the gate keeps the call
|
|
51
|
+
* for the next owner. Otherwise a shutdown or a deadline lets the call finish and records it,
|
|
52
|
+
* so the next advance replays it.
|
|
53
|
+
*/
|
|
54
|
+
export function abortOn(signal, kinds) {
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const follow = () => {
|
|
57
|
+
const kind = runAbortKind(signal);
|
|
58
|
+
if (kind && kinds.includes(kind))
|
|
59
|
+
controller.abort(signal.reason);
|
|
60
|
+
};
|
|
61
|
+
if (signal.aborted)
|
|
62
|
+
follow();
|
|
63
|
+
else
|
|
64
|
+
signal.addEventListener("abort", follow, { once: true });
|
|
65
|
+
return controller.signal;
|
|
66
|
+
}
|
|
67
|
+
/** True when `request` calls `save_artifact` of the `nylorun.artifacts` capability `manifest` has. */
|
|
68
|
+
export function isSaveArtifactCall(manifest, request) {
|
|
69
|
+
return (request.kind === "tool" &&
|
|
70
|
+
request.capabilityId === ARTIFACTS_CAPABILITY_ID &&
|
|
71
|
+
request.toolName === SAVE_ARTIFACT_TOOL &&
|
|
72
|
+
manifest?.capabilities.some((capability) => capability.id === ARTIFACTS_CAPABILITY_ID) === true);
|
|
73
|
+
}
|
|
74
|
+
/** True when `request` calls a tool of a remote (`streamable-http` or `sse`) MCP server. */
|
|
75
|
+
export function isRemoteMcpCall(routing, request) {
|
|
76
|
+
if (request.kind !== "tool")
|
|
77
|
+
return false;
|
|
78
|
+
const tool = mcpToolOf(routing.mcpSnapshot, request);
|
|
79
|
+
if (!tool)
|
|
80
|
+
return false;
|
|
81
|
+
const declared = findServer(routing.rootManifest, tool.agentId, tool.capabilityId, tool.serverName);
|
|
82
|
+
return declared !== undefined && declared.server.type !== "stdio";
|
|
83
|
+
}
|
|
84
|
+
export async function callMcpTool(mcp, routing, request, signal) {
|
|
85
|
+
const tool = mcpToolOf(routing.mcpSnapshot, request);
|
|
86
|
+
if (!tool)
|
|
87
|
+
throw new Error(`MCP tool '${request.toolName ?? ""}' is not in the session snapshot`);
|
|
88
|
+
return mcp.call({
|
|
89
|
+
sessionId: request.sessionId,
|
|
90
|
+
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
91
|
+
capabilityId: tool.capabilityId,
|
|
92
|
+
serverName: tool.serverName,
|
|
93
|
+
serverToolName: tool.serverToolName,
|
|
94
|
+
args: request.input,
|
|
95
|
+
manifest: routing.rootManifest,
|
|
96
|
+
pluginRoots: routing.pluginRoots,
|
|
97
|
+
effectId: request.effectId,
|
|
98
|
+
signal,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export async function callSandboxTool(sandbox, routing, request, signal) {
|
|
102
|
+
const capability = sandboxCapabilityOf(manifestFor(routing.rootManifest, request.agent), request.capabilityId, request.toolName);
|
|
103
|
+
if (!capability)
|
|
104
|
+
throw new Error(`'${request.toolName ?? ""}' is not a sandbox tool`);
|
|
105
|
+
// Agents used as tools share the session's sandbox; the tree declares one sandbox spec.
|
|
106
|
+
return sandbox.run({
|
|
107
|
+
id: routing.sandboxOwnerId,
|
|
108
|
+
activeTurnId: routing.activeTurnId,
|
|
109
|
+
manifest: routing.rootManifest,
|
|
110
|
+
...(routing.sandboxId === undefined ? {} : { sandboxId: routing.sandboxId }),
|
|
111
|
+
...(routing.claim === undefined ? {} : { claim: routing.claim }),
|
|
112
|
+
}, capability, request.toolName, request.input, signal);
|
|
113
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HarnessExecutors, HarnessRun, McpRecorder, PreparedRun } from "@nylorun/harness/api";
|
|
2
|
+
import type { ToolGate } from "../gates/tool-gate.js";
|
|
3
|
+
import { type McpPool } from "../mcp/pool.js";
|
|
4
|
+
import type { SandboxManager } from "../sandbox/manager.js";
|
|
5
|
+
import { type ModelRoute, type ToolRouting } from "./calls.js";
|
|
6
|
+
export interface HarnessExecutorsOptions {
|
|
7
|
+
/** Where model calls go. Read per call: a harness process learns its Tenant at `hello`. */
|
|
8
|
+
readonly model: ModelRoute;
|
|
9
|
+
readonly toolGate: Pick<ToolGate, "recovers" | "cancel">;
|
|
10
|
+
readonly mcp: McpPool;
|
|
11
|
+
/** The SandboxManager; a harness process creates it once `hello` names the Tenant. */
|
|
12
|
+
readonly sandbox: () => SandboxManager;
|
|
13
|
+
}
|
|
14
|
+
export declare function harnessExecutors(options: HarnessExecutorsOptions): HarnessExecutors;
|
|
15
|
+
/**
|
|
16
|
+
* Readies the run's MCP servers (§10.5 step 3): the first segment of a session discovers their
|
|
17
|
+
* tools and records the snapshot (the first recorded wins); later ones reconnect them and record
|
|
18
|
+
* what failed. Nothing for a session without MCP servers.
|
|
19
|
+
*/
|
|
20
|
+
export declare function prepareMcp(pool: McpPool, run: HarnessRun, record: McpRecorder): Promise<PreparedRun | undefined>;
|
|
21
|
+
/** A run's routing, as the call helpers take it. Its sandbox events are claimed for the run. */
|
|
22
|
+
export declare function routingOf(run: HarnessRun): ToolRouting;
|