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.
- package/README.md +30 -1
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.mjs +2 -2
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.mjs +2 -2
- package/dist/adapters/datadog.d.mts +1 -1
- package/dist/adapters/datadog.mjs +2 -2
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.mjs +1 -1
- package/dist/adapters/hyperdx.d.mts +1 -1
- package/dist/adapters/hyperdx.mjs +1 -1
- package/dist/adapters/memory.d.mts +1 -1
- package/dist/adapters/memory.mjs +1 -1
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.mjs +2 -2
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.mjs +2 -2
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.mjs +2 -2
- package/dist/ai/index.d.mts +27 -10
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +80 -15
- package/dist/ai/index.mjs.map +1 -1
- package/dist/asyncStorageScope-DnF7Xk8l.mjs +81 -0
- package/dist/asyncStorageScope-DnF7Xk8l.mjs.map +1 -0
- package/dist/{audit-BQt8yAHo.mjs → audit-BXCYMjcC.mjs} +31 -93
- package/dist/audit-BXCYMjcC.mjs.map +1 -0
- package/dist/{audit-D7v6JHj0.d.mts → audit-BZrUvjfW.d.mts} +71 -53
- package/dist/audit-BZrUvjfW.d.mts.map +1 -0
- package/dist/audit-action.d.mts +2 -0
- package/dist/audit-action.mjs +74 -0
- package/dist/audit-action.mjs.map +1 -0
- package/dist/audit.d.mts +2 -0
- package/dist/audit.mjs +3 -0
- package/dist/better-auth/index.d.mts +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/{index-EvnrXvQM.d.mts → catalog-1cQnNxR2.d.mts} +4 -3
- package/dist/catalog-1cQnNxR2.d.mts.map +1 -0
- package/dist/catalog.d.mts +2 -0
- package/dist/catalog.mjs +205 -0
- package/dist/catalog.mjs.map +1 -0
- package/dist/{define-DTQpu4f6.d.mts → define-Ct2QyrE9.d.mts} +3 -3
- package/dist/{define-DTQpu4f6.d.mts.map → define-Ct2QyrE9.d.mts.map} +1 -1
- package/dist/{drain-fDb-eNwz.mjs → drain-Dl2ZJKRA.mjs} +3 -3
- package/dist/{drain-fDb-eNwz.mjs.map → drain-Dl2ZJKRA.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +8 -7
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +11 -8
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/{enrich-error-stack-next.node-Dgm_rCf5.mjs → enrich-error-stack-next.node-KB0Oap7R.mjs} +1 -1
- package/dist/{enrich-error-stack-next.node-Dgm_rCf5.mjs.map → enrich-error-stack-next.node-KB0Oap7R.mjs.map} +1 -1
- package/dist/{enricher-CBRmQw6e.d.mts → enricher-BOzt2FCi.d.mts} +2 -2
- package/dist/{enricher-CBRmQw6e.d.mts.map → enricher-BOzt2FCi.d.mts.map} +1 -1
- package/dist/enrichers.d.mts +2 -2
- package/dist/{error-CpghjrkY.d.mts → error-BRymuv8U.d.mts} +2 -2
- package/dist/{error-CpghjrkY.d.mts.map → error-BRymuv8U.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-BLU4Tfpe.d.mts → errors-C7qA6CAi.d.mts} +2 -2
- package/dist/{errors-BLU4Tfpe.d.mts.map → errors-C7qA6CAi.d.mts.map} +1 -1
- package/dist/eve/index.d.mts +72 -0
- package/dist/eve/index.d.mts.map +1 -0
- package/dist/eve/index.mjs +593 -0
- package/dist/eve/index.mjs.map +1 -0
- package/dist/express/index.d.mts +4 -4
- package/dist/express/index.d.mts.map +1 -1
- package/dist/express/index.mjs +3 -3
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +4 -4
- package/dist/fastify/index.d.mts.map +1 -1
- package/dist/fastify/index.mjs +4 -2
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/fork-S9gpZmF8.mjs +72 -0
- package/dist/fork-S9gpZmF8.mjs.map +1 -0
- package/dist/hono/index.d.mts +3 -3
- package/dist/hono/index.d.mts.map +1 -1
- package/dist/hono/index.mjs +3 -6
- package/dist/hono/index.mjs.map +1 -1
- package/dist/{http-ChVS9GYc.mjs → http-pKfd8UDu.mjs} +2 -2
- package/dist/{http-ChVS9GYc.mjs.map → http-pKfd8UDu.mjs.map} +1 -1
- package/dist/http.d.mts +1 -1
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +3 -203
- package/dist/{instrumentation-create-BrjQtSKD.d.mts → instrumentation-create-gSDk0leX.d.mts} +2 -2
- package/dist/{instrumentation-create-BrjQtSKD.d.mts.map → instrumentation-create-gSDk0leX.d.mts.map} +1 -1
- package/dist/{integration-DYp2uw8O.mjs → integration-CeL90DtN.mjs} +3 -2
- package/dist/{integration-DYp2uw8O.mjs.map → integration-CeL90DtN.mjs.map} +1 -1
- package/dist/{logger-mHIWxBhJ.d.mts → logger-CxWz7QJ3.d.mts} +7 -3
- package/dist/logger-CxWz7QJ3.d.mts.map +1 -0
- package/dist/logger.d.mts +2 -2
- package/dist/logger.mjs +2 -2
- package/dist/{fork-CgGlAaHa.mjs → middleware-Cdebyfdt.mjs} +3 -69
- package/dist/middleware-Cdebyfdt.mjs.map +1 -0
- package/dist/{middleware-B_k4Mrzg.d.mts → middleware-fbtEs6bq.d.mts} +3 -3
- package/dist/middleware-fbtEs6bq.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +5 -5
- package/dist/nestjs/index.d.mts.map +1 -1
- package/dist/nestjs/index.mjs +4 -3
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +1 -1
- package/dist/next/index.d.mts +5 -5
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +3 -3
- package/dist/next/index.mjs.map +1 -1
- package/dist/next/instrumentation/create.d.mts +1 -1
- package/dist/next/instrumentation/create.mjs +3 -1
- package/dist/next/instrumentation/create.mjs.map +1 -1
- package/dist/next/instrumentation.d.mts +1 -1
- package/dist/next/stream.d.mts +1 -1
- package/dist/nitro/errorHandler.mjs +9 -4
- package/dist/nitro/errorHandler.mjs.map +1 -1
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/module.mjs +1 -1
- package/dist/nitro/plugin.mjs +2 -2
- package/dist/nitro/v3/errorHandler.d.mts +0 -10
- package/dist/nitro/v3/errorHandler.mjs +13 -3
- package/dist/nitro/v3/errorHandler.mjs.map +1 -1
- package/dist/nitro/v3/index.d.mts +2 -2
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/module.mjs +1 -1
- package/dist/nitro/v3/plugin.mjs +2 -2
- package/dist/nitro/v3/useLogger.d.mts +2 -2
- package/dist/nitro/v3/useLogger.d.mts.map +1 -1
- package/dist/nitro/v3/useLogger.mjs.map +1 -1
- package/dist/{nitro-_Hda8Deo.d.mts → nitro-CcN7HDnr.d.mts} +2 -2
- package/dist/{nitro-_Hda8Deo.d.mts.map → nitro-CcN7HDnr.d.mts.map} +1 -1
- package/dist/{nitro-ClRZLD1g.mjs → nitro-DYFCSlho.mjs} +30 -3
- package/dist/nitro-DYFCSlho.mjs.map +1 -0
- package/dist/{nitroConfigBridge-BkVWnSV3.mjs → nitroConfigBridge-lHPsE5zG.mjs} +8 -6
- package/dist/nitroConfigBridge-lHPsE5zG.mjs.map +1 -0
- package/dist/{nodeResponse-CIEEbrNE.mjs → nodeResponse-Dsl7gZUf.mjs} +1 -1
- package/dist/{nodeResponse-CIEEbrNE.mjs.map → nodeResponse-Dsl7gZUf.mjs.map} +1 -1
- package/dist/nuxt/module.d.mts +1 -1
- package/dist/nuxt/module.mjs +2 -2
- package/dist/orpc/index.d.mts +4 -4
- package/dist/orpc/index.d.mts.map +1 -1
- package/dist/orpc/index.mjs +2 -2
- package/dist/orpc/index.mjs.map +1 -1
- package/dist/{package-CNV_CXs8.mjs → package-Drb3wqqt.mjs} +2 -2
- package/dist/package-Drb3wqqt.mjs.map +1 -0
- package/dist/{parseError-BeBXEd2V.d.mts → parseError-BmoJ7XeF.d.mts} +2 -2
- package/dist/parseError-BmoJ7XeF.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +4 -4
- package/dist/react-router/index.d.mts.map +1 -1
- package/dist/react-router/index.mjs +3 -2
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/register-disk-snippet-B5svlT8-.mjs +21 -0
- package/dist/register-disk-snippet-B5svlT8-.mjs.map +1 -0
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +1 -1
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/server/useLogger.mjs.map +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/{storage-7X37OToT.mjs → storage-BeFdoGTI.mjs} +1 -1
- package/dist/storage-BeFdoGTI.mjs.map +1 -0
- package/dist/stream.d.mts +1 -1
- package/dist/stream.d.mts.map +1 -1
- package/dist/stream.mjs +7 -1
- package/dist/stream.mjs.map +1 -1
- package/dist/sveltekit/index.d.mts +3 -4
- package/dist/sveltekit/index.d.mts.map +1 -1
- package/dist/sveltekit/index.mjs +6 -3
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/toolkit.d.mts +7 -7
- package/dist/toolkit.d.mts.map +1 -1
- package/dist/toolkit.mjs +7 -6
- package/dist/types.d.mts +1 -1
- package/dist/{useLogger-Cfv8Ck8b.d.mts → useLogger-BupVLO9_.d.mts} +3 -3
- package/dist/useLogger-BupVLO9_.d.mts.map +1 -0
- package/dist/{utils-CJJG0ZYW.d.mts → utils-D_LY-J1k.d.mts} +2 -2
- package/dist/{utils-CJJG0ZYW.d.mts.map → utils-D_LY-J1k.d.mts.map} +1 -1
- package/dist/utils.d.mts +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/workers.d.mts +3 -3
- package/dist/workers.d.mts.map +1 -1
- package/dist/workers.mjs +1 -1
- package/dist/workers.mjs.map +1 -1
- package/package.json +25 -2
- package/dist/audit-BQt8yAHo.mjs.map +0 -1
- package/dist/audit-D7v6JHj0.d.mts.map +0 -1
- package/dist/fork-CgGlAaHa.mjs.map +0 -1
- package/dist/index-EvnrXvQM.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/logger-mHIWxBhJ.d.mts.map +0 -1
- package/dist/middleware-B_k4Mrzg.d.mts.map +0 -1
- package/dist/nitro-ClRZLD1g.mjs.map +0 -1
- package/dist/nitroConfigBridge-BkVWnSV3.mjs.map +0 -1
- package/dist/package-CNV_CXs8.mjs.map +0 -1
- package/dist/parseError-BeBXEd2V.d.mts.map +0 -1
- package/dist/storage-7X37OToT.mjs.map +0 -1
- 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
|