@nylorun/runtime 0.51.1-beta → 0.52.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,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.52.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - 17951fe: A client can give one Agent a trigger over the API (D88, ADR-145; Host feature `api-triggers`). `PUT /v1/agents/{agentId}/triggers/{triggerId}` creates or replaces the Agent's trigger of a fourth origin, `api`: the body is a `Trigger` exactly as a definition's `runtime.triggers` entry holds it (one of `schedule` with `timezone`, `at` or `webhook`; `session`; `prompt`) and an optional `title`. A new one is active with its next time; a replaced one gets its next time again and keeps its status; the same body again changes nothing and records no event. An id the definition declares, or one an agent's trigger or a booked wake holds, is `409 trigger_conflict`. The checks and limits are a definition's, and `runtime.limits.triggers.count` now counts the triggers an Agent's agents made and those clients put together (`422 trigger_limit`). Upgrades and moves never touch an `api` trigger, `DELETE` removes it, and pause, resume, run and a webhook's secret work as for any trigger.
8
+
9
+ - `@nylorun/core`: `TRIGGER_ORIGINS` adds `api`; `TriggerPutSchema`; `TriggerView.principal`; `agent.trigger_created` and `agent.trigger_updated` carry `origin`, `principal`, the timing and `changed` (their `sessionId` is now optional, set for `agent` only; `kind` adds `webhook`), and `agent.trigger_deleted` carries `origin`; Host feature `api-triggers`.
10
+ - `@nylorun/runtime`: the route; `agent_triggers.principal` and the `api` origin (store migration 0049, run at start).
11
+ - `@nylorun/agents`: `agents.putTrigger(agentId, triggerId, trigger)`; `deleteTrigger` deletes `api` triggers too.
12
+ - `nylorun`: `nylorun agents triggers put <id> <trigger>` with `--schedule`, `--timezone`, `--at`, `--webhook`, `--session`, `--prompt` and `--title`, or a JSON file; `rm` deletes `api` triggers.
13
+ - `@nylorun/studio`: the Triggers tab shows where each trigger came from (the definition's version, the agent that made it, or the principal that put it), its prompt and time zone for every origin, and deletes one an agent made or a client put; Activity words trigger changes and filters them.
14
+
15
+ ### Patch Changes
16
+
17
+ - Pin core to the tested release.
18
+ - Pin harness to the tested release.
19
+ - Updated dependencies [17951fe]
20
+ - Updated dependencies
21
+ - @nylorun/core@0.45.0-beta
22
+ - @nylorun/harness@0.36.5-beta
23
+
24
+ ## 0.51.2-beta
25
+
26
+ ### Patch Changes
27
+
28
+ - b04a877: Pods on Agent Substrate (preview): a pod's engine tries its first join again while the edge can't be reached. A new actor can start before its egress policy reaches Substrate's egress, so the first join failed at the network ("fetch failed") and the engine exited; Substrate doesn't restart an actor's main process, so the actor stayed RUNNING with no engine and the start failed at the ready deadline (E13 on kind: 2 of 3 first starts). The first join is now tried again from 250 ms, doubling to 5 s, for up to 4 minutes (`sandbox_join_retry` in the engine's log); a refusal is still final at once.
29
+
3
30
  ## 0.51.1-beta
4
31
 
5
32
  ### Patch Changes
@@ -2301,11 +2301,13 @@ export declare const TriggerList: z.ZodObject<{
2301
2301
  }>;
2302
2302
  origin: z.ZodEnum<{
2303
2303
  agent: "agent";
2304
+ api: "api";
2304
2305
  manifest: "manifest";
2305
2306
  wake: "wake";
2306
2307
  }>;
2307
2308
  title: z.ZodNullable<z.ZodString>;
2308
2309
  createdSessionId: z.ZodNullable<z.ZodString>;
2310
+ principal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2309
2311
  status: z.ZodEnum<{
2310
2312
  active: "active";
2311
2313
  completed: "completed";
@@ -2323,6 +2325,50 @@ export declare const TriggerList: z.ZodObject<{
2323
2325
  prompt: z.ZodOptional<z.ZodString>;
2324
2326
  }, z.core.$strict>>;
2325
2327
  }, z.core.$strict>;
