@nylorun/agents 0.8.0-beta → 0.10.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/README.md +371 -166
  3. package/dist/a2a/handler.d.ts +59 -0
  4. package/dist/a2a/handler.js +178 -0
  5. package/dist/a2a/index.d.ts +8 -0
  6. package/dist/a2a/index.js +7 -0
  7. package/dist/access.d.ts +101 -0
  8. package/dist/access.js +111 -0
  9. package/dist/action-handler.d.ts +64 -0
  10. package/dist/action-handler.js +283 -0
  11. package/dist/ag-ui/handler.d.ts +12 -4
  12. package/dist/ag-ui/handler.js +87 -281
  13. package/dist/ag-ui/index.d.ts +2 -1
  14. package/dist/ag-ui/index.js +2 -1
  15. package/dist/browser.d.ts +97 -0
  16. package/dist/browser.js +277 -0
  17. package/dist/builder.d.ts +20 -0
  18. package/dist/builder.js +26 -0
  19. package/dist/client.d.ts +3 -230
  20. package/dist/client.js +2 -335
  21. package/dist/connection.d.ts +0 -1
  22. package/dist/connection.js +3 -9
  23. package/dist/delivery-token.d.ts +57 -0
  24. package/dist/delivery-token.js +150 -0
  25. package/dist/execute-action.d.ts +6 -0
  26. package/dist/execute-action.js +24 -0
  27. package/dist/http.d.ts +31 -1
  28. package/dist/http.js +74 -14
  29. package/dist/index.d.ts +13 -9
  30. package/dist/index.js +5 -4
  31. package/dist/mcp/index.d.ts +2 -2
  32. package/dist/mcp/index.js +1 -1
  33. package/dist/sandbox/client.d.ts +4 -5
  34. package/dist/sandbox/client.js +4 -8
  35. package/dist/sandbox/index.d.ts +0 -2
  36. package/dist/sandbox/index.js +0 -1
  37. package/dist/served-definitions.d.ts +20 -0
  38. package/dist/served-definitions.js +100 -0
  39. package/dist/session-client.d.ts +282 -0
  40. package/dist/session-client.js +375 -0
  41. package/dist/sse.js +12 -0
  42. package/dist/studio-embed.d.ts +9 -0
  43. package/dist/studio-embed.js +8 -0
  44. package/dist/token-endpoint.d.ts +23 -0
  45. package/dist/token-endpoint.js +30 -0
  46. package/package.json +15 -8
  47. package/dist/ag-ui/history.d.ts +0 -7
  48. package/dist/ag-ui/history.js +0 -60
  49. package/dist/ag-ui/sse.d.ts +0 -9
  50. package/dist/ag-ui/sse.js +0 -7
  51. package/dist/ag-ui/translate.d.ts +0 -38
  52. package/dist/ag-ui/translate.js +0 -222
  53. package/dist/derived-credentials.d.ts +0 -5
  54. package/dist/derived-credentials.js +0 -17
  55. package/dist/executor.d.ts +0 -20
  56. package/dist/executor.js +0 -353
  57. package/dist/mcp/mcp.d.ts +0 -30
  58. package/dist/mcp/mcp.js +0 -80
  59. package/dist/sandbox/sandbox.d.ts +0 -27
  60. package/dist/sandbox/sandbox.js +0 -92
package/dist/http.d.ts CHANGED
@@ -1,9 +1,22 @@
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
+ }
2
11
  export interface Destination {
3
12
  url?: string;
4
13
  key?: string;
5
14
  tenant?: string;
6
15
  fetch?: typeof fetch;
16
+ /** Instead of `key`: subject tokens. The client then skips the `/health` check. */
17
+ token?: TokenSource;
18
+ /** A publishable key (Host feature `browser-access`); it also names the Tenant. */
19
+ publishableKey?: string;
7
20
  }
8
21
  export declare function env(name: string): string | undefined;
