@nylorun/runtime 0.47.1-beta → 0.48.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.48.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - 6b49200: A2A `SendMessage` takes `message.metadata.delivery: "steer"` (Host feature `a2a-steer`, ADR-140), a session message's steer (D85): to a busy context the message waits marked `steer`, and the running turn takes it before its next step, so its task becomes that turn's. To an idle context it starts a turn; to a workflow's, or a turn past its last step, it is an ordinary queued message. The task answered to a steer carries `metadata.delivery`: `steered`, or `queued` when the message started a turn or waits unmarked; callers that don't ask for steer get the same answer as before. `createA2aClient`'s `send` takes `delivery: "steer"`, and `readDelivery(task)` reads what applied.
8
+
9
+ ### Patch Changes
10
+
11
+ - Pin core to the tested release.
12
+ - Pin harness to the tested release.
13
+ - Updated dependencies [6b49200]
14
+ - Updated dependencies
15
+ - @nylorun/core@0.42.0-beta
16
+ - @nylorun/harness@0.36.2-beta
17
+
3
18
  ## 0.47.1-beta
4
19
 
5
20
  ### Patch Changes
@@ -72,7 +72,7 @@ export function a2aRoutes(api) {
72
72
  path: "/v1/a2a/agents/{agentId}",
73
73
  tags: TAGS,
74
74
  summary: "Call an agent over A2A",
75
- description: "A2A 1.0 JSON-RPC, for a subject, under the Nylorun profile v2 (Host feature `a2a-profile-v2`). `SendMessage` blocks up to the Runtime limit `a2aWaitMs` (5 minutes by default; then the task is answered still `WORKING`) unless `returnImmediately`; `GetTask` (with `historyLength`), `CancelTask` and `ListTasks`; and the four push notification config methods, plus `configuration.taskPushNotificationConfig` in `SendMessage`: each update is POSTed as one `StreamResponse`, signed with Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature` with the Runtime's `a2a.push.secret`) and `X-A2A-Notification-Token`, at least once, retried for up to 24 hours, to hosts the Runtime allows (`a2a.push.allowHosts` for private ones). A message to a busy context is queued and answered with the task that will take it (`TASK_STATE_SUBMITTED`); `message.metadata.delivery: \"reject\"` asks for `UnsupportedOperationError` (reason `CONTEXT_BUSY`) instead. A queued task never waits for good (Host feature `queue-recovery`): when its Agent refuses its turn's start (a disk copy, a lost pod), the start is tried again until the Runtime limit `queuedStartMs` (15 minutes by default), and then, or at once for a refusal no retry changes, the task is `TASK_STATE_FAILED` with a status message saying why; `CancelTask` on a queued task takes it out of the queue, a task running in the context or not, and answers `TASK_STATE_CANCELED`. Both are pushed. `message.metadata.removeTools` (Host feature `remove-tools`) names the tools a new message's turn may not use, as a session message's `removeTools`. A message is text, one data part, or text with `raw` files (stored as artifacts of the context's session) and `url` files (fetched by the Runtime), each at most the Runtime limit `inlineFileBytes` (20 MiB by default). An approval, question or connection the task waits for (`AUTH_REQUIRED` or `INPUT_REQUIRED`) is shown under `interactions/v1` and answered on the same task with `{ interactionId, approved }` or `{ interactionId, value }` under that URI in the message's `metadata`; text never approves. Artifacts carry the turn's output and the files it wrote, as `url` parts at `/v1/a2a/files/…` with `files/v1`; the task's metadata carries `progress/v1`, `telemetry/v1` and `effects/v1`. Extension data is answered only for the extensions activated with `A2A-Extensions`. A context is one session per subject, agent and `contextId`; a task is one turn. JSON-RPC errors are answered with 200; limits, scopes and unavailable streams stay HTTP errors. Not served: streaming, `GetExtendedAgentCard`.",
75
+ description: "A2A 1.0 JSON-RPC, for a subject, under the Nylorun profile v2 (Host feature `a2a-profile-v2`). `SendMessage` blocks up to the Runtime limit `a2aWaitMs` (5 minutes by default; then the task is answered still `WORKING`) unless `returnImmediately`; `GetTask` (with `historyLength`), `CancelTask` and `ListTasks`; and the four push notification config methods, plus `configuration.taskPushNotificationConfig` in `SendMessage`: each update is POSTed as one `StreamResponse`, signed with Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature` with the Runtime's `a2a.push.secret`) and `X-A2A-Notification-Token`, at least once, retried for up to 24 hours, to hosts the Runtime allows (`a2a.push.allowHosts` for private ones). A message to a busy context is queued and answered with the task that will take it (`TASK_STATE_SUBMITTED`); `message.metadata.delivery: \"reject\"` asks for `UnsupportedOperationError` (reason `CONTEXT_BUSY`) instead, and `\"steer\"` (Host feature `a2a-steer`) steers it into the running turn, as a session message's `delivery: \"steer\"`: the turn takes it before its next step and the task is that turn's; to an idle context, a workflow's, or a turn past its last step it is an ordinary message. The task answered to a steer carries `metadata.delivery`: `steered`, or `queued` when it started a turn or waits as a queued message. A queued task never waits for good (Host feature `queue-recovery`): when its Agent refuses its turn's start (a disk copy, a lost pod), the start is tried again until the Runtime limit `queuedStartMs` (15 minutes by default), and then, or at once for a refusal no retry changes, the task is `TASK_STATE_FAILED` with a status message saying why; `CancelTask` on a queued task takes it out of the queue, a task running in the context or not, and answers `TASK_STATE_CANCELED`. Both are pushed. `message.metadata.removeTools` (Host feature `remove-tools`) names the tools a new message's turn may not use, as a session message's `removeTools`. A message is text, one data part, or text with `raw` files (stored as artifacts of the context's session) and `url` files (fetched by the Runtime), each at most the Runtime limit `inlineFileBytes` (20 MiB by default). An approval, question or connection the task waits for (`AUTH_REQUIRED` or `INPUT_REQUIRED`) is shown under `interactions/v1` and answered on the same task with `{ interactionId, approved }` or `{ interactionId, value }` under that URI in the message's `metadata`; text never approves. Artifacts carry the turn's output and the files it wrote, as `url` parts at `/v1/a2a/files/…` with `files/v1`; the task's metadata carries `progress/v1`, `telemetry/v1` and `effects/v1`. Extension data is answered only for the extensions activated with `A2A-Extensions`. A context is one session per subject, agent and `contextId`; a task is one turn. JSON-RPC errors are answered with 200; limits, scopes and unavailable streams stay HTTP errors. Not served: streaming, `GetExtendedAgentCard`.",
76
76
  externalDocs: A2A_SPECIFICATION,
77
77
  request: {
78
78
  params: agentId,
@@ -205,8 +205,12 @@ export type MessageInput = {
205
205
  text: string;
206
206
  } | RawFile | UrlFile)[];
207
207
  };
