@nylorun/runtime 0.26.0-beta → 0.28.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 +171 -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.d.ts +4 -3
- package/dist/api/a2a/card.js +4 -4
- 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 +30 -25
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +8 -11
- package/dist/api/components.d.ts +6789 -3405
- package/dist/api/components.js +43 -30
- 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.js +9 -4
- package/dist/api/http/body.d.ts +17 -3
- package/dist/api/http/body.js +35 -7
- package/dist/api/http/respond.js +6 -2
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/agent-manifests.d.ts +10 -0
- package/dist/api/http/routes/agent-manifests.js +135 -0
- package/dist/api/http/routes/agents.d.ts +11 -0
- package/dist/api/http/routes/agents.js +418 -0
- package/dist/api/http/routes/artifacts.js +21 -9
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +13 -7
- 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 +3 -3
- package/dist/api/http/routes/me.d.ts +1 -1
- package/dist/api/http/routes/me.js +1 -2
- package/dist/api/http/routes/memories.d.ts +9 -0
- package/dist/api/http/routes/memories.js +152 -0
- package/dist/api/http/routes/reads.d.ts +10 -111
- package/dist/api/http/routes/reads.js +51 -29
- package/dist/api/http/routes/sessions.js +26 -54
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +77 -24
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +180 -0
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +73 -47
- package/dist/artifacts/disk.d.ts +10 -14
- package/dist/artifacts/disk.js +99 -95
- 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 +72 -27
- 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/flow-host.d.ts +0 -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 +5 -4
- package/dist/gates/egress.d.ts +13 -13
- package/dist/gates/egress.js +19 -21
- 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 +41 -13
- 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/meter.js +6 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +34 -6
- package/dist/gates/policy.js +66 -18
- 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 +2 -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 +21 -15
- 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 +113 -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 +86 -73
- package/dist/harness-api/start.d.ts +27 -5
- package/dist/harness-api/start.js +102 -19
- 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 +4 -47
- 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 +2 -4
- package/dist/host/http.js +5 -29
- 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.d.ts +15 -4
- package/dist/keys/keys.js +22 -1
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +3043 -1954
- 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 +43 -26
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/memory/service.d.ts +97 -0
- package/dist/memory/service.js +335 -0
- package/dist/memory/tool.d.ts +36 -0
- package/dist/memory/tool.js +84 -0
- 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 +13653 -9986
- package/dist/reads/cursor.js +3 -1
- package/dist/reads/types.d.ts +3 -3
- package/dist/record/agent.d.ts +27 -0
- package/dist/record/agent.js +46 -0
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/index.d.ts +2 -2
- package/dist/record/index.js +2 -2
- 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 +8 -7
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +2 -2
- package/dist/sandbox/placement.js +7 -10
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +25 -17
- package/dist/sandbox/pods/lifecycle.js +51 -29
- 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 +14 -3
- 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 +11 -6
- package/dist/sandbox/resolve.js +19 -9
- package/dist/sandbox/setup.d.ts +6 -2
- package/dist/sandbox/setup.js +47 -14
- 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/0032_agents.sql +54 -0
- package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
- package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
- package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
- package/dist/store/postgres/reads.js +17 -82
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/schema.d.ts +1382 -151
- package/dist/store/postgres/schema.js +191 -37
- package/dist/store/postgres/store.js +384 -105
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +172 -57
- package/dist/store/types.js +0 -1
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +41 -39
- package/dist/tenant/agent-manifests.d.ts +72 -0
- package/dist/tenant/agent-manifests.js +359 -0
- package/dist/tenant/agent-versions.d.ts +126 -0
- package/dist/tenant/agent-versions.js +389 -0
- package/dist/tenant/agents.d.ts +230 -0
- package/dist/tenant/{environments.js → agents.js} +493 -328
- package/dist/tenant/auth.d.ts +1 -1
- package/dist/tenant/auth.js +3 -3
- package/dist/tenant/commands.d.ts +63 -2
- package/dist/tenant/commands.js +240 -90
- package/dist/tenant/context.d.ts +57 -33
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +15 -51
- 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 +29 -6
- package/dist/tenant/identity-file.js +45 -15
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +34 -26
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +87 -0
- package/dist/tenant/limits.js +172 -0
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -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/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +7 -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.d.ts +1 -1
- package/dist/tenant/runtime.js +38 -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.d.ts +28 -45
- package/dist/tenant/sessions.js +89 -209
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +10 -10
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +51 -51
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/triggers.d.ts +143 -0
- package/dist/tenant/triggers.js +715 -0
- package/dist/tenant/turn-timing.d.ts +11 -1
- package/dist/tenant/turn-timing.js +3 -1
- package/dist/tenant/types.d.ts +9 -14
- package/dist/tenant/worker.d.ts +4 -10
- package/dist/tenant/worker.js +10 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +10 -7
- package/dist/traces/map.js +16 -12
- 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 +390 -0
- package/dist/tuning.js +677 -0
- package/dist/vault/headers.js +4 -2
- package/dist/vault/service.d.ts +13 -8
- package/dist/vault/service.js +61 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -12
- package/dist/api/http/routes/environment-manifests.d.ts +0 -9
- package/dist/api/http/routes/environment-manifests.js +0 -118
- package/dist/api/http/routes/environments.d.ts +0 -11
- package/dist/api/http/routes/environments.js +0 -332
- 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/record/environment.d.ts +0 -25
- package/dist/record/environment.js +0 -44
- 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/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -200
- package/dist/tenant/environments.d.ts +0 -174
- 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/execution/memory.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WAKE_REASONS, sessionKey, settleWithin, } from "./types.js";
|
|
2
|
+
import { hostTuning } from "../tuning.js";
|
|
2
3
|
/**
|
|
3
4
|
* In-process `DurableExecution`. Not durable: wakes, timers and sweeps live in
|
|
4
5
|
* memory. It keeps the seam's ordering guarantees within one process, which is
|
|
@@ -21,11 +22,12 @@ export class MemoryExecution {
|
|
|
21
22
|
stopGraceMs;
|
|
22
23
|
onError;
|
|
23
24
|
constructor(options = {}) {
|
|
24
|
-
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
25
|
+
const tuning = hostTuning();
|
|
26
|
+
this.sweepIntervalMs = options.sweepIntervalMs ?? tuning.sweep.intervalMs;
|
|
27
|
+
this.retryDelayMs = options.retryDelayMs ?? tuning.execution.memoryRetryMs;
|
|
28
|
+
this.maxAttempts = options.maxAttempts ?? tuning.execution.memoryAttempts;
|
|
29
|
+
this.dedupeRetentionMs = options.dedupeRetentionMs ?? tuning.execution.dedupeRetentionMs;
|
|
30
|
+
this.stopGraceMs = options.stopGraceMs ?? tuning.execution.stopGraceMs;
|
|
29
31
|
this.onError =
|
|
30
32
|
options.onError ??
|
|
31
33
|
((error) => queueMicrotask(() => {
|
|
@@ -137,7 +137,7 @@ export interface DurableExecution {
|
|
|
137
137
|
start(handlers: WorkerHandlers): Promise<void>;
|
|
138
138
|
/**
|
|
139
139
|
* Stops delivering, aborts running advances' signals and waits for them, up to the
|
|
140
|
-
* implementation's stop grace (`
|
|
140
|
+
* implementation's stop grace (Host tuning `execution.stopGraceMs`). Handlers still running then are
|
|
141
141
|
* abandoned: their sessions' leases lapse and the next advance takes over (§11.4).
|
|
142
142
|
*/
|
|
143
143
|
stop(): Promise<void>;
|
|
@@ -178,11 +178,6 @@ export declare function parseSessionKey(key: string): {
|
|
|
178
178
|
tenantId: string;
|
|
179
179
|
sessionId: string;
|
|
180
180
|
};
|
|
181
|
-
/**
|
|
182
|
-
* Default time `DurableExecution.stop` waits for running handlers after aborting them: the
|
|
183
|
-
* advance grace period (`DEFAULT_ADVANCE_GRACE_MS` in `tenant/worker.ts`).
|
|
184
|
-
*/
|
|
185
|
-
export declare const DEFAULT_STOP_GRACE_MS = 30000;
|
|
186
181
|
/**
|
|
187
182
|
* Waits until `inflight` is empty (it shrinks as its promises settle and may grow meanwhile),
|
|
188
183
|
* for at most `graceMs`. Resolves to how many were still running when the grace ran out.
|
package/dist/execution/types.js
CHANGED
|
@@ -62,11 +62,6 @@ export function parseSessionKey(key) {
|
|
|
62
62
|
throw new Error(`Invalid session key: ${key}`);
|
|
63
63
|
return { tenantId: key.slice(0, at), sessionId: key.slice(at + 1) };
|
|
64
64
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Default time `DurableExecution.stop` waits for running handlers after aborting them: the
|
|
67
|
-
* advance grace period (`DEFAULT_ADVANCE_GRACE_MS` in `tenant/worker.ts`).
|
|
68
|
-
*/
|
|
69
|
-
export const DEFAULT_STOP_GRACE_MS = 30_000;
|
|
70
65
|
/**
|
|
71
66
|
* Waits until `inflight` is empty (it shrinks as its promises settle and may grow meanwhile),
|
|
72
67
|
* for at most `graceMs`. Resolves to how many were still running when the grace ran out.
|
package/dist/gates/contract.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* - Headers: `Authorization: Bearer <run token>` (F5, `tenant/run-token.ts`), `Idempotency-Key:
|
|
7
7
|
* <effect id>`, and optionally `Nylorun-Tenant: <tenant id>`, which must then be the token's
|
|
8
8
|
* Tenant. Core's credential (`NYLORUN_GATES_TOKEN`) is refused: only a run calls the model.
|
|
9
|
-
* - Body: `ModelCallBody`, at most
|
|
9
|
+
* - Body: `ModelCallBody`, at most the Tenant's Runtime limit `modelCallBytes` (`PUT
|
|
10
|
+
* /v1/tenant/limits`, default 32 MiB). It names no session, turn or agent:
|
|
10
11
|
* the gate takes them from the token, and refuses a body that names them.
|
|
11
12
|
* - `200 {outcome}` once the call has finished, whether the outcome is a candidate or a
|
|
12
13
|
* failure; nothing is sent while it runs. `401 gate_unauthorized` for a missing, malformed,
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
* need not be live: a user cancel makes it stale, and still stops its call.
|
|
24
25
|
*/
|
|
25
26
|
import type { ModelGateOutcome } from "./model-gate.js";
|
|
26
|
-
export {
|
|
27
|
+
export { MODEL_CALLS_PATH, ModelCallBodySchema, type GateErrorBody, type ModelCallBody, } from "@nylorun/core/harness-api";
|
|
27
28
|
/** Header naming the Tenant a call is for: optional, and checked against the gate's Tenant. */
|
|
28
29
|
export declare const TENANT_HEADER = "nylorun-tenant";
|
|
29
30
|
export interface ModelCallResponse {
|
package/dist/gates/contract.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { MODEL_CALLS_PATH, ModelCallBodySchema, } from "@nylorun/core/harness-api";
|
|
2
2
|
/** Header naming the Tenant a call is for: optional, and checked against the gate's Tenant. */
|
|
3
3
|
export const TENANT_HEADER = "nylorun-tenant";
|
|
@@ -6,18 +6,19 @@
|
|
|
6
6
|
* (keys holds its key; the pod trusts its certificate, `harness/pod.ts`), and reads HTTP/1.1
|
|
7
7
|
* requests. For each request it reads the vault (one Postgres query, no cache, so a rotation
|
|
8
8
|
* applies to the next request), sets the credential's header (replacing any of that name, such
|
|
9
|
-
* as the `
|
|
9
|
+
* as the `nylorun_managed` sentinel the CLI sent), and forwards it over TLS to the address the
|
|
10
10
|
* gate already checked, verifying the host's certificate by name against the public roots.
|
|
11
11
|
*
|
|
12
12
|
* Nothing here knows a tool: the header name and its format are the credential's. A release the
|
|
13
13
|
* vault refuses (two secrets for one host, an unreadable value) answers 502 and is logged, never
|
|
14
14
|
* as an event; a value never is. Upgrades (WebSocket) and HTTP/2 are not served on this path.
|
|
15
|
-
* Each connection keeps a tally for its `
|
|
15
|
+
* Each connection keeps a tally for its `agent.egress` event (R7.3, D67): how many requests it
|
|
16
16
|
* read and which credentials it set, by id.
|
|
17
17
|
*/
|
|
18
18
|
import { createServer } from "node:http";
|
|
19
19
|
import { Agent, request } from "node:https";
|
|
20
20
|
import { createSecureContext, TLSSocket } from "node:tls";
|
|
21
|
+
import { hostTuning } from "../tuning.js";
|
|
21
22
|
/** The answer to a CONNECT the gate serves; a tunnel's byte counts leave it out. */
|
|
22
23
|
export const CONNECT_ESTABLISHED = "HTTP/1.1 200 Connection Established\r\n\r\n";
|
|
23
24
|
/** Headers that describe one hop and are never forwarded. */
|
|
@@ -62,7 +63,7 @@ export function credentialTerminator(options) {
|
|
|
62
63
|
});
|
|
63
64
|
});
|
|
64
65
|
http.keepAliveTimeout = options.idleMs;
|
|
65
|
-
http.headersTimeout =
|
|
66
|
+
http.headersTimeout = hostTuning().egress.credentialsHeadersTimeoutMs;
|
|
66
67
|
// Uploads may be long; the client socket's idle timeout still applies.
|
|
67
68
|
http.requestTimeout = 0;
|
|
68
69
|
http.on("upgrade", (req, socket) => {
|
|
@@ -131,7 +132,7 @@ export function credentialTerminator(options) {
|
|
|
131
132
|
};
|
|
132
133
|
const agent = new Agent({
|
|
133
134
|
keepAlive: true,
|
|
134
|
-
maxSockets:
|
|
135
|
+
maxSockets: hostTuning().egress.credentialsMaxSockets,
|
|
135
136
|
lookup,
|
|
136
137
|
...(credentials.upstreamCa ? { ca: credentials.upstreamCa } : {}),
|
|
137
138
|
});
|
package/dist/gates/egress.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Server } from "node:http";
|
|
2
2
|
import { type Socket } from "node:net";
|
|
3
|
-
import type { EgressRunningTurn,
|
|
3
|
+
import type { EgressRunningTurn, AgentEventPayload } from "@nylorun/core/contracts";
|
|
4
4
|
import type { EgressTokenVerdict } from "../sandbox/egress-token.js";
|
|
5
5
|
import type { SessionStore } from "../store/types.js";
|
|
6
6
|
import type { Logger } from "../tenant/types.js";
|
|
@@ -16,9 +16,13 @@ export interface EgressSandboxes {
|
|
|
16
16
|
readonly allow: readonly string[];
|
|
17
17
|
} | undefined>;
|
|
18
18
|
}
|
|
19
|
-
/** One connection's record (R7.3, D67): `
|
|
20
|
-
export type EgressConnection =
|
|
21
|
-
/**
|
|
19
|
+
/** One connection's record (R7.3, D67): `agent.egress`'s payload. */
|
|
20
|
+
export type EgressConnection = AgentEventPayload<"agent.egress">;
|
|
21
|
+
/**
|
|
22
|
+
* How many running turns one connection names, at most: fixed by the `agent.egress` event
|
|
23
|
+
* contract (`runningTurns` holds at most 16 in `/core/contracts`), which every reader
|
|
24
|
+
* validates against, so not a setting here.
|
|
25
|
+
*/
|
|
22
26
|
export declare const EGRESS_RUNNING_TURNS_MAX = 16;
|
|
23
27
|
/** Where egress-gate records connections (R7.3, D67). */
|
|
24
28
|
export interface EgressRecord {
|
|
@@ -29,14 +33,6 @@ export interface EgressRecord {
|
|
|
29
33
|
}
|
|
30
34
|
/** Ports a tunnel may open. */
|
|
31
35
|
export declare const EGRESS_PORTS: readonly number[];
|
|
32
|
-
/** Tunnels one sandbox may hold open at once. */
|
|
33
|
-
export declare const EGRESS_MAX_PER_SANDBOX = 64;
|
|
34
|
-
/** A tunnel with no traffic either way for this long is closed. */
|
|
35
|
-
export declare const EGRESS_IDLE_MS: number;
|
|
36
|
-
/** How long a sandbox's epoch and allowlist are reused before they are read again. */
|
|
37
|
-
export declare const EGRESS_CACHE_MS = 5000;
|
|
38
|
-
/** How long the gate waits for the upstream connection. */
|
|
39
|
-
export declare const EGRESS_CONNECT_TIMEOUT_MS = 10000;
|
|
40
36
|
export interface StartEgressGateOptions {
|
|
41
37
|
readonly listen: {
|
|
42
38
|
readonly host: string;
|
|
@@ -46,9 +42,13 @@ export interface StartEgressGateOptions {
|
|
|
46
42
|
/** Verifies a presented egress token (`verifyEgressToken` over the Tenant's keys). */
|
|
47
43
|
verify(raw: string): Promise<EgressTokenVerdict>;
|
|
48
44
|
readonly sandboxes: EgressSandboxes;
|
|
45
|
+
/** A tunnel with no traffic either way for this long is closed. Default `egress.idleMs`. */
|
|
49
46
|
readonly idleMs?: number;
|
|
47
|
+
/** Tunnels one sandbox may hold open at once. Default `egress.maxTunnelsPerSandbox`. */
|
|
50
48
|
readonly maxPerSandbox?: number;
|
|
49
|
+
/** How long a sandbox's epoch and allowlist are reused. Default `egress.cacheMs`. */
|
|
51
50
|
readonly cacheMs?: number;
|
|
51
|
+
/** How long the gate waits for the upstream connection. Default `egress.connectTimeoutMs`. */
|
|
52
52
|
readonly connectTimeoutMs?: number;
|
|
53
53
|
/** Tests replace name resolution. */
|
|
54
54
|
readonly lookup?: (host: string) => Promise<readonly {
|
|
@@ -90,7 +90,7 @@ export declare function storeEgressSandboxes(store: SessionStore): EgressSandbox
|
|
|
90
90
|
/**
|
|
91
91
|
* `EgressRecord` over the Tenant's record (R7.3, D67). The running turns are the sessions on the
|
|
92
92
|
* Environment whose body has an `activeTurnId`, in id order, at most `EGRESS_RUNNING_TURNS_MAX`.
|
|
93
|
-
* The write is `
|
|
93
|
+
* The write is `agent.egress` on the Environment's stream, under its row's lock; a deleted
|
|
94
94
|
* Environment gets nothing.
|
|
95
95
|
*/
|
|
96
96
|
export declare function storeEgressRecord(store: SessionStore): EgressRecord;
|
package/dist/gates/egress.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* never is. Tunnels close after `idleMs` without traffic in either direction.
|
|
26
26
|
*
|
|
27
27
|
* Every connection to a named host on a live token is recorded once (R7.3, D67), when it closes
|
|
28
|
-
* or at once when the gate refuses it, as `
|
|
28
|
+
* or at once when the gate refuses it, as `agent.egress` on the Environment's own stream:
|
|
29
29
|
* the connection is the Environment's, since its token names the pod and turns run in parallel
|
|
30
30
|
* (D66). `runningTurns` lists the turns active on the Environment as it opened, as context and
|
|
31
31
|
* never as an owner: one read, started as the name resolves and never waited for, so a turn that
|
|
@@ -41,19 +41,16 @@ import { BlockList, connect, isIP } from "node:net";
|
|
|
41
41
|
import { isSandboxHostPattern } from "@nylorun/core/define";
|
|
42
42
|
import { isPrivateAddress } from "../tenant/outbound.js";
|
|
43
43
|
import { bindListener } from "../host/http.js";
|
|
44
|
+
import { hostTuning } from "../tuning.js";
|
|
44
45
|
import { CONNECT_ESTABLISHED, credentialTerminator, } from "./egress-credentials.js";
|
|
45
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* How many running turns one connection names, at most: fixed by the `agent.egress` event
|
|
48
|
+
* contract (`runningTurns` holds at most 16 in `/core/contracts`), which every reader
|
|
49
|
+
* validates against, so not a setting here.
|
|
50
|
+
*/
|
|
46
51
|
export const EGRESS_RUNNING_TURNS_MAX = 16;
|
|
47
52
|
/** Ports a tunnel may open. */
|
|
48
53
|
export const EGRESS_PORTS = [443, 80];
|
|
49
|
-
/** Tunnels one sandbox may hold open at once. */
|
|
50
|
-
export const EGRESS_MAX_PER_SANDBOX = 64;
|
|
51
|
-
/** A tunnel with no traffic either way for this long is closed. */
|
|
52
|
-
export const EGRESS_IDLE_MS = 5 * 60_000;
|
|
53
|
-
/** How long a sandbox's epoch and allowlist are reused before they are read again. */
|
|
54
|
-
export const EGRESS_CACHE_MS = 5_000;
|
|
55
|
-
/** How long the gate waits for the upstream connection. */
|
|
56
|
-
export const EGRESS_CONNECT_TIMEOUT_MS = 10_000;
|
|
57
54
|
/** Ranges beyond `isPrivateAddress`'s that a tunnel never reaches. */
|
|
58
55
|
const RESERVED = new BlockList();
|
|
59
56
|
for (const [network, prefix] of [
|
|
@@ -159,7 +156,7 @@ function connectionRecord(sink, logger, client, at) {
|
|
|
159
156
|
const allowed = fields.outcome === "allowed";
|
|
160
157
|
const durationMs = Math.max(0, Date.now() - at.startedAt.getTime());
|
|
161
158
|
const connection = (turns) => ({
|
|
162
|
-
|
|
159
|
+
agentId: at.environmentId,
|
|
163
160
|
host: at.host,
|
|
164
161
|
port: at.port,
|
|
165
162
|
...fields,
|
|
@@ -194,10 +191,11 @@ function connectionRecord(sink, logger, client, at) {
|
|
|
194
191
|
}
|
|
195
192
|
export async function startEgressGate(options) {
|
|
196
193
|
const { logger, sandboxes } = options;
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const
|
|
194
|
+
const tuning = hostTuning().egress;
|
|
195
|
+
const idleMs = options.idleMs ?? tuning.idleMs;
|
|
196
|
+
const maxPerSandbox = options.maxPerSandbox ?? tuning.maxTunnelsPerSandbox;
|
|
197
|
+
const cacheMs = options.cacheMs ?? tuning.cacheMs;
|
|
198
|
+
const connectTimeoutMs = options.connectTimeoutMs ?? tuning.connectTimeoutMs;
|
|
201
199
|
const blocked = options.blocked ?? isEgressBlocked;
|
|
202
200
|
const lookup = options.lookup ?? ((host) => dnsLookup(host, { all: true, verbatim: true }));
|
|
203
201
|
const dial = options.dial ?? ((address, port) => connect({ host: address, port }));
|
|
@@ -209,7 +207,7 @@ export async function startEgressGate(options) {
|
|
|
209
207
|
return cached.value;
|
|
210
208
|
const value = await sandboxes.live(sandboxId);
|
|
211
209
|
cache.set(sandboxId, { at: Date.now(), value });
|
|
212
|
-
if (cache.size >
|
|
210
|
+
if (cache.size > tuning.cacheEntries)
|
|
213
211
|
cache.delete(cache.keys().next().value);
|
|
214
212
|
return value;
|
|
215
213
|
}
|
|
@@ -355,8 +353,8 @@ export async function startEgressGate(options) {
|
|
|
355
353
|
res.writeHead(405, { allow: "CONNECT", "content-type": "text/plain; charset=utf-8", connection: "close" });
|
|
356
354
|
res.end("egress-gate: only CONNECT is served; set HTTPS_PROXY and HTTP_PROXY to this address\n");
|
|
357
355
|
});
|
|
358
|
-
server.headersTimeout =
|
|
359
|
-
server.requestTimeout =
|
|
356
|
+
server.headersTimeout = tuning.headersTimeoutMs;
|
|
357
|
+
server.requestTimeout = tuning.requestTimeoutMs;
|
|
360
358
|
server.on("connect", (req, client, head) => {
|
|
361
359
|
// A client socket: idle from the request on, through the tunnel's life.
|
|
362
360
|
client.on("error", () => { });
|
|
@@ -416,7 +414,7 @@ export function storeEgressSandboxes(store) {
|
|
|
416
414
|
/**
|
|
417
415
|
* `EgressRecord` over the Tenant's record (R7.3, D67). The running turns are the sessions on the
|
|
418
416
|
* Environment whose body has an `activeTurnId`, in id order, at most `EGRESS_RUNNING_TURNS_MAX`.
|
|
419
|
-
* The write is `
|
|
417
|
+
* The write is `agent.egress` on the Environment's stream, under its row's lock; a deleted
|
|
420
418
|
* Environment gets nothing.
|
|
421
419
|
*/
|
|
422
420
|
export function storeEgressRecord(store) {
|
|
@@ -431,9 +429,9 @@ export function storeEgressRecord(store) {
|
|
|
431
429
|
},
|
|
432
430
|
async write(connection) {
|
|
433
431
|
await store.tx(async (t) => {
|
|
434
|
-
if (!(await t.environment(connection.
|
|
432
|
+
if (!(await t.environment(connection.agentId, { lock: true })))
|
|
435
433
|
return;
|
|
436
|
-
await t.
|
|
434
|
+
await t.agentEvent(connection.agentId, "agent.egress", connection);
|
|
437
435
|
});
|
|
438
436
|
},
|
|
439
437
|
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { FILES_MESSAGE_HEADER } from "@nylorun/harness";
|
|
1
2
|
export const FIXTURE_PROVIDER = "fixture";
|
|
2
3
|
const MARKER = /^fixture:(\w+)(?:\s+([\s\S]*))?$/;
|
|
3
4
|
/** The fixture model's answer to `call`. */
|
|
4
5
|
export async function fixtureAnswer(call, signal) {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
// Our harness ends a turn that has files with a user message listing them; the script is in
|
|
7
|
+
// the message before it, and a tool result before it is still the last thing that happened.
|
|
8
|
+
const prompt = call.prompt.filter((item) => !isFilesMessage(item));
|
|
9
|
+
const last = prompt.at(-1);
|
|
10
|
+
const users = prompt.filter((item) => item.kind === "message" && item.role === "user");
|
|
7
11
|
const marker = MARKER.exec(textOf(users.at(-1)?.content ?? []).trim());
|
|
8
12
|
if (marker)
|
|
9
13
|
return scripted(call, marker[1], marker[2]?.trim() ?? "", last, signal);
|
|
@@ -74,6 +78,13 @@ function candidate(call, output) {
|
|
|
74
78
|
}
|
|
75
79
|
/** About four characters a token, at least one. */
|
|
76
80
|
const tokens = (text) => Math.max(1, Math.ceil(text.length / 4));
|
|
81
|
+
/** The user message our harness adds for the turn's files (`FILES_MESSAGE_HEADER`). */
|
|
82
|
+
function isFilesMessage(item) {
|
|
83
|
+
if (item.kind !== "message" || item.role !== "user")
|
|
84
|
+
return false;
|
|
85
|
+
const first = item.content[0];
|
|
86
|
+
return first?.type === "text" && first.text === FILES_MESSAGE_HEADER;
|
|
87
|
+
}
|
|
77
88
|
function textOf(content, json = false) {
|
|
78
89
|
return content
|
|
79
90
|
.map((part) => (part.type === "text" ? (part.text ?? "") : json ? JSON.stringify(part) : ""))
|
package/dist/gates/handler.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { Logger } from "../tenant/types.js";
|
|
2
2
|
import type { BlobStore } from "../blob/index.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ModelGate, ModelGateRequest } from "./model-gate.js";
|
|
4
4
|
import { type TenantVaults } from "./tenant-vaults.js";
|
|
5
5
|
export interface ModelCallHandlerOptions {
|
|
6
6
|
readonly vaults: TenantVaults;
|
|
7
7
|
readonly logger: Logger;
|
|
8
|
-
/** Retries and timeouts; `piModel`'s defaults when absent. */
|
|
9
|
-
readonly settings?: ModelCallSettings;
|
|
10
8
|
/** The Object store, for the files a prompt names (protocol 6). */
|
|
11
9
|
readonly blobs?: BlobStore;
|
|
12
10
|
}
|
package/dist/gates/handler.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* the Tenant's ledger. Logs one line per call, never the prompt, the output or a credential.
|
|
7
7
|
*/
|
|
8
8
|
import { classifyThrown } from "../model/classify.js";
|
|
9
|
-
import { callFiles, inProcessModelGate, serve } from "./in-process.js";
|
|
9
|
+
import { callFiles, inProcessModelGate, modelCallSettings, serve } from "./in-process.js";
|
|
10
10
|
import { createMeter } from "./meter.js";
|
|
11
11
|
import { GateRefusal } from "./tenant-vaults.js";
|
|
12
12
|
export function createModelCallHandler(options) {
|
|
13
|
-
const { vaults, logger,
|
|
13
|
+
const { vaults, logger, blobs } = options;
|
|
14
14
|
const meter = createMeter({ logger });
|
|
15
15
|
return {
|
|
16
16
|
async call(named, signal) {
|
|
@@ -25,7 +25,8 @@ export function createModelCallHandler(options) {
|
|
|
25
25
|
root: vault.root,
|
|
26
26
|
readHostModel: () => vault.readHostModel(),
|
|
27
27
|
writeHostCredential: (credential) => vault.writeHostCredential(credential),
|
|
28
|
-
|
|
28
|
+
// The Tenant's Runtime limits `model*`, read for this call.
|
|
29
|
+
settings: await modelCallSettings(vault.store),
|
|
29
30
|
...(blobs ? { files: callFiles(vault.store, blobs) } : {}),
|
|
30
31
|
});
|
|
31
32
|
outcome = await meter.call(vault.store, call, () => serve(vault.store, gate, call, signal));
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ModelGate } from "./model-gate.js";
|
|
2
2
|
import type { RunTokens } from "../tenant/run-grants.js";
|
|
3
|
-
/** Above the gate's 600 s provider request timeout. */
|
|
4
|
-
export declare const GATE_CLIENT_TIMEOUT_MS = 630000;
|
|
5
3
|
export interface HttpModelGateOptions {
|
|
6
4
|
/** The gates service, e.g. `http://gateway:4100` (`NYLORUN_GATES_URL`). */
|
|
7
5
|
readonly url: string;
|
|
8
6
|
/** The run token of each session an advance of this process owns: a call's credential. */
|
|
9
7
|
readonly runTokens: RunTokens;
|
|
10
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* How long the connection may stay silent. Default `gates.clientTimeoutMs` (Host tuning, 630 s),
|
|
10
|
+
* above the gate's 600 s provider request timeout.
|
|
11
|
+
*/
|
|
11
12
|
readonly timeoutMs?: number;
|
|
12
13
|
}
|
|
13
14
|
export declare function httpModelGate(options: HttpModelGateOptions): ModelGate;
|
|
@@ -19,14 +19,11 @@ import { request as httpRequest } from "node:http";
|
|
|
19
19
|
import { request as httpsRequest } from "node:https";
|
|
20
20
|
import { failure } from "../model/classify.js";
|
|
21
21
|
import { MODEL_CALLS_PATH, TENANT_HEADER, } from "./contract.js";
|
|
22
|
-
|
|
23
|
-
const CANCEL_TIMEOUT_MS = 2_000;
|
|
24
|
-
/** Above the gate's 600 s provider request timeout. */
|
|
25
|
-
export const GATE_CLIENT_TIMEOUT_MS = 630_000;
|
|
22
|
+
import { hostTuning } from "../tuning.js";
|
|
26
23
|
export function httpModelGate(options) {
|
|
27
24
|
const endpoint = new URL(MODEL_CALLS_PATH, options.url);
|
|
28
25
|
const send = endpoint.protocol === "https:" ? httpsRequest : httpRequest;
|
|
29
|
-
const timeoutMs = options.timeoutMs ??
|
|
26
|
+
const timeoutMs = options.timeoutMs ?? hostTuning().gates.clientTimeoutMs;
|
|
30
27
|
const where = new URL(options.url).origin;
|
|
31
28
|
return {
|
|
32
29
|
recovers: true,
|
|
@@ -44,7 +41,8 @@ export function httpModelGate(options) {
|
|
|
44
41
|
[TENANT_HEADER]: request.tenantId,
|
|
45
42
|
"content-length": 0,
|
|
46
43
|
},
|
|
47
|
-
timeout
|
|
44
|
+
// A cancel is short; the call it stops ends with the provider timeout anyway.
|
|
45
|
+
timeout: hostTuning().gates.shortTimeoutMs,
|
|
48
46
|
}, (response) => {
|
|
49
47
|
response.resume();
|
|
50
48
|
response.on("end", resolve);
|
|
@@ -107,7 +105,7 @@ export function httpModelGate(options) {
|
|
|
107
105
|
responded = true;
|
|
108
106
|
readBody(response).then((text) => settle(answer(response.statusCode ?? 0, text)), (error) => lost(error));
|
|
109
107
|
});
|
|
110
|
-
outgoing.on("socket", (socket) => socket.setKeepAlive(true,
|
|
108
|
+
outgoing.on("socket", (socket) => socket.setKeepAlive(true, hostTuning().gates.keepAliveMs));
|
|
111
109
|
outgoing.on("finish", () => {
|
|
112
110
|
sent = true;
|
|
113
111
|
});
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { type HttpToolTarget, type JsonObject, type ToolManifest } from "@nylorun/core/define";
|
|
2
2
|
import type { HostEffect } from "@nylorun/harness/run";
|
|
3
|
+
import type { RuntimeLimits } from "@nylorun/core/contracts";
|
|
3
4
|
import { type OutboundPolicy } from "../tenant/outbound.js";
|
|
4
5
|
import type { Logger } from "../tenant/types.js";
|
|
5
6
|
import type { AuthorizeResult } from "../vault/service.js";
|
|
6
7
|
import type { McpCredentialRequest } from "../vault/sources.js";
|
|
7
8
|
import type { TenantVaults } from "./tenant-vaults.js";
|
|
8
|
-
/** How much of a failed answer's body the model sees. */
|
|
9
|
-
export declare const HTTP_ERROR_BODY_CHARS = 2000;
|
|
10
9
|
/** What an HTTP request came to, as a tool outcome. */
|
|
11
10
|
export type HttpOutcome = {
|
|
12
11
|
readonly kind: "completed";
|
|
@@ -49,17 +48,29 @@ export interface HttpToolCall {
|
|
|
49
48
|
*/
|
|
50
49
|
readonly traceparent?: string;
|
|
51
50
|
}
|
|
52
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* What `runHttpTool` reads of the Tenant: the session's pinned manifest, its credentials, and its
|
|
53
|
+
* Runtime limits (`toolAnswerBytes`, `httpToolTimeoutMs`, `errorExcerptChars`), read for each call.
|
|
54
|
+
*/
|
|
53
55
|
export interface HttpToolTenant {
|
|
54
56
|
session(sessionId: string): Promise<{
|
|
55
57
|
readonly manifest: unknown;
|
|
56
58
|
} | undefined>;
|
|
57
59
|
authorize(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
60
|
+
limits(): Promise<HttpToolLimits>;
|
|
58
61
|
}
|
|
62
|
+
/** The Runtime limits an HTTP tool call keeps to. */
|
|
63
|
+
export type HttpToolLimits = Pick<RuntimeLimits, "toolAnswerBytes" | "httpToolTimeoutMs" | "errorExcerptChars">;
|
|
59
64
|
/** The HTTP tool `toolName` of the agent `agentId` (the root when absent). */
|
|
60
65
|
export declare function declaredHttpTool(manifest: unknown, agentId: string | undefined, toolName: string | undefined): (ToolManifest & {
|
|
61
66
|
readonly http: HttpToolTarget;
|
|
62
67
|
}) | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Why `manifest` cannot be deployed: each HTTP tool of it, or of an agent it uses as a tool, whose
|
|
70
|
+
* `timeoutMs` is longer than the Tenant's Runtime limit `httpToolTimeoutMs`. A call also waits at
|
|
71
|
+
* most the limit in force then (`callHttpTarget`).
|
|
72
|
+
*/
|
|
73
|
+
export declare function httpToolTimeoutIssues(manifest: unknown, limitMs: number): string[];
|
|
63
74
|
/** The HTTP stage or HTTP verifier at stage key `stage` of a flow session's `manifest`. */
|
|
64
75
|
export declare function declaredFlowHttp(manifest: unknown, stage: string | undefined): import("@nylorun/core/define").FlowHttpTarget | undefined;
|
|
65
76
|
/**
|
|
@@ -67,7 +78,10 @@ export declare function declaredFlowHttp(manifest: unknown, stage: string | unde
|
|
|
67
78
|
* flow's HTTP stages or HTTP verifiers (a flow `tool` effect, named by its stage key).
|
|
68
79
|
*/
|
|
69
80
|
export declare function isHttpToolCall(manifest: unknown, request: HostEffect): boolean;
|
|
70
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* The ref a `tool` effect calls the Tool Gate with: its agent's HTTP tool, by the declared name
|
|
83
|
+
* its target names, or its flow stage.
|
|
84
|
+
*/
|
|
71
85
|
export declare function httpToolRefOf(request: HostEffect): HttpToolRef;
|
|
72
86
|
/**
|
|
73
87
|
* Sends `input` to `target` and reads the answer. Never throws: everything that can happen to
|
|
@@ -77,6 +91,12 @@ export declare function callHttpTarget(target: HttpToolTarget, input: unknown, o
|
|
|
77
91
|
readonly headers: Readonly<Record<string, string>>;
|
|
78
92
|
readonly policy: OutboundPolicy;
|
|
79
93
|
readonly signal: AbortSignal;
|
|
94
|
+
/**
|
|
95
|
+
* The Tenant's Runtime limits: the largest answer read (`toolAnswerBytes`; past it,
|
|
96
|
+
* `http.too-large`), the longest the tool may wait (`httpToolTimeoutMs`, which its own
|
|
97
|
+
* `timeoutMs` may not pass) and how much of an error answer is quoted (`errorExcerptChars`).
|
|
98
|
+
*/
|
|
99
|
+
readonly limits: HttpToolLimits;
|
|
80
100
|
/** Checked against the answer; a mismatch is `tool.invalid-output`. */
|
|
81
101
|
readonly outputSchema?: JsonObject;
|
|
82
102
|
}): Promise<HttpOutcome>;
|
package/dist/gates/http-tool.js
CHANGED
|
@@ -16,12 +16,11 @@
|
|
|
16
16
|
* `Nylorun-Agent-Id` is the flow agent's.
|
|
17
17
|
*/
|
|
18
18
|
import { AGENT_ID_HEADER, SESSION_ID_HEADER, TURN_ID_HEADER, } from "@nylorun/core/compatibility";
|
|
19
|
-
import { HTTP_TOOL_DEFAULT_TIMEOUT_MS, delegateManifest, flowHttpTarget, httpPathParameters, httpToolRequest, isWorkflowManifest, schemaFromJSON, } from "@nylorun/core/define";
|
|
19
|
+
import { HTTP_TOOL_DEFAULT_TIMEOUT_MS, delegateManifest, delegatesOf, flowHttpTarget, httpPathParameters, httpToolRequest, isWorkflowManifest, schemaFromJSON, } from "@nylorun/core/define";
|
|
20
20
|
import { scrubValues } from "../redact.js";
|
|
21
|
-
import {
|
|
21
|
+
import { send } from "../tenant/outbound.js";
|
|
22
22
|
import { credentialSecrets } from "../vault/headers.js";
|
|
23
|
-
|
|
24
|
-
export const HTTP_ERROR_BODY_CHARS = 2_000;
|
|
23
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
25
24
|
/** The HTTP tool `toolName` of the agent `agentId` (the root when absent). */
|
|
26
25
|
export function declaredHttpTool(manifest, agentId, toolName) {
|
|
27
26
|
const root = manifest;
|
|
@@ -31,6 +30,24 @@ export function declaredHttpTool(manifest, agentId, toolName) {
|
|
|
31
30
|
const tool = agent?.tools?.find((item) => item.name === toolName);
|
|
32
31
|
return tool?.http ? tool : undefined;
|
|
33
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Why `manifest` cannot be deployed: each HTTP tool of it, or of an agent it uses as a tool, whose
|
|
35
|
+
* `timeoutMs` is longer than the Tenant's Runtime limit `httpToolTimeoutMs`. A call also waits at
|
|
36
|
+
* most the limit in force then (`callHttpTarget`).
|
|
37
|
+
*/
|
|
38
|
+
export function httpToolTimeoutIssues(manifest, limitMs) {
|
|
39
|
+
const root = manifest;
|
|
40
|
+
if (!root || typeof root !== "object" || isWorkflowManifest(root))
|
|
41
|
+
return [];
|
|
42
|
+
return [root, ...delegatesOf(root).map((delegate) => delegate.manifest)].flatMap((agent) => (agent.tools ?? []).flatMap((tool) => {
|
|
43
|
+
const asked = tool.http?.timeoutMs;
|
|
44
|
+
return asked !== undefined && asked > limitMs
|
|
45
|
+
? [
|
|
46
|
+
`HTTP tool '${tool.name}' of agent '${agent.id}' asks for timeoutMs ${asked}; the Runtime limit httpToolTimeoutMs allows at most ${limitMs} (PUT /v1/tenant/limits).`,
|
|
47
|
+
]
|
|
48
|
+
: [];
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
34
51
|
/** The HTTP stage or HTTP verifier at stage key `stage` of a flow session's `manifest`. */
|
|
35
52
|
export function declaredFlowHttp(manifest, stage) {
|
|
36
53
|
return stage !== undefined && isWorkflowManifest(manifest)
|
|
@@ -44,18 +61,24 @@ export function declaredFlowHttp(manifest, stage) {
|
|
|
44
61
|
export function isHttpToolCall(manifest, request) {
|
|
45
62
|
if (request.kind !== "tool")
|
|
46
63
|
return false;
|
|
47
|
-
|
|
64
|
+
// An agent's call names what runs it (protocol 15): an `http` call names its declared tool.
|
|
65
|
+
if (request.target?.kind === "http")
|
|
66
|
+
return declaredHttpTool(manifest, request.agent?.id, request.target.tool) !== undefined;
|
|
67
|
+
if (request.target === undefined && request.toolName === undefined)
|
|
48
68
|
return declaredFlowHttp(manifest, request.key) !== undefined;
|
|
49
|
-
return
|
|
69
|
+
return false;
|
|
50
70
|
}
|
|
51
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* The ref a `tool` effect calls the Tool Gate with: its agent's HTTP tool, by the declared name
|
|
73
|
+
* its target names, or its flow stage.
|
|
74
|
+
*/
|
|
52
75
|
export function httpToolRefOf(request) {
|
|
53
|
-
if (request.
|
|
76
|
+
if (request.target?.kind !== "http")
|
|
54
77
|
return { sessionId: request.sessionId, stage: request.key };
|
|
55
78
|
return {
|
|
56
79
|
sessionId: request.sessionId,
|
|
57
80
|
...(request.agent ? { agentId: request.agent.id } : {}),
|
|
58
|
-
toolName: request.
|
|
81
|
+
toolName: request.target.tool,
|
|
59
82
|
};
|
|
60
83
|
}
|
|
61
84
|
/**
|
|
@@ -66,12 +89,14 @@ export async function callHttpTarget(target, input, options) {
|
|
|
66
89
|
const request = httpToolRequest(target, input);
|
|
67
90
|
if ("issue" in request)
|
|
68
91
|
return failed("tool.invalid-input", request.issue);
|
|
69
|
-
const
|
|
92
|
+
const { limits } = options;
|
|
93
|
+
const timeoutMs = Math.min(target.timeoutMs ?? HTTP_TOOL_DEFAULT_TIMEOUT_MS, limits.httpToolTimeoutMs);
|
|
70
94
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
71
95
|
const result = await send(request.url, request.body, { ...options.headers }, {
|
|
72
96
|
policy: options.policy,
|
|
73
97
|
method: request.method,
|
|
74
98
|
signal: AbortSignal.any([options.signal, timeout]),
|
|
99
|
+
maxResponseBytes: limits.toolAnswerBytes,
|
|
75
100
|
});
|
|
76
101
|
if (options.signal.aborted)
|
|
77
102
|
throw options.signal.reason;
|
|
@@ -85,14 +110,15 @@ export async function callHttpTarget(target, input, options) {
|
|
|
85
110
|
case "lost":
|
|
86
111
|
return failed("http.lost", `The connection was lost after the request was sent, so the call may have run: ${result.message}`);
|
|
87
112
|
case "too_large":
|
|
88
|
-
return failed("http.too-large", `The answer (HTTP ${result.status}) is larger than ${
|
|
113
|
+
return failed("http.too-large", `The answer (HTTP ${result.status}) is larger than ${limits.toolAnswerBytes} bytes (the Runtime limit toolAnswerBytes)`);
|
|
89
114
|
}
|
|
90
115
|
// The service answered, so the outcome is known; never retried, and its body is not shown (R2b C1).
|
|
91
116
|
if (result.status === 401)
|
|
92
117
|
return failed("credential_rejected", "The service answered HTTP 401: it rejected the credential, or needs one");
|
|
93
118
|
const text = result.body.toString("utf8");
|
|
94
119
|
if (result.status < 200 || result.status > 299) {
|
|
95
|
-
const
|
|
120
|
+
const chars = limits.errorExcerptChars;
|
|
121
|
+
const body = text.length > chars ? `${text.slice(0, chars)}…` : text;
|
|
96
122
|
return failed("http.status", `The service answered HTTP ${result.status}${body ? `: ${body}` : ""}`);
|
|
97
123
|
}
|
|
98
124
|
const type = String(result.headers["content-type"] ?? "");
|
|
@@ -193,6 +219,7 @@ export async function runHttpTool(tenant, call, options) {
|
|
|
193
219
|
},
|
|
194
220
|
policy: options.policy,
|
|
195
221
|
signal: options.signal,
|
|
222
|
+
limits: await tenant.limits(),
|
|
196
223
|
...(declared.outputSchema === undefined ? {} : { outputSchema: declared.outputSchema }),
|
|
197
224
|
});
|
|
198
225
|
// Nothing the service echoes of the credential reaches the model, an event or the gate's
|
|
@@ -202,7 +229,7 @@ export async function runHttpTool(tenant, call, options) {
|
|
|
202
229
|
return outcome;
|
|
203
230
|
return {
|
|
204
231
|
...outcome,
|
|
205
|
-
message: `The service answered HTTP 401: it rejected the credential '${http.credential}' from the session's
|
|
232
|
+
message: `The service answered HTTP 401: it rejected the credential '${http.credential}' from the session's Agent`,
|
|
206
233
|
server: http.credential,
|
|
207
234
|
};
|
|
208
235
|
}
|
|
@@ -213,6 +240,7 @@ export function gateHttpTools(vaults, policy, logger) {
|
|
|
213
240
|
call: (tenantId, call, signal) => runHttpTool({
|
|
214
241
|
session: async (sessionId) => (await vaults.open(tenantId)).session(sessionId),
|
|
215
242
|
authorize: async (sessionId, request) => (await vaults.open(tenantId)).authorizeMcp(sessionId, request),
|
|
243
|
+
limits: async () => (await vaults.open(tenantId)).store.tx((t) => readRuntimeLimits(t)),
|
|
216
244
|
}, call, { policy, signal, logger }),
|
|
217
245
|
};
|
|
218
246
|
}
|
|
@@ -37,10 +37,11 @@ export declare function tenantModelGate(options: {
|
|
|
37
37
|
readonly kek: () => Buffer;
|
|
38
38
|
readonly root: string;
|
|
39
39
|
readonly logger: Logger;
|
|
40
|
-
readonly settings?: ModelCallSettings;
|
|
41
40
|
/** The Tenant's Object store, for the files a prompt names. */
|
|
42
41
|
readonly blobs?: BlobStore;
|
|
43
42
|
}): ModelGate;
|
|
43
|
+
/** The Tenant's model call settings: its Runtime limits `model*`, read now. */
|
|
44
|
+
export declare function modelCallSettings(store: SessionStore): Promise<Required<ModelCallSettings>>;
|
|
44
45
|
/**
|
|
45
46
|
* Serves one call: from the fixture model while the Tenant's `model.fixture` setting is on (R4.3),
|
|
46
47
|
* else from `gate`. Read per call, so a seed applies to the next call.
|