@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.
Files changed (220) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/LICENSE.md +201 -0
  3. package/README.md +29 -0
  4. package/dist/agent_config.d.ts +167 -0
  5. package/dist/agent_config.d.ts.map +1 -0
  6. package/dist/agent_config.js +544 -0
  7. package/dist/call_interrupted.d.ts +12 -0
  8. package/dist/call_interrupted.d.ts.map +1 -0
  9. package/dist/call_interrupted.js +11 -0
  10. package/dist/checkpoint_workspace.d.ts +25 -0
  11. package/dist/checkpoint_workspace.d.ts.map +1 -0
  12. package/dist/checkpoint_workspace.js +44 -0
  13. package/dist/context.d.ts +235 -0
  14. package/dist/context.d.ts.map +1 -0
  15. package/dist/context.js +322 -0
  16. package/dist/db_config.d.ts +28 -0
  17. package/dist/db_config.d.ts.map +1 -0
  18. package/dist/db_config.js +66 -0
  19. package/dist/db_naming.d.ts +54 -0
  20. package/dist/db_naming.d.ts.map +1 -0
  21. package/dist/db_naming.js +94 -0
  22. package/dist/error_reporting.d.ts +67 -0
  23. package/dist/error_reporting.d.ts.map +1 -0
  24. package/dist/error_reporting.js +311 -0
  25. package/dist/generated/workflow/v1/activity_pb.d.ts +342 -0
  26. package/dist/generated/workflow/v1/activity_pb.d.ts.map +1 -0
  27. package/dist/generated/workflow/v1/activity_pb.js +115 -0
  28. package/dist/generated/workflow/v1/common_pb.d.ts +184 -0
  29. package/dist/generated/workflow/v1/common_pb.d.ts.map +1 -0
  30. package/dist/generated/workflow/v1/common_pb.js +86 -0
  31. package/dist/generated/workflow/v1/runtime_pb.d.ts +200 -0
  32. package/dist/generated/workflow/v1/runtime_pb.d.ts.map +1 -0
  33. package/dist/generated/workflow/v1/runtime_pb.js +40 -0
  34. package/dist/generated/workflow/v1/state_pb.d.ts +254 -0
  35. package/dist/generated/workflow/v1/state_pb.d.ts.map +1 -0
  36. package/dist/generated/workflow/v1/state_pb.js +68 -0
  37. package/dist/guardrails_evaluator/core.d.ts +23 -0
  38. package/dist/guardrails_evaluator/core.d.ts.map +1 -0
  39. package/dist/guardrails_evaluator/core.js +122 -0
  40. package/dist/guardrails_evaluator/index.d.ts +10 -0
  41. package/dist/guardrails_evaluator/index.d.ts.map +1 -0
  42. package/dist/guardrails_evaluator/index.js +11 -0
  43. package/dist/guardrails_evaluator/regex.d.ts +20 -0
  44. package/dist/guardrails_evaluator/regex.d.ts.map +1 -0
  45. package/dist/guardrails_evaluator/regex.js +233 -0
  46. package/dist/hooks.d.ts +109 -0
  47. package/dist/hooks.d.ts.map +1 -0
  48. package/dist/hooks.js +216 -0
  49. package/dist/http_path.d.ts +18 -0
  50. package/dist/http_path.d.ts.map +1 -0
  51. package/dist/http_path.js +53 -0
  52. package/dist/index.d.ts +35 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +41 -0
  55. package/dist/launcher.d.ts +130 -0
  56. package/dist/launcher.d.ts.map +1 -0
  57. package/dist/launcher.js +325 -0
  58. package/dist/logger.d.ts +96 -0
  59. package/dist/logger.d.ts.map +1 -0
  60. package/dist/logger.js +204 -0
  61. package/dist/mcp_oauth.d.ts +51 -0
  62. package/dist/mcp_oauth.d.ts.map +1 -0
  63. package/dist/mcp_oauth.js +389 -0
  64. package/dist/mcp_oauth_secret.d.ts +21 -0
  65. package/dist/mcp_oauth_secret.d.ts.map +1 -0
  66. package/dist/mcp_oauth_secret.js +122 -0
  67. package/dist/mcp_tools.d.ts +71 -0
  68. package/dist/mcp_tools.d.ts.map +1 -0
  69. package/dist/mcp_tools.js +301 -0
  70. package/dist/memory_appbound.d.ts +42 -0
  71. package/dist/memory_appbound.d.ts.map +1 -0
  72. package/dist/memory_appbound.js +159 -0
  73. package/dist/memory_writer.d.ts +49 -0
  74. package/dist/memory_writer.d.ts.map +1 -0
  75. package/dist/memory_writer.js +171 -0
  76. package/dist/metrics.d.ts +84 -0
  77. package/dist/metrics.d.ts.map +1 -0
  78. package/dist/metrics.js +205 -0
  79. package/dist/models.d.ts +1458 -0
  80. package/dist/models.d.ts.map +1 -0
  81. package/dist/models.js +1726 -0
  82. package/dist/node_logger.d.ts +43 -0
  83. package/dist/node_logger.d.ts.map +1 -0
  84. package/dist/node_logger.js +158 -0
  85. package/dist/owner_callback.d.ts +16 -0
  86. package/dist/owner_callback.d.ts.map +1 -0
  87. package/dist/owner_callback.js +40 -0
  88. package/dist/progress.d.ts +57 -0
  89. package/dist/progress.d.ts.map +1 -0
  90. package/dist/progress.js +140 -0
  91. package/dist/runtime.d.ts +131 -0
  92. package/dist/runtime.d.ts.map +1 -0
  93. package/dist/runtime.js +351 -0
  94. package/dist/secure_llm_proxy.d.ts +115 -0
  95. package/dist/secure_llm_proxy.d.ts.map +1 -0
  96. package/dist/secure_llm_proxy.js +922 -0
  97. package/dist/secure_wrapper.d.ts +332 -0
  98. package/dist/secure_wrapper.d.ts.map +1 -0
  99. package/dist/secure_wrapper.js +1249 -0
  100. package/dist/server/aer.d.ts +61 -0
  101. package/dist/server/aer.d.ts.map +1 -0
  102. package/dist/server/aer.js +1124 -0
  103. package/dist/server/auth.d.ts +56 -0
  104. package/dist/server/auth.d.ts.map +1 -0
  105. package/dist/server/auth.js +132 -0
  106. package/dist/server/base.d.ts +104 -0
  107. package/dist/server/base.d.ts.map +1 -0
  108. package/dist/server/base.js +150 -0
  109. package/dist/server/callInterrupt.d.ts +49 -0
  110. package/dist/server/callInterrupt.d.ts.map +1 -0
  111. package/dist/server/callInterrupt.js +68 -0
  112. package/dist/server/callback_delivery.d.ts +14 -0
  113. package/dist/server/callback_delivery.d.ts.map +1 -0
  114. package/dist/server/callback_delivery.js +141 -0
  115. package/dist/server/chunk_types.d.ts +50 -0
  116. package/dist/server/chunk_types.d.ts.map +1 -0
  117. package/dist/server/chunk_types.js +62 -0
  118. package/dist/server/cors.d.ts +52 -0
  119. package/dist/server/cors.d.ts.map +1 -0
  120. package/dist/server/cors.js +107 -0
  121. package/dist/server/drain.d.ts +169 -0
  122. package/dist/server/drain.d.ts.map +1 -0
  123. package/dist/server/drain.js +455 -0
  124. package/dist/server/function.d.ts +77 -0
  125. package/dist/server/function.d.ts.map +1 -0
  126. package/dist/server/function.js +337 -0
  127. package/dist/server/http_retry.d.ts +37 -0
  128. package/dist/server/http_retry.d.ts.map +1 -0
  129. package/dist/server/http_retry.js +157 -0
  130. package/dist/server/index.d.ts +7 -0
  131. package/dist/server/index.d.ts.map +1 -0
  132. package/dist/server/index.js +5 -0
  133. package/dist/server/metadata.d.ts +50 -0
  134. package/dist/server/metadata.d.ts.map +1 -0
  135. package/dist/server/metadata.js +193 -0
  136. package/dist/server/oe_url.d.ts +36 -0
  137. package/dist/server/oe_url.d.ts.map +1 -0
  138. package/dist/server/oe_url.js +50 -0
  139. package/dist/server/owner_url.d.ts +35 -0
  140. package/dist/server/owner_url.d.ts.map +1 -0
  141. package/dist/server/owner_url.js +146 -0
  142. package/dist/server/query.d.ts +42 -0
  143. package/dist/server/query.d.ts.map +1 -0
  144. package/dist/server/query.js +28 -0
  145. package/dist/server/tool.d.ts +138 -0
  146. package/dist/server/tool.d.ts.map +1 -0
  147. package/dist/server/tool.js +1017 -0
  148. package/dist/span_names.d.ts +21 -0
  149. package/dist/span_names.d.ts.map +1 -0
  150. package/dist/span_names.js +31 -0
  151. package/dist/structured_logging/constants.d.ts +17 -0
  152. package/dist/structured_logging/constants.d.ts.map +1 -0
  153. package/dist/structured_logging/constants.js +71 -0
  154. package/dist/structured_logging/env.d.ts +18 -0
  155. package/dist/structured_logging/env.d.ts.map +1 -0
  156. package/dist/structured_logging/env.js +39 -0
  157. package/dist/structured_logging/install.d.ts +56 -0
  158. package/dist/structured_logging/install.d.ts.map +1 -0
  159. package/dist/structured_logging/install.js +107 -0
  160. package/dist/structured_logging/layout.d.ts +9 -0
  161. package/dist/structured_logging/layout.d.ts.map +1 -0
  162. package/dist/structured_logging/layout.js +144 -0
  163. package/dist/structured_logging/serialize.d.ts +27 -0
  164. package/dist/structured_logging/serialize.d.ts.map +1 -0
  165. package/dist/structured_logging/serialize.js +61 -0
  166. package/dist/structured_logging/stdio_capture.d.ts +59 -0
  167. package/dist/structured_logging/stdio_capture.d.ts.map +1 -0
  168. package/dist/structured_logging/stdio_capture.js +164 -0
  169. package/dist/structured_logging/uncaught.d.ts +14 -0
  170. package/dist/structured_logging/uncaught.d.ts.map +1 -0
  171. package/dist/structured_logging/uncaught.js +58 -0
  172. package/dist/structured_logging.d.ts +48 -0
  173. package/dist/structured_logging.d.ts.map +1 -0
  174. package/dist/structured_logging.js +47 -0
  175. package/dist/tls_client.d.ts +61 -0
  176. package/dist/tls_client.d.ts.map +1 -0
  177. package/dist/tls_client.js +298 -0
  178. package/dist/tool_api_error.d.ts +62 -0
  179. package/dist/tool_api_error.d.ts.map +1 -0
  180. package/dist/tool_api_error.js +399 -0
  181. package/dist/tool_memory_ownership.d.ts +10 -0
  182. package/dist/tool_memory_ownership.d.ts.map +1 -0
  183. package/dist/tool_memory_ownership.js +36 -0
  184. package/dist/toolpod_handlers.d.ts +126 -0
  185. package/dist/toolpod_handlers.d.ts.map +1 -0
  186. package/dist/toolpod_handlers.js +1016 -0
  187. package/dist/tracing/exporters.d.ts +51 -0
  188. package/dist/tracing/exporters.d.ts.map +1 -0
  189. package/dist/tracing/exporters.js +327 -0
  190. package/dist/tracing/index.d.ts +3 -0
  191. package/dist/tracing/index.d.ts.map +1 -0
  192. package/dist/tracing/index.js +2 -0
  193. package/dist/tracing/setup.d.ts +76 -0
  194. package/dist/tracing/setup.d.ts.map +1 -0
  195. package/dist/tracing/setup.js +436 -0
  196. package/dist/utils.d.ts +204 -0
  197. package/dist/utils.d.ts.map +1 -0
  198. package/dist/utils.js +867 -0
  199. package/dist/workflow/activity.d.ts +71 -0
  200. package/dist/workflow/activity.d.ts.map +1 -0
  201. package/dist/workflow/activity.js +357 -0
  202. package/dist/workflow/attempt.d.ts +12 -0
  203. package/dist/workflow/attempt.d.ts.map +1 -0
  204. package/dist/workflow/attempt.js +96 -0
  205. package/dist/workflow/client.d.ts +46 -0
  206. package/dist/workflow/client.d.ts.map +1 -0
  207. package/dist/workflow/client.js +299 -0
  208. package/dist/workflow/context.d.ts +37 -0
  209. package/dist/workflow/context.d.ts.map +1 -0
  210. package/dist/workflow/context.js +350 -0
  211. package/dist/workflow/heartbeat.d.ts +15 -0
  212. package/dist/workflow/heartbeat.d.ts.map +1 -0
  213. package/dist/workflow/heartbeat.js +78 -0
  214. package/dist/workflow/index.d.ts +14 -0
  215. package/dist/workflow/index.d.ts.map +1 -0
  216. package/dist/workflow/index.js +10 -0
  217. package/dist/workflow/memory.d.ts +17 -0
  218. package/dist/workflow/memory.d.ts.map +1 -0
  219. package/dist/workflow/memory.js +184 -0
  220. package/package.json +73 -0