9
22
  export declare class RuntimeError extends Error {
@@ -23,27 +36,44 @@ export declare class IncompatibleRuntimeError extends Error {
23
36
  }
24
37
  export declare class Transport {
25
38
  readonly url: string;
39
+ /** The Tenant key; empty for a client that uses subject tokens. */
26
40
  readonly key: string;
41
+ /** The Tenant id; empty when a publishable key names it. */
27
42
  readonly tenant: string;
43
+ readonly token: TokenSource | undefined;
44
+ readonly publishableKey: string | undefined;
28
45
  readonly fetcher: typeof fetch;
29
46
  /** Sent on every request, e.g. `Nylorun-Subject` and `Nylorun-Scopes` (`withHeaders`). */
30
47
  readonly headers: Readonly<Record<string, string>>;
31
48
  private readonly check;
32
- constructor(options?: Destination, role?: "server" | "executor");
49
+ constructor(options?: Destination);
33
50
  /**
34
51
  * The same destination with `headers` added to every request. The copy shares this
35
52
  * transport's compatibility check, so a copy made per request does not repeat `/health`.
36
53
  */
37
54
  withHeaders(headers: Readonly<Record<string, string>>): Transport;
55
+ /**
56
+ * The same destination with another bearer (a delivery token for an Action endpoint's
57
+ * callbacks). Like `withHeaders`, the copy shares this transport's compatibility check.
58
+ */
59
+ withKey(key: string): Transport;
38
60
  /** Clears the cached Host compatibility result (used after a 426). */
39
61
  clearCompatibilityCache(): void;
40
62
  private ensureCompatible;
63
+ /** Throws `IncompatibleRuntimeError` before anything is sent when the Host lacks `feature`. */
64
+ requireFeature(feature: string, signal?: AbortSignal): Promise<void>;
41
65
  /** The Host's protocol features, including optional ones, from its `/health`. */
42
66
  hostFeatures(signal?: AbortSignal): Promise<readonly string[]>;
43
67
  private authHeaders;
44
68
  request(path: string, init?: RequestInit, options?: {
45
69
  retried426?: boolean;
70
+ retried401?: boolean;
46
71
  }): Promise<Response>;
72
+ /**
73
+ * Sends a request with this client's credentials and returns the Runtime's response as it
74
+ * is, errors included (an app server passing a response through, e.g. the AG-UI handler).
75
+ */
76
+ forward(path: string, init?: RequestInit): Promise<Response>;
47
77
  json<T>(path: string, method?: string, body?: unknown, signal?: AbortSignal): Promise<T>;
48
78
  }
49
79
  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, TENANT_HEADER, checkCompatibility, } from "@nylorun/core/compatibility";