2328
+ export declare const TriggerView: z.ZodObject<{
2329
+ id: z.ZodString;
2330
+ kind: z.ZodEnum<{
2331
+ at: "at";
2332
+ schedule: "schedule";
2333
+ wake: "wake";
2334
+ webhook: "webhook";
2335
+ }>;
2336
+ origin: z.ZodEnum<{
2337
+ agent: "agent";
2338
+ api: "api";
2339
+ manifest: "manifest";
2340
+ wake: "wake";
2341
+ }>;
2342
+ title: z.ZodNullable<z.ZodString>;
2343
+ createdSessionId: z.ZodNullable<z.ZodString>;
2344
+ principal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2345
+ status: z.ZodEnum<{
2346
+ active: "active";
2347
+ completed: "completed";
2348
+ paused: "paused";
2349
+ }>;
2350
+ pausedReason: z.ZodNullable<z.ZodString>;
2351
+ schedule: z.ZodOptional<z.ZodString>;
2352
+ timezone: z.ZodOptional<z.ZodString>;
2353
+ at: z.ZodOptional<z.ZodString>;
2354
+ session: z.ZodString;
2355
+ nextRunAt: z.ZodNullable<z.ZodString>;
2356
+ lastRunAt: z.ZodNullable<z.ZodString>;
2357
+ webhookUrl: z.ZodOptional<z.ZodString>;
2358
+ secretSet: z.ZodOptional<z.ZodBoolean>;
2359
+ prompt: z.ZodOptional<z.ZodString>;
2360
+ }, z.core.$strict>;
2361
+ export declare const PutTriggerRequest: z.ZodObject<{
2362
+ schedule: z.ZodOptional<z.ZodString>;
2363
+ timezone: z.ZodOptional<z.ZodString>;
2364
+ at: z.ZodOptional<z.ZodISODateTime>;
2365
+ webhook: z.ZodOptional<z.ZodEnum<{
2366
+ "standard-webhooks": "standard-webhooks";
2367
+ }>>;
2368
+ session: z.ZodString;
2369
+ prompt: z.ZodString;
2370
+ title: z.ZodOptional<z.ZodString>;
2371
+ }, z.core.$strict>;
2326
2372
  export declare const MemoryList: z.ZodObject<{
2327
2373
  memories: z.ZodArray<z.ZodObject<{
2328
2374
  path: z.ZodString;
@@ -2440,6 +2486,7 @@ export declare const TenantRun: z.ZodObject<{
2440
2486
  late: z.ZodBoolean;
2441
2487
  origin: z.ZodEnum<{
2442
2488
  agent: "agent";
2489
+ api: "api";
2443
2490
  manifest: "manifest";
2444
2491
  wake: "wake";
2445
2492
  }>;
@@ -2472,6 +2519,7 @@ export declare const TenantRunEvent: z.ZodObject<{
2472
2519
  late: z.ZodBoolean;
2473
2520
  origin: z.ZodEnum<{
2474
2521
  agent: "agent";
2522
+ api: "api";
2475
2523
  manifest: "manifest";
2476
2524
  wake: "wake";
2477
2525
  }>;
@@ -5,7 +5,7 @@
5
5
  * global registry.
6
6
  */
7
7
  import { z } from "zod";
8
- import { PutSessionModelRequestSchema, SessionModelViewSchema, QueuedResponseSchema, TriggerListSchema, MemoryListSchema, MemorySchema, PutMemoryRequestSchema, MemoryVersionPageSchema, MemoryVersionDetailSchema, TriggerRunPageSchema, RunTriggerResponseSchema, WebhookSecretResponseSchema, WebhookAcceptedResponseSchema, AgentManifestV8Schema, EnvironmentSecretSlotsSchema, DeleteAgentManifestResponseSchema, AgentManifestPlanSchema, AgentManifestVersionsSchema, AgentManifestViewSchema, ListAgentManifestsResponseSchema, PutAgentManifestRequestSchema, PutAgentFolderRequestSchema, AgentManifestSourceSchema, AgentManifestSourceDiffSchema, PutAgentSecretValueRequestSchema, SaveAgentManifestResponseSchema, SessionListItemSchema, ModelCallSchema, SessionPageSchema, SessionManifestViewSchema, HistoryPageSchema, SessionUsageTotalsSchema, ModelCallsPageSchema, ModelCallExportPageSchema, AgentPageSchema, ToolCallSchema, ToolCallsPageSchema, TurnTraceSchema, TurnChangesPageSchema, TurnSummarySchema, JwksSchema, SigningKeyListSchema, SigningKeyViewSchema, RotateSigningKeysRequestSchema, RevokeSigningKeyRequestSchema, MeResponseSchema, TenantStatusSchema, ResetTenantRequestSchema, ResetTenantResponseSchema, SeedTenantConfigRequestSchema, SeedTenantConfigResponseSchema, HostModelCatalogSchema, TenantSandboxViewSchema, ModelUsageTotalsSchema, ModelBudgetsSchema, PutModelBudgetsRequestSchema, ToolPolicySchema, PutToolPolicyRequestSchema, PutModelInputsRequestSchema, ModelInputsViewSchema, PutTenantEnvironmentsRequestSchema, ListProvidersResponseSchema, HostModelViewSchema, PutHostModelRequestSchema, SelectHostModelRequestSchema, PutAgentSecretRequestSchema, AgentSecretViewSchema, ListAgentSecretsResponseSchema, McpPreviewRequestSchema, McpPreviewSchema, CredentialCoverageRequestSchema, CredentialCoverageSchema, DeletedResponseSchema, TenantRunEventSchema, TenantRunSchema, ListSessionsResponseSchema, LiveEventSchema, PutAgentRequestSchema, PutSessionRequestSchema, SessionItemsResponseSchema, SessionViewSchema, StreamClosedFrameSchema, AcceptedResponseSchema, SandboxToolOutcomeSchema, AgentViewSchema, AgentSnapshotSchema, RestoreAgentRequestSchema, ForkAgentRequestSchema, AgentForkPlanSchema, ForkAgentResponseSchema, PutAgentDryRunResponseSchema, RestartAgentRequestSchema, AgentRestoreSchema, AgentDiskVersionsSchema, ListAgentsResponseSchema, DeleteAgentResponseSchema, ListAgentEventsResponseSchema, ArtifactViewSchema, ArtifactListItemSchema, ArtifactPageSchema, ArtifactVersionViewSchema, ListArtifactsResponseSchema, UploadArtifactResponseSchema, DefinitionFileViewSchema, DeleteArtifactResponseSchema, CreateArtifactLinkRequestSchema, ArtifactLinkSchema, PutTenantArtifactsRequestSchema, PutTenantSkillsRequestSchema, PutTenantLimitsRequestSchema, TenantLimitsViewSchema, TenantArtifactsViewSchema, TenantSkillsViewSchema, ArtifactTreeSchema, ArtifactDiffSchema, SessionCommandSchema, ListOperatorKeysResponseSchema, PutOperatorKeyResponseSchema, DeleteOperatorKeyResponseSchema, OperatorKeySchema, ProtocolRejectedResponseSchema, RejectedResponseSchema, EVENT_SCHEMAS, EVENT_TYPES, } from "@nylorun/core/contracts";
8
+ import { PutSessionModelRequestSchema, SessionModelViewSchema, QueuedResponseSchema, TriggerListSchema, TriggerPutSchema, TriggerViewSchema, MemoryListSchema, MemorySchema, PutMemoryRequestSchema, MemoryVersionPageSchema, MemoryVersionDetailSchema, TriggerRunPageSchema, RunTriggerResponseSchema, WebhookSecretResponseSchema, WebhookAcceptedResponseSchema, AgentManifestV8Schema, EnvironmentSecretSlotsSchema, DeleteAgentManifestResponseSchema, AgentManifestPlanSchema, AgentManifestVersionsSchema, AgentManifestViewSchema, ListAgentManifestsResponseSchema, PutAgentManifestRequestSchema, PutAgentFolderRequestSchema, AgentManifestSourceSchema, AgentManifestSourceDiffSchema, PutAgentSecretValueRequestSchema, SaveAgentManifestResponseSchema, SessionListItemSchema, ModelCallSchema, SessionPageSchema, SessionManifestViewSchema, HistoryPageSchema, SessionUsageTotalsSchema, ModelCallsPageSchema, ModelCallExportPageSchema, AgentPageSchema, ToolCallSchema, ToolCallsPageSchema, TurnTraceSchema, TurnChangesPageSchema, TurnSummarySchema, JwksSchema, SigningKeyListSchema, SigningKeyViewSchema, RotateSigningKeysRequestSchema, RevokeSigningKeyRequestSchema, MeResponseSchema, TenantStatusSchema, ResetTenantRequestSchema, ResetTenantResponseSchema, SeedTenantConfigRequestSchema, SeedTenantConfigResponseSchema, HostModelCatalogSchema, TenantSandboxViewSchema, ModelUsageTotalsSchema, ModelBudgetsSchema, PutModelBudgetsRequestSchema, ToolPolicySchema, PutToolPolicyRequestSchema, PutModelInputsRequestSchema, ModelInputsViewSchema, PutTenantEnvironmentsRequestSchema, ListProvidersResponseSchema, HostModelViewSchema, PutHostModelRequestSchema, SelectHostModelRequestSchema, PutAgentSecretRequestSchema, AgentSecretViewSchema, ListAgentSecretsResponseSchema, McpPreviewRequestSchema, McpPreviewSchema, CredentialCoverageRequestSchema, CredentialCoverageSchema, DeletedResponseSchema, TenantRunEventSchema, TenantRunSchema, ListSessionsResponseSchema, LiveEventSchema, PutAgentRequestSchema, PutSessionRequestSchema, SessionItemsResponseSchema, SessionViewSchema, StreamClosedFrameSchema, AcceptedResponseSchema, SandboxToolOutcomeSchema, AgentViewSchema, AgentSnapshotSchema, RestoreAgentRequestSchema, ForkAgentRequestSchema, AgentForkPlanSchema, ForkAgentResponseSchema, PutAgentDryRunResponseSchema, RestartAgentRequestSchema, AgentRestoreSchema, AgentDiskVersionsSchema, ListAgentsResponseSchema, DeleteAgentResponseSchema, ListAgentEventsResponseSchema, ArtifactViewSchema, ArtifactListItemSchema, ArtifactPageSchema, ArtifactVersionViewSchema, ListArtifactsResponseSchema, UploadArtifactResponseSchema, DefinitionFileViewSchema, DeleteArtifactResponseSchema, CreateArtifactLinkRequestSchema, ArtifactLinkSchema, PutTenantArtifactsRequestSchema, PutTenantSkillsRequestSchema, PutTenantLimitsRequestSchema, TenantLimitsViewSchema, TenantArtifactsViewSchema, TenantSkillsViewSchema, ArtifactTreeSchema, ArtifactDiffSchema, SessionCommandSchema, ListOperatorKeysResponseSchema, PutOperatorKeyResponseSchema, DeleteOperatorKeyResponseSchema, OperatorKeySchema, ProtocolRejectedResponseSchema, RejectedResponseSchema, EVENT_SCHEMAS, EVENT_TYPES, } from "@nylorun/core/contracts";
9
9
  function named(id, schema) {
10
10
  return schema.meta({ id });
11
11
  }
@@ -65,6 +65,8 @@ export const SessionCommand = named("SessionCommand", SessionCommandSchema);
65
65
  export const AcceptedResponse = named("AcceptedResponse", AcceptedResponseSchema);
66
66
  export const QueuedResponse = named("QueuedResponse", QueuedResponseSchema);
67
67
  export const TriggerList = named("TriggerList", TriggerListSchema);
68
+ export const TriggerView = named("TriggerView", TriggerViewSchema);
69
+ export const PutTriggerRequest = named("PutTriggerRequest", TriggerPutSchema);
68
70
  export const MemoryList = named("MemoryList", MemoryListSchema);
69
71
  export const MemoryView = named("Memory", MemorySchema);
70
72
  export const PutMemoryRequest = named("PutMemoryRequest", PutMemoryRequestSchema);
@@ -1,11 +1,12 @@
1
1
  import { z } from "zod";
2
- import { isAgentId } from "@nylorun/core/contracts";
3
- import { DeletedResponse, RunTriggerResponse, TriggerList, TriggerRunPage, WebhookAcceptedResponse, WebhookSecretResponse, } from "../../components.js";
2
+ import { isAgentId, TriggerIdSchema, TriggerPutSchema } from "@nylorun/core/contracts";
3
+ import { DeletedResponse, PutTriggerRequest, RunTriggerResponse, TriggerList, TriggerRunPage, TriggerView, WebhookAcceptedResponse, WebhookSecretResponse, } from "../../components.js";
4
4
  import { assertReaches } from "../../../tenant/agents.js";
5
5
  import { fail } from "../../../tenant/http.js";
6
6
  import { deleteTrigger, listRuns, listTriggers, receiveWebhook, rotateWebhookSecret, runTriggerNow, setTriggerStatus, } from "../../../tenant/triggers.js";
7
7
  import { readRuntimeLimits } from "../../../tenant/runtime-limits.js";
8
- import { readBytes } from "../body.js";
8
+ import { readBytes, readJson } from "../body.js";
9
+ import { putTrigger } from "../../../tenant/api-triggers.js";
9
10
  import { tenantRoute } from "../define.js";
10
11
  import { pageQuery, pageSize, parseQuery } from "./reads.js";
11
12
  import { jsonResponse } from "../respond.js";
@@ -46,7 +47,7 @@ export function triggerRoutes(api) {
46
47
  path: "/v1/agents/{agentId}/triggers",
47
48
  tags: ["Triggers"],
48
49
  summary: "List an Agent's triggers",
49
- description: "Each trigger its manifest declares, and each its agents made with the `triggers` tool (`origin: agent`, with the title and the session that made it), with what the Agent holds of it: its status (`active`, `paused` with a reason, `completed` for an `at` that ran), its prompt, when it runs next and last ran. A session's booked wake is not listed.",
50
+ description: "Each trigger its manifest declares, each its agents made with the `triggers` tool (`origin: agent`, with the title and the session that made it), and each a client put over the API (`origin: api`, with the title and the `principal` that put it), with what the Agent holds of it: its status (`active`, `paused` with a reason, `completed` for an `at` that ran), its prompt, when it runs next and last ran. A session's booked wake is not listed.",
50
51
  request: { params: agentId },
51
52
  responses: { 200: json(TriggerList, "The triggers") },
52
53
  }, async (c) => {
@@ -73,12 +74,43 @@ export function triggerRoutes(api) {
73
74
  assertReaches(c.get("scope"), id);
74
75
  return jsonResponse(200, await setTriggerStatus(c.env.tenant, id, c.req.param("triggerId"), action, originOf(c)));
75
76
  });
77
+ tenantRoute(api, WRITE, {
78
+ method: "put",
79
+ path: "/v1/agents/{agentId}/triggers/{triggerId}",
80
+ tags: ["Triggers"],
81
+ summary: "Put a trigger on an Agent",
82
+ description: "Creates or replaces the Agent's trigger `triggerId` of origin `api`: a `Trigger` exactly as a definition's `runtime.triggers` entry holds it (one of `schedule` with `timezone`, `at` or `webhook`; `session`; `prompt`), and an optional `title`. A new one is active with its next time; a replaced one gets its next time again and keeps its status, so a paused one stays paused. The same body again changes nothing and records no event. The checks and limits are a definition's; `runtime.limits.triggers.count` covers the triggers the Agent's agents made and those put here. Upgrades never touch it; `DELETE` removes it. Records `agent.trigger_created` or `agent.trigger_updated` (with `changed`) on the Agent's events. Host feature `api-triggers`.",
83
+ request: {
84
+ params: triggerPath,
85
+ body: { required: true, content: { "application/json": { schema: PutTriggerRequest } } },
86
+ },
87
+ responses: {
88
+ 200: json(TriggerView, "The trigger, as the list shows it"),
89
+ 400: { description: "Not a trigger, or one the checks refuse: a bad cron or time zone, runs closer than the minimum interval, an `at` not in the future, a prompt too long" },
90
+ 404: { description: "No such Agent" },
91
+ 409: { description: "The definition declares this id, or an agent's trigger or a booked wake holds it (`trigger_conflict`)" },
92
+ 422: { description: "The Agent holds as many triggers its agents or clients made as `limits.triggers.count` allows (`trigger_limit`)" },
93
+ },
94
+ }, async (c) => {
95
+ const id = idOf(c.req.param("agentId"));
96
+ const scope = c.get("scope");
97
+ assertReaches(scope, id);
98
+ const triggerId = TriggerIdSchema.safeParse(c.req.param("triggerId"));
99
+ if (!triggerId.success)
100
+ fail(400, triggerId.error.issues[0]?.message ?? "Not a trigger id");
101
+ const body = TriggerPutSchema.safeParse(await readJson(c));
102
+ if (!body.success)
103
+ fail(400, body.error.issues.map((issue) => `${issue.path.join(".") || "body"}: ${issue.message}`).join("; "), {
104
+ code: "invalid_request",
105
+ });
106
+ return jsonResponse(200, await putTrigger(c.env.tenant, id, triggerId.data, body.data, principalOf(scope), originOf(c)));
107
+ });
76
108
  tenantRoute(api, WRITE, {
77
109
  method: "delete",
78
110
  path: "/v1/agents/{agentId}/triggers/{triggerId}",
79
111
  tags: ["Triggers"],
80
- summary: "Delete a trigger an agent made",
81
- description: "Deletes a trigger one of the Agent's agents made with the `triggers` tool (`origin: agent`), with `agent.trigger_deleted` on the Agent's events; its runs stay. A trigger its manifest declares is refused (`trigger_from_manifest`): save a version without it, or pause it.",
112
+ summary: "Delete a trigger an agent made or a client put",
113
+ description: "Deletes a trigger one of the Agent's agents made with the `triggers` tool (`origin: agent`) or a client put (`origin: api`), with `agent.trigger_deleted` on the Agent's events; its runs stay. A trigger its manifest declares is refused (`trigger_from_manifest`): save a version without it, or pause it.",
82
114
  request: { params: triggerPath },
83
115
  responses: {
84
116
  200: json(DeletedResponse, "Deleted"),
@@ -161,6 +161,7 @@ const RUNTIME_TAGS = [
161
161
  description: "What starts an Agent's turns with no client connected: the schedules, one-shot times and webhook endpoints its manifest's `runtime.triggers` declare, and the schedules and follow-ups its agents set up with the `triggers` tool (`engine.triggers`). Each trigger's status is the Agent's: pause and resume it, run it now, delete one an agent made, make a webhook trigger's signing secret, and read its runs (`started`, `queued`, `skipped` or `failed`, with a reason), an Agent's or the whole Tenant's as a stream. In `@nylorun/agents` and `nylorun agents triggers`.",
162
162
  operations: [
163
163
  "GET /v1/agents/{agentId}/triggers",
164
+ "PUT /v1/agents/{agentId}/triggers/{triggerId}",
164
165
  "POST /v1/agents/{agentId}/triggers/{triggerId}/pause",
165
166
  "POST /v1/agents/{agentId}/triggers/{triggerId}/resume",
166
167
  "POST /v1/agents/{agentId}/triggers/{triggerId}/run",
@@ -27,7 +27,7 @@ import { createHostLogger } from "../host/logger.js";
27
27
  import { RUNTIME_VERSION } from "../version.js";
28
28
  import { localBackend, localExec } from "../adapters/sandbox/local.js";
29
29
  import { awaitNetworkPolicy } from "../sandbox/pods/network-gate.js";
30
- import { podHost } from "./pod.js";
30
+ import { firstJoin, podHost } from "./pod.js";
31
31
  import { prepareDisk } from "./setup.js";
32
32
  import { harnessRunTokens } from "./run-tokens.js";
33
33
  import { startHarnessService } from "./service.js";
@@ -59,8 +59,9 @@ export async function runHarness(stack, baseline) {
59
59
  caBundleFile: join(config.root, "egress-ca-bundle.pem"),
60
60
  ...(config.pod.egressCaFile ? { extraCaFile: config.pod.egressCaFile } : {}),
61
61
  });
62
- // Join, then make the disk ready (R2d step 2) before taking any turn.
63
- await pod.token();
62
+ // Join, then make the disk ready (R2d step 2) before taking any turn. The first join is tried
63
+ // again while the edge can't be reached yet (a new Agent Substrate actor's egress policy).
64
+ await firstJoin(pod, (message, fields) => logger.warn(message, fields));
64
65
  const host = pod;
65
66
  prepare = () => prepareDisk({
66
67
  host,
@@ -52,6 +52,23 @@ export interface PodHost {
52
52
  setupFile(index: number, path?: string): Promise<Response>;
53
53
  stop(): void;
54
54
  }
55
+ /**
56
+ * The engine's first join, tried again while the edge can't be reached. On Agent Substrate a new
57
+ * actor can start before its egress policy reaches the egress gateway, so its first request to
58
+ * the edge fails at the network ("fetch failed"), and an engine that then exited stayed down: an
59
+ * actor's main process is not restarted the way a pod's container is (E13, 2 of 3 first starts on
60
+ * a GitHub runner). A refusal (400, 401) is final at once; anything else is tried again with a
61
+ * pause from 250 ms doubling to 5 s, until `budgetMs` (default 4 min, inside the Runtime's 5-minute
62
+ * ready deadline) has passed.
63
+ */
64
+ export declare function firstJoin(host: Pick<PodHost, "token">, log: (message: string, fields: Record<string, unknown>) => void, options?: {
65
+ budgetMs?: number;
66
+ firstWaitMs?: number;
67
+ sleep?: (ms: number) => Promise<void>;
68
+ now?: () => number;
69
+ }): Promise<void>;
70
+ /** Whether `error` is the Runtime refusing a join or a renewal (final), not the network. */
71
+ export declare function isRefused(error: unknown): boolean;
55
72
  export declare function podHost(config: PodHostConfig, logger: {
56
73
  info(message: string, fields?: Record<string, unknown>): void;
57
74
  warn(message: string, fields?: Record<string, unknown>): void;
@@ -29,6 +29,38 @@ const TRUST_VARIABLES = [
29
29
  ];
30
30
  class Refused extends Error {
31
31
  }
32
+ /**
33
+ * The engine's first join, tried again while the edge can't be reached. On Agent Substrate a new
34
+ * actor can start before its egress policy reaches the egress gateway, so its first request to
35
+ * the edge fails at the network ("fetch failed"), and an engine that then exited stayed down: an
36
+ * actor's main process is not restarted the way a pod's container is (E13, 2 of 3 first starts on
37
+ * a GitHub runner). A refusal (400, 401) is final at once; anything else is tried again with a
38
+ * pause from 250 ms doubling to 5 s, until `budgetMs` (default 4 min, inside the Runtime's 5-minute
39
+ * ready deadline) has passed.
40
+ */
41
+ export async function firstJoin(host, log, options = {}) {
42
+ const now = options.now ?? Date.now;
43
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
44
+ const deadline = now() + (options.budgetMs ?? 240_000);
45
+ let wait = options.firstWaitMs ?? 250;
46
+ for (let attempt = 1;; attempt += 1) {
47
+ try {
48
+ await host.token();
49
+ return;
50
+ }
51
+ catch (error) {
52
+ if (error instanceof Refused || now() + wait > deadline)
53
+ throw error;
54
+ log("sandbox_join_retry", { attempt, waitMs: wait, error: error instanceof Error ? error.message : String(error) });
55
+ await sleep(wait);
56
+ wait = Math.min(wait * 2, 5_000);
57
+ }
58
+ }
59
+ }
60
+ /** Whether `error` is the Runtime refusing a join or a renewal (final), not the network. */
61
+ export function isRefused(error) {
62
+ return error instanceof Refused;
63
+ }
32
64
  export function podHost(config, logger, options = {}) {
33
65
  const call = options.fetch ?? fetch;
34
66
  const imageEngine = options.imageEngine ?? isBuiltinEngine(import.meta.url);
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.2.0",
3
3
  "info": {
4
4
  "title": "Nylorun Management API",
5
- "version": "0.51.1-beta",
5
+ "version": "0.52.0-beta",
6
6
  "x-nylorun-protocol": {
7
7
  "min": 16,
8
8
  "max": 18,
@@ -44,6 +44,7 @@
44
44
  "secret-routing",
45
45
  "remove-tools",
46
46
  "agent-triggers",
47
+ "api-triggers",
47
48
  "steer",
48
49
  "queue-recovery",
49
50
  "a2a-steer"