@@ -0,0 +1,171 @@
1
+ /** Native-checkpoint conversation-turn ingestion for the TypeScript runner. */
2
+ import { randomUUID } from "node:crypto";
3
+ import { getLogger } from "./logger.js";
4
+ import { normalizeContent } from "./utils.js";
5
+ const logger = getLogger("agent_engine_runner_shared.memory_writer");
6
+ /**
7
+ * Extract only the current turn from framework messages.
8
+ *
9
+ * The original user prompt is input, so many LangGraph update streams never
10
+ * emit it. Prepend it explicitly, then skip through an echoed copy when one is
11
+ * present so prior checkpoint history and the current prompt are not written
12
+ * twice. Resume legs suppress every user message because they carry no new
13
+ * user prompt.
14
+ */
15
+ export function extractTurnMessages(message, resultMessages, includeUserTurn = true) {
16
+ const turns = [];
17
+ const normalizedMessage = normalizeContent(message);
18
+ if (includeUserTurn && message) {
19
+ turns.push({ role: "user", content: normalizedMessage });
20
+ }
21
+ let startIndex = 0;
22
+ for (let index = resultMessages.length - 1; index >= 0; index -= 1) {
23
+ const candidate = resultMessages[index];
24
+ if (candidate?.role === "user" &&
25
+ normalizeContent(candidate.content) === normalizedMessage) {
26
+ startIndex = index + 1;
27
+ break;
28
+ }
29
+ }
30
+ for (const resultMessage of resultMessages.slice(startIndex)) {
31
+ const content = normalizeContent(resultMessage.content);
32
+ if (resultMessage.role === "user") {
33
+ if (!includeUserTurn)
34
+ continue;
35
+ turns.push({ role: "user", content });
36
+ continue;
37
+ }
38
+ if (resultMessage.role === "assistant") {
39
+ const toolCalls = resultMessage.toolCalls?.map((toolCall) => ({
40
+ id: toolCall.id ?? "",
41
+ name: toolCall.name ?? "",
42
+ arguments: toolCall.args ?? {},
43
+ }));
44
+ if (!content && !toolCalls?.length)
45
+ continue;
46
+ turns.push({
47
+ role: "assistant",
48
+ content,
49
+ toolCalls: toolCalls ?? null,
50
+ });
51
+ continue;
52
+ }
53
+ if (resultMessage.role === "tool") {
54
+ const toolCallId = resultMessage.toolCallId ?? "";
55
+ turns.push({
56
+ role: "tool",
57
+ content,
58
+ toolCallId,
59
+ toolName: resultMessage.name ?? "",
60
+ isError: resultMessage.isError ?? false,
61
+ });
62
+ }
63
+ }
64
+ return turns;
65
+ }
66
+ function validateTurn(turn) {
67
+ if (turn.role === "user" && !turn.content) {
68
+ throw new Error("user memory turns require content");
69
+ }
70
+ if (turn.role === "tool" && !turn.content) {
71
+ throw new Error("tool memory turns require content");
72
+ }
73
+ if (turn.role === "tool" && !turn.toolCallId) {
74
+ throw new Error("tool memory turns require a tool call id");
75
+ }
76
+ }
77
+ /** Write one current-turn batch in message order. */
78
+ async function writeTurnToMemory(runtime, args) {
79
+ const sessionId = args.sessionId?.trim();
80
+ if (!sessionId) {
81
+ logger.warn("session_id required for memory write; skipping turn write");
82
+ return;
83
+ }
84
+ const userId = args.userId?.trim();
85
+ if (!userId) {
86
+ logger.warn("user_id not set; skipping turn write");
87
+ return;
88
+ }
89
+ const turns = extractTurnMessages(args.message, args.resultMessages, args.includeUserTurn ?? true);
90
+ for (const turn of turns) {
91
+ validateTurn(turn);
92
+ await runtime.recordTurn({
93
+ ...turn,
94
+ sessionId,
95
+ userId,
96
+ // The adapter retries transport failures. Keep one key for all attempts
97
+ // of this individual turn so a committed response loss cannot duplicate it.
98
+ idempotencyKey: randomUUID(),
99
+ });
100
+ }
101
+ }
102
+ /**
103
+ * Process-wide, non-blocking turn writer.
104
+ *
105
+ * Batches are serialized so concurrent executions cannot interleave messages
106
+ * within a conversation turn. Failures are logged and swallowed: Memory is a
107
+ * best-effort side effect and must not replace an otherwise successful agent
108
+ * result. `drain` lets session release and graceful app replacement wait for
109
+ * already-queued writes.
110
+ */
111
+ export class MemoryWriter {
112
+ runtime;
113
+ tail = Promise.resolve();
114
+ pending = 0;
115
+ closed = false;
116
+ constructor(runtime) {
117
+ this.runtime = runtime;
118
+ }
119
+ get pendingWrites() {
120
+ return this.pending;
121
+ }
122
+ writeTurnAsync(args) {
123
+ if (this.closed) {
124
+ logger.warn("MemoryWriter is shut down; skipping turn write");
125
+ return;
126
+ }
127
+ this.pending += 1;
128
+ this.tail = this.tail
129
+ .then(() => writeTurnToMemory(this.runtime, args))
130
+ .catch((error) => {
131
+ const kind = error instanceof Error ? error.name : typeof error;
132
+ logger.warn(`Failed to write turn to memory (${kind})`);
133
+ })
134
+ .finally(() => {
135
+ this.pending -= 1;
136
+ });
137
+ }
138
+ async drain(timeoutMs) {
139
+ const deadline = timeoutMs === undefined ? null : Date.now() + Math.max(0, timeoutMs);
140
+ while (this.pending > 0) {
141
+ const pending = this.tail;
142
+ if (deadline === null) {
143
+ await pending;
144
+ continue;
145
+ }
146
+ const remaining = deadline - Date.now();
147
+ if (remaining <= 0)
148
+ return false;
149
+ let timer;
150
+ const completed = await Promise.race([
151
+ pending.then(() => true),
152
+ new Promise((resolve) => {
153
+ timer = setTimeout(() => resolve(false), remaining);
154
+ }),
155
+ ]);
156
+ if (timer !== undefined)
157
+ clearTimeout(timer);
158
+ if (!completed)
159
+ return false;
160
+ }
161
+ return true;
162
+ }
163
+ async shutdown() {
164
+ if (this.closed) {
165
+ await this.drain();
166
+ return;
167
+ }
168
+ this.closed = true;
169
+ await this.drain();
170
+ }
171
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Metrics and observability for the Runner SDK.
3
+ *
4
+ * Ported from `agent_engine_runner_shared/metrics.py`. Provides:
5
+ * - `Metrics` class — process-singleton latency/error/request counters.
6
+ * - `recordLatency(operation, fn, labels?)` — async wrapper that measures duration.
7
+ * - `withMetrics(operation, fn, opts?)` — HOF replacement for the Python decorator.
8
+ * - `logToolCall`/`logToolResult`/`logLlmCall`/`logLlmResult`/`logExecutionEvent`
9
+ * structured-log helpers.
10
+ *
11
+ * Node is single-threaded per event loop, so no explicit lock is needed for
12
+ * the counter maps (the GIL did the same job in Python).
13
+ */
14
+ export interface LatencyStatsJson {
15
+ count: number;
16
+ total_ms: number;
17
+ min_ms: number;
18
+ max_ms: number;
19
+ avg_ms: number;
20
+ }
21
+ export declare class Metrics {
22
+ private static latencies;
23
+ private static errors;
24
+ private static requests;
25
+ static recordLatency(operation: string, durationMs: number, labels?: Record<string, string>): void;
26
+ static recordError(operation: string, labels?: Record<string, string>): void;
27
+ static getAll(): {
28
+ latencies: Record<string, LatencyStatsJson>;
29
+ errors: Record<string, number>;
30
+ requests: Record<string, number>;
31
+ };
32
+ static reset(): void;
33
+ }
34
+ /**
35
+ * Run `fn` and record its wall-clock duration as a latency metric.
36
+ * Errors propagate; their duration is still recorded.
37
+ */
38
+ export declare function recordLatency<T>(operation: string, fn: () => Promise<T> | T, labels?: Record<string, string>): Promise<T>;
39
+ /**
40
+ * Wrap a function to record latency (and optionally errors).
41
+ *
42
+ * Replacement for the Python `@with_metrics(...)` decorator. The returned
43
+ * function preserves the original signature and awaits any returned promise
44
+ * so async/sync code can use the same wrapper.
45
+ */
46
+ export declare function withMetrics<A extends unknown[], R>(operation: string, fn: (...args: A) => R | Promise<R>, opts?: {
47
+ recordErrors?: boolean;
48
+ }): (...args: A) => Promise<R>;
49
+ export declare function logToolCall(args: {
50
+ executionId: string;
51
+ stepNumber: number;
52
+ toolName: string;
53
+ argumentKeys: string[];
54
+ isLocal: boolean;
55
+ }): void;
56
+ export declare function logToolResult(args: {
57
+ executionId: string;
58
+ stepNumber: number;
59
+ toolName: string;
60
+ status: string;
61
+ durationMs: number;
62
+ error?: string | null;
63
+ }): void;
64
+ export declare function logLlmCall(args: {
65
+ executionId: string;
66
+ stepNumber: number;
67
+ modelName: string;
68
+ messageCount: number;
69
+ }): void;
70
+ export declare function logLlmResult(args: {
71
+ executionId: string;
72
+ stepNumber: number;
73
+ modelName: string;
74
+ status: string;
75
+ durationMs: number;
76
+ toolCalls?: string[] | null;
77
+ error?: string | null;
78
+ }): void;
79
+ export declare function logExecutionEvent(args: {
80
+ executionId: string;
81
+ event: string;
82
+ details?: Record<string, unknown>;
83
+ }): void;
84
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Metrics and observability for the Runner SDK.
3
+ *
4
+ * Ported from `agent_engine_runner_shared/metrics.py`. Provides:
5
+ * - `Metrics` class — process-singleton latency/error/request counters.
6
+ * - `recordLatency(operation, fn, labels?)` — async wrapper that measures duration.
7
+ * - `withMetrics(operation, fn, opts?)` — HOF replacement for the Python decorator.
8
+ * - `logToolCall`/`logToolResult`/`logLlmCall`/`logLlmResult`/`logExecutionEvent`
9
+ * structured-log helpers.
10
+ *
11
+ * Node is single-threaded per event loop, so no explicit lock is needed for
12
+ * the counter maps (the GIL did the same job in Python).
13
+ */
14
+ import { getLogger } from "./logger.js";
15
+ const logger = getLogger("agent_engine_runner_shared.metrics");
16
+ class LatencyStats {
17
+ count = 0;
18
+ totalMs = 0;
19
+ minMs = Number.POSITIVE_INFINITY;
20
+ maxMs = 0;
21
+ record(durationMs) {
22
+ this.count += 1;
23
+ this.totalMs += durationMs;
24
+ if (durationMs < this.minMs)
25
+ this.minMs = durationMs;
26
+ if (durationMs > this.maxMs)
27
+ this.maxMs = durationMs;
28
+ }
29
+ get avgMs() {
30
+ return this.count > 0 ? this.totalMs / this.count : 0;
31
+ }
32
+ toJSON() {
33
+ return {
34
+ count: this.count,
35
+ total_ms: round2(this.totalMs),
36
+ min_ms: this.minMs === Number.POSITIVE_INFINITY ? 0 : round2(this.minMs),
37
+ max_ms: round2(this.maxMs),
38
+ avg_ms: round2(this.avgMs),
39
+ };
40
+ }
41
+ }
42
+ function round2(n) {
43
+ return Math.round(n * 100) / 100;
44
+ }
45
+ function labelKey(operation, labels) {
46
+ const entries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b));
47
+ if (entries.length === 0)
48
+ return operation;
49
+ const labelStr = entries.map(([k, v]) => `${k}=${v}`).join(",");
50
+ return `${operation}:${labelStr}`;
51
+ }
52
+ export class Metrics {
53
+ static latencies = new Map();
54
+ static errors = new Map();
55
+ static requests = new Map();
56
+ static recordLatency(operation, durationMs, labels = {}) {
57
+ const key = labelKey(operation, labels);
58
+ let stats = this.latencies.get(key);
59
+ if (!stats) {
60
+ stats = new LatencyStats();
61
+ this.latencies.set(key, stats);
62
+ }
63
+ stats.record(durationMs);
64
+ this.requests.set(operation, (this.requests.get(operation) ?? 0) + 1);
65
+ logger.debug({
66
+ metric_type: "latency",
67
+ operation,
68
+ duration_ms: round2(durationMs),
69
+ ...labels,
70
+ }, "METRIC latency");
71
+ }
72
+ static recordError(operation, labels = {}) {
73
+ const key = labelKey(operation, labels);
74
+ this.errors.set(key, (this.errors.get(key) ?? 0) + 1);
75
+ logger.warn({
76
+ metric_type: "error",
77
+ operation,
78
+ ...labels,
79
+ }, "METRIC error");
80
+ }
81
+ static getAll() {
82
+ return {
83
+ latencies: Object.fromEntries(Array.from(this.latencies.entries()).map(([k, v]) => [k, v.toJSON()])),
84
+ errors: Object.fromEntries(this.errors),
85
+ requests: Object.fromEntries(this.requests),
86
+ };
87
+ }
88
+ static reset() {
89
+ this.latencies.clear();
90
+ this.errors.clear();
91
+ this.requests.clear();
92
+ }
93
+ }
94
+ /**
95
+ * Run `fn` and record its wall-clock duration as a latency metric.
96
+ * Errors propagate; their duration is still recorded.
97
+ */
98
+ export async function recordLatency(operation, fn, labels = {}) {
99
+ const start = performance.now();
100
+ try {
101
+ return await fn();
102
+ }
103
+ finally {
104
+ Metrics.recordLatency(operation, performance.now() - start, labels);
105
+ }
106
+ }
107
+ /**
108
+ * Wrap a function to record latency (and optionally errors).
109
+ *
110
+ * Replacement for the Python `@with_metrics(...)` decorator. The returned
111
+ * function preserves the original signature and awaits any returned promise
112
+ * so async/sync code can use the same wrapper.
113
+ */
114
+ export function withMetrics(operation, fn, opts = {}) {
115
+ const recordErrors = opts.recordErrors !== false;
116
+ return async (...args) => {
117
+ const start = performance.now();
118
+ try {
119
+ return await fn(...args);
120
+ }
121
+ catch (err) {
122
+ if (recordErrors)
123
+ Metrics.recordError(operation);
124
+ throw err;
125
+ }
126
+ finally {
127
+ Metrics.recordLatency(operation, performance.now() - start);
128
+ }
129
+ };
130
+ }
131
+ // =============================================================================
132
+ // Structured Logging Helpers
133
+ // =============================================================================
134
+ export function logToolCall(args) {
135
+ logger.info({
136
+ event_type: "tool_call",
137
+ execution_id: args.executionId,
138
+ step_number: args.stepNumber,
139
+ tool_name: args.toolName,
140
+ is_local: args.isLocal,
141
+ argument_keys: args.argumentKeys,
142
+ }, `TOOL_CALL step=${args.stepNumber} tool=${args.toolName}`);
143
+ }
144
+ export function logToolResult(args) {
145
+ const ok = args.status === "success" ||
146
+ args.status === "suspend" ||
147
+ args.status === "cached";
148
+ const fields = {
149
+ event_type: "tool_result",
150
+ execution_id: args.executionId,
151
+ step_number: args.stepNumber,
152
+ tool_name: args.toolName,
153
+ status: args.status,
154
+ duration_ms: round2(args.durationMs),
155
+ error: args.error ?? null,
156
+ };
157
+ const msg = `TOOL_RESULT step=${args.stepNumber} tool=${args.toolName} status=${args.status}`;
158
+ if (ok)
159
+ logger.info(fields, msg);
160
+ else
161
+ logger.error(fields, msg);
162
+ Metrics.recordLatency("tool_execution", args.durationMs, {
163
+ tool_name: args.toolName,
164
+ });
165
+ if (args.status === "error")
166
+ Metrics.recordError("tool_execution", { tool_name: args.toolName });
167
+ }
168
+ export function logLlmCall(args) {
169
+ logger.info({
170
+ event_type: "llm_call",
171
+ execution_id: args.executionId,
172
+ step_number: args.stepNumber,
173
+ model_name: args.modelName,
174
+ message_count: args.messageCount,
175
+ }, `LLM_CALL step=${args.stepNumber} model=${args.modelName}`);
176
+ }
177
+ export function logLlmResult(args) {
178
+ const fields = {
179
+ event_type: "llm_result",
180
+ execution_id: args.executionId,
181
+ step_number: args.stepNumber,
182
+ model_name: args.modelName,
183
+ status: args.status,
184
+ duration_ms: round2(args.durationMs),
185
+ tool_calls: args.toolCalls ?? null,
186
+ error: args.error ?? null,
187
+ };
188
+ const msg = `LLM_RESULT step=${args.stepNumber} model=${args.modelName} status=${args.status}`;
189
+ if (args.status === "success")
190
+ logger.info(fields, msg);
191
+ else
192
+ logger.error(fields, msg);
193
+ Metrics.recordLatency("llm_call", args.durationMs, {
194
+ model_name: args.modelName,
195
+ });
196
+ if (args.status === "error")
197
+ Metrics.recordError("llm_call", { model_name: args.modelName });
198
+ }
199
+ export function logExecutionEvent(args) {
200
+ logger.info({
201
+ event_type: `execution_${args.event.toLowerCase()}`,
202
+ execution_id: args.executionId,
203
+ ...(args.details ?? {}),
204
+ }, `EXECUTION_${args.event} execution_id=${args.executionId.slice(0, 8)}...`);
205
+ }