208
- /** What a message to a busy context does (profile v2 §1): queued, or refused with `CONTEXT_BUSY`. */
209
- export type Delivery = "queue" | "reject";
208
+ /**
209
+ * What a message to a busy context does (profile v2 §1): queued, refused with `CONTEXT_BUSY`, or
210
+ * steered into the running turn before its next step (Host feature `a2a-steer`, ADR-140), as a
211
+ * session message's `delivery: "steer"` (D85).
212
+ */
213
+ export type Delivery = "queue" | "reject" | "steer";
210
214
  /** A caller's answer to an open interaction (`interactions/v1` on the reply message). */
211
215
  export type InteractionAnswer = {
212
216
  interactionId: string;
@@ -234,13 +234,13 @@ function parseRemoveTools(metadata) {
234
234
  return a2aFail("invalidParams", "message.metadata.removeTools must be at most 256 tool names of 1 to 256 characters", {}, "message.metadata.removeTools");
235
235
  return value;
236
236
  }
237
- /** `metadata.delivery`: `queue` (the default) or `reject`. */
237
+ /** `metadata.delivery`: `queue` (the default), `reject` or `steer`. */
238
238
  function parseDelivery(metadata) {
239
239
  const delivery = metadata.delivery;
240
240
  if (delivery === undefined || delivery === null)
241
241
  return "queue";
242
- if (delivery !== "queue" && delivery !== "reject")
243
- return a2aFail("invalidParams", 'message.metadata.delivery must be "queue" or "reject"', {}, "message.metadata.delivery");
242
+ if (delivery !== "queue" && delivery !== "reject" && delivery !== "steer")
243
+ return a2aFail("invalidParams", 'message.metadata.delivery must be "queue", "reject" or "steer"', {}, "message.metadata.delivery");
244
244
  return delivery;
245
245
  }
246
246
  /** The answer under `interactions/v1`, when the message carries one. */
@@ -18,10 +18,13 @@
18
18
  * - A message to a busy context is **queued** (`delivery: "queue"`, protocol 17) and answered
19
19
  * with the task that will take it: `SUBMITTED`, its id the queued message's own until a turn
20
20
  * takes it, then that turn's state. `metadata.delivery: "reject"` keeps v1's
21
- * `UnsupportedOperationError` (reason `CONTEXT_BUSY`). A queued task never waits for good (Host
22
- * feature `queue-recovery`): one whose turn the Agent refused to start for longer than the
23
- * Runtime limit `queuedStartMs`, or for good, is `FAILED`; `CancelTask` takes one out of the
24
- * queue as `CANCELED`. Both are pushed.
21
+ * `UnsupportedOperationError` (reason `CONTEXT_BUSY`). `metadata.delivery: "steer"` (Host
22
+ * feature `a2a-steer`, ADR-140) is a session message's steer (D85): the running turn takes the
23
+ * message before its next step, and its task is then that turn's; the task's `metadata.delivery`
24
+ * answers `steered`, or `queued` when it started a turn or waits as a queued message. A queued
25
+ * task never waits for good (Host feature `queue-recovery`): one whose turn the Agent refused to
26
+ * start for longer than the Runtime limit `queuedStartMs`, or for good, is `FAILED`;
27
+ * `CancelTask` takes one out of the queue as `CANCELED`. Both are pushed.
25
28
  * - An open interaction shows on the status message under `interactions/v1`; the caller answers
26
29
  * on the same task with `{ interactionId, approved }` (the Runtime's `approve`, recorded with
27
30
  * the caller as `approvedBy`) or `{ interactionId, value }` (`respond`). Text never approves.
@@ -267,12 +270,19 @@ async function ownedTask(caller, taskId, historyLength) {
267
270
  async function run(caller, sessionId, input,
268
271
  /** The error for a `409` conflict: the session is busy or no longer waiting. */
269
272
  conflict,
270
- /** What a message to a busy session does: queued (profile v2), or `409`. */
273
+ /**
274
+ * What a message to a busy session does: queued (profile v2), steered into its running turn
275
+ * (`a2a-steer`, D85; queued when no turn may take it), or `409`.
276
+ */
271
277
  delivery = "reject") {
272
278
  try {
273
279
  const accepted = (await command(caller.ctx, sessionId, SessionCommandSchema.parse(input), caller.scope, { ...(caller.trace ? { trace: caller.trace } : {}), delivery }));
274
280
  return accepted.status === "queued"
275
- ? { queuedMessageId: accepted.queuedMessageId, cursor: accepted.cursor }
281
+ ? {
282
+ queuedMessageId: accepted.queuedMessageId,
283
+ cursor: accepted.cursor,
284
+ ...(accepted.delivery ? { delivery: accepted.delivery } : {}),
285
+ }
276
286
  : { turnId: accepted.turnId, cursor: accepted.cursor };
277
287
  }
278
288
  catch (error) {
@@ -371,6 +381,17 @@ async function sendMessage(caller, params, signal) {
371
381
  else
372
382
  await settled(caller.ctx, sessionId, accepted, signal);
373
383
  const { task } = await ownedTask(caller, taskId, send.historyLength);
384
+ // A new message that asked to steer (`a2a-steer`, ADR-140) is told what applied: `steered`
385
+ // when it waited marked `steer` behind a running turn, which takes it before its next step
386
+ // (D85); `queued` when it started a turn, or waits as an ordinary queued message (a workflow).
387
+ // A steer the turn doesn't take after all (past its last step) starts the next turn.
388
+ if (send.delivery === "steer" && send.taskId === undefined)
389
+ return {
390
+ task: {
391
+ ...task,
392
+ metadata: { ...task.metadata, delivery: accepted.delivery === "steer" ? "steered" : "queued" },
393
+ },
394
+ };
374
395
  return { task };
375
396
  }
376
397
  /** The trace id of a turn this request started: its `traceparent`'s, else the Runtime's own. */
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.2.0",
3
3
  "info": {
4
4
  "title": "Nylorun Management API",
5
- "version": "0.47.1-beta",
5
+ "version": "0.48.0-beta",
6
6
  "x-nylorun-protocol": {
7
7
  "min": 16,
8
8
  "max": 18,
@@ -45,7 +45,8 @@
45
45
  "remove-tools",
46
46
  "agent-triggers",
47
47
  "steer",
48
- "queue-recovery"
48
+ "queue-recovery",
49
+ "a2a-steer"
49
50
  ]
50
51
  },
51
52
  "description": "For operators: the Tenant's models, vaults, signing keys, settings and application keys, at `/v1/tenant/*` on the Tenant's URL, through `@nylorun/admin`. Every operation takes a management key, acting as itself: never for a subject and never from a browser. Management keys are issued only on the Tenant's machine (`nylorun tenant keys put <id> --management`) or from `NYLORUN_MANAGEMENT_KEY_FILE`."
package/dist/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.2.0",
3
3
  "info": {
4
4
  "title": "Nylorun Runtime API",
5
- "version": "0.47.1-beta",
5
+ "version": "0.48.0-beta",
6
6
  "x-nylorun-protocol": {
7
7
  "min": 16,
8
8
  "max": 18,
@@ -45,7 +45,8 @@
45
45
  "remove-tools",
46
46
  "agent-triggers",
47
47
  "steer",
48
- "queue-recovery"
48
+ "queue-recovery",
49
+ "a2a-steer"
49
50
  ]
50
51
  },
51
52
  "description": "For developers: agent manifests, Agents, sessions (with AG-UI and A2A) and artifacts, through `@nylorun/agents`. Every Tenant serves it on its one URL, beside the Management API (`/openapi/management.json`). Every request sends `Nylorun-Protocol`. Who may call each operation is its `security` and its `x-nylorun-credentials` and `x-nylorun-scopes` (the subject scopes that reach it) fields: an application key (servers) or a trusted issuer's token (browsers and apps)."
@@ -37353,7 +37354,7 @@
37353
37354
  "A2A"
37354
37355
  ],
37355
37356
  "summary": "Call an agent over A2A",
37356
- "description": "A2A 1.0 JSON-RPC, for a subject, under the Nylorun profile v2 (Host feature `a2a-profile-v2`). `SendMessage` blocks up to the Runtime limit `a2aWaitMs` (5 minutes by default; then the task is answered still `WORKING`) unless `returnImmediately`; `GetTask` (with `historyLength`), `CancelTask` and `ListTasks`; and the four push notification config methods, plus `configuration.taskPushNotificationConfig` in `SendMessage`: each update is POSTed as one `StreamResponse`, signed with Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature` with the Runtime's `a2a.push.secret`) and `X-A2A-Notification-Token`, at least once, retried for up to 24 hours, to hosts the Runtime allows (`a2a.push.allowHosts` for private ones). A message to a busy context is queued and answered with the task that will take it (`TASK_STATE_SUBMITTED`); `message.metadata.delivery: \"reject\"` asks for `UnsupportedOperationError` (reason `CONTEXT_BUSY`) instead. A queued task never waits for good (Host feature `queue-recovery`): when its Agent refuses its turn's start (a disk copy, a lost pod), the start is tried again until the Runtime limit `queuedStartMs` (15 minutes by default), and then, or at once for a refusal no retry changes, the task is `TASK_STATE_FAILED` with a status message saying why; `CancelTask` on a queued task takes it out of the queue, a task running in the context or not, and answers `TASK_STATE_CANCELED`. Both are pushed. `message.metadata.removeTools` (Host feature `remove-tools`) names the tools a new message's turn may not use, as a session message's `removeTools`. A message is text, one data part, or text with `raw` files (stored as artifacts of the context's session) and `url` files (fetched by the Runtime), each at most the Runtime limit `inlineFileBytes` (20 MiB by default). An approval, question or connection the task waits for (`AUTH_REQUIRED` or `INPUT_REQUIRED`) is shown under `interactions/v1` and answered on the same task with `{ interactionId, approved }` or `{ interactionId, value }` under that URI in the message's `metadata`; text never approves. Artifacts carry the turn's output and the files it wrote, as `url` parts at `/v1/a2a/files/…` with `files/v1`; the task's metadata carries `progress/v1`, `telemetry/v1` and `effects/v1`. Extension data is answered only for the extensions activated with `A2A-Extensions`. A context is one session per subject, agent and `contextId`; a task is one turn. JSON-RPC errors are answered with 200; limits, scopes and unavailable streams stay HTTP errors. Not served: streaming, `GetExtendedAgentCard`.",
37357
+ "description": "A2A 1.0 JSON-RPC, for a subject, under the Nylorun profile v2 (Host feature `a2a-profile-v2`). `SendMessage` blocks up to the Runtime limit `a2aWaitMs` (5 minutes by default; then the task is answered still `WORKING`) unless `returnImmediately`; `GetTask` (with `historyLength`), `CancelTask` and `ListTasks`; and the four push notification config methods, plus `configuration.taskPushNotificationConfig` in `SendMessage`: each update is POSTed as one `StreamResponse`, signed with Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature` with the Runtime's `a2a.push.secret`) and `X-A2A-Notification-Token`, at least once, retried for up to 24 hours, to hosts the Runtime allows (`a2a.push.allowHosts` for private ones). A message to a busy context is queued and answered with the task that will take it (`TASK_STATE_SUBMITTED`); `message.metadata.delivery: \"reject\"` asks for `UnsupportedOperationError` (reason `CONTEXT_BUSY`) instead, and `\"steer\"` (Host feature `a2a-steer`) steers it into the running turn, as a session message's `delivery: \"steer\"`: the turn takes it before its next step and the task is that turn's; to an idle context, a workflow's, or a turn past its last step it is an ordinary message. The task answered to a steer carries `metadata.delivery`: `steered`, or `queued` when it started a turn or waits as a queued message. A queued task never waits for good (Host feature `queue-recovery`): when its Agent refuses its turn's start (a disk copy, a lost pod), the start is tried again until the Runtime limit `queuedStartMs` (15 minutes by default), and then, or at once for a refusal no retry changes, the task is `TASK_STATE_FAILED` with a status message saying why; `CancelTask` on a queued task takes it out of the queue, a task running in the context or not, and answers `TASK_STATE_CANCELED`. Both are pushed. `message.metadata.removeTools` (Host feature `remove-tools`) names the tools a new message's turn may not use, as a session message's `removeTools`. A message is text, one data part, or text with `raw` files (stored as artifacts of the context's session) and `url` files (fetched by the Runtime), each at most the Runtime limit `inlineFileBytes` (20 MiB by default). An approval, question or connection the task waits for (`AUTH_REQUIRED` or `INPUT_REQUIRED`) is shown under `interactions/v1` and answered on the same task with `{ interactionId, approved }` or `{ interactionId, value }` under that URI in the message's `metadata`; text never approves. Artifacts carry the turn's output and the files it wrote, as `url` parts at `/v1/a2a/files/…` with `files/v1`; the task's metadata carries `progress/v1`, `telemetry/v1` and `effects/v1`. Extension data is answered only for the extensions activated with `A2A-Extensions`. A context is one session per subject, agent and `contextId`; a task is one turn. JSON-RPC errors are answered with 200; limits, scopes and unavailable streams stay HTTP errors. Not served: streaming, `GetExtendedAgentCard`.",
37357
37358
  "externalDocs": {
37358
37359
  "description": "The A2A specification",
37359
37360
  "url": "https://a2a-protocol.org/latest/specification/"
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const RUNTIME_VERSION = "0.47.1-beta";
1
+ export declare const RUNTIME_VERSION = "0.48.0-beta";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Kept in step with package.json by runtime/server/scripts/check-package.mjs, which runs in npm run check.
2
2
  // release:prepare also rewrites this when it bumps @nylorun/runtime.
3
- export const RUNTIME_VERSION = "0.47.1-beta";
3
+ export const RUNTIME_VERSION = "0.48.0-beta";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nylorun/runtime",
3
- "version": "0.47.1-beta",
3
+ "version": "0.48.0-beta",
4
4
  "description": "Nylorun Runtime: the agent execution Host on Postgres, Restate and S2, and model providers.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -65,8 +65,8 @@
65
65
  "@hono/node-server": "^2.1.5",
66
66
  "@hono/zod-openapi": "^1.6.3",
67
67
  "@modelcontextprotocol/sdk": "^1.32.1",
68
- "@nylorun/core": "0.41.0-beta",
69
- "@nylorun/harness": "0.36.1-beta",
68
+ "@nylorun/core": "0.42.0-beta",
69
+ "@nylorun/harness": "0.36.2-beta",
70
70
  "@restatedev/restate-sdk": "1.17.3",
71
71
  "@restatedev/restate-sdk-clients": "1.17.3",
72
72
  "@s2-dev/streamstore": "0.28.0",