evlog 2.19.1 → 2.20.0

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 (190) hide show
  1. package/README.md +30 -1
  2. package/dist/adapters/axiom.d.mts +1 -1
  3. package/dist/adapters/axiom.mjs +2 -2
  4. package/dist/adapters/better-stack.d.mts +1 -1
  5. package/dist/adapters/better-stack.mjs +2 -2
  6. package/dist/adapters/datadog.d.mts +1 -1
  7. package/dist/adapters/datadog.mjs +2 -2
  8. package/dist/adapters/fs.d.mts +1 -1
  9. package/dist/adapters/fs.mjs +1 -1
  10. package/dist/adapters/hyperdx.d.mts +1 -1
  11. package/dist/adapters/hyperdx.mjs +1 -1
  12. package/dist/adapters/memory.d.mts +1 -1
  13. package/dist/adapters/memory.mjs +1 -1
  14. package/dist/adapters/otlp.d.mts +1 -1
  15. package/dist/adapters/otlp.mjs +2 -2
  16. package/dist/adapters/posthog.d.mts +1 -1
  17. package/dist/adapters/posthog.mjs +2 -2
  18. package/dist/adapters/sentry.d.mts +1 -1
  19. package/dist/adapters/sentry.mjs +2 -2
  20. package/dist/ai/index.d.mts +27 -10
  21. package/dist/ai/index.d.mts.map +1 -1
  22. package/dist/ai/index.mjs +80 -15
  23. package/dist/ai/index.mjs.map +1 -1
  24. package/dist/asyncStorageScope-DnF7Xk8l.mjs +81 -0
  25. package/dist/asyncStorageScope-DnF7Xk8l.mjs.map +1 -0
  26. package/dist/{audit-BQt8yAHo.mjs → audit-BXCYMjcC.mjs} +31 -93
  27. package/dist/audit-BXCYMjcC.mjs.map +1 -0
  28. package/dist/{audit-D7v6JHj0.d.mts → audit-BZrUvjfW.d.mts} +71 -53
  29. package/dist/audit-BZrUvjfW.d.mts.map +1 -0
  30. package/dist/audit-action.d.mts +2 -0
  31. package/dist/audit-action.mjs +74 -0
  32. package/dist/audit-action.mjs.map +1 -0
  33. package/dist/audit.d.mts +2 -0
  34. package/dist/audit.mjs +3 -0
  35. package/dist/better-auth/index.d.mts +1 -1
  36. package/dist/browser.d.mts +1 -1
  37. package/dist/{index-EvnrXvQM.d.mts → catalog-1cQnNxR2.d.mts} +4 -3
  38. package/dist/catalog-1cQnNxR2.d.mts.map +1 -0
  39. package/dist/catalog.d.mts +2 -0
  40. package/dist/catalog.mjs +205 -0
  41. package/dist/catalog.mjs.map +1 -0
  42. package/dist/{define-DTQpu4f6.d.mts → define-Ct2QyrE9.d.mts} +3 -3
  43. package/dist/{define-DTQpu4f6.d.mts.map → define-Ct2QyrE9.d.mts.map} +1 -1
  44. package/dist/{drain-fDb-eNwz.mjs → drain-Dl2ZJKRA.mjs} +3 -3
  45. package/dist/{drain-fDb-eNwz.mjs.map → drain-Dl2ZJKRA.mjs.map} +1 -1
  46. package/dist/elysia/index.d.mts +8 -7
  47. package/dist/elysia/index.d.mts.map +1 -1
  48. package/dist/elysia/index.mjs +11 -8
  49. package/dist/elysia/index.mjs.map +1 -1
  50. package/dist/{enrich-error-stack-next.node-Dgm_rCf5.mjs → enrich-error-stack-next.node-KB0Oap7R.mjs} +1 -1
  51. package/dist/{enrich-error-stack-next.node-Dgm_rCf5.mjs.map → enrich-error-stack-next.node-KB0Oap7R.mjs.map} +1 -1
  52. package/dist/{enricher-CBRmQw6e.d.mts → enricher-BOzt2FCi.d.mts} +2 -2
  53. package/dist/{enricher-CBRmQw6e.d.mts.map → enricher-BOzt2FCi.d.mts.map} +1 -1
  54. package/dist/enrichers.d.mts +2 -2
  55. package/dist/{error-CpghjrkY.d.mts → error-BRymuv8U.d.mts} +2 -2
  56. package/dist/{error-CpghjrkY.d.mts.map → error-BRymuv8U.d.mts.map} +1 -1
  57. package/dist/error.d.mts +1 -1
  58. package/dist/{errors-BLU4Tfpe.d.mts → errors-C7qA6CAi.d.mts} +2 -2
  59. package/dist/{errors-BLU4Tfpe.d.mts.map → errors-C7qA6CAi.d.mts.map} +1 -1
  60. package/dist/eve/index.d.mts +72 -0
  61. package/dist/eve/index.d.mts.map +1 -0
  62. package/dist/eve/index.mjs +593 -0
  63. package/dist/eve/index.mjs.map +1 -0
  64. package/dist/express/index.d.mts +4 -4
  65. package/dist/express/index.d.mts.map +1 -1
  66. package/dist/express/index.mjs +3 -3
  67. package/dist/express/index.mjs.map +1 -1
  68. package/dist/fastify/index.d.mts +4 -4
  69. package/dist/fastify/index.d.mts.map +1 -1
  70. package/dist/fastify/index.mjs +4 -2
  71. package/dist/fastify/index.mjs.map +1 -1
  72. package/dist/fork-S9gpZmF8.mjs +72 -0
  73. package/dist/fork-S9gpZmF8.mjs.map +1 -0
  74. package/dist/hono/index.d.mts +3 -3
  75. package/dist/hono/index.d.mts.map +1 -1
  76. package/dist/hono/index.mjs +3 -6
  77. package/dist/hono/index.mjs.map +1 -1
  78. package/dist/{http-ChVS9GYc.mjs → http-pKfd8UDu.mjs} +2 -2
  79. package/dist/{http-ChVS9GYc.mjs.map → http-pKfd8UDu.mjs.map} +1 -1
  80. package/dist/http.d.mts +1 -1
  81. package/dist/index.d.mts +9 -9
  82. package/dist/index.mjs +3 -203
  83. package/dist/{instrumentation-create-BrjQtSKD.d.mts → instrumentation-create-gSDk0leX.d.mts} +2 -2
  84. package/dist/{instrumentation-create-BrjQtSKD.d.mts.map → instrumentation-create-gSDk0leX.d.mts.map} +1 -1
  85. package/dist/{integration-DYp2uw8O.mjs → integration-CeL90DtN.mjs} +3 -2
  86. package/dist/{integration-DYp2uw8O.mjs.map → integration-CeL90DtN.mjs.map} +1 -1
  87. package/dist/{logger-mHIWxBhJ.d.mts → logger-CxWz7QJ3.d.mts} +7 -3
  88. package/dist/logger-CxWz7QJ3.d.mts.map +1 -0
  89. package/dist/logger.d.mts +2 -2
  90. package/dist/logger.mjs +2 -2
  91. package/dist/{fork-CgGlAaHa.mjs → middleware-Cdebyfdt.mjs} +3 -69
  92. package/dist/middleware-Cdebyfdt.mjs.map +1 -0
  93. package/dist/{middleware-B_k4Mrzg.d.mts → middleware-fbtEs6bq.d.mts} +3 -3
  94. package/dist/middleware-fbtEs6bq.d.mts.map +1 -0
  95. package/dist/nestjs/index.d.mts +5 -5
  96. package/dist/nestjs/index.d.mts.map +1 -1
  97. package/dist/nestjs/index.mjs +4 -3
  98. package/dist/nestjs/index.mjs.map +1 -1
  99. package/dist/next/client.d.mts +1 -1
  100. package/dist/next/index.d.mts +5 -5
  101. package/dist/next/index.d.mts.map +1 -1
  102. package/dist/next/index.mjs +3 -3
  103. package/dist/next/index.mjs.map +1 -1
  104. package/dist/next/instrumentation/create.d.mts +1 -1
  105. package/dist/next/instrumentation/create.mjs +3 -1
  106. package/dist/next/instrumentation/create.mjs.map +1 -1
  107. package/dist/next/instrumentation.d.mts +1 -1
  108. package/dist/next/stream.d.mts +1 -1
  109. package/dist/nitro/errorHandler.mjs +9 -4
  110. package/dist/nitro/errorHandler.mjs.map +1 -1
  111. package/dist/nitro/module.d.mts +2 -2
  112. package/dist/nitro/module.mjs +1 -1
  113. package/dist/nitro/plugin.mjs +2 -2
  114. package/dist/nitro/v3/errorHandler.d.mts +0 -10
  115. package/dist/nitro/v3/errorHandler.mjs +13 -3
  116. package/dist/nitro/v3/errorHandler.mjs.map +1 -1
  117. package/dist/nitro/v3/index.d.mts +2 -2
  118. package/dist/nitro/v3/module.d.mts +1 -1
  119. package/dist/nitro/v3/module.mjs +1 -1
  120. package/dist/nitro/v3/plugin.mjs +2 -2
  121. package/dist/nitro/v3/useLogger.d.mts +2 -2
  122. package/dist/nitro/v3/useLogger.d.mts.map +1 -1
  123. package/dist/nitro/v3/useLogger.mjs.map +1 -1
  124. package/dist/{nitro-_Hda8Deo.d.mts → nitro-CcN7HDnr.d.mts} +2 -2
  125. package/dist/{nitro-_Hda8Deo.d.mts.map → nitro-CcN7HDnr.d.mts.map} +1 -1
  126. package/dist/{nitro-ClRZLD1g.mjs → nitro-DYFCSlho.mjs} +30 -3
  127. package/dist/nitro-DYFCSlho.mjs.map +1 -0
  128. package/dist/{nitroConfigBridge-BkVWnSV3.mjs → nitroConfigBridge-lHPsE5zG.mjs} +8 -6
  129. package/dist/nitroConfigBridge-lHPsE5zG.mjs.map +1 -0
  130. package/dist/{nodeResponse-CIEEbrNE.mjs → nodeResponse-Dsl7gZUf.mjs} +1 -1
  131. package/dist/{nodeResponse-CIEEbrNE.mjs.map → nodeResponse-Dsl7gZUf.mjs.map} +1 -1
  132. package/dist/nuxt/module.d.mts +1 -1
  133. package/dist/nuxt/module.mjs +2 -2
  134. package/dist/orpc/index.d.mts +4 -4
  135. package/dist/orpc/index.d.mts.map +1 -1
  136. package/dist/orpc/index.mjs +2 -2
  137. package/dist/orpc/index.mjs.map +1 -1
  138. package/dist/{package-CNV_CXs8.mjs → package-Drb3wqqt.mjs} +2 -2
  139. package/dist/package-Drb3wqqt.mjs.map +1 -0
  140. package/dist/{parseError-BeBXEd2V.d.mts → parseError-BmoJ7XeF.d.mts} +2 -2
  141. package/dist/parseError-BmoJ7XeF.d.mts.map +1 -0
  142. package/dist/react-router/index.d.mts +4 -4
  143. package/dist/react-router/index.d.mts.map +1 -1
  144. package/dist/react-router/index.mjs +3 -2
  145. package/dist/react-router/index.mjs.map +1 -1
  146. package/dist/register-disk-snippet-B5svlT8-.mjs +21 -0
  147. package/dist/register-disk-snippet-B5svlT8-.mjs.map +1 -0
  148. package/dist/runtime/client/log.d.mts +1 -1
  149. package/dist/runtime/server/routes/_evlog/ingest.post.mjs +1 -1
  150. package/dist/runtime/server/useLogger.d.mts +1 -1
  151. package/dist/runtime/server/useLogger.mjs.map +1 -1
  152. package/dist/runtime/utils/parseError.d.mts +2 -2
  153. package/dist/{storage-7X37OToT.mjs → storage-BeFdoGTI.mjs} +1 -1
  154. package/dist/storage-BeFdoGTI.mjs.map +1 -0
  155. package/dist/stream.d.mts +1 -1
  156. package/dist/stream.d.mts.map +1 -1
  157. package/dist/stream.mjs +7 -1
  158. package/dist/stream.mjs.map +1 -1
  159. package/dist/sveltekit/index.d.mts +3 -4
  160. package/dist/sveltekit/index.d.mts.map +1 -1
  161. package/dist/sveltekit/index.mjs +6 -3
  162. package/dist/sveltekit/index.mjs.map +1 -1
  163. package/dist/toolkit.d.mts +7 -7
  164. package/dist/toolkit.d.mts.map +1 -1
  165. package/dist/toolkit.mjs +7 -6
  166. package/dist/types.d.mts +1 -1
  167. package/dist/{useLogger-Cfv8Ck8b.d.mts → useLogger-BupVLO9_.d.mts} +3 -3
  168. package/dist/useLogger-BupVLO9_.d.mts.map +1 -0
  169. package/dist/{utils-CJJG0ZYW.d.mts → utils-D_LY-J1k.d.mts} +2 -2
  170. package/dist/{utils-CJJG0ZYW.d.mts.map → utils-D_LY-J1k.d.mts.map} +1 -1
  171. package/dist/utils.d.mts +1 -1
  172. package/dist/vite/index.d.mts +1 -1
  173. package/dist/workers.d.mts +3 -3
  174. package/dist/workers.d.mts.map +1 -1
  175. package/dist/workers.mjs +1 -1
  176. package/dist/workers.mjs.map +1 -1
  177. package/package.json +25 -2
  178. package/dist/audit-BQt8yAHo.mjs.map +0 -1
  179. package/dist/audit-D7v6JHj0.d.mts.map +0 -1
  180. package/dist/fork-CgGlAaHa.mjs.map +0 -1
  181. package/dist/index-EvnrXvQM.d.mts.map +0 -1
  182. package/dist/index.mjs.map +0 -1
  183. package/dist/logger-mHIWxBhJ.d.mts.map +0 -1
  184. package/dist/middleware-B_k4Mrzg.d.mts.map +0 -1
  185. package/dist/nitro-ClRZLD1g.mjs.map +0 -1
  186. package/dist/nitroConfigBridge-BkVWnSV3.mjs.map +0 -1
  187. package/dist/package-CNV_CXs8.mjs.map +0 -1
  188. package/dist/parseError-BeBXEd2V.d.mts.map +0 -1
  189. package/dist/storage-7X37OToT.mjs.map +0 -1
  190. package/dist/useLogger-Cfv8Ck8b.d.mts.map +0 -1
