@nylorun/runtime 0.51.2-beta → 0.53.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 +43 -0
- package/dist/adapters/sandbox/computer.d.ts +28 -0
- package/dist/adapters/sandbox/computer.js +184 -0
- package/dist/api/components.d.ts +48 -0
- package/dist/api/components.js +3 -1
- package/dist/api/http/routes/triggers.js +38 -6
- package/dist/api/openapi.js +1 -0
- package/dist/artifacts/disk.js +10 -2
- package/dist/harness/computer.d.ts +86 -0
- package/dist/harness/computer.js +273 -0
- package/dist/harness/executors.d.ts +5 -0
- package/dist/harness/executors.js +13 -5
- package/dist/harness/pod.d.ts +2 -0
- package/dist/harness/pod.js +1 -1
- package/dist/harness/service.js +58 -22
- package/dist/harness-api/server.js +17 -0
- package/dist/harness-api/start.js +2 -0
- package/dist/harness-api/workspace.d.ts +5 -0
- package/dist/harness-api/workspace.js +9 -5
- package/dist/host/harness-entry.js +7 -2
- package/dist/host/main.js +13 -0
- package/dist/host/stack-config.d.ts +13 -1
- package/dist/host/stack-config.js +42 -2
- package/dist/management-openapi.json +2 -1
- package/dist/openapi.json +348 -44
- package/dist/sandbox/computer.d.ts +53 -0
- package/dist/sandbox/computer.js +271 -0
- package/dist/sandbox/placement.d.ts +5 -2
- package/dist/sandbox/placement.js +6 -3
- package/dist/sandbox/pods/reconcile.js +1 -0
- package/dist/sandbox/pods/spec.d.ts +5 -0
- package/dist/sandbox/pods/spec.js +1 -1
- package/dist/sandbox/records.d.ts +2 -0
- package/dist/sandbox/records.js +11 -0
- package/dist/sandbox/types.d.ts +5 -2
- package/dist/store/postgres/drizzle/0049_agent_triggers_api.sql +3 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +7 -0
- package/dist/store/postgres/schema.d.ts +21 -4
- package/dist/store/postgres/schema.js +10 -7
- package/dist/tenant/advance.js +6 -3
- package/dist/tenant/agent-triggers.js +5 -2
- package/dist/tenant/api-triggers.d.ts +33 -0
- package/dist/tenant/api-triggers.js +140 -0
- package/dist/tenant/context.d.ts +3 -0
- package/dist/tenant/runtime.js +34 -0
- package/dist/tenant/sessions.js +2 -1
- package/dist/tenant/sweep.js +2 -1
- package/dist/tenant/transcript.d.ts +8 -8
- package/dist/tenant/triggers.d.ts +6 -4
- package/dist/tenant/triggers.js +22 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triggers a client puts on one Agent (D88): `PUT /v1/agents/{agentId}/triggers/{triggerId}` with
|
|
3
|
+
* a `Trigger`, exactly as a definition's `runtime.triggers` entry holds it, and an optional title.
|
|
4
|
+
*
|
|
5
|
+
* - **Origin `api`.** The row keeps its own definition, like an agent-made trigger (D83), and the
|
|
6
|
+
* principal that put it. Upgrades and moves never touch it (`mergeAgentTriggers` merges
|
|
7
|
+
* `manifest` rows only); `DELETE` removes it; pause, resume, run and a webhook's secret work as
|
|
8
|
+
* for any trigger; it fires through `fireTimer` and `deliverRun` like every other.
|
|
9
|
+
* - **Create or replace.** A new id makes it active with its next time. The same id replaces it:
|
|
10
|
+
* `generation` goes up and the next time is worked out again, so the timer armed for the old
|
|
11
|
+
* time does nothing. A paused one stays paused; an `at` that ran is active again at its new time.
|
|
12
|
+
* - **The same body again changes nothing** and records no event, so a client may put its desired
|
|
13
|
+
* state as often as it likes.
|
|
14
|
+
* - **Conflicts.** An id the definition declares, or a row of another origin holds, is
|
|
15
|
+
* `409 trigger_conflict`: the API never replaces a trigger it didn't put.
|
|
16
|
+
* - **Checks and limits.** Those a definition save runs (`triggerIssues`), against the limits in
|
|
17
|
+
* force; `count` covers the Agent's `agent` and `api` triggers together (`422 trigger_limit`).
|
|
18
|
+
* - **Events.** `agent.trigger_created` or `agent.trigger_updated` (with `changed`) on the Agent.
|
|
19
|
+
*
|
|
20
|
+
* One transaction with the Agent locked; the timer is armed after the commit.
|
|
21
|
+
*/
|
|
22
|
+
import { triggerKindOf } from "@nylorun/core/contracts";
|
|
23
|
+
import { liveEnvironment } from "./agents.js";
|
|
24
|
+
import { fail } from "./http.js";
|
|
25
|
+
import { triggerLimitsInForce } from "./limits.js";
|
|
26
|
+
import { readRuntimeLimits } from "./runtime-limits.js";
|
|
27
|
+
import { armTrigger, newEndpointId, nextFireAt, originOf, triggerIssues, triggersOf, triggerView } from "./triggers.js";
|
|
28
|
+
/** The definition fields a put compares, in a fixed order. */
|
|
29
|
+
const DEFINITION_FIELDS = ["schedule", "timezone", "at", "webhook", "session", "prompt"];
|
|
30
|
+
/** The `Trigger` part of a put body, with only the fields it has. */
|
|
31
|
+
export function definitionOfPut(body) {
|
|
32
|
+
const definition = {};
|
|
33
|
+
for (const field of DEFINITION_FIELDS)
|
|
34
|
+
if (body[field] !== undefined)
|
|
35
|
+
definition[field] = body[field];
|
|
36
|
+
return definition;
|
|
37
|
+
}
|
|
38
|
+
/** The definition fields that differ between `before` and `after`, and `title` when it changed. */
|
|
39
|
+
export function changedFields(before, after, titleBefore, titleAfter) {
|
|
40
|
+
const changed = DEFINITION_FIELDS.filter((field) => before[field] !== after[field]);
|
|
41
|
+
if (titleBefore !== titleAfter)
|
|
42
|
+
changed.push("title");
|
|
43
|
+
return changed;
|
|
44
|
+
}
|
|
45
|
+
/** The event payload's timing and session for `trigger`. */
|
|
46
|
+
function timingOf(trigger) {
|
|
47
|
+
return {
|
|
48
|
+
session: trigger.session,
|
|
49
|
+
...(trigger.schedule === undefined ? {} : { schedule: trigger.schedule, timezone: trigger.timezone ?? "UTC" }),
|
|
50
|
+
...(trigger.at === undefined ? {} : { at: trigger.at }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `PUT /v1/agents/{agentId}/triggers/{triggerId}` (D88): creates or replaces the Agent's `api`
|
|
55
|
+
* trigger `triggerId` and answers it as the list shows it. `principal` is who put it; `origin` is
|
|
56
|
+
* the URL origin the request reached, for a webhook's URL.
|
|
57
|
+
*/
|
|
58
|
+
export async function putTrigger(ctx, agentId, triggerId, body, principal, origin = "") {
|
|
59
|
+
const definition = definitionOfPut(body);
|
|
60
|
+
const title = body.title ?? null;
|
|
61
|
+
const { row, changed } = await ctx.store.tx(async (t) => {
|
|
62
|
+
const agent = (await liveEnvironment(t, agentId, { lock: true })) ?? fail(404, `Agent ${agentId} not found`, { code: "not_found" });
|
|
63
|
+
const existing = await t.agentTrigger(agentId, triggerId, { lock: true });
|
|
64
|
+
const declared = await triggersOf(t, agent);
|
|
65
|
+
const held = existing ? originOf(existing) : declared[triggerId] !== undefined ? "manifest" : undefined;
|
|
66
|
+
if (held !== undefined && held !== "api")
|
|
67
|
+
fail(409, held === "manifest"
|
|
68
|
+
? `Trigger ${triggerId} comes from the Agent's definition: put your trigger under another id.`
|
|
69
|
+
: held === "agent"
|
|
70
|
+
? `Trigger ${triggerId} was made by one of the Agent's agents: put your trigger under another id.`
|
|
71
|
+
: `${triggerId} is a session's booked wake: put your trigger under another id.`, { code: "trigger_conflict", details: { origin: held } });
|
|
72
|
+
const before = existing?.definition;
|
|
73
|
+
if (existing && before) {
|
|
74
|
+
const fields = changedFields(before, definition, existing.title, title);
|
|
75
|
+
// The same body again: nothing changes and nothing is recorded.
|
|
76
|
+
if (fields.length === 0)
|
|
77
|
+
return { row: existing, changed: undefined };
|
|
78
|
+
}
|
|
79
|
+
const now = new Date();
|
|
80
|
+
const stamp = now.toISOString();
|
|
81
|
+
const runtime = await readRuntimeLimits(t);
|
|
82
|
+
const inForce = triggerLimitsInForce(runtime, agent.applied?.limits);
|
|
83
|
+
if (!existing) {
|
|
84
|
+
// One count for every trigger not from the definition: the tool's and the API's.
|
|
85
|
+
const made = (await t.agentTriggers(agentId)).filter((other) => originOf(other) === "agent" || originOf(other) === "api").length;
|
|
86
|
+
if (made >= inForce.count)
|
|
87
|
+
fail(422, `Agent ${agentId} has ${made} triggers its agents or clients made, the most it can have (limits.triggers.count ${inForce.count}). Delete one to add another.`, {
|
|
88
|
+
code: "trigger_limit",
|
|
89
|
+
details: { limit: "triggers.count", max: inForce.count },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const issues = triggerIssues({ [triggerId]: definition }, { ...runtime, triggersPerAgent: inForce.count, triggerMinIntervalMs: inForce.minIntervalMs }, now, (id) => `trigger ${id}`);
|
|
93
|
+
if (issues.length > 0)
|
|
94
|
+
fail(400, issues.join(" "), { code: "invalid_request", details: { issues } });
|
|
95
|
+
const paused = existing?.status === "paused";
|
|
96
|
+
const next = paused ? null : nextFireAt(agentId, triggerId, definition, now, runtime);
|
|
97
|
+
const kind = triggerKindOf(definition);
|
|
98
|
+
const row = {
|
|
99
|
+
agentId,
|
|
100
|
+
triggerId,
|
|
101
|
+
kind,
|
|
102
|
+
// A webhook has no next time and stays active; an `at` with none has run.
|
|
103
|
+
status: paused ? "paused" : kind === "at" && next === null ? "completed" : "active",
|
|
104
|
+
pausedReason: paused ? existing.pausedReason : null,
|
|
105
|
+
generation: (existing?.generation ?? -1) + 1,
|
|
106
|
+
nextFireAt: next?.toISOString() ?? null,
|
|
107
|
+
lastFireAt: existing?.lastFireAt ?? null,
|
|
108
|
+
// A webhook keeps its endpoint and secret across replaces; another kind has none.
|
|
109
|
+
endpointId: kind === "webhook" ? (existing?.endpointId ?? newEndpointId()) : null,
|
|
110
|
+
sealedSecret: kind === "webhook" ? (existing?.sealedSecret ?? null) : null,
|
|
111
|
+
sessionId: null,
|
|
112
|
+
note: null,
|
|
113
|
+
effectId: null,
|
|
114
|
+
origin: "api",
|
|
115
|
+
principal: principal ?? existing?.principal ?? null,
|
|
116
|
+
title,
|
|
117
|
+
definition,
|
|
118
|
+
createdSessionId: null,
|
|
119
|
+
createdTurnId: null,
|
|
120
|
+
createdAt: existing?.createdAt ?? stamp,
|
|
121
|
+
updatedAt: stamp,
|
|
122
|
+
};
|
|
123
|
+
await t.putAgentTrigger(row);
|
|
124
|
+
const fields = existing && before ? changedFields(before, definition, existing.title, title) : undefined;
|
|
125
|
+
await t.agentEvent(agentId, existing ? "agent.trigger_updated" : "agent.trigger_created", {
|
|
126
|
+
triggerId,
|
|
127
|
+
kind,
|
|
128
|
+
origin: "api",
|
|
129
|
+
...(row.principal === null ? {} : { principal: row.principal }),
|
|
130
|
+
...timingOf(definition),
|
|
131
|
+
...(title === null ? {} : { title }),
|
|
132
|
+
...(fields === undefined ? {} : { changed: fields }),
|
|
133
|
+
nextRunAt: row.status === "active" ? row.nextFireAt : null,
|
|
134
|
+
});
|
|
135
|
+
return { row, changed: true };
|
|
136
|
+
});
|
|
137
|
+
if (changed)
|
|
138
|
+
await armTrigger(ctx, row);
|
|
139
|
+
return triggerView(row, definition, origin);
|
|
140
|
+
}
|
package/dist/tenant/context.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* `wake` goes to `DurableExecution.wake`, which calls the Tenant's `advance` (`advance.ts`)
|
|
15
15
|
* under ownership (§10.6); `abortLocal` aborts an advance running on this process.
|
|
16
16
|
*/
|
|
17
|
+
import type { TenantComputers } from "../sandbox/computer.js";
|
|
17
18
|
import type { SessionHistory } from "./history.js";
|
|
18
19
|
import type { IssuerScope, SessionModelChoice, SessionModelRecord, SubjectScope, TenantEnvelope } from "@nylorun/core/contracts";
|
|
19
20
|
import type { DurableCheckpoint, FlowCheckpoint } from "@nylorun/harness/run";
|
|
@@ -390,6 +391,8 @@ export interface TenantPods {
|
|
|
390
391
|
readonly client: SandboxesClient;
|
|
391
392
|
/** The Runtime image the pods copy the engine from (`NYLORUN_SANDBOX_HARNESS_IMAGE`). */
|
|
392
393
|
readonly harnessImage: string;
|
|
394
|
+
/** Computers (ADR-149): every pod Agent is one, reached through the sandbox gateway. */
|
|
395
|
+
readonly computer?: TenantComputers;
|
|
393
396
|
}
|
|
394
397
|
/** The harness a session runs (R4.1): the one it pinned, else ours. */
|
|
395
398
|
export declare function harnessOf(session: Pick<Session, "harness">): string;
|
package/dist/tenant/runtime.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composition root of an open Tenant: validates the config, opens the Session Store and the
|
|
3
|
+
* vault key, builds the services and the `TenantContext`, wires the seams, registers the
|
|
4
|
+
* Tenant's Worker handlers and arms its sweep, and implements `TenantHandle` (handle,
|
|
5
|
+
* summary, drain, close) by delegating to the Tenant modules.
|
|
6
|
+
*
|
|
7
|
+
* Seams wired here: `wireStreams()` connects the store's commits to Durable Streams (the
|
|
8
|
+
* relay, and the history and SSE readers); `wireControl()` follows the control bus on
|
|
9
|
+
* Postgres (cancels and resets from other processes); `wake` goes to the
|
|
10
|
+
* `DurableExecution`, whose handlers (`worker.ts`) call `advance` and `sweep`; `abortLocal`
|
|
11
|
+
* aborts an advance running on this process.
|
|
12
|
+
*
|
|
13
|
+
* Execution: the Host passes its `TenantExecution` (`TenantOpenHooks.execution`); without
|
|
14
|
+
* one, the Tenant runs its own in-process `MemoryExecution`. No lock file: several processes may open a Tenant, and ownership of each
|
|
15
|
+
* session (§10.6) keeps its advances apart.
|
|
16
|
+
*/
|
|
17
|
+
import { computerManagers } from "../sandbox/computer.js";
|
|
1
18
|
import { mkdirSync } from "node:fs";
|
|
2
19
|
import { scriptedModel, toolFixtureModel, } from "../core/provider.js";
|
|
3
20
|
import { tenantModelGate } from "../gates/in-process.js";
|
|
@@ -185,6 +202,23 @@ export class TenantRuntime {
|
|
|
185
202
|
logger: config.logger,
|
|
186
203
|
preference,
|
|
187
204
|
isPod: async (sandboxId) => (await opened.tx((t) => t.environment(sandboxId)))?.kind === "pod",
|
|
205
|
+
...(hooks.pods.computer
|
|
206
|
+
? {
|
|
207
|
+
computer: (() => {
|
|
208
|
+
const managerOf = computerManagers(() => ctx, async (sessionId, turnId, type, payload) => {
|
|
209
|
+
if (ctx.closed)
|
|
210
|
+
return;
|
|
211
|
+
try {
|
|
212
|
+
await opened.tx((t) => t.event(sessionId, turnId, type, payload));
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
/* the session is gone (reset) or the store closed */
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
return (environmentId) => localWorkspace(managerOf(environmentId));
|
|
219
|
+
})(),
|
|
220
|
+
}
|
|
221
|
+
: {}),
|
|
188
222
|
})
|
|
189
223
|
: workspaces;
|
|
190
224
|
// A model that is not the vault's (scripted, the fixture, an embedder's provider) is
|
package/dist/tenant/sessions.js
CHANGED
|
@@ -93,7 +93,8 @@ export async function openSessionIn(ctx, t, id, body, access, options = {}) {
|
|
|
93
93
|
await checkSkillLimit(t, pin.manifest, agent);
|
|
94
94
|
const config = await readSandboxConfig(t);
|
|
95
95
|
// Placement (D38, D52): where this session's harness may run, decided now and kept.
|
|
96
|
-
|
|
96
|
+
// A computer's harness runs in the harness container (ADR-149).
|
|
97
|
+
checkPlacement(config, agent.kind, pin.harness, ctx.pods?.computer !== undefined);
|
|
97
98
|
if (agent.kind === "pod")
|
|
98
99
|
await requirePods(ctx.pods, `Agent ${agent.id}`);
|
|
99
100
|
// The model the version names (`harness.model`, Host feature `agent-model`) is the new
|
package/dist/tenant/sweep.js
CHANGED
|
@@ -328,7 +328,8 @@ async function reconcileStalePods(ctx, now) {
|
|
|
328
328
|
pod.observed === "deleting" ||
|
|
329
329
|
(pod.desired === "suspended" && pod.observed !== "suspended") ||
|
|
330
330
|
// Running, but its engine is not connected: the pod may be gone, or its volume.
|
|
331
|
-
(
|
|
331
|
+
// (A computer has no engine connection, ADR-149.)
|
|
332
|
+
(pod.desired === "running" && pod.observed === "running" && !ctx.pods?.computer && !ctx.harness.hosting(row.id)) ||
|
|
332
333
|
// Running past its idle limit: its idle timer is missing (or a turn holds it, which the
|
|
333
334
|
// reconcile leaves alone).
|
|
334
335
|
(pod.desired === "running" &&
|
|
@@ -231,7 +231,6 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
231
231
|
gate?: GateTimes | undefined;
|
|
232
232
|
policy?: ToolPolicyFact | undefined;
|
|
233
233
|
} | {
|
|
234
|
-
redacted?: undefined;
|
|
235
234
|
agent: import("@nylorun/core/define").AgentRef;
|
|
236
235
|
error: {
|
|
237
236
|
code: string;
|
|
@@ -241,6 +240,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
241
240
|
retryable?: boolean | undefined;
|
|
242
241
|
};
|
|
243
242
|
output?: undefined;
|
|
243
|
+
redacted?: undefined;
|
|
244
244
|
toolName: string;
|
|
245
245
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
246
246
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -254,7 +254,6 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
254
254
|
gate?: GateTimes | undefined;
|
|
255
255
|
policy?: ToolPolicyFact | undefined;
|
|
256
256
|
} | {
|
|
257
|
-
redacted?: undefined;
|
|
258
257
|
agent?: undefined;
|
|
259
258
|
error: {
|
|
260
259
|
code: string;
|
|
@@ -264,6 +263,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
264
263
|
retryable?: boolean | undefined;
|
|
265
264
|
};
|
|
266
265
|
output?: undefined;
|
|
266
|
+
redacted?: undefined;
|
|
267
267
|
toolName: string;
|
|
268
268
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
269
269
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -311,10 +311,10 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
311
311
|
gate?: GateTimes | undefined;
|
|
312
312
|
policy?: ToolPolicyFact | undefined;
|
|
313
313
|
} | {
|
|
314
|
-
redacted?: undefined;
|
|
315
314
|
agent: import("@nylorun/core/define").AgentRef;
|
|
316
315
|
error?: undefined;
|
|
317
316
|
output: unknown;
|
|
317
|
+
redacted?: undefined;
|
|
318
318
|
toolName: string;
|
|
319
319
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
320
320
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -328,10 +328,10 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
328
328
|
gate?: GateTimes | undefined;
|
|
329
329
|
policy?: ToolPolicyFact | undefined;
|
|
330
330
|
} | {
|
|
331
|
-
redacted?: undefined;
|
|
332
331
|
agent?: undefined;
|
|
333
332
|
error?: undefined;
|
|
334
333
|
output: unknown;
|
|
334
|
+
redacted?: undefined;
|
|
335
335
|
toolName: string;
|
|
336
336
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
337
337
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -395,7 +395,6 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
395
395
|
gate?: GateTimes | undefined;
|
|
396
396
|
policy?: ToolPolicyFact | undefined;
|
|
397
397
|
} | {
|
|
398
|
-
redacted?: undefined;
|
|
399
398
|
callId: string;
|
|
400
399
|
invocationId: string;
|
|
401
400
|
agent: import("@nylorun/core/define").AgentRef;
|
|
@@ -407,6 +406,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
407
406
|
retryable?: boolean | undefined;
|
|
408
407
|
};
|
|
409
408
|
output?: undefined;
|
|
409
|
+
redacted?: undefined;
|
|
410
410
|
toolName: string;
|
|
411
411
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
412
412
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -420,7 +420,6 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
420
420
|
gate?: GateTimes | undefined;
|
|
421
421
|
policy?: ToolPolicyFact | undefined;
|
|
422
422
|
} | {
|
|
423
|
-
redacted?: undefined;
|
|
424
423
|
callId: string;
|
|
425
424
|
invocationId: string;
|
|
426
425
|
agent?: undefined;
|
|
@@ -432,6 +431,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
432
431
|
retryable?: boolean | undefined;
|
|
433
432
|
};
|
|
434
433
|
output?: undefined;
|
|
434
|
+
redacted?: undefined;
|
|
435
435
|
toolName: string;
|
|
436
436
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
437
437
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -483,12 +483,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
483
483
|
gate?: GateTimes | undefined;
|
|
484
484
|
policy?: ToolPolicyFact | undefined;
|
|
485
485
|
} | {
|
|
486
|
-
redacted?: undefined;
|
|
487
486
|
callId: string;
|
|
488
487
|
invocationId: string;
|
|
489
488
|
agent: import("@nylorun/core/define").AgentRef;
|
|
490
489
|
error?: undefined;
|
|
491
490
|
output: unknown;
|
|
491
|
+
redacted?: undefined;
|
|
492
492
|
toolName: string;
|
|
493
493
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
494
494
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -502,12 +502,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
|
|
|
502
502
|
gate?: GateTimes | undefined;
|
|
503
503
|
policy?: ToolPolicyFact | undefined;
|
|
504
504
|
} | {
|
|
505
|
-
redacted?: undefined;
|
|
506
505
|
callId: string;
|
|
507
506
|
invocationId: string;
|
|
508
507
|
agent?: undefined;
|
|
509
508
|
error?: undefined;
|
|
510
509
|
output: unknown;
|
|
510
|
+
redacted?: undefined;
|
|
511
511
|
toolName: string;
|
|
512
512
|
status?: "cancelled" | "completed" | "failed" | undefined;
|
|
513
513
|
kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
|
|
@@ -36,6 +36,8 @@ export declare function nextFireAt(agentId: string, triggerId: string, trigger:
|
|
|
36
36
|
export declare function triggerIssues(triggers: Readonly<Record<string, Trigger>> | undefined, limits: RuntimeLimits, now?: Date,
|
|
37
37
|
/** How an issue names trigger `id`: its place in the manifest by default. */
|
|
38
38
|
label?: (id: string) => string): string[];
|
|
39
|
+
/** The triggers of the manifest version an Agent runs. */
|
|
40
|
+
export declare function triggersOf(t: Tx, agent: Environment): Promise<Record<string, Trigger>>;
|
|
39
41
|
/**
|
|
40
42
|
* The line an agent-made trigger's run starts with under the `nylorun-triggers` plugin (ADR-106):
|
|
41
43
|
* which trigger woke the turn, and that nobody is watching.
|
|
@@ -45,7 +47,7 @@ export declare function triggerFrame(triggerId: string, title: string | null | u
|
|
|
45
47
|
export declare function originOf(row: Pick<AgentTriggerRow, "origin" | "kind">): AgentTriggerRow["origin"];
|
|
46
48
|
/**
|
|
47
49
|
* The definition trigger row `row` fires with (D83): a booked wake's, from its session and note;
|
|
48
|
-
* an agent-made trigger's own, from the row; a manifest trigger's, the entry of the version the
|
|
50
|
+
* an agent-made or put trigger's own, from the row (D88); a manifest trigger's, the entry of the version the
|
|
49
51
|
* Agent runs (`manifest`, read once by the caller). Undefined when it has none.
|
|
50
52
|
*/
|
|
51
53
|
export declare function definitionOf(row: AgentTriggerRow, manifest: Readonly<Record<string, Trigger>>): Trigger | undefined;
|
|
@@ -113,9 +115,9 @@ export declare function setTriggerStatus(ctx: TenantContext, agentId: string, tr
|
|
|
113
115
|
triggers: TriggerView[];
|
|
114
116
|
}>;
|
|
115
117
|
/**
|
|
116
|
-
* `DELETE /v1/agents/{id}/triggers/{triggerId}` (D83): deletes a trigger one of the Agent's
|
|
117
|
-
* made (origin `agent`), with `agent.trigger_deleted`. A
|
|
118
|
-
* (`409 trigger_from_manifest`); its runs stay.
|
|
118
|
+
* `DELETE /v1/agents/{id}/triggers/{triggerId}` (D83, D88): deletes a trigger one of the Agent's
|
|
119
|
+
* agents made (origin `agent`) or a client put (origin `api`), with `agent.trigger_deleted`. A
|
|
120
|
+
* manifest's is the manifest's to drop (`409 trigger_from_manifest`); its runs stay.
|
|
119
121
|
*/
|
|
120
122
|
export declare function deleteTrigger(ctx: TenantContext, agentId: string, triggerId: string, principal?: string): Promise<void>;
|
|
121
123
|
/**
|
package/dist/tenant/triggers.js
CHANGED
|
@@ -164,7 +164,7 @@ label = (id) => `runtime.triggers.${id}`) {
|
|
|
164
164
|
return issues;
|
|
165
165
|
}
|
|
166
166
|
/** The triggers of the manifest version an Agent runs. */
|
|
167
|
-
async function triggersOf(t, agent) {
|
|
167
|
+
export async function triggersOf(t, agent) {
|
|
168
168
|
return (await pinnedOf(t, agent)).triggers;
|
|
169
169
|
}
|
|
170
170
|
/** What the manifest version an Agent runs says of triggers: its own, and whether it declares the capability. */
|
|
@@ -192,7 +192,7 @@ export function originOf(row) {
|
|
|
192
192
|
}
|
|
193
193
|
/**
|
|
194
194
|
* The definition trigger row `row` fires with (D83): a booked wake's, from its session and note;
|
|
195
|
-
* an agent-made trigger's own, from the row; a manifest trigger's, the entry of the version the
|
|
195
|
+
* an agent-made or put trigger's own, from the row (D88); a manifest trigger's, the entry of the version the
|
|
196
196
|
* Agent runs (`manifest`, read once by the caller). Undefined when it has none.
|
|
197
197
|
*/
|
|
198
198
|
export function definitionOf(row, manifest) {
|
|
@@ -200,13 +200,14 @@ export function definitionOf(row, manifest) {
|
|
|
200
200
|
case "wake":
|
|
201
201
|
return wakeTriggerOf(row);
|
|
202
202
|
case "agent":
|
|
203
|
+
case "api":
|
|
203
204
|
return (row.definition ?? undefined);
|
|
204
205
|
default:
|
|
205
206
|
return manifest[row.triggerId];
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
/** The columns a row of origin `manifest` or `wake` leaves empty. */
|
|
209
|
-
const NO_AGENT_FIELDS = { title: null, definition: null, createdSessionId: null, createdTurnId: null };
|
|
210
|
+
const NO_AGENT_FIELDS = { title: null, definition: null, createdSessionId: null, createdTurnId: null, principal: null };
|
|
210
211
|
/**
|
|
211
212
|
* Writes the state of a new Agent's triggers (D74): every trigger of the version it is made from
|
|
212
213
|
* starts active, with its next time. Arm them after the commit (`armAgentTriggers`).
|
|
@@ -386,8 +387,8 @@ export async function fireTimer(ctx, key) {
|
|
|
386
387
|
const trigger = definitionOf(row, pinned.triggers);
|
|
387
388
|
if (!trigger) {
|
|
388
389
|
// A manifest's that the version no longer has, or a wake with nothing to say. An agent's
|
|
389
|
-
// row always holds its definition.
|
|
390
|
-
if (origin !== "agent")
|
|
390
|
+
// or a put row always holds its definition.
|
|
391
|
+
if (origin !== "agent" && origin !== "api")
|
|
391
392
|
await t.deleteAgentTrigger(agentId, triggerId);
|
|
392
393
|
return undefined;
|
|
393
394
|
}
|
|
@@ -593,6 +594,7 @@ export function triggerView(row, trigger, origin = "") {
|
|
|
593
594
|
origin: from,
|
|
594
595
|
title: row.title,
|
|
595
596
|
createdSessionId: row.createdSessionId,
|
|
597
|
+
principal: row.principal,
|
|
596
598
|
status: row.status,
|
|
597
599
|
pausedReason: row.pausedReason,
|
|
598
600
|
...(trigger.schedule === undefined ? {} : { schedule: trigger.schedule, timezone: trigger.timezone ?? "UTC" }),
|
|
@@ -652,7 +654,11 @@ export async function setTriggerStatus(ctx, agentId, triggerId, action, origin =
|
|
|
652
654
|
const next = nextFireAt(agentId, triggerId, trigger, now, await readRuntimeLimits(t));
|
|
653
655
|
// An `at` runs once: one that ran, or whose time passed, does not run again.
|
|
654
656
|
if (trigger.at !== undefined && (next === null || row.status === "completed"))
|
|
655
|
-
fail(409, `Trigger ${triggerId} was due at ${trigger.at} and does not run again: ${originOf(row) === "agent"
|
|
657
|
+
fail(409, `Trigger ${triggerId} was due at ${trigger.at} and does not run again: ${originOf(row) === "agent"
|
|
658
|
+
? "the agent creates it again with a new time"
|
|
659
|
+
: originOf(row) === "api"
|
|
660
|
+
? "put it again with a new time"
|
|
661
|
+
: "save a manifest version with a new time"}.`, { code: "trigger_expired" });
|
|
656
662
|
const updated = {
|
|
657
663
|
...row,
|
|
658
664
|
status: "active",
|
|
@@ -670,19 +676,24 @@ export async function setTriggerStatus(ctx, agentId, triggerId, action, origin =
|
|
|
670
676
|
return listTriggers(ctx, agentId, origin);
|
|
671
677
|
}
|
|
672
678
|
/**
|
|
673
|
-
* `DELETE /v1/agents/{id}/triggers/{triggerId}` (D83): deletes a trigger one of the Agent's
|
|
674
|
-
* made (origin `agent`), with `agent.trigger_deleted`. A
|
|
675
|
-
* (`409 trigger_from_manifest`); its runs stay.
|
|
679
|
+
* `DELETE /v1/agents/{id}/triggers/{triggerId}` (D83, D88): deletes a trigger one of the Agent's
|
|
680
|
+
* agents made (origin `agent`) or a client put (origin `api`), with `agent.trigger_deleted`. A
|
|
681
|
+
* manifest's is the manifest's to drop (`409 trigger_from_manifest`); its runs stay.
|
|
676
682
|
*/
|
|
677
683
|
export async function deleteTrigger(ctx, agentId, triggerId, principal) {
|
|
678
684
|
await ctx.store.tx(async (t) => {
|
|
679
685
|
const { row } = await lockedTrigger(t, agentId, triggerId);
|
|
680
|
-
|
|
686
|
+
const origin = originOf(row);
|
|
687
|
+
if (origin !== "agent" && origin !== "api")
|
|
681
688
|
fail(409, `Trigger ${triggerId} is declared by the Agent's manifest: save a version without it, or pause it.`, {
|
|
682
689
|
code: "trigger_from_manifest",
|
|
683
690
|
});
|
|
684
691
|
await t.deleteAgentTrigger(agentId, triggerId);
|
|
685
|
-
await t.agentEvent(agentId, "agent.trigger_deleted", {
|
|
692
|
+
await t.agentEvent(agentId, "agent.trigger_deleted", {
|
|
693
|
+
triggerId,
|
|
694
|
+
origin: origin,
|
|
695
|
+
...(principal === undefined ? {} : { principal }),
|
|
696
|
+
});
|
|
686
697
|
});
|
|
687
698
|
}
|
|
688
699
|
/**
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RUNTIME_VERSION = "0.
|
|
1
|
+
export declare const RUNTIME_VERSION = "0.53.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.
|
|
3
|
+
export const RUNTIME_VERSION = "0.53.0-beta";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.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.8",
|
|
66
66
|
"@hono/zod-openapi": "^1.6.3",
|
|
67
67
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
68
|
-
"@nylorun/core": "0.
|
|
69
|
-
"@nylorun/harness": "0.36.
|
|
68
|
+
"@nylorun/core": "0.46.0-beta",
|
|
69
|
+
"@nylorun/harness": "0.36.6-beta",
|
|
70
70
|
"@restatedev/restate-sdk": "1.18.3",
|
|
71
71
|
"@restatedev/restate-sdk-clients": "1.18.3",
|
|
72
72
|
"@s2-dev/streamstore": "0.30.0",
|