@nylorun/runtime 0.16.0-beta → 0.19.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 +207 -0
- package/README.md +105 -63
- package/dist/adapters/execution/restate.d.ts +1 -3
- package/dist/adapters/execution/restate.js +4 -31
- package/dist/adapters/replication/pgoutput.js +1 -1
- package/dist/adapters/sandbox/local.js +1 -1
- package/dist/adapters/sandbox/virtual.js +1 -1
- package/dist/api/ag-ui/endpoint.js +1 -2
- package/dist/api/ag-ui/routes.d.ts +1 -1
- package/dist/api/ag-ui/routes.js +11 -13
- package/dist/api/ag-ui/translate.d.ts +1 -3
- package/dist/api/ag-ui/translate.js +2 -17
- package/dist/api/components.d.ts +4783 -2588
- package/dist/api/components.js +19 -21
- package/dist/api/gate/routes.d.ts +7 -8
- package/dist/api/gate/routes.js +58 -52
- package/dist/api/http/app.d.ts +6 -0
- package/dist/api/http/app.js +20 -14
- package/dist/api/http/define.d.ts +22 -13
- package/dist/api/http/define.js +43 -39
- package/dist/api/http/routes/access.d.ts +3 -19
- package/dist/api/http/routes/access.js +25 -184
- package/dist/api/http/routes/artifacts.js +3 -7
- package/dist/api/http/routes/files.d.ts +8 -0
- package/dist/api/http/routes/files.js +77 -0
- package/dist/api/http/routes/keys.d.ts +10 -0
- package/dist/api/http/routes/keys.js +64 -0
- package/dist/api/http/routes/me.d.ts +13 -0
- package/dist/api/http/routes/me.js +48 -0
- package/dist/api/http/routes/reads.d.ts +66 -0
- package/dist/api/http/routes/reads.js +127 -0
- package/dist/api/http/routes/sandboxes.d.ts +2 -2
- package/dist/api/http/routes/sandboxes.js +8 -7
- package/dist/api/http/routes/sessions.js +31 -20
- package/dist/api/http/routes/tenant.js +15 -18
- package/dist/api/http/routes/vaults.d.ts +13 -3
- package/dist/api/http/routes/vaults.js +136 -64
- package/dist/api/openapi.d.ts +10 -11
- package/dist/api/openapi.js +292 -68
- package/dist/artifacts/links.js +6 -6
- package/dist/artifacts/service.js +1 -1
- package/dist/core/flow-host.d.ts +44 -23
- package/dist/core/flow-host.js +88 -106
- package/dist/core/provider.d.ts +4 -1
- package/dist/core/provider.js +6 -1
- package/dist/core/sandbox-routes.d.ts +0 -9
- package/dist/core/sandbox-routes.js +2 -21
- package/dist/execution/memory.d.ts +4 -5
- package/dist/execution/memory.js +11 -25
- package/dist/execution/types.d.ts +3 -33
- package/dist/execution/types.js +2 -12
- package/dist/gates/http-tool.d.ts +86 -0
- package/dist/gates/http-tool.js +197 -0
- package/dist/gates/mcp-handler.d.ts +3 -0
- package/dist/gates/mcp-handler.js +6 -4
- package/dist/gates/meter.d.ts +2 -2
- package/dist/gates/meter.js +6 -0
- package/dist/gates/tenant-vaults.d.ts +19 -14
- package/dist/gates/tenant-vaults.js +17 -12
- package/dist/gates/tool-calls.d.ts +7 -15
- package/dist/gates/tool-calls.js +4 -4
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +23 -47
- package/dist/gates/tool-contract.d.ts +27 -39
- package/dist/gates/tool-contract.js +30 -21
- package/dist/gates/tool-gate.d.ts +25 -24
- package/dist/gates/tool-gate.js +11 -7
- package/dist/harness/calls.d.ts +10 -4
- package/dist/harness/calls.js +15 -6
- package/dist/harness/executors.d.ts +1 -1
- package/dist/harness/executors.js +17 -12
- package/dist/harness/main.d.ts +2 -2
- package/dist/harness/main.js +5 -13
- package/dist/harness/service.d.ts +4 -5
- package/dist/harness/service.js +9 -2
- package/dist/harness-api/in-process.js +2 -2
- package/dist/harness-api/record.d.ts +6 -3
- package/dist/harness-api/record.js +65 -72
- package/dist/harness-api/server.d.ts +1 -9
- package/dist/harness-api/server.js +19 -17
- package/dist/harness-api/start.js +1 -5
- package/dist/harness-api/workspace.js +2 -3
- package/dist/harness-api/ws-server.js +1 -1
- package/dist/host/app.d.ts +2 -10
- package/dist/host/app.js +24 -44
- package/dist/host/config-for.d.ts +5 -5
- package/dist/host/config-for.js +2 -3
- package/dist/host/config.d.ts +0 -11
- package/dist/host/create-host.d.ts +3 -40
- package/dist/host/create-host.js +18 -91
- package/dist/host/environment.d.ts +0 -3
- package/dist/host/environment.js +0 -9
- package/dist/host/gates.d.ts +4 -1
- package/dist/host/gates.js +9 -3
- package/dist/host/http.d.ts +1 -2
- package/dist/host/http.js +1 -7
- package/dist/host/index.d.ts +2 -2
- package/dist/host/index.js +2 -2
- package/dist/host/main.js +55 -40
- package/dist/host/operate.d.ts +29 -0
- package/dist/host/operate.js +223 -0
- package/dist/host/stack-config.d.ts +23 -20
- package/dist/host/stack-config.js +28 -44
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/infra/workers.d.ts +1 -1
- package/dist/keys/client.js +9 -1
- package/dist/keys/keys.d.ts +12 -2
- package/dist/keys/keys.js +10 -2
- package/dist/management-openapi.json +6491 -0
- package/dist/mcp/connect.d.ts +8 -4
- package/dist/mcp/connect.js +8 -27
- package/dist/mcp/pool.d.ts +6 -9
- package/dist/mcp/pool.js +8 -18
- package/dist/mcp/snapshot.d.ts +8 -2
- package/dist/mcp/snapshot.js +29 -2
- package/dist/media.d.ts +1 -1
- package/dist/media.js +1 -1
- package/dist/model/pi-model.js +3 -0
- package/dist/openapi.json +6228 -15615
- package/dist/reads/access.d.ts +4 -0
- package/dist/reads/access.js +12 -0
- package/dist/reads/cursor.d.ts +11 -0
- package/dist/reads/cursor.js +80 -0
- package/dist/reads/types.d.ts +25 -0
- package/dist/reads/types.js +1 -0
- package/dist/sandbox/manager.d.ts +16 -0
- package/dist/sandbox/manager.js +40 -0
- package/dist/sandbox/session-sandbox.js +13 -3
- package/dist/sandbox/skills.d.ts +13 -0
- package/dist/sandbox/skills.js +46 -0
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/store/ownership.d.ts +9 -0
- package/dist/store/ownership.js +13 -0
- package/dist/store/postgres/connect.d.ts +2 -0
- package/dist/store/postgres/connect.js +29 -0
- package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
- package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
- package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
- package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
- package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
- package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
- package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +49 -0
- package/dist/store/postgres/reads.d.ts +3 -0
- package/dist/store/postgres/reads.js +272 -0
- package/dist/store/postgres/schema.d.ts +423 -594
- package/dist/store/postgres/schema.js +84 -101
- package/dist/store/postgres/store.js +96 -248
- package/dist/store/postgres/tenant.d.ts +34 -2
- package/dist/store/postgres/tenant.js +16 -32
- package/dist/store/types.d.ts +44 -119
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +0 -7
- package/dist/streams/control.js +0 -13
- package/dist/streams/types.d.ts +2 -20
- package/dist/streams/types.js +1 -1
- package/dist/tenant/advance.d.ts +3 -3
- package/dist/tenant/advance.js +10 -7
- package/dist/tenant/auth.d.ts +24 -15
- package/dist/tenant/auth.js +44 -55
- package/dist/tenant/cause.d.ts +3 -3
- package/dist/tenant/cause.js +1 -1
- package/dist/tenant/commands.d.ts +2 -18
- package/dist/tenant/commands.js +38 -179
- package/dist/tenant/context.d.ts +33 -45
- package/dist/tenant/definition-files.d.ts +24 -0
- package/dist/tenant/definition-files.js +103 -0
- package/dist/tenant/effects.d.ts +20 -18
- package/dist/tenant/effects.js +59 -133
- package/dist/tenant/ephemeral.d.ts +21 -8
- package/dist/tenant/ephemeral.js +11 -12
- package/dist/tenant/host-token.js +5 -5
- package/dist/tenant/identity-file.d.ts +75 -0
- package/dist/tenant/identity-file.js +202 -0
- package/dist/tenant/issuers.d.ts +48 -0
- package/dist/tenant/issuers.js +311 -0
- package/dist/tenant/jwt.js +3 -3
- package/dist/tenant/module.d.ts +1 -4
- package/dist/tenant/module.js +3 -12
- package/dist/tenant/operator-keys.d.ts +44 -0
- package/dist/tenant/operator-keys.js +87 -0
- package/dist/tenant/outbound.d.ts +40 -1
- package/dist/tenant/outbound.js +167 -18
- package/dist/tenant/paths.d.ts +1 -1
- package/dist/tenant/paths.js +1 -3
- package/dist/tenant/principals.d.ts +13 -13
- package/dist/tenant/principals.js +35 -36
- package/dist/tenant/run-token.js +6 -6
- package/dist/tenant/runtime.d.ts +5 -3
- package/dist/tenant/runtime.js +29 -15
- package/dist/tenant/sandboxes.d.ts +9 -6
- package/dist/tenant/sandboxes.js +16 -5
- package/dist/tenant/scheduler.d.ts +1 -5
- package/dist/tenant/scheduler.js +2 -5
- package/dist/tenant/session-streams.d.ts +8 -11
- package/dist/tenant/session-streams.js +48 -32
- package/dist/tenant/session.d.ts +8 -41
- package/dist/tenant/session.js +0 -37
- package/dist/tenant/sessions.d.ts +2 -2
- package/dist/tenant/sessions.js +17 -9
- package/dist/tenant/signing-keys.d.ts +3 -2
- package/dist/tenant/signing-keys.js +5 -5
- package/dist/tenant/skill-tool.d.ts +20 -0
- package/dist/tenant/skill-tool.js +72 -0
- package/dist/tenant/slim.d.ts +1 -1
- package/dist/tenant/slim.js +1 -1
- package/dist/tenant/status.js +7 -12
- package/dist/tenant/store-pg.d.ts +15 -0
- package/dist/tenant/store-pg.js +7 -3
- package/dist/tenant/streams.d.ts +3 -11
- package/dist/tenant/streams.js +2 -29
- package/dist/tenant/sweep.js +0 -5
- package/dist/tenant/transcript.d.ts +2 -2
- package/dist/tenant/transcript.js +2 -2
- package/dist/tenant/types.d.ts +33 -19
- package/dist/tenant/worker.d.ts +1 -3
- package/dist/tenant/worker.js +3 -8
- package/dist/vault/crypto.d.ts +7 -0
- package/dist/vault/crypto.js +16 -0
- package/dist/vault/oauth.d.ts +55 -0
- package/dist/vault/oauth.js +186 -0
- package/dist/vault/service.d.ts +54 -7
- package/dist/vault/service.js +261 -17
- package/dist/vault/sources.d.ts +60 -0
- package/dist/vault/sources.js +196 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -4
- package/dist/admin-openapi.json +0 -604
- package/dist/api/http/routes/actions.d.ts +0 -8
- package/dist/api/http/routes/actions.js +0 -76
- package/dist/api/http/routes/endpoints.d.ts +0 -7
- package/dist/api/http/routes/endpoints.js +0 -100
- package/dist/host/admin-api.d.ts +0 -23
- package/dist/host/admin-api.js +0 -74
- package/dist/host/cors.d.ts +0 -27
- package/dist/host/cors.js +0 -64
- package/dist/mcp/stdio.d.ts +0 -17
- package/dist/mcp/stdio.js +0 -82
- package/dist/tenant/access-policy.d.ts +0 -30
- package/dist/tenant/access-policy.js +0 -52
- package/dist/tenant/actions.d.ts +0 -14
- package/dist/tenant/actions.js +0 -29
- package/dist/tenant/browser.d.ts +0 -15
- package/dist/tenant/browser.js +0 -39
- package/dist/tenant/delivery-token.d.ts +0 -41
- package/dist/tenant/delivery-token.js +0 -104
- package/dist/tenant/delivery.d.ts +0 -62
- package/dist/tenant/delivery.js +0 -481
- package/dist/tenant/endpoints.d.ts +0 -67
- package/dist/tenant/endpoints.js +0 -90
- package/dist/tenant/subject-limits.d.ts +0 -16
- package/dist/tenant/subject-limits.js +0 -45
- package/dist/tenant/tokens.d.ts +0 -14
- package/dist/tenant/tokens.js +0 -187
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type HttpToolTarget, type JsonObject, type ToolManifest } from "@nylorun/core/define";
|
|
2
|
+
import type { HostEffect } from "@nylorun/harness/run";
|
|
3
|
+
import { type OutboundPolicy } from "../tenant/outbound.js";
|
|
4
|
+
import type { Logger } from "../tenant/types.js";
|
|
5
|
+
import type { AuthorizeResult } from "../vault/service.js";
|
|
6
|
+
import type { McpCredentialRequest } from "../vault/sources.js";
|
|
7
|
+
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
|
+
/** What an HTTP request came to, as a tool outcome. */
|
|
11
|
+
export type HttpOutcome = {
|
|
12
|
+
readonly kind: "completed";
|
|
13
|
+
readonly output: unknown;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "failed";
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly message: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* One declared HTTP tool of a session: what the gate finds it by in the pinned manifest. An
|
|
21
|
+
* agent's HTTP tool is named by its capability and tool; a flow's HTTP stage or HTTP verifier
|
|
22
|
+
* by its stage key.
|
|
23
|
+
*/
|
|
24
|
+
export type HttpToolRef = {
|
|
25
|
+
readonly sessionId: string;
|
|
26
|
+
/** The agent used as a tool that declares it; absent for the session's root agent. */
|
|
27
|
+
readonly agentId?: string;
|
|
28
|
+
readonly capabilityId: string;
|
|
29
|
+
readonly toolName: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly sessionId: string;
|
|
32
|
+
/** The stage key of a flow's HTTP stage or HTTP verifier. */
|
|
33
|
+
readonly stage: string;
|
|
34
|
+
};
|
|
35
|
+
/** One call of an HTTP tool. `effectId` is its run-once key, sent as `Idempotency-Key`. */
|
|
36
|
+
export interface HttpToolCall {
|
|
37
|
+
readonly tool: HttpToolRef;
|
|
38
|
+
readonly effectId: string;
|
|
39
|
+
readonly turnId: string;
|
|
40
|
+
readonly input: unknown;
|
|
41
|
+
}
|
|
42
|
+
/** What `runHttpTool` reads of the Tenant: the session's pinned manifest and its credentials. */
|
|
43
|
+
export interface HttpToolTenant {
|
|
44
|
+
session(sessionId: string): Promise<{
|
|
45
|
+
readonly manifest: unknown;
|
|
46
|
+
} | undefined>;
|
|
47
|
+
authorize(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
48
|
+
}
|
|
49
|
+
/** The HTTP tool `capabilityId`/`toolName` of the agent `agentId` (the root when absent). */
|
|
50
|
+
export declare function declaredHttpTool(manifest: unknown, agentId: string | undefined, capabilityId: string | undefined, toolName: string | undefined): (ToolManifest & {
|
|
51
|
+
readonly http: HttpToolTarget;
|
|
52
|
+
}) | undefined;
|
|
53
|
+
/** The HTTP stage or HTTP verifier at stage key `stage` of a flow session's `manifest`. */
|
|
54
|
+
export declare function declaredFlowHttp(manifest: unknown, stage: string | undefined): import("@nylorun/core/define").FlowHttpTarget | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* True when `request` calls an HTTP tool the session's `manifest` declares, or runs one of a
|
|
57
|
+
* flow's HTTP stages or HTTP verifiers (a flow `tool` effect, named by its stage key).
|
|
58
|
+
*/
|
|
59
|
+
export declare function isHttpToolCall(manifest: unknown, request: HostEffect): boolean;
|
|
60
|
+
/** The ref a `tool` effect calls the Tool Gate with: its agent's HTTP tool, or its flow stage. */
|
|
61
|
+
export declare function httpToolRefOf(request: HostEffect): HttpToolRef;
|
|
62
|
+
/**
|
|
63
|
+
* Sends `input` to `target` and reads the answer. Never throws: everything that can happen to
|
|
64
|
+
* the request is an outcome. Rejects only when `signal` aborts, with its reason.
|
|
65
|
+
*/
|
|
66
|
+
export declare function callHttpTarget(target: HttpToolTarget, input: unknown, options: {
|
|
67
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
68
|
+
readonly policy: OutboundPolicy;
|
|
69
|
+
readonly signal: AbortSignal;
|
|
70
|
+
/** Checked against the answer; a mismatch is `tool.invalid-output`. */
|
|
71
|
+
readonly outputSchema?: JsonObject;
|
|
72
|
+
}): Promise<HttpOutcome>;
|
|
73
|
+
/**
|
|
74
|
+
* Runs one HTTP tool call of a session: finds the tool in the pinned manifest, adds its vault
|
|
75
|
+
* credential, the identity headers and the run-once key, and calls it. Never throws but for an
|
|
76
|
+
* abort of `signal`. Logs one line per call, never the input, the answer or a credential.
|
|
77
|
+
*/
|
|
78
|
+
export declare function runHttpTool(tenant: HttpToolTenant, call: HttpToolCall, options: {
|
|
79
|
+
readonly policy: OutboundPolicy;
|
|
80
|
+
readonly signal: AbortSignal;
|
|
81
|
+
readonly logger?: Logger;
|
|
82
|
+
}): Promise<HttpOutcome>;
|
|
83
|
+
/** The gates service's HTTP tool calls, with the Tenant's vault (`host/gates.ts`). */
|
|
84
|
+
export declare function gateHttpTools(vaults: TenantVaults, policy: OutboundPolicy, logger: Logger): {
|
|
85
|
+
call: (tenantId: string | undefined, call: HttpToolCall, signal: AbortSignal) => Promise<HttpOutcome>;
|
|
86
|
+
};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP tools (R2 M3): a tool the manifest describes as one HTTP request, made by the Tool Gate.
|
|
3
|
+
* The loop names the tool; the gate finds its URL, method and credential in the session's pinned
|
|
4
|
+
* manifest, as it finds a remote MCP server, so the loop never chooses a URL or a credential.
|
|
5
|
+
*
|
|
6
|
+
* `callHttpTarget` is the request and its answer, for any declared HTTP target: the input as a
|
|
7
|
+
* JSON body under the Host's address policy (`tenant/outbound.ts`), a 2xx answer as the output
|
|
8
|
+
* (JSON, or text when the answer is not JSON and no output schema asks for JSON), and every other
|
|
9
|
+
* answer, a timeout or a refused address as a failed outcome the model sees. `runHttpTool` adds
|
|
10
|
+
* what a tool call needs: the declaration, the vault credential and the identity headers.
|
|
11
|
+
*
|
|
12
|
+
* A flow's HTTP stages and HTTP verifiers (R2) are called the same way: the ref names the flow
|
|
13
|
+
* session and the stage key, and the gate finds the target in the pinned workflow manifest. The
|
|
14
|
+
* `Nylorun-Agent-Id` is the flow agent's.
|
|
15
|
+
*/
|
|
16
|
+
import { AGENT_ID_HEADER, SESSION_ID_HEADER, TURN_ID_HEADER, } from "@nylorun/core/compatibility";
|
|
17
|
+
import { HTTP_TOOL_DEFAULT_TIMEOUT_MS, delegateManifest, flowHttpTarget, isWorkflowManifest, schemaFromJSON, } from "@nylorun/core/define";
|
|
18
|
+
import { MAX_RESPONSE_BYTES, post } from "../tenant/outbound.js";
|
|
19
|
+
/** How much of a failed answer's body the model sees. */
|
|
20
|
+
export const HTTP_ERROR_BODY_CHARS = 2_000;
|
|
21
|
+
/** The HTTP tool `capabilityId`/`toolName` of the agent `agentId` (the root when absent). */
|
|
22
|
+
export function declaredHttpTool(manifest, agentId, capabilityId, toolName) {
|
|
23
|
+
const root = manifest;
|
|
24
|
+
if (!root || !Array.isArray(root.capabilities))
|
|
25
|
+
return undefined;
|
|
26
|
+
const agent = agentId === undefined ? root : delegateManifest(root, agentId);
|
|
27
|
+
const tool = agent?.capabilities
|
|
28
|
+
.find((capability) => capability.id === capabilityId)
|
|
29
|
+
?.tools?.find((item) => item.name === toolName);
|
|
30
|
+
return tool?.http ? tool : undefined;
|
|
31
|
+
}
|
|
32
|
+
/** The HTTP stage or HTTP verifier at stage key `stage` of a flow session's `manifest`. */
|
|
33
|
+
export function declaredFlowHttp(manifest, stage) {
|
|
34
|
+
return stage !== undefined && isWorkflowManifest(manifest)
|
|
35
|
+
? flowHttpTarget(manifest, stage)
|
|
36
|
+
: undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* True when `request` calls an HTTP tool the session's `manifest` declares, or runs one of a
|
|
40
|
+
* flow's HTTP stages or HTTP verifiers (a flow `tool` effect, named by its stage key).
|
|
41
|
+
*/
|
|
42
|
+
export function isHttpToolCall(manifest, request) {
|
|
43
|
+
if (request.kind !== "tool")
|
|
44
|
+
return false;
|
|
45
|
+
if (request.capabilityId === undefined)
|
|
46
|
+
return declaredFlowHttp(manifest, request.key) !== undefined;
|
|
47
|
+
return declaredHttpTool(manifest, request.agent?.id, request.capabilityId, request.toolName) !== undefined;
|
|
48
|
+
}
|
|
49
|
+
/** The ref a `tool` effect calls the Tool Gate with: its agent's HTTP tool, or its flow stage. */
|
|
50
|
+
export function httpToolRefOf(request) {
|
|
51
|
+
if (request.capabilityId === undefined)
|
|
52
|
+
return { sessionId: request.sessionId, stage: request.key };
|
|
53
|
+
return {
|
|
54
|
+
sessionId: request.sessionId,
|
|
55
|
+
...(request.agent ? { agentId: request.agent.id } : {}),
|
|
56
|
+
capabilityId: request.capabilityId,
|
|
57
|
+
toolName: request.toolName,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sends `input` to `target` and reads the answer. Never throws: everything that can happen to
|
|
62
|
+
* the request is an outcome. Rejects only when `signal` aborts, with its reason.
|
|
63
|
+
*/
|
|
64
|
+
export async function callHttpTarget(target, input, options) {
|
|
65
|
+
const timeoutMs = target.timeoutMs ?? HTTP_TOOL_DEFAULT_TIMEOUT_MS;
|
|
66
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
67
|
+
const result = await post(target.url, JSON.stringify(input ?? {}), { ...options.headers }, {
|
|
68
|
+
policy: options.policy,
|
|
69
|
+
method: target.method ?? "POST",
|
|
70
|
+
signal: AbortSignal.any([options.signal, timeout]),
|
|
71
|
+
});
|
|
72
|
+
if (options.signal.aborted)
|
|
73
|
+
throw options.signal.reason;
|
|
74
|
+
if (timeout.aborted)
|
|
75
|
+
return failed("http.timeout", `The service did not answer within ${timeoutMs} ms`);
|
|
76
|
+
switch (result.kind) {
|
|
77
|
+
case "not_sent":
|
|
78
|
+
return result.code === "ENDPOINT_ADDRESS_REFUSED"
|
|
79
|
+
? failed("http.refused", result.message)
|
|
80
|
+
: failed("http.unreachable", `The request was not sent: ${result.message}`);
|
|
81
|
+
case "lost":
|
|
82
|
+
return failed("http.lost", `The connection was lost after the request was sent, so the call may have run: ${result.message}`);
|
|
83
|
+
case "too_large":
|
|
84
|
+
return failed("http.too-large", `The answer (HTTP ${result.status}) is larger than ${MAX_RESPONSE_BYTES} bytes`);
|
|
85
|
+
}
|
|
86
|
+
const text = result.body.toString("utf8");
|
|
87
|
+
if (result.status < 200 || result.status > 299) {
|
|
88
|
+
const body = text.length > HTTP_ERROR_BODY_CHARS ? `${text.slice(0, HTTP_ERROR_BODY_CHARS)}…` : text;
|
|
89
|
+
return failed("http.status", `The service answered HTTP ${result.status}${body ? `: ${body}` : ""}`);
|
|
90
|
+
}
|
|
91
|
+
const type = String(result.headers["content-type"] ?? "");
|
|
92
|
+
let output;
|
|
93
|
+
if (text === "")
|
|
94
|
+
output = null;
|
|
95
|
+
else if (/[/+]json\b/i.test(type)) {
|
|
96
|
+
try {
|
|
97
|
+
output = JSON.parse(text);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return failed("http.invalid-response", "The answer says it is JSON but does not parse");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (options.outputSchema)
|
|
104
|
+
return failed("http.invalid-response", `The answer is ${type || "untyped"}, not JSON, and the tool's output schema needs JSON`);
|
|
105
|
+
else
|
|
106
|
+
output = text;
|
|
107
|
+
if (!options.outputSchema)
|
|
108
|
+
return { kind: "completed", output };
|
|
109
|
+
const checked = schemaFromJSON(options.outputSchema).validate(output);
|
|
110
|
+
if (!checked.ok)
|
|
111
|
+
return failed("tool.invalid-output", checked.issues.map((issue) => issue.message).join("; "));
|
|
112
|
+
return { kind: "completed", output: checked.value };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Runs one HTTP tool call of a session: finds the tool in the pinned manifest, adds its vault
|
|
116
|
+
* credential, the identity headers and the run-once key, and calls it. Never throws but for an
|
|
117
|
+
* abort of `signal`. Logs one line per call, never the input, the answer or a credential.
|
|
118
|
+
*/
|
|
119
|
+
export async function runHttpTool(tenant, call, options) {
|
|
120
|
+
const started = Date.now();
|
|
121
|
+
const { tool } = call;
|
|
122
|
+
let outcome;
|
|
123
|
+
try {
|
|
124
|
+
outcome = await run();
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (options.signal.aborted)
|
|
128
|
+
throw error;
|
|
129
|
+
outcome = failed("http.failed", error instanceof Error ? error.message : String(error));
|
|
130
|
+
}
|
|
131
|
+
options.logger?.info("http_tool_call", {
|
|
132
|
+
session: tool.sessionId,
|
|
133
|
+
...("stage" in tool
|
|
134
|
+
? { stage: tool.stage }
|
|
135
|
+
: {
|
|
136
|
+
capability: tool.capabilityId,
|
|
137
|
+
tool: tool.toolName,
|
|
138
|
+
...(tool.agentId === undefined ? {} : { agent: tool.agentId }),
|
|
139
|
+
}),
|
|
140
|
+
effect: call.effectId,
|
|
141
|
+
ms: Date.now() - started,
|
|
142
|
+
outcome: outcome.kind === "completed" ? "ok" : outcome.code,
|
|
143
|
+
});
|
|
144
|
+
return outcome;
|
|
145
|
+
async function run() {
|
|
146
|
+
const session = await tenant.session(tool.sessionId);
|
|
147
|
+
if (!session)
|
|
148
|
+
return failed("http.undeclared", `Session ${tool.sessionId} not found`);
|
|
149
|
+
const agentId = "stage" in tool ? undefined : tool.agentId;
|
|
150
|
+
const declared = "stage" in tool
|
|
151
|
+
? declaredFlowHttp(session.manifest, tool.stage)
|
|
152
|
+
: declaredHttpTool(session.manifest, agentId, tool.capabilityId, tool.toolName);
|
|
153
|
+
if (!declared)
|
|
154
|
+
return failed("http.undeclared", "stage" in tool
|
|
155
|
+
? `'${tool.stage}' is not an HTTP stage of the flow`
|
|
156
|
+
: `'${tool.toolName}' is not an HTTP tool of the session`);
|
|
157
|
+
const { http } = declared;
|
|
158
|
+
let credential = {};
|
|
159
|
+
if (http.credential !== undefined) {
|
|
160
|
+
const authorized = await tenant.authorize(tool.sessionId, {
|
|
161
|
+
kind: "http",
|
|
162
|
+
url: http.url,
|
|
163
|
+
serverName: http.credential,
|
|
164
|
+
...(agentId === undefined ? {} : { agentId }),
|
|
165
|
+
});
|
|
166
|
+
if (authorized.status === "refused")
|
|
167
|
+
return failed("http.credential", `The credential '${http.credential}' was refused: ${authorized.reason}`);
|
|
168
|
+
if (authorized.status === "unauthenticated")
|
|
169
|
+
return failed("http.credential", `The session's vaults hold no credential '${http.credential}' for ${authorized.url}`);
|
|
170
|
+
credential = authorized.headers;
|
|
171
|
+
}
|
|
172
|
+
return callHttpTarget(http, call.input, {
|
|
173
|
+
headers: {
|
|
174
|
+
...credential,
|
|
175
|
+
[SESSION_ID_HEADER]: tool.sessionId,
|
|
176
|
+
[TURN_ID_HEADER]: call.turnId,
|
|
177
|
+
[AGENT_ID_HEADER]: agentId ?? session.manifest.id,
|
|
178
|
+
"idempotency-key": call.effectId,
|
|
179
|
+
},
|
|
180
|
+
policy: options.policy,
|
|
181
|
+
signal: options.signal,
|
|
182
|
+
...(declared.outputSchema === undefined ? {} : { outputSchema: declared.outputSchema }),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** The gates service's HTTP tool calls, with the Tenant's vault (`host/gates.ts`). */
|
|
187
|
+
export function gateHttpTools(vaults, policy, logger) {
|
|
188
|
+
return {
|
|
189
|
+
call: (tenantId, call, signal) => runHttpTool({
|
|
190
|
+
session: async (sessionId) => (await vaults.open(tenantId)).session(sessionId),
|
|
191
|
+
authorize: async (sessionId, request) => (await vaults.open(tenantId)).authorizeMcp(sessionId, request),
|
|
192
|
+
}, call, { policy, signal, logger }),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function failed(code, message) {
|
|
196
|
+
return { kind: "failed", code, message };
|
|
197
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { openMcpServer, type McpToolPage } from "../mcp/connect.js";
|
|
2
2
|
import { type McpServerRef } from "../mcp/pool.js";
|
|
3
|
+
import type { OutboundPolicy } from "../tenant/outbound.js";
|
|
3
4
|
import type { Logger } from "../tenant/types.js";
|
|
4
5
|
import type { McpAnswer } from "./tool-contract.js";
|
|
5
6
|
import { type TenantVaults } from "./tenant-vaults.js";
|
|
@@ -13,6 +14,8 @@ export interface McpHandlerOptions {
|
|
|
13
14
|
readonly now?: () => number;
|
|
14
15
|
/** Tests replace how a server is opened. */
|
|
15
16
|
readonly open?: typeof openMcpServer;
|
|
17
|
+
/** How the gate reaches remote servers (`NYLORUN_ENDPOINT_*`). Default: no limits. */
|
|
18
|
+
readonly delivery?: OutboundPolicy;
|
|
16
19
|
}
|
|
17
20
|
export interface McpHandler {
|
|
18
21
|
connect(tenantId: string | undefined, server: McpServerRef): Promise<McpAnswer<null>>;
|
|
@@ -22,8 +22,6 @@ export function createMcpHandler(options) {
|
|
|
22
22
|
const found = findServer(session.manifest, server.agentId, server.capabilityId, server.serverName);
|
|
23
23
|
if (!found)
|
|
24
24
|
throw new NotDeclared(`MCP server '${server.serverName}' is not declared`);
|
|
25
|
-
if (found.server.type === "stdio")
|
|
26
|
-
throw new NotDeclared(`MCP server '${server.serverName}' is a stdio server, which runs beside the loop`);
|
|
27
25
|
return found.server;
|
|
28
26
|
}
|
|
29
27
|
async function connection(tenantId, server) {
|
|
@@ -38,8 +36,12 @@ export function createMcpHandler(options) {
|
|
|
38
36
|
const manifest = await declared(vault, server);
|
|
39
37
|
const connection = await open({
|
|
40
38
|
server: manifest,
|
|
41
|
-
authorize: (url) => vault.authorizeMcp(server.sessionId, {
|
|
42
|
-
|
|
39
|
+
authorize: (url) => vault.authorizeMcp(server.sessionId, {
|
|
40
|
+
url,
|
|
41
|
+
serverName: server.serverName,
|
|
42
|
+
...(server.agentId === undefined ? {} : { agentId: server.agentId }),
|
|
43
|
+
}),
|
|
44
|
+
policy: options.delivery ?? {},
|
|
43
45
|
});
|
|
44
46
|
const entry = { connection, lastUsedAt: now(), active: 0 };
|
|
45
47
|
live.set(key, entry);
|
package/dist/gates/meter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelBudgetRow,
|
|
1
|
+
import type { ModelBudgetRow, ModelUsageWrite, ModelUsageTotals, SessionStore } from "../store/types.js";
|
|
2
2
|
import type { Logger } from "../tenant/types.js";
|
|
3
3
|
import type { ModelGateOutcome, ModelGateRequest } from "./model-gate.js";
|
|
4
4
|
export interface Meter {
|
|
@@ -14,7 +14,7 @@ export interface MeterOptions {
|
|
|
14
14
|
}
|
|
15
15
|
export declare function createMeter(options: MeterOptions): Meter;
|
|
16
16
|
/** The ledger row of an outcome that carries usage; undefined for a failure. */
|
|
17
|
-
export declare function usageRow(request: ModelGateRequest, outcome: ModelGateOutcome, at: Date):
|
|
17
|
+
export declare function usageRow(request: ModelGateRequest, outcome: ModelGateOutcome, at: Date): ModelUsageWrite | undefined;
|
|
18
18
|
/**
|
|
19
19
|
* Why the budget refuses another call, or undefined. Calls in flight count at the scope's
|
|
20
20
|
* average cost per recorded call.
|
package/dist/gates/meter.js
CHANGED
|
@@ -107,6 +107,12 @@ export function usageRow(request, outcome, at) {
|
|
|
107
107
|
return {
|
|
108
108
|
id: randomUUID(),
|
|
109
109
|
effectKey: request.effectId,
|
|
110
|
+
tokensReported: typeof outcome.evidence?.extras?.tokensReported === "boolean"
|
|
111
|
+
? outcome.evidence.extras.tokensReported
|
|
112
|
+
: null,
|
|
113
|
+
costKnown: typeof outcome.evidence?.extras?.costKnown === "boolean"
|
|
114
|
+
? outcome.evidence.extras.costKnown
|
|
115
|
+
: null,
|
|
110
116
|
sessionId: request.sessionId,
|
|
111
117
|
turnId: request.turnId,
|
|
112
118
|
agentId: request.agentId,
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
import type { Credential } from "@earendil-works/pi-ai";
|
|
10
10
|
import type { ModelFailureOutcome } from "@nylorun/core/define";
|
|
11
11
|
import type { PostgresClient } from "../store/postgres/connect.js";
|
|
12
|
-
import {
|
|
12
|
+
import { type AuthorizeResult, type HostModelSecret } from "../vault/service.js";
|
|
13
|
+
import { CredentialSources, type McpCredentialRequest, type ResolverConfig } from "../vault/sources.js";
|
|
13
14
|
import type { SessionStore } from "../store/types.js";
|
|
14
15
|
import type { Session } from "../tenant/context.js";
|
|
15
16
|
import { type Keys } from "../keys/keys.js";
|
|
17
|
+
import { type OutboundPolicy } from "../tenant/outbound.js";
|
|
16
18
|
/** The Tenant, as a model call needs it. */
|
|
17
19
|
export interface TenantVault {
|
|
18
20
|
readonly tenantId: string;
|
|
@@ -23,16 +25,14 @@ export interface TenantVault {
|
|
|
23
25
|
readHostModel(): Promise<HostModelSecret | undefined>;
|
|
24
26
|
/** Writes back a credential pi-ai refreshed (OAuth). */
|
|
25
27
|
writeHostCredential(credential: Credential): Promise<void>;
|
|
26
|
-
/** A session, for the remote MCP servers its pinned manifest declares
|
|
28
|
+
/** A session, for the remote MCP servers and HTTP tools its pinned manifest declares. */
|
|
27
29
|
session(sessionId: string): Promise<Session | undefined>;
|
|
28
30
|
/**
|
|
29
|
-
* The
|
|
30
|
-
* from the session's attached vaults, refreshed when due (F4.1)
|
|
31
|
+
* The authorization of one request to a session's remote MCP server or HTTP tool: its
|
|
32
|
+
* credential from the session's attached vaults, refreshed when due (F4.1), else from the
|
|
33
|
+
* operator's credential resolver (F9 C1).
|
|
31
34
|
*/
|
|
32
|
-
authorizeMcp(sessionId: string, request:
|
|
33
|
-
url: string;
|
|
34
|
-
serverName: string;
|
|
35
|
-
}): Promise<AuthorizeResult>;
|
|
35
|
+
authorizeMcp(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
36
36
|
/** Vault writes and token signing with the Tenant's vault key (the keys service, F4.2). */
|
|
37
37
|
keys(): Keys;
|
|
38
38
|
}
|
|
@@ -52,13 +52,18 @@ export interface TenantVaultsOptions {
|
|
|
52
52
|
readonly sql: PostgresClient;
|
|
53
53
|
/** The Host root; the gate reads `keys/vault-kek` and `tenant/home` under it. */
|
|
54
54
|
readonly hostRoot: string;
|
|
55
|
+
/** The operator's credential resolver (`NYLORUN_RESOLVER_*`, F9 C1). */
|
|
56
|
+
readonly resolver?: ResolverConfig;
|
|
57
|
+
/**
|
|
58
|
+
* The Host's address policy (`NYLORUN_ENDPOINT_*`): OAuth refresh and MCP OAuth connect call
|
|
59
|
+
* authorization servers through `guardedFetch` under it (F9 C2).
|
|
60
|
+
*/
|
|
61
|
+
readonly delivery?: OutboundPolicy;
|
|
55
62
|
}
|
|
56
63
|
export declare function createTenantVaults(options: TenantVaultsOptions): TenantVaults;
|
|
57
64
|
/**
|
|
58
|
-
* A request to a session's remote MCP server, authorized from the session's attached vaults
|
|
59
|
-
* what the loop
|
|
65
|
+
* A request to a session's remote MCP server, authorized from the session's attached vaults,
|
|
66
|
+
* else the operator's credential resolver: what the loop does in its own process
|
|
67
|
+
* (`tenant/effects.ts` `authorize`). The owner and turn come from the session row.
|
|
60
68
|
*/
|
|
61
|
-
export declare function authorizeSessionMcp(
|
|
62
|
-
url: string;
|
|
63
|
-
serverName: string;
|
|
64
|
-
}): Promise<AuthorizeResult>;
|
|
69
|
+
export declare function authorizeSessionMcp(sources: CredentialSources, session: (sessionId: string) => Promise<Session | undefined>, sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
@@ -8,8 +8,10 @@ import { tenantPaths } from "../tenant/paths.js";
|
|
|
8
8
|
import { readVaultKek } from "../vault/kek.js";
|
|
9
9
|
import { HostModelVault } from "../vault/host-model.js";
|
|
10
10
|
import { VaultService } from "../vault/service.js";
|
|
11
|
+
import { CredentialSources } from "../vault/sources.js";
|
|
11
12
|
import { inProcessKeys } from "../keys/keys.js";
|
|
12
13
|
import { SigningKeys } from "../tenant/signing-keys.js";
|
|
14
|
+
import { guardedFetch } from "../tenant/outbound.js";
|
|
13
15
|
/** A call the gate refuses before the provider is called; it answers with `outcome`. */
|
|
14
16
|
export class GateRefusal extends Error {
|
|
15
17
|
outcome;
|
|
@@ -33,7 +35,15 @@ export function createTenantVaults(options) {
|
|
|
33
35
|
return kek;
|
|
34
36
|
};
|
|
35
37
|
const vault = new HostModelVault({ store, kek: readKek });
|
|
36
|
-
const credentials = new VaultService({
|
|
38
|
+
const credentials = new VaultService({
|
|
39
|
+
store,
|
|
40
|
+
kek: readKek,
|
|
41
|
+
fetch: guardedFetch(options.delivery ?? {}),
|
|
42
|
+
});
|
|
43
|
+
const sources = new CredentialSources({
|
|
44
|
+
vault: credentials,
|
|
45
|
+
...(options.resolver ? { resolver: options.resolver } : {}),
|
|
46
|
+
});
|
|
37
47
|
const keys = inProcessKeys({
|
|
38
48
|
store,
|
|
39
49
|
vault: credentials,
|
|
@@ -48,7 +58,7 @@ export function createTenantVaults(options) {
|
|
|
48
58
|
readHostModel: () => vault.readHostModel(),
|
|
49
59
|
writeHostCredential: (credential) => vault.updateHostCredential(credential),
|
|
50
60
|
session,
|
|
51
|
-
authorizeMcp: (sessionId, request) => authorizeSessionMcp(
|
|
61
|
+
authorizeMcp: (sessionId, request) => authorizeSessionMcp(sources, session, sessionId, request),
|
|
52
62
|
keys: () => keys,
|
|
53
63
|
};
|
|
54
64
|
}
|
|
@@ -77,18 +87,13 @@ export function createTenantVaults(options) {
|
|
|
77
87
|
};
|
|
78
88
|
}
|
|
79
89
|
/**
|
|
80
|
-
* A request to a session's remote MCP server, authorized from the session's attached vaults
|
|
81
|
-
* what the loop
|
|
90
|
+
* A request to a session's remote MCP server, authorized from the session's attached vaults,
|
|
91
|
+
* else the operator's credential resolver: what the loop does in its own process
|
|
92
|
+
* (`tenant/effects.ts` `authorize`). The owner and turn come from the session row.
|
|
82
93
|
*/
|
|
83
|
-
export async function authorizeSessionMcp(
|
|
94
|
+
export async function authorizeSessionMcp(sources, session, sessionId, request) {
|
|
84
95
|
const found = await session(sessionId);
|
|
85
96
|
if (!found)
|
|
86
97
|
throw new Error(`Session ${sessionId} not found`);
|
|
87
|
-
return
|
|
88
|
-
sessionId,
|
|
89
|
-
vaultIds: found.vaultIds ?? [],
|
|
90
|
-
credentialSelections: found.credentialSelections ?? [],
|
|
91
|
-
url: request.url,
|
|
92
|
-
serverName: request.serverName,
|
|
93
|
-
});
|
|
98
|
+
return sources.authorize({ ...found, id: sessionId }, request);
|
|
94
99
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Keyed
|
|
3
|
-
* (the loop's effect id) runs once:
|
|
2
|
+
* Keyed tool calls in the gates service (F4.1 G3): remote MCP calls and HTTP tool calls (R2 M3).
|
|
3
|
+
* A call sent with an `Idempotency-Key` (the loop's effect id) runs once:
|
|
4
4
|
*
|
|
5
5
|
* - While it runs, and for 30 minutes after, a re-send joins it or gets its answer from memory
|
|
6
6
|
* (`inflight.ts`), as model calls do (P1.2). So a runtime that dies or shuts down mid-call
|
|
@@ -13,28 +13,21 @@
|
|
|
13
13
|
* A cancel aborts the call and leaves its row without an answer. Settled rows are deleted a day
|
|
14
14
|
* later (`prune`).
|
|
15
15
|
*/
|
|
16
|
-
import type { McpServerRef } from "../mcp/pool.js";
|
|
17
16
|
import type { Logger } from "../tenant/types.js";
|
|
18
17
|
import { type InflightCallsOptions, type InflightOwner } from "./inflight.js";
|
|
19
|
-
import type { McpHandler } from "./mcp-handler.js";
|
|
20
18
|
import type { TenantVaults } from "./tenant-vaults.js";
|
|
21
19
|
import type { McpAnswer } from "./tool-contract.js";
|
|
22
20
|
/** How long a settled crossing is kept. */
|
|
23
21
|
export declare const TOOL_CROSSING_TTL_MS: number;
|
|
22
|
+
/** A tool call's answer: an MCP `CallToolResult`, or an HTTP tool's outcome. */
|
|
24
23
|
export type ToolCallAnswer = McpAnswer<Record<string, unknown>>;
|
|
25
|
-
export interface ToolCallRequest {
|
|
26
|
-
readonly server: McpServerRef;
|
|
27
|
-
readonly effectId: string;
|
|
28
|
-
readonly name: string;
|
|
29
|
-
readonly arguments: Record<string, unknown>;
|
|
30
|
-
}
|
|
31
24
|
export interface ToolCalls {
|
|
32
25
|
/**
|
|
33
|
-
* Runs
|
|
34
|
-
* different request already ran under it, and
|
|
35
|
-
* run that sends it (F5).
|
|
26
|
+
* Runs `call` under `key`, joins the call running under it, or answers it from its crossing.
|
|
27
|
+
* Rejects with `InflightConflict` when a different request (`hash`) already ran under it, and
|
|
28
|
+
* as `InflightCalls.run` does for `owner`, the run that sends it (F5).
|
|
36
29
|
*/
|
|
37
|
-
run(tenantId: string | undefined, key: string, hash: string,
|
|
30
|
+
run(tenantId: string | undefined, key: string, hash: string, call: (signal: AbortSignal) => Promise<ToolCallAnswer>, owner?: InflightOwner): Promise<ToolCallAnswer>;
|
|
38
31
|
/**
|
|
39
32
|
* Aborts the call under `key`; it is never run again under that key. With `sessionId`, false
|
|
40
33
|
* when the running call is another session's (nothing is aborted).
|
|
@@ -47,7 +40,6 @@ export interface ToolCalls {
|
|
|
47
40
|
}
|
|
48
41
|
export interface ToolCallsOptions {
|
|
49
42
|
readonly vaults: TenantVaults;
|
|
50
|
-
readonly mcp: McpHandler;
|
|
51
43
|
readonly logger: Logger;
|
|
52
44
|
readonly inflight?: InflightCallsOptions;
|
|
53
45
|
readonly now?: () => number;
|
package/dist/gates/tool-calls.js
CHANGED
|
@@ -5,16 +5,16 @@ const LOST = {
|
|
|
5
5
|
ok: false,
|
|
6
6
|
error: {
|
|
7
7
|
uncertain: true,
|
|
8
|
-
message: "The gateway stopped while this
|
|
8
|
+
message: "The gateway stopped while this tool call ran; the call may have reached its server, so it is not run again",
|
|
9
9
|
},
|
|
10
10
|
};
|
|
11
11
|
export function createToolCalls(options) {
|
|
12
|
-
const { vaults,
|
|
12
|
+
const { vaults, logger } = options;
|
|
13
13
|
const now = options.now ?? Date.now;
|
|
14
14
|
const inflight = createInflightCalls(options.inflight);
|
|
15
15
|
const iso = () => new Date(now()).toISOString();
|
|
16
16
|
return {
|
|
17
|
-
async run(tenantId, key, hash,
|
|
17
|
+
async run(tenantId, key, hash, call, owner) {
|
|
18
18
|
if (inflight.has(key))
|
|
19
19
|
return inflight.run(key, hash, () => Promise.resolve(LOST), owner);
|
|
20
20
|
const vault = await vaults.open(tenantId);
|
|
@@ -35,7 +35,7 @@ export function createToolCalls(options) {
|
|
|
35
35
|
// Another gateway process wrote it between the read and now: never run it twice.
|
|
36
36
|
if (!started)
|
|
37
37
|
return LOST;
|
|
38
|
-
const answer = await
|
|
38
|
+
const answer = await call(signal);
|
|
39
39
|
// A cancel or a shutdown: the call may have reached the server; the row stays open.
|
|
40
40
|
if (signal.aborted)
|
|
41
41
|
throw signal.reason ?? new Error("aborted");
|
|
@@ -4,7 +4,7 @@ export interface HttpToolGateOptions {
|
|
|
4
4
|
/** The gates service, e.g. `http://gateway:4100` (`NYLORUN_GATES_URL`). */
|
|
5
5
|
readonly url: string;
|
|
6
6
|
/**
|
|
7
|
-
* `NYLORUN_GATES_TOKEN`: core's credential, for
|
|
7
|
+
* `NYLORUN_GATES_TOKEN`: core's credential, for MCP requests outside a run. A
|
|
8
8
|
* harness has none (F6.2): its requests outside a run carry the session's last run token, or
|
|
9
9
|
* nothing, and the gate refuses them.
|
|
10
10
|
*/
|