1
+ import { PROTOCOL_FEATURES, PROTOCOL_HEADER, PROTOCOL_VERSION, PUBLISHABLE_KEY_HEADER, TENANT_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,41 @@ 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;
59
+ /** The Tenant id; empty when a publishable key names it. */
58
60
  tenant;
61
+ token;
62
+ publishableKey;
59
63
  fetcher;
60
64
  /** Sent on every request, e.g. `Nylorun-Subject` and `Nylorun-Scopes` (`withHeaders`). */
61
65
  headers = {};
62
66
  check = { compatible: false, features: [] };
63
- constructor(options = {}, role = "server") {
67
+ constructor(options = {}) {
64
68
  const url = options.url ?? env("NYLORUN_RUNTIME_URL");
65
- const key = options.key ??
66
- env(role === "server" ? "NYLORUN_SERVER_KEY" : "NYLORUN_EXECUTOR_KEY");
67
- const tenant = options.tenant ?? env("NYLORUN_TENANT");
68
- if (!url || !key)
69
- throw new Error(`Set Runtime url and ${role} key explicitly or via NYLORUN_RUNTIME_URL / NYLORUN_${role.toUpperCase()}_KEY`);
70
- if (!tenant)
69
+ const token = options.token;
70
+ const key = token ? "" : options.key ?? env("NYLORUN_SERVER_KEY");
71
+ const tenant = options.tenant ?? (token ? undefined : env("NYLORUN_TENANT"));
72
+ if (!url || (!token && !key))
73
+ throw new Error("Set Runtime url and server key explicitly or via NYLORUN_RUNTIME_URL / NYLORUN_SERVER_KEY");
74
+ if (token && options.key)
75
+ throw new Error("Use a Tenant key or subject tokens, not both");
76
+ if (!tenant && !options.publishableKey)
71
77
  throw new Error("Set tenant explicitly or via NYLORUN_TENANT");
72
78
  const parsed = new URL(url);
73
79
  if (!["http:", "https:"].includes(parsed.protocol))
74
80
  throw new Error("Runtime requires an HTTP(S) URL");
75
81
  this.url = url.replace(/\/$/, "");
76
82
  this.key = key;
77
- this.tenant = tenant;
78
- this.fetcher = options.fetch ?? globalThis.fetch;
83
+ this.tenant = tenant ?? "";
84
+ this.token = token;
85
+ this.publishableKey = options.publishableKey;
86
+ // Browsers may not call `/health` (it refuses `Origin`); token clients rely on `426`.
87
+ if (token)
88
+ this.check.compatible = true;
89
+ // Called as a method on a page's window would otherwise lose `this`.
90
+ const fetcher = options.fetch ?? globalThis.fetch;
91
+ this.fetcher = options.fetch ? fetcher : (input, init) => fetcher(input, init);
79
92
  }
80
93
  /**
81
94
  * The same destination with `headers` added to every request. The copy shares this
@@ -87,6 +100,14 @@ export class Transport {
87
100
  headers: Object.freeze({ ...this.headers, ...headers }),
88
101
  });
89
102
  }
103
+ /**
104
+ * The same destination with another bearer (a delivery token for an Action endpoint's
105
+ * callbacks). Like `withHeaders`, the copy shares this transport's compatibility check.
106
+ */
107
+ withKey(key) {
108
+ const copy = Object.create(Transport.prototype);
109
+ return Object.assign(copy, this, { key });
110
+ }
90
111
  /** Clears the cached Host compatibility result (used after a 426). */
91
112
  clearCompatibilityCache() {
92
113
  this.check.compatible = false;
@@ -123,19 +144,37 @@ export class Transport {
123
144
  if (!result.ok)
124
145
  throw new IncompatibleRuntimeError(result);
125
146
  this.check.features = [...protocol.features];
147
+ this.check.range = protocol;
126
148
  this.check.compatible = true;
127
149
  }
150
+ /** Throws `IncompatibleRuntimeError` before anything is sent when the Host lacks `feature`. */
151
+ async requireFeature(feature, signal) {
152
+ const features = await this.hostFeatures(signal);
153
+ if (!features.includes(feature))
154
+ throw new IncompatibleRuntimeError({
155
+ ok: false,
156
+ reason: "feature",
157
+ missing: [feature],
158
+ host: this.check.range ?? { min: 0, max: 0, features },
159
+ });
160
+ }
128
161
  /** The Host's protocol features, including optional ones, from its `/health`. */
129
162
  async hostFeatures(signal) {
130
163
  await this.ensureCompatible(signal);
131
164
  return this.check.features;
132
165
  }
133
- authHeaders(init = {}) {
166
+ async authHeaders(init = {}) {
134
167
  const headers = new Headers(init.headers);
135
168
  for (const [name, value] of Object.entries(this.headers))
136
169
  headers.set(name, value);
137
- headers.set("Authorization", `Bearer ${this.key}`);
138
- headers.set(TENANT_HEADER, this.tenant);
170
+ const bearer = this.token
171
+ ? await this.token.get(init.signal ?? undefined)
172
+ : this.key;
173
+ headers.set("Authorization", `Bearer ${bearer}`);
174
+ if (this.tenant)
175
+ headers.set(TENANT_HEADER, this.tenant);
176
+ if (this.publishableKey)
177
+ headers.set(PUBLISHABLE_KEY_HEADER, this.publishableKey);
139
178
  headers.set(PROTOCOL_HEADER, String(PROTOCOL_VERSION));
140
179
  if (init.body)
141
180
  headers.set("Content-Type", "application/json");
@@ -145,9 +184,18 @@ export class Transport {
145
184
  await this.ensureCompatible(init.signal === null ? undefined : init.signal);
146
185
  const response = await this.fetcher(this.url + path, {
147
186
  ...init,
148
- headers: this.authHeaders(init),
187
+ headers: await this.authHeaders(init),
149
188
  redirect: "error",
150
189
  });
190
+ // An expired or revoked subject token: get a new one and try once more.
191
+ if (response.status === 401 && this.token && !options.retried401) {
192
+ const body = (await response.clone().json().catch(() => undefined));
193
+ if (body?.code === "token_expired") {
194
+ await response.body?.cancel().catch(() => { });
195
+ this.token.invalidate();
196
+ return this.request(path, init, { ...options, retried401: true });
197
+ }
198
+ }
151
199
  if (response.status === 426) {
152
200
  this.clearCompatibilityCache();
153
201
  if (!options.retried426) {
@@ -185,6 +233,18 @@ export class Transport {
185
233
  }
186
234
  return response;
187
235
  }
236
+ /**
237
+ * Sends a request with this client's credentials and returns the Runtime's response as it
238
+ * is, errors included (an app server passing a response through, e.g. the AG-UI handler).
239
+ */
240
+ async forward(path, init = {}) {
241
+ await this.ensureCompatible(init.signal === null ? undefined : init.signal);
242
+ return this.fetcher(this.url + path, {
243
+ ...init,
244
+ headers: await this.authHeaders(init),
245
+ redirect: "error",
246
+ });
247
+ }
188
248
  async json(path, method = "GET", body, signal) {
189
249
  const response = await this.request(path, {
190
250
  method,
package/dist/index.d.ts CHANGED
@@ -1,19 +1,24 @@
1
1
  export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, TENANT_HEADER, 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
- export { Agent, AgentBuilder, AgentBuildError, AgentLifecycleError, Loop, Chain, Switch, Parallel, Map, VerdictSchema, isVerdict, withInstructions, withoutTools, WorkflowBuildError, isBuiltWorkflow, capability, tool, defineSchema, ToolError, } from "@nylorun/core/define";
3
+ export { Agent, AgentBuilder } from "./builder.js";
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
+ export type { FlowAgentBuilder, Flow, Named, StageArgs, NestedStageArgs, CapabilityIdentity, } from "@nylorun/core/define";
4
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";
5
- 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";
6
8
  export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
7
9
  export { AgentsClient, SessionClient, createClient } from "./client.js";
8
10
  export type { ActAsOptions, AgentSource, SessionView, CommandOptions, SessionSandbox, } from "./client.js";
9
- export { connectAgents } from "./executor.js";
10
- export type { ConnectOptions, AgentConnection } from "./executor.js";
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";
11
17
  export type { ExecuteActionOptions, ExecutableDefinition, } from "./execute-action.js";
12
18
  export { resolveConnection, ConnectionError } from "./connection.js";
13
19
  export type { ResolvedConnection } from "./connection.js";
14
- export { deriveExecutorToken } from "./derived-credentials.js";
15
20
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
16
- export type { Destination, IncompatibleReason } from "./http.js";
21
+ export type { Destination, IncompatibleReason, TokenSource } from "./http.js";
17
22
  export { plugin } from "./plugins/plugin.js";
18
23
  export type { PluginCapability } from "./plugins/plugin.js";
19
24
  export { loadPlugin, PluginError } from "./plugins/load.js";
@@ -23,7 +28,6 @@ export type { StdioLaunch } from "./plugins/launch.js";
23
28
  export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
24
29
  export type { SkillsCapability, SkillsOptions, SkillDiagnostic, } from "./skills/index.js";
25
30
  export { mcp, McpError } from "./mcp/index.js";
26
- export type { McpCapability, McpOptions } from "./mcp/index.js";
27
- export { sandbox, SandboxError } from "./sandbox/index.js";
28
- export type { SandboxCapability, SandboxCapabilityOptions, SandboxOptions, ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./sandbox/index.js";
31
+ export type { McpCapability, McpOptions, McpServerSpec } from "./mcp/index.js";
32
+ export type { ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./sandbox/index.js";
29
33
  export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./sandbox/index.js";
package/dist/index.js CHANGED
@@ -1,16 +1,17 @@
1
1
  // Definition authoring and wire contracts are the only harness runtime imports.
2
2
  export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, TENANT_HEADER, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
3
- export { Agent, AgentBuilder, AgentBuildError, AgentLifecycleError, Loop, Chain, Switch, Parallel, Map, VerdictSchema, isVerdict, withInstructions, withoutTools, WorkflowBuildError, isBuiltWorkflow, capability, tool, defineSchema, ToolError, } from "@nylorun/core/define";
3
+ export { Agent, AgentBuilder } from "./builder.js";
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";
4
5
  export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
5
6
  export { AgentsClient, SessionClient, createClient } from "./client.js";
6
- export { connectAgents } from "./executor.js";
7
+ export { AccessClient, PublishableKeysClient, SigningKeysClient, TokensClient, } from "./access.js";
8
+ export { createTokenEndpoint } from "./token-endpoint.js";
9
+ export { createActionHandler } from "./action-handler.js";
7
10
  export { resolveConnection, ConnectionError } from "./connection.js";
8
- export { deriveExecutorToken } from "./derived-credentials.js";
9
11
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
10
12
  export { plugin } from "./plugins/plugin.js";
11
13
  export { loadPlugin, PluginError } from "./plugins/load.js";
12
14
  export { prepareStdioLaunch, expandPluginPlaceholders } from "./plugins/launch.js";
13
15
  export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
14
16
  export { mcp, McpError } from "./mcp/index.js";
15
- export { sandbox, SandboxError } from "./sandbox/index.js";
16
17
  export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./sandbox/index.js";
@@ -1,2 +1,2 @@
1
- export { mcp, McpError } from "./mcp.js";
2
- export type { McpCapability, McpOptions } from "./mcp.js";
1
+ export { mcp, McpError, normalizeMcpServers } from "@nylorun/core/define";
2
+ export type { McpCapability, McpOptions, McpServerSpec } from "@nylorun/core/define";
package/dist/mcp/index.js CHANGED
@@ -1 +1 @@
1
- export { mcp, McpError } from "./mcp.js";
1
+ export { mcp, McpError, normalizeMcpServers } from "@nylorun/core/define";
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Claim-scoped sandbox client for executor actions (`ctx.sandbox`).
3
- * Calls `POST /v1/actions/:id/sandbox/:tool` with the live claim.
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 claimed action. Undefined callers skip when the session has none. */
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. */
@@ -1,16 +1,12 @@
1
1
  /**
2
- * Claim-scoped sandbox client for executor actions (`ctx.sandbox`).
3
- * Calls `POST /v1/actions/:id/sandbox/:tool` with the live claim.
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 claimed action. Undefined callers skip when the session has none. */
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),
@@ -1,4 +1,2 @@
1
- export { sandbox, SandboxError } from "./sandbox.js";
2
- export type { SandboxCapability, SandboxCapabilityOptions, SandboxOptions, } from "./sandbox.js";
3
1
  export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./client.js";
4
2
  export type { ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./client.js";
@@ -1,2 +1 @@
1
- export { sandbox, SandboxError } from "./sandbox.js";
2
1
  export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./client.js";
@@ -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;
@@ -0,0 +1,100 @@
1
+ /** The definitions an application serves Actions for (`createActionHandler`). */
2
+ import { AgentManifestSchema } from "@nylorun/core/contracts";
3
+ import { agentFrom, delegateOf, hashManifest, isFlowDelegate, isBuiltWorkflow, } from "@nylorun/core/define";
4
+ import { assertNoMiddlewareClosures } from "./client.js";
5
+ import { env } from "./http.js";
6
+ /** The implementation version to register: the option, `NYLORUN_IMPLEMENTATION_VERSION`, or `dev`. */
7
+ export function implementationVersionOf(options) {
8
+ return (options.implementationVersion ??
9
+ env("NYLORUN_IMPLEMENTATION_VERSION") ??
10
+ "dev");
11
+ }
12
+ /** Agents embedded in a v2 workflow: served here, but saved only as part of the workflow. */
13
+ const embeddedAgents = new WeakMap();
14
+ /** The ids among `agents` that are saved only as part of another definition. */
15
+ export function embeddedIn(agents) {
16
+ return embeddedAgents.get(agents) ?? new Set();
17
+ }
18
+ /**
19
+ * Every definition `sources` serves, by id: the agents and workflows passed in, the agents
20
+ * a v2 workflow embeds, and flow agents used as tools. `owner` names the caller in errors.
21
+ */
22
+ export function buildAgents(sources, owner) {
23
+ const agents = new Map();
24
+ const embedded = new Set();
25
+ /**
26
+ * Agents embedded in another definition (a v2 flow's agents, a flow agent used as a
27
+ * tool) may be reached from more than one place; the same definition is served once.
28
+ */
29
+ const sameAsServed = (id, manifest) => {
30
+ const served = agents.get(id);
31
+ if (!served)
32
+ return false;
33
+ if (hashManifest(served.manifest) === hashManifest(manifest))
34
+ return true;
35
+ throw new Error(`Duplicate connected agent ${id}`);
36
+ };
37
+ const addAgent = (built, shared = false) => {
38
+ assertNoMiddlewareClosures(built);
39
+ AgentManifestSchema.parse(built.manifest);
40
+ if (shared ? sameAsServed(built.id, built.manifest) : agents.has(built.id)) {
41
+ if (!shared)
42
+ throw new Error(`Duplicate connected agent ${built.id}`);
43
+ return;
44
+ }
45
+ agents.set(built.id, built);
46
+ for (const tool of built.getBinding().tools) {
47
+ const delegate = delegateOf(tool);
48
+ if (!delegate || !isFlowDelegate(delegate))
49
+ continue;
50
+ // A flow agent used as a tool runs in its own linked session: serve its code too.
51
+ if (!delegate.workflow)
52
+ throw new Error(`Flow agent '${delegate.manifest.id}' is used as a tool by '${built.id}' but has no local implementation`);
53
+ addWorkflow(delegate.workflow, true);
54
+ embedded.add(delegate.workflow.id);
55
+ }
56
+ };
57
+ const addWorkflow = (workflow, shared = false) => {
58
+ if (shared ? sameAsServed(workflow.id, workflow.manifest) : agents.has(workflow.id)) {
59
+ if (!shared)
60
+ throw new Error(`Duplicate connected agent ${workflow.id}`);
61
+ return;
62
+ }
63
+ agents.set(workflow.id, workflow);
64
+ const v2 = workflow.manifest.workflowSchemaVersion === 2;
65
+ for (const binding of Object.values(workflow.getBinding().agents)) {
66
+ addAgent(agentFrom(binding.manifest, binding.implementations), v2);
67
+ if (v2)
68
+ embedded.add(binding.manifest.id);
69
+ }
70
+ };
71
+ for (const source of sources) {
72
+ if (isBuiltWorkflow(source)) {
73
+ addWorkflow(source);
74
+ continue;
75
+ }
76
+ const built = source.build?.() ?? source;
77
+ if (isBuiltWorkflow(built)) {
78
+ addWorkflow(built);
79
+ continue;
80
+ }
81
+ addAgent(built);
82
+ }
83
+ if (!agents.size)
84
+ throw new Error(`${owner} requires at least one agent`);
85
+ // A definition passed in directly is saved on its own, even if another one embeds it.
86
+ for (const source of sources)
87
+ embedded.delete(source.id);
88
+ embeddedAgents.set(agents, embedded);
89
+ return agents;
90
+ }
91
+ /**
92
+ * The manifest hash served for a workflow's flow actions. A run is pinned to one workflow
93
+ * manifest, and its stage keys may shift between deploys, so flow actions for another hash
94
+ * are left for whoever serves it.
95
+ */
96
+ export function flowManifestHash(agent) {
97
+ return isBuiltWorkflow(agent) && agent.manifest.workflowSchemaVersion === 2
98
+ ? hashManifest(agent.manifest)
99
+ : undefined;
100
+ }