@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
package/dist/models.js
ADDED
|
@@ -0,0 +1,1726 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared wire-protocol models for Runner SDK components.
|
|
3
|
+
*
|
|
4
|
+
* Defines the API contracts between:
|
|
5
|
+
* - Orchestration Engine (OE)
|
|
6
|
+
* - Agent Execution Runtime (AER)
|
|
7
|
+
* - Tool Executor Pod
|
|
8
|
+
*
|
|
9
|
+
* Each Pydantic model in `agent_engine_runner_shared/models.py` maps to:
|
|
10
|
+
* - a Zod schema (`FooSchema`) for runtime validation, and
|
|
11
|
+
* - the inferred TS type (`Foo = z.infer<typeof FooSchema>`).
|
|
12
|
+
*
|
|
13
|
+
* Models with non-trivial methods (`LLMResult`) follow the agent-engine-sdk
|
|
14
|
+
* pattern of class + separate schema.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { recordSuspendRequest } from "./context.js";
|
|
18
|
+
import { ToolAPIErrorSchema } from "./tool_api_error.js";
|
|
19
|
+
import { JsonValueSchema, LLMInvocationOptions, LLMInvocationOptionsSchema, LLMResponse, LLMResponseSchema, LLMTokenUsage, LLMTokenUsageSchema, LLMToolCall, LLMToolCallSchema, LLMToolSchema, LLMToolSchemaValidator, MessageSchema, serializeMessage, ToolCallChunkSchema, } from "@mongodb-js/agent-engine-sdk";
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Execution Status
|
|
22
|
+
// =============================================================================
|
|
23
|
+
/** Status of an agent execution. */
|
|
24
|
+
export const ExecutionStatusSchema = z.enum([
|
|
25
|
+
"pending",
|
|
26
|
+
"running",
|
|
27
|
+
"suspended",
|
|
28
|
+
"resuming",
|
|
29
|
+
"completed",
|
|
30
|
+
"error",
|
|
31
|
+
"cancelled",
|
|
32
|
+
]);
|
|
33
|
+
export const ExecutionStatus = {
|
|
34
|
+
PENDING: "pending",
|
|
35
|
+
RUNNING: "running",
|
|
36
|
+
SUSPENDED: "suspended",
|
|
37
|
+
RESUMING: "resuming",
|
|
38
|
+
COMPLETED: "completed",
|
|
39
|
+
ERROR: "error",
|
|
40
|
+
CANCELLED: "cancelled",
|
|
41
|
+
};
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Suspend Payload (Agent → Platform)
|
|
44
|
+
// =============================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Payload returned by an agent tool to trigger human-in-the-loop suspension.
|
|
47
|
+
*
|
|
48
|
+
* Agent tools signal a suspend by returning a JSON string containing these
|
|
49
|
+
* fields. The `__suspend__` flag is stripped before the payload is passed
|
|
50
|
+
* to the framework's interrupt() handler.
|
|
51
|
+
*
|
|
52
|
+
* Example usage in an agent tool:
|
|
53
|
+
*
|
|
54
|
+
* import { suspendPayloadToJson } from '@mongodb-js/agent-engine-runner-shared'
|
|
55
|
+
* return suspendPayloadToJson({
|
|
56
|
+
* suspend_reason: 'awaiting_human_review',
|
|
57
|
+
* suspend_context: { claim_id: 'C-123', task_id: 'T-456' },
|
|
58
|
+
* })
|
|
59
|
+
*/
|
|
60
|
+
export const SuspendPayloadSchema = z.object({
|
|
61
|
+
/** Why the agent is suspending (e.g. 'awaiting_human_review'). */
|
|
62
|
+
suspend_reason: z.string(),
|
|
63
|
+
/** Arbitrary context the human reviewer needs to make a decision. */
|
|
64
|
+
suspend_context: z.record(z.string(), z.unknown()).default({}),
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Serialize a SuspendPayload to the suspend wire marker, and record an
|
|
68
|
+
* out-of-band suspend request on the current execution frame.
|
|
69
|
+
*
|
|
70
|
+
* The Tool Pod honors suspend from that recorded signal — set only here, in
|
|
71
|
+
* the tool author's own code — not by sniffing tool-result content, so
|
|
72
|
+
* untrusted data a tool relays can no longer forge a HITL suspend. The marker
|
|
73
|
+
* string is still returned unchanged for wire/replay compatibility.
|
|
74
|
+
*/
|
|
75
|
+
export function suspendPayloadToJson(payload) {
|
|
76
|
+
const marker = { ...payload, __suspend__: true };
|
|
77
|
+
recordSuspendRequest(marker);
|
|
78
|
+
return JSON.stringify(marker);
|
|
79
|
+
}
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Streaming / Interrupt Results (AER internal)
|
|
82
|
+
// =============================================================================
|
|
83
|
+
/**
|
|
84
|
+
* Result from agent execution when the agent is suspended.
|
|
85
|
+
*
|
|
86
|
+
* Contains the suspend payload directly (framework-agnostic) rather than
|
|
87
|
+
* wrapping framework-specific Interrupt objects. Any framework-specific
|
|
88
|
+
* state needed to resume (LangGraph checkpoint id, ADK function-call
|
|
89
|
+
* correlation, etc.) is carried opaquely in `metadata` — the AER never
|
|
90
|
+
* inspects it; the framework adapter writes it on suspend and reads it back
|
|
91
|
+
* from `RequestContext.metadata` on resume.
|
|
92
|
+
*/
|
|
93
|
+
export const PendingInterruptSchema = z.object({
|
|
94
|
+
id: z.string().min(1),
|
|
95
|
+
value: z.unknown(),
|
|
96
|
+
});
|
|
97
|
+
export const InterruptResultSchema = z.object({
|
|
98
|
+
suspend_payload: z.unknown(),
|
|
99
|
+
interrupts: z.array(PendingInterruptSchema).optional(),
|
|
100
|
+
resume_schema: z.record(z.string(), z.unknown()).optional(),
|
|
101
|
+
/** Opaque framework-owned resume state, round-tripped via the OE. */
|
|
102
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
103
|
+
/** Assistant/tool messages produced before suspension, for Memory ingestion. */
|
|
104
|
+
messages: z.array(MessageSchema).default([]),
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* Result from agent execution for normal completion.
|
|
108
|
+
*
|
|
109
|
+
* Returned by _execute_via_agent_stream when the agent finishes without
|
|
110
|
+
* suspend. The caller uses content for the final response and messages
|
|
111
|
+
* for memory writing.
|
|
112
|
+
*/
|
|
113
|
+
export const StreamingResultSchema = z.object({
|
|
114
|
+
/** Sanitized final AI response (thinking stripped, trailing empties skipped). */
|
|
115
|
+
content: z.string().default(""),
|
|
116
|
+
/** All messages from agent execution (sdk-core Message objects), for memory writer. */
|
|
117
|
+
messages: z.array(MessageSchema).default([]),
|
|
118
|
+
/** Opaque framework-owned state produced by the completed execution. */
|
|
119
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
120
|
+
});
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// Agent Start (Client → OE)
|
|
123
|
+
// =============================================================================
|
|
124
|
+
/** Request to invoke the agent (compatible with agent-runtime). */
|
|
125
|
+
export const InvokeRequestSchema = z.object({
|
|
126
|
+
/** User message. */
|
|
127
|
+
message: z.string(),
|
|
128
|
+
/** Session ID for trace correlation. */
|
|
129
|
+
session_id: z.string().optional(),
|
|
130
|
+
// Multi-tenant context
|
|
131
|
+
/** Organization ID for multi-tenant isolation. */
|
|
132
|
+
org_id: z.string().optional(),
|
|
133
|
+
/** User ID for personalization. */
|
|
134
|
+
user_id: z.string().optional(),
|
|
135
|
+
/** Workspace identifier for cost tracking. */
|
|
136
|
+
workspace_id: z.string().optional(),
|
|
137
|
+
/** Project ID for project-level scoping. */
|
|
138
|
+
project_id: z.string().optional(),
|
|
139
|
+
// Per-agent AER routing
|
|
140
|
+
/** AER HTTP endpoint for this agent; overrides default AER_URL. */
|
|
141
|
+
aer_url: z.string().optional(),
|
|
142
|
+
/** Tool HTTP endpoint for this agent; overrides default TOOL_URL. */
|
|
143
|
+
tool_url: z.string().optional(),
|
|
144
|
+
// Sync mode (default: wait for completion)
|
|
145
|
+
/** If true, wait for completion and return result. */
|
|
146
|
+
wait: z.boolean().default(true),
|
|
147
|
+
/** Caller-provided custom headers (X-Mdb-Agent-Engine-Custom-* HTTP headers, prefix-stripped and lowercased). */
|
|
148
|
+
custom_headers: z.record(z.string(), z.string()).optional(),
|
|
149
|
+
});
|
|
150
|
+
/** Response from invoking the agent (compatible with agent-runtime). */
|
|
151
|
+
export const InvokeResponseSchema = z.object({
|
|
152
|
+
/** The agent's response. */
|
|
153
|
+
result: z.unknown().optional(),
|
|
154
|
+
/** Session ID. */
|
|
155
|
+
session_id: z.string().optional(),
|
|
156
|
+
/** User ID used. */
|
|
157
|
+
user_id: z.string().optional(),
|
|
158
|
+
// Runner-specific fields (for async mode and tracking)
|
|
159
|
+
/** Unique execution identifier. */
|
|
160
|
+
execution_id: z.string().optional(),
|
|
161
|
+
/** Execution status. */
|
|
162
|
+
status: z.string().optional(),
|
|
163
|
+
/** Error message if failed. */
|
|
164
|
+
error: z.string().optional(),
|
|
165
|
+
});
|
|
166
|
+
// =============================================================================
|
|
167
|
+
// Execute Request (OE → AER)
|
|
168
|
+
// =============================================================================
|
|
169
|
+
/** Request to execute an agent in AER. */
|
|
170
|
+
export const ExecuteRequestSchema = z.object({
|
|
171
|
+
platform_trace_id: z.string().nullable().optional(),
|
|
172
|
+
/** Unique execution identifier. */
|
|
173
|
+
execution_id: z.string(),
|
|
174
|
+
/**
|
|
175
|
+
* User message. Defaults to empty. Promotion of `payload.message` into an
|
|
176
|
+
* empty top-level message happens in the AER (`resolveInvocationParams`),
|
|
177
|
+
* NOT at the model level — mirrors Python's `ExecuteRequest`. Keeping it
|
|
178
|
+
* out of the model lets the AER reject a message supplied in both places
|
|
179
|
+
* as a clean 400 instead of a 422 that would echo the payload into logs.
|
|
180
|
+
*/
|
|
181
|
+
message: z.string().default(""),
|
|
182
|
+
/** URL of the OE for callbacks. */
|
|
183
|
+
platform_api_url: z.string(),
|
|
184
|
+
/** Suspension generation expected by this dispatch. */
|
|
185
|
+
suspend_generation: z
|
|
186
|
+
.number()
|
|
187
|
+
.int()
|
|
188
|
+
.nonnegative()
|
|
189
|
+
.nullable()
|
|
190
|
+
.transform((value) => value ?? undefined)
|
|
191
|
+
.optional(),
|
|
192
|
+
/** Replica-specific OE owner URL for callback fallback. */
|
|
193
|
+
platform_api_owner_url: z.string().nullable().optional(),
|
|
194
|
+
/** Whether this is a resume after SUSPEND. */
|
|
195
|
+
resume: z.boolean().default(false),
|
|
196
|
+
/** Step to resume from. */
|
|
197
|
+
resume_from_step: z.number().int().optional(),
|
|
198
|
+
/** Structured data to inject on resume. */
|
|
199
|
+
resume_data: z.record(z.string(), z.unknown()).optional(),
|
|
200
|
+
// Multi-tenant context
|
|
201
|
+
/** Organization ID for multi-tenant isolation. */
|
|
202
|
+
org_id: z.string().optional(),
|
|
203
|
+
/** User ID for personalization. */
|
|
204
|
+
user_id: z.string().optional(),
|
|
205
|
+
/** Session ID; the AER adapter uses this as the LangGraph thread_id. */
|
|
206
|
+
session_id: z.string().optional(),
|
|
207
|
+
/** Workspace identifier for cost tracking. */
|
|
208
|
+
workspace_id: z.string().optional(),
|
|
209
|
+
/** Project ID for project-level scoping. */
|
|
210
|
+
project_id: z.string().optional(),
|
|
211
|
+
/** Caller-provided custom headers. */
|
|
212
|
+
custom_headers: z.record(z.string(), z.string()).optional(),
|
|
213
|
+
/**
|
|
214
|
+
* Opaque caller-provided input forwarded unchanged on every dispatch;
|
|
215
|
+
* `payload.message` fills the top-level message when it is empty.
|
|
216
|
+
*/
|
|
217
|
+
payload: z.record(z.string(), z.unknown()).optional(),
|
|
218
|
+
/**
|
|
219
|
+
* Opaque framework-owned state passed through to the selected adapter.
|
|
220
|
+
*/
|
|
221
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
222
|
+
/**
|
|
223
|
+
* True when this session's most recent prior execution was cancelled (pod
|
|
224
|
+
* torn down mid-run); the framework adapter fences off that run's partial
|
|
225
|
+
* checkpoint writes before running this turn.
|
|
226
|
+
*/
|
|
227
|
+
previous_execution_cancelled: z.boolean().default(false),
|
|
228
|
+
});
|
|
229
|
+
// =============================================================================
|
|
230
|
+
// Tool Execution (AER → OE → AER/ToolPod)
|
|
231
|
+
// =============================================================================
|
|
232
|
+
/**
|
|
233
|
+
* Request to execute a tool (AER → OE for approval).
|
|
234
|
+
*/
|
|
235
|
+
export const ToolExecuteRequestSchema = z.object({
|
|
236
|
+
/** Execution identifier. */
|
|
237
|
+
execution_id: z.string(),
|
|
238
|
+
/** Name of the tool to execute. */
|
|
239
|
+
tool_name: z.string(),
|
|
240
|
+
/** Tool arguments. */
|
|
241
|
+
arguments: z.record(z.string(), z.unknown()),
|
|
242
|
+
/** Step number in execution sequence. */
|
|
243
|
+
step_number: z.number().int(),
|
|
244
|
+
/** Stable LLM tool-call id; joins this call's execution-log records to the session message. */
|
|
245
|
+
tool_call_id: z.string().optional(),
|
|
246
|
+
/** Explicit observability kind. */
|
|
247
|
+
kind: z.string().optional(),
|
|
248
|
+
/** Whether OE should return the approved call to the current AER stack. */
|
|
249
|
+
is_local: z.boolean().default(true),
|
|
250
|
+
/** Top-level tool argument names to redact from execution logs. */
|
|
251
|
+
redact_fields: z.array(z.string()).default([]),
|
|
252
|
+
/** Credential provider type for delegated auth. */
|
|
253
|
+
provider_type: z.string().nullish(),
|
|
254
|
+
/** Requested OAuth scopes for delegated auth. */
|
|
255
|
+
scopes: z.array(z.string()).default([]),
|
|
256
|
+
/** Observability metadata. */
|
|
257
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
258
|
+
/** Caller-provided headers carried only for this active request. */
|
|
259
|
+
custom_headers: z.record(z.string(), z.string()).optional(),
|
|
260
|
+
/** Active OTel trace ID, for OE execution-log correlation. */
|
|
261
|
+
trace_id: z.string().nullish(),
|
|
262
|
+
/** Active OTel span ID, for OE execution-log correlation. */
|
|
263
|
+
span_id: z.string().nullish(),
|
|
264
|
+
});
|
|
265
|
+
/** Authorization details returned when broker consent is required. */
|
|
266
|
+
export const ElicitationInfoSchema = z.object({
|
|
267
|
+
elicitation_id: z.string(),
|
|
268
|
+
authorization_url: z.string(),
|
|
269
|
+
message: z.string().default(""),
|
|
270
|
+
created: z.boolean().default(false),
|
|
271
|
+
});
|
|
272
|
+
/** Identity of the policy that caused a guardrail block or require_review halt. */
|
|
273
|
+
export const GuardrailMetaSchema = z.object({
|
|
274
|
+
/** ID of the policy that caused the halt. */
|
|
275
|
+
guardrail_id: z.string(),
|
|
276
|
+
/** Category of the policy that caused the halt. */
|
|
277
|
+
guardrail_category: z.string(),
|
|
278
|
+
});
|
|
279
|
+
/** Response from OE for tool execution request. */
|
|
280
|
+
const ToolExecuteResponseObjectSchema = z.object({
|
|
281
|
+
/** Whether to proceed with execution. */
|
|
282
|
+
proceed: z.boolean(),
|
|
283
|
+
/** Cached result if replaying. */
|
|
284
|
+
cached_result: z.unknown().nullish(),
|
|
285
|
+
/** URL to route to (callback or OE-owned stream relay). Null when OE returns result directly. */
|
|
286
|
+
route_to: z.string().nullish(),
|
|
287
|
+
/** Reason if blocked. */
|
|
288
|
+
reason: z.string().nullish(),
|
|
289
|
+
/** Authorization elicitation details when broker consent is required. */
|
|
290
|
+
elicitation: ElicitationInfoSchema.nullish(),
|
|
291
|
+
/** Final status when OE directly executes the intercepted tool. */
|
|
292
|
+
status: z.string().nullish(),
|
|
293
|
+
/** Final result from OE-owned execution. */
|
|
294
|
+
result: z.unknown().nullish(),
|
|
295
|
+
/** Execution error message. */
|
|
296
|
+
error: z.string().nullish(),
|
|
297
|
+
/** Machine-readable failure classification for an invoke_llm error. */
|
|
298
|
+
error_code: z.string().nullish(),
|
|
299
|
+
/** Whether retry is safe. */
|
|
300
|
+
retryable: z.boolean().default(false),
|
|
301
|
+
/** Highest step number observed during nested execution. */
|
|
302
|
+
latest_step_number: z.number().int().nullish(),
|
|
303
|
+
/** Whether replay cache answered. */
|
|
304
|
+
from_cache: z.boolean().default(false),
|
|
305
|
+
/** Execution duration. */
|
|
306
|
+
duration_ms: z.number().nullish(),
|
|
307
|
+
/** Execution pod name. */
|
|
308
|
+
pod_name: z.string().nullish(),
|
|
309
|
+
/** Policy identity when a guardrail halt fires. */
|
|
310
|
+
guardrail_meta: GuardrailMetaSchema.nullish(),
|
|
311
|
+
/** Structured external API failure classification. */
|
|
312
|
+
tool_api_error: ToolAPIErrorSchema.nullish(),
|
|
313
|
+
});
|
|
314
|
+
/**
|
|
315
|
+
* Assemble `guardrail_meta` from the flat `guardrail_id`/`guardrail_category`
|
|
316
|
+
* fields the OE wire format sends, so callers work with a single structured
|
|
317
|
+
* object. Mirrors Python's `_assemble_guardrail_meta` model validator.
|
|
318
|
+
*/
|
|
319
|
+
export const ToolExecuteResponseSchema = z.preprocess((data) => {
|
|
320
|
+
if (data != null && typeof data === "object" && !Array.isArray(data)) {
|
|
321
|
+
const d = data;
|
|
322
|
+
if (d.guardrail_id && d.guardrail_category && d.guardrail_meta == null) {
|
|
323
|
+
return {
|
|
324
|
+
...d,
|
|
325
|
+
guardrail_meta: {
|
|
326
|
+
guardrail_id: d.guardrail_id,
|
|
327
|
+
guardrail_category: d.guardrail_category,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return data;
|
|
333
|
+
}, ToolExecuteResponseObjectSchema);
|
|
334
|
+
/**
|
|
335
|
+
* Report tool execution result (AER → OE).
|
|
336
|
+
*/
|
|
337
|
+
export const ToolResultRequestSchema = z.object({
|
|
338
|
+
/** Execution identifier. */
|
|
339
|
+
execution_id: z.string(),
|
|
340
|
+
/** Step number. */
|
|
341
|
+
step_number: z.number().int(),
|
|
342
|
+
/** Tool name. */
|
|
343
|
+
tool_name: z.string(),
|
|
344
|
+
/** Stable LLM tool-call id; joins this result's execution-log record to its call and the session message. */
|
|
345
|
+
tool_call_id: z.string().optional(),
|
|
346
|
+
/** Execution status: success, error, suspend, interrupted. */
|
|
347
|
+
status: z.string(),
|
|
348
|
+
/** Tool result if successful. */
|
|
349
|
+
result: z.unknown().optional(),
|
|
350
|
+
/** Error message if failed. */
|
|
351
|
+
error: z.string().optional(),
|
|
352
|
+
/** Execution duration in milliseconds. */
|
|
353
|
+
duration_ms: z.number(),
|
|
354
|
+
/** Hostname/pod name where tool was executed. */
|
|
355
|
+
pod_name: z.string().optional(),
|
|
356
|
+
// Token usage fields (populated for invoke_llm calls)
|
|
357
|
+
/** Prompt/input tokens used. */
|
|
358
|
+
prompt_tokens: z.number().int().optional(),
|
|
359
|
+
/** Completion/output tokens used. */
|
|
360
|
+
completion_tokens: z.number().int().optional(),
|
|
361
|
+
/** Total tokens used. */
|
|
362
|
+
total_tokens: z.number().int().optional(),
|
|
363
|
+
/** LLM model name. */
|
|
364
|
+
model: z.string().optional(),
|
|
365
|
+
/** Workspace identifier. */
|
|
366
|
+
workspace_id: z.string().optional(),
|
|
367
|
+
/** Explicit observability kind. */
|
|
368
|
+
kind: z.string().optional(),
|
|
369
|
+
/** Observability metadata. */
|
|
370
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
371
|
+
/** Active OTel trace ID, for OE execution-log correlation. */
|
|
372
|
+
trace_id: z.string().nullish(),
|
|
373
|
+
/** Active OTel span ID, for OE execution-log correlation. */
|
|
374
|
+
span_id: z.string().nullish(),
|
|
375
|
+
/** Structured external API failure classification. */
|
|
376
|
+
tool_api_error: ToolAPIErrorSchema.nullish(),
|
|
377
|
+
});
|
|
378
|
+
// =============================================================================
|
|
379
|
+
// Tool Pod Execution (OE → ToolPod)
|
|
380
|
+
// =============================================================================
|
|
381
|
+
export const MAX_TOOL_ARGUMENT_BYTES = 16 * 1024 * 1024;
|
|
382
|
+
const ToolArgumentsSchema = z
|
|
383
|
+
.record(z.string(), JsonValueSchema)
|
|
384
|
+
.superRefine((toolArguments, context) => {
|
|
385
|
+
let serialized;
|
|
386
|
+
try {
|
|
387
|
+
serialized = JSON.stringify(toolArguments);
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
context.addIssue({
|
|
391
|
+
code: "custom",
|
|
392
|
+
message: "tool arguments must contain JSON values",
|
|
393
|
+
});
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (Buffer.byteLength(serialized, "utf-8") > MAX_TOOL_ARGUMENT_BYTES) {
|
|
397
|
+
context.addIssue({
|
|
398
|
+
code: "custom",
|
|
399
|
+
message: `tool arguments exceed ${MAX_TOOL_ARGUMENT_BYTES} bytes`,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
/** Delegated credential injected by OE for tool execution. */
|
|
404
|
+
export const ToolAuthorizationSchema = z.object({
|
|
405
|
+
/** Bearer token for third-party API access. */
|
|
406
|
+
token: z.string(),
|
|
407
|
+
/** Token expiry as Unix seconds. */
|
|
408
|
+
expires_at: z.number().int().nullish(),
|
|
409
|
+
});
|
|
410
|
+
/** Request to execute a tool in a Tool Pod. */
|
|
411
|
+
export const ToolPodExecuteRequestSchema = z.object({
|
|
412
|
+
platform_trace_id: z.string().nullable().optional(),
|
|
413
|
+
/** Execution identifier. */
|
|
414
|
+
execution_id: z.string(),
|
|
415
|
+
/** Name of the tool. */
|
|
416
|
+
tool_name: z.string(),
|
|
417
|
+
/** Tool arguments. */
|
|
418
|
+
arguments: ToolArgumentsSchema,
|
|
419
|
+
/** Tool call ID from LLM. */
|
|
420
|
+
tool_call_id: z.string().optional(),
|
|
421
|
+
/**
|
|
422
|
+
* Step number of this call in the execution sequence, so the per-call
|
|
423
|
+
* interrupt (POST /interrupt/call) can address exactly this work.
|
|
424
|
+
* Optional for backward compatibility with older OE dispatchers: absent,
|
|
425
|
+
* the call is only drain-addressable.
|
|
426
|
+
*/
|
|
427
|
+
step_number: z.number().int().nonnegative().optional(),
|
|
428
|
+
/**
|
|
429
|
+
* Session ID for memory context. For framework integrations, this is
|
|
430
|
+
* the framework's thread ID. Must be non-empty.
|
|
431
|
+
*/
|
|
432
|
+
session_id: z.string().min(1),
|
|
433
|
+
/** User ID for context. */
|
|
434
|
+
user_id: z.string().optional(),
|
|
435
|
+
/** OE callback URL for memory and other operations. */
|
|
436
|
+
oe_url: z.string().optional(),
|
|
437
|
+
/** Replica-specific OE owner callback URL. */
|
|
438
|
+
oe_owner_url: z.string().nullable().optional(),
|
|
439
|
+
/** Delegated credential injected by OE via the credential broker. */
|
|
440
|
+
authorization: ToolAuthorizationSchema.optional(),
|
|
441
|
+
/** Caller-provided custom headers. */
|
|
442
|
+
custom_headers: z.record(z.string(), z.string()).optional(),
|
|
443
|
+
/**
|
|
444
|
+
* Opaque caller-provided invocation payload, forwarded so tools can read it
|
|
445
|
+
* via `getCurrentPayload()`.
|
|
446
|
+
*/
|
|
447
|
+
payload: z.record(z.string(), z.unknown()).optional(),
|
|
448
|
+
/**
|
|
449
|
+
* Tool execution metadata forwarded by OE (recognized keys: mcp_server,
|
|
450
|
+
* mcp_tool). Accepted for wire-compatibility; MCP tool resolution is not
|
|
451
|
+
* ported to TS, so it is currently unused here.
|
|
452
|
+
*/
|
|
453
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
454
|
+
});
|
|
455
|
+
/** Response from Tool Pod execution. */
|
|
456
|
+
export const ToolPodExecuteResponseSchema = z.object({
|
|
457
|
+
/** Execution status: success, error. */
|
|
458
|
+
status: z.string(),
|
|
459
|
+
/** Tool result. */
|
|
460
|
+
result: z.unknown().optional(),
|
|
461
|
+
/** Error message if failed. */
|
|
462
|
+
error: z.string().optional(),
|
|
463
|
+
/** Hostname/pod name where tool was executed. */
|
|
464
|
+
pod_name: z.string().optional(),
|
|
465
|
+
/** Explicit observability kind. */
|
|
466
|
+
kind: z.string().optional(),
|
|
467
|
+
/** Observability metadata. */
|
|
468
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
469
|
+
/**
|
|
470
|
+
* True when this runner reports HITL suspend out of band via `status`.
|
|
471
|
+
* Absent on older runners; the OE treats that absence as a
|
|
472
|
+
* legacy pod that still signals suspend through in-band result content.
|
|
473
|
+
*/
|
|
474
|
+
oob_suspend_supported: z.boolean().optional(),
|
|
475
|
+
/** Structured external API failure classification. */
|
|
476
|
+
tool_api_error: ToolAPIErrorSchema.nullish(),
|
|
477
|
+
});
|
|
478
|
+
// =============================================================================
|
|
479
|
+
// Guardrails Runtime Check (OE → ToolPod)
|
|
480
|
+
// =============================================================================
|
|
481
|
+
/** Runtime stage where OE is asking the Tool Pod to evaluate guardrails. */
|
|
482
|
+
export const GuardrailRuntimeStageSchema = z.enum([
|
|
483
|
+
"llm_input",
|
|
484
|
+
"llm_output",
|
|
485
|
+
"tool_input",
|
|
486
|
+
"tool_output",
|
|
487
|
+
]);
|
|
488
|
+
export const GuardrailRuntimeStage = {
|
|
489
|
+
LLM_INPUT: "llm_input",
|
|
490
|
+
LLM_OUTPUT: "llm_output",
|
|
491
|
+
TOOL_INPUT: "tool_input",
|
|
492
|
+
TOOL_OUTPUT: "tool_output",
|
|
493
|
+
};
|
|
494
|
+
/** Decision returned by the Tool Pod guardrails evaluator. */
|
|
495
|
+
export const GuardrailCheckDecisionSchema = z.enum([
|
|
496
|
+
"allow",
|
|
497
|
+
"block",
|
|
498
|
+
"modify",
|
|
499
|
+
"require_review",
|
|
500
|
+
"log_only",
|
|
501
|
+
]);
|
|
502
|
+
export const GuardrailCheckDecision = {
|
|
503
|
+
ALLOW: "allow",
|
|
504
|
+
BLOCK: "block",
|
|
505
|
+
MODIFY: "modify",
|
|
506
|
+
REQUIRE_REVIEW: "require_review",
|
|
507
|
+
LOG_ONLY: "log_only",
|
|
508
|
+
};
|
|
509
|
+
/** OE-selected guardrail policy sent to the Tool Pod for evaluation. */
|
|
510
|
+
export const GuardrailRuntimePolicySchema = z.object({
|
|
511
|
+
/** Guardrail policy identifier. */
|
|
512
|
+
id: z.string(),
|
|
513
|
+
/** Guardrail policy type. */
|
|
514
|
+
type: z.string(),
|
|
515
|
+
/** Guardrail policy status. */
|
|
516
|
+
status: z.string().default("active"),
|
|
517
|
+
/** Action requested when the policy triggers. */
|
|
518
|
+
action: z.string(),
|
|
519
|
+
/** Runtime stages this policy applies to; empty means all stages. */
|
|
520
|
+
stage_filter: z.array(z.string()).default([]),
|
|
521
|
+
/** Evaluator-specific policy configuration. */
|
|
522
|
+
config: z.record(z.string(), JsonValueSchema).default({}),
|
|
523
|
+
});
|
|
524
|
+
/**
|
|
525
|
+
* Whether `policy` applies at `stage`. Inactive policies never apply; an empty
|
|
526
|
+
* stage filter applies to all stages; otherwise the stage must match (case- and
|
|
527
|
+
* whitespace-insensitive). Mirrors `GuardrailRuntimePolicy.applies_to_stage`.
|
|
528
|
+
*/
|
|
529
|
+
export function appliesToStage(policy, stage) {
|
|
530
|
+
if (policy.status.trim().toLowerCase() !== "active") {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
if (policy.stage_filter.length === 0) {
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
return policy.stage_filter.some((value) => value.trim().toLowerCase() === stage);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Resolve the decision a triggered policy requests, from its `action` (falling
|
|
540
|
+
* back to `config.on_fail`). Mirrors `GuardrailRuntimePolicy.check_decision`.
|
|
541
|
+
*/
|
|
542
|
+
export function checkDecision(policy) {
|
|
543
|
+
const action = policy.action.trim().toLowerCase();
|
|
544
|
+
if (["modify", "transform", "fix"].includes(action)) {
|
|
545
|
+
return GuardrailCheckDecision.MODIFY;
|
|
546
|
+
}
|
|
547
|
+
if (["noop", "no_op", "warn", "log_only"].includes(action)) {
|
|
548
|
+
return GuardrailCheckDecision.LOG_ONLY;
|
|
549
|
+
}
|
|
550
|
+
if (action === "require_review") {
|
|
551
|
+
return GuardrailCheckDecision.REQUIRE_REVIEW;
|
|
552
|
+
}
|
|
553
|
+
if (["block", "exception"].includes(action)) {
|
|
554
|
+
return GuardrailCheckDecision.BLOCK;
|
|
555
|
+
}
|
|
556
|
+
const onFail = policy.config.on_fail;
|
|
557
|
+
if (typeof onFail === "string") {
|
|
558
|
+
const normalized = onFail.trim().toLowerCase();
|
|
559
|
+
if (normalized === "fix") {
|
|
560
|
+
return GuardrailCheckDecision.MODIFY;
|
|
561
|
+
}
|
|
562
|
+
if (["noop", "warn", "log_only"].includes(normalized)) {
|
|
563
|
+
return GuardrailCheckDecision.LOG_ONLY;
|
|
564
|
+
}
|
|
565
|
+
if (["block", "exception"].includes(normalized)) {
|
|
566
|
+
return GuardrailCheckDecision.BLOCK;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return GuardrailCheckDecision.ALLOW;
|
|
570
|
+
}
|
|
571
|
+
/** Runtime content and metadata to evaluate. */
|
|
572
|
+
export const GuardrailCheckInputSchema = z.object({
|
|
573
|
+
/** Text content to evaluate. */
|
|
574
|
+
text: z.string(),
|
|
575
|
+
/** Runtime metadata such as model, tool name, or source. */
|
|
576
|
+
metadata: z.record(z.string(), JsonValueSchema).default({}),
|
|
577
|
+
});
|
|
578
|
+
/** Execution context for a guardrail check. */
|
|
579
|
+
export const GuardrailCheckContextSchema = z.object({
|
|
580
|
+
/** Organization ID. */
|
|
581
|
+
org_id: z.string(),
|
|
582
|
+
/** Project ID. */
|
|
583
|
+
project_id: z.string(),
|
|
584
|
+
/** Workspace ID. */
|
|
585
|
+
workspace_id: z.string().nullish(),
|
|
586
|
+
/** Session ID. */
|
|
587
|
+
session_id: z.string().nullish(),
|
|
588
|
+
/** User ID. */
|
|
589
|
+
user_id: z.string().nullish(),
|
|
590
|
+
});
|
|
591
|
+
/** Request from OE to Tool Pod to evaluate selected guardrail policies. */
|
|
592
|
+
export const GuardrailCheckRequestSchema = z.object({
|
|
593
|
+
/** Execution identifier. */
|
|
594
|
+
execution_id: z.string(),
|
|
595
|
+
/** Runtime stage being evaluated. */
|
|
596
|
+
stage: GuardrailRuntimeStageSchema,
|
|
597
|
+
/** Content to evaluate. */
|
|
598
|
+
input: GuardrailCheckInputSchema,
|
|
599
|
+
/** Execution context. */
|
|
600
|
+
context: GuardrailCheckContextSchema,
|
|
601
|
+
/** OE-selected policies to evaluate. */
|
|
602
|
+
policies: z.array(GuardrailRuntimePolicySchema).default([]),
|
|
603
|
+
});
|
|
604
|
+
/** Evidence explaining why a guardrail policy triggered. */
|
|
605
|
+
export const GuardrailCheckEvidenceSchema = z.object({
|
|
606
|
+
/** Triggered policy identifier. */
|
|
607
|
+
policy_id: z.string(),
|
|
608
|
+
/** Human-readable evidence summary. */
|
|
609
|
+
message: z.string().default(""),
|
|
610
|
+
/** Evaluator-specific evidence metadata. */
|
|
611
|
+
metadata: z.record(z.string(), JsonValueSchema).default({}),
|
|
612
|
+
});
|
|
613
|
+
/** Decision returned by the Tool Pod guardrails evaluator. */
|
|
614
|
+
export const GuardrailCheckResponseSchema = z.object({
|
|
615
|
+
/** Guardrails decision. */
|
|
616
|
+
decision: GuardrailCheckDecisionSchema,
|
|
617
|
+
/** Whether execution may continue. */
|
|
618
|
+
allowed: z.boolean(),
|
|
619
|
+
/** Modified text when decision is modify, or original text for allow/no-op. */
|
|
620
|
+
transformed_text: z.string().nullish(),
|
|
621
|
+
/** Policy IDs that triggered. */
|
|
622
|
+
triggered_policy_ids: z.array(z.string()).default([]),
|
|
623
|
+
/** Policy trigger evidence. */
|
|
624
|
+
evidence: z.array(GuardrailCheckEvidenceSchema).default([]),
|
|
625
|
+
/** Decision reason. */
|
|
626
|
+
reason: z.string().nullish(),
|
|
627
|
+
/** Evaluator-specific response metadata. */
|
|
628
|
+
metadata: z.record(z.string(), JsonValueSchema).default({}),
|
|
629
|
+
});
|
|
630
|
+
// =============================================================================
|
|
631
|
+
// LLM Pod Execution (AER → ToolPod)
|
|
632
|
+
// =============================================================================
|
|
633
|
+
/**
|
|
634
|
+
* Typed invoke_llm arguments forwarded through OE and tool pods.
|
|
635
|
+
*
|
|
636
|
+
* Python uses Pydantic aliases:
|
|
637
|
+
* - validation alias `stop` ↔ `stop_sequences`, serialization alias `stop`
|
|
638
|
+
* - pre-validator renames `kwargs` → `options`
|
|
639
|
+
*
|
|
640
|
+
* In Zod we replicate this with a `z.preprocess` that normalizes incoming
|
|
641
|
+
* payloads into the canonical schema (using `stop_sequences` and
|
|
642
|
+
* `options`). For outgoing serialization that needs the wire alias `stop`,
|
|
643
|
+
* use `serializeInvokeLLMRequestArguments()`.
|
|
644
|
+
*/
|
|
645
|
+
export const InvokeLLMRequestArgumentsSchema = z.preprocess((value) => {
|
|
646
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
647
|
+
return value;
|
|
648
|
+
const obj = value;
|
|
649
|
+
const normalized = { ...obj };
|
|
650
|
+
// `stop` ↔ `stop_sequences` validation alias.
|
|
651
|
+
if (!("stop_sequences" in normalized) && "stop" in normalized) {
|
|
652
|
+
normalized["stop_sequences"] = normalized["stop"];
|
|
653
|
+
delete normalized["stop"];
|
|
654
|
+
}
|
|
655
|
+
// `kwargs` → `options` pre-validator.
|
|
656
|
+
if (!("options" in normalized) && "kwargs" in normalized) {
|
|
657
|
+
normalized["options"] = normalized["kwargs"];
|
|
658
|
+
delete normalized["kwargs"];
|
|
659
|
+
}
|
|
660
|
+
return normalized;
|
|
661
|
+
}, z.object({
|
|
662
|
+
/** Model name (e.g. 'gpt-5.4-mini', 'gemini-3-flash-preview'). */
|
|
663
|
+
model: z.string(),
|
|
664
|
+
/** Conversation in sdk-core Message wire format. */
|
|
665
|
+
messages: z.array(MessageSchema),
|
|
666
|
+
/**
|
|
667
|
+
* Stable identifier for the LLM instance registered via
|
|
668
|
+
* `app.llm({ llmId })`; the tool pod resolves this id against its
|
|
669
|
+
* named-LLM registry. Unnamed `app.llm()` calls register under the
|
|
670
|
+
* sentinel id `"__default__"`. Defaults to `"__default__"` for backward
|
|
671
|
+
* compatibility with callers that omit this field.
|
|
672
|
+
*/
|
|
673
|
+
llm_id: z.string().default("__default__"),
|
|
674
|
+
/** Stop sequences forwarded to the underlying LLM provider. */
|
|
675
|
+
stop_sequences: z.array(z.string()).optional(),
|
|
676
|
+
/** Serialized tool schemas for bind_tools. */
|
|
677
|
+
tools: z.array(LLMToolSchemaValidator).optional(),
|
|
678
|
+
/**
|
|
679
|
+
* Forced tool selection forwarded to the tool pod's bind_tools call
|
|
680
|
+
* (e.g. a function name from withStructuredOutput). LangChain translates
|
|
681
|
+
* the value against the bound tools.
|
|
682
|
+
*/
|
|
683
|
+
tool_choice: JsonValueSchema.optional(),
|
|
684
|
+
/** Explicit provider/model invocation options (e.g. max_tokens). */
|
|
685
|
+
options: LLMInvocationOptionsSchema.optional(),
|
|
686
|
+
/** Whether OE should approve and route a real streaming invoke_llm call. */
|
|
687
|
+
stream: z.boolean().default(false),
|
|
688
|
+
}));
|
|
689
|
+
/**
|
|
690
|
+
* Serialize InvokeLLMRequestArguments to the snake_case wire shape, the
|
|
691
|
+
* equivalent of Python's `model_dump(by_alias=True)`:
|
|
692
|
+
* - `stop_sequences` → `stop` (Pydantic `serialization_alias="stop"`), and
|
|
693
|
+
* - each Message is dumped camel→snake via `serializeMessage`.
|
|
694
|
+
*
|
|
695
|
+
* Use this when sending to a Python consumer (OE / Tool Pod) that expects the
|
|
696
|
+
* snake_case wire fields.
|
|
697
|
+
*/
|
|
698
|
+
export function serializeInvokeLLMRequestArguments(args) {
|
|
699
|
+
const { stop_sequences, messages, ...rest } = args;
|
|
700
|
+
const out = { ...rest };
|
|
701
|
+
out["messages"] = messages.map(serializeMessage);
|
|
702
|
+
if (stop_sequences !== undefined)
|
|
703
|
+
out["stop"] = stop_sequences;
|
|
704
|
+
return out;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Request to invoke LLM on a tool executor pod.
|
|
708
|
+
*
|
|
709
|
+
* Python pre-validator `_normalize_flat_payload`: if `arguments` is
|
|
710
|
+
* missing but `execution_id` is present, treat the remaining keys as
|
|
711
|
+
* the arguments payload. Same logic replicated via `z.preprocess`.
|
|
712
|
+
*/
|
|
713
|
+
export const LLMPodInvokeRequestSchema = z.preprocess((value) => {
|
|
714
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
715
|
+
return value;
|
|
716
|
+
const obj = value;
|
|
717
|
+
if ("arguments" in obj || !("execution_id" in obj))
|
|
718
|
+
return value;
|
|
719
|
+
// Routing metadata stays top-level rather than being folded into the
|
|
720
|
+
// legacy flat payload's LLM arguments.
|
|
721
|
+
const { execution_id, platform_trace_id, step_number, ...args } = obj;
|
|
722
|
+
const out = {
|
|
723
|
+
execution_id,
|
|
724
|
+
platform_trace_id,
|
|
725
|
+
arguments: args,
|
|
726
|
+
};
|
|
727
|
+
if (step_number !== undefined && step_number !== null) {
|
|
728
|
+
out["step_number"] = step_number;
|
|
729
|
+
}
|
|
730
|
+
return out;
|
|
731
|
+
}, z.object({
|
|
732
|
+
platform_trace_id: z.string().nullable().optional(),
|
|
733
|
+
/** Execution identifier. */
|
|
734
|
+
execution_id: z.string(),
|
|
735
|
+
/** Typed invoke_llm arguments. */
|
|
736
|
+
arguments: InvokeLLMRequestArgumentsSchema,
|
|
737
|
+
/**
|
|
738
|
+
* Step number of this LLM call, so the per-call interrupt
|
|
739
|
+
* (POST /interrupt/call) can address exactly this invocation. Optional
|
|
740
|
+
* for backward compatibility with older OE dispatchers.
|
|
741
|
+
*/
|
|
742
|
+
step_number: z.number().int().nonnegative().optional(),
|
|
743
|
+
}));
|
|
744
|
+
/**
|
|
745
|
+
* Normalized result from an LLM invocation.
|
|
746
|
+
*
|
|
747
|
+
* Provides a consistent shape for LLM responses regardless of the
|
|
748
|
+
* underlying provider (OpenAI, Gemini, Anthropic, etc.). Implemented as
|
|
749
|
+
* a class to mirror `LLMResponse` in agent-engine-sdk and to host the
|
|
750
|
+
* `fromResponse` / `toResponse` / `extractUsage` conversion methods.
|
|
751
|
+
*/
|
|
752
|
+
export class LLMResult {
|
|
753
|
+
content;
|
|
754
|
+
toolCalls;
|
|
755
|
+
usage;
|
|
756
|
+
metadata;
|
|
757
|
+
id;
|
|
758
|
+
name;
|
|
759
|
+
additionalKwargs;
|
|
760
|
+
responseMetadata;
|
|
761
|
+
constructor(data) {
|
|
762
|
+
this.content = data.content ?? "";
|
|
763
|
+
this.toolCalls = data.toolCalls ?? [];
|
|
764
|
+
this.usage = data.usage;
|
|
765
|
+
this.metadata = data.metadata ?? {};
|
|
766
|
+
this.id = data.id;
|
|
767
|
+
this.name = data.name;
|
|
768
|
+
this.additionalKwargs = data.additionalKwargs;
|
|
769
|
+
this.responseMetadata = data.responseMetadata;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Extract a normalized result from any LLM response object.
|
|
773
|
+
*
|
|
774
|
+
* Accepts an sdk-core `LLMResponse` directly, or a duck-typed object
|
|
775
|
+
* (e.g. raw provider response) by falling back to string-coerced
|
|
776
|
+
* content extraction.
|
|
777
|
+
*/
|
|
778
|
+
static fromResponse(response) {
|
|
779
|
+
if (response instanceof LLMResponse) {
|
|
780
|
+
return new LLMResult({
|
|
781
|
+
content: response.content,
|
|
782
|
+
toolCalls: response.toolCalls ?? [],
|
|
783
|
+
usage: response.usage,
|
|
784
|
+
metadata: jsonSafeMetadata(response.metadata) ?? {},
|
|
785
|
+
id: response.id,
|
|
786
|
+
name: response.name,
|
|
787
|
+
additionalKwargs: jsonSafeMetadata(response.additionalKwargs),
|
|
788
|
+
responseMetadata: jsonSafeMetadata(response.responseMetadata),
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
const isObj = response !== null &&
|
|
792
|
+
response !== undefined &&
|
|
793
|
+
typeof response === "object";
|
|
794
|
+
const obj = (isObj ? response : {});
|
|
795
|
+
// Mirror Python `response.content if hasattr(response, "content") else str(response)`:
|
|
796
|
+
// if the response carries a `content` attribute we use it (coerced to string),
|
|
797
|
+
// otherwise we fall back to String(response).
|
|
798
|
+
const content = isObj && "content" in obj
|
|
799
|
+
? typeof obj["content"] === "string"
|
|
800
|
+
? obj["content"]
|
|
801
|
+
: obj["content"] === undefined || obj["content"] === null
|
|
802
|
+
? ""
|
|
803
|
+
: String(obj["content"])
|
|
804
|
+
: String(response);
|
|
805
|
+
const toolCallsRaw = obj["tool_calls"];
|
|
806
|
+
const toolCalls = Array.isArray(toolCallsRaw)
|
|
807
|
+
? toolCallsRaw
|
|
808
|
+
: [];
|
|
809
|
+
const usage = LLMResult.extractUsage(response);
|
|
810
|
+
const metadata = jsonSafeMetadata(obj["metadata"]) ?? {};
|
|
811
|
+
const responseMetadata = jsonSafeMetadata(obj["response_metadata"]);
|
|
812
|
+
const additionalKwargs = jsonSafeMetadata(obj["additional_kwargs"]);
|
|
813
|
+
const id = typeof obj["id"] === "string" ? obj["id"] : undefined;
|
|
814
|
+
const name = typeof obj["name"] === "string" ? obj["name"] : undefined;
|
|
815
|
+
return new LLMResult({
|
|
816
|
+
content,
|
|
817
|
+
toolCalls,
|
|
818
|
+
usage,
|
|
819
|
+
metadata,
|
|
820
|
+
id,
|
|
821
|
+
name,
|
|
822
|
+
additionalKwargs,
|
|
823
|
+
responseMetadata,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Extract token usage metadata from any response or chunk.
|
|
828
|
+
*
|
|
829
|
+
* Checks (in order): sdk-core `LLMResponse.usage`, `usage_metadata`,
|
|
830
|
+
* `response_metadata.usage`, `response_metadata.token_usage`,
|
|
831
|
+
* `usage`, `metadata.usage`.
|
|
832
|
+
*/
|
|
833
|
+
static extractUsage(response) {
|
|
834
|
+
try {
|
|
835
|
+
if (response instanceof LLMResponse && response.usage !== undefined) {
|
|
836
|
+
return response.usage;
|
|
837
|
+
}
|
|
838
|
+
const obj = (response ?? {});
|
|
839
|
+
const fromMeta = coerceTokenUsage(obj["usage_metadata"]);
|
|
840
|
+
if (fromMeta !== undefined)
|
|
841
|
+
return fromMeta;
|
|
842
|
+
const respMeta = obj["response_metadata"];
|
|
843
|
+
if (isRecord(respMeta)) {
|
|
844
|
+
for (const key of ["usage", "token_usage"]) {
|
|
845
|
+
const coerced = coerceTokenUsage(respMeta[key]);
|
|
846
|
+
if (coerced !== undefined)
|
|
847
|
+
return coerced;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const fromUsageAttr = coerceTokenUsage(obj["usage"]);
|
|
851
|
+
if (fromUsageAttr !== undefined)
|
|
852
|
+
return fromUsageAttr;
|
|
853
|
+
const metadata = obj["metadata"];
|
|
854
|
+
if (isRecord(metadata)) {
|
|
855
|
+
return coerceTokenUsage(metadata["usage"]);
|
|
856
|
+
}
|
|
857
|
+
return undefined;
|
|
858
|
+
}
|
|
859
|
+
catch {
|
|
860
|
+
return undefined;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
/** Convert the normalized runner result to sdk-core's LLMResponse shape. */
|
|
864
|
+
toResponse() {
|
|
865
|
+
let metadata = { ...this.metadata };
|
|
866
|
+
if (this.usage !== undefined && Object.keys(metadata).length === 0) {
|
|
867
|
+
// Mirror Python: dump usage (excluding undefined) into metadata when
|
|
868
|
+
// metadata was otherwise empty.
|
|
869
|
+
const dump = {};
|
|
870
|
+
if (this.usage.inputTokens !== undefined)
|
|
871
|
+
dump["input_tokens"] = this.usage.inputTokens;
|
|
872
|
+
if (this.usage.outputTokens !== undefined)
|
|
873
|
+
dump["output_tokens"] = this.usage.outputTokens;
|
|
874
|
+
if (this.usage.promptTokens !== undefined)
|
|
875
|
+
dump["prompt_tokens"] = this.usage.promptTokens;
|
|
876
|
+
if (this.usage.completionTokens !== undefined)
|
|
877
|
+
dump["completion_tokens"] = this.usage.completionTokens;
|
|
878
|
+
if (this.usage.totalTokens !== undefined)
|
|
879
|
+
dump["total_tokens"] = this.usage.totalTokens;
|
|
880
|
+
if (this.usage.model !== undefined)
|
|
881
|
+
dump["model"] = this.usage.model;
|
|
882
|
+
metadata = dump;
|
|
883
|
+
}
|
|
884
|
+
return new LLMResponse({
|
|
885
|
+
content: this.content,
|
|
886
|
+
toolCalls: this.toolCalls.length > 0 ? this.toolCalls : undefined,
|
|
887
|
+
metadata,
|
|
888
|
+
usage: this.usage,
|
|
889
|
+
id: this.id,
|
|
890
|
+
name: this.name,
|
|
891
|
+
additionalKwargs: this.additionalKwargs,
|
|
892
|
+
responseMetadata: this.responseMetadata,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function isRecord(value) {
|
|
897
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
898
|
+
}
|
|
899
|
+
function usageInt(value) {
|
|
900
|
+
if (typeof value === "boolean" || value == null)
|
|
901
|
+
return undefined;
|
|
902
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0)
|
|
903
|
+
return Math.trunc(value);
|
|
904
|
+
return undefined;
|
|
905
|
+
}
|
|
906
|
+
function hasTokenCounts(usage) {
|
|
907
|
+
return (usage.inputTokens !== undefined ||
|
|
908
|
+
usage.outputTokens !== undefined ||
|
|
909
|
+
usage.promptTokens !== undefined ||
|
|
910
|
+
usage.completionTokens !== undefined ||
|
|
911
|
+
usage.totalTokens !== undefined);
|
|
912
|
+
}
|
|
913
|
+
function promptOf(usage) {
|
|
914
|
+
return usage.inputTokens ?? usage.promptTokens;
|
|
915
|
+
}
|
|
916
|
+
function completionOf(usage) {
|
|
917
|
+
return usage.outputTokens ?? usage.completionTokens;
|
|
918
|
+
}
|
|
919
|
+
/** Coerce a provider usage blob into LLMTokenUsage, or undefined. */
|
|
920
|
+
export function coerceTokenUsage(value) {
|
|
921
|
+
try {
|
|
922
|
+
return coerceTokenUsageInner(value);
|
|
923
|
+
}
|
|
924
|
+
catch {
|
|
925
|
+
return undefined;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
function readUsageInt(rec, ...keys) {
|
|
929
|
+
for (const key of keys) {
|
|
930
|
+
try {
|
|
931
|
+
const parsed = usageInt(rec[key]);
|
|
932
|
+
if (parsed !== undefined)
|
|
933
|
+
return parsed;
|
|
934
|
+
}
|
|
935
|
+
catch {
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return undefined;
|
|
940
|
+
}
|
|
941
|
+
function coerceTokenUsageInner(value) {
|
|
942
|
+
if (value == null)
|
|
943
|
+
return undefined;
|
|
944
|
+
if (value instanceof LLMTokenUsage) {
|
|
945
|
+
return hasTokenCounts(value) ? value : undefined;
|
|
946
|
+
}
|
|
947
|
+
if (typeof value !== "object" || Array.isArray(value))
|
|
948
|
+
return undefined;
|
|
949
|
+
const rec = value;
|
|
950
|
+
const data = {};
|
|
951
|
+
const input = readUsageInt(rec, "input_tokens", "inputTokens");
|
|
952
|
+
const output = readUsageInt(rec, "output_tokens", "outputTokens");
|
|
953
|
+
const prompt = readUsageInt(rec, "prompt_tokens", "promptTokens");
|
|
954
|
+
const completion = readUsageInt(rec, "completion_tokens", "completionTokens");
|
|
955
|
+
const total = readUsageInt(rec, "total_tokens", "totalTokens");
|
|
956
|
+
if (input !== undefined)
|
|
957
|
+
data.input_tokens = input;
|
|
958
|
+
if (output !== undefined)
|
|
959
|
+
data.output_tokens = output;
|
|
960
|
+
if (prompt !== undefined)
|
|
961
|
+
data.prompt_tokens = prompt;
|
|
962
|
+
if (completion !== undefined)
|
|
963
|
+
data.completion_tokens = completion;
|
|
964
|
+
if (total !== undefined)
|
|
965
|
+
data.total_tokens = total;
|
|
966
|
+
try {
|
|
967
|
+
const model = rec["model"];
|
|
968
|
+
if (typeof model === "string" && model)
|
|
969
|
+
data.model = model;
|
|
970
|
+
}
|
|
971
|
+
catch {
|
|
972
|
+
// ignore
|
|
973
|
+
}
|
|
974
|
+
if (data.input_tokens === undefined &&
|
|
975
|
+
data.output_tokens === undefined &&
|
|
976
|
+
data.prompt_tokens === undefined &&
|
|
977
|
+
data.completion_tokens === undefined &&
|
|
978
|
+
data.total_tokens === undefined) {
|
|
979
|
+
return undefined;
|
|
980
|
+
}
|
|
981
|
+
return new LLMTokenUsage(data);
|
|
982
|
+
}
|
|
983
|
+
/** Merge stream usage field-by-field; incoming non-null fields win. */
|
|
984
|
+
export function mergeTokenUsage(existing, incoming) {
|
|
985
|
+
const next = incoming instanceof LLMTokenUsage && hasTokenCounts(incoming)
|
|
986
|
+
? incoming
|
|
987
|
+
: coerceTokenUsage(incoming);
|
|
988
|
+
if (next === undefined)
|
|
989
|
+
return existing;
|
|
990
|
+
if (existing === undefined)
|
|
991
|
+
return next;
|
|
992
|
+
const incomingPrompt = promptOf(next);
|
|
993
|
+
const incomingCompletion = completionOf(next);
|
|
994
|
+
const prompt = incomingPrompt ?? promptOf(existing);
|
|
995
|
+
const completion = incomingCompletion ?? completionOf(existing);
|
|
996
|
+
const model = next.model ?? existing.model;
|
|
997
|
+
let total = next.totalTokens ?? existing.totalTokens;
|
|
998
|
+
if (total === undefined && prompt !== undefined && completion !== undefined) {
|
|
999
|
+
total = prompt + completion;
|
|
1000
|
+
}
|
|
1001
|
+
return new LLMTokenUsage({
|
|
1002
|
+
input_tokens: prompt,
|
|
1003
|
+
output_tokens: completion,
|
|
1004
|
+
total_tokens: total,
|
|
1005
|
+
model,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
const JSON_SAFE_DEPTH = 8;
|
|
1009
|
+
const USAGE_META_KEYS = new Set(["usage", "token_usage"]);
|
|
1010
|
+
function usageJson(usage) {
|
|
1011
|
+
return usage.toJSON();
|
|
1012
|
+
}
|
|
1013
|
+
function jsonSafeValue(value, depth) {
|
|
1014
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
1015
|
+
return value;
|
|
1016
|
+
if (typeof value === "number")
|
|
1017
|
+
return Number.isFinite(value) ? value : undefined;
|
|
1018
|
+
if (value instanceof LLMTokenUsage)
|
|
1019
|
+
return usageJson(value);
|
|
1020
|
+
if (depth >= JSON_SAFE_DEPTH)
|
|
1021
|
+
return undefined;
|
|
1022
|
+
if (Array.isArray(value)) {
|
|
1023
|
+
const items = [];
|
|
1024
|
+
for (const item of value) {
|
|
1025
|
+
if (item === null) {
|
|
1026
|
+
items.push(null);
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
const safe = jsonSafeValue(item, depth + 1);
|
|
1030
|
+
if (safe !== undefined)
|
|
1031
|
+
items.push(safe);
|
|
1032
|
+
}
|
|
1033
|
+
return items;
|
|
1034
|
+
}
|
|
1035
|
+
if (isRecord(value))
|
|
1036
|
+
return jsonSafeMetadata(value, depth);
|
|
1037
|
+
const coerced = coerceTokenUsage(value);
|
|
1038
|
+
return coerced !== undefined ? usageJson(coerced) : undefined;
|
|
1039
|
+
}
|
|
1040
|
+
/** Copy provider metadata into JSON-safe values, or undefined. */
|
|
1041
|
+
export function jsonSafeMetadata(value, depth = 0) {
|
|
1042
|
+
if (!isRecord(value) || Object.keys(value).length === 0)
|
|
1043
|
+
return undefined;
|
|
1044
|
+
if (depth >= JSON_SAFE_DEPTH)
|
|
1045
|
+
return undefined;
|
|
1046
|
+
const out = {};
|
|
1047
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
1048
|
+
if (USAGE_META_KEYS.has(key)) {
|
|
1049
|
+
const coerced = coerceTokenUsage(raw);
|
|
1050
|
+
if (coerced !== undefined)
|
|
1051
|
+
out[key] = usageJson(coerced);
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
if (raw === null) {
|
|
1055
|
+
out[key] = null;
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
const safe = jsonSafeValue(raw, depth + 1);
|
|
1059
|
+
if (safe !== undefined)
|
|
1060
|
+
out[key] = safe;
|
|
1061
|
+
}
|
|
1062
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
1063
|
+
}
|
|
1064
|
+
/** Sum LangChain-style additive usage deltas into a cumulative snapshot. */
|
|
1065
|
+
export function addTokenUsage(existing, incoming) {
|
|
1066
|
+
const next = incoming instanceof LLMTokenUsage && hasTokenCounts(incoming)
|
|
1067
|
+
? incoming
|
|
1068
|
+
: coerceTokenUsage(incoming);
|
|
1069
|
+
if (next === undefined)
|
|
1070
|
+
return existing;
|
|
1071
|
+
if (existing === undefined)
|
|
1072
|
+
return next;
|
|
1073
|
+
const prompt = (promptOf(existing) ?? 0) + (promptOf(next) ?? 0);
|
|
1074
|
+
const completion = (completionOf(existing) ?? 0) + (completionOf(next) ?? 0);
|
|
1075
|
+
const model = next.model ?? existing.model;
|
|
1076
|
+
let total;
|
|
1077
|
+
if (existing.totalTokens !== undefined || next.totalTokens !== undefined) {
|
|
1078
|
+
total = (existing.totalTokens ?? 0) + (next.totalTokens ?? 0);
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
total = prompt + completion;
|
|
1082
|
+
}
|
|
1083
|
+
return new LLMTokenUsage({
|
|
1084
|
+
input_tokens: prompt,
|
|
1085
|
+
output_tokens: completion,
|
|
1086
|
+
total_tokens: total,
|
|
1087
|
+
model,
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
function looksLikeUsageDelta(prior, incoming) {
|
|
1091
|
+
const pairs = [
|
|
1092
|
+
[promptOf(prior), promptOf(incoming)],
|
|
1093
|
+
[completionOf(prior), completionOf(incoming)],
|
|
1094
|
+
[prior.totalTokens, incoming.totalTokens],
|
|
1095
|
+
];
|
|
1096
|
+
return pairs.some(([prev, nxt]) => prev !== undefined && nxt !== undefined && nxt < prev);
|
|
1097
|
+
}
|
|
1098
|
+
/** Fold a stream chunk's usage into a running snapshot.
|
|
1099
|
+
*
|
|
1100
|
+
* Cumulative providers repeat growing totals (`10/1` then `10/2`);
|
|
1101
|
+
* last-wins merge keeps `10/2`. Additive providers zero-fill the
|
|
1102
|
+
* unchanged side (`18/1` then `0/4`); those deltas are summed.
|
|
1103
|
+
*/
|
|
1104
|
+
export function accumulateStreamUsage(existing, incoming) {
|
|
1105
|
+
const next = incoming instanceof LLMTokenUsage && hasTokenCounts(incoming)
|
|
1106
|
+
? incoming
|
|
1107
|
+
: coerceTokenUsage(incoming);
|
|
1108
|
+
if (next === undefined)
|
|
1109
|
+
return existing;
|
|
1110
|
+
if (existing === undefined)
|
|
1111
|
+
return next;
|
|
1112
|
+
if (looksLikeUsageDelta(existing, next))
|
|
1113
|
+
return addTokenUsage(existing, next);
|
|
1114
|
+
return mergeTokenUsage(existing, next);
|
|
1115
|
+
}
|
|
1116
|
+
/** Runtime validation schema for LLMResult wire data. `content` is the only required field. */
|
|
1117
|
+
export const LLMResultSchema = z.looseObject({
|
|
1118
|
+
content: z.string().default(""),
|
|
1119
|
+
tool_calls: z.array(z.unknown()).default([]),
|
|
1120
|
+
usage: LLMTokenUsageSchema.optional(),
|
|
1121
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
1122
|
+
id: z.string().optional(),
|
|
1123
|
+
name: z.string().optional(),
|
|
1124
|
+
additional_kwargs: z.record(z.string(), JsonValueSchema).optional(),
|
|
1125
|
+
response_metadata: z.record(z.string(), JsonValueSchema).optional(),
|
|
1126
|
+
});
|
|
1127
|
+
/**
|
|
1128
|
+
* Response from LLM invocation on a tool executor pod.
|
|
1129
|
+
*
|
|
1130
|
+
* Python `_sync_usage_with_result` is a model_validator that mutates
|
|
1131
|
+
* `result.usage` ↔ `usage`. TS `LLMResponse.usage` is readonly, so
|
|
1132
|
+
* mutation isn't possible; callers should use
|
|
1133
|
+
* `normalizeLLMPodInvokeResponse()` after parsing to sync the two fields
|
|
1134
|
+
* by constructing a fresh LLMResponse when needed.
|
|
1135
|
+
*/
|
|
1136
|
+
export const LLMPodInvokeResponseSchema = z.object({
|
|
1137
|
+
/** Execution status: success, error. */
|
|
1138
|
+
status: z.string(),
|
|
1139
|
+
/** LLM result with content, tool_calls, and usage. */
|
|
1140
|
+
result: LLMResponseSchema.optional(),
|
|
1141
|
+
/** Error message if failed. */
|
|
1142
|
+
error: z.string().optional(),
|
|
1143
|
+
/** Machine-readable failure classification (e.g. llm_credential_rejected). */
|
|
1144
|
+
error_code: z.string().optional(),
|
|
1145
|
+
/** Hostname/pod name where LLM was executed. */
|
|
1146
|
+
pod_name: z.string(),
|
|
1147
|
+
/** Execution duration in milliseconds. */
|
|
1148
|
+
duration_ms: z.number(),
|
|
1149
|
+
/** Token usage (input_tokens, output_tokens, total_tokens). */
|
|
1150
|
+
usage: LLMTokenUsageSchema.optional(),
|
|
1151
|
+
});
|
|
1152
|
+
/**
|
|
1153
|
+
* Sync `usage` and `result.usage` on a parsed LLMPodInvokeResponse,
|
|
1154
|
+
* mirroring Python's `_sync_usage_with_result` model_validator.
|
|
1155
|
+
*
|
|
1156
|
+
* - If `result.usage` is unset and `usage` is present, returns a new
|
|
1157
|
+
* response whose result carries the top-level usage.
|
|
1158
|
+
* - If `usage` is unset and `result.usage` is present, lifts that
|
|
1159
|
+
* `result.usage` to the top level.
|
|
1160
|
+
*
|
|
1161
|
+
* Returns the response (possibly with a freshly-built `result`/`usage`).
|
|
1162
|
+
*/
|
|
1163
|
+
export function normalizeLLMPodInvokeResponse(resp) {
|
|
1164
|
+
const resultObj = resp.result;
|
|
1165
|
+
const resultHasUsage = resultObj !== undefined &&
|
|
1166
|
+
isRecord(resultObj["usage"]) &&
|
|
1167
|
+
resultObj["usage"] !== null;
|
|
1168
|
+
let nextResult = resp.result;
|
|
1169
|
+
let nextUsage = resp.usage;
|
|
1170
|
+
if (resultObj !== undefined && !resultHasUsage && resp.usage !== undefined) {
|
|
1171
|
+
nextResult = {
|
|
1172
|
+
...resultObj,
|
|
1173
|
+
usage: resp.usage,
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
if (resp.usage === undefined && resultObj !== undefined && resultHasUsage) {
|
|
1177
|
+
nextUsage = resultObj["usage"];
|
|
1178
|
+
}
|
|
1179
|
+
return { ...resp, result: nextResult, usage: nextUsage };
|
|
1180
|
+
}
|
|
1181
|
+
/** SSE event emitted by a tool pod during invoke_llm streaming. */
|
|
1182
|
+
export const LLMPodStreamEventSchema = z.object({
|
|
1183
|
+
/** Generated text delta. */
|
|
1184
|
+
content: z.string().optional(),
|
|
1185
|
+
/** Partial tool-call deltas. */
|
|
1186
|
+
tool_call_chunks: z.array(ToolCallChunkSchema).optional(),
|
|
1187
|
+
/** Complete tool calls for compatibility with older stream senders. */
|
|
1188
|
+
tool_calls: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
1189
|
+
/** Provider message identifier. */
|
|
1190
|
+
id: z.string().optional(),
|
|
1191
|
+
/** Provider message name. */
|
|
1192
|
+
name: z.string().optional(),
|
|
1193
|
+
/** LangChain additional message kwargs. */
|
|
1194
|
+
additional_kwargs: z.record(z.string(), JsonValueSchema).optional(),
|
|
1195
|
+
/** LangChain response metadata. */
|
|
1196
|
+
response_metadata: z.record(z.string(), JsonValueSchema).optional(),
|
|
1197
|
+
/** Whether the stream is complete. */
|
|
1198
|
+
done: z.boolean().optional(),
|
|
1199
|
+
/** OE stopped this call on interrupt request; terminal, distinct from done/error. */
|
|
1200
|
+
interrupted: z.boolean().optional(),
|
|
1201
|
+
/** Error message if streaming failed. */
|
|
1202
|
+
error: z.string().optional(),
|
|
1203
|
+
/** Machine-readable failure classification (e.g. llm_credential_rejected). */
|
|
1204
|
+
error_code: z.string().optional(),
|
|
1205
|
+
/** When true with error, the same stream URL may be retried. */
|
|
1206
|
+
retryable: z.boolean().optional(),
|
|
1207
|
+
/** When set with retryable, wait this many milliseconds before retrying. */
|
|
1208
|
+
retry_after_ms: z.number().optional(),
|
|
1209
|
+
/** Hostname/pod name. */
|
|
1210
|
+
pod_name: z.string().optional(),
|
|
1211
|
+
/** Stream duration. */
|
|
1212
|
+
duration_ms: z.number().optional(),
|
|
1213
|
+
/** Final token usage. */
|
|
1214
|
+
usage: LLMTokenUsageSchema.optional(),
|
|
1215
|
+
});
|
|
1216
|
+
// =============================================================================
|
|
1217
|
+
// Executor Callback (AER → OE)
|
|
1218
|
+
// =============================================================================
|
|
1219
|
+
/** Callback from AER to OE when execution completes or suspends. */
|
|
1220
|
+
export const ExecutorCallbackRequestSchema = z.object({
|
|
1221
|
+
/** Execution identifier. */
|
|
1222
|
+
execution_id: z.string(),
|
|
1223
|
+
/** Status: COMPLETED, SUSPENDED, ERROR. */
|
|
1224
|
+
status: z.string(),
|
|
1225
|
+
/** Suspension generation expected by the originating dispatch. */
|
|
1226
|
+
suspend_generation: z
|
|
1227
|
+
.number()
|
|
1228
|
+
.int()
|
|
1229
|
+
.nonnegative()
|
|
1230
|
+
.nullable()
|
|
1231
|
+
.transform((value) => value ?? undefined)
|
|
1232
|
+
.optional(),
|
|
1233
|
+
/** Final result if completed. */
|
|
1234
|
+
result: z.unknown().optional(),
|
|
1235
|
+
/** Error message if failed. */
|
|
1236
|
+
error: z.string().optional(),
|
|
1237
|
+
/** Reason for suspension. */
|
|
1238
|
+
suspend_reason: z.string().optional(),
|
|
1239
|
+
suspend_context: z.record(z.string(), z.unknown()).optional(),
|
|
1240
|
+
interrupts: z.array(PendingInterruptSchema).optional(),
|
|
1241
|
+
resume_schema: z.record(z.string(), z.unknown()).optional(),
|
|
1242
|
+
/** Opaque framework-owned state returned by the selected adapter. */
|
|
1243
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
1244
|
+
});
|
|
1245
|
+
// =============================================================================
|
|
1246
|
+
// Resume (Client → OE)
|
|
1247
|
+
// =============================================================================
|
|
1248
|
+
/** Data provided by human reviewer when resuming a suspended execution. */
|
|
1249
|
+
export const HumanReviewDataSchema = z.object({
|
|
1250
|
+
/** Review decision: 'approved', 'rejected', or custom value. */
|
|
1251
|
+
decision: z.string(),
|
|
1252
|
+
/** Optional notes from the reviewer. */
|
|
1253
|
+
reviewer_notes: z.string().optional(),
|
|
1254
|
+
});
|
|
1255
|
+
/** Request to resume a suspended execution. */
|
|
1256
|
+
export const AgentResumeRequestSchema = z.object({
|
|
1257
|
+
/** Human review decision data (for human-in-the-loop resume). */
|
|
1258
|
+
human_review: HumanReviewDataSchema,
|
|
1259
|
+
/** Caller-provided custom headers. */
|
|
1260
|
+
custom_headers: z.record(z.string(), z.string()).optional(),
|
|
1261
|
+
});
|
|
1262
|
+
/** Response from resuming an execution. */
|
|
1263
|
+
export const AgentResumeResponseSchema = z.object({
|
|
1264
|
+
/** Execution identifier. */
|
|
1265
|
+
execution_id: z.string(),
|
|
1266
|
+
/** Execution status. */
|
|
1267
|
+
status: z.string(),
|
|
1268
|
+
});
|
|
1269
|
+
// =============================================================================
|
|
1270
|
+
// Execution Status Query
|
|
1271
|
+
// =============================================================================
|
|
1272
|
+
/** Response for execution status query. */
|
|
1273
|
+
export const ExecutionStatusResponseSchema = z.object({
|
|
1274
|
+
/** Execution identifier. */
|
|
1275
|
+
execution_id: z.string(),
|
|
1276
|
+
/** Current status. */
|
|
1277
|
+
status: ExecutionStatusSchema,
|
|
1278
|
+
/** Result if completed. */
|
|
1279
|
+
result: z.unknown().optional(),
|
|
1280
|
+
/** Error if failed. */
|
|
1281
|
+
error: z.string().optional(),
|
|
1282
|
+
/** Reason if suspended. */
|
|
1283
|
+
suspend_reason: z.string().optional(),
|
|
1284
|
+
/** Context if suspended. */
|
|
1285
|
+
suspend_context: z.record(z.string(), z.unknown()).optional(),
|
|
1286
|
+
/** Creation timestamp. ISO strings are coerced to Date, matching Pydantic. */
|
|
1287
|
+
created_at: z.coerce.date(),
|
|
1288
|
+
/** Last update timestamp. ISO strings are coerced to Date, matching Pydantic. */
|
|
1289
|
+
updated_at: z.coerce.date(),
|
|
1290
|
+
});
|
|
1291
|
+
// =============================================================================
|
|
1292
|
+
// Database Models
|
|
1293
|
+
// =============================================================================
|
|
1294
|
+
/** Execution record stored in TenantDB. */
|
|
1295
|
+
export const ExecutionSchema = z.object({
|
|
1296
|
+
/** Unique execution identifier. */
|
|
1297
|
+
id: z.string(),
|
|
1298
|
+
/** Current status. */
|
|
1299
|
+
status: ExecutionStatusSchema,
|
|
1300
|
+
/** Original user message. */
|
|
1301
|
+
message: z.string(),
|
|
1302
|
+
/** Final result. */
|
|
1303
|
+
result: z.unknown().optional(),
|
|
1304
|
+
/** Error message. */
|
|
1305
|
+
error: z.string().optional(),
|
|
1306
|
+
/** Reason for suspension. */
|
|
1307
|
+
suspend_reason: z.string().optional(),
|
|
1308
|
+
/** Context for resume. */
|
|
1309
|
+
suspend_context: z.record(z.string(), z.unknown()).optional(),
|
|
1310
|
+
// Multi-tenant context
|
|
1311
|
+
/** Session ID for trace correlation. */
|
|
1312
|
+
session_id: z.string().optional(),
|
|
1313
|
+
/** Organization ID for multi-tenant isolation. */
|
|
1314
|
+
org_id: z.string().optional(),
|
|
1315
|
+
/** User ID for personalization. */
|
|
1316
|
+
user_id: z.string().optional(),
|
|
1317
|
+
/** Workspace identifier for cost tracking. */
|
|
1318
|
+
workspace_id: z.string().optional(),
|
|
1319
|
+
/** Project ID for project-level scoping. */
|
|
1320
|
+
project_id: z.string().optional(),
|
|
1321
|
+
// Per-agent AER routing
|
|
1322
|
+
/** AER HTTP endpoint for this agent; overrides default AER_URL. */
|
|
1323
|
+
aer_url: z.string().optional(),
|
|
1324
|
+
/** Tool HTTP endpoint for this agent; overrides default TOOL_URL. */
|
|
1325
|
+
tool_url: z.string().optional(),
|
|
1326
|
+
// Timestamps. ISO strings are coerced to Date, matching Pydantic and the
|
|
1327
|
+
// other datetime fields in this file (ExecutionStatusResponse, NodeExecutionRequest).
|
|
1328
|
+
created_at: z.coerce.date().default(() => new Date()),
|
|
1329
|
+
updated_at: z.coerce.date().default(() => new Date()),
|
|
1330
|
+
});
|
|
1331
|
+
/**
|
|
1332
|
+
* Normalize a value (Date, ISO string, or null/undefined) to a UTC Date.
|
|
1333
|
+
*
|
|
1334
|
+
* MongoDB stores naive datetimes in UTC; this helper rehydrates them as
|
|
1335
|
+
* proper Date instances so callers don't mix string and Date types.
|
|
1336
|
+
*
|
|
1337
|
+
* Mirrors Python `Execution._ensure_utc`. JS `Date` has no naive/aware
|
|
1338
|
+
* distinction (every Date is internally UTC ms-since-epoch), so the TS
|
|
1339
|
+
* version exercises the same "normalize input to a canonical Date" intent
|
|
1340
|
+
* by coercing ISO strings and passing Date instances through unchanged.
|
|
1341
|
+
*/
|
|
1342
|
+
export function ensureUtc(value) {
|
|
1343
|
+
if (value === null || value === undefined)
|
|
1344
|
+
return undefined;
|
|
1345
|
+
if (value instanceof Date)
|
|
1346
|
+
return value;
|
|
1347
|
+
if (typeof value === "string") {
|
|
1348
|
+
const d = new Date(value);
|
|
1349
|
+
return Number.isNaN(d.getTime()) ? undefined : d;
|
|
1350
|
+
}
|
|
1351
|
+
return undefined;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Convert an Execution to a MongoDB persistence document.
|
|
1355
|
+
*
|
|
1356
|
+
* `updatedAt` defaults to now if omitted.
|
|
1357
|
+
*/
|
|
1358
|
+
export function executionToPersistenceDoc(exec, updatedAt) {
|
|
1359
|
+
return {
|
|
1360
|
+
execution_id: exec.id,
|
|
1361
|
+
status: exec.status,
|
|
1362
|
+
message: exec.message,
|
|
1363
|
+
session_id: exec.session_id,
|
|
1364
|
+
user_id: exec.user_id,
|
|
1365
|
+
org_id: exec.org_id,
|
|
1366
|
+
workspace_id: exec.workspace_id,
|
|
1367
|
+
project_id: exec.project_id,
|
|
1368
|
+
result: exec.result,
|
|
1369
|
+
error: exec.error,
|
|
1370
|
+
suspend_reason: exec.suspend_reason,
|
|
1371
|
+
suspend_context: exec.suspend_context,
|
|
1372
|
+
updated_at: updatedAt ?? new Date(),
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Rehydrate an Execution from a MongoDB document.
|
|
1377
|
+
*
|
|
1378
|
+
* Uses default values for Optional fields so documents created before
|
|
1379
|
+
* new fields were added still deserialize safely.
|
|
1380
|
+
*/
|
|
1381
|
+
export function executionFromPersistenceDoc(doc) {
|
|
1382
|
+
const now = new Date();
|
|
1383
|
+
return ExecutionSchema.parse({
|
|
1384
|
+
id: doc["execution_id"],
|
|
1385
|
+
status: doc["status"],
|
|
1386
|
+
message: doc["message"] ?? "",
|
|
1387
|
+
result: doc["result"],
|
|
1388
|
+
error: doc["error"],
|
|
1389
|
+
suspend_reason: doc["suspend_reason"],
|
|
1390
|
+
suspend_context: doc["suspend_context"],
|
|
1391
|
+
session_id: doc["session_id"],
|
|
1392
|
+
org_id: doc["org_id"],
|
|
1393
|
+
user_id: doc["user_id"],
|
|
1394
|
+
workspace_id: doc["workspace_id"],
|
|
1395
|
+
project_id: doc["project_id"],
|
|
1396
|
+
created_at: ensureUtc(doc["created_at"]) ?? ensureUtc(doc["updated_at"]) ?? now,
|
|
1397
|
+
updated_at: ensureUtc(doc["updated_at"]) ?? now,
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
/** Execution step record stored in TenantDB. */
|
|
1401
|
+
export const ExecutionStepSchema = z.object({
|
|
1402
|
+
/** Unique step identifier. */
|
|
1403
|
+
id: z.string(),
|
|
1404
|
+
/** Parent execution ID. */
|
|
1405
|
+
execution_id: z.string(),
|
|
1406
|
+
/** Step sequence number. */
|
|
1407
|
+
step_number: z.number().int(),
|
|
1408
|
+
/** Tool or operation name. */
|
|
1409
|
+
tool_name: z.string(),
|
|
1410
|
+
/** Arguments passed. */
|
|
1411
|
+
arguments: z.record(z.string(), z.unknown()),
|
|
1412
|
+
/** Step status: pending, success, error. */
|
|
1413
|
+
status: z.string(),
|
|
1414
|
+
/** Step result. */
|
|
1415
|
+
result: z.unknown().optional(),
|
|
1416
|
+
/** Error message. */
|
|
1417
|
+
error: z.string().optional(),
|
|
1418
|
+
/** Execution duration. */
|
|
1419
|
+
duration_ms: z.number().optional(),
|
|
1420
|
+
/** ISO strings are coerced to Date, matching Pydantic. */
|
|
1421
|
+
timestamp: z.coerce.date().default(() => new Date()),
|
|
1422
|
+
});
|
|
1423
|
+
/**
|
|
1424
|
+
* Reconstruct an ExecutionStep from an execution_logs MongoDB document.
|
|
1425
|
+
*
|
|
1426
|
+
* The execution_logs collection stores tool start/result events with
|
|
1427
|
+
* fields: execution_id, step_number, tool, inputs, status, output,
|
|
1428
|
+
* error, duration_ms, timestamp. This maps those fields back to the
|
|
1429
|
+
* ExecutionStep model for step-cache rehydration after OE restart.
|
|
1430
|
+
*/
|
|
1431
|
+
export function executionStepFromLogDoc(doc) {
|
|
1432
|
+
return ExecutionStepSchema.parse({
|
|
1433
|
+
id: doc["id"] ?? crypto.randomUUID(),
|
|
1434
|
+
execution_id: doc["execution_id"],
|
|
1435
|
+
step_number: doc["step_number"] ?? 0,
|
|
1436
|
+
tool_name: doc["tool"] ?? "",
|
|
1437
|
+
arguments: doc["inputs"] ?? {},
|
|
1438
|
+
status: doc["status"] ?? "error",
|
|
1439
|
+
result: doc["output"],
|
|
1440
|
+
error: doc["error"],
|
|
1441
|
+
duration_ms: doc["duration_ms"],
|
|
1442
|
+
timestamp: ensureUtc(doc["timestamp"]) ?? new Date(),
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
// =============================================================================
|
|
1446
|
+
// Health Check
|
|
1447
|
+
// =============================================================================
|
|
1448
|
+
/** Health status of a component. */
|
|
1449
|
+
export const HealthStatusSchema = z.enum(["healthy", "unhealthy", "degraded"]);
|
|
1450
|
+
export const HealthStatus = {
|
|
1451
|
+
HEALTHY: "healthy",
|
|
1452
|
+
UNHEALTHY: "unhealthy",
|
|
1453
|
+
DEGRADED: "degraded",
|
|
1454
|
+
};
|
|
1455
|
+
/** Health check response. */
|
|
1456
|
+
export const HealthResponseSchema = z.object({
|
|
1457
|
+
/** Health status. */
|
|
1458
|
+
status: HealthStatusSchema,
|
|
1459
|
+
/** Component name. */
|
|
1460
|
+
component: z.string(),
|
|
1461
|
+
/** Runtime mode. */
|
|
1462
|
+
mode: z.string(),
|
|
1463
|
+
/** Component version. */
|
|
1464
|
+
version: z.string().default("1.0.0"),
|
|
1465
|
+
/** Additional details. */
|
|
1466
|
+
details: z.record(z.string(), z.unknown()).default({}),
|
|
1467
|
+
});
|
|
1468
|
+
// =============================================================================
|
|
1469
|
+
// Tool Definition
|
|
1470
|
+
// =============================================================================
|
|
1471
|
+
/** Definition of a registered tool. */
|
|
1472
|
+
export const ToolDefinitionSchema = z.object({
|
|
1473
|
+
/** Tool name. */
|
|
1474
|
+
name: z.string(),
|
|
1475
|
+
/** Tool description. */
|
|
1476
|
+
description: z.string().default(""),
|
|
1477
|
+
/** Whether tool runs locally. */
|
|
1478
|
+
is_local: z.boolean().default(true),
|
|
1479
|
+
/** Credential provider type for delegated auth. */
|
|
1480
|
+
provider_type: z.string().nullish(),
|
|
1481
|
+
/** Requested OAuth scopes for delegated auth. */
|
|
1482
|
+
scopes: z.array(z.string()).default([]),
|
|
1483
|
+
/** JSON schema for parameters. */
|
|
1484
|
+
parameters: z.record(z.string(), z.unknown()).optional(),
|
|
1485
|
+
});
|
|
1486
|
+
// =============================================================================
|
|
1487
|
+
// AER Route Response Models
|
|
1488
|
+
// =============================================================================
|
|
1489
|
+
/**
|
|
1490
|
+
* Response from AER /execute endpoint.
|
|
1491
|
+
*
|
|
1492
|
+
* Returned on both normal completion and HITL suspension.
|
|
1493
|
+
*/
|
|
1494
|
+
export const AERExecuteResponseSchema = z.object({
|
|
1495
|
+
/** Execution outcome: 'completed' or 'suspended'. */
|
|
1496
|
+
status: z.string(),
|
|
1497
|
+
/** Final agent response text (present when status is 'completed'). */
|
|
1498
|
+
result: z.string().optional(),
|
|
1499
|
+
/** Why the agent suspended (present when status is 'suspended'). */
|
|
1500
|
+
suspend_reason: z.string().optional(),
|
|
1501
|
+
});
|
|
1502
|
+
/** Response from /tools endpoint listing registered tools. */
|
|
1503
|
+
export const ToolsListResponseSchema = z.object({
|
|
1504
|
+
/** Registered tool definitions with name and metadata. */
|
|
1505
|
+
tools: z.array(z.record(z.string(), z.unknown())),
|
|
1506
|
+
/** Number of registered tools (included by Tool Pod). */
|
|
1507
|
+
count: z.number().int().optional(),
|
|
1508
|
+
});
|
|
1509
|
+
// =============================================================================
|
|
1510
|
+
// Streaming
|
|
1511
|
+
// =============================================================================
|
|
1512
|
+
/**
|
|
1513
|
+
* A chunk of streaming response from the agent.
|
|
1514
|
+
*
|
|
1515
|
+
* Used for real-time streaming of agent responses via SSE or gRPC.
|
|
1516
|
+
*/
|
|
1517
|
+
export const StreamChunkSchema = z.object({
|
|
1518
|
+
/**
|
|
1519
|
+
* Chunk type string. AER-emitted values are defined in
|
|
1520
|
+
* `server/chunk_types` ('text', 'done', 'error'). OE may inject
|
|
1521
|
+
* additional infrastructure types (e.g. 'metadata', 'tool_call',
|
|
1522
|
+
* 'tool_result') before forwarding to clients.
|
|
1523
|
+
*/
|
|
1524
|
+
chunk_type: z.string(),
|
|
1525
|
+
/** Content of the chunk. */
|
|
1526
|
+
content: z.string().default(""),
|
|
1527
|
+
/** Optional metadata. */
|
|
1528
|
+
metadata: z.record(z.string(), z.string()).default({}),
|
|
1529
|
+
/** Error message if chunk_type is 'error'. */
|
|
1530
|
+
error: z.string().optional(),
|
|
1531
|
+
/** Machine-readable error code if chunk_type is 'error' (e.g. AGENT_UNAVAILABLE). */
|
|
1532
|
+
code: z.string().optional(),
|
|
1533
|
+
// Tool-specific fields
|
|
1534
|
+
/** Tool name for tool_call/tool_result chunks. */
|
|
1535
|
+
tool_name: z.string().optional(),
|
|
1536
|
+
/** Tool call ID. */
|
|
1537
|
+
tool_call_id: z.string().optional(),
|
|
1538
|
+
// Execution context
|
|
1539
|
+
/** Execution ID. */
|
|
1540
|
+
execution_id: z.string().optional(),
|
|
1541
|
+
/** Step number in execution. */
|
|
1542
|
+
step_number: z.number().int().optional(),
|
|
1543
|
+
});
|
|
1544
|
+
/** Request to start an agent execution with streaming response. */
|
|
1545
|
+
export const AgentStartStreamRequestSchema = z.object({
|
|
1546
|
+
/** User message. */
|
|
1547
|
+
message: z.string(),
|
|
1548
|
+
/** Session ID for conversation continuity. */
|
|
1549
|
+
session_id: z.string().optional(),
|
|
1550
|
+
/** Organization ID for multi-tenant isolation. */
|
|
1551
|
+
org_id: z.string().optional(),
|
|
1552
|
+
/** User ID for personalization. */
|
|
1553
|
+
user_id: z.string().optional(),
|
|
1554
|
+
});
|
|
1555
|
+
// =============================================================================
|
|
1556
|
+
// Query Response Models — Execution Logs & Node Executions
|
|
1557
|
+
// =============================================================================
|
|
1558
|
+
/** Response for execution logs query (used by API Gateway proxy). */
|
|
1559
|
+
export const ExecutionLogsQueryResponseSchema = z.object({
|
|
1560
|
+
logs: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
1561
|
+
count: z.number().int().default(0),
|
|
1562
|
+
});
|
|
1563
|
+
/** Response for node executions query (used by API Gateway proxy). */
|
|
1564
|
+
export const NodeExecutionsQueryResponseSchema = z.object({
|
|
1565
|
+
executions: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
1566
|
+
count: z.number().int().default(0),
|
|
1567
|
+
});
|
|
1568
|
+
// =============================================================================
|
|
1569
|
+
// Query Response Models — Sessions
|
|
1570
|
+
// =============================================================================
|
|
1571
|
+
/** A single session entry returned by /query/sessions. */
|
|
1572
|
+
export const SessionInfoSchema = z.object({
|
|
1573
|
+
session_id: z.string(),
|
|
1574
|
+
last_activity: z.string(),
|
|
1575
|
+
created_at: z.string(),
|
|
1576
|
+
message_count: z.number().int().default(0),
|
|
1577
|
+
last_message_preview: z.string().default(""),
|
|
1578
|
+
visibility: z.string().default("PRIVATE"),
|
|
1579
|
+
user_id: z.string().default(""),
|
|
1580
|
+
project_id: z.string().default(""),
|
|
1581
|
+
workspace_id: z.string().default(""),
|
|
1582
|
+
});
|
|
1583
|
+
/** Response for sessions list query (used by API Gateway proxy). */
|
|
1584
|
+
export const SessionsQueryResponseSchema = z.object({
|
|
1585
|
+
sessions: z.array(SessionInfoSchema).default([]),
|
|
1586
|
+
total_count: z.number().int().default(0),
|
|
1587
|
+
offset: z.number().int().default(0),
|
|
1588
|
+
limit: z.number().int().default(50),
|
|
1589
|
+
});
|
|
1590
|
+
/** A single message within a session. */
|
|
1591
|
+
export const SessionMessageSchema = z.object({
|
|
1592
|
+
id: z.string(),
|
|
1593
|
+
role: z.string(),
|
|
1594
|
+
content: z.string(),
|
|
1595
|
+
timestamp: z.string(),
|
|
1596
|
+
session_id: z.string(),
|
|
1597
|
+
name: z.string().optional(),
|
|
1598
|
+
tool_calls: z.array(LLMToolCallSchema).nullable().optional(),
|
|
1599
|
+
tool_call_id: z.string().nullable().optional(),
|
|
1600
|
+
});
|
|
1601
|
+
/** Response for session messages query (used by API Gateway proxy). */
|
|
1602
|
+
export const SessionMessagesQueryResponseSchema = z.object({
|
|
1603
|
+
messages: z.array(SessionMessageSchema).default([]),
|
|
1604
|
+
});
|
|
1605
|
+
// =============================================================================
|
|
1606
|
+
// Cost Dashboard Query Response Models
|
|
1607
|
+
// =============================================================================
|
|
1608
|
+
/** Aggregate cost metrics for the requested period. */
|
|
1609
|
+
export const CostSummarySchema = z.object({
|
|
1610
|
+
total_cost_usd: z.number().default(0.0),
|
|
1611
|
+
total_tokens: z.number().int().default(0),
|
|
1612
|
+
total_prompt_tokens: z.number().int().default(0),
|
|
1613
|
+
total_completion_tokens: z.number().int().default(0),
|
|
1614
|
+
total_llm_calls: z.number().int().default(0),
|
|
1615
|
+
unpriced_llm_calls: z.number().int().default(0),
|
|
1616
|
+
});
|
|
1617
|
+
/** Cost breakdown for a single workspace. */
|
|
1618
|
+
export const CostByWorkspaceSchema = z.object({
|
|
1619
|
+
workspace_id: z.string(),
|
|
1620
|
+
total_cost_usd: z.number().default(0.0),
|
|
1621
|
+
total_tokens: z.number().int().default(0),
|
|
1622
|
+
call_count: z.number().int().default(0),
|
|
1623
|
+
percentage: z.number().default(0.0),
|
|
1624
|
+
});
|
|
1625
|
+
/** Cost breakdown for a single model. */
|
|
1626
|
+
export const CostByModelSchema = z.object({
|
|
1627
|
+
model: z.string(),
|
|
1628
|
+
total_cost_usd: z.number().default(0.0),
|
|
1629
|
+
total_tokens: z.number().int().default(0),
|
|
1630
|
+
call_count: z.number().int().default(0),
|
|
1631
|
+
percentage: z.number().default(0.0),
|
|
1632
|
+
});
|
|
1633
|
+
/** Cost data for a single day. */
|
|
1634
|
+
export const DailyCostEntrySchema = z.object({
|
|
1635
|
+
date: z.string(),
|
|
1636
|
+
total_cost_usd: z.number().default(0.0),
|
|
1637
|
+
total_tokens: z.number().int().default(0),
|
|
1638
|
+
call_count: z.number().int().default(0),
|
|
1639
|
+
});
|
|
1640
|
+
/** Response for cost dashboard aggregation (used by API Gateway proxy). */
|
|
1641
|
+
export const CostDashboardResponseSchema = z.object({
|
|
1642
|
+
summary: CostSummarySchema.default({
|
|
1643
|
+
total_cost_usd: 0.0,
|
|
1644
|
+
total_tokens: 0,
|
|
1645
|
+
total_prompt_tokens: 0,
|
|
1646
|
+
total_completion_tokens: 0,
|
|
1647
|
+
total_llm_calls: 0,
|
|
1648
|
+
unpriced_llm_calls: 0,
|
|
1649
|
+
}),
|
|
1650
|
+
by_workspace: z.array(CostByWorkspaceSchema).default([]),
|
|
1651
|
+
by_model: z.array(CostByModelSchema).default([]),
|
|
1652
|
+
daily_trend: z.array(DailyCostEntrySchema).default([]),
|
|
1653
|
+
});
|
|
1654
|
+
// =============================================================================
|
|
1655
|
+
// Executions Query Response Models
|
|
1656
|
+
// =============================================================================
|
|
1657
|
+
/** An execution document as stored in the platform database. */
|
|
1658
|
+
export const ExecutionDocumentSchema = z.object({
|
|
1659
|
+
execution_id: z.string(),
|
|
1660
|
+
status: z.string().default(""),
|
|
1661
|
+
message: z.string().default(""),
|
|
1662
|
+
session_id: z.string().default(""),
|
|
1663
|
+
user_id: z.string().default(""),
|
|
1664
|
+
org_id: z.string().default(""),
|
|
1665
|
+
project_id: z.string().nullable().default(""),
|
|
1666
|
+
workspace_id: z.string().nullable().default(""),
|
|
1667
|
+
result: z.unknown().optional(),
|
|
1668
|
+
error: z.string().optional(),
|
|
1669
|
+
suspend_reason: z.string().optional(),
|
|
1670
|
+
suspend_context: z.record(z.string(), z.unknown()).optional(),
|
|
1671
|
+
created_at: z.string().optional(),
|
|
1672
|
+
updated_at: z.string().optional(),
|
|
1673
|
+
});
|
|
1674
|
+
/** Response for executions list query (used by API Gateway proxy). */
|
|
1675
|
+
export const ExecutionsListQueryResponseSchema = z.object({
|
|
1676
|
+
success: z.boolean().default(true),
|
|
1677
|
+
executions: z.array(ExecutionDocumentSchema).default([]),
|
|
1678
|
+
count: z.number().int().default(0),
|
|
1679
|
+
});
|
|
1680
|
+
/** Response for single execution detail query (used by API Gateway proxy). */
|
|
1681
|
+
export const ExecutionDetailQueryResponseSchema = z.object({
|
|
1682
|
+
success: z.boolean().default(true),
|
|
1683
|
+
execution: ExecutionDocumentSchema.optional(),
|
|
1684
|
+
error: z.string().optional(),
|
|
1685
|
+
});
|
|
1686
|
+
// =============================================================================
|
|
1687
|
+
// Node Execution (AER → OE)
|
|
1688
|
+
// =============================================================================
|
|
1689
|
+
/**
|
|
1690
|
+
* Report node execution event (AER → OE for logging).
|
|
1691
|
+
*/
|
|
1692
|
+
export const NodeExecutionRequestSchema = z.object({
|
|
1693
|
+
/** Execution identifier. */
|
|
1694
|
+
execution_id: z.string(),
|
|
1695
|
+
/** Name of the framework node. */
|
|
1696
|
+
node_name: z.string(),
|
|
1697
|
+
/** Node status: started, success, error, suspend. */
|
|
1698
|
+
status: z.string(),
|
|
1699
|
+
/** Event timestamp. ISO strings are coerced to Date, matching Pydantic. */
|
|
1700
|
+
timestamp: z.coerce.date(),
|
|
1701
|
+
/** Framework run ID for this node execution. */
|
|
1702
|
+
run_id: z.string(),
|
|
1703
|
+
/** Parent run ID if nested. */
|
|
1704
|
+
parent_run_id: z.string().optional(),
|
|
1705
|
+
/** Session ID for correlation. */
|
|
1706
|
+
session_id: z.string().optional(),
|
|
1707
|
+
/** User ID for personalization. */
|
|
1708
|
+
user_id: z.string().optional(),
|
|
1709
|
+
/** Node inputs (for started status). */
|
|
1710
|
+
inputs: z.record(z.string(), z.unknown()).optional(),
|
|
1711
|
+
/** Node outputs (for success status). */
|
|
1712
|
+
outputs: z.record(z.string(), z.unknown()).optional(),
|
|
1713
|
+
/** Error message (for error status). */
|
|
1714
|
+
error: z.string().optional(),
|
|
1715
|
+
/** Execution duration in milliseconds. */
|
|
1716
|
+
duration_ms: z.number().optional(),
|
|
1717
|
+
/** Organization ID. */
|
|
1718
|
+
org_id: z.string().optional(),
|
|
1719
|
+
/** Project ID. */
|
|
1720
|
+
project_id: z.string().optional(),
|
|
1721
|
+
/** Active OTel trace ID, for OE execution-log correlation. */
|
|
1722
|
+
trace_id: z.string().nullish(),
|
|
1723
|
+
/** Active OTel span ID, for OE execution-log correlation. */
|
|
1724
|
+
span_id: z.string().nullish(),
|
|
1725
|
+
});
|
|
1726
|
+
export { LLMResponse, LLMTokenUsage, LLMToolCall, LLMToolSchema, LLMInvocationOptions, };
|