@nylorun/runtime 0.23.0-beta → 0.25.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 +252 -0
- package/README.md +87 -28
- package/dist/adapters/execution/restate.js +1 -1
- package/dist/adapters/replication/pgoutput.d.ts +2 -0
- package/dist/adapters/replication/pgoutput.js +6 -3
- package/dist/adapters/sandbox/local.d.ts +9 -1
- package/dist/adapters/sandbox/local.js +16 -4
- package/dist/adapters/sandbox/virtual.js +4 -1
- package/dist/api/a2a/card.d.ts +11 -1
- package/dist/api/a2a/card.js +26 -2
- package/dist/api/a2a/endpoint.js +9 -3
- package/dist/api/a2a/protocol.d.ts +25 -2
- package/dist/api/a2a/protocol.js +20 -3
- package/dist/api/a2a/routes.d.ts +6 -1
- package/dist/api/a2a/routes.js +67 -9
- package/dist/api/a2a/tasks.js +2 -1
- package/dist/api/ag-ui/endpoint.js +8 -3
- package/dist/api/ag-ui/history.d.ts +6 -1
- package/dist/api/ag-ui/history.js +39 -3
- package/dist/api/ag-ui/routes.d.ts +4 -1
- package/dist/api/ag-ui/routes.js +63 -20
- package/dist/api/ag-ui/translate.js +3 -2
- package/dist/api/components.d.ts +5327 -1091
- package/dist/api/components.js +40 -16
- package/dist/api/gate/keys.d.ts +2 -1
- package/dist/api/gate/keys.js +14 -1
- package/dist/api/gate/routes.d.ts +6 -0
- package/dist/api/gate/routes.js +52 -3
- package/dist/api/http/app.js +17 -6
- package/dist/api/http/body.d.ts +3 -3
- package/dist/api/http/body.js +7 -7
- package/dist/api/http/define.d.ts +6 -0
- package/dist/api/http/define.js +13 -1
- package/dist/api/http/respond.js +1 -1
- package/dist/api/http/routes/artifacts.js +35 -8
- package/dist/api/http/routes/coverage.d.ts +9 -0
- package/dist/api/http/routes/coverage.js +34 -0
- package/dist/api/http/routes/environment-manifests.d.ts +9 -0
- package/dist/api/http/routes/environment-manifests.js +118 -0
- package/dist/api/http/routes/environments.d.ts +11 -0
- package/dist/api/http/routes/environments.js +332 -0
- package/dist/api/http/routes/mcp.d.ts +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- package/dist/api/http/routes/me.js +1 -1
- package/dist/api/http/routes/reads.d.ts +94 -9
- package/dist/api/http/routes/reads.js +54 -5
- package/dist/api/http/routes/sessions.js +24 -9
- package/dist/api/http/routes/tenant.js +65 -15
- package/dist/api/inbound-files.d.ts +65 -0
- package/dist/api/inbound-files.js +115 -0
- package/dist/api/openapi.js +48 -28
- package/dist/artifacts/disk.d.ts +59 -0
- package/dist/artifacts/disk.js +569 -0
- package/dist/artifacts/export.d.ts +1 -1
- package/dist/artifacts/export.js +2 -3
- package/dist/artifacts/folders.d.ts +6 -3
- package/dist/artifacts/folders.js +17 -12
- package/dist/artifacts/media-types.d.ts +15 -0
- package/dist/artifacts/media-types.js +56 -4
- package/dist/artifacts/parts.d.ts +12 -3
- package/dist/artifacts/parts.js +15 -4
- package/dist/artifacts/service.d.ts +2 -1
- package/dist/artifacts/service.js +6 -1
- package/dist/artifacts/workspace.d.ts +5 -5
- package/dist/artifacts/workspace.js +7 -19
- package/dist/core/flow-host.d.ts +3 -4
- package/dist/core/flow-host.js +5 -2
- package/dist/core/provider.d.ts +3 -2
- package/dist/core/provider.js +5 -37
- package/dist/core/sandbox-routes.d.ts +2 -21
- package/dist/core/sandbox-routes.js +11 -44
- package/dist/core/turn-manifest.d.ts +3 -2
- package/dist/core/turn-manifest.js +29 -5
- package/dist/execution/types.d.ts +1 -1
- package/dist/gates/contract.d.ts +3 -24
- package/dist/gates/contract.js +1 -36
- package/dist/gates/egress-credentials.d.ts +9 -0
- package/dist/gates/egress-credentials.js +11 -4
- package/dist/gates/egress.d.ts +22 -1
- package/dist/gates/egress.js +116 -10
- package/dist/gates/fixture-model.d.ts +28 -0
- package/dist/gates/fixture-model.js +92 -0
- package/dist/gates/handler.js +4 -4
- package/dist/gates/http-tool.d.ts +9 -6
- package/dist/gates/http-tool.js +11 -17
- package/dist/gates/in-process.d.ts +5 -0
- package/dist/gates/in-process.js +12 -1
- package/dist/gates/journal.d.ts +30 -0
- package/dist/gates/journal.js +51 -0
- package/dist/gates/mcp-handler.d.ts +4 -0
- package/dist/gates/mcp-handler.js +7 -4
- package/dist/gates/meter.d.ts +10 -2
- package/dist/gates/meter.js +20 -5
- package/dist/gates/policy.d.ts +49 -0
- package/dist/gates/policy.js +132 -0
- package/dist/gates/tenant-vaults.d.ts +7 -7
- package/dist/gates/tenant-vaults.js +5 -5
- package/dist/gates/tool-calls.d.ts +10 -5
- package/dist/gates/tool-calls.js +28 -10
- package/dist/gates/tool-client.d.ts +8 -0
- package/dist/gates/tool-client.js +27 -11
- package/dist/gates/tool-contract.d.ts +11 -69
- package/dist/gates/tool-contract.js +1 -85
- package/dist/gates/tool-gate.d.ts +1 -1
- package/dist/harness/calls.d.ts +14 -7
- package/dist/harness/calls.js +30 -24
- package/dist/harness/executors.js +12 -9
- package/dist/harness/main.js +24 -5
- package/dist/harness/pod.d.ts +39 -0
- package/dist/harness/pod.js +46 -0
- package/dist/harness/service.d.ts +0 -1
- package/dist/harness/service.js +17 -9
- package/dist/harness/setup.d.ts +29 -0
- package/dist/harness/setup.js +174 -0
- package/dist/harness/ws-client.js +3 -2
- package/dist/harness/ws-port.d.ts +1 -5
- package/dist/harness/ws-port.js +1 -5
- package/dist/harness-api/in-process.js +3 -3
- package/dist/harness-api/record.d.ts +15 -1
- package/dist/harness-api/record.js +180 -50
- package/dist/harness-api/redact.d.ts +3 -0
- package/dist/harness-api/redact.js +45 -0
- package/dist/harness-api/server.d.ts +35 -4
- package/dist/harness-api/server.js +130 -17
- package/dist/harness-api/start.d.ts +10 -3
- package/dist/harness-api/start.js +40 -10
- package/dist/harness-api/tool-journal.d.ts +105 -0
- package/dist/harness-api/tool-journal.js +132 -0
- package/dist/harness-api/tool-status.d.ts +13 -0
- package/dist/harness-api/tool-status.js +26 -0
- package/dist/harness-api/workspace.d.ts +12 -7
- package/dist/harness-api/workspace.js +47 -25
- package/dist/harness-api/ws-server.d.ts +11 -3
- package/dist/harness-api/ws-server.js +94 -15
- package/dist/host/gates.d.ts +2 -1
- package/dist/host/gates.js +10 -2
- package/dist/host/main.js +54 -5
- package/dist/host/operate.js +67 -5
- package/dist/host/stack-config.d.ts +64 -0
- package/dist/host/stack-config.js +62 -2
- package/dist/keys/client.js +4 -1
- package/dist/keys/keys.d.ts +13 -6
- package/dist/keys/keys.js +9 -8
- package/dist/management-openapi.json +1264 -2171
- package/dist/mcp/connect.d.ts +26 -22
- package/dist/mcp/connect.js +30 -34
- package/dist/mcp/pool.d.ts +3 -6
- package/dist/mcp/pool.js +14 -28
- package/dist/mcp/preview.d.ts +3 -3
- package/dist/mcp/preview.js +10 -21
- package/dist/mcp/snapshot.d.ts +16 -24
- package/dist/mcp/snapshot.js +33 -45
- package/dist/model/capabilities.d.ts +26 -0
- package/dist/model/capabilities.js +44 -0
- package/dist/model/catalog.d.ts +34 -3
- package/dist/model/catalog.js +38 -2
- package/dist/model/models.d.ts +2 -0
- package/dist/model/models.js +1 -1
- package/dist/model/native-blocks.d.ts +35 -0
- package/dist/model/native-blocks.js +170 -0
- package/dist/model/pi-model.js +114 -94
- package/dist/openapi.json +12822 -3943
- package/dist/reads/types.d.ts +16 -3
- package/dist/record/append.d.ts +3 -1
- package/dist/record/append.js +1 -0
- package/dist/record/envelope.d.ts +5 -1
- package/dist/record/envelope.js +7 -3
- package/dist/record/environment.d.ts +25 -0
- package/dist/record/environment.js +44 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/sandbox/disk-fs.d.ts +95 -0
- package/dist/sandbox/disk-fs.js +206 -0
- package/dist/sandbox/join.d.ts +17 -18
- package/dist/sandbox/join.js +46 -15
- package/dist/sandbox/manager.d.ts +47 -21
- package/dist/sandbox/manager.js +162 -107
- package/dist/sandbox/placement.d.ts +14 -13
- package/dist/sandbox/placement.js +31 -14
- package/dist/sandbox/pods/client.d.ts +9 -2
- package/dist/sandbox/pods/lifecycle.d.ts +36 -27
- package/dist/sandbox/pods/lifecycle.js +29 -26
- package/dist/sandbox/pods/reconcile.js +18 -12
- package/dist/sandbox/pods/spec.d.ts +15 -13
- package/dist/sandbox/pods/spec.js +17 -20
- package/dist/sandbox/records.d.ts +2 -2
- package/dist/sandbox/records.js +3 -3
- package/dist/sandbox/resolve.d.ts +37 -27
- package/dist/sandbox/resolve.js +73 -41
- package/dist/sandbox/runtime-tools.d.ts +9 -0
- package/dist/sandbox/runtime-tools.js +16 -0
- package/dist/sandbox/session-sandbox.d.ts +4 -6
- package/dist/sandbox/session-sandbox.js +47 -94
- package/dist/sandbox/setup.d.ts +64 -0
- package/dist/sandbox/setup.js +160 -0
- package/dist/sandbox/share.d.ts +21 -26
- package/dist/sandbox/share.js +36 -63
- package/dist/sandbox/skills.d.ts +8 -6
- package/dist/sandbox/skills.js +24 -23
- package/dist/sandbox/tenant-config.d.ts +12 -14
- package/dist/sandbox/tenant-config.js +12 -17
- package/dist/sandbox/types.d.ts +6 -0
- package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
- package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
- package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
- package/dist/store/postgres/drizzle/0024_flat_manifest.sql +7 -0
- package/dist/store/postgres/drizzle/0025_environment_manifests.sql +14 -0
- package/dist/store/postgres/drizzle/0026_environment_forks.sql +1 -0
- package/dist/store/postgres/drizzle/0027_environment_image_digest.sql +1 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +56 -0
- package/dist/store/postgres/reads.js +123 -17
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/record.d.ts +7 -0
- package/dist/store/postgres/record.js +23 -2
- package/dist/store/postgres/schema.d.ts +1126 -736
- package/dist/store/postgres/schema.js +136 -73
- package/dist/store/postgres/store.js +195 -67
- package/dist/store/types.d.ts +165 -50
- package/dist/tenant/advance.d.ts +7 -6
- package/dist/tenant/advance.js +118 -42
- package/dist/tenant/artifact-tool.d.ts +1 -1
- package/dist/tenant/artifact-tool.js +46 -26
- package/dist/tenant/commands.d.ts +15 -2
- package/dist/tenant/commands.js +115 -14
- package/dist/tenant/context.d.ts +56 -24
- package/dist/tenant/context.js +4 -15
- package/dist/tenant/effects.d.ts +1 -1
- package/dist/tenant/effects.js +44 -29
- package/dist/tenant/environment-manifests.d.ts +57 -0
- package/dist/tenant/environment-manifests.js +200 -0
- package/dist/tenant/environments.d.ts +174 -0
- package/dist/tenant/environments.js +1044 -0
- package/dist/tenant/harness-tokens.d.ts +21 -0
- package/dist/tenant/harness-tokens.js +61 -0
- package/dist/tenant/host-token.d.ts +1 -1
- package/dist/tenant/host-token.js +2 -4
- package/dist/tenant/identity-file.d.ts +4 -4
- package/dist/tenant/identity-file.js +17 -8
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/model-setting.d.ts +14 -0
- package/dist/tenant/model-setting.js +51 -0
- package/dist/tenant/principals.js +1 -1
- package/dist/tenant/reset.js +3 -3
- package/dist/tenant/runtime.d.ts +6 -1
- package/dist/tenant/runtime.js +7 -1
- package/dist/tenant/session.d.ts +3 -3
- package/dist/tenant/session.js +0 -14
- package/dist/tenant/sessions.d.ts +18 -14
- package/dist/tenant/sessions.js +99 -143
- package/dist/tenant/signing-keys.d.ts +5 -1
- package/dist/tenant/skill-tool.d.ts +3 -3
- package/dist/tenant/skill-tool.js +6 -11
- package/dist/tenant/status.js +3 -3
- package/dist/tenant/sweep.js +13 -7
- package/dist/tenant/tool-results.js +1 -1
- package/dist/tenant/tool-search.d.ts +7 -11
- package/dist/tenant/tool-search.js +8 -5
- package/dist/tenant/transcript.d.ts +245 -36
- package/dist/tenant/transcript.js +64 -12
- package/dist/tenant/turn-timing.d.ts +43 -0
- package/dist/tenant/turn-timing.js +39 -0
- package/dist/tenant/types.d.ts +2 -0
- package/dist/traces/ids.d.ts +41 -0
- package/dist/traces/ids.js +81 -0
- package/dist/traces/key.d.ts +6 -0
- package/dist/traces/key.js +37 -0
- package/dist/traces/map.d.ts +113 -0
- package/dist/traces/map.js +289 -0
- package/dist/traces/options.d.ts +7 -0
- package/dist/traces/options.js +1 -0
- package/dist/traces/otlp.d.ts +42 -0
- package/dist/traces/otlp.js +84 -0
- package/dist/traces/projector.d.ts +58 -0
- package/dist/traces/projector.js +200 -0
- package/dist/traces/read.d.ts +23 -0
- package/dist/traces/read.js +16 -0
- package/dist/vault/coverage.d.ts +26 -0
- package/dist/vault/coverage.js +62 -0
- package/dist/vault/error.d.ts +11 -1
- package/dist/vault/error.js +5 -1
- package/dist/vault/host-model.d.ts +4 -1
- package/dist/vault/host-model.js +10 -2
- package/dist/vault/service.d.ts +92 -48
- package/dist/vault/service.js +462 -376
- package/dist/vault/sources.d.ts +5 -16
- package/dist/vault/sources.js +6 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/package.json +13 -13
- package/dist/api/http/routes/sandboxes.d.ts +0 -9
- package/dist/api/http/routes/sandboxes.js +0 -153
- package/dist/api/http/routes/vaults.d.ts +0 -10
- package/dist/api/http/routes/vaults.js +0 -139
- package/dist/record/sandbox.d.ts +0 -25
- package/dist/record/sandbox.js +0 -43
- package/dist/sandbox/capability.d.ts +0 -3
- package/dist/sandbox/capability.js +0 -10
- package/dist/tenant/sandboxes.d.ts +0 -123
- package/dist/tenant/sandboxes.js +0 -566
package/dist/tenant/commands.js
CHANGED
|
@@ -22,19 +22,32 @@ import { resolveMessageManifest } from "../core/turn-manifest.js";
|
|
|
22
22
|
import { canonical } from "../store/canonical.js";
|
|
23
23
|
import { lockedSession, } from "./context.js";
|
|
24
24
|
import { fail } from "./http.js";
|
|
25
|
+
import { settleToolRow } from "../harness-api/tool-journal.js";
|
|
25
26
|
import { accessOf } from "./auth.js";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
27
|
+
import { checkEnvironmentTurn } from "./environments.js";
|
|
28
|
+
import { turnManifestOf, variantStore } from "./session.js";
|
|
28
29
|
import { slimModelEffects } from "./slim.js";
|
|
29
30
|
import { resolveMessageParts } from "../artifacts/parts.js";
|
|
31
|
+
import { currentModelInputs } from "../model/catalog.js";
|
|
32
|
+
import { endTurn, resumeTurn, startTurn } from "./turn-timing.js";
|
|
30
33
|
/** A command's identity for idempotency: everything but the request id. */
|
|
31
34
|
const semantic = (value) => {
|
|
32
35
|
const { requestId: _, ...body } = value;
|
|
33
36
|
return canonical(body);
|
|
34
37
|
};
|
|
35
|
-
export async function command(ctx, id, input, scope
|
|
38
|
+
export async function command(ctx, id, input, scope,
|
|
39
|
+
/**
|
|
40
|
+
* The W3C trace context the request carried (R7.4): a `message` records it on its
|
|
41
|
+
* `command.message`, and the turn joins the caller's trace. Not part of the command's identity.
|
|
42
|
+
*/
|
|
43
|
+
options = {}) {
|
|
36
44
|
const { store } = ctx;
|
|
37
45
|
const cascadeCancelIds = [];
|
|
46
|
+
// What the Tenant's model reads, for a message that names a file (protocol 12): read before
|
|
47
|
+
// the session's transaction. Undefined without a model in the vault: nothing is refused.
|
|
48
|
+
const inputs = input.type === "message" && "parts" in input && input.parts.some((part) => part.type === "file")
|
|
49
|
+
? await currentModelInputs(ctx)
|
|
50
|
+
: undefined;
|
|
38
51
|
const response = await store.tx(async (t) => {
|
|
39
52
|
const s = await lockedSession(t, id, accessOf(scope));
|
|
40
53
|
const command = input;
|
|
@@ -53,6 +66,8 @@ export async function command(ctx, id, input, scope) {
|
|
|
53
66
|
return existing.response;
|
|
54
67
|
}
|
|
55
68
|
let event;
|
|
69
|
+
// A tool call the Tool Gate held, which this `approve` answers (R5 step 2).
|
|
70
|
+
let held;
|
|
56
71
|
// The turn a `cancel` ended, if one was active.
|
|
57
72
|
let cancelledTurnId = null;
|
|
58
73
|
if (command.type === "cancel") {
|
|
@@ -73,15 +88,24 @@ export async function command(ctx, id, input, scope) {
|
|
|
73
88
|
turnId: cancelledTurnId,
|
|
74
89
|
});
|
|
75
90
|
if (cancelledTurnId !== null)
|
|
76
|
-
for (const effect of await t.effectsForTurn(id, cancelledTurnId, ["invoking"])) {
|
|
91
|
+
for (const effect of await t.effectsForTurn(id, cancelledTurnId, ["invoking", "held"])) {
|
|
92
|
+
// A tool call ends `cancelled` with its `tool.completed` (R5.1); it may have run.
|
|
93
|
+
if (effect.request.kind === "tool") {
|
|
94
|
+
await settleToolRow(t, s, effect, {
|
|
95
|
+
kind: "failed",
|
|
96
|
+
code: "tool.cancelled",
|
|
97
|
+
message: "The turn was cancelled while this call ran; it may have run.",
|
|
98
|
+
}, { cancelled: true });
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
77
101
|
effect.status = "uncertain";
|
|
78
102
|
effect.error =
|
|
79
103
|
"Turn cancelled before model outcome was durably recorded";
|
|
80
104
|
await t.put("effects", effect.request.effectId, effect);
|
|
81
105
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
106
|
+
// The cancelled turn's span (R7.1): its duration, agent and totals.
|
|
107
|
+
const ended = cancelledTurnId !== null ? await endTurn(t, s, cancelledTurnId) : {};
|
|
108
|
+
event = await t.event(id, cancelledTurnId, "turn.cancelled", { reason: command.reason, ...ended }, commandWriter(scope));
|
|
85
109
|
await slimModelEffects(t, id, cancelledTurnId);
|
|
86
110
|
// The process running the advance aborts it on this signal (control.ts), sent at commit.
|
|
87
111
|
await t.signal({
|
|
@@ -110,21 +134,43 @@ export async function command(ctx, id, input, scope) {
|
|
|
110
134
|
}
|
|
111
135
|
}
|
|
112
136
|
else {
|
|
137
|
+
// A session opened before protocol 11 (migration 0020) or 13 (migration 0024) keeps its
|
|
138
|
+
// history but takes no turn.
|
|
139
|
+
if (s.closedReason !== undefined)
|
|
140
|
+
fail(409, s.closedReason === "protocol_11"
|
|
141
|
+
? "This session was opened before protocol 11: open a new session that names an Environment."
|
|
142
|
+
: "This session was opened before protocol 13, on a manifest the Runtime no longer runs: register the agent again (manifest 7) and open a new session.", { code: "session_closed" });
|
|
113
143
|
if (command.type === "message") {
|
|
114
144
|
if (!["idle", "completed", "failed", "cancelled"].includes(s.status))
|
|
115
145
|
fail(409, "Session has active or unresolved work");
|
|
116
146
|
// Every turn start on a sandbox: it exists, the token's grants reach it, and no other
|
|
117
147
|
// session's turn holds it.
|
|
118
|
-
await
|
|
148
|
+
await checkEnvironmentTurn(t, s, scope, ctx);
|
|
119
149
|
s.turnStartState = s.state;
|
|
120
150
|
// A new turn folds from the latest snapshot: a cancel or failure only reverts this turn.
|
|
121
151
|
if (s.history?.snapshot !== undefined)
|
|
122
152
|
s.history = { ...s.history, from: s.history.snapshot };
|
|
123
153
|
s.activeTurnId = randomUUID();
|
|
124
|
-
|
|
154
|
+
startTurn(s);
|
|
155
|
+
if (options.trace)
|
|
156
|
+
s.turnTrace = options.trace;
|
|
157
|
+
else
|
|
158
|
+
delete s.turnTrace;
|
|
159
|
+
// File parts name artifacts the caller may read; each is pinned to a version now. An
|
|
160
|
+
// agent's model must read each one (a workflow passes them on as data).
|
|
125
161
|
const parts = "parts" in command
|
|
126
|
-
? await resolveMessageParts(t, command.parts, id, accessOf(scope))
|
|
162
|
+
? await resolveMessageParts(t, command.parts, id, accessOf(scope), isWorkflowManifest(s.manifest) ? undefined : inputs)
|
|
127
163
|
: undefined;
|
|
164
|
+
if (parts && parts.refused.length > 0)
|
|
165
|
+
fail(422, `The model does not read ${parts.refused
|
|
166
|
+
.map((part) => `${part.name} (${part.reason})`)
|
|
167
|
+
.join(", ")}.`, { code: "attachment_unsupported", details: { parts: parts.refused } });
|
|
168
|
+
// The files the session's messages named: `read_artifact` reads them again (protocol 12).
|
|
169
|
+
if (parts) {
|
|
170
|
+
const named = parts.pinned.flatMap((part) => (part.type === "file" ? [part.artifactId] : []));
|
|
171
|
+
if (named.length > 0)
|
|
172
|
+
s.fileArtifacts = [...new Set([...(s.fileArtifacts ?? []), ...named])];
|
|
173
|
+
}
|
|
128
174
|
const data = "data" in command ? command.data : undefined;
|
|
129
175
|
if (isWorkflowManifest(s.manifest)) {
|
|
130
176
|
const input = "content" in command
|
|
@@ -151,7 +197,6 @@ export async function command(ctx, id, input, scope) {
|
|
|
151
197
|
fail(400, resolved.message);
|
|
152
198
|
}
|
|
153
199
|
else {
|
|
154
|
-
rebaseSessionState(s, resolved.hash);
|
|
155
200
|
s.checkpoint = createDurableCheckpoint({
|
|
156
201
|
manifest: resolved.manifest,
|
|
157
202
|
sessionId: id,
|
|
@@ -169,6 +214,25 @@ export async function command(ctx, id, input, scope) {
|
|
|
169
214
|
}
|
|
170
215
|
}
|
|
171
216
|
}
|
|
217
|
+
else if (command.type === "approve" &&
|
|
218
|
+
(held = await heldCall(t, s, command.interactionId)) !== undefined) {
|
|
219
|
+
// A tool call the Tool Gate held for approval (R5 step 2). Approved: the replay sends it
|
|
220
|
+
// again and the gate runs it. Denied: it fails to the model as `tool.denied`, unsent.
|
|
221
|
+
if (s.status !== "paused")
|
|
222
|
+
fail(409, "Session is not awaiting a response");
|
|
223
|
+
if (command.approved) {
|
|
224
|
+
held.status = "invoking";
|
|
225
|
+
const by = principalOf(scope);
|
|
226
|
+
held.approval = { approved: true, at: new Date().toISOString(), ...(by ? { by } : {}) };
|
|
227
|
+
await t.put("effects", held.request.effectId, held);
|
|
228
|
+
}
|
|
229
|
+
else
|
|
230
|
+
await settleToolRow(t, s, held, {
|
|
231
|
+
kind: "failed",
|
|
232
|
+
code: "tool.denied",
|
|
233
|
+
message: "A person denied this call; it was not sent.",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
172
236
|
else if (isWorkflowManifest(s.manifest)) {
|
|
173
237
|
// Approvals owned by linked agent sessions must be answered there (WF-R52).
|
|
174
238
|
const conflict = await foreignInteractionConflict({
|
|
@@ -221,10 +285,13 @@ export async function command(ctx, id, input, scope) {
|
|
|
221
285
|
segment: (s.checkpoint?.segment ?? 0) + 1,
|
|
222
286
|
});
|
|
223
287
|
}
|
|
288
|
+
// A paused turn goes on: its wait for a person is not its running time (R7.1).
|
|
289
|
+
resumeTurn(s);
|
|
224
290
|
s.status = "runnable";
|
|
225
291
|
s.waits = undefined;
|
|
226
|
-
// A flow answers each interaction in the same segment
|
|
227
|
-
|
|
292
|
+
// A flow answers each interaction in the same segment, as a held tool call's approval is
|
|
293
|
+
// answered (R5 step 2): the interaction names the cause.
|
|
294
|
+
const cause = command.type !== "message" && (isWorkflowManifest(s.manifest) || held !== undefined)
|
|
228
295
|
? command.interactionId
|
|
229
296
|
: s.checkpoint.segment;
|
|
230
297
|
const commandWake = {
|
|
@@ -232,7 +299,7 @@ export async function command(ctx, id, input, scope) {
|
|
|
232
299
|
dedupeKey: `${command.type}:${s.activeTurnId}:${cause}`,
|
|
233
300
|
};
|
|
234
301
|
await t.wake(id, commandWake);
|
|
235
|
-
event = await t.event(id, s.activeTurnId, `command.${command.type}`, command);
|
|
302
|
+
event = await t.event(id, s.activeTurnId, `command.${command.type}`, command.type === "message" && options.trace ? { ...command, trace: options.trace } : command, commandWriter(scope));
|
|
236
303
|
}
|
|
237
304
|
await t.put("sessions", id, s);
|
|
238
305
|
// Direct cancel of a linked agent fails that node with agent.cancelled (WF-R54). The agent
|
|
@@ -272,3 +339,37 @@ export async function command(ctx, id, input, scope) {
|
|
|
272
339
|
}
|
|
273
340
|
return response;
|
|
274
341
|
}
|
|
342
|
+
/** Core's own commands (R4.9): the flow host's, and a drain's cancels. */
|
|
343
|
+
const CORE_PRINCIPALS = { "flow-host": "flow-host", drain: "core" };
|
|
344
|
+
/**
|
|
345
|
+
* Who sent a command (R4.9), from its credential and never its body: a client over the Runtime
|
|
346
|
+
* API (an application, or a person through it or a token), or core itself.
|
|
347
|
+
*/
|
|
348
|
+
export function commandWriter(scope) {
|
|
349
|
+
if (scope.kind === "application" && CORE_PRINCIPALS[scope.principalId] !== undefined)
|
|
350
|
+
return {
|
|
351
|
+
source: { kind: "core", id: "runtime" },
|
|
352
|
+
principal: { kind: CORE_PRINCIPALS[scope.principalId], id: scope.principalId },
|
|
353
|
+
};
|
|
354
|
+
const principal = scope.kind === "subject" || scope.kind === "token"
|
|
355
|
+
? { kind: "subject", id: scope.subject }
|
|
356
|
+
: { kind: "application", id: scope.kind === "anonymous" ? "anonymous" : scope.principalId };
|
|
357
|
+
return { source: { kind: "client", id: principal.id }, principal };
|
|
358
|
+
}
|
|
359
|
+
/** The held tool call of the session's active turn that `interactionId` names (R5 step 2). */
|
|
360
|
+
async function heldCall(t, s, interactionId) {
|
|
361
|
+
const row = await t.get("effects", interactionId);
|
|
362
|
+
if (row?.status !== "held")
|
|
363
|
+
return undefined;
|
|
364
|
+
if (row.request.sessionId !== s.id || row.request.turnId !== s.activeTurnId)
|
|
365
|
+
return undefined;
|
|
366
|
+
return row;
|
|
367
|
+
}
|
|
368
|
+
/** Who answered: the principal (its subject, when it acts for one), or the token's subject. */
|
|
369
|
+
function principalOf(scope) {
|
|
370
|
+
if (scope.kind === "token")
|
|
371
|
+
return `${scope.issuer}:${scope.subject}`;
|
|
372
|
+
if (scope.kind === "subject")
|
|
373
|
+
return `${scope.principalId}:${scope.subject}`;
|
|
374
|
+
return "principalId" in scope ? scope.principalId : undefined;
|
|
375
|
+
}
|
package/dist/tenant/context.d.ts
CHANGED
|
@@ -18,7 +18,6 @@ import type { SessionHistory } from "./history.js";
|
|
|
18
18
|
import type { IssuerScope, SubjectScope, TenantEnvelope } from "@nylorun/core/contracts";
|
|
19
19
|
import type { DurableCheckpoint, FlowCheckpoint } from "@nylorun/harness/run";
|
|
20
20
|
import type { AgentManifest, JsonValue, SandboxManifest } from "@nylorun/core/define";
|
|
21
|
-
import type { CredentialSelection } from "@nylorun/core/contracts";
|
|
22
21
|
import type { OutboxRequest, SessionStore, Tx } from "../store/types.js";
|
|
23
22
|
import type { FlowLimits } from "../core/limits.js";
|
|
24
23
|
import type { ModelProvider } from "../core/provider.js";
|
|
@@ -40,6 +39,8 @@ import type { SigningKeys } from "./signing-keys.js";
|
|
|
40
39
|
import type { BlobStore } from "../blob/index.js";
|
|
41
40
|
import type { WorkspaceReader } from "../artifacts/workspace.js";
|
|
42
41
|
import type { RunGrants } from "./run-grants.js";
|
|
42
|
+
import type { TraceOptions } from "../traces/options.js";
|
|
43
|
+
import type { TraceContext } from "../traces/ids.js";
|
|
43
44
|
export interface Session {
|
|
44
45
|
id: string;
|
|
45
46
|
agentId: string;
|
|
@@ -49,7 +50,7 @@ export interface Session {
|
|
|
49
50
|
manifestHash: string;
|
|
50
51
|
/**
|
|
51
52
|
* The hash of the definition the session was opened from; `manifestHash` differs when the
|
|
52
|
-
* Runtime pinned its
|
|
53
|
+
* Runtime pinned its `runtime` block on it (R2b C11). Absent on older sessions.
|
|
53
54
|
*/
|
|
54
55
|
definitionHash?: string;
|
|
55
56
|
/** Validated turn-manifest variants, keyed by hash (loops.md §4.2). */
|
|
@@ -74,26 +75,55 @@ export interface Session {
|
|
|
74
75
|
* turn it started (`linkedTurnEnd` in `core/flow-host.ts`).
|
|
75
76
|
*/
|
|
76
77
|
lastTurnId?: string;
|
|
78
|
+
/** When the active turn started (R7.1, `turn-timing.ts`); absent on a turn started before. */
|
|
79
|
+
turnStartedAt?: string;
|
|
80
|
+
/** How long the active turn has waited paused for a person, in ms (R7.1). */
|
|
81
|
+
pausedMs?: number;
|
|
82
|
+
/** Since when the active turn waits paused (R7.1); absent while it runs. */
|
|
83
|
+
pausedAt?: string;
|
|
84
|
+
/**
|
|
85
|
+
* The trace context the active turn's message arrived with (R7.4): its trace is the caller's,
|
|
86
|
+
* and the context core hands on (to the harness, tools and sandbox commands) is in it.
|
|
87
|
+
*/
|
|
88
|
+
turnTrace?: TraceContext;
|
|
77
89
|
creation: unknown;
|
|
78
|
-
vaultIds?: readonly string[];
|
|
79
|
-
credentialSelections?: readonly CredentialSelection[];
|
|
80
90
|
mcpSnapshot?: McpSnapshot;
|
|
81
91
|
mcpDiagnostics?: readonly McpDiagnostic[];
|
|
82
|
-
/** Session id that keys the shared sandbox; absent means this session owns it. */
|
|
83
|
-
sandboxOwnerId?: string;
|
|
84
92
|
/**
|
|
85
|
-
* The
|
|
86
|
-
*
|
|
93
|
+
* The Environment the session names (`environment`, protocol 11): its sandbox is the
|
|
94
|
+
* session's (the Environment's id keys the workspace) and its vault holds the session's
|
|
95
|
+
* credentials. It outlives the session.
|
|
96
|
+
*/
|
|
97
|
+
environmentId?: string;
|
|
98
|
+
/** The Environment's recipe version when the session opened (shown, never enforced). */
|
|
99
|
+
environmentVersion?: number;
|
|
100
|
+
/**
|
|
101
|
+
* The harness that runs the session's turns (R4.1), pinned when it opened: its Environment's
|
|
102
|
+
* harness, else `nylorun`. Absent on a session opened before R4: ours (`harnessOf`).
|
|
87
103
|
*/
|
|
88
|
-
|
|
104
|
+
harness?: string;
|
|
89
105
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
106
|
+
* A flow agent's linked session: the session whose turn opened the flow tree. Its sessions
|
|
107
|
+
* share the tree's Environment, and their turns nest in the root's, so they do not hold the
|
|
108
|
+
* Environment against each other (`checkEnvironmentTurn`).
|
|
109
|
+
*/
|
|
110
|
+
flowRootId?: string;
|
|
111
|
+
/**
|
|
112
|
+
* The Environment's sandbox spec when the session opened (kinds `virtual` and `pod`).
|
|
113
|
+
* Absent without an Environment, or for kind `none`.
|
|
93
114
|
*/
|
|
94
115
|
sandbox?: SandboxManifest;
|
|
95
|
-
/**
|
|
96
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Why the session takes no new turn: `protocol_11` for a session opened before protocol 11
|
|
118
|
+
* (migration 0020), `protocol_13` for one opened before protocol 13 (migration 0024). Its
|
|
119
|
+
* history stays readable.
|
|
120
|
+
*/
|
|
121
|
+
closedReason?: string;
|
|
122
|
+
/**
|
|
123
|
+
* The artifacts the session's messages named (protocol 12): `read_artifact` reads these
|
|
124
|
+
* Tenant-wide ones beside the session's own. Absent until a message names a file.
|
|
125
|
+
*/
|
|
126
|
+
fileArtifacts?: string[];
|
|
97
127
|
}
|
|
98
128
|
export type AuthScope = {
|
|
99
129
|
kind: "application";
|
|
@@ -110,7 +140,7 @@ export type AuthScope = {
|
|
|
110
140
|
}
|
|
111
141
|
/**
|
|
112
142
|
* A trusted issuer's token (Host feature `trusted-issuers`, F9-D12): one subject, with the
|
|
113
|
-
* issuer's scopes, agents and
|
|
143
|
+
* issuer's scopes, agents and Environment grants, until `expiresAt` (ms). Only its expiry ends it.
|
|
114
144
|
*/
|
|
115
145
|
| {
|
|
116
146
|
kind: "token";
|
|
@@ -120,8 +150,8 @@ export type AuthScope = {
|
|
|
120
150
|
/** The issuer's scopes the token holds, which may add `studio`. */
|
|
121
151
|
scopes: ReadonlySet<IssuerScope>;
|
|
122
152
|
agents: ReadonlySet<string> | "*";
|
|
123
|
-
/** The
|
|
124
|
-
|
|
153
|
+
/** The Environments it reaches: exact ids or `p/*` prefixes. Absent or empty reaches none. */
|
|
154
|
+
environments?: readonly string[];
|
|
125
155
|
expiresAt: number;
|
|
126
156
|
/** The token's `jti`, or a hash of it. */
|
|
127
157
|
tokenId: string;
|
|
@@ -151,7 +181,7 @@ export interface TenantContext {
|
|
|
151
181
|
*/
|
|
152
182
|
readonly mcp?: McpPool;
|
|
153
183
|
/**
|
|
154
|
-
* The workspace
|
|
184
|
+
* The workspace feature (F6.2): the Tenant's SandboxManager with an in-process harness,
|
|
155
185
|
* else the harness that serves workspaces (`harness-api/workspace.ts`).
|
|
156
186
|
*/
|
|
157
187
|
readonly sandbox: WorkspacePort;
|
|
@@ -183,6 +213,11 @@ export interface TenantContext {
|
|
|
183
213
|
* this process's `sandbox` today; over the Harness API's `workspace.read` from F6.2.
|
|
184
214
|
*/
|
|
185
215
|
readonly workspaces: WorkspaceReader;
|
|
216
|
+
/**
|
|
217
|
+
* How the trace route (R7.5) makes spans: the resource and the content the installation exports
|
|
218
|
+
* (R7.2). Absent: `service.name` `nylorun`, no content.
|
|
219
|
+
*/
|
|
220
|
+
readonly traces?: TraceOptions;
|
|
186
221
|
/**
|
|
187
222
|
* The run token of each session an advance of this process owns (F5): the credential the
|
|
188
223
|
* HTTP gate clients present for that session's model and MCP calls (`run-grants.ts`).
|
|
@@ -232,7 +267,7 @@ export interface TenantContext {
|
|
|
232
267
|
abortLocal(sessionId: string, turnId?: string): void;
|
|
233
268
|
/**
|
|
234
269
|
* Sandbox pods (F7.2): the sandboxes service and the Runtime image pods copy the engine from.
|
|
235
|
-
* Absent without a cluster (`nylorun sandbox enable`): kind `pod` is `
|
|
270
|
+
* Absent without a cluster (`nylorun local sandbox enable`): kind `pod` is `environment_unavailable`.
|
|
236
271
|
*/
|
|
237
272
|
readonly pods?: TenantPods;
|
|
238
273
|
/**
|
|
@@ -275,14 +310,11 @@ export interface Lease {
|
|
|
275
310
|
export declare function ownedSession(t: Tx, lease: Lease, id: string): Promise<Session>;
|
|
276
311
|
/** The session read in its own transaction, or a 404. */
|
|
277
312
|
export declare function loadSession(ctx: TenantContext, id: string, access?: SessionAccess): Promise<Session>;
|
|
278
|
-
/**
|
|
279
|
-
* A synchronous lookup over `start` and every session its `sandboxOwnerId` chain names, for
|
|
280
|
-
* the sandbox-sharing helpers, which walk that chain synchronously.
|
|
281
|
-
*/
|
|
282
|
-
export declare function sandboxLookup(t: Tx, start: string | undefined): Promise<(id: string) => Session | undefined>;
|
|
283
313
|
/** The sandboxes service a Tenant drives its pod sandboxes with (`TenantContext.pods`). */
|
|
284
314
|
export interface TenantPods {
|
|
285
315
|
readonly client: SandboxesClient;
|
|
286
316
|
/** The Runtime image the pods copy the engine from (`NYLORUN_SANDBOX_HARNESS_IMAGE`). */
|
|
287
317
|
readonly harnessImage: string;
|
|
288
318
|
}
|
|
319
|
+
/** The harness a session runs (R4.1): the one it pinned, else ours. */
|
|
320
|
+
export declare function harnessOf(session: Pick<Session, "harness">): string;
|
package/dist/tenant/context.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_HARNESS } from "@nylorun/core/contracts";
|
|
1
2
|
import { fail } from "./http.js";
|
|
2
3
|
/**
|
|
3
4
|
* `access`, when set, limits the request to the owner's sessions of the allowed agents:
|
|
@@ -34,19 +35,7 @@ export async function ownedSession(t, lease, id) {
|
|
|
34
35
|
export function loadSession(ctx, id, access) {
|
|
35
36
|
return ctx.store.tx((t) => sessionOf(t, id, access));
|
|
36
37
|
}
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*/
|
|
41
|
-
export async function sandboxLookup(t, start) {
|
|
42
|
-
const found = new Map();
|
|
43
|
-
let id = start;
|
|
44
|
-
while (id && !found.has(id)) {
|
|
45
|
-
const session = await t.get("sessions", id);
|
|
46
|
-
if (!session)
|
|
47
|
-
break;
|
|
48
|
-
found.set(id, session);
|
|
49
|
-
id = session.sandboxOwnerId;
|
|
50
|
-
}
|
|
51
|
-
return (sid) => found.get(sid);
|
|
38
|
+
/** The harness a session runs (R4.1): the one it pinned, else ours. */
|
|
39
|
+
export function harnessOf(session) {
|
|
40
|
+
return session.harness ?? DEFAULT_HARNESS;
|
|
52
41
|
}
|
package/dist/tenant/effects.d.ts
CHANGED
|
@@ -42,6 +42,6 @@ export declare function recoversModelCalls(ctx: TenantContext): boolean;
|
|
|
42
42
|
export declare function resolveNewFlowEffect(ctx: TenantContext, request: HostEffect, signal: AbortSignal, lease: Lease): Promise<EffectResolution>;
|
|
43
43
|
/**
|
|
44
44
|
* The credential of an MCP server or HTTP tool request made on behalf of a session: from its
|
|
45
|
-
*
|
|
45
|
+
* Environment's vault (`vault/sources.ts`).
|
|
46
46
|
*/
|
|
47
47
|
export declare function authorize(ctx: TenantContext, sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
package/dist/tenant/effects.js
CHANGED
|
@@ -4,9 +4,9 @@ import { mayDispatchMore } from "../core/limits.js";
|
|
|
4
4
|
import { sessionCredentials } from "../vault/sources.js";
|
|
5
5
|
import { isRemoteMcpCall } from "../harness/calls.js";
|
|
6
6
|
import { isHttpToolCall } from "../gates/http-tool.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { loadSession, ownedSession,
|
|
7
|
+
import { sandboxSpecOf } from "../sandbox/share.js";
|
|
8
|
+
import { withRuntimeTools, withSandbox } from "../sandbox/session-sandbox.js";
|
|
9
|
+
import { loadSession, ownedSession, sessionOf, } from "./context.js";
|
|
10
10
|
import { fail } from "./http.js";
|
|
11
11
|
import { turnManifestOf } from "./session.js";
|
|
12
12
|
import { command } from "./commands.js";
|
|
@@ -85,11 +85,14 @@ export async function resolveNewFlowEffect(ctx, request, signal, lease) {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
const active = await countActiveFlowWork(t, request.sessionId, request.turnId);
|
|
88
|
+
// When the run journaled the effect (R7.1); a queued effect keeps it once dispatched.
|
|
89
|
+
const startedAt = existing?.startedAt ?? new Date().toISOString();
|
|
88
90
|
if (!mayDispatchMore(active, ctx.flowLimits)) {
|
|
89
91
|
if (!existing || existing.status !== "queued")
|
|
90
92
|
await t.put("effects", request.effectId, {
|
|
91
93
|
request,
|
|
92
94
|
status: "queued",
|
|
95
|
+
startedAt,
|
|
93
96
|
});
|
|
94
97
|
return resolved({ status: "pending" });
|
|
95
98
|
}
|
|
@@ -101,19 +104,32 @@ export async function resolveNewFlowEffect(ctx, request, signal, lease) {
|
|
|
101
104
|
value: {
|
|
102
105
|
kind: "failed",
|
|
103
106
|
code: "tool.unavailable",
|
|
104
|
-
message: `The tool stage '${request.key ?? request.path ?? ""}' runs your code,
|
|
107
|
+
message: `The tool stage '${request.key ?? request.path ?? ""}' runs your code, but the Runtime runs agents from their manifests alone`,
|
|
105
108
|
},
|
|
106
109
|
};
|
|
107
|
-
await t.put("effects", request.effectId, {
|
|
108
|
-
|
|
110
|
+
await t.put("effects", request.effectId, {
|
|
111
|
+
request,
|
|
112
|
+
status: "completed",
|
|
113
|
+
outcome,
|
|
114
|
+
startedAt,
|
|
115
|
+
settledAt: startedAt,
|
|
116
|
+
});
|
|
117
|
+
const node = {
|
|
109
118
|
path: request.path,
|
|
110
119
|
kind: "tool",
|
|
111
120
|
key: request.key,
|
|
112
121
|
...(request.iterations !== undefined ? { iterations: request.iterations } : {}),
|
|
122
|
+
};
|
|
123
|
+
await t.event(workflow.id, workflow.activeTurnId, "node.started", node);
|
|
124
|
+
// Nothing ran (R7.1): the stage's outcome row says so.
|
|
125
|
+
await t.event(workflow.id, workflow.activeTurnId, "node.completed", {
|
|
126
|
+
...node,
|
|
127
|
+
effectId: request.effectId,
|
|
128
|
+
status: "failed",
|
|
113
129
|
});
|
|
114
130
|
return resolved({ status: "completed", outcome });
|
|
115
131
|
}
|
|
116
|
-
return { kind: "agent", workflow };
|
|
132
|
+
return { kind: "agent", workflow, startedAt };
|
|
117
133
|
});
|
|
118
134
|
if (step.kind === "resolved")
|
|
119
135
|
return step.resolution;
|
|
@@ -130,6 +146,7 @@ export async function resolveNewFlowEffect(ctx, request, signal, lease) {
|
|
|
130
146
|
request,
|
|
131
147
|
status: "pending",
|
|
132
148
|
agentSessionId,
|
|
149
|
+
startedAt: step.startedAt,
|
|
133
150
|
});
|
|
134
151
|
await t.put("links", agentSessionId, {
|
|
135
152
|
workflowSessionId: workflow.id,
|
|
@@ -145,14 +162,10 @@ export async function resolveNewFlowEffect(ctx, request, signal, lease) {
|
|
|
145
162
|
if (exists)
|
|
146
163
|
return;
|
|
147
164
|
const definition = leafDefinition(workflow, body, request);
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// A sandbox chosen when the tree was opened reaches every agent in it, with its tools.
|
|
153
|
-
const inherited = inheritedSandbox(sandboxOwnerId === undefined ? undefined : lookup(sandboxOwnerId) ?? workflow, definition);
|
|
154
|
-
// Without one, an agent with MCP or HTTP tools still reads its stored results (R2b C11).
|
|
155
|
-
const pinned = inherited?.manifest ? inherited : platformTools(definition.manifest);
|
|
165
|
+
// The tree's Environment reaches every agent in it: its sandbox, with its tools, and its vault.
|
|
166
|
+
const inherited = inheritedSandbox(workflow, definition);
|
|
167
|
+
// Without one, the agent still gets its runtime block: read_artifact (R2b C11) and its skill tools.
|
|
168
|
+
const pinned = inherited?.manifest ? inherited : runtimeTools(definition.manifest);
|
|
156
169
|
const created = {
|
|
157
170
|
id: agentSessionId,
|
|
158
171
|
agentId: body.agentId,
|
|
@@ -167,14 +180,18 @@ export async function resolveNewFlowEffect(ctx, request, signal, lease) {
|
|
|
167
180
|
requestId: `flow-put-${request.effectId}`,
|
|
168
181
|
agentId: body.agentId,
|
|
169
182
|
ownerUserId: workflow.ownerUserId,
|
|
170
|
-
...(
|
|
183
|
+
...(workflow.environmentId === undefined ? {} : { environment: workflow.environmentId }),
|
|
171
184
|
},
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
flowRootId: workflow.flowRootId ?? workflow.id,
|
|
186
|
+
...(workflow.environmentId === undefined
|
|
187
|
+
? {}
|
|
188
|
+
: {
|
|
189
|
+
environmentId: workflow.environmentId,
|
|
190
|
+
...(workflow.environmentVersion === undefined
|
|
191
|
+
? {}
|
|
192
|
+
: { environmentVersion: workflow.environmentVersion }),
|
|
193
|
+
}),
|
|
194
|
+
...(inherited ? { sandbox: inherited.spec } : {}),
|
|
178
195
|
};
|
|
179
196
|
await t.put("sessions", agentSessionId, created);
|
|
180
197
|
});
|
|
@@ -240,25 +257,23 @@ function inheritedSandbox(owner, definition) {
|
|
|
240
257
|
return undefined;
|
|
241
258
|
if (isWorkflowManifest(definition.manifest))
|
|
242
259
|
return { spec };
|
|
243
|
-
const sandboxed =
|
|
260
|
+
const sandboxed = withSandbox(definition.manifest, spec);
|
|
244
261
|
if (!sandboxed.ok)
|
|
245
262
|
throw new Error(sandboxed.message);
|
|
246
263
|
return { spec, manifest: sandboxed.manifest, manifestHash: sandboxed.manifestHash };
|
|
247
264
|
}
|
|
248
|
-
/** A linked agent session's `
|
|
249
|
-
function
|
|
265
|
+
/** A linked agent session's `runtime` block without a sandbox (R2b C11); undefined for a workflow. */
|
|
266
|
+
function runtimeTools(manifest) {
|
|
250
267
|
if (isWorkflowManifest(manifest))
|
|
251
268
|
return undefined;
|
|
252
|
-
const pinned =
|
|
253
|
-
if (pinned === undefined)
|
|
254
|
-
return undefined;
|
|
269
|
+
const pinned = withRuntimeTools(manifest);
|
|
255
270
|
if (!pinned.ok)
|
|
256
271
|
throw new Error(pinned.message);
|
|
257
272
|
return { manifest: pinned.manifest, manifestHash: pinned.manifestHash };
|
|
258
273
|
}
|
|
259
274
|
/**
|
|
260
275
|
* The credential of an MCP server or HTTP tool request made on behalf of a session: from its
|
|
261
|
-
*
|
|
276
|
+
* Environment's vault (`vault/sources.ts`).
|
|
262
277
|
*/
|
|
263
278
|
export async function authorize(ctx, sessionId, request) {
|
|
264
279
|
const s = await loadSession(ctx, sessionId);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment manifests (protocol 14, D63): `/v1/environment-manifests/{name}`, named and
|
|
3
|
+
* versioned definitions of a kind of Environment, saved like agents.
|
|
4
|
+
*
|
|
5
|
+
* - **Versions.** A save whose manifest hashes the same as the latest answers it unchanged;
|
|
6
|
+
* any other writes the next version. A version is never changed or deleted alone.
|
|
7
|
+
* - **No Environment changes.** Saving a version reaches no Environment: an Environment is made
|
|
8
|
+
* from one version and keeps it for life (`putEnvironment`). The plan counts those behind.
|
|
9
|
+
* - **Plan and drift.** `?dryRun=true` answers what a save would do and saves nothing: the
|
|
10
|
+
* changes from the latest, every reason the Runtime would refuse it (the Tenant's limits,
|
|
11
|
+
* missing artifacts), warnings, and how many Environments stay on older versions.
|
|
12
|
+
* `If-Match: <version>` refuses (`412 manifest_changed`) a save whose latest version moved
|
|
13
|
+
* since the client read it; `0` asks for a manifest that does not exist yet.
|
|
14
|
+
* - **Every client is equal** (Q12): a version records who saved it (`savedBy`), and nothing is
|
|
15
|
+
* read-only by where it was saved from.
|
|
16
|
+
*/
|
|
17
|
+
import { type EnvironmentManifest, type EnvironmentManifestPlan, type EnvironmentManifestVersions, type EnvironmentManifestView, type PutEnvironmentManifestRequest, type SaveEnvironmentManifestResponse } from "@nylorun/core/contracts";
|
|
18
|
+
import { type RecipeRequest } from "../sandbox/resolve.js";
|
|
19
|
+
import type { AuthScope, TenantContext } from "./context.js";
|
|
20
|
+
/** What an environment manifest asks of an Environment's recipe. */
|
|
21
|
+
export declare function manifestRecipeRequest(manifest: EnvironmentManifest): RecipeRequest;
|
|
22
|
+
/** The hash a manifest's version records: equal manifests hash the same. */
|
|
23
|
+
export declare function environmentManifestHash(manifest: EnvironmentManifest): string;
|
|
24
|
+
/** Each field that differs between `before` and `after`, by path; arrays compare whole. */
|
|
25
|
+
export declare function manifestChanges(before: unknown, after: unknown, path?: string): {
|
|
26
|
+
path: string;
|
|
27
|
+
from?: unknown;
|
|
28
|
+
to?: unknown;
|
|
29
|
+
}[];
|
|
30
|
+
/**
|
|
31
|
+
* `PUT /v1/environment-manifests/{name}`: saves `body` as manifest `name`'s next version, or
|
|
32
|
+
* answers the latest unchanged; with `dryRun`, the plan of that save and nothing saved.
|
|
33
|
+
*/
|
|
34
|
+
export declare function saveEnvironmentManifest(ctx: TenantContext, name: string, body: PutEnvironmentManifestRequest, scope: AuthScope, options: {
|
|
35
|
+
dryRun: true;
|
|
36
|
+
ifMatch?: number;
|
|
37
|
+
}): Promise<EnvironmentManifestPlan>;
|
|
38
|
+
export declare function saveEnvironmentManifest(ctx: TenantContext, name: string, body: PutEnvironmentManifestRequest, scope: AuthScope, options?: {
|
|
39
|
+
dryRun?: false;
|
|
40
|
+
ifMatch?: number;
|
|
41
|
+
}): Promise<SaveEnvironmentManifestResponse>;
|
|
42
|
+
/** `GET /v1/environment-manifests/{name}` (the latest) and `.../versions/{n}`. */
|
|
43
|
+
export declare function getEnvironmentManifest(ctx: TenantContext, name: string, version?: number): Promise<EnvironmentManifestView>;
|
|
44
|
+
/** `GET /v1/environment-manifests`: each manifest's latest version. */
|
|
45
|
+
export declare function listEnvironmentManifests(ctx: TenantContext): Promise<{
|
|
46
|
+
manifests: EnvironmentManifestView[];
|
|
47
|
+
}>;
|
|
48
|
+
/** `GET /v1/environment-manifests/{name}/versions`: every version, newest first. */
|
|
49
|
+
export declare function environmentManifestVersions(ctx: TenantContext, name: string): Promise<EnvironmentManifestVersions>;
|
|
50
|
+
/**
|
|
51
|
+
* `DELETE /v1/environment-manifests/{name}`: deletes every version, refused while any Environment
|
|
52
|
+
* was made from one (`409 manifest_in_use`): the Environment names the version it runs.
|
|
53
|
+
*/
|
|
54
|
+
export declare function deleteEnvironmentManifest(ctx: TenantContext, name: string): Promise<{
|
|
55
|
+
name: string;
|
|
56
|
+
deleted: boolean;
|
|
57
|
+
}>;
|