@nylorun/runtime 0.36.1-beta → 0.38.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 (71) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +10 -0
  3. package/dist/api/a2a/card.d.ts +10 -4
  4. package/dist/api/a2a/card.js +18 -6
  5. package/dist/api/a2a/endpoint.d.ts +5 -2
  6. package/dist/api/a2a/endpoint.js +31 -9
  7. package/dist/api/a2a/extensions.d.ts +41 -0
  8. package/dist/api/a2a/extensions.js +92 -0
  9. package/dist/api/a2a/list.d.ts +60 -0
  10. package/dist/api/a2a/list.js +169 -0
  11. package/dist/api/a2a/protocol.d.ts +105 -9
  12. package/dist/api/a2a/protocol.js +169 -18
  13. package/dist/api/a2a/push.d.ts +76 -0
  14. package/dist/api/a2a/push.js +305 -0
  15. package/dist/api/a2a/routes.d.ts +12 -6
  16. package/dist/api/a2a/routes.js +409 -112
  17. package/dist/api/a2a/tasks.d.ts +72 -4
  18. package/dist/api/a2a/tasks.js +305 -27
  19. package/dist/api/components.d.ts +538 -0
  20. package/dist/api/components.js +3 -1
  21. package/dist/api/http/app.js +2 -0
  22. package/dist/api/http/routes/artifacts.js +19 -0
  23. package/dist/api/http/routes/turns.d.ts +18 -0
  24. package/dist/api/http/routes/turns.js +67 -0
  25. package/dist/api/inbound-files.d.ts +12 -1
  26. package/dist/api/inbound-files.js +44 -0
  27. package/dist/api/openapi.js +7 -2
  28. package/dist/harness/image-engine.d.ts +14 -0
  29. package/dist/harness/image-engine.js +16 -0
  30. package/dist/harness/pod.d.ts +2 -0
  31. package/dist/harness/pod.js +5 -0
  32. package/dist/harness-api/ws-server.js +7 -1
  33. package/dist/host/config-for.d.ts +2 -0
  34. package/dist/host/config-for.js +1 -0
  35. package/dist/host/main.js +1 -0
  36. package/dist/host/stack-config.d.ts +16 -0
  37. package/dist/host/stack-config.js +30 -0
  38. package/dist/management-openapi.json +37 -2
  39. package/dist/openapi.json +1485 -738
  40. package/dist/reads/types.d.ts +19 -0
  41. package/dist/sandbox/join.d.ts +7 -1
  42. package/dist/sandbox/join.js +5 -1
  43. package/dist/sandbox/pods/builtin.d.ts +18 -0
  44. package/dist/sandbox/pods/builtin.js +33 -0
  45. package/dist/sandbox/pods/client.d.ts +5 -4
  46. package/dist/sandbox/pods/spec.js +14 -1
  47. package/dist/store/postgres/drizzle/0039_a2a_push.sql +32 -0
  48. package/dist/store/postgres/drizzle/0040_turn_index.sql +23 -0
  49. package/dist/store/postgres/drizzle/0041_turn_index_backfill.sql +88 -0
  50. package/dist/store/postgres/drizzle/0042_worker_engine_image.sql +1 -0
  51. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  52. package/dist/store/postgres/reads.js +41 -1
  53. package/dist/store/postgres/schema.d.ts +1314 -533
  54. package/dist/store/postgres/schema.js +94 -0
  55. package/dist/store/postgres/store.js +135 -3
  56. package/dist/store/postgres/turns.d.ts +62 -0
  57. package/dist/store/postgres/turns.js +153 -0
  58. package/dist/store/types.d.ts +82 -3
  59. package/dist/tenant/runtime.js +7 -5
  60. package/dist/tenant/session-model.d.ts +11 -1
  61. package/dist/tenant/session-model.js +34 -5
  62. package/dist/tenant/sessions.d.ts +2 -1
  63. package/dist/tenant/sessions.js +8 -2
  64. package/dist/tenant/sweep.d.ts +3 -0
  65. package/dist/tenant/sweep.js +39 -11
  66. package/dist/tenant/types.d.ts +23 -0
  67. package/dist/tuning.d.ts +9 -0
  68. package/dist/tuning.js +11 -0
  69. package/dist/version.d.ts +1 -1
  70. package/dist/version.js +1 -1
  71. package/package.json +3 -3
