@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,141 @@
|
|
|
1
|
+
/** Process-local executor callback delivery and redelivery. */
|
|
2
|
+
import { getLogger } from "../logger.js";
|
|
3
|
+
import { postWithRetries, RetryablePostError } from "./http_retry.js";
|
|
4
|
+
const logger = getLogger("agent_engine_runner_shared.server.callback_delivery");
|
|
5
|
+
const CALLBACK_POST_BASE_DELAY_MS = 100;
|
|
6
|
+
const CALLBACK_RETRY_MAX_DELAY_MS = 5_000;
|
|
7
|
+
function callbackKey(callback) {
|
|
8
|
+
if (callback.status !== "SUSPENDED") {
|
|
9
|
+
return terminalCallbackKey(callback.execution_id);
|
|
10
|
+
}
|
|
11
|
+
// Rolling-upgrade compatibility for OE versions that do not yet send a
|
|
12
|
+
// suspension generation. Remove this bounded legacy path once every
|
|
13
|
+
// supported OE consistently supplies suspend_generation.
|
|
14
|
+
if (callback.suspend_generation === undefined)
|
|
15
|
+
return null;
|
|
16
|
+
return JSON.stringify([
|
|
17
|
+
"suspension",
|
|
18
|
+
callback.execution_id,
|
|
19
|
+
callback.suspend_generation,
|
|
20
|
+
]);
|
|
21
|
+
}
|
|
22
|
+
function terminalCallbackKey(executionId) {
|
|
23
|
+
return JSON.stringify(["terminal", executionId]);
|
|
24
|
+
}
|
|
25
|
+
function waitForRetry(ms, signal) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
if (signal.aborted) {
|
|
28
|
+
resolve(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const onAbort = () => {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
resolve(false);
|
|
34
|
+
};
|
|
35
|
+
const timer = setTimeout(() => {
|
|
36
|
+
signal.removeEventListener("abort", onAbort);
|
|
37
|
+
resolve(true);
|
|
38
|
+
}, ms);
|
|
39
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export class CallbackDelivery {
|
|
43
|
+
pending = new Map();
|
|
44
|
+
tasks = new Map();
|
|
45
|
+
retryController = new AbortController();
|
|
46
|
+
async send(oeUrl, callback, ownerUrl, onOwnerFailure) {
|
|
47
|
+
const key = callbackKey(callback);
|
|
48
|
+
let pending = null;
|
|
49
|
+
if (key !== null) {
|
|
50
|
+
pending = this.reserve(key, oeUrl, callback);
|
|
51
|
+
if (pending === null)
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
await postWithRetries(`${oeUrl}/executor/callback`, callback, ownerUrl ? `${ownerUrl}/executor/callback` : null, onOwnerFailure, undefined, undefined, undefined, key === null
|
|
56
|
+
? () => !this.pending.has(terminalCallbackKey(callback.execution_id))
|
|
57
|
+
: undefined);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof RetryablePostError && key !== null) {
|
|
61
|
+
this.startRedelivery(key, callback.execution_id);
|
|
62
|
+
logger.warn({ err: error, execution_id: callback.execution_id }, "Retaining callback after transient delivery failure");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
logger.error({ err: error, execution_id: callback.execution_id }, key !== null
|
|
66
|
+
? "Callback failed with a non-retryable error"
|
|
67
|
+
: "Suspension callback failed; not retaining because suspend_generation is absent");
|
|
68
|
+
}
|
|
69
|
+
if (pending !== null && key !== null && this.pending.get(key) === pending) {
|
|
70
|
+
this.pending.delete(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async shutdown() {
|
|
74
|
+
this.retryController.abort();
|
|
75
|
+
await Promise.allSettled(this.tasks.values());
|
|
76
|
+
this.tasks.clear();
|
|
77
|
+
this.pending.clear();
|
|
78
|
+
}
|
|
79
|
+
async waitForTerminal(executionId) {
|
|
80
|
+
const key = terminalCallbackKey(executionId);
|
|
81
|
+
const task = this.tasks.get(key);
|
|
82
|
+
if (task !== undefined)
|
|
83
|
+
await task;
|
|
84
|
+
return !this.retryController.signal.aborted && !this.pending.has(key);
|
|
85
|
+
}
|
|
86
|
+
reserve(key, oeUrl, callback) {
|
|
87
|
+
const executionId = callback.execution_id;
|
|
88
|
+
if (this.retryController.signal.aborted) {
|
|
89
|
+
logger.error({ execution_id: executionId }, "Cannot retain callback during shutdown");
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const pending = { oeUrl, callback };
|
|
93
|
+
const existing = this.pending.get(key);
|
|
94
|
+
if (existing !== undefined) {
|
|
95
|
+
if (existing.oeUrl !== oeUrl ||
|
|
96
|
+
JSON.stringify(existing.callback) !== JSON.stringify(callback)) {
|
|
97
|
+
logger.error({ execution_id: executionId }, "Refusing to replace conflicting callback");
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
this.pending.set(key, pending);
|
|
102
|
+
return pending;
|
|
103
|
+
}
|
|
104
|
+
startRedelivery(key, executionId) {
|
|
105
|
+
if (!this.tasks.has(key)) {
|
|
106
|
+
const task = this.redeliver(key, executionId, this.retryController.signal).finally(() => {
|
|
107
|
+
this.tasks.delete(key);
|
|
108
|
+
});
|
|
109
|
+
this.tasks.set(key, task);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async redeliver(key, executionId, signal) {
|
|
113
|
+
let delayMs = CALLBACK_POST_BASE_DELAY_MS;
|
|
114
|
+
while (!signal.aborted) {
|
|
115
|
+
if (!(await waitForRetry(delayMs, signal)))
|
|
116
|
+
return;
|
|
117
|
+
const pending = this.pending.get(key);
|
|
118
|
+
if (pending === undefined)
|
|
119
|
+
return;
|
|
120
|
+
try {
|
|
121
|
+
await postWithRetries(`${pending.oeUrl}/executor/callback`, pending.callback, null, null, signal);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (signal.aborted)
|
|
125
|
+
return;
|
|
126
|
+
if (error instanceof RetryablePostError) {
|
|
127
|
+
delayMs = Math.min(delayMs * 2, CALLBACK_RETRY_MAX_DELAY_MS);
|
|
128
|
+
logger.warn({ err: error, execution_id: executionId }, "Callback redelivery remains pending");
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
logger.error({ err: error, execution_id: executionId }, "Dropping callback after a non-retryable redelivery error");
|
|
132
|
+
if (this.pending.get(key) === pending)
|
|
133
|
+
this.pending.delete(key);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (this.pending.get(key) === pending)
|
|
137
|
+
this.pending.delete(key);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-protocol constants for AER → OE stream chunks.
|
|
3
|
+
*
|
|
4
|
+
* Centralized so a typo in one site can't silently desync the consumer.
|
|
5
|
+
* Values are stable strings forwarded verbatim through OE to the API gateway
|
|
6
|
+
* and on to the UI; do not rename without coordinating across all three
|
|
7
|
+
* layers (and the Go-side StreamChunk model).
|
|
8
|
+
*/
|
|
9
|
+
export declare const TEXT = "text";
|
|
10
|
+
export declare const SUBAGENT_START = "subagent_start";
|
|
11
|
+
export declare const SUBAGENT_END = "subagent_end";
|
|
12
|
+
export declare const STEP = "step";
|
|
13
|
+
export declare const CUSTOM_EVENT = "custom_event";
|
|
14
|
+
export declare const DONE = "done";
|
|
15
|
+
export declare const ERROR = "error";
|
|
16
|
+
export declare const POLICY_DENIED_ERROR_CODE = "policy_denied";
|
|
17
|
+
/**
|
|
18
|
+
* Machine-readable discriminator set on `metadata.error_code` of an ERROR
|
|
19
|
+
* chunk/callback when a deadline expired rather than the work failing. Covers
|
|
20
|
+
* both flavours — one tool/LLM call overrunning, and the whole turn overrunning —
|
|
21
|
+
* because a consumer's response to either is the same: offer more time or a
|
|
22
|
+
* retry, not a bug report. Forwarded verbatim like the values above.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TIMEOUT_ERROR_CODE = "timeout";
|
|
25
|
+
/**
|
|
26
|
+
* Machine-readable discriminator set on `metadata.error_code` of an ERROR
|
|
27
|
+
* chunk/callback when the agent's LLM call failed after OE approval (provider
|
|
28
|
+
* 401/404, rate limit, etc.). `metadata.source` is `llm` so invoke
|
|
29
|
+
* attribution does not have to string-match the error prose.
|
|
30
|
+
*/
|
|
31
|
+
export declare const LLM_INVOCATION_ERROR_CODE = "llm_invocation_failed";
|
|
32
|
+
/** Owner attribution for LLM-provider failures. Must match the gateway invoke-owner source switch. */
|
|
33
|
+
export declare const LLM_INVOCATION_ERROR_SOURCE = "llm";
|
|
34
|
+
/**
|
|
35
|
+
* Machine-readable discriminator set on `metadata.error_code` when the LLM
|
|
36
|
+
* provider rejected the configured credential (HTTP 401/403). Unlike
|
|
37
|
+
* `llm_invocation_failed` this is deliberately stamped WITHOUT
|
|
38
|
+
* `metadata.source`: the fix is the customer's own project secret, and the
|
|
39
|
+
* gateway's owner switch maps the bare code to client-owned, whereas an
|
|
40
|
+
* `llm` source would attribute it to provider flakiness.
|
|
41
|
+
*/
|
|
42
|
+
export declare const LLM_CREDENTIAL_REJECTED_ERROR_CODE = "llm_credential_rejected";
|
|
43
|
+
/**
|
|
44
|
+
* Same carrier when a run fails because a tool call's classified external-API
|
|
45
|
+
* failure was an auth rejection (AUTH_FAILED): the rejected credential is one
|
|
46
|
+
* of the project's secrets for that tool, not the LLM key. Also stamped
|
|
47
|
+
* without `metadata.source` so the code drives client-owned attribution.
|
|
48
|
+
*/
|
|
49
|
+
export declare const TOOL_CREDENTIAL_REJECTED_ERROR_CODE = "tool_credential_rejected";
|
|
50
|
+
//# sourceMappingURL=chunk_types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk_types.d.ts","sourceRoot":"","sources":["../../src/server/chunk_types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,eAAO,MAAM,IAAI,SAAS,CAAC;AAG3B,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAC/C,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAG3C,eAAO,MAAM,IAAI,SAAS,CAAC;AAM3B,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAG3C,eAAO,MAAM,IAAI,SAAS,CAAC;AAC3B,eAAO,MAAM,KAAK,UAAU,CAAC;AAO7B,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,0BAA0B,CAAC;AAEjE,sGAAsG;AACtG,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,4BAA4B,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,mCAAmC,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-protocol constants for AER → OE stream chunks.
|
|
3
|
+
*
|
|
4
|
+
* Centralized so a typo in one site can't silently desync the consumer.
|
|
5
|
+
* Values are stable strings forwarded verbatim through OE to the API gateway
|
|
6
|
+
* and on to the UI; do not rename without coordinating across all three
|
|
7
|
+
* layers (and the Go-side StreamChunk model).
|
|
8
|
+
*/
|
|
9
|
+
// Token of agent output (post-<think> filter, per-source state).
|
|
10
|
+
export const TEXT = "text";
|
|
11
|
+
// Subagent lifecycle markers — emitted only when guardrails are disabled.
|
|
12
|
+
export const SUBAGENT_START = "subagent_start";
|
|
13
|
+
export const SUBAGENT_END = "subagent_end";
|
|
14
|
+
// Mid-execution progress update emitted by tool functions via emitStep().
|
|
15
|
+
export const STEP = "step";
|
|
16
|
+
// Output-parser frame: carries only the custom_event payload
|
|
17
|
+
// field, no platform data. Consumers gate on both this chunk_type and the
|
|
18
|
+
// field's presence — chunk_type is the explicit discriminator; presence guards
|
|
19
|
+
// against a parser payload of JSON null.
|
|
20
|
+
export const CUSTOM_EVENT = "custom_event";
|
|
21
|
+
// Stream terminators.
|
|
22
|
+
export const DONE = "done";
|
|
23
|
+
export const ERROR = "error";
|
|
24
|
+
// Machine-readable discriminator set on metadata.error_code of an ERROR
|
|
25
|
+
// chunk/callback when the OE policy engine denied the call (as opposed to a
|
|
26
|
+
// crash or timeout). Lets the OE/UI render a "blocked by policy" outcome
|
|
27
|
+
// distinctly instead of string-matching the error message. Forwarded verbatim
|
|
28
|
+
// like the chunk_type values above.
|
|
29
|
+
export const POLICY_DENIED_ERROR_CODE = "policy_denied";
|
|
30
|
+
/**
|
|
31
|
+
* Machine-readable discriminator set on `metadata.error_code` of an ERROR
|
|
32
|
+
* chunk/callback when a deadline expired rather than the work failing. Covers
|
|
33
|
+
* both flavours — one tool/LLM call overrunning, and the whole turn overrunning —
|
|
34
|
+
* because a consumer's response to either is the same: offer more time or a
|
|
35
|
+
* retry, not a bug report. Forwarded verbatim like the values above.
|
|
36
|
+
*/
|
|
37
|
+
export const TIMEOUT_ERROR_CODE = "timeout";
|
|
38
|
+
/**
|
|
39
|
+
* Machine-readable discriminator set on `metadata.error_code` of an ERROR
|
|
40
|
+
* chunk/callback when the agent's LLM call failed after OE approval (provider
|
|
41
|
+
* 401/404, rate limit, etc.). `metadata.source` is `llm` so invoke
|
|
42
|
+
* attribution does not have to string-match the error prose.
|
|
43
|
+
*/
|
|
44
|
+
export const LLM_INVOCATION_ERROR_CODE = "llm_invocation_failed";
|
|
45
|
+
/** Owner attribution for LLM-provider failures. Must match the gateway invoke-owner source switch. */
|
|
46
|
+
export const LLM_INVOCATION_ERROR_SOURCE = "llm";
|
|
47
|
+
/**
|
|
48
|
+
* Machine-readable discriminator set on `metadata.error_code` when the LLM
|
|
49
|
+
* provider rejected the configured credential (HTTP 401/403). Unlike
|
|
50
|
+
* `llm_invocation_failed` this is deliberately stamped WITHOUT
|
|
51
|
+
* `metadata.source`: the fix is the customer's own project secret, and the
|
|
52
|
+
* gateway's owner switch maps the bare code to client-owned, whereas an
|
|
53
|
+
* `llm` source would attribute it to provider flakiness.
|
|
54
|
+
*/
|
|
55
|
+
export const LLM_CREDENTIAL_REJECTED_ERROR_CODE = "llm_credential_rejected";
|
|
56
|
+
/**
|
|
57
|
+
* Same carrier when a run fails because a tool call's classified external-API
|
|
58
|
+
* failure was an auth rejection (AUTH_FAILED): the rejected credential is one
|
|
59
|
+
* of the project's secrets for that tool, not the LLM key. Also stamped
|
|
60
|
+
* without `metadata.source` so the code drives client-owned attribution.
|
|
61
|
+
*/
|
|
62
|
+
export const TOOL_CREDENTIAL_REJECTED_ERROR_CODE = "tool_credential_rejected";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS origin allowlist resolution for Runner SDK servers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Python's `agent_engine_runner_shared.server.cors`. Both runtimes are parallel
|
|
5
|
+
* implementations of the same server architecture, so this file and its
|
|
6
|
+
* Python twin must be changed together.
|
|
7
|
+
*
|
|
8
|
+
* The default is deny-all rather than `*`. Runner servers register
|
|
9
|
+
* credential-bearing routes (`/execute`, `/invoke_llm`, AER session reads),
|
|
10
|
+
* so a wildcard default meant any web page a developer visited could drive
|
|
11
|
+
* tool execution and read stored conversation history once `agentengine dev`
|
|
12
|
+
* published the ports locally.
|
|
13
|
+
*/
|
|
14
|
+
/** Result of resolving the configured origin allowlist. */
|
|
15
|
+
export interface CorsPolicy {
|
|
16
|
+
/**
|
|
17
|
+
* Value for `@fastify/cors`'s `origin` option. `false` disables
|
|
18
|
+
* cross-origin access entirely (the deny-all default).
|
|
19
|
+
*/
|
|
20
|
+
origin: false | string[] | "*";
|
|
21
|
+
/**
|
|
22
|
+
* Whether to send `Access-Control-Allow-Credentials`. Never true
|
|
23
|
+
* alongside a wildcard origin — that combination lets any site issue
|
|
24
|
+
* credentialed requests and read the responses.
|
|
25
|
+
*/
|
|
26
|
+
credentials: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalizes `scheme://host[:port]` into the form browsers put in the
|
|
30
|
+
* `Origin` header — lowercased scheme and host, default port dropped — so
|
|
31
|
+
* an operator-written `HTTPS://A.com:443` still matches `https://a.com`.
|
|
32
|
+
*
|
|
33
|
+
* Returns `null` for anything an `Origin` header never carries (a path,
|
|
34
|
+
* userinfo, a query, a non-http(s) scheme, wildcards inside the host), so
|
|
35
|
+
* the entry is reported at startup rather than silently never matching.
|
|
36
|
+
* Kept behaviourally identical to the Python twin's `_normalize_origin`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function normalizeOrigin(origin: string): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the CORS policy from a raw `CORS_ALLOWED_ORIGINS` value.
|
|
41
|
+
*
|
|
42
|
+
* Entries are trimmed before validation: an operator who writes
|
|
43
|
+
* `a.com, b.com` reasonably expects both to be honoured, and the previous
|
|
44
|
+
* behaviour of comparing the untrimmed `" b.com"` meant a configured
|
|
45
|
+
* origin silently never matched — an operator could believe they had
|
|
46
|
+
* restricted origins when they had not.
|
|
47
|
+
*
|
|
48
|
+
* Malformed entries are dropped and logged at warn level for the same
|
|
49
|
+
* reason: silent non-matching is the failure mode worth eliminating.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveCorsPolicy(raw: string | undefined): CorsPolicy;
|
|
52
|
+
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/server/cors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,2DAA2D;AAC3D,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,GAAG,CAAC;IAC/B;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4B7D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CA8CrE"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS origin allowlist resolution for Runner SDK servers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Python's `agent_engine_runner_shared.server.cors`. Both runtimes are parallel
|
|
5
|
+
* implementations of the same server architecture, so this file and its
|
|
6
|
+
* Python twin must be changed together.
|
|
7
|
+
*
|
|
8
|
+
* The default is deny-all rather than `*`. Runner servers register
|
|
9
|
+
* credential-bearing routes (`/execute`, `/invoke_llm`, AER session reads),
|
|
10
|
+
* so a wildcard default meant any web page a developer visited could drive
|
|
11
|
+
* tool execution and read stored conversation history once `agentengine dev`
|
|
12
|
+
* published the ports locally.
|
|
13
|
+
*/
|
|
14
|
+
import { getLogger } from "../logger.js";
|
|
15
|
+
const logger = getLogger("agent_engine_runner_shared.server.cors");
|
|
16
|
+
/** A DNS name, an IPv4 literal, or a bracketed IPv6 literal. */
|
|
17
|
+
const HOST_PATTERN = /^(?:[a-z0-9.-]+|\[[0-9a-f:.]+\])$/;
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes `scheme://host[:port]` into the form browsers put in the
|
|
20
|
+
* `Origin` header — lowercased scheme and host, default port dropped — so
|
|
21
|
+
* an operator-written `HTTPS://A.com:443` still matches `https://a.com`.
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` for anything an `Origin` header never carries (a path,
|
|
24
|
+
* userinfo, a query, a non-http(s) scheme, wildcards inside the host), so
|
|
25
|
+
* the entry is reported at startup rather than silently never matching.
|
|
26
|
+
* Kept behaviourally identical to the Python twin's `_normalize_origin`.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeOrigin(origin) {
|
|
29
|
+
// The literal `null` origin sandboxed iframes and `file://` documents send.
|
|
30
|
+
if (origin === "null") {
|
|
31
|
+
return "null";
|
|
32
|
+
}
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = new URL(origin);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// `new URL` normalizes an empty path to "/", so that is the only path an
|
|
44
|
+
// origin-shaped value may have.
|
|
45
|
+
if (parsed.pathname !== "/" && parsed.pathname !== "") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// WHATWG parsing permits `*` in a host, which no browser will ever send.
|
|
52
|
+
if (!HOST_PATTERN.test(parsed.hostname)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// `URL.origin` already lowercases and strips the scheme's default port.
|
|
56
|
+
return parsed.origin;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolves the CORS policy from a raw `CORS_ALLOWED_ORIGINS` value.
|
|
60
|
+
*
|
|
61
|
+
* Entries are trimmed before validation: an operator who writes
|
|
62
|
+
* `a.com, b.com` reasonably expects both to be honoured, and the previous
|
|
63
|
+
* behaviour of comparing the untrimmed `" b.com"` meant a configured
|
|
64
|
+
* origin silently never matched — an operator could believe they had
|
|
65
|
+
* restricted origins when they had not.
|
|
66
|
+
*
|
|
67
|
+
* Malformed entries are dropped and logged at warn level for the same
|
|
68
|
+
* reason: silent non-matching is the failure mode worth eliminating.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveCorsPolicy(raw) {
|
|
71
|
+
const entries = (raw ?? "")
|
|
72
|
+
.split(",")
|
|
73
|
+
.map((entry) => entry.trim())
|
|
74
|
+
.filter((entry) => entry.length > 0);
|
|
75
|
+
if (entries.length === 0) {
|
|
76
|
+
// Distinguished from the misconfiguration warnings below: an unset value
|
|
77
|
+
// used to mean "allow every origin", so a deployment that never set it is
|
|
78
|
+
// exactly the one whose cross-origin access this change removes.
|
|
79
|
+
logger.warn("CORS_ALLOWED_ORIGINS is unset — denying all cross-origin requests. " +
|
|
80
|
+
"Set it to a comma-separated list of origins to allow any.");
|
|
81
|
+
return { origin: false, credentials: false };
|
|
82
|
+
}
|
|
83
|
+
if (entries.includes("*")) {
|
|
84
|
+
// Explicit opt-in only, and never with credentials. Kept as an escape
|
|
85
|
+
// hatch for local experimentation; production origins must be listed.
|
|
86
|
+
logger.warn("CORS_ALLOWED_ORIGINS contains '*' — allowing all origins WITHOUT " +
|
|
87
|
+
"credentials. List explicit origins to enable credentialed requests.");
|
|
88
|
+
return { origin: "*", credentials: false };
|
|
89
|
+
}
|
|
90
|
+
const valid = [];
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
const normalized = normalizeOrigin(entry);
|
|
93
|
+
if (normalized !== null) {
|
|
94
|
+
if (!valid.includes(normalized)) {
|
|
95
|
+
valid.push(normalized);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
logger.warn(`Ignoring malformed CORS origin ${JSON.stringify(entry)} — expected ` +
|
|
100
|
+
`scheme://host[:port] with no trailing path.`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (valid.length === 0) {
|
|
104
|
+
return { origin: false, credentials: false };
|
|
105
|
+
}
|
|
106
|
+
return { origin: valid, credentials: true };
|
|
107
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution-scoped drain receiver for Runner SDK servers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Python's `agent_engine_runner_shared.server.drain`. Both runtimes are parallel
|
|
5
|
+
* implementations of the same server architecture, so this file and its
|
|
6
|
+
* Python twin must be changed together.
|
|
7
|
+
*
|
|
8
|
+
* When the OE durably cancels an execution it POSTs `/drain` to the Tool/AER
|
|
9
|
+
* runtime owning that execution. The runtime blocks new work for the
|
|
10
|
+
* execution, aborts tracked in-flight work where a signal channel exists,
|
|
11
|
+
* and records an idempotent final outcome the caller retrieves by re-sending
|
|
12
|
+
* the same request.
|
|
13
|
+
*
|
|
14
|
+
* Contract (pinned by the shared fixture in
|
|
15
|
+
* `client-libraries/test-fixtures/drain/contract.json`):
|
|
16
|
+
*
|
|
17
|
+
* - `POST /drain {request_id, execution_id, reason, deadline_at_ms, workspace_id}`
|
|
18
|
+
* — `workspace_id` is mandatory on workspace-scoped runtimes (`APP_ID`
|
|
19
|
+
* set) and must match it exactly.
|
|
20
|
+
* - `202 {"outcome": "accepted"}` while draining; `200` with the final
|
|
21
|
+
* outcome (`completed` / `timed_out` / `delivery_failed`) afterwards.
|
|
22
|
+
* - `deadline_at_ms` is absolute. The runtime never extends it.
|
|
23
|
+
* - `accepted` is HTTP-level acceptance only; `completed` is the only
|
|
24
|
+
* quiescence signal.
|
|
25
|
+
* - An execution this runtime never served is `delivery_failed` with
|
|
26
|
+
* `reason_code=execution_not_found` — exact OE targeting does not prove a
|
|
27
|
+
* false `completed` safe, since one execution can span AER and Tool
|
|
28
|
+
* runtimes independently.
|
|
29
|
+
*
|
|
30
|
+
* TS-vs-Python divergence, matching the existing boundary tests: Python can
|
|
31
|
+
* cancel coroutine tasks outright; a promise has no cancellation, so "notify
|
|
32
|
+
* active work" here means aborting an AbortController. That reaches work
|
|
33
|
+
* only where a signal channel already exists (AER execution context, LLM
|
|
34
|
+
* streams). Plain tool functions receive no signal — they are tracked and
|
|
35
|
+
* admission-blocked, and outliving the deadline is an honest `timed_out`.
|
|
36
|
+
*
|
|
37
|
+
* State is process-local by design: a restarted runtime has lost its active
|
|
38
|
+
* work and answers `delivery_failed`; reconciliation across restarts belongs
|
|
39
|
+
* to the caller's durable record, not to this registry.
|
|
40
|
+
*/
|
|
41
|
+
import { z } from "zod";
|
|
42
|
+
import type { FastifyInstance } from "fastify";
|
|
43
|
+
export declare const DrainRequestSchema: z.ZodObject<{
|
|
44
|
+
request_id: z.ZodString;
|
|
45
|
+
execution_id: z.ZodString;
|
|
46
|
+
reason: z.ZodEnum<{
|
|
47
|
+
execution_cancelled: "execution_cancelled";
|
|
48
|
+
rolling_restart: "rolling_restart";
|
|
49
|
+
}>;
|
|
50
|
+
deadline_at_ms: z.ZodNumber;
|
|
51
|
+
workspace_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
export type DrainRequest = z.infer<typeof DrainRequestSchema>;
|
|
54
|
+
export type DrainOutcome = "accepted" | "completed" | "timed_out" | "delivery_failed";
|
|
55
|
+
export type DrainReasonCode = "execution_not_found" | "deadline_exceeded" | "execution_draining";
|
|
56
|
+
export interface DrainVerdict {
|
|
57
|
+
status: number;
|
|
58
|
+
body: {
|
|
59
|
+
outcome: DrainOutcome;
|
|
60
|
+
reason_code?: DrainReasonCode;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Outcome of one per-call abort (POST /interrupt/call). Unlike a drain, an
|
|
65
|
+
* interrupt is surgical: the execution is never admission-latched and later
|
|
66
|
+
* calls of the same execution proceed. `not_cancellable` is the honest
|
|
67
|
+
* answer for tracked work with no signal channel (a plain tool function) —
|
|
68
|
+
* never a claimed abandonment the runtime cannot perform.
|
|
69
|
+
*/
|
|
70
|
+
export type CallInterruptOutcome = "interrupted" | "not_found" | "already_settled" | "not_cancellable";
|
|
71
|
+
/**
|
|
72
|
+
* `AbortController.abort()` reason used by the per-call abort, distinct from
|
|
73
|
+
* DRAIN_ABORT_REASON so error paths can tell the two apart.
|
|
74
|
+
*/
|
|
75
|
+
export declare const CALL_INTERRUPT_REASON = "call_interrupted";
|
|
76
|
+
/**
|
|
77
|
+
* `AbortController.abort()` reason used by the drain, so error paths can tell
|
|
78
|
+
* "stopped by cancellation" apart from the execution timeout, which shares
|
|
79
|
+
* the same controller in the AER.
|
|
80
|
+
*/
|
|
81
|
+
export declare const DRAIN_ABORT_REASON = "execution_drained";
|
|
82
|
+
/**
|
|
83
|
+
* Execution-scoped admission + active-work registry for one runtime process.
|
|
84
|
+
*
|
|
85
|
+
* Mutating methods are synchronous, so check-and-set is atomic on Node's
|
|
86
|
+
* single thread. The registry is created per server; per-process state only.
|
|
87
|
+
*/
|
|
88
|
+
export declare class DrainRegistry {
|
|
89
|
+
private readonly recordTtlMs;
|
|
90
|
+
private readonly maxDeadlineMsValue;
|
|
91
|
+
private readonly entries;
|
|
92
|
+
/**
|
|
93
|
+
* Aborts that arrived before their call registered, keyed by execution and
|
|
94
|
+
* step, valued by expiry. The window is the OE-claim → in-graph-dispatch
|
|
95
|
+
* handoff; the retention covers graph scheduling delays, not call lifetimes.
|
|
96
|
+
*/
|
|
97
|
+
private readonly preAborted;
|
|
98
|
+
constructor(opts?: {
|
|
99
|
+
recordTtlMs?: number;
|
|
100
|
+
maxDeadlineMs?: number;
|
|
101
|
+
env?: NodeJS.ProcessEnv;
|
|
102
|
+
});
|
|
103
|
+
get maxDeadlineMs(): number;
|
|
104
|
+
/** Reject work for a draining execution without registering it. */
|
|
105
|
+
checkAdmission(executionId: string): void;
|
|
106
|
+
/**
|
|
107
|
+
* Register in-flight work for an execution, rejecting drained ones.
|
|
108
|
+
*
|
|
109
|
+
* `controller` is the abort handle the drain fires; omit it for work the
|
|
110
|
+
* runtime can only wait out (e.g. a tool function with no signal channel).
|
|
111
|
+
* `stepNumber` makes the work addressable by the per-call abort
|
|
112
|
+
* (`abortCall`); work registered without it is only ever drained
|
|
113
|
+
* execution-wide.
|
|
114
|
+
*
|
|
115
|
+
* Returns true when a per-call abort preceded this registration (the Stop
|
|
116
|
+
* landed in the OE-claim → in-graph-dispatch handoff): the caller must not
|
|
117
|
+
* start the body and must settle the call interrupted.
|
|
118
|
+
*/
|
|
119
|
+
beginWork(executionId: string, controller?: AbortController, stepNumber?: number): boolean;
|
|
120
|
+
private preAbortKey;
|
|
121
|
+
private popPreAborted;
|
|
122
|
+
private sweepPreAborted;
|
|
123
|
+
/**
|
|
124
|
+
* Signal exactly one in-flight call, leaving the execution open — the
|
|
125
|
+
* surgical sibling of a drain: no admission latch, no record, no deadline,
|
|
126
|
+
* and later steps of the same execution proceed. Idempotent: a repeat
|
|
127
|
+
* abort of the same step re-reads the same outcome.
|
|
128
|
+
*/
|
|
129
|
+
abortCall(executionId: string, stepNumber: number): CallInterruptOutcome;
|
|
130
|
+
/**
|
|
131
|
+
* Close a call's interruptibility as its result report begins: the body
|
|
132
|
+
* already produced its outcome, so a late abort must read already_settled
|
|
133
|
+
* rather than claim a stop the durable record will contradict.
|
|
134
|
+
*/
|
|
135
|
+
claimSettlement(executionId: string, stepNumber: number): void;
|
|
136
|
+
/**
|
|
137
|
+
* Attach an abort handle to already-registered work. The AER builds its
|
|
138
|
+
* execution-wide controller inside the handler, after admission — a drain
|
|
139
|
+
* that landed in between aborts it here, at attach time.
|
|
140
|
+
*/
|
|
141
|
+
attachController(executionId: string, controller: AbortController): void;
|
|
142
|
+
endWork(executionId: string, controller?: AbortController, stepNumber?: number): void;
|
|
143
|
+
/** Apply one drain request; idempotent by request_id, coalescing per execution. */
|
|
144
|
+
apply(request: DrainRequest): DrainVerdict;
|
|
145
|
+
private finalize;
|
|
146
|
+
private scheduleEviction;
|
|
147
|
+
private cancelEviction;
|
|
148
|
+
private evict;
|
|
149
|
+
private current;
|
|
150
|
+
private final;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Register `POST /drain` on a runner server.
|
|
154
|
+
*
|
|
155
|
+
* The bearer-auth hook already gates the path; this adds the
|
|
156
|
+
* execution/workspace-scoped validation on top. Callers of this endpoint
|
|
157
|
+
* must not follow redirects — the bearer token must never be forwarded to
|
|
158
|
+
* another host.
|
|
159
|
+
*
|
|
160
|
+
* Scope check: the bearer token authenticates the caller but does not
|
|
161
|
+
* establish that the named drain belongs to this runtime, so when the
|
|
162
|
+
* platform scopes this process to a workspace (`APP_ID` set — every managed
|
|
163
|
+
* runtime), the request must name that workspace exactly; a missing
|
|
164
|
+
* `workspace_id` is a 400, a mismatch a 403. When `APP_ID` is unset (local
|
|
165
|
+
* development against a single unscoped runtime) no workspace check is
|
|
166
|
+
* possible and none is enforced.
|
|
167
|
+
*/
|
|
168
|
+
export declare function registerDrainRoute(app: FastifyInstance, registry: DrainRegistry, env?: NodeJS.ProcessEnv): void;
|
|
169
|
+
//# sourceMappingURL=drain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drain.d.ts","sourceRoot":"","sources":["../../src/server/drain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAY/C,eAAO,MAAM,kBAAkB;;;;;;;;;iBAa7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,MAAM,MAAM,YAAY,GACpB,UAAU,GACV,WAAW,GACX,WAAW,GACX,iBAAiB,CAAC;AAEtB,MAAM,MAAM,eAAe,GACvB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,WAAW,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;CAChE;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAC5B,aAAa,GACb,WAAW,GACX,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAiCxD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AA8CtD;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;gBAGtD,IAAI,GAAE;QACJ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACpB;IAmBR,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,mEAAmE;IACnE,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKzC;;;;;;;;;;;;OAYG;IACH,SAAS,CACP,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,eAAe,EAC5B,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO;IAuCV,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,eAAe;IAQvB;;;;;OAKG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,oBAAoB;IA0BxE;;;;OAIG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAK9D;;;;OAIG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI;IASxE,OAAO,CACL,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,eAAe,EAC5B,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI;IAuBP,mFAAmF;IACnF,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY;YAkE5B,QAAQ;IA4BtB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,KAAK;CAYd;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,aAAa,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,IAAI,CAmBN"}
|