@nylorun/runtime 0.25.0-beta → 0.27.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 +169 -0
- package/README.md +26 -16
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.js +1 -1
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +12 -18
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +4 -4
- package/dist/api/components.d.ts +326 -45
- package/dist/api/components.js +5 -1
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.d.ts +2 -2
- package/dist/api/http/app.js +2 -2
- package/dist/api/http/body.d.ts +15 -3
- package/dist/api/http/body.js +15 -7
- package/dist/api/http/define.d.ts +1 -1
- package/dist/api/http/respond.js +7 -3
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/artifacts.js +19 -7
- package/dist/api/http/routes/coverage.js +1 -1
- package/dist/api/http/routes/environment-manifests.js +1 -1
- package/dist/api/http/routes/environments.js +6 -6
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +1 -1
- package/dist/api/http/routes/reads.d.ts +35 -0
- package/dist/api/http/routes/reads.js +50 -22
- package/dist/api/http/routes/sessions.js +12 -11
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +69 -16
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +6 -2
- package/dist/artifacts/disk.d.ts +0 -4
- package/dist/artifacts/disk.js +77 -73
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +70 -25
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +4 -3
- package/dist/gates/egress.d.ts +9 -9
- package/dist/gates/egress.js +19 -19
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +53 -17
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +28 -5
- package/dist/gates/policy.js +60 -17
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +0 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +4 -9
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +102 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +82 -73
- package/dist/harness-api/start.d.ts +20 -2
- package/dist/harness-api/start.js +57 -18
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +2 -45
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +0 -2
- package/dist/host/http.js +0 -27
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +1 -3
- package/dist/keys/contract.js +1 -3
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.js +6 -1
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +1961 -994
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +13 -6
- package/dist/mcp/preview.js +37 -20
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +866 -1218
- package/dist/reads/cursor.js +3 -1
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/environment.d.ts +2 -0
- package/dist/record/environment.js +2 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +3 -2
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +5 -8
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +19 -11
- package/dist/sandbox/pods/lifecycle.js +45 -23
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +4 -1
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +8 -3
- package/dist/sandbox/resolve.js +12 -2
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +34 -11
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/migrate.js +1 -1
- package/dist/store/postgres/reads.js +10 -2
- package/dist/store/postgres/schema.d.ts +19 -0
- package/dist/store/postgres/schema.js +7 -0
- package/dist/store/postgres/store.js +55 -26
- package/dist/store/types.d.ts +17 -7
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +21 -31
- package/dist/tenant/commands.js +9 -16
- package/dist/tenant/context.d.ts +14 -17
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +9 -36
- package/dist/tenant/environment-manifests.js +9 -1
- package/dist/tenant/environments.d.ts +0 -2
- package/dist/tenant/environments.js +54 -30
- package/dist/tenant/ephemeral.d.ts +1 -1
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +12 -11
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +24 -1
- package/dist/tenant/identity-file.js +35 -7
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +31 -23
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +90 -0
- package/dist/tenant/limits.js +175 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +17 -19
- package/dist/tenant/outbound.js +18 -21
- package/dist/tenant/run-grants.js +3 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.js +30 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.js +32 -29
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +4 -3
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +39 -48
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/types.d.ts +11 -16
- package/dist/tenant/worker.d.ts +2 -10
- package/dist/tenant/worker.js +5 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +9 -6
- package/dist/traces/map.js +9 -11
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +383 -0
- package/dist/tuning.js +658 -0
- package/dist/vault/headers.js +4 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
* workspace request it is serving acts for; a `sandbox.state` claim's record becomes the
|
|
17
17
|
* workspace's row in the `sandboxes` table.
|
|
18
18
|
*
|
|
19
|
+
* Protocol 15: a harness reads and writes its run's session's artifacts over the artifact seam
|
|
20
|
+
* (`artifact.read`, `artifact.write`, `./artifacts.ts`); a run's open uploads end with it.
|
|
21
|
+
*
|
|
19
22
|
* F7.2: a pod sandbox's engine connects as that sandbox's *host* (`HarnessPeer.host`, from its
|
|
20
23
|
* host token): it leases only runs whose session is attached to its sandbox (`RunOffer.pod`),
|
|
21
24
|
* serves only that sandbox's workspace (`workspace(..., { pod })`), and claims events only for
|
|
@@ -25,20 +28,23 @@
|
|
|
25
28
|
* `connection.lost`.
|
|
26
29
|
*/
|
|
27
30
|
import { randomUUID } from "node:crypto";
|
|
28
|
-
import { DEFAULT_HARNESS, HARNESS_API_VERSION, HARNESS_CLAIMS,
|
|
29
|
-
import { definitionFilesOf } from "@nylorun/core/contracts";
|
|
30
|
-
import { TOOL_CALL_TOOL, TOOL_SEARCH_TOOL, delegatesOf, } from "@nylorun/core/define";
|
|
31
|
+
import { DEFAULT_HARNESS, HARNESS_API_VERSION, HARNESS_CLAIMS, isHarnessId, HarnessApiError, } from "@nylorun/core/harness-api";
|
|
32
|
+
import { definitionFilesOf, limitExceededMessage } from "@nylorun/core/contracts";
|
|
31
33
|
import { isOwnershipLost } from "../store/ownership.js";
|
|
32
34
|
import { harnessOf, ownedSession } from "../tenant/context.js";
|
|
33
|
-
import {
|
|
34
|
-
import { contextWindowOf } from "../model/catalog.js";
|
|
35
|
+
import { fitSnapshot, isSnapshot, mcpDiscovered, mcpFactsOf, } from "../mcp/snapshot.js";
|
|
35
36
|
import { turnManifestOf } from "../tenant/session.js";
|
|
37
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
36
38
|
import { sandboxWorkspaceKey, workspacePrefix } from "../sandbox/records.js";
|
|
37
39
|
import { abortKind } from "../tenant/worker.js";
|
|
38
|
-
import {
|
|
40
|
+
import { BlobRangeError } from "../blob/index.js";
|
|
41
|
+
import { openDefinitionFile, readDefinitionFile } from "../tenant/definition-files.js";
|
|
39
42
|
import { recordIntent, recordOutcome } from "./record.js";
|
|
43
|
+
import { createArtifactSeam } from "./artifacts.js";
|
|
44
|
+
import { RecordEntryTooLargeError } from "../record/index.js";
|
|
40
45
|
import { redactTokens } from "./redact.js";
|
|
41
46
|
import { beat, renewEveryMs, tokenOf } from "./renew.js";
|
|
47
|
+
import { hostTuning } from "../tuning.js";
|
|
42
48
|
/** No attached harness serves workspaces (or, with `pod`, that pod sandbox's). */
|
|
43
49
|
export class NoWorkspaceHarness extends Error {
|
|
44
50
|
pod;
|
|
@@ -60,11 +66,12 @@ const OUTPUTS = [
|
|
|
60
66
|
"checkpoint",
|
|
61
67
|
];
|
|
62
68
|
export function createHarnessApiServer(ctx, options = {}) {
|
|
63
|
-
const offerWaitMs = options.offerWaitMs ??
|
|
69
|
+
const offerWaitMs = options.offerWaitMs ?? hostTuning().harnessApi.offerWaitMs;
|
|
64
70
|
const connections = new Set();
|
|
65
71
|
const waiting = [];
|
|
66
72
|
const queued = [];
|
|
67
73
|
const runs = new Map();
|
|
74
|
+
const artifacts = createArtifactSeam(ctx);
|
|
68
75
|
let closed = false;
|
|
69
76
|
/**
|
|
70
77
|
* A harness may take a run of its own harness id (R4.1): a pod sandbox's host its sandbox's
|
|
@@ -81,15 +88,6 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
81
88
|
continue;
|
|
82
89
|
}
|
|
83
90
|
queued.splice(i, 1);
|
|
84
|
-
const clash = nativeToolClashes(waiting[at].connection.tools, run.offer.start);
|
|
85
|
-
if (clash.length > 0) {
|
|
86
|
-
// The harness keeps waiting for a run its tools fit; this turn fails (Harness API 3).
|
|
87
|
-
run.end({
|
|
88
|
-
kind: "refused",
|
|
89
|
-
message: `Harness '${waiting[at].connection.harness}' runs tools named like tools of this turn's agents: ${clash.join(", ")}. A tool the harness runs itself needs a name no other tool of the session has.`,
|
|
90
|
-
});
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
91
|
const lease = waiting.splice(at, 1)[0];
|
|
94
92
|
run.connection = lease.connection;
|
|
95
93
|
// The run token the advance holds now (F5): the gate calls of this run present it.
|
|
@@ -111,11 +109,18 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
111
109
|
evidence: "claimed",
|
|
112
110
|
...(runId === undefined ? {} : { runId }),
|
|
113
111
|
});
|
|
112
|
+
/** What the artifact seam serves a run: its session and turn, its limits, its signal. */
|
|
113
|
+
const artifactScope = (run) => ({
|
|
114
|
+
runId: run.grant.runId,
|
|
115
|
+
sessionId: run.grant.sessionId,
|
|
116
|
+
turnId: run.grant.turnId,
|
|
117
|
+
limits: run.offer.start.limits,
|
|
118
|
+
runSignal: run.offer.controller.signal,
|
|
119
|
+
});
|
|
114
120
|
const scope = (run) => ({
|
|
115
121
|
ctx,
|
|
116
122
|
lease: run.offer.lease,
|
|
117
123
|
signal: run.offer.controller.signal,
|
|
118
|
-
...(run.connection?.tools ? { nativeTools: run.connection.tools } : {}),
|
|
119
124
|
...(run.connection ? { writer: writerOf(run.connection, run.grant.runId) } : {}),
|
|
120
125
|
});
|
|
121
126
|
const serve = (connection) => async (method, params, signal) => {
|
|
@@ -127,8 +132,6 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
127
132
|
throw new HarnessApiError("invalid", `unsupported Harness API version ${api}: this Runtime speaks ${HARNESS_API_VERSION}`);
|
|
128
133
|
connection.harness = await harnessIdOf(connection, name);
|
|
129
134
|
connection.workspace = features.workspace !== undefined;
|
|
130
|
-
if (features.tools?.length)
|
|
131
|
-
connection.tools = new Set(features.tools.map((tool) => tool.name));
|
|
132
135
|
return {
|
|
133
136
|
api: HARNESS_API_VERSION,
|
|
134
137
|
tenantId: ctx.config.tenantId,
|
|
@@ -181,7 +184,11 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
181
184
|
case "session.mcp":
|
|
182
185
|
return recordMcp(run, p);
|
|
183
186
|
case "definition.file":
|
|
184
|
-
return definitionFile(run, p
|
|
187
|
+
return definitionFile(run, p, signal);
|
|
188
|
+
case "artifact.read":
|
|
189
|
+
return artifacts.read(artifactScope(run), p, signal);
|
|
190
|
+
case "artifact.write":
|
|
191
|
+
return artifacts.write(artifactScope(run), p, signal);
|
|
185
192
|
default:
|
|
186
193
|
throw new HarnessApiError("invalid", `Unknown request ${method}`);
|
|
187
194
|
}
|
|
@@ -211,7 +218,8 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
211
218
|
* A `sandbox.*` event the harness claims: for a run it holds (the run's session, or the
|
|
212
219
|
* session owning its sandbox), or a session one of its workspace requests acts for. A
|
|
213
220
|
* `sandbox.state` record becomes the workspace's `sandboxes` row. Claims are best effort, as
|
|
214
|
-
* the events of core's own SandboxManager are: a session gone since is skipped.
|
|
221
|
+
* the events of core's own SandboxManager are: a session gone since is skipped. One past the
|
|
222
|
+
* Tenant's `recordEntryBytes` is refused (`limit_exceeded`).
|
|
215
223
|
*/
|
|
216
224
|
const claim = async (connection, event) => {
|
|
217
225
|
if (!HARNESS_CLAIMS.includes(event.type))
|
|
@@ -240,6 +248,9 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
240
248
|
});
|
|
241
249
|
}
|
|
242
250
|
catch (error) {
|
|
251
|
+
// Past the record entry limit is the harness's to hear (`limit_exceeded`), not a lost claim.
|
|
252
|
+
if (error instanceof RecordEntryTooLargeError)
|
|
253
|
+
throw error;
|
|
243
254
|
ctx.config.logger.warn("harness event claim not recorded", {
|
|
244
255
|
sessionId: event.sessionId,
|
|
245
256
|
type: event.type,
|
|
@@ -250,22 +261,23 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
250
261
|
};
|
|
251
262
|
/**
|
|
252
263
|
* What a harness found readying the run's MCP servers: the first snapshot recorded for the
|
|
253
|
-
* session wins,
|
|
254
|
-
*
|
|
264
|
+
* session wins, pinned as it is within the Tenant's Runtime limit `mcpSnapshotBytes` (a server
|
|
265
|
+
* past it is left out, with a diagnostic: `fitSnapshot`), with `mcp.discovered` in its log;
|
|
266
|
+
* diagnostics replace those of the same server. Answers the session's facts as the run's turn
|
|
267
|
+
* sees them (protocol 15).
|
|
255
268
|
*/
|
|
256
269
|
const recordMcp = async (run, params) => {
|
|
257
270
|
const snapshot = params.snapshot;
|
|
258
271
|
if (snapshot !== undefined && !isSnapshot(snapshot))
|
|
259
272
|
throw new HarnessApiError("invalid", "The MCP snapshot is malformed");
|
|
260
|
-
// Which MCP tools the model reaches through tool_search, decided once for the session from
|
|
261
|
-
// the Tenant's model's context window (R2b C10, Q21).
|
|
262
|
-
const contextWindow = snapshot ? contextWindowOf(await ctx.vault.getHostModel()) : 0;
|
|
263
273
|
return ctx.store.tx(async (t) => {
|
|
264
274
|
const { lease } = run.offer;
|
|
265
275
|
const current = await ownedSession(t, lease, lease.sessionId);
|
|
266
|
-
|
|
276
|
+
let diagnostics = params.diagnostics;
|
|
267
277
|
if (snapshot && !current.mcpSnapshot) {
|
|
268
|
-
|
|
278
|
+
const fitted = fitSnapshot(snapshot, diagnostics, (await readRuntimeLimits(t)).mcpSnapshotBytes);
|
|
279
|
+
diagnostics = fitted.diagnostics;
|
|
280
|
+
current.mcpSnapshot = fitted.snapshot;
|
|
269
281
|
current.mcpDiagnostics = diagnostics;
|
|
270
282
|
await t.put("sessions", current.id, current);
|
|
271
283
|
await t.event(current.id, run.grant.turnId, "mcp.discovered", mcpDiscovered(current.mcpSnapshot, diagnostics, current.manifest), run.connection ? writerOf(run.connection, run.grant.runId) : undefined);
|
|
@@ -282,27 +294,48 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
282
294
|
current.mcpDiagnostics = prior;
|
|
283
295
|
await t.put("sessions", current.id, current);
|
|
284
296
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
// The turn's manifest: a variant may have disabled tools or required their approval.
|
|
288
|
-
sessionTools: [...(sessionToolsOf(current.mcpSnapshot, turnManifestOf(current)) ?? [])],
|
|
289
|
-
};
|
|
297
|
+
// The turn's manifest: a variant may have disabled tools.
|
|
298
|
+
return current.mcpSnapshot ? { mcp: mcpFactsOf(current.mcpSnapshot, turnManifestOf(current)) } : {};
|
|
290
299
|
});
|
|
291
300
|
};
|
|
292
|
-
/**
|
|
293
|
-
|
|
301
|
+
/**
|
|
302
|
+
* A definition file the run's definition names (its skills', for its sandbox), base64: whole,
|
|
303
|
+
* or the range `offset` and `length` name (none past the end), at most the run's
|
|
304
|
+
* `limits.chunkBytes`, which is also the length when the harness names none.
|
|
305
|
+
*/
|
|
306
|
+
const definitionFile = async (run, { sha256, offset, length }, signal) => {
|
|
294
307
|
if (!definitionFilesOf(run.offer.start.routing.rootManifest).has(sha256))
|
|
295
308
|
throw new HarnessApiError("invalid", `The run's definition names no file ${sha256}`);
|
|
296
|
-
const
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
309
|
+
const missing = () => new HarnessApiError("unavailable", `The Object store has no bytes for ${sha256}`);
|
|
310
|
+
if (offset === undefined && length === undefined) {
|
|
311
|
+
const bytes = await readDefinitionFile(ctx.blobs, sha256, signal);
|
|
312
|
+
if (!bytes)
|
|
313
|
+
throw missing();
|
|
314
|
+
return { base64: Buffer.from(bytes).toString("base64") };
|
|
315
|
+
}
|
|
316
|
+
const { chunkBytes } = run.offer.start.limits;
|
|
317
|
+
if (length !== undefined && length > chunkBytes)
|
|
318
|
+
throw new HarnessApiError("limit_exceeded", limitExceededMessage("The range asked for", length, "chunkBytes", chunkBytes));
|
|
319
|
+
const start = offset ?? 0;
|
|
320
|
+
const end = start + (length ?? chunkBytes) - 1;
|
|
321
|
+
let body;
|
|
322
|
+
try {
|
|
323
|
+
body = await openDefinitionFile(ctx.blobs, sha256, { range: { start, end }, signal });
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
if (error instanceof BlobRangeError)
|
|
327
|
+
return { base64: "" };
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
if (!body)
|
|
331
|
+
throw missing();
|
|
332
|
+
return { base64: Buffer.from(await new Response(body).arrayBuffer()).toString("base64") };
|
|
300
333
|
};
|
|
301
334
|
/**
|
|
302
335
|
* A custom harness's output (R4.2): its state is its own, stored as sent and never read, so it
|
|
303
|
-
* must be JSON and at most `
|
|
304
|
-
* that threw would. It cannot pause for approvals in v2 (Q14):
|
|
305
|
-
* until R5 makes them core holds.
|
|
336
|
+
* must be JSON and at most the run's `limits.harnessStateBytes`; a larger one fails the turn
|
|
337
|
+
* (`limit_exceeded`), as an engine that threw would. It cannot pause for approvals in v2 (Q14):
|
|
338
|
+
* approvals read our engine's plan until R5 makes them core holds.
|
|
306
339
|
*/
|
|
307
340
|
const checkCustomOutput = (run, method, params) => {
|
|
308
341
|
if (method === "turn.paused")
|
|
@@ -310,14 +343,16 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
310
343
|
if (params.state === undefined || run.outputs.has(method))
|
|
311
344
|
return;
|
|
312
345
|
const bytes = Buffer.byteLength(JSON.stringify(params.state) ?? "");
|
|
313
|
-
|
|
346
|
+
const { harnessStateBytes } = run.offer.start.limits;
|
|
347
|
+
if (bytes <= harnessStateBytes)
|
|
314
348
|
return;
|
|
349
|
+
const message = limitExceededMessage("The harness's state", bytes, "harnessStateBytes", harnessStateBytes);
|
|
315
350
|
output(run, "turn.failed", {
|
|
316
351
|
runId: run.grant.runId,
|
|
317
352
|
status: "failed",
|
|
318
|
-
thrown: { code: "
|
|
353
|
+
thrown: { code: "limit_exceeded", message },
|
|
319
354
|
}).catch(() => undefined);
|
|
320
|
-
throw new HarnessApiError("
|
|
355
|
+
throw new HarnessApiError("limit_exceeded", `${message} The turn failed.`);
|
|
321
356
|
};
|
|
322
357
|
const output = (run, method, params) => {
|
|
323
358
|
const known = run.outputs.get(method);
|
|
@@ -455,6 +490,8 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
455
490
|
return;
|
|
456
491
|
run.ended = true;
|
|
457
492
|
clearTimeout(timer);
|
|
493
|
+
// An upload the run left open commits nothing.
|
|
494
|
+
artifacts.endRun(run.grant.runId);
|
|
458
495
|
controller.signal.removeEventListener("abort", onAbort);
|
|
459
496
|
// An output's run stays held until core answers it, so a repeat gets the same answer.
|
|
460
497
|
if (end.kind === "output") {
|
|
@@ -501,31 +538,3 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
501
538
|
},
|
|
502
539
|
};
|
|
503
540
|
}
|
|
504
|
-
function isSnapshot(value) {
|
|
505
|
-
const snapshot = value;
|
|
506
|
-
return (typeof snapshot === "object" &&
|
|
507
|
-
snapshot !== null &&
|
|
508
|
-
snapshot.snapshotSchemaVersion === 1 &&
|
|
509
|
-
typeof snapshot.manifestHash === "string" &&
|
|
510
|
-
Array.isArray(snapshot.mcpTools));
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* The tools a harness runs itself (`features.tools`) whose names the turn already uses: a tool of
|
|
514
|
-
* the turn's agents (their own and the Runtime's `runtime` tools), a session tool, `tool_search` or
|
|
515
|
-
* `tool_call` (session tools a later step may get). Tools are known by name alone, so a native
|
|
516
|
-
* tool must not take one (Harness API 3); `hello` already refuses a name with `__`, which names an
|
|
517
|
-
* MCP server's tools. A flow turn runs no native tools.
|
|
518
|
-
*/
|
|
519
|
-
export function nativeToolClashes(tools, start) {
|
|
520
|
-
if (!tools?.size || start.engine !== "agent")
|
|
521
|
-
return [];
|
|
522
|
-
const manifest = start.manifest;
|
|
523
|
-
const taken = new Set([TOOL_SEARCH_TOOL, TOOL_CALL_TOOL]);
|
|
524
|
-
for (const agent of [manifest, ...delegatesOf(manifest).map((delegate) => delegate.manifest)])
|
|
525
|
-
for (const tool of [...(agent.tools ?? []), ...(agent.runtime?.tools ?? [])])
|
|
526
|
-
taken.add(tool.name);
|
|
527
|
-
for (const tool of (start.sessionTools ?? []))
|
|
528
|
-
if (typeof tool?.name === "string")
|
|
529
|
-
taken.add(tool.name);
|
|
530
|
-
return [...tools].filter((name) => taken.has(name)).sort();
|
|
531
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { RecordedOutcome, TurnStart } from "@nylorun/core/harness-api";
|
|
1
|
+
import type { ModelDescriptor, RecordedOutcome, RuntimeLimits, TurnEnvironment, TurnStart } from "@nylorun/core/harness-api";
|
|
2
|
+
import { type SandboxManifest } from "@nylorun/core/define";
|
|
2
3
|
import type { SandboxPodState } from "../store/types.js";
|
|
3
4
|
import { type Lease, type Session, type TenantContext } from "../tenant/context.js";
|
|
4
5
|
import { type TraceContext } from "../traces/ids.js";
|
|
@@ -6,6 +7,8 @@ import { type TraceContext } from "../traces/ids.js";
|
|
|
6
7
|
export interface SegmentStart {
|
|
7
8
|
readonly current: Session;
|
|
8
9
|
readonly fixtureModel: boolean;
|
|
10
|
+
/** The Tenant's Runtime limits as the segment starts: what `turn.start.limits` hands the harness. */
|
|
11
|
+
readonly limits: RuntimeLimits;
|
|
9
12
|
/** The transcript the engine resumes from (agents with state; empty otherwise). */
|
|
10
13
|
readonly transcript: unknown[];
|
|
11
14
|
/** The record position of `transcript` (`foldTranscriptWithCursor`). */
|
|
@@ -17,7 +20,14 @@ export interface SegmentStart {
|
|
|
17
20
|
readonly environmentId?: string;
|
|
18
21
|
/** Variables every sandbox command gets (R2c): secret names set to the sentinel, plain values. */
|
|
19
22
|
readonly environment?: Readonly<Record<string, string>>;
|
|
23
|
+
/** The sandbox spec the session pinned when it opened; absent without a sandbox. */
|
|
24
|
+
readonly spec?: SandboxManifest;
|
|
20
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* The Environment the session runs in, as `TurnStart.environment` states it (protocol 15): its
|
|
28
|
+
* id and kind, and its sandbox's workspace when the session has a sandbox.
|
|
29
|
+
*/
|
|
30
|
+
readonly environment?: TurnEnvironment;
|
|
21
31
|
/**
|
|
22
32
|
* The Environment is a pod (F7.2): its engine runs the segment. Its lifecycle state, and
|
|
23
33
|
* whether its new volume is still being seeded from a disk version (R2d step 3).
|
|
@@ -29,6 +39,11 @@ export interface SegmentStart {
|
|
|
29
39
|
};
|
|
30
40
|
/** The turn's span context (R7.4): what `TurnStart.trace` hands the harness. */
|
|
31
41
|
readonly trace?: TraceContext;
|
|
42
|
+
/**
|
|
43
|
+
* The model the turn's calls use (protocol 15), as the segment starts: what `TurnStart.model`
|
|
44
|
+
* hands an agent's harness. Absent for a flow, and when the Tenant has no model.
|
|
45
|
+
*/
|
|
46
|
+
readonly model?: ModelDescriptor;
|
|
32
47
|
}
|
|
33
48
|
/** The segment's starting transaction and fold. */
|
|
34
49
|
export declare function startSegment(ctx: TenantContext, lease: Lease,
|
|
@@ -38,7 +53,10 @@ options?: {
|
|
|
38
53
|
}): Promise<SegmentStart>;
|
|
39
54
|
/** The run's `turn.start`. */
|
|
40
55
|
export declare function buildTurnStart(ctx: TenantContext, segment: SegmentStart): TurnStart;
|
|
41
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* The engine's yield budget for a segment: the Tenant's `rollover`, else the Host tuning's
|
|
58
|
+
* (`harnessApi.rolloverSteps`, `rolloverMs`), well inside the advance deadline.
|
|
59
|
+
*/
|
|
42
60
|
export declare function yieldAfterOf(ctx: TenantContext): {
|
|
43
61
|
steps: number;
|
|
44
62
|
ms: number;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a segment starts from (§10.5): in one transaction the session's state is rebased onto
|
|
3
|
+
* the turn's manifest, a transcript still on the row is recorded, and the segment's completed
|
|
4
|
+
* outcomes are read; then the transcript is folded from the record (P0.3). `buildTurnStart`
|
|
5
|
+
* turns that into the run's `turn.start`: the checkpoint goes without its transcript, which the
|
|
6
|
+
* harness holds at the fold's cursor or reads once.
|
|
7
|
+
*/
|
|
8
|
+
import { harnessLimitsOf } from "@nylorun/core/contracts";
|
|
9
|
+
import { SANDBOX_WORKSPACE } from "@nylorun/core/define";
|
|
1
10
|
import { isWorkflowManifest } from "../core/flow-host.js";
|
|
2
11
|
import { sandboxWorkspaceOf } from "../sandbox/share.js";
|
|
3
12
|
import { ownedTx } from "../store/ownership.js";
|
|
@@ -5,13 +14,15 @@ import { harnessOf } from "../tenant/context.js";
|
|
|
5
14
|
import { HARNESS_ID } from "../sandbox/placement.js";
|
|
6
15
|
import { checkParity, foldTranscriptWithCursor, leanState, transcriptOf, transcriptUpdates, withTranscript, } from "../tenant/history.js";
|
|
7
16
|
import { usesFixtureModel } from "../tenant/model-setting.js";
|
|
8
|
-
import {
|
|
17
|
+
import { currentModelDescriptor } from "../model/descriptor.js";
|
|
18
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
19
|
+
import { flowLimitsOf } from "../core/limits.js";
|
|
20
|
+
import { turnManifestOf } from "../tenant/session.js";
|
|
21
|
+
import { mcpFactsOf } from "../mcp/snapshot.js";
|
|
9
22
|
import { bulkOutcomes } from "./record.js";
|
|
10
23
|
import { traceKey } from "../traces/key.js";
|
|
24
|
+
import { hostTuning } from "../tuning.js";
|
|
11
25
|
import { traceparentOf, turnSpanOf, turnTraceIdOf, } from "../traces/ids.js";
|
|
12
|
-
/** Segment rollover defaults (Model Calls §10), well inside the advance deadline. */
|
|
13
|
-
const ROLLOVER_STEPS = 50;
|
|
14
|
-
const ROLLOVER_MS = 20 * 60_000;
|
|
15
26
|
/** The segment's starting transaction and fold. */
|
|
16
27
|
export async function startSegment(ctx, lease,
|
|
17
28
|
/** For a harness: also the segment's outcomes and the sandbox's owner. */
|
|
@@ -19,34 +30,46 @@ options = {}) {
|
|
|
19
30
|
const id = lease.sessionId;
|
|
20
31
|
// Read before the transaction: the first read of the Tenant may create the key.
|
|
21
32
|
const key = options.harness ? await traceKey(ctx.store) : undefined;
|
|
22
|
-
const
|
|
33
|
+
const opened = await ownedTx(ctx.store, id, lease.epoch, async (t, current) => {
|
|
23
34
|
// A transcript left on our engine's state by an older Runtime is recorded once; another
|
|
24
35
|
// harness's state is its own (R4.2).
|
|
36
|
+
const limits = await readRuntimeLimits(t);
|
|
25
37
|
if (!isWorkflowManifest(current.manifest) && current.checkpoint && harnessOf(current) === HARNESS_ID) {
|
|
26
|
-
await adoptStoredTranscript(t, current);
|
|
38
|
+
await adoptStoredTranscript(t, current, limits.recordEntryBytes);
|
|
27
39
|
const cp = current.checkpoint;
|
|
28
40
|
current.checkpoint = { ...cp, state: current.state };
|
|
29
41
|
await t.put("sessions", id, current);
|
|
30
42
|
}
|
|
31
43
|
const fixtureModel = await usesFixtureModel(t);
|
|
32
44
|
if (!options.harness)
|
|
33
|
-
return { current, fixtureModel, outcomes: [], sandbox: { ownerId: id } };
|
|
45
|
+
return { current, fixtureModel, limits, outcomes: [], sandbox: { ownerId: id } };
|
|
34
46
|
const workspace = sandboxWorkspaceOf(current);
|
|
35
47
|
const resource = workspace.environmentId === undefined ? undefined : await t.environment(workspace.environmentId);
|
|
36
48
|
const environment = await ctx.vault.environmentVariables(t, current.environmentId);
|
|
37
|
-
const sandbox =
|
|
49
|
+
const sandbox = {
|
|
50
|
+
...workspace,
|
|
51
|
+
...(Object.keys(environment).length > 0 ? { environment } : {}),
|
|
52
|
+
...(current.sandbox === undefined ? {} : { spec: current.sandbox }),
|
|
53
|
+
};
|
|
38
54
|
const turnId = current.activeTurnId;
|
|
39
55
|
return {
|
|
40
56
|
current,
|
|
41
57
|
fixtureModel,
|
|
58
|
+
limits,
|
|
42
59
|
outcomes: current.checkpoint ? await bulkOutcomes(t, current, current.checkpoint) : [],
|
|
43
60
|
sandbox,
|
|
61
|
+
...(resource ? { environment: environmentOf(resource, current.sandbox) } : {}),
|
|
44
62
|
...(key && turnId ? { trace: turnContextOf(key, ctx.store.tenantId, current, turnId) } : {}),
|
|
45
63
|
...(resource?.pod
|
|
46
64
|
? { pod: { id: resource.id, pod: resource.pod, ...(resource.diskSeed ? { seeding: true } : {}) } }
|
|
47
65
|
: {}),
|
|
48
66
|
};
|
|
49
67
|
});
|
|
68
|
+
// The model an agent's harness decides files by, read outside the session's transaction.
|
|
69
|
+
const model = options.harness && !isWorkflowManifest(opened.current.manifest)
|
|
70
|
+
? await currentModelDescriptor(ctx)
|
|
71
|
+
: undefined;
|
|
72
|
+
const started = model ? { ...opened, model } : opened;
|
|
50
73
|
// One history (P0.3): the engine resumes from the transcript folded from the record.
|
|
51
74
|
const { current } = started;
|
|
52
75
|
const cp = current.checkpoint;
|
|
@@ -76,22 +99,35 @@ export function buildTurnStart(ctx, segment) {
|
|
|
76
99
|
checkpoint: !flow && cp.state && harnessOf(current) === HARNESS_ID
|
|
77
100
|
? { ...cp, state: withTranscript(cp.state, []) }
|
|
78
101
|
: cp,
|
|
79
|
-
// The turn's manifest: a variant may
|
|
80
|
-
...(flow
|
|
102
|
+
// The session's MCP discovery as the turn's manifest sees it: a variant may disable tools.
|
|
103
|
+
...(!flow && current.mcpSnapshot ? { mcp: mcpFactsOf(current.mcpSnapshot, turnManifestOf(current)) } : {}),
|
|
81
104
|
outcomes: segment.outcomes,
|
|
82
105
|
transcript: { cursor: segment.cursor },
|
|
83
106
|
options: {
|
|
84
107
|
...(flow
|
|
85
|
-
? { flowLimits:
|
|
108
|
+
? { flowLimits: flowLimitsOf(segment.limits) }
|
|
86
109
|
: { yieldAfter: yieldAfterOf(ctx) }),
|
|
87
110
|
fixtureModel: segment.fixtureModel,
|
|
88
111
|
},
|
|
89
112
|
routing: {
|
|
90
113
|
rootManifest: current.manifest,
|
|
91
|
-
...(current.mcpSnapshot ? { mcpSnapshot: current.mcpSnapshot } : {}),
|
|
92
114
|
sandbox: segment.sandbox,
|
|
93
115
|
},
|
|
94
116
|
...(segment.trace ? { trace: segment.trace } : {}),
|
|
117
|
+
limits: harnessLimitsOf(segment.limits),
|
|
118
|
+
...(segment.model && !flow ? { model: segment.model } : {}),
|
|
119
|
+
...(segment.environment && !flow ? { environment: segment.environment } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The facts of the Environment a session runs in (protocol 15): its id and kind, and, with the
|
|
124
|
+
* session's sandbox, the workspace its commands start in. The harness words them.
|
|
125
|
+
*/
|
|
126
|
+
function environmentOf(resource, spec) {
|
|
127
|
+
return {
|
|
128
|
+
id: resource.id,
|
|
129
|
+
kind: resource.kind,
|
|
130
|
+
...(spec === undefined ? {} : { sandbox: { workspace: SANDBOX_WORKSPACE } }),
|
|
95
131
|
};
|
|
96
132
|
}
|
|
97
133
|
/**
|
|
@@ -106,18 +142,21 @@ function turnContextOf(key, tenantId, s, turnId) {
|
|
|
106
142
|
...(s.turnTrace?.tracestate ? { tracestate: s.turnTrace.tracestate } : {}),
|
|
107
143
|
};
|
|
108
144
|
}
|
|
109
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* The engine's yield budget for a segment: the Tenant's `rollover`, else the Host tuning's
|
|
147
|
+
* (`harnessApi.rolloverSteps`, `rolloverMs`), well inside the advance deadline.
|
|
148
|
+
*/
|
|
110
149
|
export function yieldAfterOf(ctx) {
|
|
111
150
|
return {
|
|
112
|
-
steps: ctx.config.rollover?.steps ??
|
|
113
|
-
ms: ctx.config.rollover?.ms ??
|
|
151
|
+
steps: ctx.config.rollover?.steps ?? hostTuning().harnessApi.rolloverSteps,
|
|
152
|
+
ms: ctx.config.rollover?.ms ?? hostTuning().harnessApi.rolloverMs,
|
|
114
153
|
};
|
|
115
154
|
}
|
|
116
155
|
/**
|
|
117
156
|
* A session written before transcripts were recorded keeps its transcript on its row: record
|
|
118
157
|
* it once (the turn's starting transcript, then the turn so far), and store the row lean.
|
|
119
158
|
*/
|
|
120
|
-
async function adoptStoredTranscript(t, s) {
|
|
159
|
+
async function adoptStoredTranscript(t, s, recordEntryBytes) {
|
|
121
160
|
if (s.history)
|
|
122
161
|
return;
|
|
123
162
|
const now = transcriptOf(s.state);
|
|
@@ -131,9 +170,9 @@ async function adoptStoredTranscript(t, s) {
|
|
|
131
170
|
first ??= event.seq;
|
|
132
171
|
}
|
|
133
172
|
};
|
|
134
|
-
await write(null, transcriptUpdates([], start));
|
|
173
|
+
await write(null, transcriptUpdates([], start, recordEntryBytes));
|
|
135
174
|
if (s.activeTurnId)
|
|
136
|
-
await write(s.activeTurnId, transcriptUpdates(start, now));
|
|
175
|
+
await write(s.activeTurnId, transcriptUpdates(start, now, recordEntryBytes));
|
|
137
176
|
s.history = { from: first ?? 0, snapshot: first ?? 0 };
|
|
138
177
|
s.state = leanState(s.state);
|
|
139
178
|
s.turnStartState = leanState(s.turnStartState);
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
* Delivery (R5.3, R5.4). A tool call is sent at least once, and every send carries its effect
|
|
6
6
|
* id as the key. After a crash, a shutdown or a lost gate hop, a row still `invoking` is sent
|
|
7
7
|
* again: an MCP or HTTP tool call (it crosses the Tool Gate, which hands the tool the key) up to
|
|
8
|
-
* `
|
|
8
|
+
* the Tenant's Runtime limit `toolAttempts` sends, then it fails to the model as
|
|
9
|
+
* `tool.interrupted`. A sandbox or
|
|
9
10
|
* core-served call has no key to dedupe on, so a crash fails it to the model at once, and
|
|
10
11
|
* nothing runs it again (Q2). No tool call leaves its session `uncertain`.
|
|
11
12
|
*
|
|
12
|
-
* The row body keeps the journal's facts: `toolKind`, `server`, `
|
|
13
|
-
* `settledAt`. A row written before R5 has none of them.
|
|
13
|
+
* The row body keeps the journal's facts: `toolKind`, `server`, `tool` (the identity, protocol
|
|
14
|
+
* 15), `attempts`, `startedAt` and `settledAt`. A row written before R5 has none of them.
|
|
14
15
|
*
|
|
15
16
|
* Spans (R7.1). `tool.completed` also carries the call's effect id, the Tool Gate's own start
|
|
16
17
|
* and end of its last send (`gate`, from its `tool_crossings` row: MCP and HTTP calls through
|
|
@@ -23,8 +24,6 @@ import type { HostEffect } from "@nylorun/harness/run";
|
|
|
23
24
|
import type { Session } from "../tenant/context.js";
|
|
24
25
|
import { type GateTimes, type ToolCallFacts, type ToolCallKind, type ToolCallStatus, type ToolPolicyFact } from "../tenant/transcript.js";
|
|
25
26
|
import type { EffectDoc, Tx } from "../store/types.js";
|
|
26
|
-
/** How many times a tool call is sent before it fails to the model as `tool.interrupted`. */
|
|
27
|
-
export declare const MAX_TOOL_ATTEMPTS = 3;
|
|
28
27
|
/** A tool row's journal facts (R5), kept in its body. */
|
|
29
28
|
export interface ToolRowFacts {
|
|
30
29
|
/** Why the Tool Gate held the call for approval, and since when (R5 step 2). */
|
|
@@ -40,6 +39,8 @@ export interface ToolRowFacts {
|
|
|
40
39
|
};
|
|
41
40
|
toolKind?: ToolCallKind;
|
|
42
41
|
server?: string;
|
|
42
|
+
/** The tool's identity: an MCP tool's name on `server`, an HTTP tool's declared name. */
|
|
43
|
+
tool?: string;
|
|
43
44
|
/** The skill the call ran, and the plugin that brought its skill or MCP server. */
|
|
44
45
|
skill?: string;
|
|
45
46
|
plugin?: PluginLabel;
|
|
@@ -62,18 +63,22 @@ export type ToolRow = EffectDoc & ToolRowFacts & {
|
|
|
62
63
|
};
|
|
63
64
|
/** Where a tool call runs, and what it traces to (Q7, Q11). */
|
|
64
65
|
export interface ToolCallPlace {
|
|
65
|
-
|
|
66
|
+
/** Its target's kind; absent for a call that names none (an author's code tool). */
|
|
67
|
+
toolKind?: ToolCallKind;
|
|
66
68
|
/** Its MCP server or, for an agent's HTTP tool, its URL's host. */
|
|
67
69
|
server?: string;
|
|
70
|
+
/** Its identity: an MCP tool's own name on the server, an HTTP tool's declared name. */
|
|
71
|
+
tool?: string;
|
|
68
72
|
skill?: string;
|
|
69
73
|
plugin?: PluginLabel;
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
|
-
* Where a tool call runs, and what it traces to
|
|
73
|
-
*
|
|
74
|
-
*
|
|
76
|
+
* Where a tool call runs, and what it traces to (protocol 15): its target's kind; an MCP call
|
|
77
|
+
* names its identity (server and tool) and the plugin that brought the server, an HTTP call its
|
|
78
|
+
* declared name and its URL's host. The skill the effect names (`skill`: a skill tool's, or a
|
|
79
|
+
* sandbox call in a skill's folder) is kept when the agent has it, with the plugin that brought it.
|
|
75
80
|
*/
|
|
76
|
-
export declare function toolKindOf(s: Pick<Session, "manifest"
|
|
81
|
+
export declare function toolKindOf(s: Pick<Session, "manifest">, agentManifest: AgentManifest | undefined, request: HostEffect): ToolCallPlace;
|
|
77
82
|
/** The facts a new tool row starts with: its first send, now. */
|
|
78
83
|
export declare function firstSend(kind: ToolCallPlace, now?: Date): ToolRowFacts;
|
|
79
84
|
/** A failed outcome the Runtime gives a call a crash interrupted: it may have run. */
|
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import { pluginOf
|
|
1
|
+
import { pluginOf } from "@nylorun/core/define";
|
|
2
2
|
import { isFlowToolEffect } from "../core/flow-host.js";
|
|
3
|
-
import { declaredHttpTool
|
|
4
|
-
import { mcpToolOf } from "../mcp/snapshot.js";
|
|
5
|
-
import { sandboxToolSpec } from "../sandbox/runtime-tools.js";
|
|
3
|
+
import { declaredHttpTool } from "../gates/http-tool.js";
|
|
6
4
|
import { toolCompleted, } from "../tenant/transcript.js";
|
|
7
5
|
import { toolCallDuration, toolCallStatus } from "./tool-status.js";
|
|
8
|
-
/** How many times a tool call is sent before it fails to the model as `tool.interrupted`. */
|
|
9
|
-
export const MAX_TOOL_ATTEMPTS = 3;
|
|
10
6
|
/**
|
|
11
|
-
* Where a tool call runs, and what it traces to
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* Where a tool call runs, and what it traces to (protocol 15): its target's kind; an MCP call
|
|
8
|
+
* names its identity (server and tool) and the plugin that brought the server, an HTTP call its
|
|
9
|
+
* declared name and its URL's host. The skill the effect names (`skill`: a skill tool's, or a
|
|
10
|
+
* sandbox call in a skill's folder) is kept when the agent has it, with the plugin that brought it.
|
|
14
11
|
*/
|
|
15
|
-
export function toolKindOf(s, agentManifest, request
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
export function toolKindOf(s, agentManifest, request) {
|
|
13
|
+
const target = request.target;
|
|
14
|
+
const kind = target?.kind;
|
|
15
|
+
if (target?.kind === "mcp") {
|
|
16
|
+
const plugin = pluginOf(agentManifest, { server: target.server });
|
|
17
|
+
return {
|
|
18
|
+
toolKind: "mcp",
|
|
19
|
+
server: target.server,
|
|
20
|
+
tool: target.tool,
|
|
21
|
+
...(plugin === undefined ? {} : { plugin }),
|
|
22
|
+
};
|
|
20
23
|
}
|
|
21
|
-
if (
|
|
22
|
-
const host = httpHostOf(declaredHttpTool(s.manifest, request.agent?.id,
|
|
23
|
-
return { toolKind: "http", ...(host === undefined ? {} : { server: host }) };
|
|
24
|
+
if (target?.kind === "http") {
|
|
25
|
+
const host = httpHostOf(declaredHttpTool(s.manifest, request.agent?.id, target.tool)?.http.url);
|
|
26
|
+
return { toolKind: "http", ...(host === undefined ? {} : { server: host }), tool: target.tool };
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const skill = request.skill !== undefined && agentManifest?.skills?.[request.skill] !== undefined
|
|
29
|
+
? request.skill
|
|
30
|
+
: undefined;
|
|
28
31
|
const plugin = skill === undefined ? undefined : pluginOf(agentManifest, { skill });
|
|
29
|
-
|
|
32
|
+
return {
|
|
33
|
+
...(kind === undefined ? {} : { toolKind: kind }),
|
|
30
34
|
...(skill === undefined ? {} : { skill }),
|
|
31
35
|
...(plugin === undefined ? {} : { plugin }),
|
|
32
36
|
};
|
|
33
|
-
if (sandboxToolSpec(agentManifest, request.toolName))
|
|
34
|
-
return { toolKind: "sandbox", ...traced };
|
|
35
|
-
return { toolKind: "platform", ...traced };
|
|
36
37
|
}
|
|
37
38
|
/** The host (and port, when it names one) of an HTTP tool's URL. */
|
|
38
39
|
function httpHostOf(url) {
|
|
@@ -61,6 +62,7 @@ export function factsOf(row, status, gate) {
|
|
|
61
62
|
status,
|
|
62
63
|
...(row.toolKind === undefined ? {} : { kind: row.toolKind }),
|
|
63
64
|
...(row.server === undefined ? {} : { server: row.server }),
|
|
65
|
+
...(row.tool === undefined ? {} : { tool: row.tool }),
|
|
64
66
|
...(row.skill === undefined ? {} : { skill: row.skill }),
|
|
65
67
|
...(row.plugin === undefined ? {} : { plugin: row.plugin }),
|
|
66
68
|
...(row.attempts === undefined ? {} : { attempts: row.attempts }),
|