@@ -17,7 +17,8 @@ import { createSessionStreams, endAllStreams, } from "./session-streams.js";
17
17
  import { closeStreams, streamsStatus, wireStreams, } from "./streams.js";
18
18
  import { abortAll, abortLocal, createWorkState, drain, waitForIdle, } from "./scheduler.js";
19
19
  import { advance } from "./advance.js";
20
- import { sweep } from "./sweep.js";
20
+ import { outboxFields, sweep } from "./sweep.js";
21
+ import { deliverPush } from "../api/a2a/push.js";
21
22
  import { TenantWorkers, WORKER_ID, } from "./worker.js";
22
23
  import { authorize } from "./effects.js";
23
24
  import { inProcessToolGate, openMcpThrough } from "../gates/tool-gate.js";
@@ -276,7 +277,10 @@ export class TenantRuntime {
276
277
  },
277
278
  send: (request) => request.kind === "wake"
278
279
  ? ctx.wake(request.sessionId, request.wake)
279
- : ctx.sandboxSignal(request.sandboxId, request.signal),
280
+ : request.kind === "push"
281
+ ? // An A2A push goes to the caller's URL, not to the execution (`api/a2a/push.ts`).
282
+ deliverPush(ctx, request.push)
283
+ : ctx.sandboxSignal(request.sandboxId, request.signal),
280
284
  ...(hooks.execution?.stuckInvocations
281
285
  ? {
282
286
  stuckInvocations: () => hooks.execution.stuckInvocations(config.tenantId),
@@ -296,9 +300,7 @@ export class TenantRuntime {
296
300
  }
297
301
  catch (error) {
298
302
  config.logger.warn("outbox delivery after commit failed; the sweep sends it again", {
299
- ...(request.kind === "wake"
300
- ? { sessionId: request.sessionId, reason: request.wake.reason }
301
- : { sandboxId: request.sandboxId, signal: request.signal.kind }),
303
+ ...outboxFields(request),
302
304
  message: error instanceof Error ? error.message : String(error),
303
305
  });
304
306
  return false;
@@ -5,7 +5,8 @@
5
5
  * turn's start describes it (`turn.start.model`). The Tenant's model, and every other session's,
6
6
  * stay as they are. While its provider is disconnected the session uses the Tenant's model again.
7
7
  */
8
- import type { SessionModelChoice, SessionModelView } from "@nylorun/core/contracts";
8
+ import { type SessionModelChoice, type SessionModelView } from "@nylorun/core/contracts";
9
+ import type { Tx } from "../store/types.js";
9
10
  import { type SessionAccess, type TenantContext } from "./context.js";
10
11
  /** The model the session's next turn uses, and where it comes from. */
11
12
  export declare function readSessionModel(ctx: TenantContext, id: string, access: SessionAccess | undefined): Promise<SessionModelView>;
@@ -14,3 +15,12 @@ export declare function readSessionModel(ctx: TenantContext, id: string, access:
14
15
  * Tenant's. A turn calls one model: the choice changes only while no turn runs or waits.
15
16
  */
16
17
  export declare function chooseSessionModel(ctx: TenantContext, id: string, choice: SessionModelChoice | undefined, access: SessionAccess | undefined): Promise<SessionModelView>;
18
+ /**
19
+ * The model a new session starts with (Host feature `agent-model`): the one its version's
20
+ * `harness.model` names, as `chooseSessionModel` would take it, or undefined when it names none or
21
+ * one the Tenant cannot use now (a provider not connected, a model its catalog does not list), so
22
+ * the Tenant's model applies. Never fails the session.
23
+ */
24
+ export declare function agentModelOf(t: Tx, harness: {
25
+ readonly model?: unknown;
26
+ }): Promise<SessionModelChoice | undefined>;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * A session's own model (Host feature `session-model`): one of a connected provider's models,
3
+ * which the session's turns use in place of the Tenant's. It is kept on the session
4
+ * (`Session.model`): the Model Gate reads it at each call (`HostModelVault.readHostModel`) and a
5
+ * turn's start describes it (`turn.start.model`). The Tenant's model, and every other session's,
6
+ * stay as they are. While its provider is disconnected the session uses the Tenant's model again.
7
+ */
8
+ import { agentModelChoice } from "@nylorun/core/contracts";
1
9
  import { catalogModel, hostModelCatalog } from "../model/catalog.js";
2
10
  import { hostCredentialRowFor, modelBinding } from "../vault/service.js";
3
11
  import { takesMessages } from "./commands.js";
@@ -28,19 +36,40 @@ export async function chooseSessionModel(ctx, id, choice, access) {
28
36
  });
29
37
  return ctx.vault.getSessionModel(session.model);
30
38
  }
39
+ /**
40
+ * The model a new session starts with (Host feature `agent-model`): the one its version's
41
+ * `harness.model` names, as `chooseSessionModel` would take it, or undefined when it names none or
42
+ * one the Tenant cannot use now (a provider not connected, a model its catalog does not list), so
43
+ * the Tenant's model applies. Never fails the session.
44
+ */
45
+ export async function agentModelOf(t, harness) {
46
+ if (typeof harness.model !== "string")
47
+ return undefined;
48
+ const choice = agentModelChoice(harness.model);
49
+ return choice && (await choiceIssue(t, choice)) === undefined ? choice : undefined;
50
+ }
31
51
  /** A choice names a connected provider and, when its catalog lists models, one of them. */
32
52
  async function checkChoice(t, choice) {
53
+ const issue = await choiceIssue(t, choice);
54
+ if (issue)
55
+ fail(issue.status, issue.message, { code: issue.code });
56
+ }
57
+ /** Why `choice` is not one the Tenant can use now; undefined when it is. */
58
+ async function choiceIssue(t, choice) {
33
59
  if (!/^[a-zA-Z0-9._-]+$/.test(choice.provider))
34
- fail(400, "Invalid provider id", { code: "invalid_request" });
60
+ return { status: 400, message: "Invalid provider id", code: "invalid_request" };
35
61
  const row = await hostCredentialRowFor(t, choice.provider);
36
62
  if (!row)
37
- fail(422, `Model provider ${choice.provider} is not connected: connect it in Model Settings first`, {
63
+ return {
64
+ status: 422,
65
+ message: `Model provider ${choice.provider} is not connected: connect it in Model Settings first`,
38
66
  code: "request_rejected",
39
- });
67
+ };
40
68
  // A custom endpoint serves the models it names; the provider's own model is always one.
41
69
  if (choice.provider === "custom" || modelBinding(row).model === choice.model)
42
- return;
70
+ return undefined;
43
71
  const listed = hostModelCatalog().providers.find((provider) => provider.id === choice.provider);
44
72
  if (listed && listed.models.length > 0 && !catalogModel(choice.provider, choice.model))
45
- fail(422, `Unknown model ${choice.model} for provider ${choice.provider}`, { code: "request_rejected" });
73
+ return { status: 422, message: `Unknown model ${choice.model} for provider ${choice.provider}`, code: "request_rejected" };
74
+ return undefined;
46
75
  }
@@ -30,7 +30,8 @@ export declare function listSessions(ctx: TenantContext, agentId: string | null,
30
30
  * the session's, with the spec of what the Agent runs pinned now, and the Agent's vault holds the
31
31
  * session's credentials. It pins the manifest version the Agent runs (`definition`), as the loop
32
32
  * manifest that version's harness section makes, and the harness that section names (`pinOf`);
33
- * a turn's first segment pins the version the Agent runs then (`repinTurn`).
33
+ * a turn's first segment pins the version the Agent runs then (`repinTurn`). When that version
34
+ * names a model the Tenant can use (`harness.model`), the session starts with it as its own.
34
35
  */
35
36
  export declare function putSession(ctx: TenantContext, id: string, body: PutSessionRequest, access?: SessionAccess, options?: {
36
37
  /**
@@ -15,6 +15,7 @@ import { openAgent, recordAttachment } from "./agents.js";
15
15
  import { harnessOf, reachesAgent, } from "./context.js";
16
16
  import { fail } from "./http.js";
17
17
  import { definitionFileIssues } from "./definition-files.js";
18
+ import { agentModelOf } from "./session-model.js";
18
19
  import { limitsInForce, readTenantLimits } from "./limits.js";
19
20
  /** A session's creation identity: everything but the request id. */
20
21
  const sessionIdentity = (value) => {
@@ -51,7 +52,8 @@ export async function listSessions(ctx, agentId, access) {
51
52
  * the session's, with the spec of what the Agent runs pinned now, and the Agent's vault holds the
52
53
  * session's credentials. It pins the manifest version the Agent runs (`definition`), as the loop
53
54
  * manifest that version's harness section makes, and the harness that section names (`pinOf`);
54
- * a turn's first segment pins the version the Agent runs then (`repinTurn`).
55
+ * a turn's first segment pins the version the Agent runs then (`repinTurn`). When that version
56
+ * names a model the Tenant can use (`harness.model`), the session starts with it as its own.
55
57
  */
56
58
  export async function putSession(ctx, id, body, access, options = {}) {
57
59
  const opaque = access !== undefined;
@@ -87,13 +89,16 @@ export async function openSessionIn(ctx, t, id, body, access, options = {}) {
87
89
  opaque,
88
90
  });
89
91
  // The version the Agent runs, both sections (T3): its loop manifest, harness and sandbox spec.
90
- const { pin } = await pinOf(t, agent);
92
+ const { pin, version } = await pinOf(t, agent);
91
93
  await checkSkillLimit(t, pin.manifest, agent);
92
94
  const config = await readSandboxConfig(t);
93
95
  // Placement (D38, D52): where this session's harness may run, decided now and kept.
94
96
  checkPlacement(config, agent.kind, pin.harness);
95
97
  if (agent.kind === "pod")
96
98
  await requirePods(ctx.pods, `Agent ${agent.id}`);
99
+ // The model the version names (`harness.model`, Host feature `agent-model`) is the new
100
+ // session's own, as `chooseSessionModel` would make it, when the Tenant can use it now.
101
+ const model = await agentModelOf(t, version.body.harness);
97
102
  const created = {
98
103
  id,
99
104
  agentId: agent.id,
@@ -110,6 +115,7 @@ export async function openSessionIn(ctx, t, id, body, access, options = {}) {
110
115
  environmentId: agent.id,
111
116
  environmentVersion: agent.version,
112
117
  ...(pin.sandbox === undefined ? {} : { sandbox: pin.sandbox }),
118
+ ...(model === undefined ? {} : { model }),
113
119
  };
114
120
  await t.put("sessions", id, created);
115
121
  await recordAttachment(t, id, agent.id);
@@ -1,5 +1,6 @@
1
1
  import type { PendingRequest } from "../store/types.js";
2
2
  import type { TenantContext } from "./context.js";
3
+ import type { OutboxRequest } from "../store/types.js";
3
4
  export declare function sweep(ctx: TenantContext): Promise<void>;
4
5
  /**
5
6
  * Deliver the outbox rows that are due (`Tx.pendingOutbox`: older than `graceMs`, or whose
@@ -14,6 +15,8 @@ export declare function sweep(ctx: TenantContext): Promise<void>;
14
15
  * delivered.
15
16
  */
16
17
  export declare function deliverOutbox(ctx: Pick<TenantContext, "store" | "send" | "config">, graceMs?: number, budgetMs?: number): Promise<PendingRequest[]>;
18
+ /** What a log says of an outbox request: which session, sandbox or push, never a body. */
19
+ export declare function outboxFields(request: OutboxRequest): Record<string, unknown>;
17
20
  /** Settle pending workflow `agent` effects whose linked turn already finished. */
18
21
  export declare function reconcileLinkedAgents(ctx: Pick<TenantContext, "store">): Promise<void>;
19
22
  /**
@@ -38,7 +38,12 @@
38
38
  * timer is armed, later overdue ones only, and runs past `triggerRunRetentionMs` are deleted.
39
39
  * 5. **Control signals.** Signals on the control bus older than `sweep.signalRetentionMs` are
40
40
  * deleted: followers read back only the last two minutes (`store/postgres/control.ts`).
41
- * 6. **Hooks.** Callbacks registered with `ctx.onSweep`.
41
+ * 6. **A2A pushes** (`api/a2a/push.ts`): every session with a push config still following its
42
+ * task is pumped (its new events become `push` outbox rows, which step 1 of the next pass, or
43
+ * the delivery after commit, sends), and configs of tasks long finished are deleted. A push
44
+ * row that fails is retried with the Host tuning's `a2a.pushRetry` and dropped once it is
45
+ * `a2a.pushMaxAgeMs` old, never parked: the change feed covers what it said.
46
+ * 7. **Hooks.** Callbacks registered with `ctx.onSweep`.
42
47
  *
43
48
  * Every step runs one transaction per session it changes (a linked agent and its workflow
44
49
  * share one, child first), so the sweep follows the lock order in `store/types.ts`. A failing
@@ -51,6 +56,7 @@ import { sweepPendingSeeds } from "../artifacts/disk.js";
51
56
  import { pendingAgentEffects, reconcilePendingAgentEffect, } from "../core/flow-host.js";
52
57
  import { hostTuning } from "../tuning.js";
53
58
  import { sweepStartChecks } from "./agent-versions.js";
59
+ import { pumpPushes, pushRetry } from "../api/a2a/push.js";
54
60
  /*
55
61
  * The sweep's timings are the Host tuning's `sweep` group (`tuning.ts`):
56
62
  * - `outboxGraceMs`: how old an outbox row is before the sweep delivers it, unless its delivery
@@ -127,6 +133,8 @@ export async function sweep(ctx) {
127
133
  ["seeds", () => sweepPendingSeeds(ctx)],
128
134
  // A harness elsewhere closes its own idle MCP connections.
129
135
  ["mcp", async () => ctx.mcp?.sweep()],
136
+ // A2A pushes: new events of sessions with push configs become push outbox rows.
137
+ ["a2a-push", () => pumpPushes(ctx)],
130
138
  [
131
139
  "signals",
132
140
  () => ctx.store.tx((t) => t.pruneSignals(new Date(now.getTime() - hostTuning().sweep.signalRetentionMs))),
@@ -201,24 +209,44 @@ export async function deliverOutbox(ctx, graceMs = 0, budgetMs = hostTuning().sw
201
209
  return delivered;
202
210
  }
203
211
  }
204
- /** Records a failed delivery of `row`: retried with backoff, parked after the last attempt. */
212
+ /** What a log says of an outbox request: which session, sandbox or push, never a body. */
213
+ export function outboxFields(request) {
214
+ return request.kind === "wake"
215
+ ? { sessionId: request.sessionId, reason: request.wake.reason }
216
+ : request.kind === "push"
217
+ ? { sessionId: request.push.sessionId, taskId: request.push.taskId, pushId: request.push.id }
218
+ : { sandboxId: request.sandboxId, signal: request.signal.kind };
219
+ }
220
+ /**
221
+ * Records a failed delivery of `row`: retried with backoff, parked after the last attempt. An
222
+ * A2A push is retried on its own backoff until it is too old, then deleted (`pushRetry`).
223
+ */
205
224
  async function failed(ctx, row, error) {
206
225
  const attempts = row.attempts + 1;
226
+ const fields = {
227
+ rowId: row.id,
228
+ ...outboxFields(row.request),
229
+ attempts,
230
+ message: error instanceof Error ? error.message : String(error),
231
+ };
232
+ if (row.kind === "push") {
233
+ const retry = pushRetry(attempts, row.createdAt);
234
+ if (retry === "drop") {
235
+ await ctx.store.tx((t) => t.deleteOutbox([row]));
236
+ ctx.config.logger.warn("a2a push dropped after its last retry; the change feed covers it", fields);
237
+ }
238
+ else {
239
+ await ctx.store.tx((t) => t.failOutbox(row, { retryInMs: retry.retryInMs, park: false }));
240
+ ctx.config.logger.warn("a2a push failed; the sweep sends it again", fields);
241
+ }
242
+ return;
243
+ }
207
244
  // Parked after `sweep.outboxAttempts` failed deliveries (`Tx.failOutbox`); tried again after
208
245
  // `outboxRetry.minMs`, doubled each time.
209
246
  const { outboxAttempts, outboxRetry } = hostTuning().sweep;
210
247
  const park = attempts >= outboxAttempts;
211
248
  const retryInMs = Math.min(outboxRetry.minMs * 2 ** (attempts - 1), outboxRetry.maxMs);
212
249
  await ctx.store.tx((t) => t.failOutbox(row, { retryInMs, park }));
213
- const { request } = row;
214
- const fields = {
215
- rowId: row.id,
216
- ...(request.kind === "wake"
217
- ? { sessionId: request.sessionId, reason: request.wake.reason }
218
- : { sandboxId: request.sandboxId, signal: request.signal.kind }),
219
- attempts,
220
- message: error instanceof Error ? error.message : String(error),
221
- };
222
250
  if (park)
223
251
  ctx.config.logger.error("outbox row parked: its delivery keeps failing", fields);
224
252
  else
@@ -79,8 +79,31 @@ export interface TenantConfig {
79
79
  * of webhook URLs and the OAuth resource. Absent: the origin each request reached.
80
80
  */
81
81
  publicUrl?: string;
82
+ /** The A2A server's settings (Runtime config `a2a`, Host feature `a2a-profile-v2`). */
83
+ a2a?: A2aConfig;
82
84
  logger: Logger;
83
85
  }
86
+ /**
87
+ * Runtime config `a2a` (`NYLORUN_A2A_*`): how the A2A server pushes task updates (profile v2).
88
+ * Without `push.secret` the Runtime serves no push notifications: the card says
89
+ * `pushNotifications: false` and the push methods answer `PushNotificationNotSupportedError`.
90
+ */
91
+ export interface A2aConfig {
92
+ push?: {
93
+ /**
94
+ * `a2a.push.secret` (`NYLORUN_A2A_PUSH_SECRET`): the installation's Standard Webhooks secret,
95
+ * `whsec_<base64>`, that signs every push (`webhook-signature`). Receivers verify with it.
96
+ */
97
+ secret?: string;
98
+ /**
99
+ * `a2a.push.allowHosts` (`NYLORUN_A2A_PUSH_ALLOW_HOSTS`, comma-separated): the `host:port`s
100
+ * (`host:*` for any port) a push, or a file `url` part the Runtime fetches, may reach although they are private,
101
+ * loopback or link-local (a receiver on the Docker bridge). Every other URL of those is
102
+ * refused, whatever `delivery.privateAddresses` says.
103
+ */
104
+ allowHosts?: readonly string[];
105
+ };
106
+ }
84
107
  export interface Logger {
85
108
  info(message: string, fields?: Record<string, unknown>): void;
86
109
  warn(message: string, fields?: Record<string, unknown>): void;
package/dist/tuning.d.ts CHANGED
@@ -122,6 +122,15 @@ export declare const HostTuningSchema: z.ZodObject<{
122
122
  }, z.core.$strict>>;
123
123
  stalePodMs: z.ZodDefault<z.ZodNumber>;
124
124
  }, z.core.$strict>>;
125
+ a2a: z.ZodPrefault<z.ZodObject<{
126
+ pushRetry: z.ZodPrefault<z.ZodObject<{
127
+ minMs: z.ZodDefault<z.ZodNumber>;
128
+ maxMs: z.ZodDefault<z.ZodNumber>;
129
+ }, z.core.$strict>>;
130
+ pushMaxAgeMs: z.ZodDefault<z.ZodNumber>;
131
+ pushTimeoutMs: z.ZodDefault<z.ZodNumber>;
132
+ pushConfigRetentionMs: z.ZodDefault<z.ZodNumber>;
133
+ }, z.core.$strict>>;
125
134
  triggers: z.ZodPrefault<z.ZodObject<{
126
135
  lateGraceMs: z.ZodDefault<z.ZodNumber>;
127
136
  intervalCheckFires: z.ZodDefault<z.ZodNumber>;
package/dist/tuning.js CHANGED
@@ -208,6 +208,17 @@ export const HostTuningSchema = z
208
208
  /** How long a pod row may go unchanged before the sweep checks it. */
209
209
  stalePodMs: ms(MINUTE),
210
210
  }),
211
+ /** The A2A server's push notifications (Host feature `a2a-profile-v2`). */
212
+ a2a: group({
213
+ /** The wait after a push's failed delivery, doubled after each. */
214
+ pushRetry: backoff(5_000, 10 * MINUTE),
215
+ /** How long a push is tried before it is dropped: the change feed covers what it said. */
216
+ pushMaxAgeMs: ms(24 * HOUR),
217
+ /** How long one push's request may take. */
218
+ pushTimeoutMs: ms(10_000),
219
+ /** How long a finished task's push configs are kept, for `GetTaskPushNotificationConfig`. */
220
+ pushConfigRetentionMs: ms(7 * 24 * HOUR),
221
+ }),
211
222
  /** Triggers (track T4, D74): server timing; the Tenant's own bounds are its Runtime limits. */
212
223
  triggers: group({
213
224
  /**
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const RUNTIME_VERSION = "0.36.1-beta";
1
+ export declare const RUNTIME_VERSION = "0.38.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.36.1-beta";
3
+ export const RUNTIME_VERSION = "0.38.0-beta";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nylorun/runtime",
3
- "version": "0.36.1-beta",
3
+ "version": "0.38.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.31.0-beta",
69
- "@nylorun/harness": "0.29.7-beta",
68
+ "@nylorun/core": "0.33.0-beta",
69
+ "@nylorun/harness": "0.30.1-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",