@nylorun/agents 0.9.0-beta → 0.11.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 +165 -0
- package/README.md +296 -77
- package/dist/a2a/handler.d.ts +59 -0
- package/dist/a2a/handler.js +178 -0
- package/dist/a2a/index.d.ts +8 -0
- package/dist/a2a/index.js +7 -0
- package/dist/access.d.ts +101 -0
- package/dist/access.js +111 -0
- package/dist/action-handler.d.ts +63 -0
- package/dist/action-handler.js +279 -0
- package/dist/ag-ui/handler.d.ts +12 -4
- package/dist/ag-ui/handler.js +87 -281
- package/dist/ag-ui/index.d.ts +2 -1
- package/dist/ag-ui/index.js +2 -1
- package/dist/browser.d.ts +97 -0
- package/dist/browser.js +277 -0
- package/dist/client.d.ts +3 -230
- package/dist/client.js +3 -361
- package/dist/connection.d.ts +4 -4
- package/dist/connection.js +16 -25
- package/dist/delivery-token.d.ts +62 -0
- package/dist/delivery-token.js +159 -0
- package/dist/execute-action.d.ts +6 -0
- package/dist/execute-action.js +24 -0
- package/dist/http.d.ts +34 -3
- package/dist/http.js +68 -16
- package/dist/index.d.ts +10 -8
- package/dist/index.js +4 -4
- package/dist/sandbox/client.d.ts +4 -5
- package/dist/sandbox/client.js +4 -8
- package/dist/sandbox/index.d.ts +0 -2
- package/dist/sandbox/index.js +0 -1
- package/dist/served-definitions.d.ts +20 -0
- package/dist/served-definitions.js +100 -0
- package/dist/session-client.d.ts +282 -0
- package/dist/session-client.js +375 -0
- package/dist/sse.js +12 -0
- package/dist/studio-embed.d.ts +9 -0
- package/dist/studio-embed.js +8 -0
- package/dist/token-endpoint.d.ts +23 -0
- package/dist/token-endpoint.js +30 -0
- package/package.json +15 -8
- package/dist/ag-ui/history.d.ts +0 -7
- package/dist/ag-ui/history.js +0 -60
- package/dist/ag-ui/sse.d.ts +0 -9
- package/dist/ag-ui/sse.js +0 -7
- package/dist/ag-ui/translate.d.ts +0 -38
- package/dist/ag-ui/translate.js +0 -222
- package/dist/derived-credentials.d.ts +0 -5
- package/dist/derived-credentials.js +0 -17
- package/dist/executor.d.ts +0 -20
- package/dist/executor.js +0 -417
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies the delivery token on a request the Runtime sends to an Action endpoint: an ES256
|
|
3
|
+
* JWT signed with the Tenant's current signing key (design: Action endpoints §8.1). WebCrypto
|
|
4
|
+
* only, so the SDK takes no JWT dependency: a JWS ES256 signature is the raw `r || s` pair
|
|
5
|
+
* WebCrypto's ECDSA verifies.
|
|
6
|
+
*/
|
|
7
|
+
import { PROTOCOL_HEADER, PROTOCOL_VERSION, isTenantId } from "@nylorun/core/compatibility";
|
|
8
|
+
import { DELIVERY_TOKEN_TYPE, subjectTokenIssuer } from "@nylorun/core/contracts";
|
|
9
|
+
/** The issuer prefix of a Tenant's tokens: `urn:nylorun:tenant:<tenantId>`. */
|
|
10
|
+
const ISSUER_PREFIX = subjectTokenIssuer("");
|
|
11
|
+
/** Why a delivery was refused; `code` is sent back to the Runtime. */
|
|
12
|
+
export class DeliveryVerificationError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
name = "DeliveryVerificationError";
|
|
15
|
+
constructor(code, message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.code = code;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Clock skew tolerated on `iat` and `exp`, in seconds. */
|
|
21
|
+
const SKEW_SECONDS = 30;
|
|
22
|
+
const FORBIDDEN_HEADERS = ["jku", "jwk", "x5u", "x5c", "x5t", "x5t#S256", "crit"];
|
|
23
|
+
/** The largest token accepted: well above a real one, well below a request limit. */
|
|
24
|
+
const MAX_TOKEN_LENGTH = 4096;
|
|
25
|
+
/**
|
|
26
|
+
* Verifies `token` against `body`. The keys are the installation's, so a valid signature already
|
|
27
|
+
* binds the issuer to its one Tenant: any `urn:nylorun:tenant:<id>` issuer is accepted (and
|
|
28
|
+
* returned in `iss`) unless `tenantId` names the one expected.
|
|
29
|
+
*/
|
|
30
|
+
export async function verifyDeliveryToken(token, options) {
|
|
31
|
+
if (!token)
|
|
32
|
+
throw new DeliveryVerificationError("signature_missing", "No delivery token");
|
|
33
|
+
const parts = token.length <= MAX_TOKEN_LENGTH ? token.split(".") : [];
|
|
34
|
+
if (parts.length !== 3)
|
|
35
|
+
throw invalid("Not a compact JWS");
|
|
36
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
37
|
+
const header = json(encodedHeader);
|
|
38
|
+
if (header.alg !== "ES256" ||
|
|
39
|
+
header.typ !== DELIVERY_TOKEN_TYPE ||
|
|
40
|
+
typeof header.kid !== "string" ||
|
|
41
|
+
FORBIDDEN_HEADERS.some((name) => name in header))
|
|
42
|
+
throw invalid("Unexpected token header");
|
|
43
|
+
const key = await options.keys(header.kid);
|
|
44
|
+
if (!key)
|
|
45
|
+
throw new DeliveryVerificationError("key_unknown", `Unknown signing key ${header.kid}`);
|
|
46
|
+
const signed = await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key, bytes(base64url(encodedSignature)), new TextEncoder().encode(`${encodedHeader}.${encodedPayload}`));
|
|
47
|
+
if (!signed)
|
|
48
|
+
throw invalid("Bad signature");
|
|
49
|
+
const claims = json(encodedPayload);
|
|
50
|
+
if (typeof claims.iss !== "string" ||
|
|
51
|
+
typeof claims.aud !== "string" ||
|
|
52
|
+
typeof claims.sub !== "string" ||
|
|
53
|
+
typeof claims.agt !== "string" ||
|
|
54
|
+
!Number.isInteger(claims.gen) ||
|
|
55
|
+
typeof claims.bdy !== "string" ||
|
|
56
|
+
typeof claims.iat !== "number" ||
|
|
57
|
+
typeof claims.exp !== "number" ||
|
|
58
|
+
typeof claims.jti !== "string")
|
|
59
|
+
throw invalid("Missing claims");
|
|
60
|
+
if (!claims.iss.startsWith(ISSUER_PREFIX) ||
|
|
61
|
+
!isTenantId(claims.iss.slice(ISSUER_PREFIX.length)))
|
|
62
|
+
throw invalid("The token was not issued by a Tenant");
|
|
63
|
+
if (options.tenantId !== undefined && claims.iss !== subjectTokenIssuer(options.tenantId))
|
|
64
|
+
throw invalid("The token is for another Tenant");
|
|
65
|
+
if (options.audience !== undefined && claims.aud !== options.audience)
|
|
66
|
+
throw invalid(`The token is for ${claims.aud}, not ${options.audience}`);
|
|
67
|
+
const now = (options.now ?? Date.now()) / 1000;
|
|
68
|
+
if (claims.exp < now - SKEW_SECONDS || claims.iat > now + SKEW_SECONDS)
|
|
69
|
+
throw new DeliveryVerificationError("token_expired", "The delivery token has expired");
|
|
70
|
+
if (claims.bdy !== (await bodyHash(options.body)))
|
|
71
|
+
throw invalid("The body does not match the token");
|
|
72
|
+
return claims;
|
|
73
|
+
}
|
|
74
|
+
/** base64url SHA-256 of `body`: the token's `bdy` claim. */
|
|
75
|
+
export async function bodyHash(body) {
|
|
76
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes(body)));
|
|
77
|
+
return Buffer.from(digest).toString("base64url");
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The Tenant's public keys, fetched from `GET /v1/access/jwks` and cached by `kid`. An
|
|
81
|
+
* unknown `kid` refetches once, at most every `refetchMs`, so forged tokens cannot make the
|
|
82
|
+
* endpoint call the Runtime on every request. Concurrent lookups share one in-flight fetch.
|
|
83
|
+
*/
|
|
84
|
+
export class JwksCache {
|
|
85
|
+
source;
|
|
86
|
+
refetchMs;
|
|
87
|
+
keys = new Map();
|
|
88
|
+
lastFetch = 0;
|
|
89
|
+
pending;
|
|
90
|
+
constructor(source, refetchMs = 5000) {
|
|
91
|
+
this.source = source;
|
|
92
|
+
this.refetchMs = refetchMs;
|
|
93
|
+
}
|
|
94
|
+
lookup = async (kid) => {
|
|
95
|
+
const known = this.keys.get(kid);
|
|
96
|
+
if (known)
|
|
97
|
+
return known;
|
|
98
|
+
// Wait for an in-flight fetch: stamping the cooldown at the start of `load` used
|
|
99
|
+
// to make a concurrent ping (register + smoke, or tsx watch restart) answer
|
|
100
|
+
// `key_unknown` before the keys arrived.
|
|
101
|
+
if (this.pending) {
|
|
102
|
+
await this.pending;
|
|
103
|
+
return this.keys.get(kid);
|
|
104
|
+
}
|
|
105
|
+
if (Date.now() - this.lastFetch < this.refetchMs && this.lastFetch > 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
await (this.pending ??= this.load().finally(() => (this.pending = undefined)));
|
|
108
|
+
return this.keys.get(kid);
|
|
109
|
+
};
|
|
110
|
+
async load() {
|
|
111
|
+
const keys = "keys" in this.source ? this.source.keys : await this.fetchKeys(this.source);
|
|
112
|
+
this.lastFetch = Date.now();
|
|
113
|
+
for (const jwk of keys) {
|
|
114
|
+
const kid = jwk.kid;
|
|
115
|
+
if (typeof kid !== "string" || jwk.kty !== "EC" || jwk.crv !== "P-256")
|
|
116
|
+
continue;
|
|
117
|
+
if (this.keys.has(kid))
|
|
118
|
+
continue;
|
|
119
|
+
this.keys.set(kid, await crypto.subtle.importKey("jwk", { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y }, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async fetchKeys(source) {
|
|
123
|
+
const headers = {
|
|
124
|
+
[PROTOCOL_HEADER]: String(PROTOCOL_VERSION),
|
|
125
|
+
};
|
|
126
|
+
if (source.key)
|
|
127
|
+
headers.Authorization = `Bearer ${source.key}`;
|
|
128
|
+
const response = await (source.fetch ?? globalThis.fetch)(`${source.url.replace(/\/$/, "")}/v1/access/jwks`, { headers, redirect: "error", signal: AbortSignal.timeout(10_000) });
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
await response.body?.cancel();
|
|
131
|
+
throw new Error(`The Runtime answered ${response.status} for its public keys (GET /v1/access/jwks)`);
|
|
132
|
+
}
|
|
133
|
+
const body = (await response.json());
|
|
134
|
+
return Array.isArray(body.keys) ? body.keys : [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function invalid(message) {
|
|
138
|
+
return new DeliveryVerificationError("signature_invalid", message);
|
|
139
|
+
}
|
|
140
|
+
function base64url(value) {
|
|
141
|
+
return new Uint8Array(Buffer.from(value, "base64url"));
|
|
142
|
+
}
|
|
143
|
+
/** A copy backed by a plain `ArrayBuffer`, which WebCrypto's types require. */
|
|
144
|
+
function bytes(value) {
|
|
145
|
+
const copy = new Uint8Array(value.byteLength);
|
|
146
|
+
copy.set(value);
|
|
147
|
+
return copy;
|
|
148
|
+
}
|
|
149
|
+
function json(segment) {
|
|
150
|
+
try {
|
|
151
|
+
const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
|
|
152
|
+
if (value && typeof value === "object" && !Array.isArray(value))
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
/* fall through */
|
|
157
|
+
}
|
|
158
|
+
throw invalid("Malformed token");
|
|
159
|
+
}
|
package/dist/execute-action.d.ts
CHANGED
|
@@ -8,3 +8,9 @@ export type ExecuteActionOptions = {
|
|
|
8
8
|
};
|
|
9
9
|
/** Executes code only after a host-issued claim. No engine dependency. */
|
|
10
10
|
export declare function executeAction(action: Action, root: ExecutableDefinition, signal: AbortSignal, options?: ExecuteActionOptions): Promise<ActionOutcome>;
|
|
11
|
+
/**
|
|
12
|
+
* True when `action` runs a tool marked `background` (`ToolDefinition.background`): an Action
|
|
13
|
+
* endpoint answers its delivery at once and posts the outcome later. False for anything it
|
|
14
|
+
* cannot find; `executeAction` reports those.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runsInBackground(action: Action, root: ExecutableDefinition): boolean;
|
package/dist/execute-action.js
CHANGED
|
@@ -42,6 +42,30 @@ export async function executeAction(action, root, signal, options = {}) {
|
|
|
42
42
|
const tool = normalizeToolDefinition(raw);
|
|
43
43
|
return runTool(action, tool, ref, signal, sandbox);
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* True when `action` runs a tool marked `background` (`ToolDefinition.background`): an Action
|
|
47
|
+
* endpoint answers its delivery at once and posts the outcome later. False for anything it
|
|
48
|
+
* cannot find; `executeAction` reports those.
|
|
49
|
+
*/
|
|
50
|
+
export function runsInBackground(action, root) {
|
|
51
|
+
if (action.kind !== "tool")
|
|
52
|
+
return false;
|
|
53
|
+
try {
|
|
54
|
+
if (isBuiltWorkflow(root)) {
|
|
55
|
+
const key = action.key;
|
|
56
|
+
const impl = typeof key === "string" ? root.getBinding().nodes[key] : undefined;
|
|
57
|
+
return impl?.kind === "tool" && impl.tool.background === true;
|
|
58
|
+
}
|
|
59
|
+
if (!("capabilityId" in action))
|
|
60
|
+
return false;
|
|
61
|
+
const agent = action.agent ? delegatedAgent(root, action.agent.id) : root;
|
|
62
|
+
const raw = implementationsFor(agent)[action.capabilityId]?.tools?.[action.toolName];
|
|
63
|
+
return raw !== undefined && normalizeToolDefinition(raw).background === true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
45
69
|
async function executeWorkflowTool(action, binding, signal, sandbox) {
|
|
46
70
|
const impl = binding.nodes[action.key];
|
|
47
71
|
if (!impl || impl.kind !== "tool")
|
package/dist/http.d.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { type Compatibility, type ProtocolRange } from "@nylorun/core/compatibility";
|
|
2
|
+
/**
|
|
3
|
+
* Subject tokens for a client that holds no Tenant key (a browser or an app; Host feature
|
|
4
|
+
* `subject-tokens`). `get` returns a current token and may fetch a new one; `invalidate`
|
|
5
|
+
* drops the cached one after the Runtime answered `401 token_expired`.
|
|
6
|
+
*/
|
|
7
|
+
export interface TokenSource {
|
|
8
|
+
get(signal?: AbortSignal): Promise<string>;
|
|
9
|
+
invalidate(): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Where a client sends its requests. Nothing names a Tenant: the Runtime at `url` serves one
|
|
13
|
+
* (protocol 5).
|
|
14
|
+
*/
|
|
2
15
|
export interface Destination {
|
|
3
16
|
url?: string;
|
|
4
17
|
key?: string;
|
|
5
|
-
tenant?: string;
|
|
6
18
|
fetch?: typeof fetch;
|
|
19
|
+
/** Instead of `key`: subject tokens. The client then skips the `/health` check. */
|
|
20
|
+
token?: TokenSource;
|
|
21
|
+
/** A publishable key (Host feature `browser-access`): names the app and its origins. */
|
|
22
|
+
publishableKey?: string;
|
|
7
23
|
}
|
|
8
24
|
export declare function env(name: string): string | undefined;
|
|
9
25
|
export declare class RuntimeError extends Error {
|
|
@@ -23,27 +39,42 @@ export declare class IncompatibleRuntimeError extends Error {
|
|
|
23
39
|
}
|
|
24
40
|
export declare class Transport {
|
|
25
41
|
readonly url: string;
|
|
42
|
+
/** The Tenant key; empty for a client that uses subject tokens. */
|
|
26
43
|
readonly key: string;
|
|
27
|
-
readonly
|
|
44
|
+
readonly token: TokenSource | undefined;
|
|
45
|
+
readonly publishableKey: string | undefined;
|
|
28
46
|
readonly fetcher: typeof fetch;
|
|
29
47
|
/** Sent on every request, e.g. `Nylorun-Subject` and `Nylorun-Scopes` (`withHeaders`). */
|
|
30
48
|
readonly headers: Readonly<Record<string, string>>;
|
|
31
49
|
private readonly check;
|
|
32
|
-
constructor(options?: Destination
|
|
50
|
+
constructor(options?: Destination);
|
|
33
51
|
/**
|
|
34
52
|
* The same destination with `headers` added to every request. The copy shares this
|
|
35
53
|
* transport's compatibility check, so a copy made per request does not repeat `/health`.
|
|
36
54
|
*/
|
|
37
55
|
withHeaders(headers: Readonly<Record<string, string>>): Transport;
|
|
56
|
+
/**
|
|
57
|
+
* The same destination with another bearer (a delivery token for an Action endpoint's
|
|
58
|
+
* callbacks). Like `withHeaders`, the copy shares this transport's compatibility check.
|
|
59
|
+
*/
|
|
60
|
+
withKey(key: string): Transport;
|
|
38
61
|
/** Clears the cached Host compatibility result (used after a 426). */
|
|
39
62
|
clearCompatibilityCache(): void;
|
|
40
63
|
private ensureCompatible;
|
|
64
|
+
/** Throws `IncompatibleRuntimeError` before anything is sent when the Host lacks `feature`. */
|
|
65
|
+
requireFeature(feature: string, signal?: AbortSignal): Promise<void>;
|
|
41
66
|
/** The Host's protocol features, including optional ones, from its `/health`. */
|
|
42
67
|
hostFeatures(signal?: AbortSignal): Promise<readonly string[]>;
|
|
43
68
|
private authHeaders;
|
|
44
69
|
request(path: string, init?: RequestInit, options?: {
|
|
45
70
|
retried426?: boolean;
|
|
71
|
+
retried401?: boolean;
|
|
46
72
|
}): Promise<Response>;
|
|
73
|
+
/**
|
|
74
|
+
* Sends a request with this client's credentials and returns the Runtime's response as it
|
|
75
|
+
* is, errors included (an app server passing a response through, e.g. the AG-UI handler).
|
|
76
|
+
*/
|
|
77
|
+
forward(path: string, init?: RequestInit): Promise<Response>;
|
|
47
78
|
json<T>(path: string, method?: string, body?: unknown, signal?: AbortSignal): Promise<T>;
|
|
48
79
|
}
|
|
49
80
|
export declare const id: () => `${string}-${string}-${string}-${string}-${string}`;
|
package/dist/http.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PROTOCOL_FEATURES, PROTOCOL_HEADER, PROTOCOL_VERSION,
|
|
1
|
+
import { PROTOCOL_FEATURES, PROTOCOL_HEADER, PROTOCOL_VERSION, PUBLISHABLE_KEY_HEADER, checkCompatibility, } from "@nylorun/core/compatibility";
|
|
2
2
|
export function env(name) {
|
|
3
3
|
return typeof process !== "undefined" ? process.env[name] : undefined;
|
|
4
4
|
}
|
|
@@ -54,28 +54,35 @@ function parseProtocolRange(value) {
|
|
|
54
54
|
}
|
|
55
55
|
export class Transport {
|
|
56
56
|
url;
|
|
57
|
+
/** The Tenant key; empty for a client that uses subject tokens. */
|
|
57
58
|
key;
|
|
58
|
-
|
|
59
|
+
token;
|
|
60
|
+
publishableKey;
|
|
59
61
|
fetcher;
|
|
60
62
|
/** Sent on every request, e.g. `Nylorun-Subject` and `Nylorun-Scopes` (`withHeaders`). */
|
|
61
63
|
headers = {};
|
|
62
64
|
check = { compatible: false, features: [] };
|
|
63
|
-
constructor(options = {}
|
|
65
|
+
constructor(options = {}) {
|
|
64
66
|
const url = options.url ?? env("NYLORUN_RUNTIME_URL");
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
throw new Error("Set tenant explicitly or via NYLORUN_TENANT");
|
|
67
|
+
const token = options.token;
|
|
68
|
+
const key = token ? "" : options.key ?? env("NYLORUN_SERVER_KEY");
|
|
69
|
+
if (!url || (!token && !key))
|
|
70
|
+
throw new Error("Set Runtime url and server key explicitly or via NYLORUN_RUNTIME_URL / NYLORUN_SERVER_KEY");
|
|
71
|
+
if (token && options.key)
|
|
72
|
+
throw new Error("Use a Tenant key or subject tokens, not both");
|
|
72
73
|
const parsed = new URL(url);
|
|
73
74
|
if (!["http:", "https:"].includes(parsed.protocol))
|
|
74
75
|
throw new Error("Runtime requires an HTTP(S) URL");
|
|
75
76
|
this.url = url.replace(/\/$/, "");
|
|
76
77
|
this.key = key;
|
|
77
|
-
this.
|
|
78
|
-
this.
|
|
78
|
+
this.token = token;
|
|
79
|
+
this.publishableKey = options.publishableKey;
|
|
80
|
+
// Browsers may not call `/health` (it refuses `Origin`); token clients rely on `426`.
|
|
81
|
+
if (token)
|
|
82
|
+
this.check.compatible = true;
|
|
83
|
+
// Called as a method on a page's window would otherwise lose `this`.
|
|
84
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
85
|
+
this.fetcher = options.fetch ? fetcher : (input, init) => fetcher(input, init);
|
|
79
86
|
}
|
|
80
87
|
/**
|
|
81
88
|
* The same destination with `headers` added to every request. The copy shares this
|
|
@@ -87,6 +94,14 @@ export class Transport {
|
|
|
87
94
|
headers: Object.freeze({ ...this.headers, ...headers }),
|
|
88
95
|
});
|
|
89
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* The same destination with another bearer (a delivery token for an Action endpoint's
|
|
99
|
+
* callbacks). Like `withHeaders`, the copy shares this transport's compatibility check.
|
|
100
|
+
*/
|
|
101
|
+
withKey(key) {
|
|
102
|
+
const copy = Object.create(Transport.prototype);
|
|
103
|
+
return Object.assign(copy, this, { key });
|
|
104
|
+
}
|
|
90
105
|
/** Clears the cached Host compatibility result (used after a 426). */
|
|
91
106
|
clearCompatibilityCache() {
|
|
92
107
|
this.check.compatible = false;
|
|
@@ -123,19 +138,35 @@ export class Transport {
|
|
|
123
138
|
if (!result.ok)
|
|
124
139
|
throw new IncompatibleRuntimeError(result);
|
|
125
140
|
this.check.features = [...protocol.features];
|
|
141
|
+
this.check.range = protocol;
|
|
126
142
|
this.check.compatible = true;
|
|
127
143
|
}
|
|
144
|
+
/** Throws `IncompatibleRuntimeError` before anything is sent when the Host lacks `feature`. */
|
|
145
|
+
async requireFeature(feature, signal) {
|
|
146
|
+
const features = await this.hostFeatures(signal);
|
|
147
|
+
if (!features.includes(feature))
|
|
148
|
+
throw new IncompatibleRuntimeError({
|
|
149
|
+
ok: false,
|
|
150
|
+
reason: "feature",
|
|
151
|
+
missing: [feature],
|
|
152
|
+
host: this.check.range ?? { min: 0, max: 0, features },
|
|
153
|
+
});
|
|
154
|
+
}
|
|
128
155
|
/** The Host's protocol features, including optional ones, from its `/health`. */
|
|
129
156
|
async hostFeatures(signal) {
|
|
130
157
|
await this.ensureCompatible(signal);
|
|
131
158
|
return this.check.features;
|
|
132
159
|
}
|
|
133
|
-
authHeaders(init = {}) {
|
|
160
|
+
async authHeaders(init = {}) {
|
|
134
161
|
const headers = new Headers(init.headers);
|
|
135
162
|
for (const [name, value] of Object.entries(this.headers))
|
|
136
163
|
headers.set(name, value);
|
|
137
|
-
|
|
138
|
-
|
|
164
|
+
const bearer = this.token
|
|
165
|
+
? await this.token.get(init.signal ?? undefined)
|
|
166
|
+
: this.key;
|
|
167
|
+
headers.set("Authorization", `Bearer ${bearer}`);
|
|
168
|
+
if (this.publishableKey)
|
|
169
|
+
headers.set(PUBLISHABLE_KEY_HEADER, this.publishableKey);
|
|
139
170
|
headers.set(PROTOCOL_HEADER, String(PROTOCOL_VERSION));
|
|
140
171
|
if (init.body)
|
|
141
172
|
headers.set("Content-Type", "application/json");
|
|
@@ -145,9 +176,18 @@ export class Transport {
|
|
|
145
176
|
await this.ensureCompatible(init.signal === null ? undefined : init.signal);
|
|
146
177
|
const response = await this.fetcher(this.url + path, {
|
|
147
178
|
...init,
|
|
148
|
-
headers: this.authHeaders(init),
|
|
179
|
+
headers: await this.authHeaders(init),
|
|
149
180
|
redirect: "error",
|
|
150
181
|
});
|
|
182
|
+
// An expired or revoked subject token: get a new one and try once more.
|
|
183
|
+
if (response.status === 401 && this.token && !options.retried401) {
|
|
184
|
+
const body = (await response.clone().json().catch(() => undefined));
|
|
185
|
+
if (body?.code === "token_expired") {
|
|
186
|
+
await response.body?.cancel().catch(() => { });
|
|
187
|
+
this.token.invalidate();
|
|
188
|
+
return this.request(path, init, { ...options, retried401: true });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
151
191
|
if (response.status === 426) {
|
|
152
192
|
this.clearCompatibilityCache();
|
|
153
193
|
if (!options.retried426) {
|
|
@@ -185,6 +225,18 @@ export class Transport {
|
|
|
185
225
|
}
|
|
186
226
|
return response;
|
|
187
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Sends a request with this client's credentials and returns the Runtime's response as it
|
|
230
|
+
* is, errors included (an app server passing a response through, e.g. the AG-UI handler).
|
|
231
|
+
*/
|
|
232
|
+
async forward(path, init = {}) {
|
|
233
|
+
await this.ensureCompatible(init.signal === null ? undefined : init.signal);
|
|
234
|
+
return this.fetcher(this.url + path, {
|
|
235
|
+
...init,
|
|
236
|
+
headers: await this.authHeaders(init),
|
|
237
|
+
redirect: "error",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
188
240
|
async json(path, method = "GET", body, signal) {
|
|
189
241
|
const response = await this.request(path, {
|
|
190
242
|
method,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION,
|
|
1
|
+
export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
|
|
2
2
|
export type { ProtocolFeature, ProtocolRange, Compatibility, ErrorCode, } from "@nylorun/core/compatibility";
|
|
3
3
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
4
4
|
export { AgentBuildError, AgentLifecycleError, Loop, Chain, Switch, Parallel, Map, VerdictSchema, isVerdict, withInstructions, withoutTools, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, defineSchema, ToolError, } from "@nylorun/core/define";
|
|
5
5
|
export type { FlowAgentBuilder, Flow, Named, StageArgs, NestedStageArgs, CapabilityIdentity, } from "@nylorun/core/define";
|
|
6
6
|
export type { AgentOptions, AgentManifest, BuiltAgent, BuiltWorkflow, LoopOptions, LoopDecideArgs, LoopVerifyArgs, LoopDecision, ChainOptions, SwitchOptions, ParallelOptions, MapOptions, Verdict, WorkflowBinding, WorkflowManifest, ToolDefinition, ToolExecutionContext, ToolOutcome, Patch, Decision, TurnDecision, HookScope, BeforeHook, AfterHook, JsonValue, JsonObject, } from "@nylorun/core/define";
|
|
7
|
-
export type { LiveEvent, SessionCommand, Action, ActionOutcome, CredentialInfo, CredentialSelection, VaultInfo, SubjectScope, } from "@nylorun/core/contracts";
|
|
7
|
+
export type { LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, Action, ActionOutcome, CredentialInfo, CredentialSelection, VaultInfo, SubjectScope, } from "@nylorun/core/contracts";
|
|
8
8
|
export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
|
9
9
|
export { AgentsClient, SessionClient, createClient } from "./client.js";
|
|
10
10
|
export type { ActAsOptions, AgentSource, SessionView, CommandOptions, SessionSandbox, } from "./client.js";
|
|
11
|
-
export {
|
|
12
|
-
export type {
|
|
11
|
+
export { AccessClient, PublishableKeysClient, SigningKeysClient, TokensClient, } from "./access.js";
|
|
12
|
+
export type { CreateTokenOptions } from "./access.js";
|
|
13
|
+
export { createTokenEndpoint } from "./token-endpoint.js";
|
|
14
|
+
export type { TokenEndpointOptions } from "./token-endpoint.js";
|
|
15
|
+
export { createActionHandler } from "./action-handler.js";
|
|
16
|
+
export type { ActionHandler, ActionHandlerOptions, RegisterOptions, } from "./action-handler.js";
|
|
13
17
|
export type { ExecuteActionOptions, ExecutableDefinition, } from "./execute-action.js";
|
|
14
18
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
15
19
|
export type { ResolvedConnection } from "./connection.js";
|
|
16
|
-
export { deriveExecutorToken } from "./derived-credentials.js";
|
|
17
20
|
export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
|
18
|
-
export type { Destination, IncompatibleReason } from "./http.js";
|
|
21
|
+
export type { Destination, IncompatibleReason, TokenSource } from "./http.js";
|
|
19
22
|
export { plugin } from "./plugins/plugin.js";
|
|
20
23
|
export type { PluginCapability } from "./plugins/plugin.js";
|
|
21
24
|
export { loadPlugin, PluginError } from "./plugins/load.js";
|
|
@@ -26,6 +29,5 @@ export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsE
|
|
|
26
29
|
export type { SkillsCapability, SkillsOptions, SkillDiagnostic, } from "./skills/index.js";
|
|
27
30
|
export { mcp, McpError } from "./mcp/index.js";
|
|
28
31
|
export type { McpCapability, McpOptions, McpServerSpec } from "./mcp/index.js";
|
|
29
|
-
export {
|
|
30
|
-
export type { SandboxCapability, SandboxCapabilityOptions, SandboxOptions, ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./sandbox/index.js";
|
|
32
|
+
export type { ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./sandbox/index.js";
|
|
31
33
|
export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./sandbox/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// Definition authoring and wire contracts are the only harness runtime imports.
|
|
2
|
-
export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION,
|
|
2
|
+
export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
|
|
3
3
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
4
4
|
export { AgentBuildError, AgentLifecycleError, Loop, Chain, Switch, Parallel, Map, VerdictSchema, isVerdict, withInstructions, withoutTools, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, defineSchema, ToolError, } from "@nylorun/core/define";
|
|
5
5
|
export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
|
6
6
|
export { AgentsClient, SessionClient, createClient } from "./client.js";
|
|
7
|
-
export {
|
|
7
|
+
export { AccessClient, PublishableKeysClient, SigningKeysClient, TokensClient, } from "./access.js";
|
|
8
|
+
export { createTokenEndpoint } from "./token-endpoint.js";
|
|
9
|
+
export { createActionHandler } from "./action-handler.js";
|
|
8
10
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
9
|
-
export { deriveExecutorToken } from "./derived-credentials.js";
|
|
10
11
|
export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
|
11
12
|
export { plugin } from "./plugins/plugin.js";
|
|
12
13
|
export { loadPlugin, PluginError } from "./plugins/load.js";
|
|
13
14
|
export { prepareStdioLaunch, expandPluginPlaceholders } from "./plugins/launch.js";
|
|
14
15
|
export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
|
|
15
16
|
export { mcp, McpError } from "./mcp/index.js";
|
|
16
|
-
export { sandbox, SandboxError } from "./sandbox/index.js";
|
|
17
17
|
export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./sandbox/index.js";
|
package/dist/sandbox/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Action-scoped sandbox client (`ctx.sandbox`). Calls `POST /v1/actions/:id/sandbox/:tool`
|
|
3
|
+
* over a transport whose credential is the Action's delivery token.
|
|
4
4
|
*/
|
|
5
5
|
import { type SandboxToolName } from "@nylorun/core/define";
|
|
6
6
|
import { type Transport } from "../http.js";
|
|
@@ -45,13 +45,12 @@ export type ActionSandbox = {
|
|
|
45
45
|
}) => Promise<ActionSandboxToolResult>;
|
|
46
46
|
};
|
|
47
47
|
export type CreateActionSandboxOptions = {
|
|
48
|
+
/** The transport's key is the delivery token, which names the Action and its generation. */
|
|
48
49
|
readonly transport: Transport;
|
|
49
50
|
readonly actionId: string;
|
|
50
|
-
readonly claimId: string;
|
|
51
|
-
readonly generation: number;
|
|
52
51
|
readonly signal?: AbortSignal;
|
|
53
52
|
};
|
|
54
|
-
/** Build `ctx.sandbox` for one
|
|
53
|
+
/** Build `ctx.sandbox` for one delivered action. Callers skip it when the session has none. */
|
|
55
54
|
export declare function createActionSandbox(options: CreateActionSandboxOptions): ActionSandbox;
|
|
56
55
|
export declare function isActionSandboxTool(name: string): name is SandboxToolName;
|
|
57
56
|
/** True when the executable declares a sandbox the Runtime can share. */
|
package/dist/sandbox/client.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Action-scoped sandbox client (`ctx.sandbox`). Calls `POST /v1/actions/:id/sandbox/:tool`
|
|
3
|
+
* over a transport whose credential is the Action's delivery token.
|
|
4
4
|
*/
|
|
5
5
|
import { SANDBOX_TOOL_NAMES, } from "@nylorun/core/define";
|
|
6
6
|
import { segment } from "../http.js";
|
|
7
|
-
/** Build `ctx.sandbox` for one
|
|
7
|
+
/** Build `ctx.sandbox` for one delivered action. Callers skip it when the session has none. */
|
|
8
8
|
export function createActionSandbox(options) {
|
|
9
|
-
const call = (tool, input) => options.transport.json(`/v1/actions/${segment(options.actionId)}/sandbox/${tool}`, "POST",
|
|
10
|
-
claimId: options.claimId,
|
|
11
|
-
generation: options.generation,
|
|
12
|
-
...input,
|
|
13
|
-
}, options.signal);
|
|
9
|
+
const call = (tool, input) => options.transport.json(`/v1/actions/${segment(options.actionId)}/sandbox/${tool}`, "POST", input, options.signal);
|
|
14
10
|
const sandbox = {
|
|
15
11
|
bash: (input) => call("bash", input),
|
|
16
12
|
read: (input) => call("read", input),
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export { sandbox, SandboxError } from "@nylorun/core/define";
|
|
2
|
-
export type { SandboxCapability, SandboxCapabilityOptions, SandboxOptions, } from "@nylorun/core/define";
|
|
3
1
|
export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./client.js";
|
|
4
2
|
export type { ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./client.js";
|
package/dist/sandbox/index.js
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type BuiltWorkflow } from "@nylorun/core/define";
|
|
2
|
+
import { type AgentSource } from "./client.js";
|
|
3
|
+
import type { ExecutableDefinition } from "./execute-action.js";
|
|
4
|
+
/** The implementation version to register: the option, `NYLORUN_IMPLEMENTATION_VERSION`, or `dev`. */
|
|
5
|
+
export declare function implementationVersionOf(options: {
|
|
6
|
+
implementationVersion?: string;
|
|
7
|
+
}): string;
|
|
8
|
+
/** The ids among `agents` that are saved only as part of another definition. */
|
|
9
|
+
export declare function embeddedIn(agents: Map<string, ExecutableDefinition>): ReadonlySet<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Every definition `sources` serves, by id: the agents and workflows passed in, the agents
|
|
12
|
+
* a v2 workflow embeds, and flow agents used as tools. `owner` names the caller in errors.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildAgents(sources: readonly (AgentSource | BuiltWorkflow)[], owner: string): Map<string, ExecutableDefinition>;
|
|
15
|
+
/**
|
|
16
|
+
* The manifest hash served for a workflow's flow actions. A run is pinned to one workflow
|
|
17
|
+
* manifest, and its stage keys may shift between deploys, so flow actions for another hash
|
|
18
|
+
* are left for whoever serves it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function flowManifestHash(agent: ExecutableDefinition): string | undefined;
|