@nylorun/agents 0.6.0-beta → 0.8.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.
@@ -0,0 +1,60 @@
1
+ import { parseTranscriptEvent } from "@nylorun/core/contracts";
2
+ import { outputText, toolResultOf } from "./translate.js";
3
+ export function messagesFromEvents(items) {
4
+ const messages = [];
5
+ const results = new Set();
6
+ let sawAssistant = false;
7
+ for (const raw of items) {
8
+ if (raw.type === "command.message") {
9
+ const p = (raw.payload ?? {});
10
+ messages.push({
11
+ // The handler sends the client's message id as the idempotency key.
12
+ id: p.idempotencyKey ?? raw.eventId,
13
+ role: "user",
14
+ content: typeof p.content === "string" ? p.content : JSON.stringify(p.data),
15
+ });
16
+ sawAssistant = false;
17
+ continue;
18
+ }
19
+ const event = parseTranscriptEvent(raw);
20
+ if (!event)
21
+ continue;
22
+ const result = toolResultOf(event);
23
+ if (result) {
24
+ if (results.has(result.callId))
25
+ continue;
26
+ results.add(result.callId);
27
+ messages.push({
28
+ id: `${result.callId}:result`,
29
+ role: "tool",
30
+ toolCallId: result.callId,
31
+ content: result.content,
32
+ });
33
+ continue;
34
+ }
35
+ if (event.type === "message.assistant" && !event.payload.agent) {
36
+ const p = event.payload;
37
+ sawAssistant = true;
38
+ const message = { id: p.invocationId, role: "assistant" };
39
+ if (p.text)
40
+ message.content = p.text;
41
+ if (p.toolCalls.length)
42
+ message.toolCalls = p.toolCalls.map((call) => ({
43
+ id: call.callId,
44
+ type: "function",
45
+ function: { name: call.name, arguments: JSON.stringify(call.input ?? {}) },
46
+ }));
47
+ messages.push(message);
48
+ }
49
+ else if (event.type === "turn.completed" && !sawAssistant) {
50
+ const output = event.payload.output;
51
+ if (output !== undefined && output !== null && outputText(output))
52
+ messages.push({
53
+ id: raw.eventId,
54
+ role: "assistant",
55
+ content: outputText(output),
56
+ });
57
+ }
58
+ }
59
+ return messages;
60
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@nylorun/agents/ag-ui`: serve the Tenant's agents to AG-UI clients from the app's own
3
+ * server. The only entry point that loads `@ag-ui/core`.
4
+ */
5
+ export { createAgUiHandler } from "./handler.js";
6
+ export type { AgUiHandler, AgUiHandlerOptions, AgUiSessionOptions, } from "./handler.js";
7
+ export { toNodeListener } from "./node.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@nylorun/agents/ag-ui`: serve the Tenant's agents to AG-UI clients from the app's own
3
+ * server. The only entry point that loads `@ag-ui/core`.
4
+ */
5
+ export { createAgUiHandler } from "./handler.js";
6
+ export { toNodeListener } from "./node.js";
@@ -0,0 +1,5 @@
1
+ /** `node:http` and Express adapter for a fetch-style handler. */
2
+ import type { IncomingMessage, ServerResponse } from "node:http";
3
+ export declare function toNodeListener(handler: {
4
+ readonly fetch: (request: Request) => Promise<Response>;
5
+ }): (req: IncomingMessage, res: ServerResponse) => void;
@@ -0,0 +1,51 @@
1
+ import { Readable } from "node:stream";
2
+ export function toNodeListener(handler) {
3
+ return (req, res) => {
4
+ void (async () => {
5
+ // A closed connection aborts the request, which stops the read, never the turn.
6
+ const controller = new AbortController();
7
+ res.on("close", () => controller.abort());
8
+ const host = req.headers.host ?? "localhost";
9
+ const url = new URL(req.url ?? "/", `http://${host}`);
10
+ const headers = new Headers();
11
+ for (const [name, value] of Object.entries(req.headers)) {
12
+ if (value === undefined)
13
+ continue;
14
+ for (const item of Array.isArray(value) ? value : [value])
15
+ headers.append(name, item);
16
+ }
17
+ const hasBody = req.method !== "GET" && req.method !== "HEAD";
18
+ const request = new Request(url, {
19
+ method: req.method,
20
+ headers,
21
+ signal: controller.signal,
22
+ ...(hasBody
23
+ ? {
24
+ body: Readable.toWeb(req),
25
+ duplex: "half",
26
+ }
27
+ : {}),
28
+ });
29
+ let response;
30
+ try {
31
+ response = await handler.fetch(request);
32
+ }
33
+ catch {
34
+ response = new Response(JSON.stringify({ error: "Internal error" }), {
35
+ status: 500,
36
+ headers: { "content-type": "application/json" },
37
+ });
38
+ }
39
+ res.writeHead(response.status, Object.fromEntries(response.headers));
40
+ if (!response.body) {
41
+ res.end();
42
+ return;
43
+ }
44
+ res.flushHeaders();
45
+ const body = Readable.fromWeb(response.body);
46
+ body.on("error", () => res.destroy());
47
+ res.on("close", () => body.destroy());
48
+ body.pipe(res);
49
+ })();
50
+ };
51
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AG-UI over server-sent events: one `data:` line of JSON per event. The last AG-UI event
3
+ * made from a Runtime event carries that event's cursor as `id:`, so a client that resumes
4
+ * from `Last-Event-ID` never lands inside a group.
5
+ */
6
+ import type { BaseEvent } from "@ag-ui/core";
7
+ export declare const SSE_CONTENT_TYPE = "text/event-stream";
8
+ export declare function sseFrame(event: BaseEvent, id?: string): string;
9
+ export declare const SSE_HEARTBEAT = ": keep-alive\n\n";
@@ -0,0 +1,7 @@
1
+ export const SSE_CONTENT_TYPE = "text/event-stream";
2
+ export function sseFrame(event, id) {
3
+ // Cursors are opaque base64; a line break would end the field early.
4
+ const idLine = id && !/[\r\n]/.test(id) ? `id: ${id}\n` : "";
5
+ return `${idLine}data: ${JSON.stringify(event)}\n\n`;
6
+ }
7
+ export const SSE_HEARTBEAT = ": keep-alive\n\n";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Runtime transcript events → AG-UI 1.0 events for one run. Ids: an assistant message is its
3
+ * model call's `invocationId`, a tool call is the model's `callId`, a tool result is
4
+ * `<callId>:result`, so live runs and `messagesFromEvents` agree.
5
+ */
6
+ import { type BaseEvent } from "@ag-ui/core";
7
+ import { type LiveEvent, type TranscriptEvent } from "@nylorun/core/contracts";
8
+ /** The text a tool result shows; `undefined` while there is no result yet. */
9
+ export declare function toolResultText(result: unknown): string | undefined;
10
+ export declare function outputText(output: unknown): string;
11
+ /** One Runtime event's result for a run: AG-UI events, and whether the run ended. */
12
+ export interface Step {
13
+ readonly events: BaseEvent[];
14
+ readonly finished: boolean;
15
+ }
16
+ /** A tool result from any of the three places a call can end. */
17
+ export declare function toolResultOf(event: TranscriptEvent): {
18
+ callId: string;
19
+ content: string;
20
+ } | undefined;
21
+ export declare class RunTranslator {
22
+ private readonly threadId;
23
+ private readonly runId;
24
+ /** Tool invocation → the model's call id, for interrupts that name the invocation. */
25
+ private readonly callIds;
26
+ private readonly closed;
27
+ private sawAssistant;
28
+ /**
29
+ * `reattached`: the run continues from a cursor, so earlier assistant messages already
30
+ * reached the client and a workflow's output is not repeated as text.
31
+ */
32
+ constructor(threadId: string, runId: string, options?: {
33
+ reattached?: boolean;
34
+ });
35
+ translate(raw: LiveEvent): Step;
36
+ started(): BaseEvent;
37
+ private finished;
38
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Runtime transcript events → AG-UI 1.0 events for one run. Ids: an assistant message is its
3
+ * model call's `invocationId`, a tool call is the model's `callId`, a tool result is
4
+ * `<callId>:result`, so live runs and `messagesFromEvents` agree.
5
+ */
6
+ import { EventType } from "@ag-ui/core";
7
+ import { parseTranscriptEvent, } from "@nylorun/core/contracts";
8
+ /** The text a tool result shows; `undefined` while there is no result yet. */
9
+ export function toolResultText(result) {
10
+ const value = result;
11
+ switch (value?.kind) {
12
+ case "completed":
13
+ return outputText(value.output ?? null);
14
+ case "failed":
15
+ return JSON.stringify({ error: value.code, message: value.message });
16
+ case "denied":
17
+ return JSON.stringify({ denied: true, reason: value.reason });
18
+ default:
19
+ // interaction-required and deferred: the call is still open.
20
+ return undefined;
21
+ }
22
+ }
23
+ export function outputText(output) {
24
+ return typeof output === "string" ? output : JSON.stringify(output);
25
+ }
26
+ const none = { events: [], finished: false };
27
+ /** A tool result from any of the three places a call can end. */
28
+ export function toolResultOf(event) {
29
+ switch (event.type) {
30
+ case "action.completed": {
31
+ const p = event.payload;
32
+ if (p.kind !== "tool" || !p.callId || p.agent)
33
+ return undefined;
34
+ const content = toolResultText(p.result);
35
+ return content === undefined ? undefined : { callId: p.callId, content };
36
+ }
37
+ case "tool.completed": {
38
+ const p = event.payload;
39
+ if (p.agent)
40
+ return undefined;
41
+ return {
42
+ callId: p.callId,
43
+ content: p.error !== undefined
44
+ ? JSON.stringify({ error: p.error.code, message: p.error.message })
45
+ : outputText(p.output ?? null),
46
+ };
47
+ }
48
+ case "delegation.completed": {
49
+ // An agent used as a tool: its own work stays inside it; the parent sees the result.
50
+ const p = event.payload;
51
+ if (!p.callId)
52
+ return undefined;
53
+ const content = toolResultText(p.outcome);
54
+ return content === undefined ? undefined : { callId: p.callId, content };
55
+ }
56
+ default:
57
+ return undefined;
58
+ }
59
+ }
60
+ export class RunTranslator {
61
+ threadId;
62
+ runId;
63
+ /** Tool invocation → the model's call id, for interrupts that name the invocation. */
64
+ callIds = new Map();
65
+ closed = new Set();
66
+ sawAssistant;
67
+ /**
68
+ * `reattached`: the run continues from a cursor, so earlier assistant messages already
69
+ * reached the client and a workflow's output is not repeated as text.
70
+ */
71
+ constructor(threadId, runId, options = {}) {
72
+ this.threadId = threadId;
73
+ this.runId = runId;
74
+ this.sawAssistant = options.reattached ?? false;
75
+ }
76
+ translate(raw) {
77
+ const event = parseTranscriptEvent(raw);
78
+ if (!event)
79
+ return none;
80
+ if (event.type === "action.pending" || event.type === "action.completed") {
81
+ const { callId, invocationId } = event.payload;
82
+ if (callId && invocationId)
83
+ this.callIds.set(invocationId, callId);
84
+ }
85
+ const result = toolResultOf(event);
86
+ if (result) {
87
+ if (this.closed.has(result.callId))
88
+ return none;
89
+ this.closed.add(result.callId);
90
+ return {
91
+ events: [
92
+ {
93
+ type: EventType.TOOL_CALL_RESULT,
94
+ messageId: `${result.callId}:result`,
95
+ toolCallId: result.callId,
96
+ content: result.content,
97
+ role: "tool",
98
+ },
99
+ ],
100
+ finished: false,
101
+ };
102
+ }
103
+ switch (event.type) {
104
+ case "message.assistant": {
105
+ const p = event.payload;
106
+ if (p.agent)
107
+ return none;
108
+ this.sawAssistant = true;
109
+ return { events: assistantEvents(p), finished: false };
110
+ }
111
+ case "turn.completed": {
112
+ const p = event.payload;
113
+ // Workflows finish without a model step of their own: show their output.
114
+ const text = this.sawAssistant || p.output === undefined || p.output === null
115
+ ? []
116
+ : textEvents(raw.eventId, outputText(p.output));
117
+ return {
118
+ events: [...text, this.finished({ type: "success" }, p.output)],
119
+ finished: true,
120
+ };
121
+ }
122
+ case "turn.paused": {
123
+ const interrupts = event.payload.interactions.map((w) => {
124
+ const interaction = w.interaction;
125
+ const toolCallId = this.callIds.get(w.invocationId);
126
+ return {
127
+ id: interaction.id,
128
+ reason: interaction.kind === "approval" ? "tool_approval" : "input_required",
129
+ ...(typeof interaction.prompt === "string"
130
+ ? { message: interaction.prompt }
131
+ : {}),
132
+ ...(toolCallId ? { toolCallId } : {}),
133
+ ...(interaction.kind === "approval"
134
+ ? {
135
+ responseSchema: {
136
+ type: "object",
137
+ properties: { approved: { type: "boolean" } },
138
+ required: ["approved"],
139
+ },
140
+ }
141
+ : {}),
142
+ metadata: { nylorun: { kind: interaction.kind } },
143
+ };
144
+ });
145
+ return {
146
+ events: [this.finished({ type: "interrupt", interrupts })],
147
+ finished: true,
148
+ };
149
+ }
150
+ case "turn.cancelled":
151
+ return { events: [this.finished({ type: "cancelled" })], finished: true };
152
+ case "turn.failed": {
153
+ const p = event.payload;
154
+ return {
155
+ events: [
156
+ {
157
+ type: EventType.RUN_ERROR,
158
+ message: p.error?.message ?? p.message ?? "The agent failed",
159
+ code: p.error?.code ?? "turn.failed",
160
+ },
161
+ ],
162
+ finished: true,
163
+ };
164
+ }
165
+ case "effect.uncertain":
166
+ case "action.uncertain":
167
+ // The Runtime cannot tell whether a side effect happened; the run goes on.
168
+ return {
169
+ events: [
170
+ {
171
+ type: EventType.CUSTOM,
172
+ name: "nylorun.uncertain",
173
+ value: event.payload,
174
+ },
175
+ ],
176
+ finished: false,
177
+ };
178
+ default:
179
+ return none;
180
+ }
181
+ }
182
+ started() {
183
+ return {
184
+ type: EventType.RUN_STARTED,
185
+ threadId: this.threadId,
186
+ runId: this.runId,
187
+ };
188
+ }
189
+ finished(outcome, result) {
190
+ return {
191
+ type: EventType.RUN_FINISHED,
192
+ threadId: this.threadId,
193
+ runId: this.runId,
194
+ outcome,
195
+ ...(result === undefined ? {} : { result }),
196
+ };
197
+ }
198
+ }
199
+ function textEvents(messageId, text) {
200
+ if (!text)
201
+ return [];
202
+ return [
203
+ { type: EventType.TEXT_MESSAGE_START, messageId, role: "assistant" },
204
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId, delta: text },
205
+ { type: EventType.TEXT_MESSAGE_END, messageId },
206
+ ];
207
+ }
208
+ function assistantEvents(p) {
209
+ const events = textEvents(p.invocationId, p.text);
210
+ for (const call of p.toolCalls)
211
+ events.push({
212
+ type: EventType.TOOL_CALL_START,
213
+ toolCallId: call.callId,
214
+ toolCallName: call.name,
215
+ parentMessageId: p.invocationId,
216
+ }, {
217
+ type: EventType.TOOL_CALL_ARGS,
218
+ toolCallId: call.callId,
219
+ delta: JSON.stringify(call.input ?? {}),
220
+ }, { type: EventType.TOOL_CALL_END, toolCallId: call.callId });
221
+ return events;
222
+ }
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AgentManifest, BuiltAgent, BuiltWorkflow, JsonValue, SandboxToolName, WorkflowManifest } from "@nylorun/core/define";
2
- import { type CredentialInfo, type CredentialSelection, type SessionCommand, type VaultInfo } from "@nylorun/core/contracts";
2
+ import { type SubjectScope, type CredentialInfo, type CredentialSelection, type SessionCommand, type VaultInfo } from "@nylorun/core/contracts";
3
3
  import { Transport, type Destination } from "./http.js";
4
4
  /** Application-principal access to a session's sandbox built-ins (workflows.md §8). */
5
5
  export type SessionSandbox = {
@@ -22,9 +22,26 @@ export interface SessionView {
22
22
  waits?: unknown;
23
23
  [key: string]: unknown;
24
24
  }
25
+ export interface ActAsOptions {
26
+ /** What the subject may do; the Runtime grants nothing else. Default `["sessions:own"]`. */
27
+ scopes?: readonly SubjectScope[];
28
+ }
25
29
  export declare class AgentsClient {
26
30
  readonly transport: Transport;
31
+ /** The subject this client acts for (`as`), or `undefined` for the principal itself. */
32
+ readonly subject: string | undefined;
27
33
  constructor(destination?: Destination);
34
+ /**
35
+ * A client that acts for `subject`: every call sends `Nylorun-Subject` and
36
+ * `Nylorun-Scopes`, and the Runtime limits it to the scopes and to the subject's own sessions
37
+ * and vaults (Host feature `subject-headers`). For app servers, which authenticate the person
38
+ * themselves and hold the key; nothing is minted, cached or refreshed.
39
+ */
40
+ as(subject: string, options?: ActAsOptions): AgentsClient;
41
+ /** The Runtime's protocol features, including optional ones such as `transcript-events`. */
42
+ hostFeatures(options?: {
43
+ signal?: AbortSignal;
44
+ }): Promise<readonly string[]>;
28
45
  listAgents(options?: {
29
46
  signal?: AbortSignal;
30
47
  }): Promise<{
package/dist/client.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { SANDBOX_TOOL_NAMES, delegateOf, isBuiltWorkflow, isSandboxToolName, } from "@nylorun/core/define";
2
- import { LiveEventSchema, SessionItemsResponseSchema, } from "@nylorun/core/contracts";
2
+ import { SCOPES_HEADER, SUBJECT_HEADER } from "@nylorun/core/compatibility";
3
+ import { LiveEventSchema, SessionItemsResponseSchema, parseSubjectHeaders, } from "@nylorun/core/contracts";
3
4
  import { resolveConnection } from "./connection.js";
4
5
  import { Transport, id, segment } from "./http.js";
5
6
  import { observeSSE } from "./sse.js";
@@ -57,8 +58,37 @@ export function assertNoMiddlewareClosures(agent) {
57
58
  }
58
59
  export class AgentsClient {
59
60
  transport;
61
+ /** The subject this client acts for (`as`), or `undefined` for the principal itself. */
62
+ subject;
60
63
  constructor(destination = {}) {
61
64
  this.transport = new Transport(destination);
65
+ this.subject = undefined;
66
+ }
67
+ /**
68
+ * A client that acts for `subject`: every call sends `Nylorun-Subject` and
69
+ * `Nylorun-Scopes`, and the Runtime limits it to the scopes and to the subject's own sessions
70
+ * and vaults (Host feature `subject-headers`). For app servers, which authenticate the person
71
+ * themselves and hold the key; nothing is minted, cached or refreshed.
72
+ */
73
+ as(subject, options = {}) {
74
+ if (this.subject !== undefined)
75
+ throw new Error(`This client already acts for ${this.subject}`);
76
+ const scopes = [...(options.scopes ?? ["sessions:own"])].join(" ");
77
+ const parsed = parseSubjectHeaders(subject, scopes);
78
+ if (!parsed.ok)
79
+ throw new TypeError(parsed.message);
80
+ const client = Object.create(AgentsClient.prototype);
81
+ return Object.assign(client, {
82
+ transport: this.transport.withHeaders({
83
+ [SUBJECT_HEADER]: subject,
84
+ [SCOPES_HEADER]: [...parsed.scopes].join(" "),
85
+ }),
86
+ subject,
87
+ });
88
+ }
89
+ /** The Runtime's protocol features, including optional ones such as `transcript-events`. */
90
+ hostFeatures(options = {}) {
91
+ return this.transport.hostFeatures(options.signal);
62
92
  }
63
93
  listAgents(options = {}) {
64
94
  return this.transport.json("/v1/agents", "GET", undefined, options.signal);
@@ -10,7 +10,9 @@ export class ConnectionError extends Error {
10
10
  }
11
11
  }
12
12
  function missing(tried) {
13
- throw new ConnectionError(`connection_missing: no Runtime connection found (tried ${tried.join(", ")})`);
13
+ throw new ConnectionError(`connection_missing: no Runtime connection found (tried ${tried.join(", ")}). ` +
14
+ `Start the local stack with "npx nylorun up" and link this project with "npx @nylorun/cli tenant create", ` +
15
+ `or set NYLORUN_RUNTIME_URL, NYLORUN_TENANT and NYLORUN_SERVER_KEY.`);
14
16
  }
15
17
  function stripTrailingSlash(url) {
16
18
  return url.replace(/\/$/, "");
package/dist/http.d.ts CHANGED
@@ -26,11 +26,20 @@ export declare class Transport {
26
26
  readonly key: string;
27
27
  readonly tenant: string;
28
28
  readonly fetcher: typeof fetch;
29
- private compatible;
29
+ /** Sent on every request, e.g. `Nylorun-Subject` and `Nylorun-Scopes` (`withHeaders`). */
30
+ readonly headers: Readonly<Record<string, string>>;
31
+ private readonly check;
30
32
  constructor(options?: Destination, role?: "server" | "executor");
33
+ /**
34
+ * The same destination with `headers` added to every request. The copy shares this
35
+ * transport's compatibility check, so a copy made per request does not repeat `/health`.
36
+ */
37
+ withHeaders(headers: Readonly<Record<string, string>>): Transport;
31
38
  /** Clears the cached Host compatibility result (used after a 426). */
32
39
  clearCompatibilityCache(): void;
33
40
  private ensureCompatible;
41
+ /** The Host's protocol features, including optional ones, from its `/health`. */
42
+ hostFeatures(signal?: AbortSignal): Promise<readonly string[]>;
34
43
  private authHeaders;
35
44
  request(path: string, init?: RequestInit, options?: {
36
45
  retried426?: boolean;
package/dist/http.js CHANGED
@@ -57,7 +57,9 @@ export class Transport {
57
57
  key;
58
58
  tenant;
59
59
  fetcher;
60
- compatible = false;
60
+ /** Sent on every request, e.g. `Nylorun-Subject` and `Nylorun-Scopes` (`withHeaders`). */
61
+ headers = {};
62
+ check = { compatible: false, features: [] };
61
63
  constructor(options = {}, role = "server") {
62
64
  const url = options.url ?? env("NYLORUN_RUNTIME_URL");
63
65
  const key = options.key ??
@@ -75,12 +77,22 @@ export class Transport {
75
77
  this.tenant = tenant;
76
78
  this.fetcher = options.fetch ?? globalThis.fetch;
77
79
  }
80
+ /**
81
+ * The same destination with `headers` added to every request. The copy shares this
82
+ * transport's compatibility check, so a copy made per request does not repeat `/health`.
83
+ */
84
+ withHeaders(headers) {
85
+ const copy = Object.create(Transport.prototype);
86
+ return Object.assign(copy, this, {
87
+ headers: Object.freeze({ ...this.headers, ...headers }),
88
+ });
89
+ }
78
90
  /** Clears the cached Host compatibility result (used after a 426). */
79
91
  clearCompatibilityCache() {
80
- this.compatible = false;
92
+ this.check.compatible = false;
81
93
  }
82
94
  async ensureCompatible(signal) {
83
- if (this.compatible)
95
+ if (this.check.compatible)
84
96
  return;
85
97
  const response = await this.fetcher(`${this.url}/health`, {
86
98
  method: "GET",
@@ -110,10 +122,18 @@ export class Transport {
110
122
  const result = checkCompatibility({ version: PROTOCOL_VERSION, required: [...PROTOCOL_FEATURES] }, protocol);
111
123
  if (!result.ok)
112
124
  throw new IncompatibleRuntimeError(result);
113
- this.compatible = true;
125
+ this.check.features = [...protocol.features];
126
+ this.check.compatible = true;
127
+ }
128
+ /** The Host's protocol features, including optional ones, from its `/health`. */
129
+ async hostFeatures(signal) {
130
+ await this.ensureCompatible(signal);
131
+ return this.check.features;
114
132
  }
115
133
  authHeaders(init = {}) {
116
134
  const headers = new Headers(init.headers);
135
+ for (const [name, value] of Object.entries(this.headers))
136
+ headers.set(name, value);
117
137
  headers.set("Authorization", `Bearer ${this.key}`);
118
138
  headers.set(TENANT_HEADER, this.tenant);
119
139
  headers.set(PROTOCOL_HEADER, String(PROTOCOL_VERSION));
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, TENANT_HEADER, PROTOCOL_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
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
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";
4
4
  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, } from "@nylorun/core/contracts";
5
+ export type { LiveEvent, SessionCommand, Action, ActionOutcome, CredentialInfo, CredentialSelection, VaultInfo, SubjectScope, } from "@nylorun/core/contracts";
6
+ export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
6
7
  export { AgentsClient, SessionClient, createClient } from "./client.js";
7
- export type { AgentSource, SessionView, CommandOptions, SessionSandbox, } from "./client.js";
8
+ export type { ActAsOptions, AgentSource, SessionView, CommandOptions, SessionSandbox, } from "./client.js";
8
9
  export { connectAgents } from "./executor.js";
9
10
  export type { ConnectOptions, AgentConnection } from "./executor.js";
10
11
  export type { ExecuteActionOptions, ExecutableDefinition, } from "./execute-action.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
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, TENANT_HEADER, PROTOCOL_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
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
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";
4
+ export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
4
5
  export { AgentsClient, SessionClient, createClient } from "./client.js";
5
6
  export { connectAgents } from "./executor.js";
6
7
  export { resolveConnection, ConnectionError } from "./connection.js";
@@ -17,8 +17,7 @@ export declare class SandboxError extends Error {
17
17
  * Give the agent an isolated Linux machine with a persistent `/workspace`.
18
18
  *
19
19
  * Adds the built-in `bash`, `read`, `write`, `edit`, `grep` and `glob` tools. They run in the
20
- * Nylorun Runtime, which picks the backend (a microsandbox VM where available). Agent code
21
- * declares requirements only.
20
+ * Nylorun Runtime, which picks the backend. Agent code declares requirements only.
22
21
  *
23
22
  * ```ts
24
23
  * Agent({ id: "analyst", instructions: "..." }).use(sandbox())
@@ -11,8 +11,7 @@ export class SandboxError extends Error {
11
11
  * Give the agent an isolated Linux machine with a persistent `/workspace`.
12
12
  *
13
13
  * Adds the built-in `bash`, `read`, `write`, `edit`, `grep` and `glob` tools. They run in the
14
- * Nylorun Runtime, which picks the backend (a microsandbox VM where available). Agent code
15
- * declares requirements only.
14
+ * Nylorun Runtime, which picks the backend. Agent code declares requirements only.
16
15
  *
17
16
  * ```ts
18
17
  * Agent({ id: "analyst", instructions: "..." }).use(sandbox())