@nylorun/runtime 0.22.0-beta → 0.24.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 +161 -0
- package/README.md +95 -30
- package/dist/adapters/execution/restate.d.ts +15 -3
- package/dist/adapters/execution/restate.js +78 -84
- 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/adapters/streams/s2.js +1 -13
- package/dist/api/a2a/endpoint.js +5 -1
- package/dist/api/a2a/routes.d.ts +2 -0
- package/dist/api/a2a/routes.js +2 -2
- package/dist/api/a2a/tasks.js +2 -1
- package/dist/api/ag-ui/endpoint.js +4 -2
- package/dist/api/ag-ui/history.js +4 -1
- package/dist/api/ag-ui/routes.d.ts +4 -1
- package/dist/api/ag-ui/routes.js +11 -10
- package/dist/api/ag-ui/translate.js +3 -2
- package/dist/api/components.d.ts +4953 -1204
- package/dist/api/components.js +25 -16
- package/dist/api/gate/keys.js +7 -1
- package/dist/api/gate/routes.d.ts +6 -0
- package/dist/api/gate/routes.js +39 -3
- package/dist/api/http/app.js +4 -4
- 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/environments.d.ts +11 -0
- package/dist/api/http/routes/environments.js +283 -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 +45 -9
- package/dist/api/http/routes/reads.js +54 -5
- package/dist/api/http/routes/sessions.js +11 -6
- package/dist/api/http/routes/tenant.js +32 -10
- package/dist/api/openapi.js +31 -26
- package/dist/artifacts/disk.d.ts +59 -0
- package/dist/artifacts/disk.js +563 -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/service.d.ts +2 -1
- package/dist/artifacts/service.js +6 -1
- package/dist/artifacts/workspace.d.ts +1 -1
- package/dist/artifacts/workspace.js +1 -1
- package/dist/core/flow-host.d.ts +3 -22
- package/dist/core/flow-host.js +11 -37
- 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 +10 -43
- package/dist/execution/types.d.ts +11 -9
- package/dist/execution/types.js +5 -7
- 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 +28 -1
- package/dist/gates/egress.js +115 -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 +6 -2
- package/dist/gates/http-tool.js +3 -5
- 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 +52 -0
- package/dist/gates/mcp-handler.d.ts +4 -0
- package/dist/gates/mcp-handler.js +5 -1
- 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 +134 -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 +10 -68
- package/dist/gates/tool-contract.js +1 -85
- package/dist/gates/tool-gate.d.ts +1 -1
- package/dist/harness/calls.d.ts +12 -5
- package/dist/harness/calls.js +21 -12
- package/dist/harness/executors.js +12 -9
- package/dist/harness/main.js +18 -5
- package/dist/harness/pod.d.ts +34 -0
- package/dist/harness/pod.js +41 -0
- package/dist/harness/service.d.ts +0 -1
- package/dist/harness/service.js +13 -5
- package/dist/harness/setup.d.ts +27 -0
- package/dist/harness/setup.js +150 -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 +174 -44
- package/dist/harness-api/redact.d.ts +3 -0
- package/dist/harness-api/redact.js +45 -0
- package/dist/harness-api/server.d.ts +8 -0
- package/dist/harness-api/server.js +93 -13
- 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 +96 -0
- package/dist/harness-api/tool-journal.js +102 -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 +6 -3
- package/dist/harness-api/workspace.js +27 -11
- package/dist/harness-api/ws-server.d.ts +11 -3
- package/dist/harness-api/ws-server.js +94 -15
- package/dist/host/execution.d.ts +8 -6
- package/dist/host/gates.d.ts +2 -1
- package/dist/host/gates.js +10 -2
- package/dist/host/main.js +64 -21
- package/dist/host/operate.js +67 -5
- package/dist/host/stack-config.d.ts +70 -0
- package/dist/host/stack-config.js +67 -1
- package/dist/infra/execution.d.ts +18 -1
- package/dist/infra/execution.js +28 -4
- package/dist/infra/index.d.ts +1 -1
- package/dist/keys/client.js +4 -1
- package/dist/keys/keys.d.ts +5 -5
- package/dist/keys/keys.js +5 -7
- package/dist/management-openapi.json +999 -2211
- package/dist/mcp/connect.d.ts +25 -20
- package/dist/mcp/connect.js +29 -30
- package/dist/mcp/pool.d.ts +2 -2
- package/dist/mcp/pool.js +1 -1
- package/dist/mcp/preview.d.ts +3 -3
- package/dist/mcp/preview.js +10 -20
- package/dist/mcp/snapshot.d.ts +5 -11
- package/dist/mcp/snapshot.js +6 -7
- package/dist/model/pi-model.js +8 -0
- package/dist/openapi.json +6955 -2526
- 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/retry.d.ts +21 -0
- package/dist/retry.js +34 -0
- 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 -20
- package/dist/sandbox/manager.d.ts +16 -8
- package/dist/sandbox/manager.js +33 -17
- package/dist/sandbox/placement.d.ts +13 -12
- package/dist/sandbox/placement.js +29 -12
- package/dist/sandbox/pods/client.d.ts +6 -1
- package/dist/sandbox/pods/lifecycle.d.ts +28 -27
- package/dist/sandbox/pods/lifecycle.js +14 -26
- package/dist/sandbox/pods/reconcile.js +21 -15
- package/dist/sandbox/pods/spec.d.ts +15 -13
- package/dist/sandbox/pods/spec.js +16 -20
- package/dist/sandbox/records.d.ts +2 -2
- package/dist/sandbox/records.js +3 -3
- package/dist/sandbox/resolve.d.ts +35 -27
- package/dist/sandbox/resolve.js +66 -41
- package/dist/sandbox/session-sandbox.js +1 -1
- package/dist/sandbox/setup.d.ts +61 -0
- package/dist/sandbox/setup.js +123 -0
- package/dist/sandbox/share.d.ts +11 -21
- package/dist/sandbox/share.js +12 -41
- 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/control.js +8 -1
- package/dist/store/postgres/drizzle/0018_wake_outbox_retries.sql +3 -0
- package/dist/store/postgres/drizzle/0019_sandbox_signal_outbox.sql +18 -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/meta/_journal.json +42 -0
- package/dist/store/postgres/reads.js +117 -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 +1213 -779
- package/dist/store/postgres/schema.js +136 -76
- package/dist/store/postgres/store.js +283 -117
- package/dist/store/types.d.ts +206 -88
- package/dist/streams/relay/core.js +1 -13
- package/dist/tenant/advance.d.ts +7 -6
- package/dist/tenant/advance.js +121 -49
- package/dist/tenant/artifact-tool.js +3 -4
- package/dist/tenant/commands.d.ts +15 -2
- package/dist/tenant/commands.js +94 -14
- package/dist/tenant/context.d.ts +67 -33
- package/dist/tenant/context.js +4 -15
- package/dist/tenant/control.d.ts +8 -1
- package/dist/tenant/control.js +2 -0
- package/dist/tenant/effects.d.ts +1 -1
- package/dist/tenant/effects.js +37 -20
- package/dist/tenant/environments.d.ts +167 -0
- package/dist/tenant/environments.js +823 -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/principals.js +1 -1
- package/dist/tenant/reset.js +3 -3
- package/dist/tenant/runtime.d.ts +6 -1
- package/dist/tenant/runtime.js +30 -4
- package/dist/tenant/scheduler.d.ts +7 -0
- package/dist/tenant/scheduler.js +38 -7
- package/dist/tenant/session-streams.d.ts +0 -1
- package/dist/tenant/session-streams.js +1 -13
- package/dist/tenant/session.d.ts +3 -3
- package/dist/tenant/session.js +0 -14
- package/dist/tenant/sessions.d.ts +13 -13
- package/dist/tenant/sessions.js +61 -138
- package/dist/tenant/signing-keys.d.ts +5 -1
- package/dist/tenant/status.js +3 -3
- package/dist/tenant/streams.js +14 -29
- package/dist/tenant/sweep.d.ts +39 -12
- package/dist/tenant/sweep.js +168 -63
- package/dist/tenant/transcript.d.ts +210 -20
- package/dist/tenant/transcript.js +62 -11
- 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 +42 -0
- package/dist/traces/ids.js +85 -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 +317 -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 +64 -0
- package/dist/vault/error.d.ts +11 -1
- package/dist/vault/error.js +5 -1
- package/dist/vault/service.d.ts +47 -48
- package/dist/vault/service.js +321 -382
- 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 +14 -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/tenant/sandboxes.d.ts +0 -122
- package/dist/tenant/sandboxes.js +0 -562
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RecordedOutcome, TurnStart } from "@nylorun/core/harness-api";
|
|
2
2
|
import type { SandboxPodState } from "../store/types.js";
|
|
3
3
|
import { type Lease, type Session, type TenantContext } from "../tenant/context.js";
|
|
4
|
+
import { type TraceContext } from "../traces/ids.js";
|
|
4
5
|
/** A segment ready to run: the session as it starts, and its transcript folded from the record. */
|
|
5
6
|
export interface SegmentStart {
|
|
6
7
|
readonly current: Session;
|
|
@@ -10,18 +11,24 @@ export interface SegmentStart {
|
|
|
10
11
|
/** The record position of `transcript` (`foldTranscriptWithCursor`). */
|
|
11
12
|
readonly cursor: number;
|
|
12
13
|
readonly outcomes: RecordedOutcome[];
|
|
13
|
-
/** The
|
|
14
|
+
/** The session's sandbox workspace: the session, and the Environment whose workspace it is. */
|
|
14
15
|
readonly sandbox: {
|
|
15
16
|
readonly ownerId: string;
|
|
16
|
-
readonly
|
|
17
|
+
readonly environmentId?: string;
|
|
17
18
|
/** Variables every sandbox command gets (R2c): secret names set to the sentinel, plain values. */
|
|
18
19
|
readonly environment?: Readonly<Record<string, string>>;
|
|
19
20
|
};
|
|
20
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* The Environment is a pod (F7.2): its engine runs the segment. Its lifecycle state, and
|
|
23
|
+
* whether its new volume is still being seeded from a disk version (R2d step 3).
|
|
24
|
+
*/
|
|
21
25
|
readonly pod?: {
|
|
22
26
|
readonly id: string;
|
|
23
27
|
readonly pod: SandboxPodState;
|
|
28
|
+
readonly seeding?: boolean;
|
|
24
29
|
};
|
|
30
|
+
/** The turn's span context (R7.4): what `TurnStart.trace` hands the harness. */
|
|
31
|
+
readonly trace?: TraceContext;
|
|
25
32
|
}
|
|
26
33
|
/** The segment's starting transaction and fold. */
|
|
27
34
|
export declare function startSegment(ctx: TenantContext, lease: Lease,
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { isWorkflowManifest } from "../core/flow-host.js";
|
|
2
2
|
import { sandboxWorkspaceOf } from "../sandbox/share.js";
|
|
3
3
|
import { ownedTx } from "../store/ownership.js";
|
|
4
|
-
import {
|
|
4
|
+
import { harnessOf } from "../tenant/context.js";
|
|
5
|
+
import { HARNESS_ID } from "../sandbox/placement.js";
|
|
5
6
|
import { checkParity, foldTranscriptWithCursor, leanState, transcriptOf, transcriptUpdates, withTranscript, } from "../tenant/history.js";
|
|
6
7
|
import { usesFixtureModel } from "../tenant/model-setting.js";
|
|
7
|
-
import {
|
|
8
|
+
import { sessionToolsOf, turnManifestOf } from "../tenant/session.js";
|
|
8
9
|
import { bulkOutcomes } from "./record.js";
|
|
10
|
+
import { traceKey } from "../traces/key.js";
|
|
11
|
+
import { traceparentOf, turnSpanOf, turnTraceIdOf, } from "../traces/ids.js";
|
|
9
12
|
/** Segment rollover defaults (Model Calls §10), well inside the advance deadline. */
|
|
10
13
|
const ROLLOVER_STEPS = 50;
|
|
11
14
|
const ROLLOVER_MS = 20 * 60_000;
|
|
@@ -14,9 +17,12 @@ export async function startSegment(ctx, lease,
|
|
|
14
17
|
/** For a harness: also the segment's outcomes and the sandbox's owner. */
|
|
15
18
|
options = {}) {
|
|
16
19
|
const id = lease.sessionId;
|
|
20
|
+
// Read before the transaction: the first read of the Tenant may create the key.
|
|
21
|
+
const key = options.harness ? await traceKey(ctx.store) : undefined;
|
|
17
22
|
const started = await ownedTx(ctx.store, id, lease.epoch, async (t, current) => {
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
// A transcript left on our engine's state by an older Runtime is recorded once; another
|
|
24
|
+
// harness's state is its own (R4.2).
|
|
25
|
+
if (!isWorkflowManifest(current.manifest) && current.checkpoint && harnessOf(current) === HARNESS_ID) {
|
|
20
26
|
await adoptStoredTranscript(t, current);
|
|
21
27
|
const cp = current.checkpoint;
|
|
22
28
|
current.checkpoint = { ...cp, state: current.state };
|
|
@@ -25,22 +31,33 @@ options = {}) {
|
|
|
25
31
|
const fixtureModel = await usesFixtureModel(t);
|
|
26
32
|
if (!options.harness)
|
|
27
33
|
return { current, fixtureModel, outcomes: [], sandbox: { ownerId: id } };
|
|
28
|
-
const workspace = sandboxWorkspaceOf(current
|
|
29
|
-
const resource = workspace.
|
|
30
|
-
const
|
|
34
|
+
const workspace = sandboxWorkspaceOf(current);
|
|
35
|
+
const resource = workspace.environmentId === undefined ? undefined : await t.environment(workspace.environmentId);
|
|
36
|
+
const environment = await ctx.vault.environmentVariables(t, current.environmentId);
|
|
31
37
|
const sandbox = Object.keys(environment).length > 0 ? { ...workspace, environment } : workspace;
|
|
38
|
+
const turnId = current.activeTurnId;
|
|
32
39
|
return {
|
|
33
40
|
current,
|
|
34
41
|
fixtureModel,
|
|
35
42
|
outcomes: current.checkpoint ? await bulkOutcomes(t, current, current.checkpoint) : [],
|
|
36
43
|
sandbox,
|
|
37
|
-
...(
|
|
44
|
+
...(key && turnId ? { trace: turnContextOf(key, ctx.store.tenantId, current, turnId) } : {}),
|
|
45
|
+
...(resource?.pod
|
|
46
|
+
? { pod: { id: resource.id, pod: resource.pod, ...(resource.diskSeed ? { seeding: true } : {}) } }
|
|
47
|
+
: {}),
|
|
38
48
|
};
|
|
39
49
|
});
|
|
40
50
|
// One history (P0.3): the engine resumes from the transcript folded from the record.
|
|
41
51
|
const { current } = started;
|
|
42
52
|
const cp = current.checkpoint;
|
|
43
|
-
if (isWorkflowManifest(current.manifest)
|
|
53
|
+
if (isWorkflowManifest(current.manifest))
|
|
54
|
+
return { ...started, transcript: [], cursor: -1 };
|
|
55
|
+
// Another harness's state says nothing about its transcript (R4.2): fold it every segment.
|
|
56
|
+
if (harnessOf(current) !== HARNESS_ID) {
|
|
57
|
+
const { transcript, cursor } = await foldSession(ctx, current);
|
|
58
|
+
return { ...started, transcript, cursor };
|
|
59
|
+
}
|
|
60
|
+
if (!cp?.state)
|
|
44
61
|
return { ...started, transcript: [], cursor: -1 };
|
|
45
62
|
const { transcript, cursor } = await foldSession(ctx, current);
|
|
46
63
|
checkParity(id, transcript, transcriptOf(cp.state));
|
|
@@ -56,7 +73,7 @@ export function buildTurnStart(ctx, segment) {
|
|
|
56
73
|
type: !flow && (input?.kind === "approve" || input?.kind === "respond") ? "approval.answer" : "turn.start",
|
|
57
74
|
engine: flow ? "flow" : "agent",
|
|
58
75
|
manifest: flow ? current.manifest : turnManifestOf(current),
|
|
59
|
-
checkpoint: !flow && cp.state
|
|
76
|
+
checkpoint: !flow && cp.state && harnessOf(current) === HARNESS_ID
|
|
60
77
|
? { ...cp, state: withTranscript(cp.state, []) }
|
|
61
78
|
: cp,
|
|
62
79
|
// The turn's manifest: a variant may have disabled MCP tools or required their approval.
|
|
@@ -74,6 +91,19 @@ export function buildTurnStart(ctx, segment) {
|
|
|
74
91
|
...(current.mcpSnapshot ? { mcpSnapshot: current.mcpSnapshot } : {}),
|
|
75
92
|
sandbox: segment.sandbox,
|
|
76
93
|
},
|
|
94
|
+
...(segment.trace ? { trace: segment.trace } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The context of turn `turnId`'s span (R7.4): its trace (the caller's when its message carried a
|
|
99
|
+
* `traceparent`) and its span, as the trace projector derives them, with the caller's
|
|
100
|
+
* `tracestate`.
|
|
101
|
+
*/
|
|
102
|
+
function turnContextOf(key, tenantId, s, turnId) {
|
|
103
|
+
const traceId = turnTraceIdOf(key, { tenantId, sessionId: s.id, turnId }, s.turnTrace);
|
|
104
|
+
return {
|
|
105
|
+
traceparent: traceparentOf(traceId, turnSpanOf(key, turnId)),
|
|
106
|
+
...(s.turnTrace?.tracestate ? { tracestate: s.turnTrace.tracestate } : {}),
|
|
77
107
|
};
|
|
78
108
|
}
|
|
79
109
|
/** The engine's yield budget for a segment. */
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The effect journal's tool calls (R5): every tool call is a row of `nylorun.effects`, and how
|
|
3
|
+
* it ends is one `tool.completed` with its status, kind, server, attempts and duration (R5.1).
|
|
4
|
+
*
|
|
5
|
+
* Delivery (R5.3, R5.4). A tool call is sent at least once, and every send carries its effect
|
|
6
|
+
* id as the key. After a crash, a shutdown or a lost gate hop, a row still `invoking` is sent
|
|
7
|
+
* again: an MCP or HTTP tool call (it crosses the Tool Gate, which hands the tool the key) up to
|
|
8
|
+
* `MAX_TOOL_ATTEMPTS` sends, then it fails to the model as `tool.interrupted`. A sandbox or
|
|
9
|
+
* core-served call has no key to dedupe on, so a crash fails it to the model at once, and
|
|
10
|
+
* nothing runs it again (Q2). No tool call leaves its session `uncertain`.
|
|
11
|
+
*
|
|
12
|
+
* The row body keeps the journal's facts: `toolKind`, `server`, `attempts`, `startedAt` and
|
|
13
|
+
* `settledAt`. A row written before R5 has none of them.
|
|
14
|
+
*
|
|
15
|
+
* Spans (R7.1). `tool.completed` also carries the call's effect id, the Tool Gate's own start
|
|
16
|
+
* and end of its last send (`gate`, from its `tool_crossings` row: MCP and HTTP calls through
|
|
17
|
+
* the gateway, while the row is kept), and what the tool policy did when it did more than allow
|
|
18
|
+
* (`policy`). A flow's HTTP stage, which names no tool call, ends in `node.completed` instead.
|
|
19
|
+
*/
|
|
20
|
+
import type { EventWriter } from "@nylorun/core/contracts";
|
|
21
|
+
import type { AgentManifest } from "@nylorun/core/define";
|
|
22
|
+
import type { HostEffect } from "@nylorun/harness/run";
|
|
23
|
+
import type { Session } from "../tenant/context.js";
|
|
24
|
+
import { type GateTimes, type ToolCallFacts, type ToolCallKind, type ToolCallStatus, type ToolPolicyFact } from "../tenant/transcript.js";
|
|
25
|
+
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
|
+
/** A tool row's journal facts (R5), kept in its body. */
|
|
29
|
+
export interface ToolRowFacts {
|
|
30
|
+
/** Why the Tool Gate held the call for approval, and since when (R5 step 2). */
|
|
31
|
+
hold?: {
|
|
32
|
+
reason: string;
|
|
33
|
+
at: string;
|
|
34
|
+
};
|
|
35
|
+
/** A person's approval of a held call (R5 step 2): the gate then sends it. */
|
|
36
|
+
approval?: {
|
|
37
|
+
approved: true;
|
|
38
|
+
at: string;
|
|
39
|
+
by?: string;
|
|
40
|
+
};
|
|
41
|
+
toolKind?: ToolCallKind;
|
|
42
|
+
server?: string;
|
|
43
|
+
attempts?: number;
|
|
44
|
+
startedAt?: string;
|
|
45
|
+
settledAt?: string;
|
|
46
|
+
/**
|
|
47
|
+
* The tool span's context (R7.4), `00-<trace>-<span>-01`: the Tool Gate reads it with the row
|
|
48
|
+
* and sends it as `traceparent`, and a harness hands it to a sandbox command as `TRACEPARENT`.
|
|
49
|
+
*/
|
|
50
|
+
traceparent?: string;
|
|
51
|
+
}
|
|
52
|
+
export type ToolRow = EffectDoc & ToolRowFacts & {
|
|
53
|
+
requestHash?: string;
|
|
54
|
+
outcome?: {
|
|
55
|
+
value: unknown;
|
|
56
|
+
statePatch?: Record<string, unknown>;
|
|
57
|
+
};
|
|
58
|
+
error?: string;
|
|
59
|
+
};
|
|
60
|
+
/** Where a tool call runs, and its MCP server or HTTP tool capability. */
|
|
61
|
+
export declare function toolKindOf(s: Pick<Session, "manifest" | "mcpSnapshot">, agentManifest: AgentManifest | undefined, request: HostEffect): {
|
|
62
|
+
toolKind: ToolCallKind;
|
|
63
|
+
server?: string;
|
|
64
|
+
};
|
|
65
|
+
/** The facts a new tool row starts with: its first send, now. */
|
|
66
|
+
export declare function firstSend(kind: {
|
|
67
|
+
toolKind: ToolCallKind;
|
|
68
|
+
server?: string;
|
|
69
|
+
}, now?: Date): ToolRowFacts;
|
|
70
|
+
/** A failed outcome the Runtime gives a call a crash interrupted: it may have run. */
|
|
71
|
+
export declare function interrupted(message: string): {
|
|
72
|
+
kind: string;
|
|
73
|
+
code: string;
|
|
74
|
+
message: string;
|
|
75
|
+
retryable: boolean;
|
|
76
|
+
};
|
|
77
|
+
/** The facts `tool.completed` carries for `row`, settled with `status`, and the gate's times. */
|
|
78
|
+
export declare function factsOf(row: ToolRow, status: ToolCallStatus, gate?: GateTimes): ToolCallFacts;
|
|
79
|
+
/**
|
|
80
|
+
* What the Tenant's tool policy did with `row` (R5 step 2), when it did more than allow it: held
|
|
81
|
+
* it for a person (`ask`, with who approved it, when someone did), or refused it unsent (`deny`).
|
|
82
|
+
*/
|
|
83
|
+
export declare function policyOf(row: ToolRow): ToolPolicyFact | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Settles a tool row with `value` (`completed`) or as `cancelled`, and writes its one
|
|
86
|
+
* `tool.completed` (an agent's tool call), or `node.completed` (a flow's HTTP stage or verifier,
|
|
87
|
+
* which names no call, R7.1). Writes `row`.
|
|
88
|
+
*/
|
|
89
|
+
export declare function settleToolRow(t: Tx, s: Pick<Session, "id">, row: ToolRow, value: unknown, options?: {
|
|
90
|
+
cancelled?: boolean;
|
|
91
|
+
now?: Date;
|
|
92
|
+
/** Who reported the outcome (R4.9): the harness that ran the call; absent, core. */
|
|
93
|
+
writer?: EventWriter;
|
|
94
|
+
}): Promise<{
|
|
95
|
+
value: unknown;
|
|
96
|
+
}>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { isFlowToolEffect } from "../core/flow-host.js";
|
|
2
|
+
import { isHttpToolCall } from "../gates/http-tool.js";
|
|
3
|
+
import { mcpToolOf } from "../mcp/snapshot.js";
|
|
4
|
+
import { sandboxCapabilityOf } from "../sandbox/capability.js";
|
|
5
|
+
import { toolCompleted, } from "../tenant/transcript.js";
|
|
6
|
+
import { toolCallDuration, toolCallStatus } from "./tool-status.js";
|
|
7
|
+
/** How many times a tool call is sent before it fails to the model as `tool.interrupted`. */
|
|
8
|
+
export const MAX_TOOL_ATTEMPTS = 3;
|
|
9
|
+
/** Where a tool call runs, and its MCP server or HTTP tool capability. */
|
|
10
|
+
export function toolKindOf(s, agentManifest, request) {
|
|
11
|
+
const mcp = mcpToolOf(s.mcpSnapshot, request);
|
|
12
|
+
if (mcp)
|
|
13
|
+
return { toolKind: "mcp", server: mcp.serverName };
|
|
14
|
+
if (isHttpToolCall(s.manifest, request))
|
|
15
|
+
return { toolKind: "http", ...(request.capabilityId ? { server: request.capabilityId } : {}) };
|
|
16
|
+
if (sandboxCapabilityOf(agentManifest, request.capabilityId, request.toolName))
|
|
17
|
+
return { toolKind: "sandbox" };
|
|
18
|
+
return { toolKind: "platform" };
|
|
19
|
+
}
|
|
20
|
+
/** The facts a new tool row starts with: its first send, now. */
|
|
21
|
+
export function firstSend(kind, now = new Date()) {
|
|
22
|
+
return { ...kind, attempts: 1, startedAt: now.toISOString() };
|
|
23
|
+
}
|
|
24
|
+
/** A failed outcome the Runtime gives a call a crash interrupted: it may have run. */
|
|
25
|
+
export function interrupted(message) {
|
|
26
|
+
return { kind: "failed", code: "tool.interrupted", message, retryable: false };
|
|
27
|
+
}
|
|
28
|
+
/** The facts `tool.completed` carries for `row`, settled with `status`, and the gate's times. */
|
|
29
|
+
export function factsOf(row, status, gate) {
|
|
30
|
+
const durationMs = toolCallDuration(row);
|
|
31
|
+
const policy = policyOf(row);
|
|
32
|
+
return {
|
|
33
|
+
status,
|
|
34
|
+
...(row.toolKind === undefined ? {} : { kind: row.toolKind }),
|
|
35
|
+
...(row.server === undefined ? {} : { server: row.server }),
|
|
36
|
+
...(row.attempts === undefined ? {} : { attempts: row.attempts }),
|
|
37
|
+
...(durationMs === undefined ? {} : { durationMs }),
|
|
38
|
+
effectId: row.request.effectId,
|
|
39
|
+
...(gate === undefined ? {} : { gate }),
|
|
40
|
+
...(policy === undefined ? {} : { policy }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* What the Tenant's tool policy did with `row` (R5 step 2), when it did more than allow it: held
|
|
45
|
+
* it for a person (`ask`, with who approved it, when someone did), or refused it unsent (`deny`).
|
|
46
|
+
*/
|
|
47
|
+
export function policyOf(row) {
|
|
48
|
+
if (row.hold)
|
|
49
|
+
return {
|
|
50
|
+
decision: "ask",
|
|
51
|
+
reason: row.hold.reason,
|
|
52
|
+
...(row.approval?.by === undefined ? {} : { approvedBy: row.approval.by }),
|
|
53
|
+
};
|
|
54
|
+
const value = row.outcome?.value;
|
|
55
|
+
// `bypolicy` refuses a call before its first send.
|
|
56
|
+
if (row.attempts === 0 && value?.code === "tool.denied")
|
|
57
|
+
return { decision: "deny", reason: String(value.message ?? "") };
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The Tool Gate's own start and end of `row`'s last send (R7.1), from its `tool_crossings` row:
|
|
62
|
+
* an MCP or HTTP call the gateway ran and settled. Undefined for any other call, one never sent,
|
|
63
|
+
* and one whose crossing is gone (pruned a day after it settled) or not settled.
|
|
64
|
+
*/
|
|
65
|
+
async function gateOf(t, row) {
|
|
66
|
+
if ((row.toolKind !== "mcp" && row.toolKind !== "http") || !row.attempts)
|
|
67
|
+
return undefined;
|
|
68
|
+
const crossing = await t.toolCrossing(row.request.effectId);
|
|
69
|
+
return crossing?.settledAt ? { startedAt: crossing.startedAt, endedAt: crossing.settledAt } : undefined;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Settles a tool row with `value` (`completed`) or as `cancelled`, and writes its one
|
|
73
|
+
* `tool.completed` (an agent's tool call), or `node.completed` (a flow's HTTP stage or verifier,
|
|
74
|
+
* which names no call, R7.1). Writes `row`.
|
|
75
|
+
*/
|
|
76
|
+
export async function settleToolRow(t, s, row, value, options = {}) {
|
|
77
|
+
const request = row.request;
|
|
78
|
+
row.status = options.cancelled ? "cancelled" : "completed";
|
|
79
|
+
row.outcome = { value };
|
|
80
|
+
row.settledAt = (options.now ?? new Date()).toISOString();
|
|
81
|
+
await t.put("effects", request.effectId, row);
|
|
82
|
+
const status = toolCallStatus(row);
|
|
83
|
+
const gate = await gateOf(t, row);
|
|
84
|
+
if (isFlowToolEffect(request)) {
|
|
85
|
+
const durationMs = toolCallDuration(row);
|
|
86
|
+
await t.event(s.id, request.turnId, "node.completed", {
|
|
87
|
+
path: request.path,
|
|
88
|
+
kind: row.toolKind ?? "http",
|
|
89
|
+
key: request.key,
|
|
90
|
+
...(request.iterations !== undefined ? { iterations: request.iterations } : {}),
|
|
91
|
+
effectId: request.effectId,
|
|
92
|
+
status,
|
|
93
|
+
...(durationMs === undefined ? {} : { durationMs }),
|
|
94
|
+
...(gate === undefined ? {} : { gate }),
|
|
95
|
+
}, options.writer);
|
|
96
|
+
return row.outcome;
|
|
97
|
+
}
|
|
98
|
+
const transcript = toolCompleted(request, value, factsOf(row, status, gate));
|
|
99
|
+
if (transcript)
|
|
100
|
+
await t.event(s.id, request.turnId, "tool.completed", transcript, options.writer);
|
|
101
|
+
return row.outcome;
|
|
102
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** What a tool row's body says of how it ended. */
|
|
2
|
+
export interface ToolRowState {
|
|
3
|
+
readonly status: string;
|
|
4
|
+
readonly outcome?: {
|
|
5
|
+
readonly value: unknown;
|
|
6
|
+
};
|
|
7
|
+
readonly startedAt?: string;
|
|
8
|
+
readonly settledAt?: string;
|
|
9
|
+
}
|
|
10
|
+
/** How a tool row ended, as `tool.completed` and `/calls/tool` say it. */
|
|
11
|
+
export declare function toolCallStatus(row: ToolRowState): "completed" | "failed" | "cancelled" | "invoking" | "held" | "uncertain";
|
|
12
|
+
/** From the first send to the outcome, or undefined when the call never ran or has not ended. */
|
|
13
|
+
export declare function toolCallDuration(row: ToolRowState): number | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a tool row of the effect journal reads (R5.1, R5.2), from its body alone: shared by the
|
|
3
|
+
* journal (`tool-journal.ts`) and the session reads (`/calls/tool`).
|
|
4
|
+
*/
|
|
5
|
+
import { durationBetween } from "../tenant/transcript.js";
|
|
6
|
+
/** The codes of a call the Runtime ended rather than the tool: its status is `failed`. */
|
|
7
|
+
const RUNTIME_ENDED = new Set(["tool.disabled", "tool.unavailable", "tool.interrupted", "tool.denied"]);
|
|
8
|
+
/** How a tool row ended, as `tool.completed` and `/calls/tool` say it. */
|
|
9
|
+
export function toolCallStatus(row) {
|
|
10
|
+
if (row.status === "cancelled")
|
|
11
|
+
return "cancelled";
|
|
12
|
+
if (row.status === "held")
|
|
13
|
+
return "held";
|
|
14
|
+
if (row.status === "uncertain")
|
|
15
|
+
return "uncertain";
|
|
16
|
+
if (row.status !== "completed")
|
|
17
|
+
return "invoking";
|
|
18
|
+
const value = row.outcome?.value;
|
|
19
|
+
return value?.kind === "failed" && typeof value.code === "string" && RUNTIME_ENDED.has(value.code)
|
|
20
|
+
? "failed"
|
|
21
|
+
: "completed";
|
|
22
|
+
}
|
|
23
|
+
/** From the first send to the outcome, or undefined when the call never ran or has not ended. */
|
|
24
|
+
export function toolCallDuration(row) {
|
|
25
|
+
return durationBetween(row.startedAt, row.settledAt);
|
|
26
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CapabilityManifest, SandboxToolName } from "@nylorun/core/define";
|
|
2
2
|
import type { SandboxManager, SandboxSessionRef } from "../sandbox/manager.js";
|
|
3
|
+
import { type DiskOp } from "../sandbox/disk-fs.js";
|
|
3
4
|
import type { SandboxSelectionReport } from "../sandbox/select.js";
|
|
4
5
|
import type { SandboxToolOutcome } from "../sandbox/tools.js";
|
|
5
6
|
import type { SessionStore } from "../store/types.js";
|
|
@@ -17,6 +18,8 @@ export interface WorkspacePort {
|
|
|
17
18
|
listFiles(session: SandboxSessionRef, capability: CapabilityManifest, dir: string, maxEntries: number, signal: AbortSignal): ReturnType<SandboxManager["listFiles"]>;
|
|
18
19
|
/** Reads a file of the workspace of `session` as bytes, at most `maxBytes` (`save_artifact`). */
|
|
19
20
|
readBytes(session: SandboxSessionRef, capability: CapabilityManifest, path: string, maxBytes: number, signal: AbortSignal): ReturnType<SandboxManager["readBytes"]>;
|
|
21
|
+
/** One operation on the disk of the workspace of `session` (R2d step 3). */
|
|
22
|
+
disk(session: SandboxSessionRef, capability: CapabilityManifest, op: DiskOp, signal: AbortSignal): ReturnType<SandboxManager["disk"]>;
|
|
20
23
|
report(): Promise<WorkspaceReport>;
|
|
21
24
|
/** One Tenant sweep pass: stop idle workspaces, remove those whose owner is gone. */
|
|
22
25
|
sweep(options: {
|
|
@@ -24,8 +27,8 @@ export interface WorkspacePort {
|
|
|
24
27
|
sessionExists: Exists;
|
|
25
28
|
sandboxExists: Exists;
|
|
26
29
|
}): Promise<void>;
|
|
27
|
-
/** Deletes sandbox resource `
|
|
28
|
-
removeSandbox(
|
|
30
|
+
/** Deletes sandbox resource `environmentId`'s workspace (the resource was deleted). */
|
|
31
|
+
removeSandbox(environmentId: string): Promise<void>;
|
|
29
32
|
/** Deletes every workspace with its files (a sandboxes reset). */
|
|
30
33
|
removeAll(): Promise<void>;
|
|
31
34
|
close(): Promise<void>;
|
|
@@ -48,6 +51,6 @@ export declare function remoteWorkspace(options: RemoteWorkspaceOptions): Worksp
|
|
|
48
51
|
* everything else goes to `base`. `podOf` says whether a sandbox resource is a pod sandbox.
|
|
49
52
|
*/
|
|
50
53
|
export declare function withPodWorkspaces(base: WorkspacePort, options: Omit<RemoteWorkspaceOptions, "pod"> & {
|
|
51
|
-
readonly isPod: (
|
|
54
|
+
readonly isPod: (environmentId: string) => Promise<boolean>;
|
|
52
55
|
}): WorkspacePort;
|
|
53
56
|
export {};
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
* - **Pod sandboxes** (F7.2, `withPodWorkspaces`): a session attached to a pod sandbox has its
|
|
16
16
|
* workspace in the pod, served by the sandbox's engine (its host connection). Its tool calls,
|
|
17
17
|
* listings and reads go there whatever serves the other workspaces; with the pod stopped or
|
|
18
|
-
* starting they are `409
|
|
18
|
+
* starting they are `409 environment_unavailable`.
|
|
19
19
|
*/
|
|
20
20
|
import { HarnessApiError } from "@nylorun/core/harness-api";
|
|
21
|
+
import { fromWireAnswer, toWireOp } from "../sandbox/disk-fs.js";
|
|
21
22
|
import { DEFAULT_SANDBOX_IMAGE } from "../sandbox/policy.js";
|
|
22
23
|
import { fail } from "../tenant/http.js";
|
|
23
24
|
import { NoWorkspaceHarness } from "./server.js";
|
|
@@ -27,9 +28,10 @@ export function localWorkspace(manager) {
|
|
|
27
28
|
run: (session, capability, toolName, input, signal) => manager.run(session, capability, toolName, input, signal),
|
|
28
29
|
listFiles: (session, capability, dir, maxEntries, signal) => manager.listFiles(session, capability, dir, maxEntries, signal),
|
|
29
30
|
readBytes: (session, capability, path, maxBytes, signal) => manager.readBytes(session, capability, path, maxBytes, signal),
|
|
31
|
+
disk: (session, capability, op, signal) => manager.disk(session, capability, op, signal),
|
|
30
32
|
report: () => manager.report(),
|
|
31
33
|
sweep: (options) => manager.sweep(options),
|
|
32
|
-
removeSandbox: (
|
|
34
|
+
removeSandbox: (environmentId) => manager.removeSandbox(environmentId),
|
|
33
35
|
removeAll: () => manager.reconcile(() => false, () => false),
|
|
34
36
|
close: () => manager.close(),
|
|
35
37
|
};
|
|
@@ -45,7 +47,7 @@ const METHODS = {
|
|
|
45
47
|
function sessionOf(session) {
|
|
46
48
|
return {
|
|
47
49
|
ownerId: session.id,
|
|
48
|
-
...(session.
|
|
50
|
+
...(session.environmentId === undefined ? {} : { environmentId: session.environmentId }),
|
|
49
51
|
activeTurnId: session.activeTurnId,
|
|
50
52
|
};
|
|
51
53
|
}
|
|
@@ -55,7 +57,7 @@ export function remoteWorkspace(options) {
|
|
|
55
57
|
const target = options.pod === undefined ? undefined : { pod: options.pod };
|
|
56
58
|
const unavailable = (error) => error.pod === undefined
|
|
57
59
|
? fail(503, error.message, { code: "request_rejected" })
|
|
58
|
-
: fail(409, error.message, { code: "
|
|
60
|
+
: fail(409, error.message, { code: "environment_unavailable" });
|
|
59
61
|
/** The harness's answer, or `undefined` when none serves workspaces. */
|
|
60
62
|
const ask = async (method, params, signal) => {
|
|
61
63
|
try {
|
|
@@ -74,9 +76,9 @@ export function remoteWorkspace(options) {
|
|
|
74
76
|
const { workspaces } = answer;
|
|
75
77
|
const gone = [];
|
|
76
78
|
for (const record of workspaces) {
|
|
77
|
-
const kept = record.
|
|
79
|
+
const kept = record.environmentId === undefined
|
|
78
80
|
? await sessionExists(record.sessionId)
|
|
79
|
-
: await sandboxExists(record.
|
|
81
|
+
: await sandboxExists(record.environmentId);
|
|
80
82
|
if (!kept)
|
|
81
83
|
gone.push(record.key);
|
|
82
84
|
}
|
|
@@ -154,6 +156,19 @@ export function remoteWorkspace(options) {
|
|
|
154
156
|
throw error;
|
|
155
157
|
}
|
|
156
158
|
},
|
|
159
|
+
async disk(session, capability, op, signal) {
|
|
160
|
+
try {
|
|
161
|
+
const answer = (await options.server.workspace("list" in op || "read" in op ? "workspace.read" : "workspace.write", { session: sessionOf(session), spec: capability.sandbox ?? {}, disk: toWireOp(op) }, signal, target));
|
|
162
|
+
return answer.kind === "failed"
|
|
163
|
+
? { kind: "failed", code: String(answer.code), message: String(answer.message) }
|
|
164
|
+
: fromWireAnswer(answer);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (error instanceof NoWorkspaceHarness)
|
|
168
|
+
return unavailable(error);
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
157
172
|
async report() {
|
|
158
173
|
const answer = await ask("workspace.report", {}).catch(() => undefined);
|
|
159
174
|
if (answer)
|
|
@@ -168,12 +183,12 @@ export function remoteWorkspace(options) {
|
|
|
168
183
|
};
|
|
169
184
|
},
|
|
170
185
|
sweep: (pass) => sweep(pass.sessionExists, pass.sandboxExists, pass.now),
|
|
171
|
-
async removeSandbox(
|
|
186
|
+
async removeSandbox(environmentId) {
|
|
172
187
|
// With no harness connected, the next sweep removes it: its resource is gone.
|
|
173
|
-
await ask("workspace.remove", {
|
|
188
|
+
await ask("workspace.remove", { environmentIds: [environmentId] });
|
|
174
189
|
await store.tx(async (t) => {
|
|
175
190
|
for (const row of await t.listSandboxes())
|
|
176
|
-
if (row.
|
|
191
|
+
if (row.environmentId === environmentId)
|
|
177
192
|
await t.delete("sandboxes", row.key);
|
|
178
193
|
});
|
|
179
194
|
},
|
|
@@ -188,13 +203,14 @@ export function remoteWorkspace(options) {
|
|
|
188
203
|
* everything else goes to `base`. `podOf` says whether a sandbox resource is a pod sandbox.
|
|
189
204
|
*/
|
|
190
205
|
export function withPodWorkspaces(base, options) {
|
|
191
|
-
const routed = async (session) => session.
|
|
192
|
-
? remoteWorkspace({ ...options, pod: session.
|
|
206
|
+
const routed = async (session) => session.environmentId !== undefined && (await options.isPod(session.environmentId))
|
|
207
|
+
? remoteWorkspace({ ...options, pod: session.environmentId })
|
|
193
208
|
: base;
|
|
194
209
|
return {
|
|
195
210
|
...base,
|
|
196
211
|
run: async (session, capability, toolName, input, signal) => (await routed(session)).run(session, capability, toolName, input, signal),
|
|
197
212
|
listFiles: async (session, capability, dir, maxEntries, signal) => (await routed(session)).listFiles(session, capability, dir, maxEntries, signal),
|
|
198
213
|
readBytes: async (session, capability, path, maxBytes, signal) => (await routed(session)).readBytes(session, capability, path, maxBytes, signal),
|
|
214
|
+
disk: async (session, capability, op, signal) => (await routed(session)).disk(session, capability, op, signal),
|
|
199
215
|
};
|
|
200
216
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type Server } from "node:http";
|
|
2
2
|
import { type HarnessChannel } from "@nylorun/core/harness-api";
|
|
3
|
+
import { HOST_JOIN_PATH, HOST_RENEW_PATH } from "@nylorun/core/harness-api";
|
|
3
4
|
import { type WsPortOptions } from "../harness/ws-port.js";
|
|
4
5
|
import { type HostAuthority } from "../sandbox/join.js";
|
|
5
6
|
import type { HarnessPeer } from "./server.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare const
|
|
7
|
+
export { HOST_JOIN_PATH, HOST_RENEW_PATH };
|
|
8
|
+
/** Where a pod's engine reads its setup and reports it (R2d step 2, `sandbox/setup.ts`). */
|
|
9
|
+
export declare const HOST_SETUP_PATH = "/nylorun/harness/v1/host/setup";
|
|
10
|
+
/** Where it reads its input files: `/{index}`, and a folder's file `/{index}/{path}`. */
|
|
11
|
+
export declare const HOST_FILES_PATH = "/nylorun/harness/v1/host/files";
|
|
9
12
|
/** Attaches a harness's channel to the open Tenant; returns a function that detaches it. */
|
|
10
13
|
export type HarnessAttach = (channel: HarnessChannel, peer: HarnessPeer) => () => void;
|
|
11
14
|
export interface HarnessListenerOptions {
|
|
@@ -17,6 +20,11 @@ export interface HarnessListenerOptions {
|
|
|
17
20
|
readonly token?: string;
|
|
18
21
|
/** The open Tenant's `attachHarness`, or undefined while there is none. */
|
|
19
22
|
readonly attach: () => Promise<HarnessAttach | undefined>;
|
|
23
|
+
/**
|
|
24
|
+
* The custom harness a per-harness token is for (R4.1), or undefined. Absent, only the harness
|
|
25
|
+
* token and host tokens are accepted.
|
|
26
|
+
*/
|
|
27
|
+
readonly harnessOfToken?: (token: string) => Promise<string | undefined>;
|
|
20
28
|
/** The open Tenant's host authority (sandbox pods, F7.2), or undefined without one. */
|
|
21
29
|
readonly hosts?: () => Promise<HostAuthority | undefined>;
|
|
22
30
|
readonly logger: {
|