@mongodb-js/agent-engine-runner-shared 0.11.3
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 +55 -0
- package/LICENSE.md +201 -0
- package/README.md +29 -0
- package/dist/agent_config.d.ts +167 -0
- package/dist/agent_config.d.ts.map +1 -0
- package/dist/agent_config.js +544 -0
- package/dist/call_interrupted.d.ts +12 -0
- package/dist/call_interrupted.d.ts.map +1 -0
- package/dist/call_interrupted.js +11 -0
- package/dist/checkpoint_workspace.d.ts +25 -0
- package/dist/checkpoint_workspace.d.ts.map +1 -0
- package/dist/checkpoint_workspace.js +44 -0
- package/dist/context.d.ts +235 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +322 -0
- package/dist/db_config.d.ts +28 -0
- package/dist/db_config.d.ts.map +1 -0
- package/dist/db_config.js +66 -0
- package/dist/db_naming.d.ts +54 -0
- package/dist/db_naming.d.ts.map +1 -0
- package/dist/db_naming.js +94 -0
- package/dist/error_reporting.d.ts +67 -0
- package/dist/error_reporting.d.ts.map +1 -0
- package/dist/error_reporting.js +311 -0
- package/dist/generated/workflow/v1/activity_pb.d.ts +342 -0
- package/dist/generated/workflow/v1/activity_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/activity_pb.js +115 -0
- package/dist/generated/workflow/v1/common_pb.d.ts +184 -0
- package/dist/generated/workflow/v1/common_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/common_pb.js +86 -0
- package/dist/generated/workflow/v1/runtime_pb.d.ts +200 -0
- package/dist/generated/workflow/v1/runtime_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/runtime_pb.js +40 -0
- package/dist/generated/workflow/v1/state_pb.d.ts +254 -0
- package/dist/generated/workflow/v1/state_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/state_pb.js +68 -0
- package/dist/guardrails_evaluator/core.d.ts +23 -0
- package/dist/guardrails_evaluator/core.d.ts.map +1 -0
- package/dist/guardrails_evaluator/core.js +122 -0
- package/dist/guardrails_evaluator/index.d.ts +10 -0
- package/dist/guardrails_evaluator/index.d.ts.map +1 -0
- package/dist/guardrails_evaluator/index.js +11 -0
- package/dist/guardrails_evaluator/regex.d.ts +20 -0
- package/dist/guardrails_evaluator/regex.d.ts.map +1 -0
- package/dist/guardrails_evaluator/regex.js +233 -0
- package/dist/hooks.d.ts +109 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +216 -0
- package/dist/http_path.d.ts +18 -0
- package/dist/http_path.d.ts.map +1 -0
- package/dist/http_path.js +53 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/launcher.d.ts +130 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +325 -0
- package/dist/logger.d.ts +96 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +204 -0
- package/dist/mcp_oauth.d.ts +51 -0
- package/dist/mcp_oauth.d.ts.map +1 -0
- package/dist/mcp_oauth.js +389 -0
- package/dist/mcp_oauth_secret.d.ts +21 -0
- package/dist/mcp_oauth_secret.d.ts.map +1 -0
- package/dist/mcp_oauth_secret.js +122 -0
- package/dist/mcp_tools.d.ts +71 -0
- package/dist/mcp_tools.d.ts.map +1 -0
- package/dist/mcp_tools.js +301 -0
- package/dist/memory_appbound.d.ts +42 -0
- package/dist/memory_appbound.d.ts.map +1 -0
- package/dist/memory_appbound.js +159 -0
- package/dist/memory_writer.d.ts +49 -0
- package/dist/memory_writer.d.ts.map +1 -0
- package/dist/memory_writer.js +171 -0
- package/dist/metrics.d.ts +84 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +205 -0
- package/dist/models.d.ts +1458 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +1726 -0
- package/dist/node_logger.d.ts +43 -0
- package/dist/node_logger.d.ts.map +1 -0
- package/dist/node_logger.js +158 -0
- package/dist/owner_callback.d.ts +16 -0
- package/dist/owner_callback.d.ts.map +1 -0
- package/dist/owner_callback.js +40 -0
- package/dist/progress.d.ts +57 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +140 -0
- package/dist/runtime.d.ts +131 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +351 -0
- package/dist/secure_llm_proxy.d.ts +115 -0
- package/dist/secure_llm_proxy.d.ts.map +1 -0
- package/dist/secure_llm_proxy.js +922 -0
- package/dist/secure_wrapper.d.ts +332 -0
- package/dist/secure_wrapper.d.ts.map +1 -0
- package/dist/secure_wrapper.js +1249 -0
- package/dist/server/aer.d.ts +61 -0
- package/dist/server/aer.d.ts.map +1 -0
- package/dist/server/aer.js +1124 -0
- package/dist/server/auth.d.ts +56 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +132 -0
- package/dist/server/base.d.ts +104 -0
- package/dist/server/base.d.ts.map +1 -0
- package/dist/server/base.js +150 -0
- package/dist/server/callInterrupt.d.ts +49 -0
- package/dist/server/callInterrupt.d.ts.map +1 -0
- package/dist/server/callInterrupt.js +68 -0
- package/dist/server/callback_delivery.d.ts +14 -0
- package/dist/server/callback_delivery.d.ts.map +1 -0
- package/dist/server/callback_delivery.js +141 -0
- package/dist/server/chunk_types.d.ts +50 -0
- package/dist/server/chunk_types.d.ts.map +1 -0
- package/dist/server/chunk_types.js +62 -0
- package/dist/server/cors.d.ts +52 -0
- package/dist/server/cors.d.ts.map +1 -0
- package/dist/server/cors.js +107 -0
- package/dist/server/drain.d.ts +169 -0
- package/dist/server/drain.d.ts.map +1 -0
- package/dist/server/drain.js +455 -0
- package/dist/server/function.d.ts +77 -0
- package/dist/server/function.d.ts.map +1 -0
- package/dist/server/function.js +337 -0
- package/dist/server/http_retry.d.ts +37 -0
- package/dist/server/http_retry.d.ts.map +1 -0
- package/dist/server/http_retry.js +157 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +5 -0
- package/dist/server/metadata.d.ts +50 -0
- package/dist/server/metadata.d.ts.map +1 -0
- package/dist/server/metadata.js +193 -0
- package/dist/server/oe_url.d.ts +36 -0
- package/dist/server/oe_url.d.ts.map +1 -0
- package/dist/server/oe_url.js +50 -0
- package/dist/server/owner_url.d.ts +35 -0
- package/dist/server/owner_url.d.ts.map +1 -0
- package/dist/server/owner_url.js +146 -0
- package/dist/server/query.d.ts +42 -0
- package/dist/server/query.d.ts.map +1 -0
- package/dist/server/query.js +28 -0
- package/dist/server/tool.d.ts +138 -0
- package/dist/server/tool.d.ts.map +1 -0
- package/dist/server/tool.js +1017 -0
- package/dist/span_names.d.ts +21 -0
- package/dist/span_names.d.ts.map +1 -0
- package/dist/span_names.js +31 -0
- package/dist/structured_logging/constants.d.ts +17 -0
- package/dist/structured_logging/constants.d.ts.map +1 -0
- package/dist/structured_logging/constants.js +71 -0
- package/dist/structured_logging/env.d.ts +18 -0
- package/dist/structured_logging/env.d.ts.map +1 -0
- package/dist/structured_logging/env.js +39 -0
- package/dist/structured_logging/install.d.ts +56 -0
- package/dist/structured_logging/install.d.ts.map +1 -0
- package/dist/structured_logging/install.js +107 -0
- package/dist/structured_logging/layout.d.ts +9 -0
- package/dist/structured_logging/layout.d.ts.map +1 -0
- package/dist/structured_logging/layout.js +144 -0
- package/dist/structured_logging/serialize.d.ts +27 -0
- package/dist/structured_logging/serialize.d.ts.map +1 -0
- package/dist/structured_logging/serialize.js +61 -0
- package/dist/structured_logging/stdio_capture.d.ts +59 -0
- package/dist/structured_logging/stdio_capture.d.ts.map +1 -0
- package/dist/structured_logging/stdio_capture.js +164 -0
- package/dist/structured_logging/uncaught.d.ts +14 -0
- package/dist/structured_logging/uncaught.d.ts.map +1 -0
- package/dist/structured_logging/uncaught.js +58 -0
- package/dist/structured_logging.d.ts +48 -0
- package/dist/structured_logging.d.ts.map +1 -0
- package/dist/structured_logging.js +47 -0
- package/dist/tls_client.d.ts +61 -0
- package/dist/tls_client.d.ts.map +1 -0
- package/dist/tls_client.js +298 -0
- package/dist/tool_api_error.d.ts +62 -0
- package/dist/tool_api_error.d.ts.map +1 -0
- package/dist/tool_api_error.js +399 -0
- package/dist/tool_memory_ownership.d.ts +10 -0
- package/dist/tool_memory_ownership.d.ts.map +1 -0
- package/dist/tool_memory_ownership.js +36 -0
- package/dist/toolpod_handlers.d.ts +126 -0
- package/dist/toolpod_handlers.d.ts.map +1 -0
- package/dist/toolpod_handlers.js +1016 -0
- package/dist/tracing/exporters.d.ts +51 -0
- package/dist/tracing/exporters.d.ts.map +1 -0
- package/dist/tracing/exporters.js +327 -0
- package/dist/tracing/index.d.ts +3 -0
- package/dist/tracing/index.d.ts.map +1 -0
- package/dist/tracing/index.js +2 -0
- package/dist/tracing/setup.d.ts +76 -0
- package/dist/tracing/setup.d.ts.map +1 -0
- package/dist/tracing/setup.js +436 -0
- package/dist/utils.d.ts +204 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +867 -0
- package/dist/workflow/activity.d.ts +71 -0
- package/dist/workflow/activity.d.ts.map +1 -0
- package/dist/workflow/activity.js +357 -0
- package/dist/workflow/attempt.d.ts +12 -0
- package/dist/workflow/attempt.d.ts.map +1 -0
- package/dist/workflow/attempt.js +96 -0
- package/dist/workflow/client.d.ts +46 -0
- package/dist/workflow/client.d.ts.map +1 -0
- package/dist/workflow/client.js +299 -0
- package/dist/workflow/context.d.ts +37 -0
- package/dist/workflow/context.d.ts.map +1 -0
- package/dist/workflow/context.js +350 -0
- package/dist/workflow/heartbeat.d.ts +15 -0
- package/dist/workflow/heartbeat.d.ts.map +1 -0
- package/dist/workflow/heartbeat.js +78 -0
- package/dist/workflow/index.d.ts +14 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/memory.d.ts +17 -0
- package/dist/workflow/memory.d.ts.map +1 -0
- package/dist/workflow/memory.js +184 -0
- package/package.json +73 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function mode for the Runner SDK.
|
|
3
|
+
*
|
|
4
|
+
* Function mode runs a tool CLI-style: the process boots, runs a single named
|
|
5
|
+
* tool from the registry, reports the result to the OE, and exits. It is the
|
|
6
|
+
* per-call counterpart to the long-running tool server (mode `tool`).
|
|
7
|
+
*
|
|
8
|
+
* Mirrors Python's `agent_engine_runner_shared/server/function.py`.
|
|
9
|
+
*
|
|
10
|
+
* How the invocation arrives: fctr seeds `RunRequest.metadata` into the guest
|
|
11
|
+
* metadata directory before the workload starts. The OE sets one key,
|
|
12
|
+
* `request`, to a JSON `ToolFunctionRequest` envelope carrying both the
|
|
13
|
+
* `ToolPodExecuteRequest` and the tool-call `step` number (required by
|
|
14
|
+
* `ToolResultRequest`, owned by OE dispatch). The result is POSTed to
|
|
15
|
+
* `{oe_url}/tool/result`, the same endpoint the AER already uses.
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import os from "node:os";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { getLogger } from "../logger.js";
|
|
22
|
+
import { ownerDelivered } from "../owner_callback.js";
|
|
23
|
+
import { getCurrentExecutionMetadata, runWithCustomerOrigin, runWithExecutionContext, } from "../context.js";
|
|
24
|
+
import { ToolPodExecuteRequestSchema, } from "../models.js";
|
|
25
|
+
import { populateLlmRegistryFromEntrypoint } from "../hooks.js";
|
|
26
|
+
import { getRequestTimeout, logToolRequest, toolRedactFields, } from "../utils.js";
|
|
27
|
+
import { fetchPlatform, getFetchOptionsWithTLS } from "../tls_client.js";
|
|
28
|
+
import { executeErrorResponse, requestCredentialValues, } from "../tool_api_error.js";
|
|
29
|
+
import { getCurrentTraceContext } from "../tracing/setup.js";
|
|
30
|
+
import { discardResponseBody, retryAfterWaitMs } from "./http_retry.js";
|
|
31
|
+
import { resolveOeUrl } from "./oe_url.js";
|
|
32
|
+
import { resolveOwnerUrl } from "./owner_url.js";
|
|
33
|
+
const logger = getLogger("agent_engine_runner_shared.server.function");
|
|
34
|
+
// Guest metadata directory, mirroring fctr's util.MetadataDir. fctr
|
|
35
|
+
// materializes RunRequest.metadata into <dir>/<key> files before the
|
|
36
|
+
// workload starts. A fixed convention: the platform does not announce it.
|
|
37
|
+
const METADATA_DIR = "/run/meta";
|
|
38
|
+
const REQUEST_KEY = "request";
|
|
39
|
+
const TOOL_RESULT_PATH = "/tool/result";
|
|
40
|
+
const MAX_POST_ATTEMPTS = 3;
|
|
41
|
+
const POST_BASE_DELAY_MS = 100;
|
|
42
|
+
/** The function-mode invocation envelope delivered at /run/meta/request. */
|
|
43
|
+
export const ToolFunctionRequestSchema = z.object({
|
|
44
|
+
request: ToolPodExecuteRequestSchema,
|
|
45
|
+
step: z.number().int(),
|
|
46
|
+
});
|
|
47
|
+
function readMetadata(metadataDir, key) {
|
|
48
|
+
const filePath = path.join(metadataDir, key);
|
|
49
|
+
let value;
|
|
50
|
+
try {
|
|
51
|
+
value = fs.readFileSync(filePath, "utf-8");
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
if (e.code === "ENOENT") {
|
|
55
|
+
throw new Error(`function mode requires metadata key '${key}' at ${filePath}`, { cause: e });
|
|
56
|
+
}
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
59
|
+
if (!value.trim()) {
|
|
60
|
+
throw new Error(`function mode metadata key '${key}' at ${filePath} is empty`);
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
function readFunctionRequest(metadataDir) {
|
|
65
|
+
const raw = readMetadata(metadataDir, REQUEST_KEY);
|
|
66
|
+
let parsed;
|
|
67
|
+
try {
|
|
68
|
+
parsed = JSON.parse(raw);
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
throw new Error(`metadata key '${REQUEST_KEY}' is not a valid ToolFunctionRequest: ${e.message}`, { cause: e });
|
|
72
|
+
}
|
|
73
|
+
const result = ToolFunctionRequestSchema.safeParse(parsed);
|
|
74
|
+
if (!result.success) {
|
|
75
|
+
throw new Error(`metadata key '${REQUEST_KEY}' is not a valid ToolFunctionRequest: ${result.error.message}`);
|
|
76
|
+
}
|
|
77
|
+
return result.data;
|
|
78
|
+
}
|
|
79
|
+
function buildResultRequest(request, response, step, durationMs) {
|
|
80
|
+
const { traceId, spanId } = getCurrentTraceContext();
|
|
81
|
+
return {
|
|
82
|
+
execution_id: request.execution_id,
|
|
83
|
+
step_number: step,
|
|
84
|
+
tool_name: request.tool_name,
|
|
85
|
+
status: response.status,
|
|
86
|
+
result: response.result,
|
|
87
|
+
error: response.error,
|
|
88
|
+
duration_ms: durationMs,
|
|
89
|
+
pod_name: response.pod_name,
|
|
90
|
+
kind: response.kind,
|
|
91
|
+
metadata: response.metadata ?? {},
|
|
92
|
+
trace_id: traceId ?? undefined,
|
|
93
|
+
span_id: spanId ?? undefined,
|
|
94
|
+
tool_api_error: response.tool_api_error,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Encode the result to a JSON-safe object for the /tool/result POST.
|
|
99
|
+
*
|
|
100
|
+
* A tool may return a value the JSON encoder cannot handle (e.g. a circular
|
|
101
|
+
* reference). Downgrade any encode failure to an error report so the OE still
|
|
102
|
+
* receives a terminal signal. Mirrors Python's _result_payload.
|
|
103
|
+
*/
|
|
104
|
+
export function buildResultPayload(result, executionId) {
|
|
105
|
+
try {
|
|
106
|
+
JSON.stringify(result);
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
logger.error(`function mode: result for execution_id=${executionId} is not JSON-serializable: ${e.message}`);
|
|
111
|
+
return {
|
|
112
|
+
execution_id: result.execution_id,
|
|
113
|
+
step_number: result.step_number,
|
|
114
|
+
tool_name: result.tool_name,
|
|
115
|
+
status: "error",
|
|
116
|
+
result: undefined,
|
|
117
|
+
duration_ms: result.duration_ms,
|
|
118
|
+
pod_name: result.pod_name,
|
|
119
|
+
kind: result.kind,
|
|
120
|
+
metadata: {},
|
|
121
|
+
error: `tool result is not JSON-serializable: ${e.message}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
126
|
+
/**
|
|
127
|
+
* POST body to url with bounded exponential backoff.
|
|
128
|
+
*
|
|
129
|
+
* Retries on transient transport errors and 5xx responses. Does not retry on
|
|
130
|
+
* 4xx. Re-throws the final error on exhaustion. Mirrors Python's
|
|
131
|
+
* post_json_with_retries and the shared postWithRetries server helper.
|
|
132
|
+
*
|
|
133
|
+
* `ownerUrl` — when given (already validated against `url` and path-appended by
|
|
134
|
+
* the caller), a single owner pre-attempt runs *before* the service loop and
|
|
135
|
+
* does not consume the service retry budget (so the trusted `url` always gets
|
|
136
|
+
* its full `maxAttempts`; total tries are `maxAttempts + 1`). The owner is
|
|
137
|
+
* best-effort: any failure — a transport error OR any non-2xx response (4xx or
|
|
138
|
+
* 5xx) — marks the replica unusable, is logged, and falls through to the
|
|
139
|
+
* service loop. The owner is never retried and an owner response never throws.
|
|
140
|
+
* When the trusted `url` returns 503 with a delta-seconds `Retry-After`, that
|
|
141
|
+
* attempt waits the capped hint instead of the exponential backoff.
|
|
142
|
+
*/
|
|
143
|
+
async function postWithRetries(url, body, ownerUrl, onOwnerFailure, maxAttempts = MAX_POST_ATTEMPTS, baseDelayMs = POST_BASE_DELAY_MS) {
|
|
144
|
+
const timeoutMs = getRequestTimeout() * 1000;
|
|
145
|
+
const serialized = JSON.stringify(body);
|
|
146
|
+
// Owner pre-attempt per the shared owner-callback policy (owner_callback.ts):
|
|
147
|
+
// a single best-effort POST that does not consume the service retry budget.
|
|
148
|
+
if (ownerUrl != null) {
|
|
149
|
+
if (await ownerDelivered(ownerUrl, () => ({
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
body: serialized,
|
|
153
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
154
|
+
...getFetchOptionsWithTLS(ownerUrl),
|
|
155
|
+
}), (msg) => logger.warn(`function mode: /tool/result ${msg} URL ${url}`))) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
onOwnerFailure?.();
|
|
159
|
+
}
|
|
160
|
+
const tlsOptions = getFetchOptionsWithTLS(url);
|
|
161
|
+
let lastError = null;
|
|
162
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
163
|
+
let retryWaitMs = null;
|
|
164
|
+
let response;
|
|
165
|
+
try {
|
|
166
|
+
response = await fetchPlatform(url, {
|
|
167
|
+
method: "POST",
|
|
168
|
+
headers: { "Content-Type": "application/json" },
|
|
169
|
+
body: serialized,
|
|
170
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
171
|
+
...tlsOptions,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
lastError = e;
|
|
176
|
+
}
|
|
177
|
+
if (response) {
|
|
178
|
+
const ok = response.ok;
|
|
179
|
+
const status = response.status;
|
|
180
|
+
const retryAfter = response.headers?.get?.("Retry-After") ?? null;
|
|
181
|
+
await discardResponseBody(response);
|
|
182
|
+
if (ok)
|
|
183
|
+
return;
|
|
184
|
+
if (status < 500) {
|
|
185
|
+
throw new Error(`HTTP ${status} from ${url}`);
|
|
186
|
+
}
|
|
187
|
+
lastError = new Error(`HTTP ${status} from ${url}`);
|
|
188
|
+
if (status === 503) {
|
|
189
|
+
retryWaitMs = retryAfterWaitMs(retryAfter);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (attempt < maxAttempts - 1) {
|
|
193
|
+
await sleep(retryWaitMs ?? baseDelayMs * 2 ** attempt);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
throw (lastError ??
|
|
197
|
+
new Error(`postWithRetries exhausted ${maxAttempts} attempts but recorded no error`));
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Runs one metadata-delivered tool call and exits. Not a server.
|
|
201
|
+
*
|
|
202
|
+
* Reads the ToolFunctionRequest envelope from the guest metadata directory,
|
|
203
|
+
* invokes the named tool from the registry, reports the ToolResultRequest
|
|
204
|
+
* to {oe_url}/tool/result, and returns. Mirrors Python's ToolFunctionRunner.
|
|
205
|
+
*
|
|
206
|
+
* `metadataDir` is injectable for tests.
|
|
207
|
+
*/
|
|
208
|
+
export class ToolFunctionRunner {
|
|
209
|
+
runtime;
|
|
210
|
+
metadataDir;
|
|
211
|
+
constructor(runtime, { metadataDir = METADATA_DIR } = {}) {
|
|
212
|
+
this.runtime = runtime;
|
|
213
|
+
this.metadataDir = metadataDir;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Run one tool from the metadata-delivered request and report the result.
|
|
217
|
+
*
|
|
218
|
+
* Re-raises if the result POST cannot be delivered after retries so fctr
|
|
219
|
+
* records the execution as failed.
|
|
220
|
+
*/
|
|
221
|
+
async run() {
|
|
222
|
+
const functionRequest = readFunctionRequest(this.metadataDir);
|
|
223
|
+
const { request, step } = functionRequest;
|
|
224
|
+
// Pin the callback base to the runner's own configured OE (deploy-time
|
|
225
|
+
// OE_URL) before it is used as the callback base or the owner-URL trust
|
|
226
|
+
// anchor. The request field is only honoured when no OE_URL is stamped
|
|
227
|
+
// (local dev / tests). Resolving once here means the owner URL is validated
|
|
228
|
+
// against a trusted anchor, not the raw request value.
|
|
229
|
+
const oeUrl = resolveOeUrl(request.oe_url ?? "").replace(/\/+$/, "");
|
|
230
|
+
if (!oeUrl) {
|
|
231
|
+
throw new Error("function mode requires oe_url on the request to report the result");
|
|
232
|
+
}
|
|
233
|
+
// Prefer the replica-specific owner OE, validated against the trusted
|
|
234
|
+
// resolved oeUrl above. A forged owner URL validates to null and is ignored.
|
|
235
|
+
const ownerUrl = resolveOwnerUrl(request.oe_owner_url, oeUrl);
|
|
236
|
+
const ownerUrlFailure = { failed: false };
|
|
237
|
+
// Install before ready()/registry prep and keep through result delivery so
|
|
238
|
+
// startup and callback failures still emit platform_trace_id.
|
|
239
|
+
await runWithExecutionContext({
|
|
240
|
+
executionId: request.execution_id,
|
|
241
|
+
traceId: request.platform_trace_id,
|
|
242
|
+
wrapper: null,
|
|
243
|
+
oeUrl,
|
|
244
|
+
oeOwnerUrl: ownerUrl,
|
|
245
|
+
ownerUrlFailure,
|
|
246
|
+
userId: request.user_id,
|
|
247
|
+
sessionId: request.session_id,
|
|
248
|
+
authorization: request.authorization,
|
|
249
|
+
customHeaders: request.custom_headers,
|
|
250
|
+
payload: request.payload,
|
|
251
|
+
}, async () => {
|
|
252
|
+
await this.runtime.graphBuilder?.ready?.();
|
|
253
|
+
// Populate the named-LLM registry so a tool calling an LLM works,
|
|
254
|
+
// same as ToolServer.onStartup(). Not required if no LLMs are called,
|
|
255
|
+
// but matches the server-mode startup contract.
|
|
256
|
+
this._prepare();
|
|
257
|
+
logger.info(`function mode: invoking tool=${JSON.stringify(request.tool_name)} execution_id=${request.execution_id} step=${step}`);
|
|
258
|
+
const started = performance.now();
|
|
259
|
+
const response = await this._invokeToolFn(request, ownerUrlFailure);
|
|
260
|
+
const durationMs = performance.now() - started;
|
|
261
|
+
const payload = buildResultPayload(buildResultRequest(request, response, step, durationMs), request.execution_id);
|
|
262
|
+
try {
|
|
263
|
+
await postWithRetries(`${oeUrl}${TOOL_RESULT_PATH}`, payload, ownerUrl && !ownerUrlFailure.failed
|
|
264
|
+
? `${ownerUrl}${TOOL_RESULT_PATH}`
|
|
265
|
+
: null, () => {
|
|
266
|
+
ownerUrlFailure.failed = true;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
logger.error(`function mode: failed to deliver result for execution_id=${request.execution_id}: ${e.message}`);
|
|
271
|
+
throw e;
|
|
272
|
+
}
|
|
273
|
+
logger.info(`function mode: reported ${payload["status"]} result for execution_id=${request.execution_id} step=${step}`);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
_prepare() {
|
|
277
|
+
populateLlmRegistryFromEntrypoint(this.runtime.graphBuilder, logger, {
|
|
278
|
+
entrypointFailed: "Failed to run entrypoint for LLM registration; LLM calls from this tool will fail",
|
|
279
|
+
noLlmRegistered: "Entrypoint ran but did not call app.llm(); LLM calls from this tool will fail",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
async _invokeToolFn(request, ownerUrlFailure) {
|
|
283
|
+
const podName = os.hostname();
|
|
284
|
+
logToolRequest(request.tool_name ?? "(empty)", request.arguments, 0, "TOOL_FUNCTION", toolRedactFields(this.runtime.toolDefinitions, request.tool_name));
|
|
285
|
+
if (!request.tool_name) {
|
|
286
|
+
return {
|
|
287
|
+
status: "error",
|
|
288
|
+
error: "Tool name cannot be empty",
|
|
289
|
+
pod_name: podName,
|
|
290
|
+
metadata: {},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const func = this.runtime.tools[request.tool_name];
|
|
294
|
+
if (!func) {
|
|
295
|
+
return {
|
|
296
|
+
status: "error",
|
|
297
|
+
error: `Unknown tool: ${request.tool_name}`,
|
|
298
|
+
pod_name: podName,
|
|
299
|
+
metadata: {},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
// Pin the callback base to the trusted resolved OE before it anchors the
|
|
303
|
+
// execution context and the owner-URL validation (see run()).
|
|
304
|
+
const oeUrl = resolveOeUrl(request.oe_url ?? "");
|
|
305
|
+
return runWithExecutionContext({
|
|
306
|
+
executionId: request.execution_id,
|
|
307
|
+
traceId: request.platform_trace_id,
|
|
308
|
+
wrapper: null,
|
|
309
|
+
oeUrl,
|
|
310
|
+
// Emit() from the tool prefers this owner replica, validated against
|
|
311
|
+
// the trusted resolved oeUrl above.
|
|
312
|
+
oeOwnerUrl: resolveOwnerUrl(request.oe_owner_url, oeUrl),
|
|
313
|
+
ownerUrlFailure,
|
|
314
|
+
userId: request.user_id,
|
|
315
|
+
sessionId: request.session_id,
|
|
316
|
+
authorization: request.authorization,
|
|
317
|
+
customHeaders: request.custom_headers,
|
|
318
|
+
payload: request.payload,
|
|
319
|
+
}, async () => {
|
|
320
|
+
try {
|
|
321
|
+
const maybePromise = runWithCustomerOrigin(() => func(request.arguments));
|
|
322
|
+
const result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
|
|
323
|
+
const metadata = getCurrentExecutionMetadata();
|
|
324
|
+
return {
|
|
325
|
+
status: "success",
|
|
326
|
+
result,
|
|
327
|
+
pod_name: podName,
|
|
328
|
+
kind: metadata["memory"] ? "memory" : undefined,
|
|
329
|
+
metadata,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
return executeErrorResponse(e, this.runtime.toolDefinitions[request.tool_name], podName, getCurrentExecutionMetadata(), requestCredentialValues());
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared retry-policy and bounded POST helpers for OE transports.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Python's `agent_engine_runner_shared.server.http_retry`. The AER chunk/callback
|
|
5
|
+
* path (`server/aer.ts`), function mode (`server/function.ts`) and the tool
|
|
6
|
+
* result settlement loop (`secure_wrapper.ts`) share policy here so their
|
|
7
|
+
* cooperative back-pressure behaviour stays identical.
|
|
8
|
+
*/
|
|
9
|
+
export declare function waitForRetry(ms: number, signal: AbortSignal): Promise<boolean>;
|
|
10
|
+
export declare class RetryablePostError extends Error {
|
|
11
|
+
}
|
|
12
|
+
export declare const RETRY_AFTER_MAX_WAIT_MS = 10000;
|
|
13
|
+
/** Release an unread fetch body without changing callback delivery semantics. */
|
|
14
|
+
export declare function discardResponseBody(response: Response): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a delta-seconds `Retry-After` header; ignore the HTTP-date form.
|
|
17
|
+
*
|
|
18
|
+
* Returns the non-negative integer seconds, or `null` when the header is
|
|
19
|
+
* absent or not in delta-seconds form (an HTTP-date, or garbage). Callers fall
|
|
20
|
+
* back to their normal backoff when this returns `null`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseRetryAfterSeconds(headerValue: string | null | undefined): number | null;
|
|
23
|
+
/**
|
|
24
|
+
* The wait (in ms) an honored delta-seconds `Retry-After` implies, capped at
|
|
25
|
+
* {@link RETRY_AFTER_MAX_WAIT_MS}, or `null` when the header is not honorable.
|
|
26
|
+
*/
|
|
27
|
+
export declare function retryAfterWaitMs(headerValue: string | null | undefined): number | null;
|
|
28
|
+
/**
|
|
29
|
+
* POST to OE with one optional owner attempt and bounded service retries.
|
|
30
|
+
*
|
|
31
|
+
* Transport failures and 5xx responses are retried; 4xx responses fail
|
|
32
|
+
* immediately. An owner URL gets one best-effort pre-attempt without consuming
|
|
33
|
+
* the service retry budget. A 503 `Retry-After` delta overrides the normal
|
|
34
|
+
* exponential delay up to the configured cap.
|
|
35
|
+
*/
|
|
36
|
+
export declare function postWithRetries(url: string, body: unknown, ownerUrl?: string | null, onOwnerFailure?: (() => void) | null, cancellationSignal?: AbortSignal, maxAttempts?: number, baseDelayMs?: number, shouldAttempt?: () => boolean): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=http_retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http_retry.d.ts","sourceRoot":"","sources":["../../src/server/http_retry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAKhD,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C,iFAAiF;AACjF,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,MAAM,GAAG,IAAI,CAOf;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,MAAM,GAAG,IAAI,CAIf;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,cAAc,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,EACpC,kBAAkB,CAAC,EAAE,WAAW,EAChC,WAAW,SAAwB,EACnC,WAAW,SAA6B,EACxC,aAAa,CAAC,EAAE,MAAM,OAAO,GAC5B,OAAO,CAAC,IAAI,CAAC,CA6Df"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared retry-policy and bounded POST helpers for OE transports.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Python's `agent_engine_runner_shared.server.http_retry`. The AER chunk/callback
|
|
5
|
+
* path (`server/aer.ts`), function mode (`server/function.ts`) and the tool
|
|
6
|
+
* result settlement loop (`secure_wrapper.ts`) share policy here so their
|
|
7
|
+
* cooperative back-pressure behaviour stays identical.
|
|
8
|
+
*/
|
|
9
|
+
import { getLogger } from "../logger.js";
|
|
10
|
+
import { ownerDelivered as ownerPreAttemptDelivered } from "../owner_callback.js";
|
|
11
|
+
import { fetchPlatform, getFetchOptionsWithTLS } from "../tls_client.js";
|
|
12
|
+
import { getRequestTimeout } from "../utils.js";
|
|
13
|
+
const logger = getLogger("agent_engine_runner_shared.server.http_retry");
|
|
14
|
+
const REQUEST_TIMEOUT_MS = getRequestTimeout() * 1000;
|
|
15
|
+
const DEFAULT_POST_ATTEMPTS = 3;
|
|
16
|
+
const DEFAULT_POST_BASE_DELAY_MS = 100;
|
|
17
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
export function waitForRetry(ms, signal) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
if (signal.aborted) {
|
|
21
|
+
resolve(false);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const onAbort = () => {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
resolve(false);
|
|
27
|
+
};
|
|
28
|
+
const timer = setTimeout(() => {
|
|
29
|
+
signal.removeEventListener("abort", onAbort);
|
|
30
|
+
resolve(true);
|
|
31
|
+
}, ms);
|
|
32
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export class RetryablePostError extends Error {
|
|
36
|
+
}
|
|
37
|
+
// Ceiling on an honored `Retry-After` wait: a cooperative 503 hint is worth a
|
|
38
|
+
// short pause, but the runner must not park a callback for an OE-chosen
|
|
39
|
+
// duration.
|
|
40
|
+
export const RETRY_AFTER_MAX_WAIT_MS = 10_000;
|
|
41
|
+
/** Release an unread fetch body without changing callback delivery semantics. */
|
|
42
|
+
export async function discardResponseBody(response) {
|
|
43
|
+
try {
|
|
44
|
+
await response.body?.cancel();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// The response outcome is authoritative; cleanup failure must not replace it.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse a delta-seconds `Retry-After` header; ignore the HTTP-date form.
|
|
52
|
+
*
|
|
53
|
+
* Returns the non-negative integer seconds, or `null` when the header is
|
|
54
|
+
* absent or not in delta-seconds form (an HTTP-date, or garbage). Callers fall
|
|
55
|
+
* back to their normal backoff when this returns `null`.
|
|
56
|
+
*/
|
|
57
|
+
export function parseRetryAfterSeconds(headerValue) {
|
|
58
|
+
if (headerValue == null)
|
|
59
|
+
return null;
|
|
60
|
+
const value = headerValue.trim();
|
|
61
|
+
// Only ASCII digits (matches Python's `str.isdigit()`): no sign, decimal, or
|
|
62
|
+
// HTTP-date. An empty string fails, so a blank header falls through.
|
|
63
|
+
if (!/^\d+$/.test(value))
|
|
64
|
+
return null;
|
|
65
|
+
return Number(value);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The wait (in ms) an honored delta-seconds `Retry-After` implies, capped at
|
|
69
|
+
* {@link RETRY_AFTER_MAX_WAIT_MS}, or `null` when the header is not honorable.
|
|
70
|
+
*/
|
|
71
|
+
export function retryAfterWaitMs(headerValue) {
|
|
72
|
+
const seconds = parseRetryAfterSeconds(headerValue);
|
|
73
|
+
if (seconds === null)
|
|
74
|
+
return null;
|
|
75
|
+
return Math.min(seconds * 1000, RETRY_AFTER_MAX_WAIT_MS);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* POST to OE with one optional owner attempt and bounded service retries.
|
|
79
|
+
*
|
|
80
|
+
* Transport failures and 5xx responses are retried; 4xx responses fail
|
|
81
|
+
* immediately. An owner URL gets one best-effort pre-attempt without consuming
|
|
82
|
+
* the service retry budget. A 503 `Retry-After` delta overrides the normal
|
|
83
|
+
* exponential delay up to the configured cap.
|
|
84
|
+
*/
|
|
85
|
+
export async function postWithRetries(url, body, ownerUrl, onOwnerFailure, cancellationSignal, maxAttempts = DEFAULT_POST_ATTEMPTS, baseDelayMs = DEFAULT_POST_BASE_DELAY_MS, shouldAttempt) {
|
|
86
|
+
const serialized = JSON.stringify(body);
|
|
87
|
+
if (shouldAttempt?.() === false)
|
|
88
|
+
return;
|
|
89
|
+
if (ownerUrl != null) {
|
|
90
|
+
if (await ownerDelivered(ownerUrl, url, serialized))
|
|
91
|
+
return;
|
|
92
|
+
onOwnerFailure?.();
|
|
93
|
+
}
|
|
94
|
+
let lastError = null;
|
|
95
|
+
const tlsOptions = getFetchOptionsWithTLS(url);
|
|
96
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
97
|
+
if (shouldAttempt?.() === false)
|
|
98
|
+
return;
|
|
99
|
+
let retryWaitMs = null;
|
|
100
|
+
let response;
|
|
101
|
+
try {
|
|
102
|
+
const requestSignal = cancellationSignal
|
|
103
|
+
? AbortSignal.any([
|
|
104
|
+
cancellationSignal,
|
|
105
|
+
AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
106
|
+
])
|
|
107
|
+
: AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
108
|
+
response = await fetchPlatform(url, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/json" },
|
|
111
|
+
body: serialized,
|
|
112
|
+
signal: requestSignal,
|
|
113
|
+
...tlsOptions,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (cancellationSignal?.aborted)
|
|
118
|
+
throw error;
|
|
119
|
+
lastError = new RetryablePostError(error instanceof Error ? error.message : String(error));
|
|
120
|
+
}
|
|
121
|
+
if (response) {
|
|
122
|
+
const ok = response.ok;
|
|
123
|
+
const status = response.status;
|
|
124
|
+
const retryAfter = response.headers?.get?.("Retry-After") ?? null;
|
|
125
|
+
await discardResponseBody(response);
|
|
126
|
+
if (ok)
|
|
127
|
+
return;
|
|
128
|
+
if (status < 500)
|
|
129
|
+
throw new Error(`HTTP ${status} from ${url}`);
|
|
130
|
+
lastError = new RetryablePostError(`HTTP ${status} from ${url}`);
|
|
131
|
+
if (status === 503)
|
|
132
|
+
retryWaitMs = retryAfterWaitMs(retryAfter);
|
|
133
|
+
}
|
|
134
|
+
if (attempt < maxAttempts - 1) {
|
|
135
|
+
const delayMs = retryWaitMs ?? baseDelayMs * 2 ** attempt;
|
|
136
|
+
if (cancellationSignal) {
|
|
137
|
+
if (!(await waitForRetry(delayMs, cancellationSignal))) {
|
|
138
|
+
throw cancellationSignal.reason;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
await sleep(delayMs);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
throw (lastError ??
|
|
147
|
+
new Error(`postWithRetries exhausted ${maxAttempts} attempts but recorded no error`));
|
|
148
|
+
}
|
|
149
|
+
async function ownerDelivered(ownerUrl, serviceUrl, serialized) {
|
|
150
|
+
return ownerPreAttemptDelivered(ownerUrl, () => ({
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: { "Content-Type": "application/json" },
|
|
153
|
+
body: serialized,
|
|
154
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
155
|
+
...getFetchOptionsWithTLS(ownerUrl),
|
|
156
|
+
}), (message) => logger.warn(`${message} URL ${serviceUrl}`));
|
|
157
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as chunkTypes from "./chunk_types.js";
|
|
2
|
+
export * from "./base.js";
|
|
3
|
+
export { AERServer } from "./aer.js";
|
|
4
|
+
export { ToolServer, LLMRegistryLoadError } from "./tool.js";
|
|
5
|
+
export { ToolFunctionRunner, ToolFunctionRequestSchema, buildResultPayload, type ToolFunctionRequest, } from "./function.js";
|
|
6
|
+
export type { AERQueryPlugin } from "./query.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * as chunkTypes from "./chunk_types.js";
|
|
2
|
+
export * from "./base.js";
|
|
3
|
+
export { AERServer } from "./aer.js";
|
|
4
|
+
export { ToolServer, LLMRegistryLoadError } from "./tool.js";
|
|
5
|
+
export { ToolFunctionRunner, ToolFunctionRequestSchema, buildResultPayload, } from "./function.js";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read request-scoped secrets from the fctr metadata directory.
|
|
3
|
+
*
|
|
4
|
+
* In RPC mode the OE delivers per-tool secrets via `SetMetadata` before each
|
|
5
|
+
* tool call and clears them after (when restriction is enabled). fctr
|
|
6
|
+
* materializes the keyset as files at `/run/meta/<KEY>`. This module reads
|
|
7
|
+
* those files so the tool server can inject them into `process.env` for the
|
|
8
|
+
* duration of the call — or permanently when restriction is disabled.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors Python's `agent_engine_runner_shared.server.metadata`.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Read all key-value pairs from the metadata directory.
|
|
14
|
+
*
|
|
15
|
+
* Returns a record mapping filename (the secret name) to file content (the
|
|
16
|
+
* secret value). Skips files that cannot be read and logs a warning. Returns
|
|
17
|
+
* an empty record when the directory does not exist.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readMetadataSecrets(metadataDir?: string): Record<string, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Overwrite `process.env` from `/run/meta`; no restore or prune.
|
|
22
|
+
*/
|
|
23
|
+
export declare function mergeMetadataEnv(metadataDir?: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Run `fn` with metadata secrets merged into `process.env`, restoring the
|
|
26
|
+
* environment afterwards (even on throw).
|
|
27
|
+
*
|
|
28
|
+
* Mirrors Python's `applied_metadata_env` context manager for non-streaming
|
|
29
|
+
* call paths.
|
|
30
|
+
*
|
|
31
|
+
* @throws if this region overlaps a concurrent secret-bearing region; see
|
|
32
|
+
* {@link applyMetadataEnv} for the fail-closed rule and the clean-vs-tainted
|
|
33
|
+
* restore semantics.
|
|
34
|
+
*/
|
|
35
|
+
export declare function withMetadataEnv<T>(fn: () => Promise<T>, metadataDir?: string): Promise<T>;
|
|
36
|
+
/**
|
|
37
|
+
* Wrap an async generator so metadata secrets are present in `process.env`
|
|
38
|
+
* for the lifetime of the iteration, restoring the environment when iteration
|
|
39
|
+
* completes, throws, or is closed early.
|
|
40
|
+
*
|
|
41
|
+
* Mirrors Python's `applied_metadata_env` context manager for streaming call
|
|
42
|
+
* paths.
|
|
43
|
+
*
|
|
44
|
+
* @throws if this region overlaps a concurrent secret-bearing region — note a
|
|
45
|
+
* stream holds its region open for the whole iteration; see
|
|
46
|
+
* {@link applyMetadataEnv} for the fail-closed rule and the clean-vs-tainted
|
|
47
|
+
* restore semantics.
|
|
48
|
+
*/
|
|
49
|
+
export declare function withMetadataEnvGen<T>(genFactory: () => AsyncGenerator<T>, metadataDir?: string): AsyncGenerator<T>;
|
|
50
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/server/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAwBH;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,GAAE,MAAqB,GACjC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBxB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,GAAE,MAAqB,GAAG,IAAI,CAQzE;AAwFD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,GAAE,MAAqB,GACjC,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAuB,kBAAkB,CAAC,CAAC,EACzC,UAAU,EAAE,MAAM,cAAc,CAAC,CAAC,CAAC,EACnC,WAAW,GAAE,MAAqB,GACjC,cAAc,CAAC,CAAC,CAAC,CAOnB"}
|