@@ -0,0 +1,72 @@
1
+ import { et as LoggerConfig, l as AuditableLogger } from "../audit-BZrUvjfW.mjs";
2
+ import { t as BaseEvlogOptions } from "../middleware-fbtEs6bq.mjs";
3
+ import { ModelCost } from "../ai/index.mjs";
4
+ import { HookDefinition } from "eve/hooks";
5
+
6
+ //#region src/eve/index.d.ts
7
+ /** Options for {@link defineEvlogHook}. */
8
+ interface EvlogEveOptions extends BaseEvlogOptions {
9
+ /** Passed to {@link initLogger} on the first hook invocation. */
10
+ init?: LoggerConfig;
11
+ /**
12
+ * When `true` (default), user message content from `message.received` is
13
+ * omitted from the wide event. Set to `false` to include a truncated preview.
14
+ */
15
+ redactMessage?: boolean;
16
+ /**
17
+ * Pricing map for {@link AIEventData.estimatedCost}. Keys are model IDs,
18
+ * values are dollars per 1M tokens — same shape as `evlog/ai`.
19
+ */
20
+ cost?: Record<string, ModelCost>;
21
+ /**
22
+ * Model ID used with `cost` when eve stream events do not expose the model
23
+ * name. When `cost` has exactly one entry, that key is used automatically.
24
+ */
25
+ model?: string;
26
+ /**
27
+ * Max in-memory sessions for context carry-over and approval state.
28
+ * Oldest sessions are evicted when exceeded. Default `256`.
29
+ */
30
+ maxSessions?: number;
31
+ }
32
+ /** Minimal session shape accepted by {@link useLogger} as a fallback lookup key. */
33
+ interface EveTurnSessionContext {
34
+ readonly session: {
35
+ readonly id: string;
36
+ readonly turn?: {
37
+ readonly id?: string;
38
+ };
39
+ };
40
+ }
41
+ declare function useLogger(ctx?: EveTurnSessionContext): AuditableLogger;
42
+ /**
43
+ * Create an eve stream hook that emits one evlog wide event per agent turn.
44
+ *
45
+ * Export the result as the default export of `agent/hooks/evlog.ts`. eve
46
+ * auto-discovers hook files; evlog maps turn lifecycle events to a wide event
47
+ * with AI usage, tool executions, and your drain/enrich/keep pipeline.
48
+ *
49
+ * Complements eve Agent Runs and OpenTelemetry — it does not replace them.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * // agent/hooks/evlog.ts
54
+ * import { defineEvlogHook } from 'evlog/eve'
55
+ * import { createAxiomDrain } from 'evlog/axiom'
56
+ *
57
+ * export default defineEvlogHook({
58
+ * drain: createAxiomDrain(),
59
+ * enrich: (ctx) => {
60
+ * ctx.event.runtime = process.env.VERCEL_REGION
61
+ * },
62
+ * })
63
+ * ```
64
+ */
65
+ declare function defineEvlogHook(options?: EvlogEveOptions): HookDefinition;
66
+ /** @internal Simulates eve tool execution where AsyncLocalStorage did not propagate. */
67
+ declare function detachActiveTurnLoggerForTests(): void;
68
+ /** @internal Resets module state between unit tests. Clears turn maps, session context, and the Eve init flag. */
69
+ declare function resetEvlogEveForTests(): void;
70
+ //#endregion
71
+ export { EveTurnSessionContext, EvlogEveOptions, type ModelCost, defineEvlogHook, detachActiveTurnLoggerForTests, resetEvlogEveForTests, useLogger };
72
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/eve/index.ts"],"mappings":";;;;;;;UAiBiB,eAAA,SAAwB,gBAAA;EAAR;EAE/B,IAAA,GAAO,YAAA;EAAA;;;;EAKP,aAAA;EAPuD;;;;EAYvD,IAAA,GAAO,MAAA,SAAe,SAAA;EAAtB;;;;EAKA,KAAA;EAKW;;AAIb;;EAJE,WAAA;AAAA;;UAIe,qBAAA;EAAA,SACN,OAAA;IAAA,SACE,EAAA;IAAA,SACA,IAAA;MAAA,SAAkB,EAAA;IAAA;EAAA;AAAA;AAAA,iBA4Of,SAAA,CAAU,GAAA,GAAM,qBAAA,GAAwB,eAAA;;;;;;AA8WxD;;;;;;;;;AA2MA;;;;;AAMA;;;;iBAjNgB,eAAA,CAAgB,OAAA,GAAS,eAAA,GAAuB,cAAA;;iBA2MhD,8BAAA,CAAA;;iBAMA,qBAAA,CAAA"}
@@ -0,0 +1,593 @@
1
+ import { C as isLoggerInitialized, w as isLoggerLocked, x as initLogger } from "../audit-BXCYMjcC.mjs";
2
+ import { t as createMiddlewareLogger } from "../middleware-Cdebyfdt.mjs";
3
+ import { n as clearAsyncLocalStorage, r as patchAsyncLocalStorageEnterWith, t as bindAsyncLocalStorage } from "../asyncStorageScope-DnF7Xk8l.mjs";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+ import { defineHook } from "eve/hooks";
6
+ //#region src/eve/index.ts
7
+ const DEFAULT_MAX_SESSIONS = 256;
8
+ /** Top-level wide-event keys that stay turn-scoped and are not carried across turns. */
9
+ const TURN_ONLY_KEYS = new Set([
10
+ "eve",
11
+ "ai",
12
+ "message",
13
+ "method",
14
+ "path",
15
+ "status",
16
+ "duration",
17
+ "level",
18
+ "error",
19
+ "agent",
20
+ "channel",
21
+ "approval",
22
+ "audit",
23
+ "requestId",
24
+ "service",
25
+ "timestamp",
26
+ "traceId",
27
+ "spanId"
28
+ ]);
29
+ function turnKey(sessionId, turnId) {
30
+ return `${sessionId}:${turnId}`;
31
+ }
32
+ function freshAccumulator(options) {
33
+ return {
34
+ calls: 0,
35
+ steps: 0,
36
+ inputTokens: 0,
37
+ outputTokens: 0,
38
+ cacheReadTokens: 0,
39
+ cacheWriteTokens: 0,
40
+ toolExecutions: [],
41
+ subagents: [],
42
+ pausedForInput: false,
43
+ costMap: options.cost,
44
+ costModel: resolveCostModel(options)
45
+ };
46
+ }
47
+ function resolveCostModel(options) {
48
+ if (options.model) return options.model;
49
+ const keys = options.cost ? Object.keys(options.cost) : [];
50
+ if (keys.length === 1) return keys[0];
51
+ }
52
+ function computeEstimatedCost(state) {
53
+ if (!state.costMap || !state.costModel) return void 0;
54
+ const pricing = state.costMap[state.costModel];
55
+ if (!pricing) return void 0;
56
+ const total = state.inputTokens / 1e6 * pricing.input + state.outputTokens / 1e6 * pricing.output;
57
+ return total > 0 ? Math.round(total * 1e6) / 1e6 : void 0;
58
+ }
59
+ function buildAiField(state) {
60
+ const totalTokens = state.inputTokens + state.outputTokens;
61
+ const data = {
62
+ calls: state.calls,
63
+ inputTokens: state.inputTokens,
64
+ outputTokens: state.outputTokens,
65
+ totalTokens,
66
+ steps: state.steps
67
+ };
68
+ if (state.costModel) data.model = state.costModel;
69
+ if (state.cacheReadTokens > 0) data.cacheReadTokens = state.cacheReadTokens;
70
+ if (state.cacheWriteTokens > 0) data.cacheWriteTokens = state.cacheWriteTokens;
71
+ if (state.finishReason) data.finishReason = state.finishReason;
72
+ const estimatedCost = computeEstimatedCost(state);
73
+ if (estimatedCost !== void 0) data.estimatedCost = estimatedCost;
74
+ if (state.toolExecutions.length > 0) data.tools = state.toolExecutions.map((t) => ({ ...t }));
75
+ return data;
76
+ }
77
+ function extractToolName(result) {
78
+ if (result && typeof result === "object") {
79
+ const record = result;
80
+ if (typeof record.toolName === "string") return record.toolName;
81
+ if (typeof record.subagentName === "string") return record.subagentName;
82
+ if (typeof record.name === "string") return record.name;
83
+ }
84
+ return "unknown";
85
+ }
86
+ function extractCallId(result) {
87
+ if (result && typeof result === "object") {
88
+ const { callId } = result;
89
+ if (typeof callId === "string") return callId;
90
+ }
91
+ }
92
+ function truncateMessage(message, maxLength = 500) {
93
+ if (message.length <= maxLength) return message;
94
+ return `${message.slice(0, maxLength)}…`;
95
+ }
96
+ function ensureInit(options) {
97
+ const state = getEveGlobalState();
98
+ if (options.maxSessions !== void 0) state.maxSessions = options.maxSessions;
99
+ if (isEveInitialized()) return;
100
+ if (!isLoggerLocked() && !isLoggerInitialized()) initLogger(options.init ?? { env: { service: "eve-agent" } });
101
+ setEveInitialized(true);
102
+ }
103
+ const turnLoggerStorage = new AsyncLocalStorage();
104
+ patchAsyncLocalStorageEnterWith(turnLoggerStorage);
105
+ const activeTurnLoggers = /* @__PURE__ */ new WeakSet();
106
+ function bindTurnLogger(logger) {
107
+ bindAsyncLocalStorage(turnLoggerStorage, logger);
108
+ activeTurnLoggers.add(logger);
109
+ }
110
+ function unbindTurnLogger(logger) {
111
+ activeTurnLoggers.delete(logger);
112
+ if (turnLoggerStorage.getStore() === logger) clearAsyncLocalStorage(turnLoggerStorage);
113
+ }
114
+ function resolveTurnLogger(ctx) {
115
+ const sessionId = ctx.session.id;
116
+ const turnId = ctx.session.turn?.id ?? activeTurnBySession().get(sessionId);
117
+ if (!turnId) throw new Error("[evlog] useLogger() could not resolve the active turn. Ensure defineEvlogHook() is registered and the turn has started.");
118
+ const state = turnStates().get(turnKey(sessionId, turnId));
119
+ if (!state) throw new Error("[evlog] useLogger() could not find a logger for the current turn. Ensure defineEvlogHook() is registered and the turn has started.");
120
+ return state.logger;
121
+ }
122
+ /**
123
+ * Turn-scoped logger for eve tool `execute()` handlers.
124
+ *
125
+ * When {@link defineEvlogHook} is registered, the logger is bound via
126
+ * AsyncLocalStorage on `turn.started`. Inside tool handlers, `useLogger()`
127
+ * resolves from ALS when it propagated, otherwise from the sole active turn
128
+ * in the process (typical eve dev). Pass eve tool `ctx` only when multiple
129
+ * sessions are active concurrently.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * import { useLogger } from 'evlog/eve'
134
+ *
135
+ * export default defineTool({
136
+ * async execute(input) {
137
+ * const log = useLogger()
138
+ * log.set({ order: { id: input.orderId } })
139
+ * },
140
+ * })
141
+ * ```
142
+ */
143
+ function resolveActiveTurnLogger() {
144
+ const active = activeTurnBySession();
145
+ if (active.size !== 1) return null;
146
+ const [sessionId, turnId] = active.entries().next().value;
147
+ return turnStates().get(turnKey(sessionId, turnId))?.logger ?? null;
148
+ }
149
+ function useLogger(ctx) {
150
+ const fromStorage = turnLoggerStorage.getStore();
151
+ if (fromStorage && activeTurnLoggers.has(fromStorage)) return fromStorage;
152
+ if (ctx?.session?.id) return resolveTurnLogger(ctx);
153
+ const active = resolveActiveTurnLogger();
154
+ if (active) return active;
155
+ throw new Error("[evlog] useLogger() was called outside an evlog eve turn. Add agent/hooks/evlog.ts with defineEvlogHook() or pass ctx from the tool handler.");
156
+ }
157
+ const EVE_GLOBAL_STATE = Symbol.for("evlog.eve.state");
158
+ function getEveGlobalState() {
159
+ const host = globalThis;
160
+ if (!host[EVE_GLOBAL_STATE]) host[EVE_GLOBAL_STATE] = {
161
+ turnStates: /* @__PURE__ */ new Map(),
162
+ activeTurnBySession: /* @__PURE__ */ new Map(),
163
+ sessionSnapshots: /* @__PURE__ */ new Map(),
164
+ sessionPendingActions: /* @__PURE__ */ new Map(),
165
+ sessionApprovals: /* @__PURE__ */ new Map(),
166
+ sessionRollups: /* @__PURE__ */ new Map(),
167
+ maxSessions: DEFAULT_MAX_SESSIONS,
168
+ initialized: false
169
+ };
170
+ return host[EVE_GLOBAL_STATE];
171
+ }
172
+ function turnStates() {
173
+ return getEveGlobalState().turnStates;
174
+ }
175
+ function activeTurnBySession() {
176
+ return getEveGlobalState().activeTurnBySession;
177
+ }
178
+ function sessionSnapshots() {
179
+ return getEveGlobalState().sessionSnapshots;
180
+ }
181
+ function sessionPendingActions() {
182
+ return getEveGlobalState().sessionPendingActions;
183
+ }
184
+ function sessionApprovals() {
185
+ return getEveGlobalState().sessionApprovals;
186
+ }
187
+ function sessionRollups() {
188
+ return getEveGlobalState().sessionRollups;
189
+ }
190
+ function touchSession(sessionId) {
191
+ const rollups = sessionRollups();
192
+ const rollup = rollups.get(sessionId) ?? {
193
+ turnCount: 0,
194
+ lastAccess: 0
195
+ };
196
+ rollup.lastAccess = Date.now();
197
+ rollups.set(sessionId, rollup);
198
+ evictStaleSessions();
199
+ }
200
+ function clearSessionState(sessionId) {
201
+ sessionSnapshots().delete(sessionId);
202
+ sessionRollups().delete(sessionId);
203
+ sessionPendingActions().delete(sessionId);
204
+ sessionApprovals().delete(sessionId);
205
+ }
206
+ function evictStaleSessions() {
207
+ const { maxSessions } = getEveGlobalState();
208
+ const rollups = sessionRollups();
209
+ if (rollups.size <= maxSessions) return;
210
+ const oldest = [...rollups.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess).slice(0, rollups.size - maxSessions);
211
+ for (const [sessionId] of oldest) {
212
+ if (activeTurnBySession().has(sessionId)) continue;
213
+ clearSessionState(sessionId);
214
+ }
215
+ }
216
+ function pruneEmptySessionMaps(sessionId) {
217
+ if (sessionPendingActions().get(sessionId)?.size === 0) sessionPendingActions().delete(sessionId);
218
+ if (sessionApprovals().get(sessionId)?.length === 0) sessionApprovals().delete(sessionId);
219
+ }
220
+ function bumpSessionTurnCount(sessionId) {
221
+ touchSession(sessionId);
222
+ const rollup = sessionRollups().get(sessionId);
223
+ rollup.turnCount += 1;
224
+ return rollup.turnCount;
225
+ }
226
+ /** Turn-level label — only set when the turn ends in a non-routine state. */
227
+ function derivePhase(ctx, accumulator, httpStatus) {
228
+ const approval = ctx.approval;
229
+ if (approval?.status === "rejected") return "rejected";
230
+ if (approval?.status === "pending" || accumulator.pausedForInput) return "awaiting-approval";
231
+ if (httpStatus >= 400) return "failed";
232
+ }
233
+ function getSessionPendingActions(sessionId) {
234
+ let map = sessionPendingActions().get(sessionId);
235
+ if (!map) {
236
+ map = /* @__PURE__ */ new Map();
237
+ sessionPendingActions().set(sessionId, map);
238
+ }
239
+ return map;
240
+ }
241
+ function trackPendingAction(sessionId, turnId, action) {
242
+ touchSession(sessionId);
243
+ getSessionPendingActions(sessionId).set(action.callId, {
244
+ toolName: action.toolName,
245
+ startedAt: action.startedAt ?? Date.now(),
246
+ turnId
247
+ });
248
+ }
249
+ function resolveToolDurationMs(state, callId) {
250
+ const pending = callId ? getSessionPendingActions(state.sessionId).get(callId) : void 0;
251
+ if (pending) {
252
+ if (pending.turnId !== state.turnId && state.accumulator.stepStartedAt !== void 0) return Math.max(0, Date.now() - state.accumulator.stepStartedAt);
253
+ return Math.max(0, Date.now() - pending.startedAt);
254
+ }
255
+ if (state.accumulator.stepStartedAt !== void 0) return Math.max(0, Date.now() - state.accumulator.stepStartedAt);
256
+ return 0;
257
+ }
258
+ function consumeSessionApproval(sessionId, toolName, callId) {
259
+ const list = sessionApprovals().get(sessionId);
260
+ if (!list?.length) return void 0;
261
+ const index = list.findIndex((approval) => callId ? approval.callId === callId : approval.toolName === toolName);
262
+ if (index === -1) return void 0;
263
+ const [approval] = list.splice(index, 1);
264
+ if (list.length === 0) sessionApprovals().delete(sessionId);
265
+ return approval;
266
+ }
267
+ function storeSessionApprovals(sessionId, pending) {
268
+ touchSession(sessionId);
269
+ const existing = sessionApprovals().get(sessionId) ?? [];
270
+ sessionApprovals().set(sessionId, [...existing, ...pending]);
271
+ }
272
+ function isEveInitialized() {
273
+ return getEveGlobalState().initialized;
274
+ }
275
+ function setEveInitialized(value) {
276
+ getEveGlobalState().initialized = value;
277
+ }
278
+ function applySessionContext(sessionId, logger) {
279
+ touchSession(sessionId);
280
+ const snapshot = sessionSnapshots().get(sessionId);
281
+ if (snapshot && Object.keys(snapshot).length > 0) logger.set({ ...snapshot });
282
+ }
283
+ function persistSessionContext(sessionId, logger) {
284
+ const ctx = logger.getContext();
285
+ const snapshot = { ...sessionSnapshots().get(sessionId) ?? {} };
286
+ for (const [key, value] of Object.entries(ctx)) if (!TURN_ONLY_KEYS.has(key) && value !== void 0) snapshot[key] = value;
287
+ sessionSnapshots().set(sessionId, snapshot);
288
+ }
289
+ function flushEveMetadata(state) {
290
+ if (state.accumulator.subagents.length === 0) return;
291
+ state.logger.set({ eve: { subagents: state.accumulator.subagents.map((s) => ({ ...s })) } });
292
+ }
293
+ function getOrCreateTurnState(sessionId, turnId, options, ctx) {
294
+ const key = turnKey(sessionId, turnId);
295
+ const existing = turnStates().get(key);
296
+ if (existing) return existing;
297
+ touchSession(sessionId);
298
+ const middlewareOptions = {
299
+ method: "EVE",
300
+ path: `/sessions/${sessionId}/turns/${turnId}`,
301
+ requestId: turnId,
302
+ drain: options.drain,
303
+ enrich: options.enrich,
304
+ keep: options.keep,
305
+ redact: options.redact,
306
+ plugins: options.plugins,
307
+ include: options.include,
308
+ exclude: options.exclude,
309
+ routes: options.routes
310
+ };
311
+ const { logger, finish, skipped } = createMiddlewareLogger(middlewareOptions);
312
+ if (skipped) return null;
313
+ const state = {
314
+ logger,
315
+ finish,
316
+ middlewareOptions,
317
+ accumulator: freshAccumulator(options),
318
+ sessionId,
319
+ turnId
320
+ };
321
+ applySessionContext(sessionId, logger);
322
+ logger.set({
323
+ eve: {
324
+ sessionId,
325
+ turnId
326
+ },
327
+ agent: {
328
+ name: ctx.agent.name,
329
+ ...ctx.agent.nodeId ? { nodeId: ctx.agent.nodeId } : {}
330
+ },
331
+ channel: {
332
+ kind: ctx.channel.kind ?? "unknown",
333
+ ...ctx.channel.continuationToken ? { continuing: true } : {}
334
+ }
335
+ });
336
+ turnStates().set(key, state);
337
+ activeTurnBySession().set(sessionId, turnId);
338
+ return state;
339
+ }
340
+ function flushAi(state) {
341
+ state.logger.set({ ai: buildAiField(state.accumulator) });
342
+ }
343
+ async function finishTurn(sessionId, turnId, opts) {
344
+ const key = turnKey(sessionId, turnId);
345
+ const state = turnStates().get(key);
346
+ if (!state) return;
347
+ try {
348
+ flushEveMetadata(state);
349
+ flushAi(state);
350
+ const httpStatus = opts.status ?? (opts.error ? 500 : 200);
351
+ const phase = derivePhase(state.logger.getContext(), state.accumulator, httpStatus);
352
+ const sessionTurns = bumpSessionTurnCount(sessionId);
353
+ state.logger.set({ eve: {
354
+ ...phase ? { phase } : {},
355
+ sessionTurns
356
+ } });
357
+ persistSessionContext(sessionId, state.logger);
358
+ await state.finish(opts);
359
+ } finally {
360
+ unbindTurnLogger(state.logger);
361
+ turnStates().delete(key);
362
+ if (activeTurnBySession().get(sessionId) === turnId) activeTurnBySession().delete(sessionId);
363
+ pruneEmptySessionMaps(sessionId);
364
+ }
365
+ }
366
+ function runSafe(fn) {
367
+ (async () => {
368
+ try {
369
+ await fn();
370
+ } catch (err) {
371
+ console.error("[evlog] eve hook handler failed:", err);
372
+ }
373
+ })();
374
+ }
375
+ function getTurnState(sessionId, turnId) {
376
+ return turnStates().get(turnKey(sessionId, turnId));
377
+ }
378
+ /**
379
+ * Create an eve stream hook that emits one evlog wide event per agent turn.
380
+ *
381
+ * Export the result as the default export of `agent/hooks/evlog.ts`. eve
382
+ * auto-discovers hook files; evlog maps turn lifecycle events to a wide event
383
+ * with AI usage, tool executions, and your drain/enrich/keep pipeline.
384
+ *
385
+ * Complements eve Agent Runs and OpenTelemetry — it does not replace them.
386
+ *
387
+ * @example
388
+ * ```ts
389
+ * // agent/hooks/evlog.ts
390
+ * import { defineEvlogHook } from 'evlog/eve'
391
+ * import { createAxiomDrain } from 'evlog/axiom'
392
+ *
393
+ * export default defineEvlogHook({
394
+ * drain: createAxiomDrain(),
395
+ * enrich: (ctx) => {
396
+ * ctx.event.runtime = process.env.VERCEL_REGION
397
+ * },
398
+ * })
399
+ * ```
400
+ */
401
+ function defineEvlogHook(options = {}) {
402
+ const redactMessage = options.redactMessage ?? true;
403
+ return defineHook({ events: {
404
+ "turn.started"(event, ctx) {
405
+ try {
406
+ ensureInit(options);
407
+ const state = getOrCreateTurnState(ctx.session.id, event.data.turnId, options, ctx);
408
+ state?.logger.set({ eve: { turnSequence: event.data.sequence } });
409
+ if (state) bindTurnLogger(state.logger);
410
+ } catch (err) {
411
+ console.error("[evlog] eve hook handler failed:", err);
412
+ }
413
+ },
414
+ "message.received"(event, ctx) {
415
+ runSafe(() => {
416
+ if (redactMessage) return;
417
+ const state = getTurnState(ctx.session.id, event.data.turnId);
418
+ if (!state) return;
419
+ state.logger.set({ message: { received: truncateMessage(event.data.message) } });
420
+ });
421
+ },
422
+ "step.started"(event, ctx) {
423
+ runSafe(() => {
424
+ const state = getTurnState(ctx.session.id, event.data.turnId);
425
+ if (!state) return;
426
+ state.accumulator.stepStartedAt = Date.now();
427
+ });
428
+ },
429
+ "step.completed"(event, ctx) {
430
+ runSafe(() => {
431
+ const state = getTurnState(ctx.session.id, event.data.turnId);
432
+ if (!state) return;
433
+ const acc = state.accumulator;
434
+ acc.steps += 1;
435
+ acc.calls += 1;
436
+ acc.finishReason = event.data.finishReason;
437
+ const { usage } = event.data;
438
+ if (usage) {
439
+ acc.inputTokens += usage.inputTokens ?? 0;
440
+ acc.outputTokens += usage.outputTokens ?? 0;
441
+ acc.cacheReadTokens += usage.cacheReadTokens ?? 0;
442
+ acc.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
443
+ }
444
+ });
445
+ },
446
+ "actions.requested"(event, ctx) {
447
+ runSafe(() => {
448
+ const state = getTurnState(ctx.session.id, event.data.turnId);
449
+ if (!state) return;
450
+ bindTurnLogger(state.logger);
451
+ const startedAt = Date.now();
452
+ for (const action of event.data.actions) if (action.kind === "tool-call") trackPendingAction(ctx.session.id, event.data.turnId, {
453
+ callId: action.callId,
454
+ toolName: action.toolName,
455
+ startedAt
456
+ });
457
+ });
458
+ },
459
+ "input.requested"(event, ctx) {
460
+ runSafe(() => {
461
+ const state = getTurnState(ctx.session.id, event.data.turnId);
462
+ if (!state) return;
463
+ state.accumulator.pausedForInput = true;
464
+ const pending = event.data.requests.map((req) => ({
465
+ toolName: req.action.toolName,
466
+ callId: req.action.callId
467
+ }));
468
+ storeSessionApprovals(ctx.session.id, pending);
469
+ const [first] = pending;
470
+ if (first) state.logger.set({ approval: {
471
+ status: "pending",
472
+ tool: first.toolName
473
+ } });
474
+ });
475
+ },
476
+ "action.result"(event, ctx) {
477
+ runSafe(() => {
478
+ const state = getTurnState(ctx.session.id, event.data.turnId);
479
+ if (!state) return;
480
+ const callId = extractCallId(event.data.result);
481
+ const sessionPending = getSessionPendingActions(state.sessionId);
482
+ const turnPending = callId ? sessionPending.get(callId) : void 0;
483
+ const { status } = event.data;
484
+ const rejected = status === "rejected";
485
+ const success = status === "completed";
486
+ const toolName = turnPending?.toolName ?? extractToolName(event.data.result);
487
+ const durationMs = resolveToolDurationMs(state, callId);
488
+ if (callId) sessionPending.delete(callId);
489
+ const execution = {
490
+ name: toolName,
491
+ durationMs,
492
+ success
493
+ };
494
+ if (rejected) {
495
+ execution.error = "rejected";
496
+ state.accumulator.pausedForInput = false;
497
+ consumeSessionApproval(state.sessionId, toolName, callId);
498
+ state.logger.set({ approval: {
499
+ status: "rejected",
500
+ tool: toolName
501
+ } });
502
+ } else if (success) {
503
+ const approval = consumeSessionApproval(state.sessionId, toolName, callId);
504
+ if (approval) {
505
+ state.accumulator.pausedForInput = false;
506
+ state.logger.set({ approval: {
507
+ status: "approved",
508
+ tool: approval.toolName
509
+ } });
510
+ }
511
+ } else if (event.data.error) execution.error = event.data.error.message;
512
+ state.accumulator.toolExecutions.push(execution);
513
+ });
514
+ },
515
+ "subagent.called"(event, ctx) {
516
+ runSafe(() => {
517
+ const state = getTurnState(ctx.session.id, event.data.turnId);
518
+ if (!state) return;
519
+ state.accumulator.subagents.push({
520
+ callId: event.data.callId,
521
+ name: event.data.name,
522
+ toolName: event.data.toolName,
523
+ childSessionId: event.data.childSessionId,
524
+ status: "called"
525
+ });
526
+ });
527
+ },
528
+ "subagent.completed"(event, ctx) {
529
+ runSafe(() => {
530
+ const sessionId = ctx.session.id;
531
+ const turnId = activeTurnBySession().get(sessionId);
532
+ if (!turnId) return;
533
+ const state = getTurnState(sessionId, turnId);
534
+ if (!state) return;
535
+ const existing = state.accumulator.subagents.find((s) => s.callId === event.data.callId);
536
+ if (existing) {
537
+ existing.status = "completed";
538
+ existing.output = event.data.output;
539
+ } else state.accumulator.subagents.push({
540
+ callId: event.data.callId,
541
+ name: event.data.subagentName,
542
+ status: "completed",
543
+ output: event.data.output
544
+ });
545
+ });
546
+ },
547
+ async "turn.completed"(event, ctx) {
548
+ try {
549
+ await finishTurn(ctx.session.id, event.data.turnId, { status: 200 });
550
+ } catch (err) {
551
+ console.error("[evlog] eve hook handler failed:", err);
552
+ }
553
+ },
554
+ async "turn.failed"(event, ctx) {
555
+ try {
556
+ getTurnState(ctx.session.id, event.data.turnId)?.logger.set({ eve: { failure: {
557
+ code: event.data.code,
558
+ message: event.data.message,
559
+ ...event.data.details ? { details: event.data.details } : {}
560
+ } } });
561
+ const error = new Error(event.data.message);
562
+ error.name = event.data.code;
563
+ await finishTurn(ctx.session.id, event.data.turnId, {
564
+ error,
565
+ status: 500
566
+ });
567
+ } catch (err) {
568
+ console.error("[evlog] eve hook handler failed:", err);
569
+ }
570
+ }
571
+ } });
572
+ }
573
+ /** @internal Simulates eve tool execution where AsyncLocalStorage did not propagate. */
574
+ function detachActiveTurnLoggerForTests() {
575
+ const logger = turnLoggerStorage.getStore();
576
+ if (logger) unbindTurnLogger(logger);
577
+ }
578
+ /** @internal Resets module state between unit tests. Clears turn maps, session context, and the Eve init flag. */
579
+ function resetEvlogEveForTests() {
580
+ clearAsyncLocalStorage(turnLoggerStorage);
581
+ turnStates().clear();
582
+ activeTurnBySession().clear();
583
+ sessionSnapshots().clear();
584
+ sessionPendingActions().clear();
585
+ sessionApprovals().clear();
586
+ sessionRollups().clear();
587
+ setEveInitialized(false);
588
+ delete globalThis[EVE_GLOBAL_STATE];
589
+ }
590
+ //#endregion
591
+ export { defineEvlogHook, detachActiveTurnLoggerForTests, resetEvlogEveForTests, useLogger };
592
+
593
+ //# sourceMappingURL=index.mjs.map