@nylorun/agents 0.14.0-beta → 0.16.1-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.
@@ -1,159 +0,0 @@
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, tenantTokenIssuer } from "@nylorun/core/contracts";
9
- /** The issuer prefix of a Tenant's tokens: `urn:nylorun:tenant:<tenantId>`. */
10
- const ISSUER_PREFIX = tenantTokenIssuer("");
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 !== tenantTokenIssuer(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
- }
@@ -1,16 +0,0 @@
1
- import { type BuiltAgent, type BuiltWorkflow } from "@nylorun/core/define";
2
- import type { Action, ActionOutcome } from "@nylorun/core/contracts";
3
- import type { ActionSandbox } from "./sandbox/client.js";
4
- export type ExecutableDefinition = BuiltAgent | BuiltWorkflow;
5
- export type ExecuteActionOptions = {
6
- /** Claim-scoped sandbox client; omitted when the session has no sandbox. */
7
- readonly sandbox?: ActionSandbox;
8
- };
9
- /** Executes code only after a host-issued claim. No engine dependency. */
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;
@@ -1,288 +0,0 @@
1
- import { delegateOf, implementationsFor, isBuiltWorkflow, isToolError, normalizeToolDefinition, normalizedSchemasFor, runHookPoint, } from "@nylorun/core/define";
2
- class Suspend {
3
- outcome;
4
- constructor(outcome) {
5
- this.outcome = outcome;
6
- }
7
- }
8
- const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
9
- ? value
10
- : {};
11
- /** Executes code only after a host-issued claim. No engine dependency. */
12
- export async function executeAction(action, root, signal, options = {}) {
13
- const sandbox = options.sandbox;
14
- if (action.kind === "fn" || action.kind === "verify") {
15
- if (!isBuiltWorkflow(root))
16
- throw new Error(`Action ${action.kind} requires a workflow definition`);
17
- return executeWorkflowFn(action, root.getBinding(), signal, sandbox);
18
- }
19
- if (isBuiltWorkflow(root)) {
20
- if (action.kind === "tool" && "key" in action && typeof action.key === "string")
21
- return executeWorkflowTool(action, root.getBinding(), signal, sandbox);
22
- throw new Error(`Unsupported action kind ${action.kind} on workflow`);
23
- }
24
- // Work for an agent used as a tool runs that agent's code, served from the root's binding.
25
- const agent = action.agent ? delegatedAgent(root, action.agent.id) : root;
26
- const ref = action.agent ?? { id: root.id, path: root.id };
27
- if (action.kind === "hook")
28
- // All capabilities at this hook point run concurrently in one action. Never throws.
29
- return {
30
- value: {
31
- results: await runHookPoint(implementationsFor(agent), action.hook, action.input),
32
- },
33
- };
34
- if (action.kind !== "tool" || !("capabilityId" in action))
35
- throw new Error(`Unsupported action kind ${action.kind}`);
36
- const impl = implementationsFor(agent)[action.capabilityId];
37
- if (!impl)
38
- throw new Error(`No capability ${action.capabilityId}`);
39
- const raw = impl.tools?.[action.toolName];
40
- if (!raw)
41
- throw new Error("Missing tool implementation");
42
- const tool = normalizeToolDefinition(raw);
43
- return runTool(action, tool, ref, signal, sandbox);
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
- }
69
- async function executeWorkflowTool(action, binding, signal, sandbox) {
70
- const impl = binding.nodes[action.key];
71
- if (!impl || impl.kind !== "tool")
72
- throw new Error(`No tool implementation for key ${action.key}`);
73
- const path = "path" in action && typeof action.path === "string" ? action.path : action.key;
74
- return runTool(action, impl.tool, { id: action.agentId, path }, signal, sandbox);
75
- }
76
- async function runTool(action, raw, ref, signal, sandbox) {
77
- const tool = "execute" in raw && typeof raw.execute === "function" && "inputSchema" in raw
78
- ? raw
79
- : normalizeToolDefinition(raw);
80
- const schemas = "inputSchema" in tool && tool.inputSchema && "validate" in tool.inputSchema
81
- ? {
82
- inputSchema: tool.inputSchema,
83
- outputSchema: "outputSchema" in tool ? tool.outputSchema : undefined,
84
- }
85
- : normalizedSchemasFor(tool);
86
- const input = schemas.inputSchema.validate(action.input);
87
- if (!input.ok)
88
- return {
89
- value: {
90
- kind: "failed",
91
- code: "tool.invalid-input",
92
- message: JSON.stringify(input.issues),
93
- },
94
- };
95
- const ctx = action.context;
96
- const state = { ...object(ctx.state) };
97
- const statePatch = {};
98
- const resume = object(ctx.resume);
99
- const token = object(resume.token);
100
- const memo = object(token._nylorun);
101
- const steps = { ...object(memo.steps) };
102
- const answers = { ...object(memo.answers) };
103
- let waitIndex = 0;
104
- let gateApproved = Boolean(memo.gateApproved) ||
105
- Boolean(memo.approvalGate && resume.kind === "approval" && resume.approved);
106
- const durableToken = (extra = {}) => ({
107
- _nylorun: { steps, answers, waitIndex, gateApproved, ...extra },
108
- });
109
- const wait = async (kind, prompt, metadata) => {
110
- const index = String(waitIndex++);
111
- if (index in answers)
112
- return answers[index];
113
- if (!memo.approvalGate &&
114
- resume.kind === kind &&
115
- Number(memo.waitIndex ?? 0) === Number(index)) {
116
- const answer = kind === "approval" ? Boolean(resume.approved) : resume.value;
117
- answers[index] = answer;
118
- return answer;
119
- }
120
- throw new Suspend({
121
- kind: "interaction-required",
122
- interaction: { kind, prompt, ...(metadata ? { metadata } : {}) },
123
- token: {
124
- _nylorun: { steps, answers, waitIndex: Number(index), gateApproved },
125
- },
126
- });
127
- };
128
- const context = {
129
- executionId: String(ctx.executionId ?? action.sessionId),
130
- turnId: String(ctx.turnId ?? action.turnId),
131
- stepId: String(ctx.stepId ?? ""),
132
- callId: String(ctx.callId ?? action.actionId),
133
- invocationId: String(ctx.invocationId ?? action.actionId),
134
- idempotencyKey: String(ctx.idempotencyKey ?? action.actionId),
135
- signal,
136
- info: ctx.info,
137
- session: { id: action.sessionId },
138
- agent: ref,
139
- ...(ctx.resume ? { resume: ctx.resume } : {}),
140
- ...(sandbox ? { sandbox } : {}),
141
- state: {
142
- get: (key) => state[key],
143
- set: (key, value) => {
144
- state[key] = value;
145
- statePatch[key] = value;
146
- },
147
- entries: () => ({ ...state }),
148
- },
149
- progress() {
150
- /* Canonical events are host-owned; progress transport is deferred. */
151
- },
152
- ask: (prompt, options) => wait("response", prompt, options),
153
- approve: (prompt) => wait("approval", prompt),
154
- sleep: async (duration) => {
155
- throw new Suspend({
156
- kind: "deferred",
157
- token: durableToken({ wait: { kind: "sleep", duration } }),
158
- });
159
- },
160
- waitFor: async (event, options) => {
161
- throw new Suspend({
162
- kind: "deferred",
163
- token: durableToken({ wait: { kind: "waitFor", event, ...options } }),
164
- });
165
- },
166
- step: async (name, fn) => {
167
- if (Object.prototype.hasOwnProperty.call(steps, name))
168
- return steps[name];
169
- signal.throwIfAborted();
170
- const value = await fn();
171
- // Memoized values are durable only with a persisted action outcome. Interrupted actions stay uncertain.
172
- steps[name] = JSON.parse(JSON.stringify(value ?? null));
173
- return value;
174
- },
175
- };
176
- try {
177
- signal.throwIfAborted();
178
- const approval = "approval" in tool ? tool.approval : undefined;
179
- if (approval && !gateApproved) {
180
- const result = await approval(input.value);
181
- if (result)
182
- throw new Suspend({
183
- kind: "interaction-required",
184
- interaction: {
185
- kind: "approval",
186
- prompt: typeof result === "string"
187
- ? result
188
- : `Approve ${"name" in tool ? tool.name : "tool"}?`,
189
- },
190
- token: durableToken({ approvalGate: true }),
191
- });
192
- gateApproved = true;
193
- }
194
- if (resume.kind === "approval" && resume.approved === false)
195
- return { value: { kind: "denied", reason: "Approval denied" } };
196
- const execute = "execute" in tool && typeof tool.execute === "function"
197
- ? tool.execute
198
- : undefined;
199
- if (!execute)
200
- throw new Error("Missing tool implementation");
201
- const value = await execute(input.value, context);
202
- const tagged = object(value);
203
- let outcome;
204
- if ([
205
- "completed",
206
- "failed",
207
- "denied",
208
- "interaction-required",
209
- "deferred",
210
- ].includes(tagged.kind))
211
- outcome = value;
212
- else
213
- outcome = { kind: "completed", output: value ?? null };
214
- if (outcome.kind === "completed" && schemas.outputSchema) {
215
- const parsed = schemas.outputSchema.validate(outcome.output);
216
- outcome = parsed.ok
217
- ? { ...outcome, output: parsed.value }
218
- : {
219
- kind: "failed",
220
- code: "tool.invalid-output",
221
- message: JSON.stringify(parsed.issues),
222
- };
223
- }
224
- return { value: outcome, statePatch };
225
- }
226
- catch (error) {
227
- if (signal.aborted)
228
- throw signal.reason;
229
- return {
230
- value: error instanceof Suspend
231
- ? error.outcome
232
- : {
233
- kind: "failed",
234
- code: isToolError(error) ? error.code : "tool.execution-failed",
235
- message: message(error),
236
- },
237
- statePatch,
238
- };
239
- }
240
- }
241
- async function executeWorkflowFn(action, binding, signal, sandbox) {
242
- const impl = binding.nodes[action.key];
243
- if (!impl || impl.kind !== action.kind)
244
- throw new Error(`No ${action.kind} implementation for key ${action.key}`);
245
- signal.throwIfAborted();
246
- try {
247
- // Verify may receive a tool-like context later (L3/L4); tracer passes input only.
248
- const value = action.kind === "verify"
249
- ? await impl.fn(action.input, {
250
- signal,
251
- info: action.context.info,
252
- session: { id: action.sessionId },
253
- ...(sandbox ? { sandbox } : {}),
254
- step: async (_name, fn) => fn(),
255
- approve: async () => {
256
- throw new Error("Approvals in verify are not available in the tracer");
257
- },
258
- ask: async () => {
259
- throw new Error("ask in verify is not available in the tracer");
260
- },
261
- progress() { },
262
- })
263
- : await impl.fn(action.input);
264
- return { value };
265
- }
266
- catch (error) {
267
- if (signal.aborted)
268
- throw signal.reason;
269
- return {
270
- value: {
271
- kind: "failed",
272
- code: action.kind === "verify" ? "loop.verify-failed" : "fn.failed",
273
- message: message(error),
274
- },
275
- };
276
- }
277
- }
278
- function delegatedAgent(root, id) {
279
- for (const capability of Object.values(implementationsFor(root))) {
280
- const child = delegateOf(capability.tools?.[id])?.agent;
281
- if (child)
282
- return child;
283
- }
284
- throw new Error(`Agent '${root.id}' does not use '${id}' as a tool`);
285
- }
286
- function message(error) {
287
- return error instanceof Error ? error.message : String(error);
288
- }
@@ -1,17 +0,0 @@
1
- import type { McpServerManifest } from "@nylorun/core/define";
2
- /** Single-pass replacement. Text inserted for a token is not scanned again. */
3
- export declare function expandPluginPlaceholders(value: string, pluginRoot: string, pluginData: string): string;
4
- export interface StdioLaunch {
5
- readonly command: string;
6
- readonly args: readonly string[];
7
- readonly cwd: string;
8
- readonly env: Readonly<Record<string, string>>;
9
- }
10
- export declare function prepareStdioLaunch(server: Extract<McpServerManifest, {
11
- type: "stdio";
12
- }>, options: {
13
- readonly pluginRoot: string;
14
- readonly pluginData: string;
15
- readonly baseEnv?: Readonly<Record<string, string | undefined>>;
16
- }): StdioLaunch;
17
- export declare function resolveContainedCwd(cwd: string, pluginRoot: string, pluginData: string): string;
@@ -1,71 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
- import { isAbsolute, relative, resolve, sep } from "node:path";
3
- const ROOT_TOKEN = "${PLUGIN_ROOT}";
4
- const DATA_TOKEN = "${PLUGIN_DATA}";
5
- /** Single-pass replacement. Text inserted for a token is not scanned again. */
6
- export function expandPluginPlaceholders(value, pluginRoot, pluginData) {
7
- let out = "";
8
- for (let index = 0; index < value.length;) {
9
- if (value.startsWith(ROOT_TOKEN, index)) {
10
- out += pluginRoot;
11
- index += ROOT_TOKEN.length;
12
- continue;
13
- }
14
- if (value.startsWith(DATA_TOKEN, index)) {
15
- out += pluginData;
16
- index += DATA_TOKEN.length;
17
- continue;
18
- }
19
- out += value[index];
20
- index += 1;
21
- }
22
- return out;
23
- }
24
- export function prepareStdioLaunch(server, options) {
25
- const pluginRoot = options.pluginRoot;
26
- const pluginData = options.pluginData;
27
- mkdirSync(pluginData, { recursive: true });
28
- const command = resolveCommand(server.command, pluginRoot);
29
- const args = (server.args ?? []).map((arg) => expandPluginPlaceholders(arg, pluginRoot, pluginData));
30
- const cwd = server.cwd
31
- ? resolveContainedCwd(server.cwd, pluginRoot, pluginData)
32
- : pluginRoot;
33
- const env = {};
34
- for (const [key, value] of Object.entries(options.baseEnv ?? {})) {
35
- if (typeof value === "string")
36
- setEnv(env, key, value);
37
- }
38
- for (const [key, value] of Object.entries(server.env ?? {}))
39
- setEnv(env, key, expandPluginPlaceholders(value, pluginRoot, pluginData));
40
- setEnv(env, "PLUGIN_ROOT", pluginRoot);
41
- setEnv(env, "PLUGIN_DATA", pluginData);
42
- return { command, args, cwd, env };
43
- }
44
- function resolveCommand(command, pluginRoot) {
45
- if (command.startsWith("./"))
46
- return resolve(pluginRoot, command);
47
- return command;
48
- }
49
- export function resolveContainedCwd(cwd, pluginRoot, pluginData) {
50
- const expanded = expandPluginPlaceholders(cwd, pluginRoot, pluginData);
51
- const absolute = isAbsolute(expanded) ? expanded : resolve(pluginRoot, expanded);
52
- const root = cwdTargetsData(cwd) ? pluginData : pluginRoot;
53
- if (!isInside(root, absolute))
54
- throw new Error(`Working directory escapes ${root}`);
55
- return absolute;
56
- }
57
- function cwdTargetsData(cwd) {
58
- return cwd === DATA_TOKEN || cwd.startsWith(`${DATA_TOKEN}/`);
59
- }
60
- function isInside(root, target) {
61
- const rel = relative(root, target);
62
- return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
63
- }
64
- function setEnv(env, key, value) {
65
- if (process.platform === "win32") {
66
- const existing = Object.keys(env).find((name) => name.toLowerCase() === key.toLowerCase());
67
- if (existing && existing !== key)
68
- delete env[existing];
69
- }
70
- env[key] = value;
71
- }
@@ -1,63 +0,0 @@
1
- /**
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
- */
5
- import { type SandboxToolName } from "@nylorun/core/define";
6
- import { type Transport } from "../http.js";
7
- export type ActionSandboxToolResult = {
8
- readonly kind: "completed";
9
- readonly output: unknown;
10
- } | {
11
- readonly kind: "failed";
12
- readonly code: string;
13
- readonly message: string;
14
- };
15
- export type ActionSandbox = {
16
- readonly bash: (input: {
17
- command: string;
18
- timeout?: number;
19
- }) => Promise<ActionSandboxToolResult>;
20
- readonly read: (input: {
21
- path: string;
22
- offset?: number;
23
- limit?: number;
24
- }) => Promise<ActionSandboxToolResult>;
25
- readonly write: (input: {
26
- path: string;
27
- content: string;
28
- }) => Promise<ActionSandboxToolResult>;
29
- readonly edit: (input: {
30
- path: string;
31
- old_string: string;
32
- new_string: string;
33
- replace_all?: boolean;
34
- }) => Promise<ActionSandboxToolResult>;
35
- readonly grep: (input: {
36
- pattern: string;
37
- path?: string;
38
- glob?: string;
39
- ignore_case?: boolean;
40
- max_results?: number;
41
- }) => Promise<ActionSandboxToolResult>;
42
- readonly glob: (input: {
43
- pattern: string;
44
- path?: string;
45
- }) => Promise<ActionSandboxToolResult>;
46
- };
47
- export type CreateActionSandboxOptions = {
48
- /** The transport's key is the delivery token, which names the Action and its generation. */
49
- readonly transport: Transport;
50
- readonly actionId: string;
51
- readonly signal?: AbortSignal;
52
- };
53
- /** Build `ctx.sandbox` for one delivered action. Callers skip it when the session has none. */
54
- export declare function createActionSandbox(options: CreateActionSandboxOptions): ActionSandbox;
55
- export declare function isActionSandboxTool(name: string): name is SandboxToolName;
56
- /** True when the executable declares a sandbox the Runtime can share. */
57
- export declare function definitionDeclaresSandbox(manifest: {
58
- kind?: string;
59
- sandbox?: unknown;
60
- capabilities?: readonly {
61
- sandbox?: unknown;
62
- }[];
63
- }): boolean;
@@ -1,29 +0,0 @@
1
- /**
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
- */
5
- import { SANDBOX_TOOL_NAMES, } from "@nylorun/core/define";
6
- import { segment } from "../http.js";
7
- /** Build `ctx.sandbox` for one delivered action. Callers skip it when the session has none. */
8
- export function createActionSandbox(options) {
9
- const call = (tool, input) => options.transport.json(`/v1/actions/${segment(options.actionId)}/sandbox/${tool}`, "POST", input, options.signal);
10
- const sandbox = {
11
- bash: (input) => call("bash", input),
12
- read: (input) => call("read", input),
13
- write: (input) => call("write", input),
14
- edit: (input) => call("edit", input),
15
- grep: (input) => call("grep", input),
16
- glob: (input) => call("glob", input),
17
- };
18
- // Freeze so tools cannot replace built-ins.
19
- return Object.freeze(sandbox);
20
- }
21
- export function isActionSandboxTool(name) {
22
- return SANDBOX_TOOL_NAMES.includes(name);
23
- }
24
- /** True when the executable declares a sandbox the Runtime can share. */
25
- export function definitionDeclaresSandbox(manifest) {
26
- if (manifest.kind === "workflow")
27
- return manifest.sandbox !== undefined;
28
- return (manifest.capabilities ?? []).some((item) => item.sandbox !== undefined);
29
- }
@@ -1,2 +0,0 @@
1
- export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./client.js";
2
- export type { ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./client.js";
@@ -1 +0,0 @@
1
- export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./client.js";
@@ -1,20 +0,0 @@
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;