@nylorun/runtime 0.40.0-beta → 0.42.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 +30 -0
- package/dist/adapters/streams/s2.js +9 -2
- package/dist/api/components.d.ts +131 -0
- package/dist/api/components.js +3 -1
- package/dist/api/http/app.js +2 -0
- package/dist/api/http/routes/runs.d.ts +8 -0
- package/dist/api/http/routes/runs.js +30 -0
- package/dist/api/http/routes/triggers.d.ts +2 -1
- package/dist/api/http/routes/triggers.js +32 -4
- package/dist/api/openapi.js +3 -1
- package/dist/gates/policy.d.ts +6 -4
- package/dist/gates/policy.js +10 -2
- package/dist/harness-api/record.js +51 -6
- package/dist/harness-api/start.js +15 -9
- package/dist/harness-api/tool-journal.js +3 -0
- package/dist/management-openapi.json +11 -6
- package/dist/openapi.json +550 -12
- package/dist/store/postgres/drizzle/0043_agent_triggers_origin.sql +11 -0
- package/dist/store/postgres/drizzle/0044_agent_triggers_origin_backfill.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +14 -0
- package/dist/store/postgres/schema.d.ts +151 -4
- package/dist/store/postgres/schema.js +25 -4
- package/dist/store/postgres/store.js +20 -2
- package/dist/store/types.d.ts +10 -0
- package/dist/streams/types.d.ts +16 -1
- package/dist/streams/types.js +8 -1
- package/dist/tenant/advance.js +7 -0
- package/dist/tenant/agent-triggers.d.ts +60 -0
- package/dist/tenant/agent-triggers.js +196 -0
- package/dist/tenant/agents.d.ts +1 -1
- package/dist/tenant/agents.js +5 -1
- package/dist/tenant/limits.d.ts +26 -7
- package/dist/tenant/limits.js +39 -6
- package/dist/tenant/runs-stream.d.ts +51 -0
- package/dist/tenant/runs-stream.js +164 -0
- package/dist/tenant/sweep.js +3 -0
- package/dist/tenant/triggers.d.ts +36 -5
- package/dist/tenant/triggers.js +183 -48
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -38,7 +38,8 @@ import { abortKind } from "../tenant/worker.js";
|
|
|
38
38
|
import { redactTokens } from "./redact.js";
|
|
39
39
|
import { toolCallDecision } from "../gates/policy.js";
|
|
40
40
|
import { traceKey } from "../traces/key.js";
|
|
41
|
-
import { serveRuntimeTool } from "../tenant/triggers.js";
|
|
41
|
+
import { armTrigger, serveRuntimeTool } from "../tenant/triggers.js";
|
|
42
|
+
import { serveTriggersTool, triggersRefusal } from "../tenant/agent-triggers.js";
|
|
42
43
|
import { serveMemoryTool } from "../memory/tool.js";
|
|
43
44
|
import { effectSpanOf, traceparentOf, turnTraceIdOf } from "../traces/ids.js";
|
|
44
45
|
import { firstSend, interrupted, settleToolRow, toolKindOf, } from "./tool-journal.js";
|
|
@@ -63,7 +64,10 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
63
64
|
const request = effect;
|
|
64
65
|
// Read before the transaction: the first read of the Tenant may create the key.
|
|
65
66
|
const key = await traceKey(ctx.store);
|
|
67
|
+
/** Triggers a `triggers` call wrote (D83): armed once the transaction commits. */
|
|
68
|
+
const toArm = [];
|
|
66
69
|
const answer = await ctx.store.tx(async (t) => {
|
|
70
|
+
toArm.length = 0;
|
|
67
71
|
const s = await ownedSession(t, lease, request.sessionId);
|
|
68
72
|
if (s.status === "cancelled" || s.activeTurnId !== request.turnId)
|
|
69
73
|
throw TURN_CANCELLED();
|
|
@@ -81,6 +85,13 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
81
85
|
// to join it or collect its outcome (P1.2).
|
|
82
86
|
if (existing.status === "invoking" && request.kind === "model" && recoversModelCalls(ctx))
|
|
83
87
|
return { status: "execute", rejoin: true };
|
|
88
|
+
// An operation the Runtime serves that a person approved while it was held (D83): served
|
|
89
|
+
// now, as the turn resumes, in this transaction; never resent.
|
|
90
|
+
if (existing.status === "invoking" &&
|
|
91
|
+
existing.request.kind === "tool" &&
|
|
92
|
+
existing.request.target?.kind === "runtime" &&
|
|
93
|
+
existing.approval?.approved === true)
|
|
94
|
+
return { status: "completed", outcome: await serveRuntimeRow(t, s, existing, scope, toArm) };
|
|
84
95
|
// A tool call a crash, a shutdown or a lost gate hop interrupted (R5.3, R5.4).
|
|
85
96
|
if (existing.status === "invoking" && existing.request.kind === "tool")
|
|
86
97
|
return resend(t, s, existing, recoversToolCalls(ctx));
|
|
@@ -141,12 +152,20 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
141
152
|
if (!agentManifest)
|
|
142
153
|
throw new Error(`Agent '${request.agent?.id ?? ""}' is not used as a tool`);
|
|
143
154
|
// An operation the Runtime serves itself (track T4): settled here, in the call's own
|
|
144
|
-
// transaction, keyed by its effect id, so a replay finds it completed.
|
|
155
|
+
// transaction, keyed by its effect id, so a replay finds it completed. The Tenant's tool
|
|
156
|
+
// policy decides it first (D83): a rule of kind `runtime` may deny it or hold it for a person.
|
|
145
157
|
if (request.kind === "tool" && request.target?.kind === "runtime") {
|
|
146
158
|
const row = { request, requestHash, status: "invoking", ...firstSend(toolKindOf(s, agentManifest, request)) };
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
159
|
+
// Off means off (ADR-100): a call the session's agent cannot make is refused unasked.
|
|
160
|
+
const unavailable = request.target.operation === "triggers" ? await triggersRefusal(t, s, request) : undefined;
|
|
161
|
+
if (unavailable) {
|
|
162
|
+
row.attempts = 0;
|
|
163
|
+
return { status: "completed", outcome: await settleToolRow(t, s, row, unavailable, { writer: coreIn(scope) }) };
|
|
164
|
+
}
|
|
165
|
+
const decided = await bypolicy(t, s, row, "runtime");
|
|
166
|
+
if (decided)
|
|
167
|
+
return decided;
|
|
168
|
+
return { status: "completed", outcome: await serveRuntimeRow(t, s, row, scope, toArm) };
|
|
150
169
|
}
|
|
151
170
|
const refused = request.kind === "tool" ? refusal(s, request) : undefined;
|
|
152
171
|
if (refused) {
|
|
@@ -182,12 +201,37 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
182
201
|
await t.put("effects", request.effectId, row);
|
|
183
202
|
return { status: "execute", ...(row.traceparent ? { traceparent: row.traceparent } : {}) };
|
|
184
203
|
});
|
|
204
|
+
// No I/O in the transaction: the triggers it wrote are armed now; the sweep arms what this misses.
|
|
205
|
+
for (const row of toArm)
|
|
206
|
+
await armTrigger(ctx, row).catch((error) => ctx.config.logger.warn("trigger not armed after its call; the sweep arms it", {
|
|
207
|
+
triggerId: row.triggerId,
|
|
208
|
+
message: error instanceof Error ? error.message : String(error),
|
|
209
|
+
}));
|
|
185
210
|
if (answer !== "flow")
|
|
186
211
|
return answer;
|
|
187
212
|
if (!isFlowEffect(request))
|
|
188
213
|
return { status: "pending" };
|
|
189
214
|
return resolveNewFlowEffect(ctx, request, signal, lease);
|
|
190
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Serves the `runtime` effect of tool row `row` in `t` and settles the row (track T4, D72, D83):
|
|
218
|
+
* `memory`, `schedule_wake` or `triggers`. Rows a `triggers` call wrote go on `toArm`.
|
|
219
|
+
*/
|
|
220
|
+
async function serveRuntimeRow(t, s, row, scope, toArm) {
|
|
221
|
+
const request = row.request;
|
|
222
|
+
const operation = request.target?.kind === "runtime" ? request.target.operation : undefined;
|
|
223
|
+
let served;
|
|
224
|
+
if (operation === "memory")
|
|
225
|
+
served = await serveMemoryTool(t, s, request);
|
|
226
|
+
else if (operation === "triggers") {
|
|
227
|
+
const result = await serveTriggersTool(t, s, request);
|
|
228
|
+
toArm.push(...result.arm);
|
|
229
|
+
served = result.outcome;
|
|
230
|
+
}
|
|
231
|
+
else
|
|
232
|
+
served = await serveRuntimeTool(t, s, request);
|
|
233
|
+
return settleToolRow(t, s, row, served, { writer: coreIn(scope) });
|
|
234
|
+
}
|
|
191
235
|
/**
|
|
192
236
|
* Why an agent's tool call cannot run, by what it names as its target (protocol 15), or
|
|
193
237
|
* undefined when it can:
|
|
@@ -199,7 +243,8 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
199
243
|
* crosses the Tool Gate;
|
|
200
244
|
* - `sandbox`: one of the sandbox operations, in a session with a sandbox;
|
|
201
245
|
* - `harness`: a tool the harness runs in its own process; its egress gate is its only control.
|
|
202
|
-
* - `runtime`: served by `recordIntent` itself, before this (`schedule_wake`, track T4
|
|
246
|
+
* - `runtime`: served by `recordIntent` itself, before this (`schedule_wake`, track T4; `memory`,
|
|
247
|
+
* track T5; `triggers`, D83), after the Tenant's tool policy.
|
|
203
248
|
*
|
|
204
249
|
* No target, and anything else, runs nothing (`tool.unavailable`).
|
|
205
250
|
*/
|
|
@@ -87,7 +87,7 @@ options = {}) {
|
|
|
87
87
|
...(memory ? { memory } : {}),
|
|
88
88
|
outcomes: current.checkpoint ? await bulkOutcomes(t, current, current.checkpoint) : [],
|
|
89
89
|
sandbox,
|
|
90
|
-
...(resource ? { agent: agentOf(resource, current.sandbox, await
|
|
90
|
+
...(resource ? { agent: agentOf(resource, current.sandbox, await pinnedEngineFacts(t, current)) } : {}),
|
|
91
91
|
...(key && turnId ? { trace: turnContextOf(key, ctx.store.tenantId, current, turnId) } : {}),
|
|
92
92
|
...(resource?.pod
|
|
93
93
|
? {
|
|
@@ -224,22 +224,28 @@ async function memoryRefreshOf(t, s) {
|
|
|
224
224
|
}
|
|
225
225
|
/**
|
|
226
226
|
* The facts of the Agent a session runs on (Harness API 5): its id and kind, and, with the
|
|
227
|
-
* session's sandbox, the workspace its commands start in
|
|
227
|
+
* session's sandbox, the workspace its commands start in; whether the version it runs books its
|
|
228
|
+
* own wakes and declares the triggers capability. The harness words them.
|
|
228
229
|
*/
|
|
229
|
-
function agentOf(resource, spec,
|
|
230
|
+
function agentOf(resource, spec, facts) {
|
|
230
231
|
return {
|
|
231
232
|
id: resource.id,
|
|
232
233
|
kind: resource.kind,
|
|
233
234
|
...(spec === undefined ? {} : { sandbox: { workspace: SANDBOX_WORKSPACE } }),
|
|
234
|
-
...(selfWake ? { selfWake: true } : {}),
|
|
235
|
+
...(facts.selfWake ? { selfWake: true } : {}),
|
|
236
|
+
...(facts.triggers ? { triggers: true } : {}),
|
|
235
237
|
};
|
|
236
238
|
}
|
|
237
|
-
/**
|
|
238
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Whether the manifest version the session runs sets `runtime.selfWake` (track T4, D77) and
|
|
241
|
+
* declares `harness.engine.triggers` (D83).
|
|
242
|
+
*/
|
|
243
|
+
async function pinnedEngineFacts(t, s) {
|
|
239
244
|
if (!s.definition)
|
|
240
|
-
return false;
|
|
241
|
-
const
|
|
242
|
-
|
|
245
|
+
return { selfWake: false, triggers: false };
|
|
246
|
+
const body = (await t.agentManifest(s.definition.name, s.definition.version))?.body;
|
|
247
|
+
const harness = body?.harness;
|
|
248
|
+
return { selfWake: body?.runtime?.selfWake === true, triggers: harness?.engine?.triggers === true };
|
|
243
249
|
}
|
|
244
250
|
/**
|
|
245
251
|
* The context of turn `turnId`'s span (R7.4): its trace (the caller's when its message carried a
|
|
@@ -25,6 +25,9 @@ export function toolKindOf(s, agentManifest, request) {
|
|
|
25
25
|
const host = httpHostOf(declaredHttpTool(s.manifest, request.agent?.id, target.tool)?.http.url);
|
|
26
26
|
return { toolKind: "http", ...(host === undefined ? {} : { server: host }), tool: target.tool };
|
|
27
27
|
}
|
|
28
|
+
// An operation the Runtime serves (D83): its identity is the operation, as the policy names it.
|
|
29
|
+
if (target?.kind === "runtime")
|
|
30
|
+
return { toolKind: "runtime", tool: target.operation };
|
|
28
31
|
const skill = request.skill !== undefined && agentManifest?.skills?.[request.skill] !== undefined
|
|
29
32
|
? request.skill
|
|
30
33
|
: undefined;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.2.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Nylorun Management API",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.42.0-beta",
|
|
6
6
|
"x-nylorun-protocol": {
|
|
7
7
|
"min": 16,
|
|
8
8
|
"max": 18,
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"turns",
|
|
43
43
|
"agent-model",
|
|
44
44
|
"secret-routing",
|
|
45
|
-
"remove-tools"
|
|
45
|
+
"remove-tools",
|
|
46
|
+
"agent-triggers"
|
|
46
47
|
]
|
|
47
48
|
},
|
|
48
49
|
"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`."
|
|
@@ -2561,8 +2562,10 @@
|
|
|
2561
2562
|
"type": "string",
|
|
2562
2563
|
"enum": [
|
|
2563
2564
|
"mcp",
|
|
2564
|
-
"http"
|
|
2565
|
-
|
|
2565
|
+
"http",
|
|
2566
|
+
"runtime"
|
|
2567
|
+
],
|
|
2568
|
+
"description": "mcp, http, or runtime: an operation the Runtime serves itself (triggers, memory, schedule_wake), which only a rule naming it matches"
|
|
2566
2569
|
},
|
|
2567
2570
|
"agent": {
|
|
2568
2571
|
"type": "string",
|
|
@@ -2663,8 +2666,10 @@
|
|
|
2663
2666
|
"type": "string",
|
|
2664
2667
|
"enum": [
|
|
2665
2668
|
"mcp",
|
|
2666
|
-
"http"
|
|
2667
|
-
|
|
2669
|
+
"http",
|
|
2670
|
+
"runtime"
|
|
2671
|
+
],
|
|
2672
|
+
"description": "mcp, http, or runtime: an operation the Runtime serves itself (triggers, memory, schedule_wake), which only a rule naming it matches"
|
|
2668
2673
|
},
|
|
2669
2674
|
"agent": {
|
|
2670
2675
|
"type": "string",
|