@nylorun/runtime 0.23.0-beta → 0.24.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +148 -0
- package/README.md +79 -28
- package/dist/adapters/execution/restate.js +1 -1
- package/dist/adapters/replication/pgoutput.d.ts +2 -0
- package/dist/adapters/replication/pgoutput.js +6 -3
- package/dist/adapters/sandbox/local.d.ts +9 -1
- package/dist/adapters/sandbox/local.js +16 -4
- package/dist/adapters/sandbox/virtual.js +4 -1
- package/dist/api/a2a/endpoint.js +5 -1
- package/dist/api/a2a/routes.d.ts +2 -0
- package/dist/api/a2a/routes.js +2 -2
- package/dist/api/a2a/tasks.js +2 -1
- package/dist/api/ag-ui/endpoint.js +4 -2
- package/dist/api/ag-ui/history.js +4 -1
- package/dist/api/ag-ui/routes.d.ts +4 -1
- package/dist/api/ag-ui/routes.js +11 -10
- package/dist/api/ag-ui/translate.js +3 -2
- package/dist/api/components.d.ts +4953 -1204
- package/dist/api/components.js +25 -16
- package/dist/api/gate/keys.js +7 -1
- package/dist/api/gate/routes.d.ts +6 -0
- package/dist/api/gate/routes.js +39 -3
- package/dist/api/http/app.js +4 -4
- package/dist/api/http/define.d.ts +6 -0
- package/dist/api/http/define.js +13 -1
- package/dist/api/http/respond.js +1 -1
- package/dist/api/http/routes/artifacts.js +35 -8
- package/dist/api/http/routes/coverage.d.ts +9 -0
- package/dist/api/http/routes/coverage.js +34 -0
- package/dist/api/http/routes/environments.d.ts +11 -0
- package/dist/api/http/routes/environments.js +283 -0
- package/dist/api/http/routes/mcp.d.ts +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- package/dist/api/http/routes/me.js +1 -1
- package/dist/api/http/routes/reads.d.ts +45 -9
- package/dist/api/http/routes/reads.js +54 -5
- package/dist/api/http/routes/sessions.js +11 -6
- package/dist/api/http/routes/tenant.js +32 -10
- package/dist/api/openapi.js +31 -26
- package/dist/artifacts/disk.d.ts +59 -0
- package/dist/artifacts/disk.js +563 -0
- package/dist/artifacts/export.d.ts +1 -1
- package/dist/artifacts/export.js +2 -3
- package/dist/artifacts/folders.d.ts +6 -3
- package/dist/artifacts/folders.js +17 -12
- package/dist/artifacts/service.d.ts +2 -1
- package/dist/artifacts/service.js +6 -1
- package/dist/artifacts/workspace.d.ts +1 -1
- package/dist/artifacts/workspace.js +1 -1
- package/dist/core/flow-host.d.ts +2 -3
- package/dist/core/flow-host.js +3 -0
- package/dist/core/provider.d.ts +3 -2
- package/dist/core/provider.js +5 -37
- package/dist/core/sandbox-routes.d.ts +2 -21
- package/dist/core/sandbox-routes.js +10 -43
- package/dist/execution/types.d.ts +1 -1
- package/dist/gates/contract.d.ts +3 -24
- package/dist/gates/contract.js +1 -36
- package/dist/gates/egress-credentials.d.ts +9 -0
- package/dist/gates/egress-credentials.js +11 -4
- package/dist/gates/egress.d.ts +28 -1
- package/dist/gates/egress.js +115 -10
- package/dist/gates/fixture-model.d.ts +28 -0
- package/dist/gates/fixture-model.js +92 -0
- package/dist/gates/handler.js +4 -4
- package/dist/gates/http-tool.d.ts +6 -2
- package/dist/gates/http-tool.js +3 -5
- package/dist/gates/in-process.d.ts +5 -0
- package/dist/gates/in-process.js +12 -1
- package/dist/gates/journal.d.ts +30 -0
- package/dist/gates/journal.js +52 -0
- package/dist/gates/mcp-handler.d.ts +4 -0
- package/dist/gates/mcp-handler.js +5 -1
- package/dist/gates/meter.d.ts +10 -2
- package/dist/gates/meter.js +20 -5
- package/dist/gates/policy.d.ts +49 -0
- package/dist/gates/policy.js +134 -0
- package/dist/gates/tenant-vaults.d.ts +7 -7
- package/dist/gates/tenant-vaults.js +5 -5
- package/dist/gates/tool-calls.d.ts +10 -5
- package/dist/gates/tool-calls.js +28 -10
- package/dist/gates/tool-client.d.ts +8 -0
- package/dist/gates/tool-client.js +27 -11
- package/dist/gates/tool-contract.d.ts +10 -68
- package/dist/gates/tool-contract.js +1 -85
- package/dist/gates/tool-gate.d.ts +1 -1
- package/dist/harness/calls.d.ts +12 -5
- package/dist/harness/calls.js +21 -12
- package/dist/harness/executors.js +12 -9
- package/dist/harness/main.js +18 -5
- package/dist/harness/pod.d.ts +34 -0
- package/dist/harness/pod.js +41 -0
- package/dist/harness/service.d.ts +0 -1
- package/dist/harness/service.js +13 -5
- package/dist/harness/setup.d.ts +27 -0
- package/dist/harness/setup.js +150 -0
- package/dist/harness/ws-client.js +3 -2
- package/dist/harness/ws-port.d.ts +1 -5
- package/dist/harness/ws-port.js +1 -5
- package/dist/harness-api/in-process.js +3 -3
- package/dist/harness-api/record.d.ts +15 -1
- package/dist/harness-api/record.js +174 -44
- package/dist/harness-api/redact.d.ts +3 -0
- package/dist/harness-api/redact.js +45 -0
- package/dist/harness-api/server.d.ts +8 -0
- package/dist/harness-api/server.js +93 -13
- package/dist/harness-api/start.d.ts +10 -3
- package/dist/harness-api/start.js +40 -10
- package/dist/harness-api/tool-journal.d.ts +96 -0
- package/dist/harness-api/tool-journal.js +102 -0
- package/dist/harness-api/tool-status.d.ts +13 -0
- package/dist/harness-api/tool-status.js +26 -0
- package/dist/harness-api/workspace.d.ts +6 -3
- package/dist/harness-api/workspace.js +27 -11
- package/dist/harness-api/ws-server.d.ts +11 -3
- package/dist/harness-api/ws-server.js +94 -15
- package/dist/host/gates.d.ts +2 -1
- package/dist/host/gates.js +10 -2
- package/dist/host/main.js +54 -5
- package/dist/host/operate.js +67 -5
- package/dist/host/stack-config.d.ts +64 -0
- package/dist/host/stack-config.js +61 -1
- package/dist/keys/client.js +4 -1
- package/dist/keys/keys.d.ts +5 -5
- package/dist/keys/keys.js +5 -7
- package/dist/management-openapi.json +999 -2211
- package/dist/mcp/connect.d.ts +25 -20
- package/dist/mcp/connect.js +29 -30
- package/dist/mcp/pool.d.ts +2 -2
- package/dist/mcp/pool.js +1 -1
- package/dist/mcp/preview.d.ts +3 -3
- package/dist/mcp/preview.js +10 -20
- package/dist/mcp/snapshot.d.ts +5 -11
- package/dist/mcp/snapshot.js +6 -7
- package/dist/model/pi-model.js +8 -0
- package/dist/openapi.json +6955 -2526
- package/dist/reads/types.d.ts +16 -3
- package/dist/record/append.d.ts +3 -1
- package/dist/record/append.js +1 -0
- package/dist/record/envelope.d.ts +5 -1
- package/dist/record/envelope.js +7 -3
- package/dist/record/environment.d.ts +25 -0
- package/dist/record/environment.js +44 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/sandbox/disk-fs.d.ts +95 -0
- package/dist/sandbox/disk-fs.js +206 -0
- package/dist/sandbox/join.d.ts +17 -18
- package/dist/sandbox/join.js +42 -15
- package/dist/sandbox/manager.d.ts +16 -8
- package/dist/sandbox/manager.js +33 -17
- package/dist/sandbox/placement.d.ts +13 -12
- package/dist/sandbox/placement.js +29 -12
- package/dist/sandbox/pods/client.d.ts +6 -1
- package/dist/sandbox/pods/lifecycle.d.ts +28 -27
- package/dist/sandbox/pods/lifecycle.js +14 -26
- package/dist/sandbox/pods/reconcile.js +18 -12
- package/dist/sandbox/pods/spec.d.ts +15 -13
- package/dist/sandbox/pods/spec.js +16 -20
- package/dist/sandbox/records.d.ts +2 -2
- package/dist/sandbox/records.js +3 -3
- package/dist/sandbox/resolve.d.ts +35 -27
- package/dist/sandbox/resolve.js +66 -41
- package/dist/sandbox/session-sandbox.js +1 -1
- package/dist/sandbox/setup.d.ts +61 -0
- package/dist/sandbox/setup.js +123 -0
- package/dist/sandbox/share.d.ts +11 -21
- package/dist/sandbox/share.js +12 -41
- package/dist/sandbox/tenant-config.d.ts +12 -14
- package/dist/sandbox/tenant-config.js +12 -17
- package/dist/sandbox/types.d.ts +6 -0
- package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
- package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
- package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/reads.js +117 -17
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/record.d.ts +7 -0
- package/dist/store/postgres/record.js +23 -2
- package/dist/store/postgres/schema.d.ts +934 -761
- package/dist/store/postgres/schema.js +102 -73
- package/dist/store/postgres/store.js +119 -66
- package/dist/store/types.d.ts +109 -48
- package/dist/tenant/advance.d.ts +7 -6
- package/dist/tenant/advance.js +115 -42
- package/dist/tenant/artifact-tool.js +3 -4
- package/dist/tenant/commands.d.ts +15 -2
- package/dist/tenant/commands.js +93 -12
- package/dist/tenant/context.d.ts +48 -22
- package/dist/tenant/context.js +4 -15
- package/dist/tenant/effects.d.ts +1 -1
- package/dist/tenant/effects.js +37 -20
- package/dist/tenant/environments.d.ts +167 -0
- package/dist/tenant/environments.js +823 -0
- package/dist/tenant/harness-tokens.d.ts +21 -0
- package/dist/tenant/harness-tokens.js +61 -0
- package/dist/tenant/host-token.d.ts +1 -1
- package/dist/tenant/host-token.js +2 -4
- package/dist/tenant/identity-file.d.ts +4 -4
- package/dist/tenant/identity-file.js +17 -8
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/principals.js +1 -1
- package/dist/tenant/reset.js +3 -3
- package/dist/tenant/runtime.d.ts +6 -1
- package/dist/tenant/runtime.js +7 -1
- package/dist/tenant/session.d.ts +3 -3
- package/dist/tenant/session.js +0 -14
- package/dist/tenant/sessions.d.ts +13 -13
- package/dist/tenant/sessions.js +61 -138
- package/dist/tenant/signing-keys.d.ts +5 -1
- package/dist/tenant/status.js +3 -3
- package/dist/tenant/sweep.js +12 -6
- package/dist/tenant/transcript.d.ts +210 -20
- package/dist/tenant/transcript.js +62 -11
- package/dist/tenant/turn-timing.d.ts +43 -0
- package/dist/tenant/turn-timing.js +39 -0
- package/dist/tenant/types.d.ts +2 -0
- package/dist/traces/ids.d.ts +42 -0
- package/dist/traces/ids.js +85 -0
- package/dist/traces/key.d.ts +6 -0
- package/dist/traces/key.js +37 -0
- package/dist/traces/map.d.ts +113 -0
- package/dist/traces/map.js +317 -0
- package/dist/traces/options.d.ts +7 -0
- package/dist/traces/options.js +1 -0
- package/dist/traces/otlp.d.ts +42 -0
- package/dist/traces/otlp.js +84 -0
- package/dist/traces/projector.d.ts +58 -0
- package/dist/traces/projector.js +200 -0
- package/dist/traces/read.d.ts +23 -0
- package/dist/traces/read.js +16 -0
- package/dist/vault/coverage.d.ts +26 -0
- package/dist/vault/coverage.js +64 -0
- package/dist/vault/error.d.ts +11 -1
- package/dist/vault/error.js +5 -1
- package/dist/vault/service.d.ts +47 -48
- package/dist/vault/service.js +321 -382
- package/dist/vault/sources.d.ts +5 -16
- package/dist/vault/sources.js +6 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/package.json +13 -13
- package/dist/api/http/routes/sandboxes.d.ts +0 -9
- package/dist/api/http/routes/sandboxes.js +0 -153
- package/dist/api/http/routes/vaults.d.ts +0 -10
- package/dist/api/http/routes/vaults.js +0 -139
- package/dist/record/sandbox.d.ts +0 -25
- package/dist/record/sandbox.js +0 -43
- package/dist/tenant/sandboxes.d.ts +0 -123
- package/dist/tenant/sandboxes.js +0 -566
package/dist/mcp/connect.d.ts
CHANGED
|
@@ -12,10 +12,16 @@ export interface McpRequestOptions {
|
|
|
12
12
|
/** Options of one tool call. */
|
|
13
13
|
export interface McpCallOptions extends McpRequestOptions {
|
|
14
14
|
/**
|
|
15
|
-
* The call's effect id. A gate
|
|
16
|
-
*
|
|
15
|
+
* The call's effect id. A gate keeps a keyed call's outcome, so a re-send after a takeover
|
|
16
|
+
* joins it (F4.1), and the server gets it in `_meta` (R5.5).
|
|
17
17
|
*/
|
|
18
18
|
readonly key?: string;
|
|
19
|
+
/**
|
|
20
|
+
* The call's span context (R7.4), sent as the `traceparent` header on the call's requests where
|
|
21
|
+
* this process holds the connection. A client of the gates service leaves it out: the gate reads
|
|
22
|
+
* it from the call's journal row.
|
|
23
|
+
*/
|
|
24
|
+
readonly traceparent?: string;
|
|
19
25
|
}
|
|
20
26
|
/** A page of `tools/list`, as the MCP SDK returns it. */
|
|
21
27
|
export interface McpToolPage {
|
|
@@ -73,9 +79,8 @@ export type McpToolFailure = {
|
|
|
73
79
|
readonly message: string;
|
|
74
80
|
/** True when calling again may work (R2b C7). */
|
|
75
81
|
readonly retryable?: boolean;
|
|
76
|
-
/** With `credential_rejected`: the server
|
|
82
|
+
/** With `credential_rejected`: the server. */
|
|
77
83
|
readonly server?: string;
|
|
78
|
-
readonly vault?: "installation" | "user";
|
|
79
84
|
/** How many credential values the message had in it, replaced (R2b C8). */
|
|
80
85
|
readonly redacted?: number;
|
|
81
86
|
};
|
|
@@ -93,8 +98,8 @@ export type McpToolOutcome = {
|
|
|
93
98
|
* - `mcp.forbidden`: the server answered `403`.
|
|
94
99
|
* - `mcp.error`: the server answered a JSON-RPC error; the message has its code.
|
|
95
100
|
* - `mcp.status`: the server answered another HTTP error status, without a JSON-RPC error.
|
|
96
|
-
* - `mcp.lost`: sent, and the answer was lost, so the tool may have run. The model sees it
|
|
97
|
-
*
|
|
101
|
+
* - `mcp.lost`: sent, and the answer was lost, so the tool may have run. The model sees it, and
|
|
102
|
+
* decides whether to call again (R5; it replaced R2b's read-only and idempotent exception).
|
|
98
103
|
* - `mcp.too-large`: the answer passed `MCP_RESULT_MAX_BYTES` (R2b C11), as an HTTP tool's
|
|
99
104
|
* answer is `http.too-large`. The tool ran; its answer was not kept.
|
|
100
105
|
*/
|
|
@@ -110,9 +115,8 @@ export interface McpCallFailure {
|
|
|
110
115
|
/** The request reached the server, so it may have acted on it. */
|
|
111
116
|
readonly sent: boolean;
|
|
112
117
|
readonly retryable: boolean;
|
|
113
|
-
/** With `credential_rejected`: the server
|
|
118
|
+
/** With `credential_rejected`: the server. */
|
|
114
119
|
readonly server?: string;
|
|
115
|
-
readonly vault?: "installation" | "user";
|
|
116
120
|
/** How many credential values the message had in it, replaced (R2b C8). */
|
|
117
121
|
readonly redacted?: number;
|
|
118
122
|
}
|
|
@@ -134,16 +138,16 @@ export declare class McpCallFailed extends Error {
|
|
|
134
138
|
*/
|
|
135
139
|
export declare class CredentialRejected extends McpCallFailed {
|
|
136
140
|
readonly server: string;
|
|
137
|
-
/**
|
|
138
|
-
readonly
|
|
141
|
+
/** Whether a credential of the session's Environment was sent. */
|
|
142
|
+
readonly sent: boolean;
|
|
139
143
|
/**
|
|
140
144
|
* The answer's `WWW-Authenticate`, which may name the server's protected-resource metadata
|
|
141
145
|
* (RFC 9728 `resource_metadata`): a tool preview reads it (R2b C12). Never shown to the model.
|
|
142
146
|
*/
|
|
143
147
|
readonly challenge?: string | undefined;
|
|
144
148
|
constructor(server: string,
|
|
145
|
-
/**
|
|
146
|
-
|
|
149
|
+
/** Whether a credential of the session's Environment was sent. */
|
|
150
|
+
sent: boolean,
|
|
147
151
|
/**
|
|
148
152
|
* The answer's `WWW-Authenticate`, which may name the server's protected-resource metadata
|
|
149
153
|
* (RFC 9728 `resource_metadata`): a tool preview reads it (R2b C12). Never shown to the model.
|
|
@@ -162,9 +166,16 @@ export declare class McpConnectionStale extends McpCallFailed {
|
|
|
162
166
|
}
|
|
163
167
|
/** A tool call that never reached its server: its connection could not be opened. */
|
|
164
168
|
export declare function unreachable(server: string, error: unknown): McpCallFailed;
|
|
169
|
+
/**
|
|
170
|
+
* Where a `tools/call` carries its key (R5.5): MCP has no standard key field, and `_meta` is the
|
|
171
|
+
* spec's place for extra request data. The key is the call's effect id, the same on every send,
|
|
172
|
+
* so a server that reads it can run the call once.
|
|
173
|
+
*/
|
|
174
|
+
export declare const IDEMPOTENCY_META_KEY = "nylorun/idempotency-key";
|
|
165
175
|
/**
|
|
166
176
|
* An SDK `Client` as an `McpClient`. Its answers and failures carry no credential value sent
|
|
167
|
-
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7).
|
|
177
|
+
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7). A keyed call sends its key in
|
|
178
|
+
* `_meta` (R5.5).
|
|
168
179
|
*/
|
|
169
180
|
export declare function sdkClient(client: Client, serverName: string): McpClient;
|
|
170
181
|
export declare function openMcpServer(input: {
|
|
@@ -212,11 +223,5 @@ export declare function listMcpTools(client: McpClient, input: {
|
|
|
212
223
|
* be opened again (the caller retries once), and with any error that leaves the call's fate
|
|
213
224
|
* unknown.
|
|
214
225
|
*/
|
|
215
|
-
export declare function callMcpTool(client: McpClient, serverToolName: string, args: unknown, options?: McpCallOptions
|
|
216
|
-
/**
|
|
217
|
-
* The tool is read-only or idempotent (`McpToolRecord.annotations`): a call whose answer was
|
|
218
|
-
* lost after it was sent is `mcp.lost` to the model. Otherwise it is left uncertain (Q15).
|
|
219
|
-
*/
|
|
220
|
-
readonly retrySafe?: boolean;
|
|
221
|
-
}): Promise<McpToolOutcome>;
|
|
226
|
+
export declare function callMcpTool(client: McpClient, serverToolName: string, args: unknown, options?: McpCallOptions): Promise<McpToolOutcome>;
|
|
222
227
|
export declare function diagnosticFromError(capabilityId: string, serverName: string, error: unknown): McpDiagnostic;
|
package/dist/mcp/connect.js
CHANGED
|
@@ -29,7 +29,7 @@ export class McpCallFailed extends Error {
|
|
|
29
29
|
/** The failure the gate answered, as the class the loop's own call would have thrown. */
|
|
30
30
|
static from(failure) {
|
|
31
31
|
return failure.code === "credential_rejected" && failure.server !== undefined
|
|
32
|
-
? new CredentialRejected(failure.server, failure.
|
|
32
|
+
? new CredentialRejected(failure.server, !failure.message.includes("holds none"))
|
|
33
33
|
: new McpCallFailed(failure);
|
|
34
34
|
}
|
|
35
35
|
/** The failed tool outcome the model sees. */
|
|
@@ -41,7 +41,6 @@ export class McpCallFailed extends Error {
|
|
|
41
41
|
message: failure.message,
|
|
42
42
|
retryable: failure.retryable,
|
|
43
43
|
...(failure.server === undefined ? {} : { server: failure.server }),
|
|
44
|
-
...(failure.vault === undefined ? {} : { vault: failure.vault }),
|
|
45
44
|
...(failure.redacted ? { redacted: failure.redacted } : {}),
|
|
46
45
|
};
|
|
47
46
|
}
|
|
@@ -54,11 +53,11 @@ export class McpCallFailed extends Error {
|
|
|
54
53
|
*/
|
|
55
54
|
export class CredentialRejected extends McpCallFailed {
|
|
56
55
|
server;
|
|
57
|
-
|
|
56
|
+
sent;
|
|
58
57
|
challenge;
|
|
59
58
|
constructor(server,
|
|
60
|
-
/**
|
|
61
|
-
|
|
59
|
+
/** Whether a credential of the session's Environment was sent. */
|
|
60
|
+
sent,
|
|
62
61
|
/**
|
|
63
62
|
* The answer's `WWW-Authenticate`, which may name the server's protected-resource metadata
|
|
64
63
|
* (RFC 9728 `resource_metadata`): a tool preview reads it (R2b C12). Never shown to the model.
|
|
@@ -66,16 +65,15 @@ export class CredentialRejected extends McpCallFailed {
|
|
|
66
65
|
challenge) {
|
|
67
66
|
super({
|
|
68
67
|
code: "credential_rejected",
|
|
69
|
-
message:
|
|
70
|
-
? `The MCP server '${server}' answered HTTP 401: it
|
|
71
|
-
: `The MCP server '${server}' answered HTTP 401: it
|
|
68
|
+
message: sent
|
|
69
|
+
? `The MCP server '${server}' answered HTTP 401: it rejected the credential from the session's Environment`
|
|
70
|
+
: `The MCP server '${server}' answered HTTP 401: it needs a credential, and the session's Environment holds none for it`,
|
|
72
71
|
sent: true,
|
|
73
72
|
retryable: false,
|
|
74
73
|
server,
|
|
75
|
-
...(vault === undefined ? {} : { vault }),
|
|
76
74
|
});
|
|
77
75
|
this.server = server;
|
|
78
|
-
this.
|
|
76
|
+
this.sent = sent;
|
|
79
77
|
this.challenge = challenge;
|
|
80
78
|
this.name = "CredentialRejected";
|
|
81
79
|
}
|
|
@@ -106,18 +104,26 @@ export function unreachable(server, error) {
|
|
|
106
104
|
}
|
|
107
105
|
/** The operation a request of the transport's fetch belongs to. */
|
|
108
106
|
const tracking = new AsyncLocalStorage();
|
|
109
|
-
function tracked(call, run) {
|
|
107
|
+
function tracked(call, run, traceparent) {
|
|
110
108
|
const tracker = {
|
|
111
109
|
secrets: new Set(),
|
|
112
110
|
sent: false,
|
|
113
111
|
settled: false,
|
|
114
112
|
...(call ? { calling: new AbortController() } : {}),
|
|
113
|
+
...(traceparent ? { traceparent } : {}),
|
|
115
114
|
};
|
|
116
115
|
return tracking.run(tracker, () => run(tracker));
|
|
117
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Where a `tools/call` carries its key (R5.5): MCP has no standard key field, and `_meta` is the
|
|
119
|
+
* spec's place for extra request data. The key is the call's effect id, the same on every send,
|
|
120
|
+
* so a server that reads it can run the call once.
|
|
121
|
+
*/
|
|
122
|
+
export const IDEMPOTENCY_META_KEY = "nylorun/idempotency-key";
|
|
118
123
|
/**
|
|
119
124
|
* An SDK `Client` as an `McpClient`. Its answers and failures carry no credential value sent
|
|
120
|
-
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7).
|
|
125
|
+
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7). A keyed call sends its key in
|
|
126
|
+
* `_meta` (R5.5).
|
|
121
127
|
*/
|
|
122
128
|
export function sdkClient(client, serverName) {
|
|
123
129
|
return {
|
|
@@ -133,7 +139,10 @@ export function sdkClient(client, serverName) {
|
|
|
133
139
|
callTool: (params, options) => tracked(true, async (tracker) => {
|
|
134
140
|
const lost = tracker.calling.signal;
|
|
135
141
|
try {
|
|
136
|
-
const
|
|
142
|
+
const keyed = options?.key === undefined
|
|
143
|
+
? params
|
|
144
|
+
: { ...params, _meta: { [IDEMPOTENCY_META_KEY]: options.key } };
|
|
145
|
+
const result = (await client.callTool(keyed, undefined, {
|
|
137
146
|
signal: options?.signal ? AbortSignal.any([options.signal, lost]) : lost,
|
|
138
147
|
}));
|
|
139
148
|
// An answer the stream did not count (an `sse` server answers on its event stream).
|
|
@@ -151,7 +160,7 @@ export function sdkClient(client, serverName) {
|
|
|
151
160
|
finally {
|
|
152
161
|
tracker.settled = true;
|
|
153
162
|
}
|
|
154
|
-
}),
|
|
163
|
+
}, options?.traceparent),
|
|
155
164
|
};
|
|
156
165
|
}
|
|
157
166
|
export async function openMcpServer(input) {
|
|
@@ -275,7 +284,6 @@ export async function listMcpTools(client, input) {
|
|
|
275
284
|
continue;
|
|
276
285
|
const { tool, inputSchema } = candidate;
|
|
277
286
|
const outputSchema = tool.outputSchema ? usableSchema(tool.outputSchema) : undefined;
|
|
278
|
-
const annotations = retryHints(tool.annotations);
|
|
279
287
|
tools.push({
|
|
280
288
|
capabilityId: input.capabilityId,
|
|
281
289
|
serverName: input.serverName,
|
|
@@ -286,7 +294,6 @@ export async function listMcpTools(client, input) {
|
|
|
286
294
|
: {}),
|
|
287
295
|
inputSchema,
|
|
288
296
|
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
289
|
-
...(annotations === undefined ? {} : { annotations }),
|
|
290
297
|
});
|
|
291
298
|
}
|
|
292
299
|
return { tools, omitted, renamed, disabled, unknownTools };
|
|
@@ -307,6 +314,7 @@ export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
|
307
314
|
}, {
|
|
308
315
|
...(options.key === undefined ? {} : { key: options.key }),
|
|
309
316
|
...(options.signal ? { signal: options.signal } : {}),
|
|
317
|
+
...(options.traceparent === undefined ? {} : { traceparent: options.traceparent }),
|
|
310
318
|
});
|
|
311
319
|
}
|
|
312
320
|
catch (error) {
|
|
@@ -315,11 +323,7 @@ export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
|
315
323
|
// The connection's to handle: its holder opens it again and sends the call once more.
|
|
316
324
|
if (error instanceof McpConnectionStale)
|
|
317
325
|
throw error;
|
|
318
|
-
//
|
|
319
|
-
// tool the server says is safe to repeat; any other waits for an operator.
|
|
320
|
-
if (error.code === "mcp.lost" && !options.retrySafe)
|
|
321
|
-
throw new Error(error.message);
|
|
322
|
-
// Otherwise the outcome is known: the model sees it.
|
|
326
|
+
// The model sees it, `mcp.lost` included: sent, then lost, so the tool may have run (R5).
|
|
323
327
|
return error.outcome();
|
|
324
328
|
}
|
|
325
329
|
const { result } = answer;
|
|
@@ -418,6 +422,9 @@ function authorizedFetch(server, endpoint, authorize, policy) {
|
|
|
418
422
|
for (const [key, value] of Object.entries(result.headers))
|
|
419
423
|
headers.set(key, value);
|
|
420
424
|
const tracker = tracking.getStore();
|
|
425
|
+
// The tool call's span (R7.4): per request, so a pooled connection carries each call's own.
|
|
426
|
+
if (tracker?.traceparent)
|
|
427
|
+
headers.set("traceparent", tracker.traceparent);
|
|
421
428
|
if (tracker && result.status === "authorized")
|
|
422
429
|
for (const secret of credentialSecrets(result.headers, result.identity))
|
|
423
430
|
tracker.secrets.add(secret);
|
|
@@ -436,7 +443,7 @@ function authorizedFetch(server, endpoint, authorize, policy) {
|
|
|
436
443
|
tracker.sent = true;
|
|
437
444
|
if (response.status === 401) {
|
|
438
445
|
await response.body?.cancel().catch(() => undefined);
|
|
439
|
-
throw new CredentialRejected(server.name, result.status === "authorized"
|
|
446
|
+
throw new CredentialRejected(server.name, result.status === "authorized", response.headers.get("www-authenticate") ?? undefined);
|
|
440
447
|
}
|
|
441
448
|
// The server ended the connection's session: the tool never saw the request.
|
|
442
449
|
if (post && response.status === 404 && headers.has("mcp-session-id")) {
|
|
@@ -622,14 +629,6 @@ function rpcErrorOf(body) {
|
|
|
622
629
|
}
|
|
623
630
|
return undefined;
|
|
624
631
|
}
|
|
625
|
-
/** The hints that calling a tool again is safe, when the server gives any. */
|
|
626
|
-
function retryHints(annotations) {
|
|
627
|
-
const readOnly = annotations?.readOnlyHint === true;
|
|
628
|
-
const idempotent = annotations?.idempotentHint === true;
|
|
629
|
-
if (!readOnly && !idempotent)
|
|
630
|
-
return undefined;
|
|
631
|
-
return { ...(readOnly ? { readOnlyHint: true } : {}), ...(idempotent ? { idempotentHint: true } : {}) };
|
|
632
|
-
}
|
|
633
632
|
function sameOrigin(requestUrl, serverUrl) {
|
|
634
633
|
try {
|
|
635
634
|
return new URL(requestUrl).origin === new URL(serverUrl).origin;
|
package/dist/mcp/pool.d.ts
CHANGED
|
@@ -58,8 +58,8 @@ export declare class McpPool {
|
|
|
58
58
|
manifest: AgentManifest;
|
|
59
59
|
/** The effect id: a gate runs the call once under it (F4.1). */
|
|
60
60
|
effectId?: string;
|
|
61
|
-
/** The
|
|
62
|
-
|
|
61
|
+
/** The call's span context (R7.4), sent as `traceparent` on a connection this process holds. */
|
|
62
|
+
traceparent?: string;
|
|
63
63
|
signal?: AbortSignal;
|
|
64
64
|
}): Promise<McpToolOutcome>;
|
|
65
65
|
/** The session's connection to the server, opened when there is none, or why it cannot be. */
|
package/dist/mcp/pool.js
CHANGED
|
@@ -126,8 +126,8 @@ export class McpPool {
|
|
|
126
126
|
try {
|
|
127
127
|
return await callMcpTool(live.connection.client, input.serverToolName, input.args, {
|
|
128
128
|
...(input.effectId === undefined ? {} : { key: input.effectId }),
|
|
129
|
-
...(input.retrySafe ? { retrySafe: true } : {}),
|
|
130
129
|
...(input.signal ? { signal: input.signal } : {}),
|
|
130
|
+
...(input.traceparent === undefined ? {} : { traceparent: input.traceparent }),
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
catch (error) {
|
package/dist/mcp/preview.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A remote MCP server's tools before an agent names it (R2b C12, `POST /v1/tenant/mcp/preview`,
|
|
3
|
-
* `nylorun mcp inspect`,
|
|
3
|
+
* `nylorun mcp inspect`, an Environment's Secrets tab in Studio). It runs where plaintext credentials live:
|
|
4
4
|
* the keys service (`keys/keys.ts`, `previewMcp`), in the gateway or in process.
|
|
5
5
|
*
|
|
6
|
-
* One connection: `initialize` and `tools/list` with the
|
|
6
|
+
* One connection: `initialize` and `tools/list` with the Environment's credential for the
|
|
7
7
|
* URL (its headers, and its `via`), under the Host's address policy, within 15 s; then it closes.
|
|
8
8
|
* It never calls a tool, and sends no identity header: no person asked. A server that answers
|
|
9
9
|
* `401` is `authRequired`, with its RFC 9728 protected-resource metadata when it publishes any,
|
|
@@ -16,7 +16,7 @@ import { openMcpServer } from "./connect.js";
|
|
|
16
16
|
/** How long a preview may take, `initialize` and every `tools/list` page together. */
|
|
17
17
|
export declare const MCP_PREVIEW_TIMEOUT_MS = 15000;
|
|
18
18
|
export interface McpPreviewOptions {
|
|
19
|
-
readonly vault: Pick<VaultService, "authorize" | "
|
|
19
|
+
readonly vault: Pick<VaultService, "authorize" | "listEnvironmentSecrets">;
|
|
20
20
|
/** The Host's address policy for developer URLs (`TenantConfig.delivery`). */
|
|
21
21
|
readonly policy: OutboundPolicy;
|
|
22
22
|
/** Default `MCP_PREVIEW_TIMEOUT_MS`. */
|
package/dist/mcp/preview.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A remote MCP server's tools before an agent names it (R2b C12, `POST /v1/tenant/mcp/preview`,
|
|
3
|
-
* `nylorun mcp inspect`,
|
|
3
|
+
* `nylorun mcp inspect`, an Environment's Secrets tab in Studio). It runs where plaintext credentials live:
|
|
4
4
|
* the keys service (`keys/keys.ts`, `previewMcp`), in the gateway or in process.
|
|
5
5
|
*
|
|
6
|
-
* One connection: `initialize` and `tools/list` with the
|
|
6
|
+
* One connection: `initialize` and `tools/list` with the Environment's credential for the
|
|
7
7
|
* URL (its headers, and its `via`), under the Host's address policy, within 15 s; then it closes.
|
|
8
8
|
* It never calls a tool, and sends no identity header: no person asked. A server that answers
|
|
9
9
|
* `401` is `authRequired`, with its RFC 9728 protected-resource metadata when it publishes any,
|
|
10
10
|
* read under the same policy. No credential value sent is in the answer (`scrubValues`, C8).
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { McpPreviewRequestSchema, } from "@nylorun/core/contracts";
|
|
13
13
|
import { scrubValues } from "../redact.js";
|
|
14
14
|
import { HttpError } from "../tenant/http.js";
|
|
15
15
|
import { guardedFetch, OutboundRefused } from "../tenant/outbound.js";
|
|
@@ -119,28 +119,18 @@ export async function previewMcpServer(body, options) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
-
* The
|
|
123
|
-
*
|
|
124
|
-
* them.
|
|
122
|
+
* The credential of Environment `environmentId`'s vault for the URL; none without an
|
|
123
|
+
* Environment (a preview without a credential). An Environment that does not exist is a 404.
|
|
125
124
|
*/
|
|
126
125
|
async function credentialFor(vault, request) {
|
|
127
|
-
|
|
128
|
-
if (request.vaultId !== undefined) {
|
|
129
|
-
const found = await vault.getVault(request.vaultId);
|
|
130
|
-
if (found.ownerUserId !== INSTALLATION_OWNER)
|
|
131
|
-
throw new VaultError(400, "A preview uses an installation vault's credential: no person asked for it");
|
|
132
|
-
vaultIds = [found.id];
|
|
133
|
-
}
|
|
134
|
-
else
|
|
135
|
-
vaultIds = (await vault.listVaults(undefined, { installation: true })).map((found) => found.id);
|
|
136
|
-
if (vaultIds.length === 0)
|
|
126
|
+
if (request.environmentId === undefined)
|
|
137
127
|
return { status: "unauthenticated", url: request.url, headers: {} };
|
|
138
|
-
|
|
128
|
+
// The 404 of a missing Environment, before any request leaves.
|
|
129
|
+
await vault.listEnvironmentSecrets(request.environmentId);
|
|
130
|
+
const result = await vault.authorize({ environmentId: request.environmentId, url: request.url });
|
|
139
131
|
if (result.status !== "refused")
|
|
140
132
|
return result;
|
|
141
|
-
throw new HttpError(409, result.reason
|
|
142
|
-
? "Several installation vaults hold a credential for this URL: name one with vaultId"
|
|
143
|
-
: `The installation vault's credential for this URL cannot be used (${result.reason})`, { code: "request_rejected", details: { reason: result.reason, credentialIds: result.credentialIds } });
|
|
133
|
+
throw new HttpError(409, `Environment ${request.environmentId}'s credential for this URL cannot be used (${result.reason})`, { code: "request_rejected", details: { reason: result.reason, credentialIds: result.credentialIds } });
|
|
144
134
|
}
|
|
145
135
|
/** A preview that listed nothing: `mcp_preview_failed`, with the code a tool call would have had. */
|
|
146
136
|
function failed(server, error, signal, secrets, timeoutMs) {
|
package/dist/mcp/snapshot.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EventPayload } from "@nylorun/core/contracts";
|
|
2
2
|
import type { JsonObject } from "@nylorun/core/define";
|
|
3
|
-
import { type AgentManifest } from "@nylorun/core/define";
|
|
3
|
+
import { type AgentManifest, type ResolvedMcpToolSettings } from "@nylorun/core/define";
|
|
4
4
|
import type { DurableSessionTool, HostEffect } from "@nylorun/harness/run";
|
|
5
5
|
export interface McpToolRecord {
|
|
6
6
|
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
@@ -14,14 +14,6 @@ export interface McpToolRecord {
|
|
|
14
14
|
readonly description?: string;
|
|
15
15
|
readonly inputSchema: JsonObject;
|
|
16
16
|
readonly outputSchema?: JsonObject;
|
|
17
|
-
/**
|
|
18
|
-
* The server's hints that calling the tool again is safe (R2b C7, Q15): a call whose answer
|
|
19
|
-
* was lost after it was sent is then `mcp.lost` to the model, not `uncertain`.
|
|
20
|
-
*/
|
|
21
|
-
readonly annotations?: {
|
|
22
|
-
readonly readOnlyHint?: boolean;
|
|
23
|
-
readonly idempotentHint?: boolean;
|
|
24
|
-
};
|
|
25
17
|
/**
|
|
26
18
|
* Out of the model's tool list for the session's life (R2b C10): core decides it once, when it
|
|
27
19
|
* records the snapshot (`pinDeferral`), so every model call of the session lists the same tools.
|
|
@@ -78,12 +70,14 @@ export declare function normalizeToolName(serverName: string, serverToolName: st
|
|
|
78
70
|
readonly suffixed?: boolean;
|
|
79
71
|
}): string;
|
|
80
72
|
export declare function declaredToolNames(manifest: AgentManifest): Set<string>;
|
|
73
|
+
/** The tool's settings in `manifest` (R2b C9); the defaults when its server is not there. */
|
|
74
|
+
export declare function mcpToolSettingsIn(manifest: AgentManifest, tool: McpToolRecord): ResolvedMcpToolSettings;
|
|
81
75
|
/**
|
|
82
76
|
* The tools of a snapshot, as the engine gets them for a turn whose manifest is `manifest` (the
|
|
83
77
|
* session's pin, or a turn variant that tightened its MCP servers' tools):
|
|
84
78
|
*
|
|
85
|
-
* - a tool its settings disable is left out (R2b C9);
|
|
86
|
-
*
|
|
79
|
+
* - a tool its settings disable is left out (R2b C9); the Tool Gate holds a call its approval
|
|
80
|
+
* or the Tenant's tool policy asks a person about (R5 step 2);
|
|
87
81
|
* - a deferred tool (R2b C10) is marked so, out of the model's list, and its agent gets
|
|
88
82
|
* `tool_search`, with the note on the servers it holds deferred tools of, and `tool_call` in
|
|
89
83
|
* its `nylorun.tools` capability. An agent whose turn has no such capability keeps its tools in
|
package/dist/mcp/snapshot.js
CHANGED
|
@@ -58,7 +58,7 @@ function serverOf(manifest, tool) {
|
|
|
58
58
|
return agent?.capabilities.find((item) => item.id === tool.capabilityId)?.mcpServers?.[tool.serverName];
|
|
59
59
|
}
|
|
60
60
|
/** The tool's settings in `manifest` (R2b C9); the defaults when its server is not there. */
|
|
61
|
-
function
|
|
61
|
+
export function mcpToolSettingsIn(manifest, tool) {
|
|
62
62
|
const server = serverOf(manifest, tool);
|
|
63
63
|
return server ? mcpToolSettings(server, tool.serverToolName) : { enabled: true, approval: "never" };
|
|
64
64
|
}
|
|
@@ -71,8 +71,8 @@ function reachesDeferred(manifest, agentId) {
|
|
|
71
71
|
* The tools of a snapshot, as the engine gets them for a turn whose manifest is `manifest` (the
|
|
72
72
|
* session's pin, or a turn variant that tightened its MCP servers' tools):
|
|
73
73
|
*
|
|
74
|
-
* - a tool its settings disable is left out (R2b C9);
|
|
75
|
-
*
|
|
74
|
+
* - a tool its settings disable is left out (R2b C9); the Tool Gate holds a call its approval
|
|
75
|
+
* or the Tenant's tool policy asks a person about (R5 step 2);
|
|
76
76
|
* - a deferred tool (R2b C10) is marked so, out of the model's list, and its agent gets
|
|
77
77
|
* `tool_search`, with the note on the servers it holds deferred tools of, and `tool_call` in
|
|
78
78
|
* its `nylorun.tools` capability. An agent whose turn has no such capability keeps its tools in
|
|
@@ -87,7 +87,7 @@ export function sessionToolsOf(snapshot, manifest) {
|
|
|
87
87
|
const deferred = new Map();
|
|
88
88
|
const notes = new Map();
|
|
89
89
|
for (const tool of snapshot.mcpTools) {
|
|
90
|
-
const settings =
|
|
90
|
+
const settings = mcpToolSettingsIn(manifest, tool);
|
|
91
91
|
if (!settings.enabled)
|
|
92
92
|
continue;
|
|
93
93
|
const defer = tool.deferred === true && reachesDeferred(manifest, tool.agentId);
|
|
@@ -98,7 +98,6 @@ export function sessionToolsOf(snapshot, manifest) {
|
|
|
98
98
|
...(tool.description === undefined ? {} : { description: tool.description }),
|
|
99
99
|
inputSchema: tool.inputSchema,
|
|
100
100
|
...(tool.outputSchema === undefined ? {} : { outputSchema: tool.outputSchema }),
|
|
101
|
-
...(settings.approval === "always" ? { approval: "always" } : {}),
|
|
102
101
|
...(defer ? { deferred: true } : {}),
|
|
103
102
|
};
|
|
104
103
|
if (!defer) {
|
|
@@ -145,7 +144,7 @@ export const CHARS_PER_TOKEN = 4;
|
|
|
145
144
|
export function pinDeferral(snapshot, manifest, contextWindow) {
|
|
146
145
|
const limit = contextWindow * CHARS_PER_TOKEN * DEFER_CONTEXT_SHARE;
|
|
147
146
|
const listed = new Map();
|
|
148
|
-
const set = snapshot.mcpTools.map((tool) =>
|
|
147
|
+
const set = snapshot.mcpTools.map((tool) => mcpToolSettingsIn(manifest, tool).deferred);
|
|
149
148
|
snapshot.mcpTools.forEach((tool, i) => {
|
|
150
149
|
if (set[i] === true)
|
|
151
150
|
return;
|
|
@@ -175,7 +174,7 @@ export function mcpToolOf(snapshot, request, manifest) {
|
|
|
175
174
|
}
|
|
176
175
|
/** True when the tool's settings in `manifest` leave it enabled (R2b C9). */
|
|
177
176
|
export function isEnabledIn(manifest, tool) {
|
|
178
|
-
return
|
|
177
|
+
return mcpToolSettingsIn(manifest, tool).enabled;
|
|
179
178
|
}
|
|
180
179
|
/** The manifest of the agent an effect belongs to: the root, or an agent it uses as a tool. */
|
|
181
180
|
export function manifestFor(manifest, agent) {
|
package/dist/model/pi-model.js
CHANGED
|
@@ -38,6 +38,9 @@ export function piModel(options = {}) {
|
|
|
38
38
|
};
|
|
39
39
|
return async (call, context) => {
|
|
40
40
|
context.signal.throwIfAborted();
|
|
41
|
+
// The first stream event's time, from here (R7.1): the meter keeps it on the usage row.
|
|
42
|
+
const calledAt = Date.now();
|
|
43
|
+
let firstEventAt;
|
|
41
44
|
const root = options.root ?? "";
|
|
42
45
|
const stored = await options.readHostModel?.();
|
|
43
46
|
if (!stored)
|
|
@@ -100,6 +103,8 @@ export function piModel(options = {}) {
|
|
|
100
103
|
timer = setTimeout(() => idle.abort(), settings.idleTimeoutMs);
|
|
101
104
|
};
|
|
102
105
|
arm();
|
|
106
|
+
// Only the answer's own attempt counts: a retried attempt's first event is not its.
|
|
107
|
+
firstEventAt = undefined;
|
|
103
108
|
try {
|
|
104
109
|
const stream = registry.streamSimple(selected, request, {
|
|
105
110
|
...invocationOptions,
|
|
@@ -107,6 +112,7 @@ export function piModel(options = {}) {
|
|
|
107
112
|
});
|
|
108
113
|
for await (const event of stream) {
|
|
109
114
|
arm();
|
|
115
|
+
firstEventAt ??= Date.now();
|
|
110
116
|
if (event.type === "text_delta" && options.onPreview) {
|
|
111
117
|
try {
|
|
112
118
|
void Promise.resolve(options.onPreview({
|
|
@@ -218,6 +224,8 @@ export function piModel(options = {}) {
|
|
|
218
224
|
// The engine keeps the next prompt inside this window (Model Calls §7).
|
|
219
225
|
contextWindow: selected.contextWindow,
|
|
220
226
|
maxOutputTokens: selected.maxTokens,
|
|
227
|
+
// From the call to the provider's first stream event (R7.1); absent when none came.
|
|
228
|
+
...(firstEventAt !== undefined ? { firstTokenMs: firstEventAt - calledAt } : {}),
|
|
221
229
|
},
|
|
222
230
|
},
|
|
223
231
|
};
|