@nylorun/runtime 0.17.0-beta → 0.19.1-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 +155 -0
- package/README.md +89 -58
- 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/routes.js +1 -1
- 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 +4871 -2614
- package/dist/api/components.js +12 -11
- 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 +18 -14
- package/dist/api/http/define.d.ts +14 -2
- package/dist/api/http/define.js +34 -15
- package/dist/api/http/routes/access.d.ts +3 -3
- package/dist/api/http/routes/access.js +18 -17
- package/dist/api/http/routes/artifacts.js +2 -4
- 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.js +5 -2
- 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.js +7 -4
- package/dist/api/http/routes/sessions.js +25 -8
- package/dist/api/http/routes/tenant.js +15 -18
- package/dist/api/http/routes/vaults.d.ts +10 -9
- package/dist/api/http/routes/vaults.js +27 -23
- package/dist/api/openapi.d.ts +10 -11
- package/dist/api/openapi.js +288 -63
- package/dist/artifacts/links.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 +1 -3
- package/dist/gates/meter.d.ts +2 -2
- package/dist/gates/meter.js +6 -0
- package/dist/gates/tenant-vaults.d.ts +5 -9
- 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 -9
- package/dist/host/app.js +15 -25
- package/dist/host/config-for.d.ts +1 -5
- package/dist/host/config-for.js +0 -3
- package/dist/host/config.d.ts +0 -5
- package/dist/host/create-host.d.ts +3 -33
- package/dist/host/create-host.js +18 -94
- 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 +3 -2
- 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 +27 -34
- package/dist/host/operate.d.ts +29 -0
- package/dist/host/operate.js +223 -0
- package/dist/host/stack-config.d.ts +12 -12
- package/dist/host/stack-config.js +5 -32
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/infra/workers.d.ts +1 -1
- package/dist/keys/keys.js +2 -2
- package/dist/management-openapi.json +6491 -0
- package/dist/mcp/connect.d.ts +8 -4
- package/dist/mcp/connect.js +6 -26
- package/dist/mcp/pool.d.ts +5 -9
- package/dist/mcp/pool.js +8 -19
- 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 +9372 -17030
- 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/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 +28 -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 +294 -489
- package/dist/store/postgres/schema.js +41 -76
- package/dist/store/postgres/store.js +51 -154
- package/dist/store/postgres/tenant.d.ts +34 -2
- package/dist/store/postgres/tenant.js +16 -32
- package/dist/store/types.d.ts +27 -96
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +0 -2
- package/dist/streams/control.js +0 -5
- package/dist/streams/types.d.ts +1 -10
- package/dist/tenant/advance.d.ts +3 -3
- package/dist/tenant/advance.js +10 -7
- package/dist/tenant/auth.d.ts +14 -9
- package/dist/tenant/auth.js +24 -28
- 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 +35 -172
- package/dist/tenant/context.d.ts +14 -24
- package/dist/tenant/definition-files.d.ts +24 -0
- package/dist/tenant/definition-files.js +103 -0
- package/dist/tenant/effects.d.ts +18 -20
- package/dist/tenant/effects.js +56 -119
- package/dist/tenant/ephemeral.d.ts +7 -6
- package/dist/tenant/ephemeral.js +3 -11
- package/dist/tenant/host-token.js +1 -1
- package/dist/tenant/jwt.js +1 -1
- package/dist/tenant/module.d.ts +1 -4
- package/dist/tenant/module.js +3 -12
- package/dist/tenant/operator-keys.d.ts +34 -8
- package/dist/tenant/operator-keys.js +50 -18
- package/dist/tenant/outbound.d.ts +10 -1
- package/dist/tenant/outbound.js +44 -27
- package/dist/tenant/paths.d.ts +1 -1
- package/dist/tenant/paths.js +1 -3
- package/dist/tenant/principals.d.ts +11 -2
- package/dist/tenant/principals.js +27 -8
- package/dist/tenant/run-token.js +3 -3
- package/dist/tenant/runtime.d.ts +4 -6
- package/dist/tenant/runtime.js +19 -18
- package/dist/tenant/sandboxes.d.ts +4 -1
- package/dist/tenant/sandboxes.js +11 -0
- package/dist/tenant/scheduler.d.ts +1 -5
- package/dist/tenant/scheduler.js +2 -5
- package/dist/tenant/session-streams.d.ts +2 -1
- package/dist/tenant/session-streams.js +39 -5
- package/dist/tenant/session.d.ts +8 -41
- package/dist/tenant/session.js +0 -37
- package/dist/tenant/sessions.js +16 -8
- package/dist/tenant/signing-keys.d.ts +3 -3
- 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 -10
- package/dist/tenant/streams.js +1 -24
- 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 +15 -22
- package/dist/tenant/worker.d.ts +1 -3
- package/dist/tenant/worker.js +3 -8
- package/dist/vault/sources.d.ts +7 -1
- package/dist/vault/sources.js +22 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/admin-openapi.json +0 -904
- package/dist/api/http/routes/actions.d.ts +0 -8
- package/dist/api/http/routes/actions.js +0 -75
- 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 -30
- package/dist/host/admin-api.js +0 -140
- package/dist/mcp/stdio.d.ts +0 -17
- package/dist/mcp/stdio.js +0 -82
- package/dist/tenant/actions.d.ts +0 -14
- package/dist/tenant/actions.js +0 -29
- 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
|
@@ -27,16 +27,7 @@
|
|
|
27
27
|
* becomes `uncertain` is settled in the Session Store and `advance` returns
|
|
28
28
|
* `done`.
|
|
29
29
|
* - **Sweeps.** `armSweep` arms one self-re-arming sweep per Tenant; arming an
|
|
30
|
-
* armed Tenant is a no-op. The sweep
|
|
31
|
-
* orphaned `runnable` sessions.
|
|
32
|
-
* - **One delivery per Action at a time.** `deliver` runs
|
|
33
|
-
* `WorkerHandlers.deliver` for the key `<tenantId>:<actionId>` at least once
|
|
34
|
-
* after it resolves. Two deliveries for one key never overlap; repeated
|
|
35
|
-
* `deliver` calls may each run it, so the handler reads the Action's state
|
|
36
|
-
* and does nothing when there is nothing to deliver.
|
|
37
|
-
* - **Retry after `retry`.** A delivery that returns `retry` is run again for
|
|
38
|
-
* the same key after `retryAfterMs`. This is how an Action endpoint that is
|
|
39
|
-
* down or busy is retried; the handler throws only on infrastructure errors.
|
|
30
|
+
* armed Tenant is a no-op. The sweep re-wakes orphaned `runnable` sessions.
|
|
40
31
|
* - **One reconcile per sandbox at a time** (F7.2). `sandbox` runs
|
|
41
32
|
* `WorkerHandlers.sandbox` for the key `<tenantId>:<sandboxId>` at least once
|
|
42
33
|
* after a `reconcile` resolves, never overlapping another run for the key. A
|
|
@@ -49,8 +40,6 @@
|
|
|
49
40
|
export type WakeReason =
|
|
50
41
|
/** Session commands. */
|
|
51
42
|
"message" | "approve" | "respond"
|
|
52
|
-
/** An Action's outcome was recorded (a delivery's answer or a background result). */
|
|
53
|
-
| "action_result"
|
|
54
43
|
/** A linked agent turn completed, failed or was cancelled. */
|
|
55
44
|
| "linked"
|
|
56
45
|
/** Queued flow effects, pending agent effects reconciled. */
|
|
@@ -98,14 +87,6 @@ export interface SandboxResult {
|
|
|
98
87
|
at: number;
|
|
99
88
|
}[];
|
|
100
89
|
}
|
|
101
|
-
export type DeliverResult = {
|
|
102
|
-
status: "done";
|
|
103
|
-
}
|
|
104
|
-
/** The Action is still to be delivered; run again for this key after `retryAfterMs`. */
|
|
105
|
-
| {
|
|
106
|
-
status: "retry";
|
|
107
|
-
retryAfterMs: number;
|
|
108
|
-
};
|
|
109
90
|
/** What a Worker runs when Durable Session Execution calls it. */
|
|
110
91
|
export interface WorkerHandlers {
|
|
111
92
|
/**
|
|
@@ -118,11 +99,6 @@ export interface WorkerHandlers {
|
|
|
118
99
|
sweep(tenantId: string): Promise<void>;
|
|
119
100
|
/** A durable timer set with `timer` fired. Required when `timer` is used. */
|
|
120
101
|
fire?(tenantId: string, key: string): Promise<void>;
|
|
121
|
-
/**
|
|
122
|
-
* Delivers one Action to its Action endpoint. `signal` aborts when the Worker
|
|
123
|
-
* stops. Throw only on infrastructure errors. Required when `deliver` is used.
|
|
124
|
-
*/
|
|
125
|
-
deliver?(tenantId: string, actionId: string, signal: AbortSignal): Promise<DeliverResult>;
|
|
126
102
|
/**
|
|
127
103
|
* Reconciles one pod sandbox (F7.2), serialized per sandbox. Throw only on infrastructure
|
|
128
104
|
* errors. Required when `sandbox` is used.
|
|
@@ -138,12 +114,6 @@ export interface DurableExecution {
|
|
|
138
114
|
* implementation can; `fire` must be idempotent either way.
|
|
139
115
|
*/
|
|
140
116
|
timer(tenantId: string, key: string, at: Date): Promise<void>;
|
|
141
|
-
/**
|
|
142
|
-
* At-least-once: `WorkerHandlers.deliver(tenantId, actionId)` runs after this
|
|
143
|
-
* resolves, never overlapping another delivery of the same Action. Send it
|
|
144
|
-
* after the transaction that made the Action pending commits.
|
|
145
|
-
*/
|
|
146
|
-
deliver(tenantId: string, actionId: string): Promise<void>;
|
|
147
117
|
/**
|
|
148
118
|
* A pod sandbox's reconcile, or one of its timers (F7.2): see "One reconcile per sandbox
|
|
149
119
|
* at a time". Absent where pods are not supported.
|
|
@@ -183,7 +153,7 @@ export interface StuckInvocation {
|
|
|
183
153
|
handler: string;
|
|
184
154
|
/**
|
|
185
155
|
* Object key: `<tenantId>:<sessionId>`, `<tenantId>`, `<tenantId>:<timer key>` or
|
|
186
|
-
* `<tenantId>:<
|
|
156
|
+
* `<tenantId>:<sandboxId>`.
|
|
187
157
|
*/
|
|
188
158
|
key: string;
|
|
189
159
|
tenantId?: string;
|
|
@@ -191,7 +161,7 @@ export interface StuckInvocation {
|
|
|
191
161
|
lastFailure?: string;
|
|
192
162
|
modifiedAt?: string;
|
|
193
163
|
}
|
|
194
|
-
/** The key one advance (or one
|
|
164
|
+
/** The key one advance (or one sandbox reconcile, with a sandbox id) at a time is serialized on. */
|
|
195
165
|
export declare function sessionKey(tenantId: string, sessionId: string): string;
|
|
196
166
|
/** Splits a key made by `sessionKey`. Tenant ids never contain `:`. */
|
|
197
167
|
export declare function parseSessionKey(key: string): {
|
package/dist/execution/types.js
CHANGED
|
@@ -27,16 +27,7 @@
|
|
|
27
27
|
* becomes `uncertain` is settled in the Session Store and `advance` returns
|
|
28
28
|
* `done`.
|
|
29
29
|
* - **Sweeps.** `armSweep` arms one self-re-arming sweep per Tenant; arming an
|
|
30
|
-
* armed Tenant is a no-op. The sweep
|
|
31
|
-
* orphaned `runnable` sessions.
|
|
32
|
-
* - **One delivery per Action at a time.** `deliver` runs
|
|
33
|
-
* `WorkerHandlers.deliver` for the key `<tenantId>:<actionId>` at least once
|
|
34
|
-
* after it resolves. Two deliveries for one key never overlap; repeated
|
|
35
|
-
* `deliver` calls may each run it, so the handler reads the Action's state
|
|
36
|
-
* and does nothing when there is nothing to deliver.
|
|
37
|
-
* - **Retry after `retry`.** A delivery that returns `retry` is run again for
|
|
38
|
-
* the same key after `retryAfterMs`. This is how an Action endpoint that is
|
|
39
|
-
* down or busy is retried; the handler throws only on infrastructure errors.
|
|
30
|
+
* armed Tenant is a no-op. The sweep re-wakes orphaned `runnable` sessions.
|
|
40
31
|
* - **One reconcile per sandbox at a time** (F7.2). `sandbox` runs
|
|
41
32
|
* `WorkerHandlers.sandbox` for the key `<tenantId>:<sandboxId>` at least once
|
|
42
33
|
* after a `reconcile` resolves, never overlapping another run for the key. A
|
|
@@ -49,13 +40,12 @@ export const WAKE_REASONS = [
|
|
|
49
40
|
"message",
|
|
50
41
|
"approve",
|
|
51
42
|
"respond",
|
|
52
|
-
"action_result",
|
|
53
43
|
"linked",
|
|
54
44
|
"flow",
|
|
55
45
|
"recover",
|
|
56
46
|
"rollover",
|
|
57
47
|
];
|
|
58
|
-
/** The key one advance (or one
|
|
48
|
+
/** The key one advance (or one sandbox reconcile, with a sandbox id) at a time is serialized on. */
|
|
59
49
|
export function sessionKey(tenantId, sessionId) {
|
|
60
50
|
return `${tenantId}:${sessionId}`;
|
|
61
51
|
}
|
|
@@ -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) {
|
|
@@ -43,7 +41,7 @@ export function createMcpHandler(options) {
|
|
|
43
41
|
serverName: server.serverName,
|
|
44
42
|
...(server.agentId === undefined ? {} : { agentId: server.agentId }),
|
|
45
43
|
}),
|
|
46
|
-
|
|
44
|
+
policy: options.delivery ?? {},
|
|
47
45
|
});
|
|
48
46
|
const entry = { connection, lastUsedAt: now(), active: 0 };
|
|
49
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,
|
|
@@ -25,18 +25,14 @@ export interface TenantVault {
|
|
|
25
25
|
readHostModel(): Promise<HostModelSecret | undefined>;
|
|
26
26
|
/** Writes back a credential pi-ai refreshed (OAuth). */
|
|
27
27
|
writeHostCredential(credential: Credential): Promise<void>;
|
|
28
|
-
/** 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. */
|
|
29
29
|
session(sessionId: string): Promise<Session | undefined>;
|
|
30
30
|
/**
|
|
31
|
-
* The authorization of one request to a session's remote MCP server
|
|
32
|
-
* session's attached vaults, refreshed when due (F4.1), else from the
|
|
33
|
-
* resolver (F9 C1).
|
|
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).
|
|
34
34
|
*/
|
|
35
|
-
authorizeMcp(sessionId: string, request:
|
|
36
|
-
url: string;
|
|
37
|
-
serverName: string;
|
|
38
|
-
agentId?: string;
|
|
39
|
-
}): Promise<AuthorizeResult>;
|
|
35
|
+
authorizeMcp(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
40
36
|
/** Vault writes and token signing with the Tenant's vault key (the keys service, F4.2). */
|
|
41
37
|
keys(): Keys;
|
|
42
38
|
}
|
|
@@ -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
|
*/
|