@nylorun/runtime 0.26.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 +130 -0
- package/README.md +18 -14
- 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/body.d.ts +15 -3
- package/dist/api/http/body.js +15 -7
- package/dist/api/http/respond.js +6 -2
- 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 +11 -10
- 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 +14 -16
- 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 +40 -12
- 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 +0 -2
- package/dist/keys/contract.js +0 -2
- 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 +12 -5
- package/dist/mcp/preview.js +36 -19
- 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 +838 -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/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 +49 -28
- 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 +11 -10
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +23 -0
- package/dist/tenant/identity-file.js +34 -6
- 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 +11 -14
- package/dist/tenant/outbound.js +10 -15
- 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 +9 -14
- 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
package/dist/tenant/module.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { openError, TenantOpenError } from "./cause.js";
|
|
2
2
|
import { TimeoutError, withTimeout } from "./pool.js";
|
|
3
|
+
import { hostTuning } from "../tuning.js";
|
|
3
4
|
import { TenantUnavailableError, } from "./types.js";
|
|
4
|
-
const OPEN_TIMEOUT_MS = 30_000;
|
|
5
|
-
/** Retries of an open that failed outside the Tenant: from 1 s, doubling, at most 30 s apart. */
|
|
6
|
-
const RETRY_FIRST_MS = 1_000;
|
|
7
|
-
const RETRY_MAX_MS = 30_000;
|
|
8
5
|
/**
|
|
9
6
|
* Deep Tenant module (§8): the Host's one Tenant over an injected opener (tenancy.md §5).
|
|
10
7
|
*
|
|
@@ -15,11 +12,15 @@ const RETRY_MAX_MS = 30_000;
|
|
|
15
12
|
* with backoff, and by the next request, which gets 503 meanwhile.
|
|
16
13
|
*/
|
|
17
14
|
export function createTenantModule(options) {
|
|
18
|
-
const
|
|
15
|
+
const tuning = hostTuning().tenant;
|
|
16
|
+
const { logger, openTimeoutMs = tuning.openTimeoutMs } = options;
|
|
17
|
+
// Retries of an open that failed outside the Tenant: from `openRetry.minMs`, doubling, at most
|
|
18
|
+
// `openRetry.maxMs` apart.
|
|
19
|
+
const { minMs: firstRetryMs, maxMs: maxRetryMs } = tuning.openRetry;
|
|
19
20
|
let state = { kind: "idle" };
|
|
20
21
|
let opening;
|
|
21
22
|
let retry;
|
|
22
|
-
let nextDelayMs =
|
|
23
|
+
let nextDelayMs = firstRetryMs;
|
|
23
24
|
const current = () => state;
|
|
24
25
|
async function openOnce() {
|
|
25
26
|
let handle;
|
|
@@ -64,7 +65,7 @@ export function createTenantModule(options) {
|
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
state = { kind: "open", handle };
|
|
67
|
-
nextDelayMs =
|
|
68
|
+
nextDelayMs = firstRetryMs;
|
|
68
69
|
logger.info("tenant opened", { tenantId: handle.envelope.id });
|
|
69
70
|
try {
|
|
70
71
|
await options.onOpen?.(handle);
|
|
@@ -92,7 +93,7 @@ export function createTenantModule(options) {
|
|
|
92
93
|
if (retry)
|
|
93
94
|
return;
|
|
94
95
|
const delayMs = nextDelayMs;
|
|
95
|
-
nextDelayMs = Math.min(delayMs * 2,
|
|
96
|
+
nextDelayMs = Math.min(delayMs * 2, maxRetryMs);
|
|
96
97
|
retry = setTimeout(() => {
|
|
97
98
|
retry = undefined;
|
|
98
99
|
void load();
|
|
@@ -30,8 +30,6 @@ export type OutboundResult = {
|
|
|
30
30
|
code: string;
|
|
31
31
|
message: string;
|
|
32
32
|
};
|
|
33
|
-
/** The largest answer read from an endpoint. */
|
|
34
|
-
export declare const MAX_RESPONSE_BYTES: number;
|
|
35
33
|
/** True when `address` is private, loopback, link-local or unspecified (IPv4-mapped included). */
|
|
36
34
|
export declare function isPrivateAddress(address: string): boolean;
|
|
37
35
|
/** Why the Host refuses `url`, or `undefined` when it may be called. */
|
|
@@ -44,7 +42,8 @@ export declare function refusal(url: URL, policy: OutboundPolicy): string | unde
|
|
|
44
42
|
export declare function send(target: string, body: string | undefined, headers: Record<string, string>, options: {
|
|
45
43
|
policy: OutboundPolicy;
|
|
46
44
|
signal: AbortSignal;
|
|
47
|
-
|
|
45
|
+
/** The largest answer read: the Tenant's Runtime limit `toolAnswerBytes` for an HTTP tool. */
|
|
46
|
+
maxResponseBytes: number;
|
|
48
47
|
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
49
48
|
}): Promise<OutboundResult>;
|
|
50
49
|
/**
|
|
@@ -71,23 +70,21 @@ export declare class OutboundFailed extends Error {
|
|
|
71
70
|
*/
|
|
72
71
|
sent?: boolean);
|
|
73
72
|
}
|
|
74
|
-
/** The largest answer `guardedFetch` reads. */
|
|
75
|
-
export declare const MAX_FETCH_RESPONSE_BYTES: number;
|
|
76
|
-
/** How long a `guardedFetch` request may take without a signal of its own. */
|
|
77
|
-
export declare const FETCH_TIMEOUT_MS = 30000;
|
|
78
73
|
/**
|
|
79
74
|
* A `fetch` under the Host's address policy (F9 C2): the literal-IP and DNS checks of `post`
|
|
80
75
|
* on the address actually connected to, `localhost` rewritten for the local stack, no
|
|
81
|
-
* redirects (`redirect: "error"`; a 3xx answer rejects),
|
|
82
|
-
* unless the caller passes a signal
|
|
83
|
-
*
|
|
76
|
+
* redirects (`redirect: "error"`; a 3xx answer rejects), an answer of at most `maxResponseBytes`
|
|
77
|
+
* and a `timeoutMs` timeout unless the caller passes a signal, both the caller's (a Tenant's
|
|
78
|
+
* Runtime limits). A refused URL or address rejects with `OutboundRefused`, any other failure
|
|
79
|
+
* with `OutboundFailed`.
|
|
84
80
|
*
|
|
85
81
|
* `stream`: the Response resolves once the headers arrive and its body streams, unbounded and
|
|
86
82
|
* with no timeout but the caller's signal: remote MCP servers, whose answers and event streams
|
|
87
83
|
* stay open (`mcp/connect.ts`).
|
|
88
84
|
*/
|
|
89
|
-
export declare function guardedFetch(policy: OutboundPolicy, options
|
|
90
|
-
maxResponseBytes
|
|
91
|
-
timeoutMs
|
|
92
|
-
|
|
85
|
+
export declare function guardedFetch(policy: OutboundPolicy, options: {
|
|
86
|
+
maxResponseBytes: number;
|
|
87
|
+
timeoutMs: number;
|
|
88
|
+
} | {
|
|
89
|
+
stream: true;
|
|
93
90
|
}): typeof fetch;
|
package/dist/tenant/outbound.js
CHANGED
|
@@ -13,8 +13,6 @@ import { request as httpRequest } from "node:http";
|
|
|
13
13
|
import { request as httpsRequest } from "node:https";
|
|
14
14
|
import { BlockList, isIP } from "node:net";
|
|
15
15
|
import { Readable } from "node:stream";
|
|
16
|
-
/** The largest answer read from an endpoint. */
|
|
17
|
-
export const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
18
16
|
const PRIVATE = new BlockList();
|
|
19
17
|
for (const [network, prefix] of [
|
|
20
18
|
["0.0.0.0", 8],
|
|
@@ -82,7 +80,7 @@ function guardedLookup(policy) {
|
|
|
82
80
|
*/
|
|
83
81
|
export async function send(target, body, headers, options) {
|
|
84
82
|
const { policy, signal } = options;
|
|
85
|
-
const limit = options.maxResponseBytes
|
|
83
|
+
const limit = options.maxResponseBytes;
|
|
86
84
|
const url = new URL(target);
|
|
87
85
|
const refused = refusal(url, policy);
|
|
88
86
|
if (refused)
|
|
@@ -168,25 +166,22 @@ export class OutboundFailed extends Error {
|
|
|
168
166
|
this.name = "OutboundFailed";
|
|
169
167
|
}
|
|
170
168
|
}
|
|
171
|
-
/** The largest answer `guardedFetch` reads. */
|
|
172
|
-
export const MAX_FETCH_RESPONSE_BYTES = 1024 * 1024;
|
|
173
|
-
/** How long a `guardedFetch` request may take without a signal of its own. */
|
|
174
|
-
export const FETCH_TIMEOUT_MS = 30_000;
|
|
175
169
|
/**
|
|
176
170
|
* A `fetch` under the Host's address policy (F9 C2): the literal-IP and DNS checks of `post`
|
|
177
171
|
* on the address actually connected to, `localhost` rewritten for the local stack, no
|
|
178
|
-
* redirects (`redirect: "error"`; a 3xx answer rejects),
|
|
179
|
-
* unless the caller passes a signal
|
|
180
|
-
*
|
|
172
|
+
* redirects (`redirect: "error"`; a 3xx answer rejects), an answer of at most `maxResponseBytes`
|
|
173
|
+
* and a `timeoutMs` timeout unless the caller passes a signal, both the caller's (a Tenant's
|
|
174
|
+
* Runtime limits). A refused URL or address rejects with `OutboundRefused`, any other failure
|
|
175
|
+
* with `OutboundFailed`.
|
|
181
176
|
*
|
|
182
177
|
* `stream`: the Response resolves once the headers arrive and its body streams, unbounded and
|
|
183
178
|
* with no timeout but the caller's signal: remote MCP servers, whose answers and event streams
|
|
184
179
|
* stay open (`mcp/connect.ts`).
|
|
185
180
|
*/
|
|
186
|
-
export function guardedFetch(policy, options
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
181
|
+
export function guardedFetch(policy, options) {
|
|
182
|
+
const stream = "stream" in options;
|
|
183
|
+
const limit = "stream" in options ? Infinity : options.maxResponseBytes;
|
|
184
|
+
const timeoutMs = "stream" in options ? undefined : options.timeoutMs;
|
|
190
185
|
const lookup = guardedLookup(policy);
|
|
191
186
|
const guarded = async (input, init = {}) => {
|
|
192
187
|
const request = input instanceof Request ? input : undefined;
|
|
@@ -217,7 +212,7 @@ export function guardedFetch(policy, options = {}) {
|
|
|
217
212
|
}
|
|
218
213
|
const originalHost = url.host;
|
|
219
214
|
dockerHost(url, policy);
|
|
220
|
-
const signal = init.signal ?? request?.signal ?? (
|
|
215
|
+
const signal = init.signal ?? request?.signal ?? (timeoutMs === undefined ? undefined : AbortSignal.timeout(timeoutMs));
|
|
221
216
|
const outgoing = { host: originalHost };
|
|
222
217
|
for (const [name, value] of headers)
|
|
223
218
|
outgoing[name] = value;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { mintRunToken
|
|
1
|
+
import { mintRunToken } from "./run-token.js";
|
|
2
|
+
import { hostTuning } from "../tuning.js";
|
|
2
3
|
export function createRunGrants() {
|
|
3
4
|
const grants = new Map();
|
|
4
5
|
return {
|
|
@@ -50,7 +51,7 @@ export async function renewRunGrant(ctx, lease, run, active = () => true) {
|
|
|
50
51
|
const held = ctx.runGrants.get(lease.sessionId);
|
|
51
52
|
if (held && held.claims.epoch !== lease.epoch)
|
|
52
53
|
return;
|
|
53
|
-
if (held && held.claims.expiresAt - Date.now() >
|
|
54
|
+
if (held && held.claims.expiresAt - Date.now() > hostTuning().keys.runTokenRenewSeconds * 1000)
|
|
54
55
|
return;
|
|
55
56
|
const grant = await mintRunToken(ctx, lease, held ? { agentId: held.claims.agentId, activeTurnId: held.claims.turnId } : run);
|
|
56
57
|
if (active())
|
|
@@ -4,10 +4,6 @@ import type { SessionStore } from "../store/types.js";
|
|
|
4
4
|
import type { Lease } from "./context.js";
|
|
5
5
|
export declare const RUN_TOKEN_TYP = "nylorun-run+jwt";
|
|
6
6
|
export declare const RUN_TOKEN_AUD = "nylorun-gates";
|
|
7
|
-
/** How long a run token lives. */
|
|
8
|
-
export declare const RUN_TOKEN_TTL_SECONDS: number;
|
|
9
|
-
/** The lease heartbeat re-mints a token with less than this left. */
|
|
10
|
-
export declare const RUN_TOKEN_RENEW_SECONDS: number;
|
|
11
7
|
/** What a verified run token says: one lease of one session's turn. */
|
|
12
8
|
export interface RunClaims {
|
|
13
9
|
readonly tenantId: string;
|
package/dist/tenant/run-token.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Signed by the keys service with the Tenant's current signing key, like capability links,
|
|
8
8
|
* and told apart from them by `typ` and `aud`. Internal: not in `openapi.json`. A token lives
|
|
9
|
-
* `
|
|
9
|
+
* `keys.runTokenTtlSeconds` (Host tuning), no longer than `keys.tokenTtlMaxSeconds`, so a key rotation never
|
|
10
10
|
* revokes a live one (`signing-keys.ts`).
|
|
11
11
|
*
|
|
12
12
|
* ```text
|
|
@@ -18,15 +18,10 @@
|
|
|
18
18
|
import { randomUUID } from "node:crypto";
|
|
19
19
|
import { errors, importJWK, jwtVerify } from "jose";
|
|
20
20
|
import { tenantTokenIssuer } from "@nylorun/core/contracts";
|
|
21
|
-
import {
|
|
21
|
+
import { clockToleranceSeconds, tokenKeyId } from "./jwt.js";
|
|
22
|
+
import { hostTuning } from "../tuning.js";
|
|
22
23
|
export const RUN_TOKEN_TYP = "nylorun-run+jwt";
|
|
23
24
|
export const RUN_TOKEN_AUD = "nylorun-gates";
|
|
24
|
-
/** How long a run token lives. */
|
|
25
|
-
export const RUN_TOKEN_TTL_SECONDS = 15 * 60;
|
|
26
|
-
/** The lease heartbeat re-mints a token with less than this left. */
|
|
27
|
-
export const RUN_TOKEN_RENEW_SECONDS = 5 * 60;
|
|
28
|
-
/** Allowed spread between `iat` and `exp` beyond the lifetime. */
|
|
29
|
-
const LIFETIME_SLACK_SECONDS = 60;
|
|
30
25
|
/**
|
|
31
26
|
* Mints the run token of `lease` over `session`'s active turn. Throws when the session has
|
|
32
27
|
* no active turn: there is no run to name.
|
|
@@ -37,7 +32,7 @@ export async function mintRunToken(signer, lease, session) {
|
|
|
37
32
|
throw new Error(`Session ${lease.sessionId} has no active turn to mint a run token for`);
|
|
38
33
|
const tenantId = signer.config.tenantId;
|
|
39
34
|
const iat = Math.floor(Date.now() / 1000);
|
|
40
|
-
const exp = iat +
|
|
35
|
+
const exp = iat + hostTuning().keys.runTokenTtlSeconds;
|
|
41
36
|
// Signed by the keys service (F4.2): this process never holds the private key.
|
|
42
37
|
const { token } = await signer.keys.sign({
|
|
43
38
|
typ: RUN_TOKEN_TYP,
|
|
@@ -92,7 +87,7 @@ export async function verifyRunToken(store, tenantId, raw, cache = new Map()) {
|
|
|
92
87
|
issuer: tenantTokenIssuer(tenantId),
|
|
93
88
|
audience: RUN_TOKEN_AUD,
|
|
94
89
|
typ: RUN_TOKEN_TYP,
|
|
95
|
-
clockTolerance:
|
|
90
|
+
clockTolerance: clockToleranceSeconds(),
|
|
96
91
|
requiredClaims: ["sub", "iat", "exp", "jti"],
|
|
97
92
|
});
|
|
98
93
|
payload = verified.payload;
|
|
@@ -112,7 +107,7 @@ export async function verifyRunToken(store, tenantId, raw, cache = new Map()) {
|
|
|
112
107
|
typeof epc !== "number" ||
|
|
113
108
|
!Number.isSafeInteger(epc) ||
|
|
114
109
|
epc < 1 ||
|
|
115
|
-
exp - iat >
|
|
110
|
+
exp - iat > hostTuning().keys.runTokenTtlSeconds + hostTuning().keys.tokenLifetimeSlackSeconds)
|
|
116
111
|
return refused("claims");
|
|
117
112
|
return {
|
|
118
113
|
ok: true,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Tenant's Runtime limits (harness boundary S0), stored as Tenant setting `limits.config`
|
|
3
|
+
* (`GET`/`PUT /v1/tenant/limits`): what one record entry, model call, model file, tool answer,
|
|
4
|
+
* custom harness state and Harness API chunk may hold. Unset limits take their defaults
|
|
5
|
+
* (`RUNTIME_LIMIT_DEFAULTS`); none may pass its ceiling (`runtimeLimitIssues`), which the route
|
|
6
|
+
* checks before it writes.
|
|
7
|
+
*
|
|
8
|
+
* Read from Postgres where a limit is checked, never kept: the Record seam, the store's event
|
|
9
|
+
* write, the gates and the segment's `turn.start` each read it in their own transaction.
|
|
10
|
+
*/
|
|
11
|
+
import { type RuntimeLimits, type TenantLimitsConfig } from "@nylorun/core/contracts";
|
|
12
|
+
export declare const LIMITS_CONFIG_SETTING = "limits.config";
|
|
13
|
+
interface SettingsReader {
|
|
14
|
+
getSetting(key: string): Promise<string | undefined>;
|
|
15
|
+
}
|
|
16
|
+
interface SettingsWriter extends SettingsReader {
|
|
17
|
+
putSetting(key: string, value: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/** The Tenant's stored limits; a stored value that does not parse counts as none set. */
|
|
20
|
+
export declare function readLimitsConfig(t: SettingsReader): Promise<TenantLimitsConfig>;
|
|
21
|
+
/** The Runtime limits in force: the Tenant's, with the default for each it leaves out. */
|
|
22
|
+
export declare function readRuntimeLimits(t: SettingsReader): Promise<RuntimeLimits>;
|
|
23
|
+
export declare function writeLimitsConfig(t: SettingsWriter, config: TenantLimitsConfig): Promise<void>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Tenant's Runtime limits (harness boundary S0), stored as Tenant setting `limits.config`
|
|
3
|
+
* (`GET`/`PUT /v1/tenant/limits`): what one record entry, model call, model file, tool answer,
|
|
4
|
+
* custom harness state and Harness API chunk may hold. Unset limits take their defaults
|
|
5
|
+
* (`RUNTIME_LIMIT_DEFAULTS`); none may pass its ceiling (`runtimeLimitIssues`), which the route
|
|
6
|
+
* checks before it writes.
|
|
7
|
+
*
|
|
8
|
+
* Read from Postgres where a limit is checked, never kept: the Record seam, the store's event
|
|
9
|
+
* write, the gates and the segment's `turn.start` each read it in their own transaction.
|
|
10
|
+
*/
|
|
11
|
+
import { TenantLimitsConfigSchema, runtimeLimitsOf, } from "@nylorun/core/contracts";
|
|
12
|
+
export const LIMITS_CONFIG_SETTING = "limits.config";
|
|
13
|
+
/** The Tenant's stored limits; a stored value that does not parse counts as none set. */
|
|
14
|
+
export async function readLimitsConfig(t) {
|
|
15
|
+
const raw = await t.getSetting(LIMITS_CONFIG_SETTING);
|
|
16
|
+
if (raw === undefined)
|
|
17
|
+
return {};
|
|
18
|
+
const parsed = TenantLimitsConfigSchema.safeParse(JSON.parse(raw));
|
|
19
|
+
return parsed.success ? parsed.data : {};
|
|
20
|
+
}
|
|
21
|
+
/** The Runtime limits in force: the Tenant's, with the default for each it leaves out. */
|
|
22
|
+
export async function readRuntimeLimits(t) {
|
|
23
|
+
return runtimeLimitsOf((await readLimitsConfig(t)).limits);
|
|
24
|
+
}
|
|
25
|
+
export async function writeLimitsConfig(t, config) {
|
|
26
|
+
await t.putSetting(LIMITS_CONFIG_SETTING, JSON.stringify(TenantLimitsConfigSchema.parse(config)));
|
|
27
|
+
}
|
package/dist/tenant/runtime.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
|
-
import { resolveFlowLimits } from "../core/limits.js";
|
|
3
2
|
import { scriptedModel, toolFixtureModel, } from "../core/provider.js";
|
|
4
3
|
import { tenantModelGate } from "../gates/in-process.js";
|
|
5
4
|
import { createKekFile, readVaultKek } from "../vault/kek.js";
|
|
6
5
|
import { SigningKeys } from "./signing-keys.js";
|
|
6
|
+
import { readRuntimeLimits } from "./runtime-limits.js";
|
|
7
7
|
import { VaultService } from "../vault/service.js";
|
|
8
8
|
import { McpPool } from "../mcp/pool.js";
|
|
9
9
|
import { SandboxManager } from "../sandbox/manager.js";
|
|
@@ -20,7 +20,8 @@ import { advance } from "./advance.js";
|
|
|
20
20
|
import { sweep } from "./sweep.js";
|
|
21
21
|
import { TenantWorkers, WORKER_ID, } from "./worker.js";
|
|
22
22
|
import { authorize } from "./effects.js";
|
|
23
|
-
import { inProcessToolGate } from "../gates/tool-gate.js";
|
|
23
|
+
import { inProcessToolGate, openMcpThrough } from "../gates/tool-gate.js";
|
|
24
|
+
import { providerModelGate } from "../gates/provider-gate.js";
|
|
24
25
|
import { inProcessKeys } from "../keys/keys.js";
|
|
25
26
|
import { tenantApi } from "../api/http/app.js";
|
|
26
27
|
import { createFsBlobStore } from "../blob/index.js";
|
|
@@ -31,24 +32,16 @@ import { localWorkspace, remoteWorkspace, withPodWorkspaces, } from "../harness-
|
|
|
31
32
|
import { hostAuthority } from "../sandbox/join.js";
|
|
32
33
|
import { harnessOfToken } from "./harness-tokens.js";
|
|
33
34
|
import { reconcileSandbox } from "../sandbox/pods/reconcile.js";
|
|
34
|
-
import {
|
|
35
|
-
import { readDefinitionFile } from "./definition-files.js";
|
|
36
|
-
/** Default ownership lease of an advance; the heartbeat renews it every third. */
|
|
37
|
-
const DEFAULT_OWNER_LEASE_MS = 30_000;
|
|
35
|
+
import { hostTuning } from "../tuning.js";
|
|
38
36
|
function validateConfig(config) {
|
|
39
37
|
for (const key of ["leaseMs", "ownerLeaseMs", "sweepIntervalMs"]) {
|
|
40
38
|
const value = config[key];
|
|
41
39
|
if (value !== undefined && (!Number.isFinite(value) || value <= 0))
|
|
42
40
|
throw new Error(`${key} must be finite and positive`);
|
|
43
41
|
}
|
|
44
|
-
const flowLimits = resolveFlowLimits({
|
|
45
|
-
flow: config.flow,
|
|
46
|
-
env: config.flowEnv,
|
|
47
|
-
});
|
|
48
42
|
if ((config.model.kind === "fixture" || config.model.kind === "scripted") &&
|
|
49
43
|
config.mode === "shared")
|
|
50
44
|
throw new Error("fixture/scripted models require ephemeral or test mode");
|
|
51
|
-
return flowLimits;
|
|
52
45
|
}
|
|
53
46
|
export class TenantRuntime {
|
|
54
47
|
ctx;
|
|
@@ -81,7 +74,7 @@ export class TenantRuntime {
|
|
|
81
74
|
}
|
|
82
75
|
static async open(config, hooks) {
|
|
83
76
|
const envelope = hooks.envelope;
|
|
84
|
-
|
|
77
|
+
validateConfig(config);
|
|
85
78
|
const paths = config.paths;
|
|
86
79
|
mkdirSync(paths.root, { recursive: true });
|
|
87
80
|
mkdirSync(paths.home, { recursive: true });
|
|
@@ -128,6 +121,7 @@ export class TenantRuntime {
|
|
|
128
121
|
inProcessToolGate(config.delivery ?? {}, {
|
|
129
122
|
session: (sessionId) => opened.tx((t) => t.get("sessions", sessionId)),
|
|
130
123
|
authorize: (sessionId, request) => authorize(ctx, sessionId, request),
|
|
124
|
+
limits: () => opened.tx((t) => readRuntimeLimits(t)),
|
|
131
125
|
logger: config.logger,
|
|
132
126
|
});
|
|
133
127
|
const signingKeys = new SigningKeys({ tenantId: config.tenantId, kek: ensureKek });
|
|
@@ -144,13 +138,9 @@ export class TenantRuntime {
|
|
|
144
138
|
const preference = seededBackend ?? config.sandbox.backend;
|
|
145
139
|
// With a harness in this process, the Tenant's MCP pool and SandboxManager are its
|
|
146
140
|
// executors; otherwise the harness holds both (F6.2).
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
policy: config.delivery ?? {},
|
|
151
|
-
...(toolGate.openMcp ? { openRemote: (server) => toolGate.openMcp(server) } : {}),
|
|
152
|
-
})
|
|
153
|
-
: undefined;
|
|
141
|
+
// The pool opens every server through the Tool Gate: core never authorizes an MCP
|
|
142
|
+
// connection outside a gate (protocol 15).
|
|
143
|
+
const mcp = inProcess ? new McpPool({ openRemote: openMcpThrough(toolGate) }) : undefined;
|
|
154
144
|
const ephemeral = config.mode === "ephemeral";
|
|
155
145
|
const manager = inProcess
|
|
156
146
|
? new SandboxManager({
|
|
@@ -160,12 +150,6 @@ export class TenantRuntime {
|
|
|
160
150
|
defaultSandboxBackends({ root: paths.sandboxes }),
|
|
161
151
|
preference,
|
|
162
152
|
ephemeral,
|
|
163
|
-
definitionFile: async (sha256) => {
|
|
164
|
-
const bytes = await readDefinitionFile(blobs, sha256);
|
|
165
|
-
if (!bytes)
|
|
166
|
-
throw new Error(`The Object store has no bytes for ${sha256}`);
|
|
167
|
-
return bytes;
|
|
168
|
-
},
|
|
169
153
|
emit: async (sessionId, turnId, type, payload) => {
|
|
170
154
|
if (ctx.closed)
|
|
171
155
|
return;
|
|
@@ -201,9 +185,13 @@ export class TenantRuntime {
|
|
|
201
185
|
isPod: async (sandboxId) => (await opened.tx((t) => t.environment(sandboxId)))?.kind === "pod",
|
|
202
186
|
})
|
|
203
187
|
: workspaces;
|
|
204
|
-
|
|
188
|
+
// A model that is not the vault's (scripted, the fixture, an embedder's provider) is
|
|
189
|
+
// served by a gate over it, metered like any (protocol 15): no harness calls a provider.
|
|
190
|
+
const scripted = config.model.kind !== "vault";
|
|
205
191
|
let modelProvider;
|
|
206
|
-
if (
|
|
192
|
+
if (!scripted)
|
|
193
|
+
modelProvider = undefined;
|
|
194
|
+
else if (hooks.modelProvider)
|
|
207
195
|
modelProvider = hooks.modelProvider;
|
|
208
196
|
else if (config.model.kind === "scripted")
|
|
209
197
|
modelProvider = scriptedModel(config.model.output);
|
|
@@ -214,21 +202,23 @@ export class TenantRuntime {
|
|
|
214
202
|
const blobs = hooks.blobs ?? createFsBlobStore({ root: paths.blobs });
|
|
215
203
|
// With the gates service the loop never reads a model credential: only a Runtime
|
|
216
204
|
// without one (embedding, the ephemeral Runtime, tests) reads it here.
|
|
217
|
-
const modelGate =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
205
|
+
const modelGate = modelProvider
|
|
206
|
+
? providerModelGate(modelProvider, { store: opened, logger: config.logger })
|
|
207
|
+
: (hooks.modelGate ??
|
|
208
|
+
tenantModelGate({
|
|
209
|
+
store: opened,
|
|
210
|
+
kek: ensureKek,
|
|
211
|
+
root: paths.home,
|
|
212
|
+
logger: config.logger,
|
|
213
|
+
blobs,
|
|
214
|
+
}));
|
|
226
215
|
const sessionStreams = createSessionStreams();
|
|
227
216
|
const workers = hooks.execution?.workers ?? new TenantWorkers();
|
|
228
217
|
const local = hooks.execution
|
|
229
218
|
? undefined
|
|
230
219
|
: new MemoryExecution({
|
|
231
|
-
sweepIntervalMs: config.sweepIntervalMs ??
|
|
220
|
+
sweepIntervalMs: config.sweepIntervalMs ??
|
|
221
|
+
Math.min(config.leaseMs ?? Infinity, hostTuning().sweep.intervalMs),
|
|
232
222
|
// Close's `detach` step waits no longer for running work than for its advances.
|
|
233
223
|
stopGraceMs: workers.graceMs,
|
|
234
224
|
onError: (error) => config.logger.error("tenant execution failed", {
|
|
@@ -245,9 +235,7 @@ export class TenantRuntime {
|
|
|
245
235
|
vault,
|
|
246
236
|
...(mcp ? { mcp } : {}),
|
|
247
237
|
sandbox,
|
|
248
|
-
|
|
249
|
-
modelProvider,
|
|
250
|
-
useVaultModel,
|
|
238
|
+
scriptedModel: scripted,
|
|
251
239
|
modelGate,
|
|
252
240
|
toolGate,
|
|
253
241
|
get harness() {
|
|
@@ -260,11 +248,10 @@ export class TenantRuntime {
|
|
|
260
248
|
signingKeys,
|
|
261
249
|
keys,
|
|
262
250
|
blobs,
|
|
263
|
-
workspaces: sandboxWorkspaceReader(sandbox),
|
|
264
251
|
...(hooks.traces ? { traces: hooks.traces } : {}),
|
|
265
252
|
...(hooks.runGrants ? { runGrants: hooks.runGrants } : {}),
|
|
266
253
|
workerId: hooks.workerId ?? WORKER_ID,
|
|
267
|
-
ownerLeaseMs: config.ownerLeaseMs ??
|
|
254
|
+
ownerLeaseMs: config.ownerLeaseMs ?? hostTuning().tenant.ownerLeaseMs,
|
|
268
255
|
wake: async (sessionId, wake) => {
|
|
269
256
|
if (ctx.closing || ctx.closed)
|
|
270
257
|
return false;
|
|
@@ -372,7 +359,7 @@ export class TenantRuntime {
|
|
|
372
359
|
harness: harnessStatusOf(this.ctx),
|
|
373
360
|
};
|
|
374
361
|
}
|
|
375
|
-
drain(activeWork, timeoutMs =
|
|
362
|
+
drain(activeWork, timeoutMs = hostTuning().tenant.drainTimeoutMs) {
|
|
376
363
|
return drain(this.ctx, activeWork, timeoutMs);
|
|
377
364
|
}
|
|
378
365
|
/**
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { historyCursor, historyResume } from "../reads/cursor.js";
|
|
22
22
|
import { decodeCursor, encodeCursor } from "../record/index.js";
|
|
23
23
|
import { sleep } from "../retry.js";
|
|
24
|
+
import { hostTuning } from "../tuning.js";
|
|
24
25
|
import { basinOf } from "../streams/basin.js";
|
|
25
26
|
import { sessionStream, } from "../streams/types.js";
|
|
26
27
|
import { fail } from "./http.js";
|
|
@@ -33,8 +34,6 @@ export function streamHolderOf(scope) {
|
|
|
33
34
|
export function createSessionStreams() {
|
|
34
35
|
return { wiring: undefined, generation: 0, sessions: new Map() };
|
|
35
36
|
}
|
|
36
|
-
const RETRY_MIN_MS = 100;
|
|
37
|
-
const RETRY_MAX_MS = 2000;
|
|
38
37
|
function frame(event) {
|
|
39
38
|
return `id: ${event.cursor}\nevent: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`;
|
|
40
39
|
}
|
|
@@ -66,7 +65,7 @@ export function openSse(request, response, onClose) {
|
|
|
66
65
|
"x-accel-buffering": "no",
|
|
67
66
|
});
|
|
68
67
|
response.flushHeaders();
|
|
69
|
-
const timer = setInterval(() => send(response, ": keepalive\n\n"),
|
|
68
|
+
const timer = setInterval(() => send(response, ": keepalive\n\n"), hostTuning().api.eventStreamKeepaliveMs);
|
|
70
69
|
timer.unref();
|
|
71
70
|
request.on("close", () => {
|
|
72
71
|
clearInterval(timer);
|
|
@@ -313,7 +312,9 @@ function runFeed(ctx, feed) {
|
|
|
313
312
|
const read = feed.read;
|
|
314
313
|
const signal = read.signal;
|
|
315
314
|
void (async () => {
|
|
316
|
-
|
|
315
|
+
// A failed read is retried after `streams.retry` (Host tuning), doubling.
|
|
316
|
+
const { minMs: retryMinMs, maxMs: retryMaxMs } = hostTuning().streams.retry;
|
|
317
|
+
let delay = retryMinMs;
|
|
317
318
|
while (!signal.aborted) {
|
|
318
319
|
const streams = ctx.sessionStreams.wiring?.streams;
|
|
319
320
|
if (!streams)
|
|
@@ -326,7 +327,7 @@ function runFeed(ctx, feed) {
|
|
|
326
327
|
if (record.seq < feed.next)
|
|
327
328
|
continue;
|
|
328
329
|
feed.next = record.seq + 1;
|
|
329
|
-
delay =
|
|
330
|
+
delay = retryMinMs;
|
|
330
331
|
for (const observer of feed.observers)
|
|
331
332
|
deliver(observer, record);
|
|
332
333
|
}
|
|
@@ -345,7 +346,7 @@ function runFeed(ctx, feed) {
|
|
|
345
346
|
if (!(await stillCurrent(ctx, feed)))
|
|
346
347
|
break;
|
|
347
348
|
await sleep(delay, signal);
|
|
348
|
-
delay = Math.min(delay * 2,
|
|
349
|
+
delay = Math.min(delay * 2, retryMaxMs);
|
|
349
350
|
}
|
|
350
351
|
if (signal.aborted || feed.read !== read)
|
|
351
352
|
return;
|
package/dist/tenant/session.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { AgentManifest } from "@nylorun/core/define";
|
|
10
10
|
import type { TurnManifestStore } from "../core/turn-manifest.js";
|
|
11
11
|
import type { Session } from "./context.js";
|
|
12
|
-
export { manifestFor
|
|
12
|
+
export { manifestFor } from "../mcp/snapshot.js";
|
|
13
13
|
/** Turn-manifest store backed by the session's `variants` map (no schema change). */
|
|
14
14
|
export declare function variantStore(session: Session): TurnManifestStore;
|
|
15
15
|
/** Manifest this turn's checkpoint is pinned to (session pin or a stored variant). */
|
package/dist/tenant/session.js
CHANGED