From 7f4c3ba1b214bb98fb13e340501821792cf7a8a1 Mon Sep 17 00:00:00 2001 From: jerry Date: Sat, 28 Feb 2026 14:33:10 +0800 Subject: [PATCH 01/18] feat(agent-tracing): add @eggjs/agent-tracing package for AI agent tracing Add tracing support for AI agents built with LangGraph and Claude Agent SDK. - LangGraphTracer: extends LangChain BaseTracer, hooks into graph lifecycle - ClaudeAgentTracer: converts Claude SDK messages to LangChain Run format - TraceSession: streaming support for real-time message processing - TracingService: shared log formatting, OSS upload, and log service sync - applyTracerConfig: shared configure() logic for both tracers - Comprehensive tests for configure, LangGraph integration, and Claude integration Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 929 +++++++++++++----- tegg/core/agent-tracing/module.yml | 3 + tegg/core/agent-tracing/package.json | 89 ++ .../agent-tracing/src/ClaudeAgentTracer.ts | 517 ++++++++++ .../core/agent-tracing/src/LangGraphTracer.ts | 82 ++ tegg/core/agent-tracing/src/TracingService.ts | 228 +++++ tegg/core/agent-tracing/src/claude.ts | 1 + tegg/core/agent-tracing/src/index.ts | 2 + tegg/core/agent-tracing/src/langgraph.ts | 1 + tegg/core/agent-tracing/src/types.ts | 188 ++++ .../test/claude-agent-integration.test.ts | 361 +++++++ .../core/agent-tracing/test/configure.test.ts | 229 +++++ .../test/langgraph-integration.test.ts | 315 ++++++ tegg/core/agent-tracing/test/test-utils.ts | 30 + tegg/core/agent-tracing/tsconfig.json | 3 + tegg/core/agent-tracing/vitest.config.ts | 9 + tsconfig.json | 3 + 17 files changed, 2759 insertions(+), 231 deletions(-) create mode 100644 tegg/core/agent-tracing/module.yml create mode 100644 tegg/core/agent-tracing/package.json create mode 100644 tegg/core/agent-tracing/src/ClaudeAgentTracer.ts create mode 100644 tegg/core/agent-tracing/src/LangGraphTracer.ts create mode 100644 tegg/core/agent-tracing/src/TracingService.ts create mode 100644 tegg/core/agent-tracing/src/claude.ts create mode 100644 tegg/core/agent-tracing/src/index.ts create mode 100644 tegg/core/agent-tracing/src/langgraph.ts create mode 100644 tegg/core/agent-tracing/src/types.ts create mode 100644 tegg/core/agent-tracing/test/claude-agent-integration.test.ts create mode 100644 tegg/core/agent-tracing/test/configure.test.ts create mode 100644 tegg/core/agent-tracing/test/langgraph-integration.test.ts create mode 100644 tegg/core/agent-tracing/test/test-utils.ts create mode 100644 tegg/core/agent-tracing/tsconfig.json create mode 100644 tegg/core/agent-tracing/vitest.config.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e682d6463..a2ddd7c956 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,7 +191,7 @@ catalogs: version: 2.1.4 body-parser: specifier: ^2.0.0 - version: 2.2.2 + version: 2.2.0 bytes: specifier: ^3.1.2 version: 3.1.2 @@ -335,7 +335,7 @@ catalogs: version: 2.2.0 http-errors: specifier: ^2.0.0 - version: 2.0.1 + version: 2.0.0 humanize-ms: specifier: ^2.0.0 version: 2.0.0 @@ -462,9 +462,6 @@ catalogs: onelogger: specifier: ^1.0.1 version: 1.0.1 - oss-client: - specifier: ^2.5.1 - version: 2.5.1 oxc-minify: specifier: ^0.105.0 version: 0.105.0 @@ -1170,7 +1167,7 @@ importers: version: 1.0.2 http-errors: specifier: 'catalog:' - version: 2.0.1 + version: 2.0.0 is-type-of: specifier: 'catalog:' version: 2.2.0 @@ -1354,7 +1351,7 @@ importers: dependencies: http-errors: specifier: 'catalog:' - version: 2.0.1 + version: 2.0.0 inflection: specifier: 'catalog:' version: 3.0.2 @@ -1422,7 +1419,7 @@ importers: version: 5.0.3 body-parser: specifier: 'catalog:' - version: 2.2.2 + version: 2.2.0 cookie-parser: specifier: 'catalog:' version: 1.4.7 @@ -2050,7 +2047,7 @@ importers: dependencies: vitepress: specifier: 'catalog:' - version: 2.0.0-alpha.15(@types/node@24.10.2)(axios@1.13.5)(esbuild@0.27.0)(jiti@2.6.1)(less@4.4.2)(nprogress@0.2.0)(oxc-minify@0.105.0)(postcss@8.5.6)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.2) + version: 2.0.0-alpha.15(@types/node@24.10.2)(esbuild@0.27.0)(jiti@2.6.1)(less@4.4.2)(nprogress@0.2.0)(oxc-minify@0.105.0)(postcss@8.5.6)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.2) vitepress-plugin-llms: specifier: 'catalog:' version: 1.10.0 @@ -2059,21 +2056,51 @@ importers: specifier: 'catalog:' version: 0.105.0 - tegg/core/agent-runtime: + tegg/core/agent-tracing: dependencies: + '@eggjs/background-task': + specifier: workspace:* + version: link:../background-task + '@eggjs/core-decorator': + specifier: workspace:* + version: link:../core-decorator '@eggjs/tegg-types': specifier: workspace:* version: link:../types - egg-logger: - specifier: 'catalog:' - version: 3.6.1 - oss-client: + ali-oss: + specifier: ^6.21.0 + version: 6.23.0 + onelogger: specifier: 'catalog:' - version: 2.5.1 + version: 1.0.1 + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: + '@anthropic-ai/claude-agent-sdk': + specifier: ^0.2.52 + version: 0.2.59(zod@4.3.6) + '@anthropic-ai/sdk': + specifier: ^0.78.0 + version: 0.78.0(zod@4.3.6) + '@eggjs/tegg-common-util': + specifier: workspace:* + version: link:../common-util + '@langchain/core': + specifier: ^1.1.1 + version: 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph': + specifier: ^0.2.74 + version: 0.2.74(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6)) + '@types/ali-oss': + specifier: ^6.16.0 + version: 6.23.3 '@types/node': specifier: 'catalog:' version: 24.10.2 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 typescript: specifier: 'catalog:' version: 5.9.3 @@ -2231,7 +2258,7 @@ importers: version: link:../types '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) is-type-of: specifier: 'catalog:' version: 2.2.0 @@ -2488,16 +2515,16 @@ importers: version: link:../types '@langchain/core': specifier: ^1.1.1 - version: 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) + version: 1.1.28(openai@6.25.0(zod@3.25.76)) '@langchain/langgraph': specifier: ^1.0.2 - version: 1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) + version: 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) '@langchain/openai': specifier: ^1.1.0 - version: 1.2.8(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(ws@8.19.0) + version: 1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) langchain: specifier: ^1.1.2 - version: 1.2.25(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(openai@6.22.0(ws@8.19.0)(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)) + version: 1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(openai@6.25.0(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)) devDependencies: '@eggjs/controller-decorator': specifier: workspace:* @@ -2566,10 +2593,10 @@ importers: version: link:../types '@langchain/mcp-adapters': specifier: ^1.0.0 - version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)) + version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)) '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@3.25.76) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76) urllib: specifier: 'catalog:' version: 4.8.2 @@ -2707,9 +2734,6 @@ importers: tegg/core/tegg: dependencies: - '@eggjs/agent-runtime': - specifier: workspace:* - version: link:../agent-runtime '@eggjs/ajv-decorator': specifier: workspace:* version: link:../ajv-decorator @@ -2826,7 +2850,7 @@ importers: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) devDependencies: '@types/node': specifier: 'catalog:' @@ -2985,9 +3009,6 @@ importers: tegg/plugin/controller: dependencies: - '@eggjs/agent-runtime': - specifier: workspace:* - version: link:../../core/agent-runtime '@eggjs/controller-decorator': specifier: workspace:* version: link:../../core/controller-decorator @@ -3020,7 +3041,7 @@ importers: version: link:../../core/types '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) await-event: specifier: 'catalog:' version: 2.1.0 @@ -3221,19 +3242,19 @@ importers: version: link:../../core/types '@langchain/core': specifier: ^1.1.1 - version: 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) + version: 1.1.28(openai@6.25.0(zod@4.3.6)) '@langchain/langgraph': specifier: ^1.0.2 - version: 1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) + version: 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) '@langchain/mcp-adapters': specifier: ^1.0.0 - version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)) + version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)) '@langchain/openai': specifier: ^1.0.0 - version: 1.2.8(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(ws@8.19.0) + version: 1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) langchain: specifier: ^1.1.2 - version: 1.2.25(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(openai@6.22.0(ws@8.19.0)(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)) + version: 1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(openai@6.25.0(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)) urllib: specifier: 'catalog:' version: 4.8.2 @@ -3267,7 +3288,7 @@ importers: version: link:../../../plugins/tracer '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) '@types/node': specifier: 'catalog:' version: 24.10.2 @@ -3294,7 +3315,7 @@ importers: version: link:../../core/runtime '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@3.25.76) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76) egg: specifier: workspace:* version: link:../../../packages/egg @@ -3331,7 +3352,7 @@ importers: version: link:../../core/types '@modelcontextprotocol/sdk': specifier: ^1.23.0 - version: 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) await-event: specifier: 'catalog:' version: 2.1.0 @@ -3787,6 +3808,21 @@ importers: packages: + '@anthropic-ai/claude-agent-sdk@0.2.59': + resolution: {integrity: sha512-xPOUZZimZI5ChaO791olWGXqaRvCwOfj9/1micu42EL9czdcwiDm0WK1OGsqb2mZ7LSCoYWBB0ZHVKOxehemDA==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^4.0.0 + + '@anthropic-ai/sdk@0.78.0': + resolution: {integrity: sha512-PzQhR715td/m1UaaN5hHXjYB8Gl2lF9UVhrrGrZeysiF6Rb74Wc9GCB8hzLdzmQtBd1qe89F9OptgB9Za1Ib5w==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} @@ -3804,6 +3840,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} @@ -4201,6 +4241,105 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@ioredis/as-callback@3.0.0': resolution: {integrity: sha512-Kqv1rZ3WbgOrS+hgzJ5xG5WQuhvzzSTRYvNeyPMLOAM78MHSnuKI20JeJGbpuAt//LCuP0vsexZcorqW7kWhJg==} @@ -4260,19 +4399,38 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@langchain/core@1.1.26': - resolution: {integrity: sha512-Xnwi4xEKEtZcGwjW5xpZVP/Dc+WckFxULMShETuCpD6TxNFS6yRM+FhNUO1DDCkRkGn9b1fuzVZrNYb9W7F32A==} + '@langchain/core@1.1.28': + resolution: {integrity: sha512-6FAGdezEp8zHY92LtnsAiv54KaG41nBdsuukk+R+1484edV20cVOyIc36ANuGKPx0pmYFCBWhCUdO0jxB/zn2Q==} engines: {node: '>=20'} + '@langchain/langgraph-checkpoint@0.0.18': + resolution: {integrity: sha512-IS7zJj36VgY+4pf8ZjsVuUWef7oTwt1y9ylvwu0aLuOn1d0fg05Om9DLm3v2GZ2Df6bhLV1kfWAM0IAl9O5rQQ==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.2.31 <0.4.0' + '@langchain/langgraph-checkpoint@1.0.0': resolution: {integrity: sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==} engines: {node: '>=18'} peerDependencies: '@langchain/core': ^1.0.1 - '@langchain/langgraph-sdk@2.0.0': - resolution: {integrity: sha512-Xdkl1hve84ZGQ7fgpiBIBvjODhtjbPPccY4snOtYgSdzRXZkESsi2Y7RDKgFe1nC9+DbX+QaYom0raD/XFBKAw==} - deprecated: This version is not intended for use. Please use 1.x versions of @langchain/langgraph-sdk instead + '@langchain/langgraph-sdk@0.0.112': + resolution: {integrity: sha512-/9W5HSWCqYgwma6EoOspL4BGYxGxeJP6lIquPSF4FA0JlKopaUv58ucZC3vAgdJyCgg6sorCIV/qg7SGpEcCLw==} + peerDependencies: + '@langchain/core': '>=0.2.31 <0.4.0' + react: ^18 || ^19 + react-dom: ^18 || ^19 + peerDependenciesMeta: + '@langchain/core': + optional: true + react: + optional: true + react-dom: + optional: true + + '@langchain/langgraph-sdk@1.6.5': + resolution: {integrity: sha512-JjprmbhgCnoNJ9DUKcvrEU+C9FfKsNGyT3ooqWxAY5Cx2qofhXmDJOpTCqqbxfDHPKG0RjTs5HgVK3WW5M6Big==} peerDependencies: '@langchain/core': ^1.1.16 react: ^18 || ^19 @@ -4285,8 +4443,18 @@ packages: react-dom: optional: true - '@langchain/langgraph@1.1.5': - resolution: {integrity: sha512-uJC/asydf/GoHpo9x42lf9hs8ufCkMuJ9sDle5ybP7sMD0XryOfE0E4J3deARk9ZadCCt6zeCoCNu/mTbx8+Sg==} + '@langchain/langgraph@0.2.74': + resolution: {integrity: sha512-oHpEi5sTZTPaeZX1UnzfM2OAJ21QGQrwReTV6+QnX7h8nDCBzhtipAw1cK616S+X8zpcVOjgOtJuaJhXa4mN8w==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.2.36 <0.3.0 || >=0.3.40 < 0.4.0' + zod-to-json-schema: ^3.x + peerDependenciesMeta: + zod-to-json-schema: + optional: true + + '@langchain/langgraph@1.2.0': + resolution: {integrity: sha512-wyqKIzXTAfXX3L1d8R7icM+HmRQBTbuNLWtUlpRJ/JP/ux1ei/sOSt6p8f90ARoOP2iJVlM70wOBYWaGErdBlA==} engines: {node: '>=18'} peerDependencies: '@langchain/core': ^1.1.16 @@ -4303,14 +4471,14 @@ packages: '@langchain/core': ^1.0.0 '@langchain/langgraph': ^1.0.0 - '@langchain/openai@1.2.8': - resolution: {integrity: sha512-qliwC7sb7/Kw0tsl/EiMchMThKt62rZbyofKXtxPwYBte3BMzMXo2HKaEFvAN2QHVOuDi4voqQ7ZlRXc/o2e8w==} + '@langchain/openai@1.2.11': + resolution: {integrity: sha512-//a3OcvHKuz9QXAn3QJjASdtSyyF1JmsZKVzuUa2uR2YLFAS+XMZYLfel9RaNdt8LWkwhSpCiVvegqUGRBU+uw==} engines: {node: '>=20'} peerDependencies: - '@langchain/core': ^1.0.0 + '@langchain/core': ^1.1.29 - '@modelcontextprotocol/sdk@1.26.0': - resolution: {integrity: sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==} + '@modelcontextprotocol/sdk@1.27.1': + resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -5060,6 +5228,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -5188,6 +5359,9 @@ packages: '@types/accepts@1.3.7': resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + '@types/ali-oss@6.23.3': + resolution: {integrity: sha512-huSf6njkqhSeR37OMJTGMCyUg2d9Zp2AioefhYuUMeh0vNM+WybctrBor7bO/RcCbLCQI6sHn6NtPBIWALYVJg==} + '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -5369,6 +5543,9 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/safe-timers@1.1.2': resolution: {integrity: sha512-NkLK2vnHShzLyef0eCMT5ocUJOQNB3Ppq3nKOCHPoxCs5PAJV/AH5DyWTKYHpaGlicE9qmC3MwNRNvt3oML76w==} @@ -5636,6 +5813,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + address@2.0.3: resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==} engines: {node: '>= 16.0.0'} @@ -5644,6 +5825,10 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} + agentkeepalive@3.5.3: + resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==} + engines: {node: '>= 4.0.0'} + agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -5676,6 +5861,10 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ali-oss@6.23.0: + resolution: {integrity: sha512-FipRmyd16Pr/tEey/YaaQ/24Pc3HEpLM9S1DRakEuXlSLXNIJnu1oJtHM53eVYpvW3dXapSjrip3xylZUTIZVQ==} + engines: {node: '>=8'} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -5704,6 +5893,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} @@ -5803,9 +5996,6 @@ packages: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} - axios@1.13.5: - resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -5849,6 +6039,10 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} @@ -5856,6 +6050,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@1.9.4: + resolution: {integrity: sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -5878,6 +6075,9 @@ packages: bug-versions@1.117.0: resolution: {integrity: sha512-c2F2PKelgC9zSy+AwdD6w/HQzdfofyZ7641jYkTxmIYxOgWDYPlVlEYa/puE/11K4pzQfxviI0loVyo1YuZVNQ==} + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} @@ -6284,6 +6484,9 @@ packages: resolution: {integrity: sha512-5dVBvpBLBnPwSsYXqfybFyehMmC/EenKEcf23AhCTgTf48JFBbmJKqoZBsERDnjL0FyiVTYWdFsRfTLHxLyKdQ==} engines: {node: '>=6'} + dateformat@2.2.0: + resolution: {integrity: sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==} + dayjs@1.11.18: resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} @@ -6517,6 +6720,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + end-or-error@1.0.1: + resolution: {integrity: sha512-OclLMSug+k2A0JKuf494im25ANRBVW8qsjmwbgX7lQ8P82H21PQ1PWkoYwb9y5yMBS69BPlwtzdIFClo3+7kOQ==} + engines: {node: '>= 0.11.14'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -6750,15 +6957,6 @@ packages: focus-trap@7.6.6: resolution: {integrity: sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -6770,8 +6968,8 @@ packages: form-data-encoder@1.9.0: resolution: {integrity: sha512-rahaRMkN8P8d/tgK/BLPX+WBVM27NbvdXBxqQujBtkDAIFspaRqN7Od7lfdGQA6KAD+f82fYCLBq1ipvcu8qLw==} - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} format@0.2.2: @@ -6870,6 +7068,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-ready@1.0.0: + resolution: {integrity: sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==} + get-ready@3.4.0: resolution: {integrity: sha512-D7N1WED4f5rQUveyl19GxfJupMeR+y106EVK6na/zI+w34FcTAShvetX0X2k61x+lJXCEiOnGHk0xro6YRyrjQ==} engines: {node: '>= 16.13.0'} @@ -6891,13 +7092,11 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@11.0.3: resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@13.0.0: @@ -6906,12 +7105,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + deprecated: Glob versions prior to v9 are no longer supported globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -6990,8 +7189,8 @@ packages: heredoc@1.3.1: resolution: {integrity: sha512-VL/rh/EXkhgpIWSNNde3OPc067oQiorfY+Nhkwgo2jAAIgrLLb5N92wmOblTyMWwCcIKo+8aQzk5s5YxLbVJPQ==} - hono@4.11.10: - resolution: {integrity: sha512-kyWP5PAiMooEvGrA9jcD3IXF7ATu8+o7B3KCbPXid5se52NPqnOpM/r9qeW2heMnOekF4kqR1fXJqCYeCLKrZg==} + hono@4.12.3: + resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} engines: {node: '>=16.9.0'} hookable@5.5.3: @@ -7243,8 +7442,8 @@ packages: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} - is-network-error@1.3.0: - resolution: {integrity: sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==} + is-network-error@1.3.1: + resolution: {integrity: sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==} engines: {node: '>=16'} is-number-object@1.1.1: @@ -7347,6 +7546,9 @@ packages: isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -7403,6 +7605,9 @@ packages: jose@6.1.3: resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-base64@2.6.4: + resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} + js-beautify@1.15.4: resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} engines: {node: '>=14'} @@ -7435,6 +7640,10 @@ packages: resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -7463,6 +7672,9 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jstoxml@2.2.9: + resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==} + junk@4.0.1: resolution: {integrity: sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==} engines: {node: '>=12.20'} @@ -7476,6 +7688,7 @@ packages: keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} @@ -7512,14 +7725,14 @@ packages: resolution: {integrity: sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==} engines: {node: '>= 7.6.0'} - langchain@1.2.25: - resolution: {integrity: sha512-29qay7nZxkmkH3PRp8cjBpZmGCmA3EW8JwEYqZa0a5CW38nvO2Tr1rbFd26cnlLcxtA5hBRH57/XSPoWLjHJSw==} + langchain@1.2.28: + resolution: {integrity: sha512-MDJO14BMXtWYchBEmMaYU2CNe1b82L+6Oba4QyIr5Z1S4oCseMtqguZiKCIkv/K99ZGoe1iVtxPtN1Ja3mHvDQ==} engines: {node: '>=20'} peerDependencies: - '@langchain/core': ^1.1.26 + '@langchain/core': ^1.1.29 - langsmith@0.5.4: - resolution: {integrity: sha512-qYkNIoKpf0ZYt+cYzrDV+XI3FCexApmZmp8EMs3eDTMv0OvrHMLoxJ9IpkeoXJSX24+GPk0/jXjKx2hWerpy9w==} + langsmith@0.5.7: + resolution: {integrity: sha512-FjYf2oBGMoSXnaT4SRaFguIiGJaonZ5VKWKJDPl9awLZjz2RkN29AcQWceecSINVzXzTvtRWPOjAWT+XggqNNg==} peerDependencies: '@opentelemetry/api': '*' '@opentelemetry/exporter-trace-otlp-proto': '*' @@ -7923,11 +8136,6 @@ packages: engines: {node: '>=4.0.0'} hasBin: true - mime@3.0.0: - resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} - engines: {node: '>=10.0.0'} - hasBin: true - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -8275,8 +8483,8 @@ packages: oniguruma-to-es@4.3.3: resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} - openai@6.22.0: - resolution: {integrity: sha512-7Yvy17F33Bi9RutWbsaYt5hJEEJ/krRPOrwan+f9aCPuMat1WVsb2VNSII5W1EksKT6fF69TG/xj4XzodK3JZw==} + openai@6.25.0: + resolution: {integrity: sha512-mEh6VZ2ds2AGGokWARo18aPISI1OhlgdEIC1ewhkZr8pSIT31dec0ecr9Nhxx0JlybyOgoAT1sWeKtwPZzJyww==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -8296,13 +8504,6 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - oss-client@2.5.1: - resolution: {integrity: sha512-LSm191DdZm7E/jKhaGLBf/EjrA+6E+RjTjHjq8dvEtMb9hi4O1FWe6IwSncPHCwHXgmVOk3iHll87WqbNN4NXw==} - engines: {node: '>= 16.0.0'} - - oss-interface@1.5.0: - resolution: {integrity: sha512-NqVR42QclE/EosbfzKfrJ8OUPHsHt1T6dMoXnLz7lOtHcefMGgtaN70yRMMdhL0/bt1VF59jh+4lwdP3PrhHqg==} - osx-release@1.1.0: resolution: {integrity: sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==} engines: {node: '>=0.10.0'} @@ -8382,6 +8583,10 @@ packages: resolution: {integrity: sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==} engines: {node: '>=20'} + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + p-retry@7.1.1: resolution: {integrity: sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==} engines: {node: '>=20'} @@ -8582,6 +8787,9 @@ packages: resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + promise-all-reject-late@1.0.1: resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} @@ -8610,9 +8818,6 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -8632,6 +8837,10 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} @@ -8685,6 +8894,9 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -8774,6 +8986,10 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -8878,6 +9094,9 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -8899,6 +9118,9 @@ packages: sax@1.4.3: resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + sdk-base@2.0.1: + resolution: {integrity: sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==} + sdk-base@3.6.0: resolution: {integrity: sha512-jxHUIrRLlAoRFRwiXKhOGjd6BeFWO/jz7tv+E7lbMSef6F9jzFN2Sv3hLW58oDDKscKaBGG6vQdkbXn7isE7fw==} @@ -9147,9 +9369,16 @@ packages: stream-combiner@0.2.2: resolution: {integrity: sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==} + stream-http@2.8.2: + resolution: {integrity: sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==} + stream-slice@0.1.2: resolution: {integrity: sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==} + stream-wormhole@1.1.0: + resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==} + engines: {node: '>=4.0.0'} + stream-wormhole@2.0.1: resolution: {integrity: sha512-B31SiV7tNxOGqvC367O1dVL4nWBgCT7xqtHtikvxUkrbxgvtUemYc6WyO8+Z5cLW8/HUvYmNkgZIjTXYLIDYVw==} engines: {node: '>=16.0.0'} @@ -9182,6 +9411,9 @@ packages: resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} engines: {node: '>=20'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -9265,7 +9497,6 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me tcp-base@3.2.0: resolution: {integrity: sha512-fFAqH8QTbheuEbXLdbxTSe31Gkw6Lg3nq4loyrxIXM6+ILGdbYXEblgyuu7UltOkOHbP/q2iqaC+gIXXu0C5bg==} @@ -9320,6 +9551,9 @@ packages: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} + to-arraybuffer@1.0.1: + resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -9349,6 +9583,9 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -9540,6 +9777,15 @@ packages: urijs@1.19.11: resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} + urllib@2.44.0: + resolution: {integrity: sha512-zRCJqdfYllRDA9bXUtx+vccyRqtJPKsw85f44zH7zPD28PIvjMqIgw9VwoTLV7xTBWZsbebUFVHU5ghQcWku2A==} + engines: {node: '>= 0.10.0'} + peerDependencies: + proxy-agent: ^5.0.0 + peerDependenciesMeta: + proxy-agent: + optional: true + urllib@3.27.3: resolution: {integrity: sha512-24ht/hHAkhWXbqSCM499B8gFPXaf4aFQJNqJH5pMsvfyo+aM3zKuQ/uiZ/mdz41Nnw6ItuiqMVMK5VDAlAHLlw==} engines: {node: '>= 14.19.3'} @@ -9567,10 +9813,18 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + uuid@13.0.0: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -9761,18 +10015,6 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xml2js@0.6.2: resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} @@ -9786,6 +10028,10 @@ packages: engines: {node: '>= 0.10.0'} hasBin: true + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -9855,6 +10101,26 @@ packages: snapshots: + '@anthropic-ai/claude-agent-sdk@0.2.59(zod@4.3.6)': + dependencies: + zod: 4.3.6 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + + '@anthropic-ai/sdk@0.78.0(zod@4.3.6)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 4.3.6 + '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -9871,6 +10137,8 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/runtime@7.28.6': {} + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -10106,9 +10374,9 @@ snapshots: '@hapi/bourne@3.0.0': {} - '@hono/node-server@1.19.9(hono@4.11.10)': + '@hono/node-server@1.19.9(hono@4.12.3)': dependencies: - hono: 4.11.10 + hono: 4.12.3 '@iconify-json/simple-icons@1.2.60': dependencies: @@ -10116,6 +10384,68 @@ snapshots: '@iconify/types@2.0.0': {} + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + '@ioredis/as-callback@3.0.0': {} '@ioredis/commands@1.4.0': {} @@ -10189,14 +10519,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76))': + '@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 - ansi-styles: 6.2.3 + ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.5.4(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) + langsmith: 0.5.7(openai@6.25.0(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 uuid: 10.0.0 @@ -10207,14 +10537,14 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6))': + '@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))': dependencies: '@cfworker/json-schema': 4.1.1 - ansi-styles: 6.2.3 + ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.5.4(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) + langsmith: 0.5.7(openai@6.25.0(zod@4.3.6)) mustache: 4.2.0 p-queue: 6.6.2 uuid: 10.0.0 @@ -10225,39 +10555,66 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))': + '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + dependencies: + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + uuid: 10.0.0 + + '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) uuid: 10.0.0 - '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))': + '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) uuid: 10.0.0 - '@langchain/langgraph-sdk@2.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))': + '@langchain/langgraph-sdk@0.0.112(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + dependencies: + '@types/json-schema': 7.0.15 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 9.0.1 + optionalDependencies: + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + + '@langchain/langgraph-sdk@1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))': dependencies: '@types/json-schema': 7.0.15 p-queue: 9.1.0 p-retry: 7.1.1 uuid: 13.0.0 optionalDependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) - '@langchain/langgraph-sdk@2.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))': + '@langchain/langgraph-sdk@1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': dependencies: '@types/json-schema': 7.0.15 p-queue: 9.1.0 p-retry: 7.1.1 uuid: 13.0.0 optionalDependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)': + '@langchain/langgraph@0.2.74(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76))) - '@langchain/langgraph-sdk': 2.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76))) + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + '@langchain/langgraph-sdk': 0.0.112(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + uuid: 10.0.0 + zod: 3.25.76 + optionalDependencies: + zod-to-json-schema: 3.25.1(zod@4.3.6) + transitivePeerDependencies: + - react + - react-dom + + '@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)': + dependencies: + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 3.25.76 @@ -10267,11 +10624,11 @@ snapshots: - react - react-dom - '@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6)': + '@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6)': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76))) - '@langchain/langgraph-sdk': 2.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76))) + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 4.3.6 @@ -10281,11 +10638,11 @@ snapshots: - react - react-dom - '@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)': + '@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6))) - '@langchain/langgraph-sdk': 2.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6))) + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 4.3.6 @@ -10295,11 +10652,11 @@ snapshots: - react - react-dom - '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76))': + '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) - '@langchain/langgraph': 1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) - '@modelcontextprotocol/sdk': 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) + '@modelcontextprotocol/sdk': 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) debug: 4.4.3(supports-color@8.1.1) zod: 4.3.6 optionalDependencies: @@ -10308,11 +10665,11 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(@langchain/langgraph@1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6))': + '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) - '@langchain/langgraph': 1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) - '@modelcontextprotocol/sdk': 1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) + '@modelcontextprotocol/sdk': 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) debug: 4.4.3(supports-color@8.1.1) zod: 4.3.6 optionalDependencies: @@ -10321,27 +10678,27 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@langchain/openai@1.2.8(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(ws@8.19.0)': + '@langchain/openai@1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) js-tiktoken: 1.0.21 - openai: 6.22.0(ws@8.19.0)(zod@4.3.6) + openai: 6.25.0(zod@4.3.6) zod: 4.3.6 transitivePeerDependencies: - ws - '@langchain/openai@1.2.8(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(ws@8.19.0)': + '@langchain/openai@1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) js-tiktoken: 1.0.21 - openai: 6.22.0(ws@8.19.0)(zod@4.3.6) + openai: 6.25.0(zod@4.3.6) zod: 4.3.6 transitivePeerDependencies: - ws - '@modelcontextprotocol/sdk@1.26.0(@cfworker/json-schema@4.1.1)(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.9(hono@4.11.10) + '@hono/node-server': 1.19.9(hono@4.12.3) ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 @@ -10351,7 +10708,7 @@ snapshots: eventsource-parser: 3.0.6 express: 5.2.1 express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.11.10 + hono: 4.12.3 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -10363,9 +10720,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@modelcontextprotocol/sdk@1.26.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)': + '@modelcontextprotocol/sdk@1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.9(hono@4.11.10) + '@hono/node-server': 1.19.9(hono@4.12.3) ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 @@ -10375,7 +10732,7 @@ snapshots: eventsource-parser: 3.0.6 express: 5.2.1 express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.11.10 + hono: 4.12.3 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -10963,6 +11320,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} '@swc-node/core@1.14.1(@swc/core@1.15.3)(@swc/types@0.1.25)': @@ -11068,6 +11427,8 @@ snapshots: dependencies: '@types/node': 24.10.2 + '@types/ali-oss@6.23.3': {} + '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -11252,6 +11613,8 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/retry@0.12.0': {} + '@types/safe-timers@1.1.2': {} '@types/send@0.17.5': @@ -11280,7 +11643,7 @@ snapshots: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 '@types/node': 24.10.2 - form-data: 4.0.5 + form-data: 4.0.4 '@types/type-is@1.6.7': dependencies: @@ -11364,7 +11727,7 @@ snapshots: '@vitest/expect@4.0.15': dependencies: - '@standard-schema/spec': 1.1.0 + '@standard-schema/spec': 1.0.0 '@types/chai': 5.2.3 '@vitest/spy': 4.0.15 '@vitest/utils': 4.0.15 @@ -11491,13 +11854,12 @@ snapshots: '@vueuse/shared': 14.0.0(vue@3.5.25(typescript@5.9.3)) vue: 3.5.25(typescript@5.9.3) - '@vueuse/integrations@14.0.0(axios@1.13.5)(focus-trap@7.6.6)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3))': + '@vueuse/integrations@14.0.0(focus-trap@7.6.6)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3))': dependencies: '@vueuse/core': 14.0.0(vue@3.5.25(typescript@5.9.3)) '@vueuse/shared': 14.0.0(vue@3.5.25(typescript@5.9.3)) vue: 3.5.25(typescript@5.9.3) optionalDependencies: - axios: 1.13.5 focus-trap: 7.6.6 nprogress: 0.2.0 @@ -11535,6 +11897,8 @@ snapshots: acorn@8.15.0: {} + address@1.2.2: {} + address@2.0.3: {} agent-base@6.0.2: @@ -11543,6 +11907,10 @@ snapshots: transitivePeerDependencies: - supports-color + agentkeepalive@3.5.3: + dependencies: + humanize-ms: 1.2.1 + agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -11572,6 +11940,37 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ali-oss@6.23.0: + dependencies: + address: 1.2.2 + agentkeepalive: 3.5.3 + bowser: 1.9.4 + copy-to: 2.0.1 + dateformat: 2.2.0 + debug: 4.4.3(supports-color@8.1.1) + destroy: 1.2.0 + end-or-error: 1.0.1 + get-ready: 1.0.0 + humanize-ms: 1.2.1 + is-type-of: 1.4.0 + js-base64: 2.6.4 + jstoxml: 2.2.9 + lodash: 4.17.21 + merge-descriptors: 1.0.3 + mime: 2.6.0 + platform: 1.3.6 + pump: 3.0.3 + qs: 6.14.0 + sdk-base: 2.0.1 + stream-http: 2.8.2 + stream-wormhole: 1.1.0 + urllib: 2.44.0 + utility: 1.18.0 + xml2js: 0.6.2 + transitivePeerDependencies: + - proxy-agent + - supports-color + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -11594,6 +11993,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} ansis@3.17.0: {} @@ -11674,15 +12075,6 @@ snapshots: aws-ssl-profiles@1.1.2: {} - axios@1.13.5: - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - optional: true - bail@2.0.2: {} balanced-match@1.0.2: {} @@ -11740,12 +12132,26 @@ snapshots: transitivePeerDependencies: - supports-color + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3(supports-color@8.1.1) + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.1 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + body-parser@2.2.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3(supports-color@8.1.1) - http-errors: 2.0.1 + http-errors: 2.0.0 iconv-lite: 0.7.0 on-finished: 2.4.1 qs: 6.15.0 @@ -11756,6 +12162,8 @@ snapshots: boolbase@1.0.0: {} + bowser@1.9.4: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -11780,6 +12188,8 @@ snapshots: bug-versions@1.117.0: {} + builtin-status-codes@3.0.0: {} + builtins@1.0.3: {} busboy@1.6.0: @@ -12054,7 +12464,7 @@ snapshots: dependencies: '@hapi/bourne': 3.0.0 inflation: 2.1.0 - qs: 6.15.0 + qs: 6.14.0 raw-body: 2.5.2 type-is: 1.6.18 @@ -12261,6 +12671,8 @@ snapshots: dargs@6.1.0: {} + dateformat@2.2.0: {} + dayjs@1.11.18: {} debounce@3.0.0: {} @@ -12481,6 +12893,8 @@ snapshots: dependencies: once: 1.4.0 + end-or-error@1.0.1: {} + entities@4.5.0: {} entities@6.0.1: {} @@ -12717,14 +13131,14 @@ snapshots: etag: 1.8.1 finalhandler: 2.1.1 fresh: 2.0.0 - http-errors: 2.0.1 + http-errors: 2.0.0 merge-descriptors: 2.0.0 mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.0 + qs: 6.14.0 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -12839,9 +13253,6 @@ snapshots: dependencies: tabbable: 6.3.0 - follow-redirects@1.15.11: - optional: true - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -12853,7 +13264,7 @@ snapshots: form-data-encoder@1.9.0: {} - form-data@4.0.5: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -12966,6 +13377,8 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-ready@1.0.0: {} + get-ready@3.4.0: {} get-stream@6.0.1: {} @@ -13113,7 +13526,7 @@ snapshots: heredoc@1.3.1: {} - hono@4.11.10: {} + hono@4.12.3: {} hookable@5.5.3: {} @@ -13355,7 +13768,7 @@ snapshots: is-map@2.0.3: {} - is-network-error@1.3.0: {} + is-network-error@1.3.1: {} is-number-object@1.1.1: dependencies: @@ -13440,6 +13853,8 @@ snapshots: isarray@0.0.1: {} + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} @@ -13505,6 +13920,8 @@ snapshots: jose@6.1.3: {} + js-base64@2.6.4: {} + js-beautify@1.15.4: dependencies: config-chain: 1.1.13 @@ -13534,6 +13951,11 @@ snapshots: json-parse-even-better-errors@3.0.2: {} + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.28.6 + ts-algebra: 2.0.0 + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} @@ -13552,6 +13974,8 @@ snapshots: jsonparse@1.3.1: {} + jstoxml@2.2.9: {} + junk@4.0.1: {} just-diff-apply@5.5.0: {} @@ -13604,13 +14028,13 @@ snapshots: transitivePeerDependencies: - supports-color - langchain@1.2.25(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(openai@6.22.0(ws@8.19.0)(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)): + langchain@1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(openai@6.25.0(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)): dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) - '@langchain/langgraph': 1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@3.25.76))) - langsmith: 0.5.4(openai@6.22.0(ws@8.19.0)(zod@3.25.76)) - uuid: 10.0.0 + '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + langsmith: 0.5.7(openai@6.25.0(zod@3.25.76)) + uuid: 11.1.0 zod: 4.3.6 transitivePeerDependencies: - '@opentelemetry/api' @@ -13621,13 +14045,13 @@ snapshots: - react-dom - zod-to-json-schema - langchain@1.2.25(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(openai@6.22.0(ws@8.19.0)(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)): + langchain@1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(openai@6.25.0(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)): dependencies: - '@langchain/core': 1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) - '@langchain/langgraph': 1.1.5(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.26(openai@6.22.0(ws@8.19.0)(zod@4.3.6))) - langsmith: 0.5.4(openai@6.22.0(ws@8.19.0)(zod@4.3.6)) - uuid: 10.0.0 + '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + langsmith: 0.5.7(openai@6.25.0(zod@4.3.6)) + uuid: 11.1.0 zod: 4.3.6 transitivePeerDependencies: - '@opentelemetry/api' @@ -13638,27 +14062,27 @@ snapshots: - react-dom - zod-to-json-schema - langsmith@0.5.4(openai@6.22.0(ws@8.19.0)(zod@3.25.76)): + langsmith@0.5.7(openai@6.25.0(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 - chalk: 4.1.2 + chalk: 5.6.2 console-table-printer: 2.15.0 p-queue: 6.6.2 semver: 7.7.3 uuid: 10.0.0 optionalDependencies: - openai: 6.22.0(ws@8.19.0)(zod@3.25.76) + openai: 6.25.0(zod@3.25.76) - langsmith@0.5.4(openai@6.22.0(ws@8.19.0)(zod@4.3.6)): + langsmith@0.5.7(openai@6.25.0(zod@4.3.6)): dependencies: '@types/uuid': 10.0.0 - chalk: 4.1.2 + chalk: 5.6.2 console-table-printer: 2.15.0 p-queue: 6.6.2 semver: 7.7.3 uuid: 10.0.0 optionalDependencies: - openai: 6.22.0(ws@8.19.0)(zod@4.3.6) + openai: 6.25.0(zod@4.3.6) leoric@2.13.8(mysql2@3.15.2): dependencies: @@ -14156,8 +14580,6 @@ snapshots: mime@2.6.0: {} - mime@3.0.0: {} - mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -14566,15 +14988,13 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 - openai@6.22.0(ws@8.19.0)(zod@3.25.76): + openai@6.25.0(zod@3.25.76): optionalDependencies: - ws: 8.19.0 zod: 3.25.76 optional: true - openai@6.22.0(ws@8.19.0)(zod@4.3.6): + openai@6.25.0(zod@4.3.6): optionalDependencies: - ws: 8.19.0 zod: 4.3.6 ora@4.1.1: @@ -14593,21 +15013,6 @@ snapshots: osx-release: 1.1.0 win-release: 1.1.1 - oss-client@2.5.1: - dependencies: - is-type-of: 2.2.0 - mime: 3.0.0 - ms: 2.1.3 - oss-interface: 1.5.0 - stream-wormhole: 2.0.1 - urllib: 4.8.2 - utility: 2.5.0 - xml2js: 0.6.2 - - oss-interface@1.5.0: - dependencies: - type-fest: 4.41.0 - osx-release@1.1.0: dependencies: minimist: 1.2.8 @@ -14730,9 +15135,14 @@ snapshots: eventemitter3: 5.0.1 p-timeout: 7.0.1 + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + p-retry@7.1.1: dependencies: - is-network-error: 1.3.0 + is-network-error: 1.3.1 p-timeout@3.2.0: dependencies: @@ -14908,6 +15318,8 @@ snapshots: proc-log@3.0.0: {} + process-nextick-args@2.0.1: {} + promise-all-reject-late@1.0.1: {} promise-call-limit@1.0.2: {} @@ -14928,9 +15340,6 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - proxy-from-env@1.1.0: - optional: true - prr@1.0.1: optional: true @@ -14952,6 +15361,10 @@ snapshots: dependencies: side-channel: 1.1.0 + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -15007,6 +15420,16 @@ snapshots: normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -15114,6 +15537,8 @@ snapshots: retry@0.12.0: {} + retry@0.13.1: {} + reusify@1.1.0: {} rfdc@1.4.1: {} @@ -15226,6 +15651,8 @@ snapshots: dependencies: mri: 1.2.0 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-regex-test@1.1.0: @@ -15249,6 +15676,10 @@ snapshots: sax@1.4.3: {} + sdk-base@2.0.1: + dependencies: + get-ready: 1.0.0 + sdk-base@3.6.0: dependencies: await-event: 2.1.0 @@ -15552,8 +15983,18 @@ snapshots: duplexer: 0.1.2 through: 2.3.8 + stream-http@2.8.2: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 2.3.8 + to-arraybuffer: 1.0.1 + xtend: 4.0.2 + stream-slice@0.1.2: {} + stream-wormhole@1.1.0: {} + stream-wormhole@2.0.1: {} streamsearch@1.1.0: {} @@ -15589,6 +16030,10 @@ snapshots: get-east-asian-width: 1.4.0 strip-ansi: 7.1.2 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -15628,11 +16073,11 @@ snapshots: cookiejar: 2.1.4 debug: 4.4.3(supports-color@8.1.1) fast-safe-stringify: 2.1.1 - form-data: 4.0.5 + form-data: 4.0.4 formidable: 3.5.4 methods: 1.1.2 mime: 2.6.0 - qs: 6.15.0 + qs: 6.14.0 transitivePeerDependencies: - supports-color @@ -15731,6 +16176,8 @@ snapshots: tmp@0.2.5: {} + to-arraybuffer@1.0.1: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -15749,6 +16196,8 @@ snapshots: trough@2.2.0: {} + ts-algebra@2.0.0: {} + ts-node@10.9.2(@swc/core@1.15.3)(@types/node@24.10.2)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -15961,6 +16410,21 @@ snapshots: urijs@1.19.11: {} + urllib@2.44.0: + dependencies: + any-promise: 1.3.0 + content-type: 1.0.5 + default-user-agent: 1.0.0 + digest-header: 1.1.0 + ee-first: 1.1.1 + formstream: 1.5.2 + humanize-ms: 1.2.1 + iconv-lite: 0.6.3 + pump: 3.0.3 + qs: 6.14.0 + statuses: 1.5.0 + utility: 1.18.0 + urllib@3.27.3: dependencies: default-user-agent: 1.0.0 @@ -15970,17 +16434,17 @@ snapshots: formstream: 1.5.2 mime-types: 2.1.35 pump: 3.0.3 - qs: 6.15.0 + qs: 6.14.0 type-fest: 4.41.0 undici: 5.29.0 ylru: 1.4.0 urllib@4.8.2: dependencies: - form-data: 4.0.5 + form-data: 4.0.4 formstream: 1.5.2 mime-types: 2.1.35 - qs: 6.15.0 + qs: 6.14.0 type-fest: 4.41.0 undici: 7.16.0 ylru: 2.0.0 @@ -16005,8 +16469,12 @@ snapshots: uuid@10.0.0: {} + uuid@11.1.0: {} + uuid@13.0.0: {} + uuid@9.0.1: {} + v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: @@ -16059,7 +16527,7 @@ snapshots: transitivePeerDependencies: - supports-color - vitepress@2.0.0-alpha.15(@types/node@24.10.2)(axios@1.13.5)(esbuild@0.27.0)(jiti@2.6.1)(less@4.4.2)(nprogress@0.2.0)(oxc-minify@0.105.0)(postcss@8.5.6)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.2): + vitepress@2.0.0-alpha.15(@types/node@24.10.2)(esbuild@0.27.0)(jiti@2.6.1)(less@4.4.2)(nprogress@0.2.0)(oxc-minify@0.105.0)(postcss@8.5.6)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.2): dependencies: '@docsearch/css': 4.3.2 '@docsearch/js': 4.3.2 @@ -16072,7 +16540,7 @@ snapshots: '@vue/devtools-api': 8.0.5 '@vue/shared': 3.5.25 '@vueuse/core': 14.0.0(vue@3.5.25(typescript@5.9.3)) - '@vueuse/integrations': 14.0.0(axios@1.13.5)(focus-trap@7.6.6)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3)) + '@vueuse/integrations': 14.0.0(focus-trap@7.6.6)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3)) focus-trap: 7.6.6 mark.js: 8.11.1 minisearch: 7.2.0 @@ -16270,9 +16738,6 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@8.19.0: - optional: true - xml2js@0.6.2: dependencies: sax: 1.4.3 @@ -16285,6 +16750,8 @@ snapshots: commander: 2.20.3 cssfilter: 0.0.10 + xtend@4.0.2: {} + y18n@4.0.3: {} y18n@5.0.8: {} diff --git a/tegg/core/agent-tracing/module.yml b/tegg/core/agent-tracing/module.yml new file mode 100644 index 0000000000..a2b12ee1ee --- /dev/null +++ b/tegg/core/agent-tracing/module.yml @@ -0,0 +1,3 @@ +# @eggjs/agent-tracing module configuration +# +# Config is passed via tracer.configure(). See TracerConfig interface. diff --git a/tegg/core/agent-tracing/package.json b/tegg/core/agent-tracing/package.json new file mode 100644 index 0000000000..231157671b --- /dev/null +++ b/tegg/core/agent-tracing/package.json @@ -0,0 +1,89 @@ +{ + "name": "@eggjs/agent-tracing", + "version": "4.0.2-beta.1", + "description": "Tracing support for AI agents (LangGraph, Claude Agent SDK)", + "keywords": [ + "agent", + "claude", + "egg", + "langchain", + "langgraph", + "tegg", + "tracing", + "typescript" + ], + "homepage": "https://github.com/eggjs/egg/tree/next/tegg/core/agent-tracing", + "bugs": { + "url": "https://github.com/eggjs/egg/issues" + }, + "license": "MIT", + "author": "killagu ", + "repository": { + "type": "git", + "url": "git+https://github.com/eggjs/egg.git", + "directory": "tegg/core/agent-tracing" + }, + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": "./src/index.ts", + "./claude": "./src/claude.ts", + "./langgraph": "./src/langgraph.ts", + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public", + "exports": { + ".": "./dist/index.js", + "./claude": "./dist/claude.js", + "./langgraph": "./dist/langgraph.js", + "./package.json": "./package.json" + } + }, + "scripts": { + "typecheck": "tsgo --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@eggjs/background-task": "workspace:*", + "@eggjs/core-decorator": "workspace:*", + "@eggjs/tegg-types": "workspace:*", + "ali-oss": "^6.21.0", + "onelogger": "catalog:", + "uuid": "^10.0.0" + }, + "devDependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.52", + "@anthropic-ai/sdk": "^0.78.0", + "@eggjs/tegg-common-util": "workspace:*", + "@langchain/core": "^1.1.1", + "@langchain/langgraph": "^0.2.74", + "@types/ali-oss": "^6.16.0", + "@types/node": "catalog:", + "@types/uuid": "^10.0.0", + "typescript": "catalog:" + }, + "peerDependencies": { + "@anthropic-ai/claude-agent-sdk": ">=0.2.52", + "@langchain/core": "^1.1.1" + }, + "peerDependenciesMeta": { + "@anthropic-ai/claude-agent-sdk": { + "optional": true + }, + "@langchain/core": { + "optional": true + } + }, + "engines": { + "node": ">=22.18.0" + }, + "eggModule": { + "name": "teggAgentTracing" + } +} diff --git a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts new file mode 100644 index 0000000000..b408639587 --- /dev/null +++ b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts @@ -0,0 +1,517 @@ +import type { SDKMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'; +import { SingletonProto, Inject } from '@eggjs/core-decorator'; +import { AccessLevel } from '@eggjs/tegg-types'; +import type { Logger } from '@eggjs/tegg-types'; +import type { Run } from '@langchain/core/tracers/base'; +import { v4 as uuidv4 } from 'uuid'; + +import type { TracingService } from './TracingService.ts'; +import { + type ClaudeMessage, + type ClaudeContentBlock, + type ClaudeTokenUsage, + type IRunCost, + RunStatus, + type TracerConfig, + applyTracerConfig, +} from './types.ts'; + +/** + * TraceSession - Manages state for a single agent execution with streaming support. + * Allows processing messages one-by-one and logging them immediately. + */ +export class TraceSession { + private traceId: string; + private rootRun: Run | null = null; + private rootRunId: string; + private startTime: number; + private executionOrder = 2; // Start at 2, root is 1 + private pendingToolUses = new Map(); + private tracer: ClaudeAgentTracer; + + constructor(tracer: ClaudeAgentTracer, sessionId?: string) { + this.tracer = tracer; + this.traceId = sessionId || uuidv4(); + this.rootRunId = uuidv4(); + this.startTime = Date.now(); + } + + /** + * Process a single SDK message and log it immediately. + * Non-tracing message types (tool_progress, stream_event, status, etc.) are automatically ignored. + */ + async processMessage(message: SDKMessage): Promise { + try { + const converted = this.tracer.convertSDKMessage(message); + if (!converted) return; + + if (converted.type === 'system' && converted.subtype === 'init') { + await this.handleInit(converted); + } else if (converted.type === 'assistant') { + await this.handleAssistant(converted); + } else if (converted.type === 'user') { + await this.handleUser(converted); + } else if (converted.type === 'result') { + await this.handleResult(converted); + } + } catch (e) { + this.tracer.logger.warn('[ClaudeAgentTracer] processMessage error:', e); + } + } + + private async handleInit(message: ClaudeMessage): Promise { + this.traceId = message.session_id || this.traceId; + this.rootRun = this.tracer.createRootRunInternal(message, this.startTime, this.traceId, this.rootRunId); + this.tracer.logTrace(this.rootRun, RunStatus.START); + } + + private async handleAssistant(message: ClaudeMessage): Promise { + if (!this.rootRun) { + this.tracer.logger.warn('[ClaudeAgentTracer] Received assistant message before init'); + return; + } + + const content = message.message?.content || []; + const hasToolUse = content.some((c) => c.type === 'tool_use'); + const hasText = content.some((c) => c.type === 'text'); + + if (hasToolUse) { + // Create LLM run that initiated tool calls + const llmRun = this.tracer.createLLMRunInternal( + message, + this.rootRunId, + this.traceId, + this.executionOrder++, + this.startTime, + true, + ); + this.rootRun.child_runs.push(llmRun); + this.tracer.logTrace(llmRun, RunStatus.END); + + // Create tool runs (will be completed when tool_result arrives) + for (const block of content) { + if (block.type === 'tool_use') { + const toolRun = this.tracer.createToolRunStartInternal( + block, + this.rootRunId, + this.traceId, + this.executionOrder++, + this.startTime, + ); + this.rootRun.child_runs.push(toolRun); + this.pendingToolUses.set(block.id, toolRun); + this.tracer.logTrace(toolRun, RunStatus.START); + } + } + } else if (hasText) { + // Text-only response + const llmRun = this.tracer.createLLMRunInternal( + message, + this.rootRunId, + this.traceId, + this.executionOrder++, + this.startTime, + false, + ); + this.rootRun.child_runs.push(llmRun); + this.tracer.logTrace(llmRun, RunStatus.END); + } + } + + private async handleUser(message: ClaudeMessage): Promise { + if (!message.message?.content) return; + + for (const block of message.message.content) { + if (block.type === 'tool_result') { + const toolRun = this.pendingToolUses.get(block.tool_use_id); + if (toolRun) { + this.tracer.completeToolRunInternal(toolRun, block, this.startTime); + const status = block.is_error ? RunStatus.ERROR : RunStatus.END; + this.tracer.logTrace(toolRun, status); + this.pendingToolUses.delete(block.tool_use_id); + } + } + } + } + + private async handleResult(message: ClaudeMessage): Promise { + if (!this.rootRun) { + this.tracer.logger.warn('[ClaudeAgentTracer] Received result message before init'); + return; + } + + // Complete any pending tool runs + for (const [toolUseId, toolRun] of this.pendingToolUses) { + this.tracer.logger.warn(`[ClaudeAgentTracer] Tool run ${toolUseId} did not receive result`); + toolRun.end_time = this.startTime; + this.tracer.logTrace(toolRun, RunStatus.ERROR); + } + this.pendingToolUses.clear(); + + // Update and log root run end + this.rootRun.end_time = this.startTime + (message.duration_ms || 0); + this.rootRun.outputs = { + result: message.result, + is_error: message.is_error, + num_turns: message.num_turns, + }; + + if (message.usage || message.modelUsage) { + const cost = this.tracer.createRunCostInternal(message); + if (this.rootRun.outputs) { + (this.rootRun.outputs as any).llmOutput = cost; + } + } + + if (message.is_error) { + this.rootRun.error = message.result; + } + + this.rootRun.child_execution_order = this.executionOrder - 1; + const status = message.is_error ? RunStatus.ERROR : RunStatus.END; + this.tracer.logTrace(this.rootRun, status); + } + + /** + * Get current trace ID + */ + getTraceId(): string { + return this.traceId; + } +} + +/** + * ClaudeAgentTracer - Converts Claude SDK messages to LangChain Run format + * and logs them to the same remote logging system as LangGraphTracer. + * + * Supports both batch processing (processMessages) and streaming (createSession). + */ +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class ClaudeAgentTracer { + /** @internal */ + @Inject() + readonly logger: Logger; + + @Inject() + private tracingService: TracingService; + + name = 'ClaudeAgentTracer'; + agentName = ''; + + /** + * Configure the tracer with agent name and service credentials. + */ + configure(config: TracerConfig): void { + applyTracerConfig(this, this.tracingService, config); + } + + /** + * Create a new trace session for streaming message processing. + * Use this for real-time tracing where messages arrive one-by-one. + * + * @example + * const session = claudeTracer.createSession(); + * for await (const message of agent.run('task')) { + * await session.processMessage(message); + * } + */ + public createSession(sessionId?: string): TraceSession { + return new TraceSession(this, sessionId); + } + + /** + * Main entry point - convert SDK messages to Run trees and log them. + * Use this when you have all messages collected (batch processing). + * For real-time streaming, use createSession() instead. + * + * Non-tracing message types (tool_progress, stream_event, status, etc.) are automatically filtered out. + */ + public async processMessages(sdkMessages: SDKMessage[]): Promise { + try { + if (!sdkMessages || sdkMessages.length === 0) { + this.logger.warn('[ClaudeAgentTracer] No messages to process'); + return; + } + + // Pre-validate: ensure there is an init message before creating session + const hasInit = sdkMessages.some((m) => m.type === 'system' && 'subtype' in m && m.subtype === 'init'); + if (!hasInit) { + this.logger.warn('[ClaudeAgentTracer] No system/init message found'); + return; + } + + // Delegate to TraceSession for message processing + const session = this.createSession(); + for (const msg of sdkMessages) { + await session.processMessage(msg); + } + } catch (e) { + this.logger.warn('[ClaudeAgentTracer] processMessages error:', e); + } + } + + /** + * @internal + * Convert an SDKMessage to internal ClaudeMessage format. + * Returns null for message types that are not relevant to tracing. + */ + convertSDKMessage(msg: SDKMessage): ClaudeMessage | null { + // SDKSystemMessage (init) + if (msg.type === 'system' && 'subtype' in msg && msg.subtype === 'init') { + return msg as unknown as ClaudeMessage; + } + + // SDKAssistantMessage + if (msg.type === 'assistant' && 'message' in msg && 'parent_tool_use_id' in msg) { + return { + type: 'assistant', + uuid: msg.uuid, + session_id: msg.session_id, + message: msg.message as any, + parent_tool_use_id: msg.parent_tool_use_id, + }; + } + + // SDKUserMessage (tool results, not replay) + if (msg.type === 'user' && 'message' in msg && !('isReplay' in msg && (msg as any).isReplay)) { + return { + type: 'user', + uuid: msg.uuid || uuidv4(), + session_id: msg.session_id, + message: msg.message as any, + parent_tool_use_id: (msg as any).parent_tool_use_id, + }; + } + + // SDKResultMessage (success or error) + if (msg.type === 'result') { + const resultMsg = msg as SDKResultMessage; + const isSuccess = resultMsg.subtype === 'success'; + return { + type: 'result', + subtype: isSuccess ? 'success' : 'error', + is_error: resultMsg.is_error, + duration_ms: resultMsg.duration_ms, + duration_api_ms: resultMsg.duration_api_ms, + num_turns: resultMsg.num_turns, + result: isSuccess ? (resultMsg as any).result : (resultMsg as any).errors?.join('; ') || 'Unknown error', + session_id: resultMsg.session_id, + total_cost_usd: resultMsg.total_cost_usd, + usage: resultMsg.usage as any, + modelUsage: resultMsg.modelUsage as any, + uuid: resultMsg.uuid, + }; + } + + // Ignore all other SDK message types (tool_progress, stream_event, status, hook, etc.) + return null; + } + + /** + * @internal + * Create root run from init message (used by TraceSession) + */ + createRootRunInternal(initMsg: ClaudeMessage, startTime: number, traceId: string, rootRunId?: string): Run { + const runId = rootRunId || initMsg.uuid || uuidv4(); + + return { + id: runId, + name: this.name, + run_type: 'chain', + inputs: { + tools: initMsg.tools || [], + model: initMsg.model, + session_id: initMsg.session_id, + mcp_servers: initMsg.mcp_servers, + agents: initMsg.agents, + slash_commands: initMsg.slash_commands, + }, + outputs: undefined, + start_time: startTime, + end_time: undefined, + execution_order: 1, + child_execution_order: 1, + child_runs: [], + events: [], + trace_id: traceId, + parent_run_id: undefined, + tags: [], + extra: { + apiKeySource: initMsg.apiKeySource, + claude_code_version: initMsg.claude_code_version, + output_style: initMsg.output_style, + permissionMode: initMsg.permissionMode, + }, + } as Run; + } + + /** + * @internal + * Create LLM run from assistant message (used by TraceSession) + */ + createLLMRunInternal( + msg: ClaudeMessage, + rootRunId: string, + traceId: string, + order: number, + startTime: number, + isToolCall: boolean, + ): Run { + const runId = msg.uuid || uuidv4(); + const content = msg.message?.content || []; + + const textBlocks = content.filter((c) => c.type === 'text'); + const toolBlocks = content.filter((c) => c.type === 'tool_use'); + + const inputs = { + messages: textBlocks.map((c) => (c as any).text).filter(Boolean), + }; + + const outputs: any = {}; + if (isToolCall) { + outputs.tool_calls = toolBlocks.map((c) => ({ + id: (c as any).id, + name: (c as any).name, + input: (c as any).input, + })); + } else { + outputs.content = textBlocks.map((c) => (c as any).text).join(''); + } + + if (msg.message?.usage) { + outputs.llmOutput = this.createLLMOutput(msg.message.usage); + } + + return { + id: runId, + name: 'LLM', + run_type: 'llm', + inputs, + outputs, + start_time: startTime, + end_time: startTime, + execution_order: order, + child_execution_order: order, + child_runs: [], + events: [], + trace_id: traceId, + parent_run_id: rootRunId, + tags: [], + extra: { + model: msg.message?.model, + }, + } as Run; + } + + /** + * @internal + * Create tool run at start (before result, used by TraceSession) + */ + createToolRunStartInternal( + toolUseBlock: ClaudeContentBlock, + rootRunId: string, + traceId: string, + order: number, + startTime: number, + ): Run { + const toolUse = toolUseBlock as any; + const runId = uuidv4(); + + return { + id: runId, + name: toolUse.name || 'Tool', + run_type: 'tool', + inputs: { + tool_use_id: toolUse.id, + ...toolUse.input, + }, + outputs: undefined, + start_time: startTime, + end_time: undefined, + execution_order: order, + child_execution_order: order, + child_runs: [], + events: [], + trace_id: traceId, + parent_run_id: rootRunId, + tags: [], + extra: { + tool_use_id: toolUse.id, + }, + } as Run; + } + + /** + * @internal + * Complete tool run with result (used by TraceSession) + */ + completeToolRunInternal(toolRun: Run, toolResultBlock: ClaudeContentBlock, startTime: number): void { + const result = toolResultBlock as any; + toolRun.end_time = startTime; + toolRun.outputs = { + content: result.content, + }; + + if (result.is_error) { + toolRun.error = typeof result.content === 'string' ? result.content : JSON.stringify(result.content); + } + } + + /** + * Extract token usage from Claude SDK usage object into IRunCost format. + */ + private extractTokenUsage(usage: ClaudeTokenUsage): IRunCost { + const result: IRunCost = {}; + + if (usage.input_tokens !== undefined) { + result.promptTokens = usage.input_tokens; + } + if (usage.output_tokens !== undefined) { + result.completionTokens = usage.output_tokens; + } + if (usage.cache_creation_input_tokens !== undefined) { + result.cacheCreationInputTokens = usage.cache_creation_input_tokens; + } + if (usage.cache_read_input_tokens !== undefined) { + result.cacheReadInputTokens = usage.cache_read_input_tokens; + } + + const totalTokens = (usage.input_tokens || 0) + (usage.output_tokens || 0); + if (totalTokens > 0) { + result.totalTokens = totalTokens; + } + + return result; + } + + /** + * Create LLM output from usage + */ + private createLLMOutput(usage: ClaudeTokenUsage): IRunCost { + return this.extractTokenUsage(usage); + } + + /** + * @internal + * Create run cost from result message (used by TraceSession) + */ + createRunCostInternal(resultMsg: ClaudeMessage): IRunCost { + const cost: IRunCost = resultMsg.usage ? this.extractTokenUsage(resultMsg.usage) : {}; + + if (resultMsg.total_cost_usd !== undefined) { + cost.totalCost = resultMsg.total_cost_usd; + } + + return cost; + } + + /** + * @internal + * Log trace - delegates to TracingService (used by TraceSession) + */ + logTrace(run: Run, status: RunStatus): void { + this.tracingService.logTrace(run, status, this.name, this.agentName); + } +} diff --git a/tegg/core/agent-tracing/src/LangGraphTracer.ts b/tegg/core/agent-tracing/src/LangGraphTracer.ts new file mode 100644 index 0000000000..d4b16defcb --- /dev/null +++ b/tegg/core/agent-tracing/src/LangGraphTracer.ts @@ -0,0 +1,82 @@ +import { SingletonProto, Inject } from '@eggjs/core-decorator'; +import { AccessLevel } from '@eggjs/tegg-types'; +import { BaseTracer } from '@langchain/core/tracers/base'; +import type { Run } from '@langchain/core/tracers/base'; + +import type { TracingService } from './TracingService.ts'; +import { RunStatus, type TracerConfig, applyTracerConfig } from './types.ts'; + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class LangGraphTracer extends BaseTracer { + @Inject() + private tracingService: TracingService; + + name = 'LangGraphTracer'; + + agentName = ''; + + /** + * Configure the tracer with agent name and service credentials. + */ + configure(config: TracerConfig): void { + applyTracerConfig(this, this.tracingService, config); + } + + protected persistRun(_: Run): Promise { + return Promise.resolve(undefined); + } + + private logTrace(run: Run, status: RunStatus): void { + this.tracingService.logTrace(run, status, this.name, this.agentName); + } + + onChainStart(run: Run): void | Promise { + this.logTrace(run, RunStatus.START); + } + onChainEnd(run: Run): void | Promise { + this.logTrace(run, RunStatus.END); + } + onChainError(run: Run): void | Promise { + this.logTrace(run, RunStatus.ERROR); + } + + onToolStart(run: Run): void | Promise { + this.logTrace(run, RunStatus.START); + } + onToolEnd(run: Run): void | Promise { + this.logTrace(run, RunStatus.END); + } + + onToolError(run: Run): void | Promise { + this.logTrace(run, RunStatus.ERROR); + } + + onLLMStart(run: Run): void | Promise { + this.logTrace(run, RunStatus.START); + } + onLLMEnd(run: Run): void | Promise { + this.logTrace(run, RunStatus.END); + } + onLLMError(run: Run): void | Promise { + this.logTrace(run, RunStatus.ERROR); + } + + onRetrieverStart(run: Run): void | Promise { + return this.logTrace(run, RunStatus.START); + } + onRetrieverEnd(run: Run): void | Promise { + this.logTrace(run, RunStatus.END); + } + onRetrieverError(run: Run): void | Promise { + this.logTrace(run, RunStatus.ERROR); + } + + onAgentAction(run: Run): void | Promise { + this.logTrace(run, RunStatus.START); + } + onAgentEnd(run: Run): void | Promise { + this.logTrace(run, RunStatus.END); + } +} diff --git a/tegg/core/agent-tracing/src/TracingService.ts b/tegg/core/agent-tracing/src/TracingService.ts new file mode 100644 index 0000000000..48b09ca24d --- /dev/null +++ b/tegg/core/agent-tracing/src/TracingService.ts @@ -0,0 +1,228 @@ +import type { BackgroundTaskHelper } from '@eggjs/background-task'; +import { SingletonProto, Inject } from '@eggjs/core-decorator'; +import { AccessLevel } from '@eggjs/tegg-types'; +import type { Logger } from '@eggjs/tegg-types'; +import type { Run } from '@langchain/core/tracers/base'; +import OSS from 'ali-oss'; +import { getCustomLogger } from 'onelogger'; + +import { type AgentTracingConfig, FIELDS_TO_OSS, type IResource, RunStatus } from './types.ts'; + +/** + * TracingService - Shared service for common tracing operations. + * Used by both LangGraphTracer and ClaudeAgentTracer to avoid code duplication. + */ +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class TracingService { + @Inject() + public readonly logger: Logger; + + @Inject() + private backgroundTaskHelper: BackgroundTaskHelper; + + private config: AgentTracingConfig = {}; + private ossClient: OSS | null = null; + private ossInitialized = false; + + /** + * Configure OSS and/or logService credentials. + * Validates required fields and resets cached OSS client. + */ + configure(config: AgentTracingConfig): void { + if (config.oss) { + const { accessKeyId, accessKeySecret, bucket, region } = config.oss; + if (!accessKeyId || !accessKeySecret || !bucket || !region) { + throw new TypeError('[TracingService] oss config requires accessKeyId, accessKeySecret, bucket, and region'); + } + } + if (config.logService) { + if (!config.logService.url) { + throw new TypeError('[TracingService] logService config requires url'); + } + } + this.config = config; + this.ossClient = null; + this.ossInitialized = false; + } + + /** + * Get the current environment (local, pre, prod, gray) + */ + getEnv(): string { + const env = process.env.FAAS_ENV || process.env.SERVER_ENV || 'local'; + if (env === 'prepub') { + return 'pre'; + } + return env; + } + + /** + * Check if running in online environment (prod, pre, gray) + */ + isOnlineEnv(): boolean { + const env = this.getEnv(); + return ['prod', 'pre', 'gray'].includes(env); + } + + /** + * Generate log info prefix for a run + */ + getLogInfoPrefix(run: Run, status: RunStatus, name: string): string { + const env = this.getEnv(); + if (process.env.FAAS_ENV || env === 'local') { + return ( + `[agent_run][${name}]:` + + `traceId=${run.trace_id},` + + `type=${run.parent_run_id ? 'child_run' : 'root_run'},` + + `status=${status},` + + `run_id=${run.id},` + + `parent_run_id=${run.parent_run_id ?? ''}` + ); + } + + return ( + `[agent_run][${name}]:` + + `traceId=${run.trace_id},` + + `type=${run.parent_run_id ? 'child_run' : 'root_run'},` + + `status=${status},` + + `env=${env},` + + `run_id=${run.id},` + + `parent_run_id=${run.parent_run_id ?? ''}` + ); + } + + /** + * Lazily initialize the ali-oss client from configured credentials. + * Returns null if OSS is not configured via configure(). + */ + private getOssClient(): OSS | null { + if (this.ossInitialized) { + return this.ossClient; + } + this.ossInitialized = true; + + const ossConfig = this.config.oss; + if (!ossConfig) { + this.logger.warn( + '[TracingService] OSS not configured. Call configure({ oss: { ... } }) first. OSS uploads will be skipped.', + ); + return null; + } + + const options: OSS.Options = { + accessKeyId: ossConfig.accessKeyId, + accessKeySecret: ossConfig.accessKeySecret, + bucket: ossConfig.bucket, + region: ossConfig.region, + }; + + if (ossConfig.endpoint) { + options.endpoint = ossConfig.endpoint; + } + + this.ossClient = new OSS(options); + return this.ossClient; + } + + /** + * Upload content to OSS using ali-oss SDK. + * Gracefully skips if OSS env vars are not configured. + */ + async uploadToOss(key: string, fileContent: string): Promise { + const client = this.getOssClient(); + if (!client) { + return; + } + this.logger.info(`Uploading to OSS with key: ${key}`); + const result = await client.put(key, Buffer.from(fileContent)); + this.logger.info(`Upload response for key ${key}: ${result.res.status}`); + } + + /** + * Sync local tracing logs to a log service endpoint. + * Configured via configure({ logService: { url, headers } }). + * Silently skips if logService is not configured. + */ + async syncLocalToLogService(log: string, agentName: string): Promise { + const logServiceConfig = this.config.logService; + if (!logServiceConfig?.url) { + return; + } + + if (!agentName) { + this.logger.warn('[TraceLogErr] syncLocalToLogService: agentName is empty'); + return; + } + + try { + await fetch(logServiceConfig.url, { + method: 'POST', + headers: { + 'content-type': 'application/json', + ...logServiceConfig.headers, + }, + body: JSON.stringify({ + log: `[${agentName}]${log}`, + }), + }); + } catch (e) { + this.logger.warn('[TraceLogErr] syncLocalToLogService error:', e); + } + } + + /** + * Log trace run data with OSS upload for large fields + */ + logTrace(run: Run, status: RunStatus, name: string, agentName: string): void { + try { + const { child_runs: childs, ...runData } = run; + if (runData.tags?.includes('langsmith:hidden')) { + return; + } + + const env = this.getEnv(); + FIELDS_TO_OSS.forEach((field) => { + if (!runData[field]) { + return; + } + const jsonstr = JSON.stringify(runData[field]); + if (field === 'outputs') { + (runData as any).cost = runData?.outputs?.llmOutput; + } + delete runData[field]; + const key = `agents/${name}/${env}/traces/${run.trace_id}/runs/${run.id}/${field}`; + this.backgroundTaskHelper.run(async () => { + try { + await this.uploadToOss(key, jsonstr); + } catch (e) { + this.logger.warn( + `[TraceLogErr] Failed to upload run data to OSS for run_id=${run.id}, field=${field}, error:`, + e, + ); + } + }); + (runData as any)[field] = { compress: 'none', key } as IResource; + }); + + const runJSON = JSON.stringify({ ...runData, child_run_ids: childs?.map((child) => child.id) }); + const logInfo = this.getLogInfoPrefix(run, status, name) + `,run=${runJSON}`; + + if (process.env.FAAS_ENV) { + this.logger.info(logInfo); + } else { + const logger = getCustomLogger('agentTraceLogger') || this.logger; + logger.info(`[${agentName}]${logInfo}`); + } + + if (env === 'local') { + this.backgroundTaskHelper.run(async () => { + await this.syncLocalToLogService(logInfo, agentName); + }); + } + } catch (e) { + this.logger.warn('[TraceLogErr] logTrace error:', e); + } + } +} diff --git a/tegg/core/agent-tracing/src/claude.ts b/tegg/core/agent-tracing/src/claude.ts new file mode 100644 index 0000000000..56edead0c7 --- /dev/null +++ b/tegg/core/agent-tracing/src/claude.ts @@ -0,0 +1 @@ +export { ClaudeAgentTracer, TraceSession } from './ClaudeAgentTracer.ts'; diff --git a/tegg/core/agent-tracing/src/index.ts b/tegg/core/agent-tracing/src/index.ts new file mode 100644 index 0000000000..9dfc4b8561 --- /dev/null +++ b/tegg/core/agent-tracing/src/index.ts @@ -0,0 +1,2 @@ +export * from './types.ts'; +export { TracingService } from './TracingService.ts'; diff --git a/tegg/core/agent-tracing/src/langgraph.ts b/tegg/core/agent-tracing/src/langgraph.ts new file mode 100644 index 0000000000..a696438096 --- /dev/null +++ b/tegg/core/agent-tracing/src/langgraph.ts @@ -0,0 +1 @@ +export { LangGraphTracer } from './LangGraphTracer.ts'; diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts new file mode 100644 index 0000000000..7ee315fe5d --- /dev/null +++ b/tegg/core/agent-tracing/src/types.ts @@ -0,0 +1,188 @@ +import type { Run } from '@langchain/core/tracers/base'; + +// Claude SDK Message Types + +export interface ClaudeTextContent { + type: 'text'; + text: string; +} + +export interface ClaudeToolUseContent { + type: 'tool_use'; + id: string; + name: string; + input?: Record; +} + +export interface ClaudeToolResultContent { + type: 'tool_result'; + content: string | ClaudeToolResultContent[]; + tool_use_id: string; + is_error?: boolean; +} + +export type ClaudeContentBlock = ClaudeTextContent | ClaudeToolUseContent | ClaudeToolResultContent; + +export interface ClaudeTokenUsage { + input_tokens: number; + output_tokens: number; + cache_creation_input_tokens?: number; + cache_read_input_tokens?: number; + server_tool_use?: { + web_search_requests?: number; + web_fetch_requests?: number; + }; + service_tier?: string; + cache_creation?: { + ephemeral_1h_input_tokens?: number; + ephemeral_5m_input_tokens?: number; + }; +} + +export interface ClaudeMessageContent { + id?: string; + type?: string; + role?: string; + content?: ClaudeContentBlock[]; + model?: string; + usage?: ClaudeTokenUsage; + context_management?: any; + stop_reason?: string; + stop_sequence?: string; + container?: any; + [key: string]: any; +} + +export interface ClaudeModelUsage { + [modelName: string]: { + inputTokens: number; + outputTokens: number; + cacheReadInputTokens?: number; + cacheCreationInputTokens?: number; + webSearchRequests?: number; + costUSD?: number; + contextWindow?: number; + maxOutputTokens?: number; + }; +} + +export interface ClaudeMessage { + type: 'system' | 'assistant' | 'user' | 'result'; + subtype?: 'init' | 'success' | 'error'; + session_id?: string; + uuid: string; + message?: ClaudeMessageContent; + + // System/init message fields + cwd?: string; + tools?: string[]; + mcp_servers?: Array<{ name: string; status: string }>; + model?: string; + permissionMode?: string; + slash_commands?: string[]; + apiKeySource?: string; + claude_code_version?: string; + output_style?: string; + agents?: string[]; + skills?: any[]; + plugins?: any[]; + + // Result message fields + is_error?: boolean; + duration_ms?: number; + duration_api_ms?: number; + num_turns?: number; + result?: string; + total_cost_usd?: number; + usage?: ClaudeTokenUsage; + modelUsage?: ClaudeModelUsage; + permission_denials?: any[]; + + // User message fields (tool results) + parent_tool_use_id?: string | null; + tool_use_id?: string; + tool_use_result?: string; + + // Error fields + error?: string; + + [key: string]: any; +} + +// Shared tracing types (from LangGraphTracer) + +export interface IResource { + compress: 'none' | 'gzip'; + key: string; +} + +export interface IRunCost { + promptTokens?: number; + completionTokens?: number; + totalTokens?: number; + cacheCreationInputTokens?: number; + cacheReadInputTokens?: number; + totalCost?: number; +} + +const FIELDS_TO_OSS = ['inputs', 'outputs', 'attachments', 'serialized', 'events'] as const; + +export interface ILogRun extends Omit { + child_run_ids?: string[]; + outputs?: IResource; + inputs?: IResource; + attachments?: IResource; + serialized?: IResource; + events?: IResource; + cost?: IRunCost; +} + +export const RunStatus = { + START: 'start', + END: 'end', + ERROR: 'error', +} as const; +export type RunStatus = (typeof RunStatus)[keyof typeof RunStatus]; + +export interface OssConfig { + accessKeyId: string; + accessKeySecret: string; + bucket: string; + region: string; + endpoint?: string; +} + +export interface LogServiceConfig { + url: string; + headers?: Record; +} + +/** Internal config used by TracingService */ +export interface AgentTracingConfig { + oss?: OssConfig; + logService?: LogServiceConfig; +} + +/** User-facing config passed to tracer.configure() */ +export interface TracerConfig { + agentName?: string; + oss?: OssConfig; + logService?: LogServiceConfig; +} + +/** Apply user-facing TracerConfig to a tracer instance and its TracingService. */ +export function applyTracerConfig( + tracer: { agentName: string }, + tracingService: { configure(config: AgentTracingConfig): void }, + config: TracerConfig, +): void { + if (config.agentName !== undefined) { + tracer.agentName = config.agentName; + } + tracingService.configure({ + oss: config.oss, + logService: config.logService, + }); +} + +export { FIELDS_TO_OSS }; diff --git a/tegg/core/agent-tracing/test/claude-agent-integration.test.ts b/tegg/core/agent-tracing/test/claude-agent-integration.test.ts new file mode 100644 index 0000000000..0f29b736f5 --- /dev/null +++ b/tegg/core/agent-tracing/test/claude-agent-integration.test.ts @@ -0,0 +1,361 @@ +import assert from 'node:assert'; + +import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; +import { describe, it } from 'vitest'; + +import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; +import { createMockLogger, createMockTracingService } from './test-utils.ts'; + +// ---------- Tracing log helpers ---------- + +function extractRunFromLog(log: string): any { + const match = log.match(/,run=({.*})$/); + return match ? JSON.parse(match[1]) : null; +} + +function extractStatus(log: string): string | null { + const match = log.match(/status=(\w+)/); + return match ? match[1] : null; +} + +function extractRunType(log: string): string | null { + const match = log.match(/type=(root_run|child_run)/); + return match ? match[1] : null; +} + +// ---------- Shared setup ---------- + +function createTestEnv() { + const logs: string[] = []; + process.env.FAAS_ENV = 'dev'; + + const mockLogger = createMockLogger(logs); + const tracingService = createMockTracingService(logs); + + const claudeTracer = new ClaudeAgentTracer(); + (claudeTracer as any).logger = mockLogger; + (claudeTracer as any).tracingService = tracingService; + + function getTracingLogs(): string[] { + return logs.filter((log) => log.includes('[agent_run][ClaudeAgentTracer]')); + } + + function parseAllRuns(): Array<{ run: any; status: string; logType: string }> { + const entries: Array<{ run: any; status: string; logType: string }> = []; + for (const log of getTracingLogs()) { + const run = extractRunFromLog(log); + const status = extractStatus(log); + const logType = extractRunType(log); + if (run && status && logType) { + entries.push({ run, status, logType }); + } + } + return entries; + } + + return { logs, claudeTracer, parseAllRuns, getTracingLogs }; +} + +// ---------- Mock data factories ---------- + +function createMockInit(overrides?: Partial): SDKMessage { + return { + type: 'system', + subtype: 'init', + session_id: 'test-session-001', + uuid: 'uuid-init', + tools: ['Bash', 'Read'], + model: 'claude-sonnet-4-5-20250929', + cwd: '/test', + mcp_servers: [], + permissionMode: 'default', + apiKeySource: 'api_key', + claude_code_version: '1.0.0', + output_style: 'text', + slash_commands: [], + skills: [], + plugins: [], + ...overrides, + } as unknown as SDKMessage; +} + +function createMockAssistantWithTool(overrides?: Partial): SDKMessage { + return { + type: 'assistant', + uuid: 'uuid-assistant-tool', + session_id: 'test-session-001', + parent_tool_use_id: null, + message: { + id: 'msg_1', + type: 'message', + role: 'assistant', + content: [ + { type: 'text', text: 'Let me run that command for you.' }, + { type: 'tool_use', id: 'tu_1', name: 'Bash', input: { command: 'echo hello' } }, + ], + model: 'claude-sonnet-4-5-20250929', + usage: { input_tokens: 100, output_tokens: 50 }, + stop_reason: 'tool_use', + }, + ...overrides, + } as unknown as SDKMessage; +} + +function createMockUserToolResult(overrides?: Partial): SDKMessage { + return { + type: 'user', + uuid: 'uuid-user-result', + session_id: 'test-session-001', + parent_tool_use_id: null, + message: { + role: 'user', + content: [ + { + type: 'tool_result', + tool_use_id: 'tu_1', + content: 'hello', + is_error: false, + }, + ], + }, + ...overrides, + } as unknown as SDKMessage; +} + +function createMockAssistantTextOnly(overrides?: Partial): SDKMessage { + return { + type: 'assistant', + uuid: 'uuid-assistant-text', + session_id: 'test-session-001', + parent_tool_use_id: null, + message: { + id: 'msg_2', + type: 'message', + role: 'assistant', + content: [{ type: 'text', text: 'The answer is 21.' }], + model: 'claude-sonnet-4-5-20250929', + usage: { input_tokens: 80, output_tokens: 30 }, + stop_reason: 'end_turn', + }, + ...overrides, + } as unknown as SDKMessage; +} + +function createMockResult(overrides?: Partial): SDKMessage { + return { + type: 'result', + subtype: 'success', + session_id: 'test-session-001', + uuid: 'uuid-result', + is_error: false, + duration_ms: 1500, + duration_api_ms: 1200, + num_turns: 1, + result: 'hello', + stop_reason: null, + total_cost_usd: 0.003, + usage: { + input_tokens: 100, + output_tokens: 50, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + }, + modelUsage: {}, + permission_denials: [], + ...overrides, + } as unknown as SDKMessage; +} + +// Noise messages that should be filtered out +function createMockToolProgress(): SDKMessage { + return { + type: 'tool_progress', + tool_use_id: 'tu_1', + tool_name: 'Bash', + parent_tool_use_id: null, + elapsed_time_seconds: 0.5, + uuid: 'uuid-progress', + session_id: 'test-session-001', + } as unknown as SDKMessage; +} + +function createMockStreamEvent(): SDKMessage { + return { + type: 'stream_event', + event: { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } }, + parent_tool_use_id: null, + uuid: 'uuid-stream', + session_id: 'test-session-001', + } as unknown as SDKMessage; +} + +// ---------- Tests ---------- + +describe('egg-agent-tracing/test/claude-agent-integration.test.ts', () => { + describe('Streaming mode + tool use', () => { + it('should trace tool execution with session.processMessage', async () => { + const { claudeTracer, parseAllRuns } = createTestEnv(); + const session = claudeTracer.createSession(); + + // Feed messages one-by-one, including noise messages that should be filtered + const messages: SDKMessage[] = [ + createMockInit(), + createMockStreamEvent(), // noise — should be ignored + createMockAssistantWithTool(), + createMockToolProgress(), // noise — should be ignored + createMockUserToolResult(), + createMockResult(), + ]; + + for (const msg of messages) { + await session.processMessage(msg); + } + + const entries = parseAllRuns(); + + // Root run start + end + const rootStart = entries.find((e) => e.logType === 'root_run' && e.status === 'start'); + assert(rootStart, 'Should have root_run start'); + assert.strictEqual(rootStart!.run.run_type, 'chain'); + + const rootEnd = entries.find((e) => e.logType === 'root_run' && e.status === 'end'); + assert(rootEnd, 'Should have root_run end'); + + // LLM child run + const llmRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'llm'); + assert(llmRuns.length >= 1, `Should have >= 1 LLM run, got ${llmRuns.length}`); + + // Tool child run start + end + const toolRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'tool'); + assert(toolRuns.length >= 2, `Should have >= 2 tool run entries (start+end), got ${toolRuns.length}`); + + const toolStart = toolRuns.find((e) => e.status === 'start'); + assert(toolStart, 'Should have tool start'); + assert.strictEqual(toolStart!.run.name, 'Bash'); + + const toolEnd = toolRuns.find((e) => e.status === 'end'); + assert(toolEnd, 'Should have tool end'); + + // All runs share the same trace_id = session_id + const traceIds = new Set(entries.map((e) => e.run.trace_id)); + assert.strictEqual(traceIds.size, 1, `All runs should share one trace_id, got ${traceIds.size}`); + assert.strictEqual([...traceIds][0], 'test-session-001', 'trace_id should match session_id'); + + // Child runs reference root run as parent + const childEntries = entries.filter((e) => e.logType === 'child_run'); + for (const child of childEntries) { + assert.strictEqual( + child.run.parent_run_id, + rootStart!.run.id, + `Child run ${child.run.name} should reference root as parent`, + ); + } + + // Cost data on root end + const cost = rootEnd!.run.cost; + assert(cost, 'Root end should have cost'); + assert.strictEqual(cost.promptTokens, 100); + assert.strictEqual(cost.completionTokens, 50); + assert.strictEqual(cost.totalCost, 0.003); + }); + }); + + describe('Batch mode + text-only', () => { + it('should trace a text-only response via processMessages', async () => { + const { claudeTracer, parseAllRuns } = createTestEnv(); + + const messages: SDKMessage[] = [ + createMockInit(), + createMockAssistantTextOnly(), + createMockResult({ + usage: { + input_tokens: 80, + output_tokens: 30, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + }, + total_cost_usd: 0.002, + }), + ]; + + await claudeTracer.processMessages(messages); + + const entries = parseAllRuns(); + assert(entries.length > 0, 'Should have tracing entries'); + + // Root run start + end + const rootEntries = entries.filter((e) => e.logType === 'root_run'); + assert(rootEntries.length >= 2, `Should have root start + end, got ${rootEntries.length}`); + + const rootEnd = rootEntries.find((e) => e.status === 'end'); + assert(rootEnd, 'Should have root end'); + + // LLM child run with text content + const llmRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'llm'); + assert(llmRuns.length >= 1, `Should have >= 1 LLM run, got ${llmRuns.length}`); + + // No tool runs + const toolRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'tool'); + assert.strictEqual(toolRuns.length, 0, 'Should have no tool runs for text-only'); + + // Cost and token counts + const cost = rootEnd!.run.cost; + assert(cost, 'Should have cost'); + assert.strictEqual(cost.promptTokens, 80); + assert.strictEqual(cost.completionTokens, 30); + assert.strictEqual(cost.totalTokens, 110); + assert.strictEqual(cost.totalCost, 0.002); + + // trace_id consistency + const traceIds = new Set(entries.map((e) => e.run.trace_id)); + assert.strictEqual(traceIds.size, 1, 'All runs should share one trace_id'); + }); + }); + + describe('Error scenario', () => { + it('should trace an error result with ERROR status', async () => { + const { claudeTracer, parseAllRuns } = createTestEnv(); + const session = claudeTracer.createSession(); + + const messages: SDKMessage[] = [ + createMockInit(), + createMockAssistantTextOnly(), + { + type: 'result', + subtype: 'error_during_execution', + session_id: 'test-session-001', + uuid: 'uuid-result-err', + is_error: true, + duration_ms: 500, + duration_api_ms: 400, + num_turns: 1, + stop_reason: null, + total_cost_usd: 0.001, + errors: ['Something went wrong', 'Another error'], + usage: { + input_tokens: 50, + output_tokens: 10, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + }, + modelUsage: {}, + permission_denials: [], + } as unknown as SDKMessage, + ]; + + for (const msg of messages) { + await session.processMessage(msg); + } + + const entries = parseAllRuns(); + + // Root run should end with ERROR status + const rootEnd = entries.find((e) => e.logType === 'root_run' && e.status === 'error'); + assert(rootEnd, 'Should have root_run with error status'); + + // The root run should have error field populated + // (error is extracted from result message via convertSDKMessage) + assert(rootEnd!.run, 'Root end run should exist'); + }); + }); +}); diff --git a/tegg/core/agent-tracing/test/configure.test.ts b/tegg/core/agent-tracing/test/configure.test.ts new file mode 100644 index 0000000000..d7bb81f6f0 --- /dev/null +++ b/tegg/core/agent-tracing/test/configure.test.ts @@ -0,0 +1,229 @@ +import assert from 'node:assert'; + +import { describe, it, beforeEach } from 'vitest'; + +import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; +import { LangGraphTracer } from '../src/LangGraphTracer.ts'; +import { TracingService } from '../src/TracingService.ts'; +import { createMockLogger, createMockTracingService } from './test-utils.ts'; + +describe('egg-agent-tracing/test/configure.test.ts', () => { + describe('TracingService.configure()', () => { + let tracingService: TracingService; + + beforeEach(() => { + tracingService = createMockTracingService(); + }); + + it('should accept empty config', () => { + assert.doesNotThrow(() => { + tracingService.configure({}); + }); + }); + + it('should accept complete oss config', () => { + assert.doesNotThrow(() => { + tracingService.configure({ + oss: { + accessKeyId: 'ak', + accessKeySecret: 'sk', + bucket: 'my-bucket', + region: 'cn-hangzhou', + }, + }); + }); + }); + + it('should accept complete logService config', () => { + assert.doesNotThrow(() => { + tracingService.configure({ + logService: { + url: 'https://log.example.com/api', + headers: { Authorization: 'Bearer token' }, + }, + }); + }); + }); + + it('should accept both oss and logService config', () => { + assert.doesNotThrow(() => { + tracingService.configure({ + oss: { + accessKeyId: 'ak', + accessKeySecret: 'sk', + bucket: 'my-bucket', + region: 'cn-hangzhou', + }, + logService: { + url: 'https://log.example.com/api', + }, + }); + }); + }); + + it('should throw TypeError when oss.accessKeyId is missing', () => { + assert.throws( + () => { + tracingService.configure({ + oss: { + accessKeyId: '', + accessKeySecret: 'sk', + bucket: 'my-bucket', + region: 'cn-hangzhou', + }, + }); + }, + TypeError, + 'should throw TypeError for missing accessKeyId', + ); + }); + + it('should throw TypeError when oss.accessKeySecret is missing', () => { + assert.throws( + () => { + tracingService.configure({ + oss: { + accessKeyId: 'ak', + accessKeySecret: '', + bucket: 'my-bucket', + region: 'cn-hangzhou', + }, + }); + }, + TypeError, + 'should throw TypeError for missing accessKeySecret', + ); + }); + + it('should throw TypeError when oss.bucket is missing', () => { + assert.throws( + () => { + tracingService.configure({ + oss: { + accessKeyId: 'ak', + accessKeySecret: 'sk', + bucket: '', + region: 'cn-hangzhou', + }, + }); + }, + TypeError, + 'should throw TypeError for missing bucket', + ); + }); + + it('should throw TypeError when oss.region is missing', () => { + assert.throws( + () => { + tracingService.configure({ + oss: { + accessKeyId: 'ak', + accessKeySecret: 'sk', + bucket: 'my-bucket', + region: '', + }, + }); + }, + TypeError, + 'should throw TypeError for missing region', + ); + }); + + it('should throw TypeError when logService.url is missing', () => { + assert.throws( + () => { + tracingService.configure({ + logService: { + url: '', + }, + }); + }, + TypeError, + 'should throw TypeError for missing logService url', + ); + }); + + it('should reset OSS client when reconfigured', () => { + tracingService.configure({ + oss: { + accessKeyId: 'ak1', + accessKeySecret: 'sk1', + bucket: 'bucket1', + region: 'region1', + }, + }); + + // Access private state to verify reset + assert.strictEqual((tracingService as any).ossInitialized, false); + assert.strictEqual((tracingService as any).ossClient, null); + }); + }); + + describe('LangGraphTracer.configure()', () => { + it('should set agentName and delegate to TracingService', () => { + const tracingService = createMockTracingService(); + const tracer = new LangGraphTracer(); + (tracer as any).tracingService = tracingService; + + tracer.configure({ + agentName: 'MyAgent', + oss: { + accessKeyId: 'ak', + accessKeySecret: 'sk', + bucket: 'bucket', + region: 'region', + }, + }); + + assert.strictEqual(tracer.agentName, 'MyAgent'); + assert.strictEqual(tracer.name, 'LangGraphTracer', 'name should remain default'); + }); + + it('should not change agentName when not provided', () => { + const tracingService = createMockTracingService(); + const tracer = new LangGraphTracer(); + (tracer as any).tracingService = tracingService; + tracer.agentName = 'existing'; + + tracer.configure({}); + + assert.strictEqual(tracer.agentName, 'existing'); + }); + }); + + describe('ClaudeAgentTracer.configure()', () => { + it('should set agentName and delegate to TracingService', () => { + const tracingService = createMockTracingService(); + const mockLogger = createMockLogger(); + const tracer = new ClaudeAgentTracer(); + (tracer as any).logger = mockLogger; + (tracer as any).tracingService = tracingService; + + tracer.configure({ + agentName: 'MyClaude', + oss: { + accessKeyId: 'ak', + accessKeySecret: 'sk', + bucket: 'bucket', + region: 'region', + }, + }); + + assert.strictEqual(tracer.agentName, 'MyClaude'); + assert.strictEqual(tracer.name, 'ClaudeAgentTracer', 'name should remain default'); + }); + + it('should not change agentName when not provided', () => { + const tracingService = createMockTracingService(); + const mockLogger = createMockLogger(); + const tracer = new ClaudeAgentTracer(); + (tracer as any).logger = mockLogger; + (tracer as any).tracingService = tracingService; + tracer.agentName = 'existing'; + + tracer.configure({}); + + assert.strictEqual(tracer.agentName, 'existing'); + }); + }); +}); diff --git a/tegg/core/agent-tracing/test/langgraph-integration.test.ts b/tegg/core/agent-tracing/test/langgraph-integration.test.ts new file mode 100644 index 0000000000..0061e0cf7a --- /dev/null +++ b/tegg/core/agent-tracing/test/langgraph-integration.test.ts @@ -0,0 +1,315 @@ +import assert from 'node:assert'; + +import { FakeLLM } from '@langchain/core/utils/testing'; +import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; +import { describe, it, beforeEach } from 'vitest'; + +import { LangGraphTracer } from '../src/LangGraphTracer.ts'; +import { createMockTracingService } from './test-utils.ts'; + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Extract the run JSON object from a tracing log line. + * Log format: "[agent_run][...]:traceId=...,run={...JSON...}" + */ +function extractRunFromLog(log: string): any { + const match = log.match(/,run=({.*})$/); + return match ? JSON.parse(match[1]) : null; +} + +/** Shared state schema for test graphs */ +const GraphState = Annotation.Root({ + query: Annotation, + result: Annotation, +}); + +describe('egg-agent-tracing/test/langgraph-integration.test.ts', () => { + let tracer: LangGraphTracer; + let logs: string[] = []; + + beforeEach(() => { + logs = []; + process.env.FAAS_ENV = 'dev'; + + const tracingService = createMockTracingService(logs); + + tracer = new LangGraphTracer(); + (tracer as any).tracingService = tracingService; + }); + + /** Helper: get only [agent_run] tracing logs */ + function getTracingLogs(): string[] { + return logs.filter((log) => log.includes('[agent_run]')); + } + + describe('Single-node StateGraph triggers chain lifecycle hooks', () => { + it('should trigger onChainStart and onChainEnd via graph.invoke', async () => { + const graph = new StateGraph(GraphState) + .addNode('process', (state: typeof GraphState.State) => { + return { result: `processed: ${state.query}` }; + }) + .addEdge(START, 'process') + .addEdge('process', END) + .compile(); + + await graph.invoke({ query: 'hello', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const tracingLogs = getTracingLogs(); + const startLogs = tracingLogs.filter((log) => log.includes('status=start')); + const endLogs = tracingLogs.filter((log) => log.includes('status=end')); + + assert(startLogs.length >= 1, `Should have at least one start log, got ${startLogs.length}`); + assert(endLogs.length >= 1, `Should have at least one end log, got ${endLogs.length}`); + + // Parse run data and verify run_type is chain (StateGraph nodes are chain runs) + const chainStartLog = startLogs.find((log) => { + const run = extractRunFromLog(log); + return run && run.run_type === 'chain'; + }); + assert(chainStartLog, 'Should have a chain start log with run_type=chain'); + }); + + it('should produce Run with valid id, trace_id, and run_type fields', async () => { + const graph = new StateGraph(GraphState) + .addNode('echo', (state: typeof GraphState.State) => { + return { result: state.query }; + }) + .addEdge(START, 'echo') + .addEdge('echo', END) + .compile(); + + await graph.invoke({ query: 'test', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const tracingLogs = getTracingLogs(); + assert(tracingLogs.length > 0, 'Should have tracing logs'); + + for (const log of tracingLogs) { + const run = extractRunFromLog(log); + if (!run) continue; + assert(run.id, 'Run should have an id'); + assert(run.trace_id, 'Run should have a trace_id'); + assert(run.run_type, 'Run should have a run_type'); + } + }); + }); + + describe('Multi-node linear StateGraph triggers parent-child chain hooks', () => { + it('should generate root and child chain runs with parent_run_id relationship', async () => { + const graph = new StateGraph(GraphState) + .addNode('preprocess', (state: typeof GraphState.State) => { + return { query: state.query.toUpperCase() }; + }) + .addNode('respond', (state: typeof GraphState.State) => { + return { result: `answer to ${state.query}` }; + }) + .addEdge(START, 'preprocess') + .addEdge('preprocess', 'respond') + .addEdge('respond', END) + .compile(); + + await graph.invoke({ query: 'question', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const tracingLogs = getTracingLogs(); + + // Collect all runs by parsing logs + const runs: Array<{ run: any; status: string }> = []; + for (const log of tracingLogs) { + const statusMatch = log.match(/status=(\w+)/); + const run = extractRunFromLog(log); + if (run && statusMatch) { + runs.push({ run, status: statusMatch[1] }); + } + } + + const chainRuns = runs.filter((r) => r.run.run_type === 'chain'); + assert(chainRuns.length >= 2, `Should have at least 2 chain runs (root + child nodes), got ${chainRuns.length}`); + + // The graph root run should have no parent_run_id + const rootRun = chainRuns.find((r) => !r.run.parent_run_id); + assert(rootRun, 'Should have a root chain run (no parent_run_id)'); + + // Child node runs should have parent_run_id + const childRuns = chainRuns.filter((r) => r.run.parent_run_id); + assert(childRuns.length >= 1, 'Should have at least one child chain run with parent_run_id'); + + // Verify the log prefix correctly marks root vs child + const rootLogs = tracingLogs.filter((log) => log.includes('type=root_run')); + const childLogs = tracingLogs.filter((log) => log.includes('type=child_run')); + assert(rootLogs.length >= 1, 'Should have root_run type logs'); + assert(childLogs.length >= 1, 'Should have child_run type logs'); + }); + + it('should share the same trace_id across all runs in a graph invocation', async () => { + const graph = new StateGraph(GraphState) + .addNode('step1', (state: typeof GraphState.State) => { + return { query: `[step1] ${state.query}` }; + }) + .addNode('step2', (state: typeof GraphState.State) => { + return { result: `[step2] ${state.query}` }; + }) + .addEdge(START, 'step1') + .addEdge('step1', 'step2') + .addEdge('step2', END) + .compile(); + + await graph.invoke({ query: 'hello', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const tracingLogs = getTracingLogs(); + const traceIds = new Set(); + + for (const log of tracingLogs) { + const run = extractRunFromLog(log); + if (run?.trace_id) { + traceIds.add(run.trace_id); + } + } + + assert(traceIds.size === 1, `All runs should share the same trace_id, got ${traceIds.size} distinct trace_ids`); + }); + }); + + describe('StateGraph with LLM node triggers both chain and LLM hooks', () => { + it('should trace both chain runs (graph) and LLM runs (FakeLLM inside node)', async () => { + const llm = new FakeLLM({ response: 'llm answer' }); + + const graph = new StateGraph(GraphState) + .addNode('ask_llm', async (state: typeof GraphState.State) => { + const response = await llm.invoke(state.query); + return { result: response }; + }) + .addEdge(START, 'ask_llm') + .addEdge('ask_llm', END) + .compile(); + + await graph.invoke({ query: 'what is LangGraph?', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const tracingLogs = getTracingLogs(); + + const runs: Array<{ run: any; status: string }> = []; + for (const log of tracingLogs) { + const statusMatch = log.match(/status=(\w+)/); + const run = extractRunFromLog(log); + if (run && statusMatch) { + runs.push({ run, status: statusMatch[1] }); + } + } + + // Should have chain runs from the graph itself + const chainRuns = runs.filter((r) => r.run.run_type === 'chain'); + assert(chainRuns.length >= 1, `Should have at least one chain run, got ${chainRuns.length}`); + + // Should have LLM runs from the FakeLLM invocation inside the node + const llmRuns = runs.filter((r) => r.run.run_type === 'llm'); + assert(llmRuns.length >= 2, `Should have at least 2 LLM logs (start + end), got ${llmRuns.length}`); + }); + }); + + describe('StateGraph node error triggers onChainError', () => { + it('should trigger error hook and include error status in log', async () => { + const graph = new StateGraph(GraphState) + .addNode('fail_node', () => { + throw new Error('Node execution failed'); + }) + .addEdge(START, 'fail_node') + .addEdge('fail_node', END) + .compile(); + + try { + await graph.invoke({ query: 'trigger error', result: '' }, { callbacks: [tracer] }); + } catch { + // Expected error + } + + await sleep(500); + + const tracingLogs = getTracingLogs(); + + // Should have a start log + const startLogs = tracingLogs.filter((log) => log.includes('status=start')); + assert(startLogs.length >= 1, 'Should have at least one start log before the error'); + + // Should have an error log + const errorLogs = tracingLogs.filter((log) => log.includes('status=error')); + assert(errorLogs.length >= 1, `Should have at least one error log, got ${errorLogs.length}`); + + // Verify the error run has the correct error field + const errorRun = extractRunFromLog(errorLogs[0]); + assert(errorRun, 'Should be able to parse error run from log'); + assert(errorRun.error, 'Error run should have error field set'); + }); + }); + + describe('Run data completeness via graph.invoke', () => { + it('should produce runs with all required fields populated', async () => { + const graph = new StateGraph(GraphState) + .addNode('check', (state: typeof GraphState.State) => { + return { result: `checked: ${state.query}` }; + }) + .addEdge(START, 'check') + .addEdge('check', END) + .compile(); + + await graph.invoke({ query: 'check fields', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const tracingLogs = getTracingLogs(); + assert(tracingLogs.length > 0, 'Should have tracing logs'); + + for (const log of tracingLogs) { + const run = extractRunFromLog(log); + if (!run) continue; + + // Core fields must be present + assert(typeof run.id === 'string' && run.id.length > 0, 'Run must have non-empty id'); + assert(typeof run.trace_id === 'string' && run.trace_id.length > 0, 'Run must have non-empty trace_id'); + assert(typeof run.run_type === 'string', 'Run must have run_type'); + assert(typeof run.name === 'string', 'Run must have name'); + + // Log prefix must contain traceId and run_id + assert(log.includes(`traceId=${run.trace_id}`), 'Log prefix must include traceId'); + assert(log.includes(`run_id=${run.id}`), 'Log prefix must include run_id'); + } + }); + + it('should produce end runs with non-null outputs or OSS reference', async () => { + const graph = new StateGraph(GraphState) + .addNode('output_node', (state: typeof GraphState.State) => { + return { result: `output for ${state.query}` }; + }) + .addEdge(START, 'output_node') + .addEdge('output_node', END) + .compile(); + + await graph.invoke({ query: 'output test', result: '' }, { callbacks: [tracer] }); + + await sleep(500); + + const endLogs = getTracingLogs().filter((log) => log.includes('status=end')); + assert(endLogs.length >= 1, 'Should have end logs'); + + for (const log of endLogs) { + const run = extractRunFromLog(log); + if (!run) continue; + // Outputs should be uploaded to OSS, so the field should be an IResource + if (run.outputs) { + assert( + run.outputs.key || typeof run.outputs === 'object', + 'End run outputs should be present (as OSS resource or object)', + ); + } + } + }); + }); +}); diff --git a/tegg/core/agent-tracing/test/test-utils.ts b/tegg/core/agent-tracing/test/test-utils.ts new file mode 100644 index 0000000000..b5e9ba943e --- /dev/null +++ b/tegg/core/agent-tracing/test/test-utils.ts @@ -0,0 +1,30 @@ +import type { Logger } from '@eggjs/tegg-types'; + +import { TracingService } from '../src/TracingService.ts'; + +export function createMockLogger(logs?: string[]): Logger { + return { + info: (msg: string) => { + logs?.push(msg); + }, + warn: (msg: string) => { + logs?.push(msg); + }, + error: (msg: string) => { + logs?.push(msg); + }, + } as unknown as Logger; +} + +export function createMockBackgroundTaskHelper(): { run: (fn: () => Promise) => Promise } { + return { + run: async (fn: () => Promise) => fn(), + }; +} + +export function createMockTracingService(logs?: string[]): TracingService { + const tracingService = new TracingService(); + (tracingService as any).logger = createMockLogger(logs); + (tracingService as any).backgroundTaskHelper = createMockBackgroundTaskHelper(); + return tracingService; +} diff --git a/tegg/core/agent-tracing/tsconfig.json b/tegg/core/agent-tracing/tsconfig.json new file mode 100644 index 0000000000..618c6c3e97 --- /dev/null +++ b/tegg/core/agent-tracing/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tsconfig.json" +} diff --git a/tegg/core/agent-tracing/vitest.config.ts b/tegg/core/agent-tracing/vitest.config.ts new file mode 100644 index 0000000000..2b417434e4 --- /dev/null +++ b/tegg/core/agent-tracing/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({ + test: { + testTimeout: 15000, + include: ['test/**/*.test.ts'], + exclude: ['**/test/fixtures/**', '**/node_modules/**', '**/dist/**'], + }, +}); diff --git a/tsconfig.json b/tsconfig.json index 0a167c7226..c2cdac1259 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -118,6 +118,9 @@ }, { "path": "./tegg/core/agent-runtime" + }, + { + "path": "./tegg/core/agent-tracing" } ] } From 981861822a33345c510dd956adc0a2c8442fdf5f Mon Sep 17 00:00:00 2001 From: jerry Date: Mon, 2 Mar 2026 00:11:41 +0800 Subject: [PATCH 02/18] refactor(agent-tracing): rename test files to PascalCase and clean up code - Rename test files to match PascalCase convention - Remove unused module.yml - Clean up TracingService and ClaudeAgentTracer code - Remove unnecessary dependencies from package.json Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 6 ----- tegg/core/agent-tracing/module.yml | 3 --- tegg/core/agent-tracing/package.json | 4 +--- .../agent-tracing/src/ClaudeAgentTracer.ts | 23 ++++++++++--------- .../core/agent-tracing/src/LangGraphTracer.ts | 2 +- tegg/core/agent-tracing/src/TracingService.ts | 14 ++--------- ...tion.test.ts => ClaudeAgentTracer.test.ts} | 4 ++-- .../{configure.test.ts => Configure.test.ts} | 4 ++-- ...ration.test.ts => LangGraphTracer.test.ts} | 4 ++-- .../test/{test-utils.ts => TestUtils.ts} | 0 10 files changed, 22 insertions(+), 42 deletions(-) delete mode 100644 tegg/core/agent-tracing/module.yml rename tegg/core/agent-tracing/test/{claude-agent-integration.test.ts => ClaudeAgentTracer.test.ts} (98%) rename tegg/core/agent-tracing/test/{configure.test.ts => Configure.test.ts} (97%) rename tegg/core/agent-tracing/test/{langgraph-integration.test.ts => LangGraphTracer.test.ts} (98%) rename tegg/core/agent-tracing/test/{test-utils.ts => TestUtils.ts} (100%) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2ddd7c956..072f2a3bd6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2073,9 +2073,6 @@ importers: onelogger: specifier: 'catalog:' version: 1.0.1 - uuid: - specifier: ^10.0.0 - version: 10.0.0 devDependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.52 @@ -2098,9 +2095,6 @@ importers: '@types/node': specifier: 'catalog:' version: 24.10.2 - '@types/uuid': - specifier: ^10.0.0 - version: 10.0.0 typescript: specifier: 'catalog:' version: 5.9.3 diff --git a/tegg/core/agent-tracing/module.yml b/tegg/core/agent-tracing/module.yml deleted file mode 100644 index a2b12ee1ee..0000000000 --- a/tegg/core/agent-tracing/module.yml +++ /dev/null @@ -1,3 +0,0 @@ -# @eggjs/agent-tracing module configuration -# -# Config is passed via tracer.configure(). See TracerConfig interface. diff --git a/tegg/core/agent-tracing/package.json b/tegg/core/agent-tracing/package.json index 231157671b..af12d7e165 100644 --- a/tegg/core/agent-tracing/package.json +++ b/tegg/core/agent-tracing/package.json @@ -54,8 +54,7 @@ "@eggjs/core-decorator": "workspace:*", "@eggjs/tegg-types": "workspace:*", "ali-oss": "^6.21.0", - "onelogger": "catalog:", - "uuid": "^10.0.0" + "onelogger": "catalog:" }, "devDependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.52", @@ -65,7 +64,6 @@ "@langchain/langgraph": "^0.2.74", "@types/ali-oss": "^6.16.0", "@types/node": "catalog:", - "@types/uuid": "^10.0.0", "typescript": "catalog:" }, "peerDependencies": { diff --git a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts index b408639587..e0a7ffd0a2 100644 --- a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts +++ b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts @@ -1,9 +1,10 @@ +import { randomUUID } from 'node:crypto'; + import type { SDKMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'; import { SingletonProto, Inject } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; -import { v4 as uuidv4 } from 'uuid'; import type { TracingService } from './TracingService.ts'; import { @@ -31,8 +32,8 @@ export class TraceSession { constructor(tracer: ClaudeAgentTracer, sessionId?: string) { this.tracer = tracer; - this.traceId = sessionId || uuidv4(); - this.rootRunId = uuidv4(); + this.traceId = sessionId || randomUUID(); + this.rootRunId = randomUUID(); this.startTime = Date.now(); } @@ -59,13 +60,13 @@ export class TraceSession { } } - private async handleInit(message: ClaudeMessage): Promise { + private handleInit(message: ClaudeMessage): void { this.traceId = message.session_id || this.traceId; this.rootRun = this.tracer.createRootRunInternal(message, this.startTime, this.traceId, this.rootRunId); this.tracer.logTrace(this.rootRun, RunStatus.START); } - private async handleAssistant(message: ClaudeMessage): Promise { + private handleAssistant(message: ClaudeMessage): void { if (!this.rootRun) { this.tracer.logger.warn('[ClaudeAgentTracer] Received assistant message before init'); return; @@ -118,7 +119,7 @@ export class TraceSession { } } - private async handleUser(message: ClaudeMessage): Promise { + private handleUser(message: ClaudeMessage): void { if (!message.message?.content) return; for (const block of message.message.content) { @@ -134,7 +135,7 @@ export class TraceSession { } } - private async handleResult(message: ClaudeMessage): Promise { + private handleResult(message: ClaudeMessage): void { if (!this.rootRun) { this.tracer.logger.warn('[ClaudeAgentTracer] Received result message before init'); return; @@ -278,7 +279,7 @@ export class ClaudeAgentTracer { if (msg.type === 'user' && 'message' in msg && !('isReplay' in msg && (msg as any).isReplay)) { return { type: 'user', - uuid: msg.uuid || uuidv4(), + uuid: msg.uuid || randomUUID(), session_id: msg.session_id, message: msg.message as any, parent_tool_use_id: (msg as any).parent_tool_use_id, @@ -314,7 +315,7 @@ export class ClaudeAgentTracer { * Create root run from init message (used by TraceSession) */ createRootRunInternal(initMsg: ClaudeMessage, startTime: number, traceId: string, rootRunId?: string): Run { - const runId = rootRunId || initMsg.uuid || uuidv4(); + const runId = rootRunId || initMsg.uuid || randomUUID(); return { id: runId, @@ -359,7 +360,7 @@ export class ClaudeAgentTracer { startTime: number, isToolCall: boolean, ): Run { - const runId = msg.uuid || uuidv4(); + const runId = msg.uuid || randomUUID(); const content = msg.message?.content || []; const textBlocks = content.filter((c) => c.type === 'text'); @@ -417,7 +418,7 @@ export class ClaudeAgentTracer { startTime: number, ): Run { const toolUse = toolUseBlock as any; - const runId = uuidv4(); + const runId = randomUUID(); return { id: runId, diff --git a/tegg/core/agent-tracing/src/LangGraphTracer.ts b/tegg/core/agent-tracing/src/LangGraphTracer.ts index d4b16defcb..de52ac553e 100644 --- a/tegg/core/agent-tracing/src/LangGraphTracer.ts +++ b/tegg/core/agent-tracing/src/LangGraphTracer.ts @@ -64,7 +64,7 @@ export class LangGraphTracer extends BaseTracer { } onRetrieverStart(run: Run): void | Promise { - return this.logTrace(run, RunStatus.START); + this.logTrace(run, RunStatus.START); } onRetrieverEnd(run: Run): void | Promise { this.logTrace(run, RunStatus.END); diff --git a/tegg/core/agent-tracing/src/TracingService.ts b/tegg/core/agent-tracing/src/TracingService.ts index 48b09ca24d..8358d66ba8 100644 --- a/tegg/core/agent-tracing/src/TracingService.ts +++ b/tegg/core/agent-tracing/src/TracingService.ts @@ -71,23 +71,13 @@ export class TracingService { */ getLogInfoPrefix(run: Run, status: RunStatus, name: string): string { const env = this.getEnv(); - if (process.env.FAAS_ENV || env === 'local') { - return ( - `[agent_run][${name}]:` + - `traceId=${run.trace_id},` + - `type=${run.parent_run_id ? 'child_run' : 'root_run'},` + - `status=${status},` + - `run_id=${run.id},` + - `parent_run_id=${run.parent_run_id ?? ''}` - ); - } - + const envSegment = process.env.FAAS_ENV || env === 'local' ? '' : `env=${env},`; return ( `[agent_run][${name}]:` + `traceId=${run.trace_id},` + `type=${run.parent_run_id ? 'child_run' : 'root_run'},` + `status=${status},` + - `env=${env},` + + `${envSegment}` + `run_id=${run.id},` + `parent_run_id=${run.parent_run_id ?? ''}` ); diff --git a/tegg/core/agent-tracing/test/claude-agent-integration.test.ts b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts similarity index 98% rename from tegg/core/agent-tracing/test/claude-agent-integration.test.ts rename to tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts index 0f29b736f5..4a1fb55688 100644 --- a/tegg/core/agent-tracing/test/claude-agent-integration.test.ts +++ b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts @@ -4,7 +4,7 @@ import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; import { describe, it } from 'vitest'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; -import { createMockLogger, createMockTracingService } from './test-utils.ts'; +import { createMockLogger, createMockTracingService } from './TestUtils.ts'; // ---------- Tracing log helpers ---------- @@ -191,7 +191,7 @@ function createMockStreamEvent(): SDKMessage { // ---------- Tests ---------- -describe('egg-agent-tracing/test/claude-agent-integration.test.ts', () => { +describe('test/ClaudeAgentTracer.test.ts', () => { describe('Streaming mode + tool use', () => { it('should trace tool execution with session.processMessage', async () => { const { claudeTracer, parseAllRuns } = createTestEnv(); diff --git a/tegg/core/agent-tracing/test/configure.test.ts b/tegg/core/agent-tracing/test/Configure.test.ts similarity index 97% rename from tegg/core/agent-tracing/test/configure.test.ts rename to tegg/core/agent-tracing/test/Configure.test.ts index d7bb81f6f0..d64af6a3b1 100644 --- a/tegg/core/agent-tracing/test/configure.test.ts +++ b/tegg/core/agent-tracing/test/Configure.test.ts @@ -5,9 +5,9 @@ import { describe, it, beforeEach } from 'vitest'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; import { TracingService } from '../src/TracingService.ts'; -import { createMockLogger, createMockTracingService } from './test-utils.ts'; +import { createMockLogger, createMockTracingService } from './TestUtils.ts'; -describe('egg-agent-tracing/test/configure.test.ts', () => { +describe('test/Configure.test.ts', () => { describe('TracingService.configure()', () => { let tracingService: TracingService; diff --git a/tegg/core/agent-tracing/test/langgraph-integration.test.ts b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts similarity index 98% rename from tegg/core/agent-tracing/test/langgraph-integration.test.ts rename to tegg/core/agent-tracing/test/LangGraphTracer.test.ts index 0061e0cf7a..6e84362eba 100644 --- a/tegg/core/agent-tracing/test/langgraph-integration.test.ts +++ b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts @@ -5,7 +5,7 @@ import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; import { describe, it, beforeEach } from 'vitest'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; -import { createMockTracingService } from './test-utils.ts'; +import { createMockTracingService } from './TestUtils.ts'; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -24,7 +24,7 @@ const GraphState = Annotation.Root({ result: Annotation, }); -describe('egg-agent-tracing/test/langgraph-integration.test.ts', () => { +describe('test/LangGraphTracer.test.ts', () => { let tracer: LangGraphTracer; let logs: string[] = []; diff --git a/tegg/core/agent-tracing/test/test-utils.ts b/tegg/core/agent-tracing/test/TestUtils.ts similarity index 100% rename from tegg/core/agent-tracing/test/test-utils.ts rename to tegg/core/agent-tracing/test/TestUtils.ts From 547bafe068a832529434eb48022157bcf820aef7 Mon Sep 17 00:00:00 2001 From: jerry Date: Mon, 2 Mar 2026 15:36:42 +0800 Subject: [PATCH 03/18] fix(deps): bump @langchain/core to ^1.1.29 and dedupe lockfile - Bump @langchain/core from ^1.1.1 to ^1.1.29 in langchain-decorator, langchain plugin, and agent-tracing to fix `this._addVersion is not a function` error caused by @langchain/openai@1.2.11 requiring the new _addVersion method introduced in @langchain/core@1.1.29 - Run pnpm dedupe to resolve ERR_PNPM_DEDUPE_CHECK_ISSUES in CI Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 203 +++++++++------------ tegg/core/agent-tracing/package.json | 4 +- tegg/core/langchain-decorator/package.json | 2 +- tegg/plugin/langchain/package.json | 2 +- 4 files changed, 91 insertions(+), 120 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 072f2a3bd6..9c9ceb9c38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,7 +191,7 @@ catalogs: version: 2.1.4 body-parser: specifier: ^2.0.0 - version: 2.2.0 + version: 2.2.2 bytes: specifier: ^3.1.2 version: 3.1.2 @@ -335,7 +335,7 @@ catalogs: version: 2.2.0 http-errors: specifier: ^2.0.0 - version: 2.0.0 + version: 2.0.1 humanize-ms: specifier: ^2.0.0 version: 2.0.0 @@ -1167,7 +1167,7 @@ importers: version: 1.0.2 http-errors: specifier: 'catalog:' - version: 2.0.0 + version: 2.0.1 is-type-of: specifier: 'catalog:' version: 2.2.0 @@ -1351,7 +1351,7 @@ importers: dependencies: http-errors: specifier: 'catalog:' - version: 2.0.0 + version: 2.0.1 inflection: specifier: 'catalog:' version: 3.0.2 @@ -1419,7 +1419,7 @@ importers: version: 5.0.3 body-parser: specifier: 'catalog:' - version: 2.2.0 + version: 2.2.2 cookie-parser: specifier: 'catalog:' version: 1.4.7 @@ -2084,11 +2084,11 @@ importers: specifier: workspace:* version: link:../common-util '@langchain/core': - specifier: ^1.1.1 - version: 1.1.28(openai@6.25.0(zod@4.3.6)) + specifier: ^1.1.29 + version: 1.1.29(openai@6.25.0(zod@4.3.6)) '@langchain/langgraph': specifier: ^0.2.74 - version: 0.2.74(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6)) + version: 0.2.74(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6)) '@types/ali-oss': specifier: ^6.16.0 version: 6.23.3 @@ -2508,17 +2508,17 @@ importers: specifier: workspace:* version: link:../types '@langchain/core': - specifier: ^1.1.1 - version: 1.1.28(openai@6.25.0(zod@3.25.76)) + specifier: ^1.1.29 + version: 1.1.29(openai@6.25.0(zod@3.25.76)) '@langchain/langgraph': specifier: ^1.0.2 - version: 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) + version: 1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) '@langchain/openai': specifier: ^1.1.0 - version: 1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + version: 1.2.11(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))) langchain: specifier: ^1.1.2 - version: 1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(openai@6.25.0(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)) + version: 1.2.28(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(openai@6.25.0(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)) devDependencies: '@eggjs/controller-decorator': specifier: workspace:* @@ -2587,7 +2587,7 @@ importers: version: link:../types '@langchain/mcp-adapters': specifier: ^1.0.0 - version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)) + version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)) '@modelcontextprotocol/sdk': specifier: ^1.23.0 version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76) @@ -3235,20 +3235,20 @@ importers: specifier: workspace:* version: link:../../core/types '@langchain/core': - specifier: ^1.1.1 - version: 1.1.28(openai@6.25.0(zod@4.3.6)) + specifier: ^1.1.29 + version: 1.1.29(openai@6.25.0(zod@4.3.6)) '@langchain/langgraph': specifier: ^1.0.2 - version: 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) + version: 1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) '@langchain/mcp-adapters': specifier: ^1.0.0 - version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)) + version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)) '@langchain/openai': specifier: ^1.0.0 - version: 1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + version: 1.2.11(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))) langchain: specifier: ^1.1.2 - version: 1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(openai@6.25.0(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)) + version: 1.2.28(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(openai@6.25.0(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)) urllib: specifier: 'catalog:' version: 4.8.2 @@ -4393,8 +4393,8 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@langchain/core@1.1.28': - resolution: {integrity: sha512-6FAGdezEp8zHY92LtnsAiv54KaG41nBdsuukk+R+1484edV20cVOyIc36ANuGKPx0pmYFCBWhCUdO0jxB/zn2Q==} + '@langchain/core@1.1.29': + resolution: {integrity: sha512-BPoegTtIdZX4gl2kxcSXAlLrrJFl1cxeRsk9DM/wlIuvyPrFwjWqrEK5NwF5diDt5XSArhQxIFaifGAl4F7fgw==} engines: {node: '>=20'} '@langchain/langgraph-checkpoint@0.0.18': @@ -5222,9 +5222,6 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -6033,10 +6030,6 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} - engines: {node: '>=18'} - body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} @@ -7086,11 +7079,13 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@11.0.3: resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@13.0.0: @@ -7099,12 +7094,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -7682,7 +7677,6 @@ packages: keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} @@ -8831,10 +8825,6 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} @@ -9491,6 +9481,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me tcp-base@3.2.0: resolution: {integrity: sha512-fFAqH8QTbheuEbXLdbxTSe31Gkw6Lg3nq4loyrxIXM6+ILGdbYXEblgyuu7UltOkOHbP/q2iqaC+gIXXu0C5bg==} @@ -10513,7 +10504,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))': + '@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 @@ -10523,7 +10514,7 @@ snapshots: langsmith: 0.5.7(openai@6.25.0(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 - uuid: 10.0.0 + uuid: 11.1.0 zod: 4.3.6 transitivePeerDependencies: - '@opentelemetry/api' @@ -10531,7 +10522,7 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))': + '@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 @@ -10541,7 +10532,7 @@ snapshots: langsmith: 0.5.7(openai@6.25.0(zod@4.3.6)) mustache: 4.2.0 p-queue: 6.6.2 - uuid: 10.0.0 + uuid: 11.1.0 zod: 4.3.6 transitivePeerDependencies: - '@opentelemetry/api' @@ -10549,53 +10540,53 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) uuid: 10.0.0 - '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))': + '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) uuid: 10.0.0 - '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) uuid: 10.0.0 - '@langchain/langgraph-sdk@0.0.112(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + '@langchain/langgraph-sdk@0.0.112(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph-sdk@1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))': + '@langchain/langgraph-sdk@1.6.5(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))': dependencies: '@types/json-schema': 7.0.15 p-queue: 9.1.0 p-retry: 7.1.1 uuid: 13.0.0 optionalDependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) - '@langchain/langgraph-sdk@1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + '@langchain/langgraph-sdk@1.6.5(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))': dependencies: '@types/json-schema': 7.0.15 p-queue: 9.1.0 p-retry: 7.1.1 uuid: 13.0.0 optionalDependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph@0.2.74(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))': + '@langchain/langgraph@0.2.74(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) - '@langchain/langgraph-sdk': 0.0.112(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))) + '@langchain/langgraph-sdk': 0.0.112(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: @@ -10604,11 +10595,11 @@ snapshots: - react - react-dom - '@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)': + '@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) - '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))) + '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 3.25.76 @@ -10618,11 +10609,11 @@ snapshots: - react - react-dom - '@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6)': + '@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6)': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) - '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))) + '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 4.3.6 @@ -10632,11 +10623,11 @@ snapshots: - react - react-dom - '@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)': + '@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6)': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) - '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))) + '@langchain/langgraph-sdk': 1.6.5(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 4.3.6 @@ -10646,10 +10637,10 @@ snapshots: - react - react-dom - '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76))': + '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) - '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) '@modelcontextprotocol/sdk': 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) debug: 4.4.3(supports-color@8.1.1) zod: 4.3.6 @@ -10659,10 +10650,10 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6))': + '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(@langchain/langgraph@1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) '@modelcontextprotocol/sdk': 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) debug: 4.4.3(supports-color@8.1.1) zod: 4.3.6 @@ -10672,18 +10663,18 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@langchain/openai@1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))': + '@langchain/openai@1.2.11(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) js-tiktoken: 1.0.21 openai: 6.25.0(zod@4.3.6) zod: 4.3.6 transitivePeerDependencies: - ws - '@langchain/openai@1.2.11(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))': + '@langchain/openai@1.2.11(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))': dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) js-tiktoken: 1.0.21 openai: 6.25.0(zod@4.3.6) zod: 4.3.6 @@ -11314,8 +11305,6 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} - '@standard-schema/spec@1.0.0': {} - '@standard-schema/spec@1.1.0': {} '@swc-node/core@1.14.1(@swc/core@1.15.3)(@swc/types@0.1.25)': @@ -11721,7 +11710,7 @@ snapshots: '@vitest/expect@4.0.15': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 '@vitest/spy': 4.0.15 '@vitest/utils': 4.0.15 @@ -11954,7 +11943,7 @@ snapshots: mime: 2.6.0 platform: 1.3.6 pump: 3.0.3 - qs: 6.14.0 + qs: 6.15.0 sdk-base: 2.0.1 stream-http: 2.8.2 stream-wormhole: 1.1.0 @@ -12126,26 +12115,12 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.0: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3(supports-color@8.1.1) - http-errors: 2.0.0 - iconv-lite: 0.6.3 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.1 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - body-parser@2.2.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3(supports-color@8.1.1) - http-errors: 2.0.0 + http-errors: 2.0.1 iconv-lite: 0.7.0 on-finished: 2.4.1 qs: 6.15.0 @@ -12458,7 +12433,7 @@ snapshots: dependencies: '@hapi/bourne': 3.0.0 inflation: 2.1.0 - qs: 6.14.0 + qs: 6.15.0 raw-body: 2.5.2 type-is: 1.6.18 @@ -13125,14 +13100,14 @@ snapshots: etag: 1.8.1 finalhandler: 2.1.1 fresh: 2.0.0 - http-errors: 2.0.0 + http-errors: 2.0.1 merge-descriptors: 2.0.0 mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.14.0 + qs: 6.15.0 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -14022,11 +13997,11 @@ snapshots: transitivePeerDependencies: - supports-color - langchain@1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(openai@6.25.0(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)): + langchain@1.2.28(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(openai@6.25.0(zod@3.25.76))(zod-to-json-schema@3.25.1(zod@3.25.76)): dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@3.25.76)) - '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@3.25.76))) + '@langchain/core': 1.1.29(openai@6.25.0(zod@3.25.76)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76)))(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@4.3.6) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@3.25.76))) langsmith: 0.5.7(openai@6.25.0(zod@3.25.76)) uuid: 11.1.0 zod: 4.3.6 @@ -14039,11 +14014,11 @@ snapshots: - react-dom - zod-to-json-schema - langchain@1.2.28(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(openai@6.25.0(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)): + langchain@1.2.28(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(openai@6.25.0(zod@4.3.6))(zod-to-json-schema@3.25.1(zod@4.3.6)): dependencies: - '@langchain/core': 1.1.28(openai@6.25.0(zod@4.3.6)) - '@langchain/langgraph': 1.2.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.28(openai@6.25.0(zod@4.3.6))) + '@langchain/core': 1.1.29(openai@6.25.0(zod@4.3.6)) + '@langchain/langgraph': 1.2.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6))(zod@4.3.6) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6))) langsmith: 0.5.7(openai@6.25.0(zod@4.3.6)) uuid: 11.1.0 zod: 4.3.6 @@ -15355,10 +15330,6 @@ snapshots: dependencies: side-channel: 1.1.0 - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -16071,7 +16042,7 @@ snapshots: formidable: 3.5.4 methods: 1.1.2 mime: 2.6.0 - qs: 6.14.0 + qs: 6.15.0 transitivePeerDependencies: - supports-color @@ -16415,7 +16386,7 @@ snapshots: humanize-ms: 1.2.1 iconv-lite: 0.6.3 pump: 3.0.3 - qs: 6.14.0 + qs: 6.15.0 statuses: 1.5.0 utility: 1.18.0 @@ -16428,7 +16399,7 @@ snapshots: formstream: 1.5.2 mime-types: 2.1.35 pump: 3.0.3 - qs: 6.14.0 + qs: 6.15.0 type-fest: 4.41.0 undici: 5.29.0 ylru: 1.4.0 @@ -16438,7 +16409,7 @@ snapshots: form-data: 4.0.4 formstream: 1.5.2 mime-types: 2.1.35 - qs: 6.14.0 + qs: 6.15.0 type-fest: 4.41.0 undici: 7.16.0 ylru: 2.0.0 diff --git a/tegg/core/agent-tracing/package.json b/tegg/core/agent-tracing/package.json index af12d7e165..368168e551 100644 --- a/tegg/core/agent-tracing/package.json +++ b/tegg/core/agent-tracing/package.json @@ -60,7 +60,7 @@ "@anthropic-ai/claude-agent-sdk": "^0.2.52", "@anthropic-ai/sdk": "^0.78.0", "@eggjs/tegg-common-util": "workspace:*", - "@langchain/core": "^1.1.1", + "@langchain/core": "^1.1.29", "@langchain/langgraph": "^0.2.74", "@types/ali-oss": "^6.16.0", "@types/node": "catalog:", @@ -68,7 +68,7 @@ }, "peerDependencies": { "@anthropic-ai/claude-agent-sdk": ">=0.2.52", - "@langchain/core": "^1.1.1" + "@langchain/core": "^1.1.29" }, "peerDependenciesMeta": { "@anthropic-ai/claude-agent-sdk": { diff --git a/tegg/core/langchain-decorator/package.json b/tegg/core/langchain-decorator/package.json index fd9577e5a1..bf7a2d4bf0 100644 --- a/tegg/core/langchain-decorator/package.json +++ b/tegg/core/langchain-decorator/package.json @@ -92,7 +92,7 @@ "@eggjs/core-decorator": "workspace:*", "@eggjs/tegg-common-util": "workspace:*", "@eggjs/tegg-types": "workspace:*", - "@langchain/core": "^1.1.1", + "@langchain/core": "^1.1.29", "@langchain/langgraph": "^1.0.2", "@langchain/openai": "^1.1.0", "langchain": "^1.1.2" diff --git a/tegg/plugin/langchain/package.json b/tegg/plugin/langchain/package.json index 859e3b58c1..0f4e962956 100644 --- a/tegg/plugin/langchain/package.json +++ b/tegg/plugin/langchain/package.json @@ -75,7 +75,7 @@ "@eggjs/tegg-common-util": "workspace:*", "@eggjs/tegg-runtime": "workspace:*", "@eggjs/tegg-types": "workspace:*", - "@langchain/core": "^1.1.1", + "@langchain/core": "^1.1.29", "@langchain/langgraph": "^1.0.2", "@langchain/mcp-adapters": "^1.0.0", "@langchain/openai": "^1.0.0", From 91efaeadccbc1e7941eba6c4f06329d46a00d95a Mon Sep 17 00:00:00 2001 From: jerry Date: Thu, 5 Mar 2026 14:10:23 +0800 Subject: [PATCH 04/18] refactor(agent-tracing): replace ali-oss direct dependency with IOssClient injection Remove hard dependency on ali-oss. TracingService now receives an optional IOssClient via @InjectOptional(), allowing users to provide any OSS implementation through the Tegg IoC container. Co-Authored-By: Claude Sonnet 4.6 --- tegg/core/agent-tracing/package.json | 2 - tegg/core/agent-tracing/src/IOssClient.ts | 27 ++++ tegg/core/agent-tracing/src/TracingService.ts | 66 ++-------- tegg/core/agent-tracing/src/index.ts | 1 + tegg/core/agent-tracing/src/types.ts | 3 - .../core/agent-tracing/test/Configure.test.ts | 124 ------------------ tegg/core/agent-tracing/test/TestUtils.ts | 8 ++ 7 files changed, 49 insertions(+), 182 deletions(-) create mode 100644 tegg/core/agent-tracing/src/IOssClient.ts diff --git a/tegg/core/agent-tracing/package.json b/tegg/core/agent-tracing/package.json index 368168e551..e75705aa72 100644 --- a/tegg/core/agent-tracing/package.json +++ b/tegg/core/agent-tracing/package.json @@ -53,7 +53,6 @@ "@eggjs/background-task": "workspace:*", "@eggjs/core-decorator": "workspace:*", "@eggjs/tegg-types": "workspace:*", - "ali-oss": "^6.21.0", "onelogger": "catalog:" }, "devDependencies": { @@ -62,7 +61,6 @@ "@eggjs/tegg-common-util": "workspace:*", "@langchain/core": "^1.1.29", "@langchain/langgraph": "^0.2.74", - "@types/ali-oss": "^6.16.0", "@types/node": "catalog:", "typescript": "catalog:" }, diff --git a/tegg/core/agent-tracing/src/IOssClient.ts b/tegg/core/agent-tracing/src/IOssClient.ts new file mode 100644 index 0000000000..a6baf6fb00 --- /dev/null +++ b/tegg/core/agent-tracing/src/IOssClient.ts @@ -0,0 +1,27 @@ +/** + * Abstract OSS client for dependency injection. + * + * To enable OSS uploads in TracingService, implement this class in your application + * and register it with Tegg IoC. The implementation class MUST be named `OssClient` + * (or use `@SingletonProto({ name: 'ossClient' })`) so the container can resolve it. + * + * @example + * ```typescript + * import { SingletonProto } from '@eggjs/core-decorator'; + * import { AccessLevel } from '@eggjs/tegg-types'; + * import { IOssClient } from '@eggjs/agent-tracing'; + * + * // Class name must be OssClient (registers as 'ossClient' in the IoC container) + * @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) + * export class OssClient extends IOssClient { + * async put(key: string, content: string | Buffer): Promise { + * // your OSS implementation here + * } + * } + * ``` + * + * If no implementation is registered, OSS uploads are silently skipped. + */ +export abstract class IOssClient { + abstract put(key: string, content: string | Buffer): Promise; +} diff --git a/tegg/core/agent-tracing/src/TracingService.ts b/tegg/core/agent-tracing/src/TracingService.ts index 8358d66ba8..02e32a6d94 100644 --- a/tegg/core/agent-tracing/src/TracingService.ts +++ b/tegg/core/agent-tracing/src/TracingService.ts @@ -1,11 +1,11 @@ import type { BackgroundTaskHelper } from '@eggjs/background-task'; -import { SingletonProto, Inject } from '@eggjs/core-decorator'; +import { SingletonProto, Inject, InjectOptional } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; -import OSS from 'ali-oss'; import { getCustomLogger } from 'onelogger'; +import { IOssClient } from './IOssClient.ts'; import { type AgentTracingConfig, FIELDS_TO_OSS, type IResource, RunStatus } from './types.ts'; /** @@ -22,29 +22,22 @@ export class TracingService { @Inject() private backgroundTaskHelper: BackgroundTaskHelper; + @InjectOptional() + private readonly ossClient: IOssClient; + private config: AgentTracingConfig = {}; - private ossClient: OSS | null = null; - private ossInitialized = false; /** - * Configure OSS and/or logService credentials. - * Validates required fields and resets cached OSS client. + * Configure logService credentials. + * Validates required fields. */ configure(config: AgentTracingConfig): void { - if (config.oss) { - const { accessKeyId, accessKeySecret, bucket, region } = config.oss; - if (!accessKeyId || !accessKeySecret || !bucket || !region) { - throw new TypeError('[TracingService] oss config requires accessKeyId, accessKeySecret, bucket, and region'); - } - } if (config.logService) { if (!config.logService.url) { throw new TypeError('[TracingService] logService config requires url'); } } this.config = config; - this.ossClient = null; - this.ossInitialized = false; } /** @@ -84,50 +77,17 @@ export class TracingService { } /** - * Lazily initialize the ali-oss client from configured credentials. - * Returns null if OSS is not configured via configure(). - */ - private getOssClient(): OSS | null { - if (this.ossInitialized) { - return this.ossClient; - } - this.ossInitialized = true; - - const ossConfig = this.config.oss; - if (!ossConfig) { - this.logger.warn( - '[TracingService] OSS not configured. Call configure({ oss: { ... } }) first. OSS uploads will be skipped.', - ); - return null; - } - - const options: OSS.Options = { - accessKeyId: ossConfig.accessKeyId, - accessKeySecret: ossConfig.accessKeySecret, - bucket: ossConfig.bucket, - region: ossConfig.region, - }; - - if (ossConfig.endpoint) { - options.endpoint = ossConfig.endpoint; - } - - this.ossClient = new OSS(options); - return this.ossClient; - } - - /** - * Upload content to OSS using ali-oss SDK. - * Gracefully skips if OSS env vars are not configured. + * Upload content to OSS using the injected IOssClient implementation. + * Gracefully skips if no IOssClient is provided. */ async uploadToOss(key: string, fileContent: string): Promise { - const client = this.getOssClient(); - if (!client) { + if (!this.ossClient) { + this.logger.warn('[TracingService] OSS client not configured. Provide an IOssClient implementation.'); return; } this.logger.info(`Uploading to OSS with key: ${key}`); - const result = await client.put(key, Buffer.from(fileContent)); - this.logger.info(`Upload response for key ${key}: ${result.res.status}`); + await this.ossClient.put(key, Buffer.from(fileContent)); + this.logger.info(`Upload completed for key: ${key}`); } /** diff --git a/tegg/core/agent-tracing/src/index.ts b/tegg/core/agent-tracing/src/index.ts index 9dfc4b8561..113db23233 100644 --- a/tegg/core/agent-tracing/src/index.ts +++ b/tegg/core/agent-tracing/src/index.ts @@ -1,2 +1,3 @@ export * from './types.ts'; export { TracingService } from './TracingService.ts'; +export { IOssClient } from './IOssClient.ts'; diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts index 7ee315fe5d..3e61a3dafc 100644 --- a/tegg/core/agent-tracing/src/types.ts +++ b/tegg/core/agent-tracing/src/types.ts @@ -159,14 +159,12 @@ export interface LogServiceConfig { /** Internal config used by TracingService */ export interface AgentTracingConfig { - oss?: OssConfig; logService?: LogServiceConfig; } /** User-facing config passed to tracer.configure() */ export interface TracerConfig { agentName?: string; - oss?: OssConfig; logService?: LogServiceConfig; } @@ -180,7 +178,6 @@ export function applyTracerConfig( tracer.agentName = config.agentName; } tracingService.configure({ - oss: config.oss, logService: config.logService, }); } diff --git a/tegg/core/agent-tracing/test/Configure.test.ts b/tegg/core/agent-tracing/test/Configure.test.ts index d64af6a3b1..d64e01313d 100644 --- a/tegg/core/agent-tracing/test/Configure.test.ts +++ b/tegg/core/agent-tracing/test/Configure.test.ts @@ -21,19 +21,6 @@ describe('test/Configure.test.ts', () => { }); }); - it('should accept complete oss config', () => { - assert.doesNotThrow(() => { - tracingService.configure({ - oss: { - accessKeyId: 'ak', - accessKeySecret: 'sk', - bucket: 'my-bucket', - region: 'cn-hangzhou', - }, - }); - }); - }); - it('should accept complete logService config', () => { assert.doesNotThrow(() => { tracingService.configure({ @@ -45,90 +32,6 @@ describe('test/Configure.test.ts', () => { }); }); - it('should accept both oss and logService config', () => { - assert.doesNotThrow(() => { - tracingService.configure({ - oss: { - accessKeyId: 'ak', - accessKeySecret: 'sk', - bucket: 'my-bucket', - region: 'cn-hangzhou', - }, - logService: { - url: 'https://log.example.com/api', - }, - }); - }); - }); - - it('should throw TypeError when oss.accessKeyId is missing', () => { - assert.throws( - () => { - tracingService.configure({ - oss: { - accessKeyId: '', - accessKeySecret: 'sk', - bucket: 'my-bucket', - region: 'cn-hangzhou', - }, - }); - }, - TypeError, - 'should throw TypeError for missing accessKeyId', - ); - }); - - it('should throw TypeError when oss.accessKeySecret is missing', () => { - assert.throws( - () => { - tracingService.configure({ - oss: { - accessKeyId: 'ak', - accessKeySecret: '', - bucket: 'my-bucket', - region: 'cn-hangzhou', - }, - }); - }, - TypeError, - 'should throw TypeError for missing accessKeySecret', - ); - }); - - it('should throw TypeError when oss.bucket is missing', () => { - assert.throws( - () => { - tracingService.configure({ - oss: { - accessKeyId: 'ak', - accessKeySecret: 'sk', - bucket: '', - region: 'cn-hangzhou', - }, - }); - }, - TypeError, - 'should throw TypeError for missing bucket', - ); - }); - - it('should throw TypeError when oss.region is missing', () => { - assert.throws( - () => { - tracingService.configure({ - oss: { - accessKeyId: 'ak', - accessKeySecret: 'sk', - bucket: 'my-bucket', - region: '', - }, - }); - }, - TypeError, - 'should throw TypeError for missing region', - ); - }); - it('should throw TypeError when logService.url is missing', () => { assert.throws( () => { @@ -142,21 +45,6 @@ describe('test/Configure.test.ts', () => { 'should throw TypeError for missing logService url', ); }); - - it('should reset OSS client when reconfigured', () => { - tracingService.configure({ - oss: { - accessKeyId: 'ak1', - accessKeySecret: 'sk1', - bucket: 'bucket1', - region: 'region1', - }, - }); - - // Access private state to verify reset - assert.strictEqual((tracingService as any).ossInitialized, false); - assert.strictEqual((tracingService as any).ossClient, null); - }); }); describe('LangGraphTracer.configure()', () => { @@ -167,12 +55,6 @@ describe('test/Configure.test.ts', () => { tracer.configure({ agentName: 'MyAgent', - oss: { - accessKeyId: 'ak', - accessKeySecret: 'sk', - bucket: 'bucket', - region: 'region', - }, }); assert.strictEqual(tracer.agentName, 'MyAgent'); @@ -201,12 +83,6 @@ describe('test/Configure.test.ts', () => { tracer.configure({ agentName: 'MyClaude', - oss: { - accessKeyId: 'ak', - accessKeySecret: 'sk', - bucket: 'bucket', - region: 'region', - }, }); assert.strictEqual(tracer.agentName, 'MyClaude'); diff --git a/tegg/core/agent-tracing/test/TestUtils.ts b/tegg/core/agent-tracing/test/TestUtils.ts index b5e9ba943e..4e6e231f43 100644 --- a/tegg/core/agent-tracing/test/TestUtils.ts +++ b/tegg/core/agent-tracing/test/TestUtils.ts @@ -1,5 +1,6 @@ import type { Logger } from '@eggjs/tegg-types'; +import { IOssClient } from '../src/IOssClient.ts'; import { TracingService } from '../src/TracingService.ts'; export function createMockLogger(logs?: string[]): Logger { @@ -22,9 +23,16 @@ export function createMockBackgroundTaskHelper(): { run: (fn: () => Promise }; } +export function createMockOssClient(): IOssClient { + return { + put: async (_key: string, _content: string | Buffer) => {}, + } as IOssClient; +} + export function createMockTracingService(logs?: string[]): TracingService { const tracingService = new TracingService(); (tracingService as any).logger = createMockLogger(logs); (tracingService as any).backgroundTaskHelper = createMockBackgroundTaskHelper(); + (tracingService as any).ossClient = createMockOssClient(); return tracingService; } From 04b0c9ac511ba6d931aed82004edb0f20c83cf75 Mon Sep 17 00:00:00 2001 From: jerry Date: Thu, 5 Mar 2026 14:32:23 +0800 Subject: [PATCH 05/18] refactor(agent-tracing): replace logService config with ILogServiceClient IoC injection Remove hardcoded HTTP POST logic in syncLocalToLogService and replace with an abstract ILogServiceClient that users implement and register via Tegg IoC, consistent with the existing IOssClient injection pattern. Co-Authored-By: Claude Sonnet 4.6 --- .../agent-tracing/src/ILogServiceClient.ts | 31 +++++++++++++++ tegg/core/agent-tracing/src/TracingService.ts | 38 +++++-------------- tegg/core/agent-tracing/src/index.ts | 1 + tegg/core/agent-tracing/src/types.ts | 12 +----- .../core/agent-tracing/test/Configure.test.ts | 25 ------------ tegg/core/agent-tracing/test/TestUtils.ts | 10 +++++ 6 files changed, 53 insertions(+), 64 deletions(-) create mode 100644 tegg/core/agent-tracing/src/ILogServiceClient.ts diff --git a/tegg/core/agent-tracing/src/ILogServiceClient.ts b/tegg/core/agent-tracing/src/ILogServiceClient.ts new file mode 100644 index 0000000000..06dadbc5ed --- /dev/null +++ b/tegg/core/agent-tracing/src/ILogServiceClient.ts @@ -0,0 +1,31 @@ +/** + * Abstract log service client for dependency injection. + * + * To enable log service syncing in TracingService, implement this class in your application + * and register it with Tegg IoC. The implementation class MUST be named `LogServiceClient` + * (or use `@SingletonProto({ name: 'logServiceClient' })`) so the container can resolve it. + * + * @example + * ```typescript + * import { SingletonProto } from '@eggjs/core-decorator'; + * import { AccessLevel } from '@eggjs/tegg-types'; + * import { ILogServiceClient } from '@eggjs/agent-tracing'; + * + * // Class name must be LogServiceClient (registers as 'logServiceClient' in the IoC container) + * @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) + * export class LogServiceClient extends ILogServiceClient { + * async send(log: string): Promise { + * await fetch('https://log.example.com/api', { + * method: 'POST', + * headers: { 'content-type': 'application/json' }, + * body: JSON.stringify({ log }), + * }); + * } + * } + * ``` + * + * If no implementation is registered, log service syncing is silently skipped. + */ +export abstract class ILogServiceClient { + abstract send(log: string): Promise; +} diff --git a/tegg/core/agent-tracing/src/TracingService.ts b/tegg/core/agent-tracing/src/TracingService.ts index 02e32a6d94..cbfe6a629e 100644 --- a/tegg/core/agent-tracing/src/TracingService.ts +++ b/tegg/core/agent-tracing/src/TracingService.ts @@ -5,6 +5,7 @@ import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; import { getCustomLogger } from 'onelogger'; +import { ILogServiceClient } from './ILogServiceClient.ts'; import { IOssClient } from './IOssClient.ts'; import { type AgentTracingConfig, FIELDS_TO_OSS, type IResource, RunStatus } from './types.ts'; @@ -25,19 +26,11 @@ export class TracingService { @InjectOptional() private readonly ossClient: IOssClient; - private config: AgentTracingConfig = {}; + @InjectOptional() + private readonly logServiceClient: ILogServiceClient; - /** - * Configure logService credentials. - * Validates required fields. - */ - configure(config: AgentTracingConfig): void { - if (config.logService) { - if (!config.logService.url) { - throw new TypeError('[TracingService] logService config requires url'); - } - } - this.config = config; + configure(_config: AgentTracingConfig): void { + // Reserved for future configuration options } /** @@ -91,32 +84,19 @@ export class TracingService { } /** - * Sync local tracing logs to a log service endpoint. - * Configured via configure({ logService: { url, headers } }). - * Silently skips if logService is not configured. + * Sync local tracing logs to the injected ILogServiceClient implementation. + * Silently skips if no ILogServiceClient is registered. */ async syncLocalToLogService(log: string, agentName: string): Promise { - const logServiceConfig = this.config.logService; - if (!logServiceConfig?.url) { + if (!this.logServiceClient) { return; } - if (!agentName) { this.logger.warn('[TraceLogErr] syncLocalToLogService: agentName is empty'); return; } - try { - await fetch(logServiceConfig.url, { - method: 'POST', - headers: { - 'content-type': 'application/json', - ...logServiceConfig.headers, - }, - body: JSON.stringify({ - log: `[${agentName}]${log}`, - }), - }); + await this.logServiceClient.send(`[${agentName}]${log}`); } catch (e) { this.logger.warn('[TraceLogErr] syncLocalToLogService error:', e); } diff --git a/tegg/core/agent-tracing/src/index.ts b/tegg/core/agent-tracing/src/index.ts index 113db23233..737a85cf00 100644 --- a/tegg/core/agent-tracing/src/index.ts +++ b/tegg/core/agent-tracing/src/index.ts @@ -1,3 +1,4 @@ export * from './types.ts'; export { TracingService } from './TracingService.ts'; export { IOssClient } from './IOssClient.ts'; +export { ILogServiceClient } from './ILogServiceClient.ts'; diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts index 3e61a3dafc..3f1c95a39f 100644 --- a/tegg/core/agent-tracing/src/types.ts +++ b/tegg/core/agent-tracing/src/types.ts @@ -152,20 +152,14 @@ export interface OssConfig { endpoint?: string; } -export interface LogServiceConfig { - url: string; - headers?: Record; -} - /** Internal config used by TracingService */ export interface AgentTracingConfig { - logService?: LogServiceConfig; + // Reserved for future configuration options } /** User-facing config passed to tracer.configure() */ export interface TracerConfig { agentName?: string; - logService?: LogServiceConfig; } /** Apply user-facing TracerConfig to a tracer instance and its TracingService. */ @@ -177,9 +171,7 @@ export function applyTracerConfig( if (config.agentName !== undefined) { tracer.agentName = config.agentName; } - tracingService.configure({ - logService: config.logService, - }); + tracingService.configure({}); } export { FIELDS_TO_OSS }; diff --git a/tegg/core/agent-tracing/test/Configure.test.ts b/tegg/core/agent-tracing/test/Configure.test.ts index d64e01313d..865a3f5d2a 100644 --- a/tegg/core/agent-tracing/test/Configure.test.ts +++ b/tegg/core/agent-tracing/test/Configure.test.ts @@ -20,31 +20,6 @@ describe('test/Configure.test.ts', () => { tracingService.configure({}); }); }); - - it('should accept complete logService config', () => { - assert.doesNotThrow(() => { - tracingService.configure({ - logService: { - url: 'https://log.example.com/api', - headers: { Authorization: 'Bearer token' }, - }, - }); - }); - }); - - it('should throw TypeError when logService.url is missing', () => { - assert.throws( - () => { - tracingService.configure({ - logService: { - url: '', - }, - }); - }, - TypeError, - 'should throw TypeError for missing logService url', - ); - }); }); describe('LangGraphTracer.configure()', () => { diff --git a/tegg/core/agent-tracing/test/TestUtils.ts b/tegg/core/agent-tracing/test/TestUtils.ts index 4e6e231f43..038bac2aa4 100644 --- a/tegg/core/agent-tracing/test/TestUtils.ts +++ b/tegg/core/agent-tracing/test/TestUtils.ts @@ -1,5 +1,6 @@ import type { Logger } from '@eggjs/tegg-types'; +import { ILogServiceClient } from '../src/ILogServiceClient.ts'; import { IOssClient } from '../src/IOssClient.ts'; import { TracingService } from '../src/TracingService.ts'; @@ -29,10 +30,19 @@ export function createMockOssClient(): IOssClient { } as IOssClient; } +export function createMockLogServiceClient(logs?: string[]): ILogServiceClient { + return { + send: async (log: string) => { + logs?.push(log); + }, + } as ILogServiceClient; +} + export function createMockTracingService(logs?: string[]): TracingService { const tracingService = new TracingService(); (tracingService as any).logger = createMockLogger(logs); (tracingService as any).backgroundTaskHelper = createMockBackgroundTaskHelper(); (tracingService as any).ossClient = createMockOssClient(); + (tracingService as any).logServiceClient = createMockLogServiceClient(logs); return tracingService; } From 4a583e18af608fd7c15f73a9d0b21f7f6d475a5f Mon Sep 17 00:00:00 2001 From: jerry Date: Thu, 5 Mar 2026 15:13:21 +0800 Subject: [PATCH 06/18] fix(deps): sync pnpm-lock.yaml with agent-tracing package.json Update lockfile to reflect the current state of tegg/core/agent-tracing/package.json, fixing CI frozen-lockfile install failure. Co-Authored-By: Claude Sonnet 4.6 --- pnpm-lock.yaml | 208 +------------------------------------------------ 1 file changed, 2 insertions(+), 206 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c9ceb9c38..e71c0237d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2067,9 +2067,6 @@ importers: '@eggjs/tegg-types': specifier: workspace:* version: link:../types - ali-oss: - specifier: ^6.21.0 - version: 6.23.0 onelogger: specifier: 'catalog:' version: 1.0.1 @@ -2089,9 +2086,6 @@ importers: '@langchain/langgraph': specifier: ^0.2.74 version: 0.2.74(@langchain/core@1.1.29(openai@6.25.0(zod@4.3.6)))(zod-to-json-schema@3.25.1(zod@4.3.6)) - '@types/ali-oss': - specifier: ^6.16.0 - version: 6.23.3 '@types/node': specifier: 'catalog:' version: 24.10.2 @@ -5350,9 +5344,6 @@ packages: '@types/accepts@1.3.7': resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} - '@types/ali-oss@6.23.3': - resolution: {integrity: sha512-huSf6njkqhSeR37OMJTGMCyUg2d9Zp2AioefhYuUMeh0vNM+WybctrBor7bO/RcCbLCQI6sHn6NtPBIWALYVJg==} - '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -5804,10 +5795,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - address@1.2.2: - resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} - engines: {node: '>= 10.0.0'} - address@2.0.3: resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==} engines: {node: '>= 16.0.0'} @@ -5816,10 +5803,6 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agentkeepalive@3.5.3: - resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==} - engines: {node: '>= 4.0.0'} - agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -5852,10 +5835,6 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ali-oss@6.23.0: - resolution: {integrity: sha512-FipRmyd16Pr/tEey/YaaQ/24Pc3HEpLM9S1DRakEuXlSLXNIJnu1oJtHM53eVYpvW3dXapSjrip3xylZUTIZVQ==} - engines: {node: '>=8'} - ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -6037,9 +6016,6 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - bowser@1.9.4: - resolution: {integrity: sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==} - brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -6062,9 +6038,6 @@ packages: bug-versions@1.117.0: resolution: {integrity: sha512-c2F2PKelgC9zSy+AwdD6w/HQzdfofyZ7641jYkTxmIYxOgWDYPlVlEYa/puE/11K4pzQfxviI0loVyo1YuZVNQ==} - builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} @@ -6471,9 +6444,6 @@ packages: resolution: {integrity: sha512-5dVBvpBLBnPwSsYXqfybFyehMmC/EenKEcf23AhCTgTf48JFBbmJKqoZBsERDnjL0FyiVTYWdFsRfTLHxLyKdQ==} engines: {node: '>=6'} - dateformat@2.2.0: - resolution: {integrity: sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==} - dayjs@1.11.18: resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} @@ -6707,10 +6677,6 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - end-or-error@1.0.1: - resolution: {integrity: sha512-OclLMSug+k2A0JKuf494im25ANRBVW8qsjmwbgX7lQ8P82H21PQ1PWkoYwb9y5yMBS69BPlwtzdIFClo3+7kOQ==} - engines: {node: '>= 0.11.14'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -7055,9 +7021,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-ready@1.0.0: - resolution: {integrity: sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==} - get-ready@3.4.0: resolution: {integrity: sha512-D7N1WED4f5rQUveyl19GxfJupMeR+y106EVK6na/zI+w34FcTAShvetX0X2k61x+lJXCEiOnGHk0xro6YRyrjQ==} engines: {node: '>= 16.13.0'} @@ -7535,9 +7498,6 @@ packages: isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -7594,9 +7554,6 @@ packages: jose@6.1.3: resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} - js-base64@2.6.4: - resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} - js-beautify@1.15.4: resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} engines: {node: '>=14'} @@ -7661,9 +7618,6 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - jstoxml@2.2.9: - resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==} - junk@4.0.1: resolution: {integrity: sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==} engines: {node: '>=12.20'} @@ -8775,9 +8729,6 @@ packages: resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - promise-all-reject-late@1.0.1: resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} @@ -8878,9 +8829,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} deprecated: This package is no longer supported. Please use @npmcli/package-json instead. - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -9078,9 +9026,6 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -9102,9 +9047,6 @@ packages: sax@1.4.3: resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} - sdk-base@2.0.1: - resolution: {integrity: sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==} - sdk-base@3.6.0: resolution: {integrity: sha512-jxHUIrRLlAoRFRwiXKhOGjd6BeFWO/jz7tv+E7lbMSef6F9jzFN2Sv3hLW58oDDKscKaBGG6vQdkbXn7isE7fw==} @@ -9353,16 +9295,9 @@ packages: stream-combiner@0.2.2: resolution: {integrity: sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==} - stream-http@2.8.2: - resolution: {integrity: sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==} - stream-slice@0.1.2: resolution: {integrity: sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==} - stream-wormhole@1.1.0: - resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==} - engines: {node: '>=4.0.0'} - stream-wormhole@2.0.1: resolution: {integrity: sha512-B31SiV7tNxOGqvC367O1dVL4nWBgCT7xqtHtikvxUkrbxgvtUemYc6WyO8+Z5cLW8/HUvYmNkgZIjTXYLIDYVw==} engines: {node: '>=16.0.0'} @@ -9395,9 +9330,6 @@ packages: resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} engines: {node: '>=20'} - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -9536,9 +9468,6 @@ packages: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} - to-arraybuffer@1.0.1: - resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -9762,15 +9691,6 @@ packages: urijs@1.19.11: resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} - urllib@2.44.0: - resolution: {integrity: sha512-zRCJqdfYllRDA9bXUtx+vccyRqtJPKsw85f44zH7zPD28PIvjMqIgw9VwoTLV7xTBWZsbebUFVHU5ghQcWku2A==} - engines: {node: '>= 0.10.0'} - peerDependencies: - proxy-agent: ^5.0.0 - peerDependenciesMeta: - proxy-agent: - optional: true - urllib@3.27.3: resolution: {integrity: sha512-24ht/hHAkhWXbqSCM499B8gFPXaf4aFQJNqJH5pMsvfyo+aM3zKuQ/uiZ/mdz41Nnw6ItuiqMVMK5VDAlAHLlw==} engines: {node: '>= 14.19.3'} @@ -10000,23 +9920,11 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - xml2js@0.6.2: - resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} - engines: {node: '>=4.0.0'} - - xmlbuilder@11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - xss@1.0.15: resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} engines: {node: '>= 0.10.0'} hasBin: true - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -11410,8 +11318,6 @@ snapshots: dependencies: '@types/node': 24.10.2 - '@types/ali-oss@6.23.3': {} - '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -11880,8 +11786,6 @@ snapshots: acorn@8.15.0: {} - address@1.2.2: {} - address@2.0.3: {} agent-base@6.0.2: @@ -11890,10 +11794,6 @@ snapshots: transitivePeerDependencies: - supports-color - agentkeepalive@3.5.3: - dependencies: - humanize-ms: 1.2.1 - agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -11923,37 +11823,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ali-oss@6.23.0: - dependencies: - address: 1.2.2 - agentkeepalive: 3.5.3 - bowser: 1.9.4 - copy-to: 2.0.1 - dateformat: 2.2.0 - debug: 4.4.3(supports-color@8.1.1) - destroy: 1.2.0 - end-or-error: 1.0.1 - get-ready: 1.0.0 - humanize-ms: 1.2.1 - is-type-of: 1.4.0 - js-base64: 2.6.4 - jstoxml: 2.2.9 - lodash: 4.17.21 - merge-descriptors: 1.0.3 - mime: 2.6.0 - platform: 1.3.6 - pump: 3.0.3 - qs: 6.15.0 - sdk-base: 2.0.1 - stream-http: 2.8.2 - stream-wormhole: 1.1.0 - urllib: 2.44.0 - utility: 1.18.0 - xml2js: 0.6.2 - transitivePeerDependencies: - - proxy-agent - - supports-color - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -12131,8 +12000,6 @@ snapshots: boolbase@1.0.0: {} - bowser@1.9.4: {} - brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -12157,8 +12024,6 @@ snapshots: bug-versions@1.117.0: {} - builtin-status-codes@3.0.0: {} - builtins@1.0.3: {} busboy@1.6.0: @@ -12640,8 +12505,6 @@ snapshots: dargs@6.1.0: {} - dateformat@2.2.0: {} - dayjs@1.11.18: {} debounce@3.0.0: {} @@ -12862,8 +12725,6 @@ snapshots: dependencies: once: 1.4.0 - end-or-error@1.0.1: {} - entities@4.5.0: {} entities@6.0.1: {} @@ -13346,8 +13207,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-ready@1.0.0: {} - get-ready@3.4.0: {} get-stream@6.0.1: {} @@ -13822,8 +13681,6 @@ snapshots: isarray@0.0.1: {} - isarray@1.0.0: {} - isarray@2.0.5: {} isexe@2.0.0: {} @@ -13889,8 +13746,6 @@ snapshots: jose@6.1.3: {} - js-base64@2.6.4: {} - js-beautify@1.15.4: dependencies: config-chain: 1.1.13 @@ -13943,8 +13798,6 @@ snapshots: jsonparse@1.3.1: {} - jstoxml@2.2.9: {} - junk@4.0.1: {} just-diff-apply@5.5.0: {} @@ -15287,8 +15140,6 @@ snapshots: proc-log@3.0.0: {} - process-nextick-args@2.0.1: {} - promise-all-reject-late@1.0.1: {} promise-call-limit@1.0.2: {} @@ -15385,16 +15236,6 @@ snapshots: normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -15616,8 +15457,6 @@ snapshots: dependencies: mri: 1.2.0 - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} safe-regex-test@1.1.0: @@ -15639,11 +15478,8 @@ snapshots: '@parcel/watcher': 2.5.1 optional: true - sax@1.4.3: {} - - sdk-base@2.0.1: - dependencies: - get-ready: 1.0.0 + sax@1.4.3: + optional: true sdk-base@3.6.0: dependencies: @@ -15948,18 +15784,8 @@ snapshots: duplexer: 0.1.2 through: 2.3.8 - stream-http@2.8.2: - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 2.3.8 - to-arraybuffer: 1.0.1 - xtend: 4.0.2 - stream-slice@0.1.2: {} - stream-wormhole@1.1.0: {} - stream-wormhole@2.0.1: {} streamsearch@1.1.0: {} @@ -15995,10 +15821,6 @@ snapshots: get-east-asian-width: 1.4.0 strip-ansi: 7.1.2 - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -16141,8 +15963,6 @@ snapshots: tmp@0.2.5: {} - to-arraybuffer@1.0.1: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -16375,21 +16195,6 @@ snapshots: urijs@1.19.11: {} - urllib@2.44.0: - dependencies: - any-promise: 1.3.0 - content-type: 1.0.5 - default-user-agent: 1.0.0 - digest-header: 1.1.0 - ee-first: 1.1.1 - formstream: 1.5.2 - humanize-ms: 1.2.1 - iconv-lite: 0.6.3 - pump: 3.0.3 - qs: 6.15.0 - statuses: 1.5.0 - utility: 1.18.0 - urllib@3.27.3: dependencies: default-user-agent: 1.0.0 @@ -16703,20 +16508,11 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - xml2js@0.6.2: - dependencies: - sax: 1.4.3 - xmlbuilder: 11.0.1 - - xmlbuilder@11.0.1: {} - xss@1.0.15: dependencies: commander: 2.20.3 cssfilter: 0.0.10 - xtend@4.0.2: {} - y18n@4.0.3: {} y18n@5.0.8: {} From d455b1c08b5a802cb17579da3db04a30f7a64595 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 6 Mar 2026 10:39:04 +0800 Subject: [PATCH 07/18] refactor(agent-tracing): apply code review fixes - Use ES private fields (#) for TraceSession instead of private keyword - Remove unused OssConfig interface from types.ts - Inline createLLMOutput into extractTokenUsage call site - Switch test assertions to node:assert/strict - Refactor test utils: replace log-string parsing with direct Run capture (createCapturingTracingService replaces createMockTracingService) Co-Authored-By: Claude Sonnet 4.6 --- .../agent-tracing/src/ClaudeAgentTracer.ts | 137 ++++++------- tegg/core/agent-tracing/src/types.ts | 8 - .../test/ClaudeAgentTracer.test.ts | 122 ++++------- .../core/agent-tracing/test/Configure.test.ts | 14 +- .../test/LangGraphTracer.test.ts | 194 ++++++------------ tegg/core/agent-tracing/test/TestUtils.ts | 51 +++-- 6 files changed, 202 insertions(+), 324 deletions(-) diff --git a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts index e0a7ffd0a2..5fa31b1f77 100644 --- a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts +++ b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts @@ -22,19 +22,19 @@ import { * Allows processing messages one-by-one and logging them immediately. */ export class TraceSession { - private traceId: string; - private rootRun: Run | null = null; - private rootRunId: string; - private startTime: number; - private executionOrder = 2; // Start at 2, root is 1 - private pendingToolUses = new Map(); - private tracer: ClaudeAgentTracer; + #traceId: string; + #rootRun: Run | null = null; + #rootRunId: string; + #startTime: number; + #executionOrder = 2; // Start at 2, root is 1 + #pendingToolUses = new Map(); + #tracer: ClaudeAgentTracer; constructor(tracer: ClaudeAgentTracer, sessionId?: string) { - this.tracer = tracer; - this.traceId = sessionId || randomUUID(); - this.rootRunId = randomUUID(); - this.startTime = Date.now(); + this.#tracer = tracer; + this.#traceId = sessionId || randomUUID(); + this.#rootRunId = randomUUID(); + this.#startTime = Date.now(); } /** @@ -43,32 +43,32 @@ export class TraceSession { */ async processMessage(message: SDKMessage): Promise { try { - const converted = this.tracer.convertSDKMessage(message); + const converted = this.#tracer.convertSDKMessage(message); if (!converted) return; if (converted.type === 'system' && converted.subtype === 'init') { - await this.handleInit(converted); + this.handleInit(converted); } else if (converted.type === 'assistant') { - await this.handleAssistant(converted); + this.handleAssistant(converted); } else if (converted.type === 'user') { - await this.handleUser(converted); + this.handleUser(converted); } else if (converted.type === 'result') { - await this.handleResult(converted); + this.handleResult(converted); } } catch (e) { - this.tracer.logger.warn('[ClaudeAgentTracer] processMessage error:', e); + this.#tracer.logger.warn('[ClaudeAgentTracer] processMessage error:', e); } } private handleInit(message: ClaudeMessage): void { - this.traceId = message.session_id || this.traceId; - this.rootRun = this.tracer.createRootRunInternal(message, this.startTime, this.traceId, this.rootRunId); - this.tracer.logTrace(this.rootRun, RunStatus.START); + this.#traceId = message.session_id || this.#traceId; + this.#rootRun = this.#tracer.createRootRunInternal(message, this.#startTime, this.#traceId, this.#rootRunId); + this.#tracer.logTrace(this.#rootRun, RunStatus.START); } private handleAssistant(message: ClaudeMessage): void { - if (!this.rootRun) { - this.tracer.logger.warn('[ClaudeAgentTracer] Received assistant message before init'); + if (!this.#rootRun) { + this.#tracer.logger.warn('[ClaudeAgentTracer] Received assistant message before init'); return; } @@ -78,44 +78,44 @@ export class TraceSession { if (hasToolUse) { // Create LLM run that initiated tool calls - const llmRun = this.tracer.createLLMRunInternal( + const llmRun = this.#tracer.createLLMRunInternal( message, - this.rootRunId, - this.traceId, - this.executionOrder++, - this.startTime, + this.#rootRunId, + this.#traceId, + this.#executionOrder++, + this.#startTime, true, ); - this.rootRun.child_runs.push(llmRun); - this.tracer.logTrace(llmRun, RunStatus.END); + this.#rootRun.child_runs.push(llmRun); + this.#tracer.logTrace(llmRun, RunStatus.END); // Create tool runs (will be completed when tool_result arrives) for (const block of content) { if (block.type === 'tool_use') { - const toolRun = this.tracer.createToolRunStartInternal( + const toolRun = this.#tracer.createToolRunStartInternal( block, - this.rootRunId, - this.traceId, - this.executionOrder++, - this.startTime, + this.#rootRunId, + this.#traceId, + this.#executionOrder++, + this.#startTime, ); - this.rootRun.child_runs.push(toolRun); - this.pendingToolUses.set(block.id, toolRun); - this.tracer.logTrace(toolRun, RunStatus.START); + this.#rootRun.child_runs.push(toolRun); + this.#pendingToolUses.set(block.id, toolRun); + this.#tracer.logTrace(toolRun, RunStatus.START); } } } else if (hasText) { // Text-only response - const llmRun = this.tracer.createLLMRunInternal( + const llmRun = this.#tracer.createLLMRunInternal( message, - this.rootRunId, - this.traceId, - this.executionOrder++, - this.startTime, + this.#rootRunId, + this.#traceId, + this.#executionOrder++, + this.#startTime, false, ); - this.rootRun.child_runs.push(llmRun); - this.tracer.logTrace(llmRun, RunStatus.END); + this.#rootRun.child_runs.push(llmRun); + this.#tracer.logTrace(llmRun, RunStatus.END); } } @@ -124,60 +124,60 @@ export class TraceSession { for (const block of message.message.content) { if (block.type === 'tool_result') { - const toolRun = this.pendingToolUses.get(block.tool_use_id); + const toolRun = this.#pendingToolUses.get(block.tool_use_id); if (toolRun) { - this.tracer.completeToolRunInternal(toolRun, block, this.startTime); + this.#tracer.completeToolRunInternal(toolRun, block, this.#startTime); const status = block.is_error ? RunStatus.ERROR : RunStatus.END; - this.tracer.logTrace(toolRun, status); - this.pendingToolUses.delete(block.tool_use_id); + this.#tracer.logTrace(toolRun, status); + this.#pendingToolUses.delete(block.tool_use_id); } } } } private handleResult(message: ClaudeMessage): void { - if (!this.rootRun) { - this.tracer.logger.warn('[ClaudeAgentTracer] Received result message before init'); + if (!this.#rootRun) { + this.#tracer.logger.warn('[ClaudeAgentTracer] Received result message before init'); return; } // Complete any pending tool runs - for (const [toolUseId, toolRun] of this.pendingToolUses) { - this.tracer.logger.warn(`[ClaudeAgentTracer] Tool run ${toolUseId} did not receive result`); - toolRun.end_time = this.startTime; - this.tracer.logTrace(toolRun, RunStatus.ERROR); + for (const [toolUseId, toolRun] of this.#pendingToolUses) { + this.#tracer.logger.warn(`[ClaudeAgentTracer] Tool run ${toolUseId} did not receive result`); + toolRun.end_time = this.#startTime; + this.#tracer.logTrace(toolRun, RunStatus.ERROR); } - this.pendingToolUses.clear(); + this.#pendingToolUses.clear(); // Update and log root run end - this.rootRun.end_time = this.startTime + (message.duration_ms || 0); - this.rootRun.outputs = { + this.#rootRun.end_time = this.#startTime + (message.duration_ms || 0); + this.#rootRun.outputs = { result: message.result, is_error: message.is_error, num_turns: message.num_turns, }; if (message.usage || message.modelUsage) { - const cost = this.tracer.createRunCostInternal(message); - if (this.rootRun.outputs) { - (this.rootRun.outputs as any).llmOutput = cost; + const cost = this.#tracer.createRunCostInternal(message); + if (this.#rootRun.outputs) { + (this.#rootRun.outputs as any).llmOutput = cost; } } if (message.is_error) { - this.rootRun.error = message.result; + this.#rootRun.error = message.result; } - this.rootRun.child_execution_order = this.executionOrder - 1; + this.#rootRun.child_execution_order = this.#executionOrder - 1; const status = message.is_error ? RunStatus.ERROR : RunStatus.END; - this.tracer.logTrace(this.rootRun, status); + this.#tracer.logTrace(this.#rootRun, status); } /** * Get current trace ID */ getTraceId(): string { - return this.traceId; + return this.#traceId; } } @@ -382,7 +382,7 @@ export class ClaudeAgentTracer { } if (msg.message?.usage) { - outputs.llmOutput = this.createLLMOutput(msg.message.usage); + outputs.llmOutput = this.extractTokenUsage(msg.message.usage); } return { @@ -487,13 +487,6 @@ export class ClaudeAgentTracer { return result; } - /** - * Create LLM output from usage - */ - private createLLMOutput(usage: ClaudeTokenUsage): IRunCost { - return this.extractTokenUsage(usage); - } - /** * @internal * Create run cost from result message (used by TraceSession) diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts index 3f1c95a39f..df786bb18a 100644 --- a/tegg/core/agent-tracing/src/types.ts +++ b/tegg/core/agent-tracing/src/types.ts @@ -144,14 +144,6 @@ export const RunStatus = { } as const; export type RunStatus = (typeof RunStatus)[keyof typeof RunStatus]; -export interface OssConfig { - accessKeyId: string; - accessKeySecret: string; - bucket: string; - region: string; - endpoint?: string; -} - /** Internal config used by TracingService */ export interface AgentTracingConfig { // Reserved for future configuration options diff --git a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts index 4a1fb55688..fb149614e5 100644 --- a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts +++ b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts @@ -1,59 +1,25 @@ -import assert from 'node:assert'; +import assert from 'node:assert/strict'; import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; import { describe, it } from 'vitest'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; -import { createMockLogger, createMockTracingService } from './TestUtils.ts'; - -// ---------- Tracing log helpers ---------- - -function extractRunFromLog(log: string): any { - const match = log.match(/,run=({.*})$/); - return match ? JSON.parse(match[1]) : null; -} - -function extractStatus(log: string): string | null { - const match = log.match(/status=(\w+)/); - return match ? match[1] : null; -} - -function extractRunType(log: string): string | null { - const match = log.match(/type=(root_run|child_run)/); - return match ? match[1] : null; -} +import { RunStatus } from '../src/types.ts'; +import { createMockLogger, createCapturingTracingService } from './TestUtils.ts'; // ---------- Shared setup ---------- function createTestEnv() { - const logs: string[] = []; process.env.FAAS_ENV = 'dev'; - const mockLogger = createMockLogger(logs); - const tracingService = createMockTracingService(logs); + const { tracingService, capturedRuns } = createCapturingTracingService(); + const mockLogger = createMockLogger(); const claudeTracer = new ClaudeAgentTracer(); (claudeTracer as any).logger = mockLogger; (claudeTracer as any).tracingService = tracingService; - function getTracingLogs(): string[] { - return logs.filter((log) => log.includes('[agent_run][ClaudeAgentTracer]')); - } - - function parseAllRuns(): Array<{ run: any; status: string; logType: string }> { - const entries: Array<{ run: any; status: string; logType: string }> = []; - for (const log of getTracingLogs()) { - const run = extractRunFromLog(log); - const status = extractStatus(log); - const logType = extractRunType(log); - if (run && status && logType) { - entries.push({ run, status, logType }); - } - } - return entries; - } - - return { logs, claudeTracer, parseAllRuns, getTracingLogs }; + return { claudeTracer, capturedRuns }; } // ---------- Mock data factories ---------- @@ -194,7 +160,7 @@ function createMockStreamEvent(): SDKMessage { describe('test/ClaudeAgentTracer.test.ts', () => { describe('Streaming mode + tool use', () => { it('should trace tool execution with session.processMessage', async () => { - const { claudeTracer, parseAllRuns } = createTestEnv(); + const { claudeTracer, capturedRuns } = createTestEnv(); const session = claudeTracer.createSession(); // Feed messages one-by-one, including noise messages that should be filtered @@ -211,58 +177,56 @@ describe('test/ClaudeAgentTracer.test.ts', () => { await session.processMessage(msg); } - const entries = parseAllRuns(); - // Root run start + end - const rootStart = entries.find((e) => e.logType === 'root_run' && e.status === 'start'); + const rootStart = capturedRuns.find((e) => !e.run.parent_run_id && e.status === RunStatus.START); assert(rootStart, 'Should have root_run start'); - assert.strictEqual(rootStart!.run.run_type, 'chain'); + assert.strictEqual(rootStart.run.run_type, 'chain'); - const rootEnd = entries.find((e) => e.logType === 'root_run' && e.status === 'end'); + const rootEnd = capturedRuns.find((e) => !e.run.parent_run_id && e.status === RunStatus.END); assert(rootEnd, 'Should have root_run end'); // LLM child run - const llmRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'llm'); + const llmRuns = capturedRuns.filter((e) => !!e.run.parent_run_id && e.run.run_type === 'llm'); assert(llmRuns.length >= 1, `Should have >= 1 LLM run, got ${llmRuns.length}`); // Tool child run start + end - const toolRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'tool'); + const toolRuns = capturedRuns.filter((e) => !!e.run.parent_run_id && e.run.run_type === 'tool'); assert(toolRuns.length >= 2, `Should have >= 2 tool run entries (start+end), got ${toolRuns.length}`); - const toolStart = toolRuns.find((e) => e.status === 'start'); + const toolStart = toolRuns.find((e) => e.status === RunStatus.START); assert(toolStart, 'Should have tool start'); - assert.strictEqual(toolStart!.run.name, 'Bash'); + assert.strictEqual(toolStart.run.name, 'Bash'); - const toolEnd = toolRuns.find((e) => e.status === 'end'); + const toolEnd = toolRuns.find((e) => e.status === RunStatus.END); assert(toolEnd, 'Should have tool end'); // All runs share the same trace_id = session_id - const traceIds = new Set(entries.map((e) => e.run.trace_id)); + const traceIds = new Set(capturedRuns.map((e) => e.run.trace_id)); assert.strictEqual(traceIds.size, 1, `All runs should share one trace_id, got ${traceIds.size}`); assert.strictEqual([...traceIds][0], 'test-session-001', 'trace_id should match session_id'); // Child runs reference root run as parent - const childEntries = entries.filter((e) => e.logType === 'child_run'); + const childEntries = capturedRuns.filter((e) => !!e.run.parent_run_id); for (const child of childEntries) { assert.strictEqual( child.run.parent_run_id, - rootStart!.run.id, + rootStart.run.id, `Child run ${child.run.name} should reference root as parent`, ); } // Cost data on root end - const cost = rootEnd!.run.cost; - assert(cost, 'Root end should have cost'); - assert.strictEqual(cost.promptTokens, 100); - assert.strictEqual(cost.completionTokens, 50); - assert.strictEqual(cost.totalCost, 0.003); + const llmOutput = (rootEnd.run.outputs as any)?.llmOutput; + assert(llmOutput, 'Root end should have llmOutput'); + assert.strictEqual(llmOutput.promptTokens, 100); + assert.strictEqual(llmOutput.completionTokens, 50); + assert.strictEqual(llmOutput.totalCost, 0.003); }); }); describe('Batch mode + text-only', () => { it('should trace a text-only response via processMessages', async () => { - const { claudeTracer, parseAllRuns } = createTestEnv(); + const { claudeTracer, capturedRuns } = createTestEnv(); const messages: SDKMessage[] = [ createMockInit(), @@ -280,41 +244,40 @@ describe('test/ClaudeAgentTracer.test.ts', () => { await claudeTracer.processMessages(messages); - const entries = parseAllRuns(); - assert(entries.length > 0, 'Should have tracing entries'); + assert(capturedRuns.length > 0, 'Should have tracing entries'); // Root run start + end - const rootEntries = entries.filter((e) => e.logType === 'root_run'); + const rootEntries = capturedRuns.filter((e) => !e.run.parent_run_id); assert(rootEntries.length >= 2, `Should have root start + end, got ${rootEntries.length}`); - const rootEnd = rootEntries.find((e) => e.status === 'end'); + const rootEnd = rootEntries.find((e) => e.status === RunStatus.END); assert(rootEnd, 'Should have root end'); // LLM child run with text content - const llmRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'llm'); + const llmRuns = capturedRuns.filter((e) => !!e.run.parent_run_id && e.run.run_type === 'llm'); assert(llmRuns.length >= 1, `Should have >= 1 LLM run, got ${llmRuns.length}`); // No tool runs - const toolRuns = entries.filter((e) => e.logType === 'child_run' && e.run.run_type === 'tool'); + const toolRuns = capturedRuns.filter((e) => !!e.run.parent_run_id && e.run.run_type === 'tool'); assert.strictEqual(toolRuns.length, 0, 'Should have no tool runs for text-only'); // Cost and token counts - const cost = rootEnd!.run.cost; - assert(cost, 'Should have cost'); - assert.strictEqual(cost.promptTokens, 80); - assert.strictEqual(cost.completionTokens, 30); - assert.strictEqual(cost.totalTokens, 110); - assert.strictEqual(cost.totalCost, 0.002); + const llmOutput = (rootEnd.run.outputs as any)?.llmOutput; + assert(llmOutput, 'Should have llmOutput'); + assert.strictEqual(llmOutput.promptTokens, 80); + assert.strictEqual(llmOutput.completionTokens, 30); + assert.strictEqual(llmOutput.totalTokens, 110); + assert.strictEqual(llmOutput.totalCost, 0.002); // trace_id consistency - const traceIds = new Set(entries.map((e) => e.run.trace_id)); + const traceIds = new Set(capturedRuns.map((e) => e.run.trace_id)); assert.strictEqual(traceIds.size, 1, 'All runs should share one trace_id'); }); }); describe('Error scenario', () => { it('should trace an error result with ERROR status', async () => { - const { claudeTracer, parseAllRuns } = createTestEnv(); + const { claudeTracer, capturedRuns } = createTestEnv(); const session = claudeTracer.createSession(); const messages: SDKMessage[] = [ @@ -347,15 +310,10 @@ describe('test/ClaudeAgentTracer.test.ts', () => { await session.processMessage(msg); } - const entries = parseAllRuns(); - // Root run should end with ERROR status - const rootEnd = entries.find((e) => e.logType === 'root_run' && e.status === 'error'); - assert(rootEnd, 'Should have root_run with error status'); - - // The root run should have error field populated - // (error is extracted from result message via convertSDKMessage) - assert(rootEnd!.run, 'Root end run should exist'); + const rootError = capturedRuns.find((e) => !e.run.parent_run_id && e.status === RunStatus.ERROR); + assert(rootError, 'Should have root_run with error status'); + assert(rootError.run, 'Root error run should exist'); }); }); }); diff --git a/tegg/core/agent-tracing/test/Configure.test.ts b/tegg/core/agent-tracing/test/Configure.test.ts index 865a3f5d2a..5c63ad750e 100644 --- a/tegg/core/agent-tracing/test/Configure.test.ts +++ b/tegg/core/agent-tracing/test/Configure.test.ts @@ -1,18 +1,18 @@ -import assert from 'node:assert'; +import assert from 'node:assert/strict'; import { describe, it, beforeEach } from 'vitest'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; import { TracingService } from '../src/TracingService.ts'; -import { createMockLogger, createMockTracingService } from './TestUtils.ts'; +import { createMockLogger, createCapturingTracingService } from './TestUtils.ts'; describe('test/Configure.test.ts', () => { describe('TracingService.configure()', () => { let tracingService: TracingService; beforeEach(() => { - tracingService = createMockTracingService(); + tracingService = createCapturingTracingService().tracingService; }); it('should accept empty config', () => { @@ -24,7 +24,7 @@ describe('test/Configure.test.ts', () => { describe('LangGraphTracer.configure()', () => { it('should set agentName and delegate to TracingService', () => { - const tracingService = createMockTracingService(); + const { tracingService } = createCapturingTracingService(); const tracer = new LangGraphTracer(); (tracer as any).tracingService = tracingService; @@ -37,7 +37,7 @@ describe('test/Configure.test.ts', () => { }); it('should not change agentName when not provided', () => { - const tracingService = createMockTracingService(); + const { tracingService } = createCapturingTracingService(); const tracer = new LangGraphTracer(); (tracer as any).tracingService = tracingService; tracer.agentName = 'existing'; @@ -50,7 +50,7 @@ describe('test/Configure.test.ts', () => { describe('ClaudeAgentTracer.configure()', () => { it('should set agentName and delegate to TracingService', () => { - const tracingService = createMockTracingService(); + const { tracingService } = createCapturingTracingService(); const mockLogger = createMockLogger(); const tracer = new ClaudeAgentTracer(); (tracer as any).logger = mockLogger; @@ -65,7 +65,7 @@ describe('test/Configure.test.ts', () => { }); it('should not change agentName when not provided', () => { - const tracingService = createMockTracingService(); + const { tracingService } = createCapturingTracingService(); const mockLogger = createMockLogger(); const tracer = new ClaudeAgentTracer(); (tracer as any).logger = mockLogger; diff --git a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts index 6e84362eba..0331c183eb 100644 --- a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts +++ b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts @@ -1,23 +1,15 @@ -import assert from 'node:assert'; +import assert from 'node:assert/strict'; import { FakeLLM } from '@langchain/core/utils/testing'; import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; import { describe, it, beforeEach } from 'vitest'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; -import { createMockTracingService } from './TestUtils.ts'; +import { RunStatus } from '../src/types.ts'; +import { type CapturedEntry, createCapturingTracingService } from './TestUtils.ts'; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); -/** - * Extract the run JSON object from a tracing log line. - * Log format: "[agent_run][...]:traceId=...,run={...JSON...}" - */ -function extractRunFromLog(log: string): any { - const match = log.match(/,run=({.*})$/); - return match ? JSON.parse(match[1]) : null; -} - /** Shared state schema for test graphs */ const GraphState = Annotation.Root({ query: Annotation, @@ -26,23 +18,18 @@ const GraphState = Annotation.Root({ describe('test/LangGraphTracer.test.ts', () => { let tracer: LangGraphTracer; - let logs: string[] = []; + let capturedRuns: CapturedEntry[]; beforeEach(() => { - logs = []; process.env.FAAS_ENV = 'dev'; - const tracingService = createMockTracingService(logs); + const capturing = createCapturingTracingService(); + capturedRuns = capturing.capturedRuns; tracer = new LangGraphTracer(); - (tracer as any).tracingService = tracingService; + (tracer as any).tracingService = capturing.tracingService; }); - /** Helper: get only [agent_run] tracing logs */ - function getTracingLogs(): string[] { - return logs.filter((log) => log.includes('[agent_run]')); - } - describe('Single-node StateGraph triggers chain lifecycle hooks', () => { it('should trigger onChainStart and onChainEnd via graph.invoke', async () => { const graph = new StateGraph(GraphState) @@ -57,19 +44,15 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); - const startLogs = tracingLogs.filter((log) => log.includes('status=start')); - const endLogs = tracingLogs.filter((log) => log.includes('status=end')); + const startEntries = capturedRuns.filter((e) => e.status === RunStatus.START); + const endEntries = capturedRuns.filter((e) => e.status === RunStatus.END); - assert(startLogs.length >= 1, `Should have at least one start log, got ${startLogs.length}`); - assert(endLogs.length >= 1, `Should have at least one end log, got ${endLogs.length}`); + assert(startEntries.length >= 1, `Should have at least one start entry, got ${startEntries.length}`); + assert(endEntries.length >= 1, `Should have at least one end entry, got ${endEntries.length}`); - // Parse run data and verify run_type is chain (StateGraph nodes are chain runs) - const chainStartLog = startLogs.find((log) => { - const run = extractRunFromLog(log); - return run && run.run_type === 'chain'; - }); - assert(chainStartLog, 'Should have a chain start log with run_type=chain'); + // Verify a chain run is present with run_type=chain + const chainStart = startEntries.find((e) => e.run.run_type === 'chain'); + assert(chainStart, 'Should have a chain start entry with run_type=chain'); }); it('should produce Run with valid id, trace_id, and run_type fields', async () => { @@ -85,15 +68,12 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); - assert(tracingLogs.length > 0, 'Should have tracing logs'); + assert(capturedRuns.length > 0, 'Should have captured runs'); - for (const log of tracingLogs) { - const run = extractRunFromLog(log); - if (!run) continue; - assert(run.id, 'Run should have an id'); - assert(run.trace_id, 'Run should have a trace_id'); - assert(run.run_type, 'Run should have a run_type'); + for (const entry of capturedRuns) { + assert(entry.run.id, 'Run should have an id'); + assert(entry.run.trace_id, 'Run should have a trace_id'); + assert(entry.run.run_type, 'Run should have a run_type'); } }); }); @@ -116,34 +96,25 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); - - // Collect all runs by parsing logs - const runs: Array<{ run: any; status: string }> = []; - for (const log of tracingLogs) { - const statusMatch = log.match(/status=(\w+)/); - const run = extractRunFromLog(log); - if (run && statusMatch) { - runs.push({ run, status: statusMatch[1] }); - } - } - - const chainRuns = runs.filter((r) => r.run.run_type === 'chain'); - assert(chainRuns.length >= 2, `Should have at least 2 chain runs (root + child nodes), got ${chainRuns.length}`); + const chainEntries = capturedRuns.filter((e) => e.run.run_type === 'chain'); + assert( + chainEntries.length >= 2, + `Should have at least 2 chain runs (root + child nodes), got ${chainEntries.length}`, + ); // The graph root run should have no parent_run_id - const rootRun = chainRuns.find((r) => !r.run.parent_run_id); + const rootRun = chainEntries.find((e) => !e.run.parent_run_id); assert(rootRun, 'Should have a root chain run (no parent_run_id)'); // Child node runs should have parent_run_id - const childRuns = chainRuns.filter((r) => r.run.parent_run_id); + const childRuns = chainEntries.filter((e) => e.run.parent_run_id); assert(childRuns.length >= 1, 'Should have at least one child chain run with parent_run_id'); - // Verify the log prefix correctly marks root vs child - const rootLogs = tracingLogs.filter((log) => log.includes('type=root_run')); - const childLogs = tracingLogs.filter((log) => log.includes('type=child_run')); - assert(rootLogs.length >= 1, 'Should have root_run type logs'); - assert(childLogs.length >= 1, 'Should have child_run type logs'); + // Verify root vs child distinction + const rootEntries = capturedRuns.filter((e) => !e.run.parent_run_id); + const childEntries = capturedRuns.filter((e) => !!e.run.parent_run_id); + assert(rootEntries.length >= 1, 'Should have root run entries'); + assert(childEntries.length >= 1, 'Should have child run entries'); }); it('should share the same trace_id across all runs in a graph invocation', async () => { @@ -163,17 +134,12 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); - const traceIds = new Set(); - - for (const log of tracingLogs) { - const run = extractRunFromLog(log); - if (run?.trace_id) { - traceIds.add(run.trace_id); - } - } - - assert(traceIds.size === 1, `All runs should share the same trace_id, got ${traceIds.size} distinct trace_ids`); + const traceIds = new Set(capturedRuns.map((e) => e.run.trace_id).filter(Boolean)); + assert.strictEqual( + traceIds.size, + 1, + `All runs should share the same trace_id, got ${traceIds.size} distinct trace_ids`, + ); }); }); @@ -194,29 +160,18 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); - - const runs: Array<{ run: any; status: string }> = []; - for (const log of tracingLogs) { - const statusMatch = log.match(/status=(\w+)/); - const run = extractRunFromLog(log); - if (run && statusMatch) { - runs.push({ run, status: statusMatch[1] }); - } - } - // Should have chain runs from the graph itself - const chainRuns = runs.filter((r) => r.run.run_type === 'chain'); - assert(chainRuns.length >= 1, `Should have at least one chain run, got ${chainRuns.length}`); + const chainEntries = capturedRuns.filter((e) => e.run.run_type === 'chain'); + assert(chainEntries.length >= 1, `Should have at least one chain run, got ${chainEntries.length}`); // Should have LLM runs from the FakeLLM invocation inside the node - const llmRuns = runs.filter((r) => r.run.run_type === 'llm'); - assert(llmRuns.length >= 2, `Should have at least 2 LLM logs (start + end), got ${llmRuns.length}`); + const llmEntries = capturedRuns.filter((e) => e.run.run_type === 'llm'); + assert(llmEntries.length >= 2, `Should have at least 2 LLM entries (start + end), got ${llmEntries.length}`); }); }); describe('StateGraph node error triggers onChainError', () => { - it('should trigger error hook and include error status in log', async () => { + it('should trigger error hook and include error status in captured runs', async () => { const graph = new StateGraph(GraphState) .addNode('fail_node', () => { throw new Error('Node execution failed'); @@ -233,20 +188,17 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); + // Should have a start entry + const startEntries = capturedRuns.filter((e) => e.status === RunStatus.START); + assert(startEntries.length >= 1, 'Should have at least one start entry before the error'); - // Should have a start log - const startLogs = tracingLogs.filter((log) => log.includes('status=start')); - assert(startLogs.length >= 1, 'Should have at least one start log before the error'); + // Should have an error entry + const errorEntries = capturedRuns.filter((e) => e.status === RunStatus.ERROR); + assert(errorEntries.length >= 1, `Should have at least one error entry, got ${errorEntries.length}`); - // Should have an error log - const errorLogs = tracingLogs.filter((log) => log.includes('status=error')); - assert(errorLogs.length >= 1, `Should have at least one error log, got ${errorLogs.length}`); - - // Verify the error run has the correct error field - const errorRun = extractRunFromLog(errorLogs[0]); - assert(errorRun, 'Should be able to parse error run from log'); - assert(errorRun.error, 'Error run should have error field set'); + // Verify the error run has the error field set + assert(errorEntries[0].run, 'Error run should exist'); + assert(errorEntries[0].run.error, 'Error run should have error field set'); }); }); @@ -264,26 +216,20 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const tracingLogs = getTracingLogs(); - assert(tracingLogs.length > 0, 'Should have tracing logs'); - - for (const log of tracingLogs) { - const run = extractRunFromLog(log); - if (!run) continue; + assert(capturedRuns.length > 0, 'Should have captured runs'); - // Core fields must be present - assert(typeof run.id === 'string' && run.id.length > 0, 'Run must have non-empty id'); - assert(typeof run.trace_id === 'string' && run.trace_id.length > 0, 'Run must have non-empty trace_id'); - assert(typeof run.run_type === 'string', 'Run must have run_type'); - assert(typeof run.name === 'string', 'Run must have name'); - - // Log prefix must contain traceId and run_id - assert(log.includes(`traceId=${run.trace_id}`), 'Log prefix must include traceId'); - assert(log.includes(`run_id=${run.id}`), 'Log prefix must include run_id'); + for (const entry of capturedRuns) { + assert(typeof entry.run.id === 'string' && entry.run.id.length > 0, 'Run must have non-empty id'); + assert( + typeof entry.run.trace_id === 'string' && entry.run.trace_id.length > 0, + 'Run must have non-empty trace_id', + ); + assert(typeof entry.run.run_type === 'string', 'Run must have run_type'); + assert(typeof entry.run.name === 'string', 'Run must have name'); } }); - it('should produce end runs with non-null outputs or OSS reference', async () => { + it('should produce end runs with outputs present', async () => { const graph = new StateGraph(GraphState) .addNode('output_node', (state: typeof GraphState.State) => { return { result: `output for ${state.query}` }; @@ -296,18 +242,12 @@ describe('test/LangGraphTracer.test.ts', () => { await sleep(500); - const endLogs = getTracingLogs().filter((log) => log.includes('status=end')); - assert(endLogs.length >= 1, 'Should have end logs'); - - for (const log of endLogs) { - const run = extractRunFromLog(log); - if (!run) continue; - // Outputs should be uploaded to OSS, so the field should be an IResource - if (run.outputs) { - assert( - run.outputs.key || typeof run.outputs === 'object', - 'End run outputs should be present (as OSS resource or object)', - ); + const endEntries = capturedRuns.filter((e) => e.status === RunStatus.END); + assert(endEntries.length >= 1, 'Should have end entries'); + + for (const entry of endEntries) { + if (entry.run.outputs) { + assert(typeof entry.run.outputs === 'object', 'End run outputs should be an object'); } } }); diff --git a/tegg/core/agent-tracing/test/TestUtils.ts b/tegg/core/agent-tracing/test/TestUtils.ts index 038bac2aa4..f21dc7c102 100644 --- a/tegg/core/agent-tracing/test/TestUtils.ts +++ b/tegg/core/agent-tracing/test/TestUtils.ts @@ -1,9 +1,15 @@ import type { Logger } from '@eggjs/tegg-types'; +import type { Run } from '@langchain/core/tracers/base'; -import { ILogServiceClient } from '../src/ILogServiceClient.ts'; -import { IOssClient } from '../src/IOssClient.ts'; import { TracingService } from '../src/TracingService.ts'; +export interface CapturedEntry { + run: Run; + status: string; + name: string; + agentName: string; +} + export function createMockLogger(logs?: string[]): Logger { return { info: (msg: string) => { @@ -18,31 +24,20 @@ export function createMockLogger(logs?: string[]): Logger { } as unknown as Logger; } -export function createMockBackgroundTaskHelper(): { run: (fn: () => Promise) => Promise } { - return { - run: async (fn: () => Promise) => fn(), - }; -} - -export function createMockOssClient(): IOssClient { - return { - put: async (_key: string, _content: string | Buffer) => {}, - } as IOssClient; -} - -export function createMockLogServiceClient(logs?: string[]): ILogServiceClient { - return { - send: async (log: string) => { - logs?.push(log); +/** + * Create a mock TracingService that captures Run objects directly. + * Use capturedRuns to assert on traced runs without parsing log strings. + */ +export function createCapturingTracingService(): { + tracingService: TracingService; + capturedRuns: CapturedEntry[]; +} { + const capturedRuns: CapturedEntry[] = []; + const tracingService = { + configure: () => {}, + logTrace: (run: Run, status: string, name: string, agentName: string) => { + capturedRuns.push({ run, status, name, agentName }); }, - } as ILogServiceClient; -} - -export function createMockTracingService(logs?: string[]): TracingService { - const tracingService = new TracingService(); - (tracingService as any).logger = createMockLogger(logs); - (tracingService as any).backgroundTaskHelper = createMockBackgroundTaskHelper(); - (tracingService as any).ossClient = createMockOssClient(); - (tracingService as any).logServiceClient = createMockLogServiceClient(logs); - return tracingService; + } as unknown as TracingService; + return { tracingService, capturedRuns }; } From 054f009356c8066d27412f702de3c051a26b86a1 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 6 Mar 2026 14:24:49 +0800 Subject: [PATCH 08/18] test(agent-tracing): improve coverage for TracingService and LangGraphTracer Add comprehensive TracingService tests covering getEnv, isOnlineEnv, getLogInfoPrefix, uploadToOss, syncLocalToLogService, and logTrace. Add direct hook invocation tests in LangGraphTracer for tool, LLM, retriever and agent callbacks to reach ~89% overall coverage. Co-Authored-By: Claude Sonnet 4.6 --- .../test/LangGraphTracer.test.ts | 73 +++++ .../agent-tracing/test/TracingService.test.ts | 300 ++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 tegg/core/agent-tracing/test/TracingService.test.ts diff --git a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts index 0331c183eb..f9d9bd628f 100644 --- a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts +++ b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts @@ -1,5 +1,6 @@ import assert from 'node:assert/strict'; +import type { Run } from '@langchain/core/tracers/base'; import { FakeLLM } from '@langchain/core/utils/testing'; import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; import { describe, it, beforeEach } from 'vitest'; @@ -8,6 +9,28 @@ import { LangGraphTracer } from '../src/LangGraphTracer.ts'; import { RunStatus } from '../src/types.ts'; import { type CapturedEntry, createCapturingTracingService } from './TestUtils.ts'; +function makeMockRun(overrides?: Partial): Run { + return { + id: 'run-001', + name: 'TestRun', + run_type: 'chain', + inputs: {}, + outputs: {}, + start_time: Date.now(), + end_time: Date.now() + 100, + execution_order: 1, + child_execution_order: 1, + child_runs: [], + events: [], + trace_id: 'trace-001', + parent_run_id: undefined, + tags: [], + extra: {}, + error: undefined, + ...overrides, + } as Run; +} + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); /** Shared state schema for test graphs */ @@ -202,6 +225,56 @@ describe('test/LangGraphTracer.test.ts', () => { }); }); + describe('Direct hook invocation (unit coverage)', () => { + it('should log tool hooks: onToolStart, onToolEnd, onToolError', () => { + const run = makeMockRun({ run_type: 'tool', name: 'BashTool' }); + tracer.onToolStart(run); + tracer.onToolEnd(run); + tracer.onToolError({ ...run, error: 'tool failed' } as Run); + + const toolEntries = capturedRuns.filter((e) => e.run.run_type === 'tool'); + assert.strictEqual(toolEntries.length, 3); + assert.strictEqual(toolEntries[0].status, RunStatus.START); + assert.strictEqual(toolEntries[1].status, RunStatus.END); + assert.strictEqual(toolEntries[2].status, RunStatus.ERROR); + }); + + it('should log LLM hooks: onLLMStart, onLLMEnd, onLLMError', () => { + const run = makeMockRun({ run_type: 'llm', name: 'claude-3' }); + tracer.onLLMStart(run); + tracer.onLLMEnd(run); + tracer.onLLMError({ ...run, error: 'llm error' } as Run); + + const llmEntries = capturedRuns.filter((e) => e.run.run_type === 'llm'); + assert.strictEqual(llmEntries.length, 3); + assert.strictEqual(llmEntries[0].status, RunStatus.START); + assert.strictEqual(llmEntries[1].status, RunStatus.END); + assert.strictEqual(llmEntries[2].status, RunStatus.ERROR); + }); + + it('should log retriever hooks: onRetrieverStart, onRetrieverEnd, onRetrieverError', () => { + const run = makeMockRun({ run_type: 'retriever', name: 'VectorRetriever' }); + tracer.onRetrieverStart(run); + tracer.onRetrieverEnd(run); + tracer.onRetrieverError({ ...run, error: 'retriever error' } as Run); + + const retrieverEntries = capturedRuns.filter((e) => e.run.run_type === 'retriever'); + assert.strictEqual(retrieverEntries.length, 3); + assert.strictEqual(retrieverEntries[0].status, RunStatus.START); + assert.strictEqual(retrieverEntries[1].status, RunStatus.END); + assert.strictEqual(retrieverEntries[2].status, RunStatus.ERROR); + }); + + it('should log agent hooks: onAgentAction, onAgentEnd', () => { + const run = makeMockRun({ run_type: 'chain', name: 'AgentExecutor' }); + tracer.onAgentAction(run); + tracer.onAgentEnd(run); + + assert.strictEqual(capturedRuns[0].status, RunStatus.START); + assert.strictEqual(capturedRuns[1].status, RunStatus.END); + }); + }); + describe('Run data completeness via graph.invoke', () => { it('should produce runs with all required fields populated', async () => { const graph = new StateGraph(GraphState) diff --git a/tegg/core/agent-tracing/test/TracingService.test.ts b/tegg/core/agent-tracing/test/TracingService.test.ts new file mode 100644 index 0000000000..92beb6ec61 --- /dev/null +++ b/tegg/core/agent-tracing/test/TracingService.test.ts @@ -0,0 +1,300 @@ +import assert from 'node:assert/strict'; + +import type { Run } from '@langchain/core/tracers/base'; +import { afterEach, beforeEach, describe, it } from 'vitest'; + +import { TracingService } from '../src/TracingService.ts'; +import { RunStatus } from '../src/types.ts'; + +// ---------- Helpers ---------- + +function makeRun(overrides?: Partial): Run { + return { + id: 'run-001', + name: 'TestRun', + run_type: 'chain', + inputs: { query: 'hello' }, + outputs: { result: 'ok' }, + start_time: 1000, + end_time: 2000, + execution_order: 1, + child_execution_order: 1, + child_runs: [], + events: [], + trace_id: 'trace-abc', + parent_run_id: undefined, + tags: [], + extra: {}, + ...overrides, + } as Run; +} + +function makeTracingService({ + withOss = true, + withLogService = true, +}: { + withOss?: boolean; + withLogService?: boolean; +} = {}) { + const infoLogs: string[] = []; + const warnLogs: string[] = []; + const ossPuts: Array<{ key: string; content: string }> = []; + const logServiceSends: string[] = []; + + const service = new TracingService(); + + (service as any).logger = { + info: (msg: string) => infoLogs.push(msg), + warn: (...args: unknown[]) => warnLogs.push(args.join(' ')), + error: (msg: string) => warnLogs.push(msg), + }; + + (service as any).backgroundTaskHelper = { + run: async (fn: () => Promise) => fn(), + }; + + if (withOss) { + (service as any).ossClient = { + put: async (key: string, content: Buffer) => { + ossPuts.push({ key, content: content.toString() }); + }, + }; + } + + if (withLogService) { + (service as any).logServiceClient = { + send: async (log: string) => { + logServiceSends.push(log); + }, + }; + } + + return { service, infoLogs, warnLogs, ossPuts, logServiceSends }; +} + +// ---------- Tests ---------- + +describe('test/TracingService.test.ts', () => { + let originalFaasEnv: string | undefined; + let originalServerEnv: string | undefined; + + beforeEach(() => { + originalFaasEnv = process.env.FAAS_ENV; + originalServerEnv = process.env.SERVER_ENV; + }); + + afterEach(() => { + if (originalFaasEnv === undefined) { + delete process.env.FAAS_ENV; + } else { + process.env.FAAS_ENV = originalFaasEnv; + } + if (originalServerEnv === undefined) { + delete process.env.SERVER_ENV; + } else { + process.env.SERVER_ENV = originalServerEnv; + } + }); + + describe('getEnv()', () => { + it('should return FAAS_ENV when set', () => { + process.env.FAAS_ENV = 'prod'; + delete process.env.SERVER_ENV; + const { service } = makeTracingService(); + assert.strictEqual(service.getEnv(), 'prod'); + }); + + it('should fall back to SERVER_ENV when FAAS_ENV is not set', () => { + delete process.env.FAAS_ENV; + process.env.SERVER_ENV = 'gray'; + const { service } = makeTracingService(); + assert.strictEqual(service.getEnv(), 'gray'); + }); + + it('should normalize prepub to pre', () => { + delete process.env.FAAS_ENV; + process.env.SERVER_ENV = 'prepub'; + const { service } = makeTracingService(); + assert.strictEqual(service.getEnv(), 'pre'); + }); + + it('should default to local when neither env var is set', () => { + delete process.env.FAAS_ENV; + delete process.env.SERVER_ENV; + const { service } = makeTracingService(); + assert.strictEqual(service.getEnv(), 'local'); + }); + }); + + describe('isOnlineEnv()', () => { + it('should return true for prod', () => { + process.env.FAAS_ENV = 'prod'; + const { service } = makeTracingService(); + assert.strictEqual(service.isOnlineEnv(), true); + }); + + it('should return true for pre', () => { + process.env.FAAS_ENV = 'pre'; + const { service } = makeTracingService(); + assert.strictEqual(service.isOnlineEnv(), true); + }); + + it('should return true for gray', () => { + process.env.FAAS_ENV = 'gray'; + const { service } = makeTracingService(); + assert.strictEqual(service.isOnlineEnv(), true); + }); + + it('should return false for local', () => { + delete process.env.FAAS_ENV; + delete process.env.SERVER_ENV; + const { service } = makeTracingService(); + assert.strictEqual(service.isOnlineEnv(), false); + }); + + it('should return false for dev', () => { + process.env.FAAS_ENV = 'dev'; + const { service } = makeTracingService(); + assert.strictEqual(service.isOnlineEnv(), false); + }); + }); + + describe('getLogInfoPrefix()', () => { + it('should format prefix for root run with FAAS_ENV set', () => { + process.env.FAAS_ENV = 'prod'; + const { service } = makeTracingService(); + const run = makeRun({ trace_id: 'trace-xyz', id: 'run-123', parent_run_id: undefined }); + const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); + assert(prefix.includes('[agent_run][MyAgent]')); + assert(prefix.includes('traceId=trace-xyz')); + assert(prefix.includes('type=root_run')); + assert(prefix.includes('status=start')); + assert(prefix.includes('run_id=run-123')); + assert(prefix.includes('parent_run_id=')); + }); + + it('should mark child run when parent_run_id is set', () => { + process.env.FAAS_ENV = 'dev'; + const { service } = makeTracingService(); + const run = makeRun({ parent_run_id: 'parent-001' }); + const prefix = service.getLogInfoPrefix(run, RunStatus.END, 'MyAgent'); + assert(prefix.includes('type=child_run')); + assert(prefix.includes('parent_run_id=parent-001')); + }); + + it('should include env segment when SERVER_ENV is set and FAAS_ENV is not', () => { + delete process.env.FAAS_ENV; + process.env.SERVER_ENV = 'pre'; + const { service } = makeTracingService(); + const run = makeRun(); + const prefix = service.getLogInfoPrefix(run, RunStatus.END, 'MyAgent'); + assert(prefix.includes('env=pre')); + }); + }); + + describe('uploadToOss()', () => { + it('should upload content to OSS client', async () => { + process.env.FAAS_ENV = 'dev'; + const { service, ossPuts } = makeTracingService({ withOss: true }); + await service.uploadToOss('my/key', 'hello content'); + assert.strictEqual(ossPuts.length, 1); + assert.strictEqual(ossPuts[0].key, 'my/key'); + assert.strictEqual(ossPuts[0].content, 'hello content'); + }); + + it('should warn and skip when no OSS client is configured', async () => { + const { service, warnLogs } = makeTracingService({ withOss: false }); + await service.uploadToOss('my/key', 'content'); + assert(warnLogs.some((log) => log.includes('OSS client not configured'))); + }); + }); + + describe('syncLocalToLogService()', () => { + it('should send log to log service with agentName prefix', async () => { + const { service, logServiceSends } = makeTracingService({ withLogService: true }); + await service.syncLocalToLogService('trace log line', 'MyAgent'); + assert.strictEqual(logServiceSends.length, 1); + assert(logServiceSends[0].includes('[MyAgent]')); + assert(logServiceSends[0].includes('trace log line')); + }); + + it('should skip silently when no log service client configured', async () => { + const { service, logServiceSends } = makeTracingService({ withLogService: false }); + await service.syncLocalToLogService('trace log line', 'MyAgent'); + assert.strictEqual(logServiceSends.length, 0); + }); + + it('should warn when agentName is empty', async () => { + const { service, warnLogs } = makeTracingService({ withLogService: true }); + await service.syncLocalToLogService('log', ''); + assert(warnLogs.some((log) => log.includes('agentName is empty'))); + }); + + it('should handle log service errors gracefully', async () => { + const { service, warnLogs } = makeTracingService({ withLogService: false }); + (service as any).logServiceClient = { + send: async () => { + throw new Error('network error'); + }, + }; + await service.syncLocalToLogService('log', 'MyAgent'); + assert(warnLogs.some((log) => log.includes('syncLocalToLogService error'))); + }); + }); + + describe('logTrace()', () => { + it('should log trace via logger.info when FAAS_ENV is set', () => { + process.env.FAAS_ENV = 'dev'; + const { service, infoLogs } = makeTracingService(); + const run = makeRun({ outputs: undefined }); + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + assert(infoLogs.some((log) => log.includes('[agent_run]'))); + }); + + it('should skip runs tagged with langsmith:hidden', () => { + process.env.FAAS_ENV = 'dev'; + const { service, infoLogs } = makeTracingService(); + const run = makeRun({ tags: ['langsmith:hidden'] }); + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + assert.strictEqual(infoLogs.length, 0); + }); + + it('should upload outputs field to OSS and replace with IResource', async () => { + process.env.FAAS_ENV = 'dev'; + const { service, ossPuts, infoLogs } = makeTracingService({ withOss: true }); + const run = makeRun({ outputs: { result: 'data', llmOutput: { promptTokens: 10 } } }); + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + // backgroundTaskHelper runs synchronously in mock, so OSS put should be done + assert(ossPuts.length >= 1, 'Should have uploaded to OSS'); + // The logged run should have outputs replaced with IResource + const logLine = infoLogs.find((log) => log.includes('[agent_run]')); + assert(logLine, 'Should have a log line'); + const runJson = logLine!.match(/,run=({.*})$/)?.[1]; + assert(runJson, 'Should have run JSON in log'); + const parsed = JSON.parse(runJson); + assert(parsed.outputs?.key, 'outputs should be replaced with IResource'); + assert.strictEqual(parsed.cost?.promptTokens, 10, 'cost should be extracted from llmOutput'); + }); + + it('should sync to log service when env is local', async () => { + delete process.env.FAAS_ENV; + delete process.env.SERVER_ENV; + const { service, logServiceSends } = makeTracingService({ withLogService: true }); + const run = makeRun({ outputs: undefined }); + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + assert(logServiceSends.length >= 1, 'Should have synced to log service in local env'); + }); + + it('should include child run ids in logged json', () => { + process.env.FAAS_ENV = 'dev'; + const { service, infoLogs } = makeTracingService(); + const childRun = makeRun({ id: 'child-001' }); + const run = makeRun({ child_runs: [childRun], outputs: undefined }); + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + const logLine = infoLogs.find((log) => log.includes('[agent_run]')); + const runJson = logLine?.match(/,run=({.*})$/)?.[1]; + const parsed = JSON.parse(runJson!); + assert.deepStrictEqual(parsed.child_run_ids, ['child-001']); + }); + }); +}); From 551a1c41f2157e42fa29ebaade7c14aca1ce5ee6 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 6 Mar 2026 15:49:25 +0800 Subject: [PATCH 09/18] fix(agent-tracing): use Date.now() for child run timestamps in TraceSession Child runs (LLM, tool) were incorrectly using the session start time (#startTime) instead of the actual event time. This caused all child spans to have zero duration and be anchored to session creation, making trace timeline analysis inaccurate. - handleAssistant: capture eventTime = Date.now() per message - handleUser: use Date.now() when completing tool runs - handleResult: use Date.now() for orphaned pending tool runs - Root run end_time (#startTime + duration_ms) remains unchanged Co-Authored-By: Claude Sonnet 4.6 --- tegg/core/agent-tracing/src/ClaudeAgentTracer.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts index 5fa31b1f77..b05d50eeb9 100644 --- a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts +++ b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts @@ -77,13 +77,14 @@ export class TraceSession { const hasText = content.some((c) => c.type === 'text'); if (hasToolUse) { + const eventTime = Date.now(); // Create LLM run that initiated tool calls const llmRun = this.#tracer.createLLMRunInternal( message, this.#rootRunId, this.#traceId, this.#executionOrder++, - this.#startTime, + eventTime, true, ); this.#rootRun.child_runs.push(llmRun); @@ -97,7 +98,7 @@ export class TraceSession { this.#rootRunId, this.#traceId, this.#executionOrder++, - this.#startTime, + eventTime, ); this.#rootRun.child_runs.push(toolRun); this.#pendingToolUses.set(block.id, toolRun); @@ -111,7 +112,7 @@ export class TraceSession { this.#rootRunId, this.#traceId, this.#executionOrder++, - this.#startTime, + Date.now(), false, ); this.#rootRun.child_runs.push(llmRun); @@ -126,7 +127,7 @@ export class TraceSession { if (block.type === 'tool_result') { const toolRun = this.#pendingToolUses.get(block.tool_use_id); if (toolRun) { - this.#tracer.completeToolRunInternal(toolRun, block, this.#startTime); + this.#tracer.completeToolRunInternal(toolRun, block, Date.now()); const status = block.is_error ? RunStatus.ERROR : RunStatus.END; this.#tracer.logTrace(toolRun, status); this.#pendingToolUses.delete(block.tool_use_id); @@ -144,7 +145,7 @@ export class TraceSession { // Complete any pending tool runs for (const [toolUseId, toolRun] of this.#pendingToolUses) { this.#tracer.logger.warn(`[ClaudeAgentTracer] Tool run ${toolUseId} did not receive result`); - toolRun.end_time = this.#startTime; + toolRun.end_time = Date.now(); this.#tracer.logTrace(toolRun, RunStatus.ERROR); } this.#pendingToolUses.clear(); From b7fb28531c838a80705268fa89376b3b9240f035 Mon Sep 17 00:00:00 2001 From: jerry Date: Tue, 10 Mar 2026 11:32:38 +0800 Subject: [PATCH 10/18] test(agent-tracing): improve coverage for ClaudeAgentTracer and TracingService Add missing test cases to cover uncovered code paths: ClaudeAgentTracer: - Guard clauses: warn when assistant/result received before init message - Pending tool runs cleanup: ERROR status when result arrives before tool_result - processMessages edge cases: empty array and missing init message - Internal error handling: catch blocks in processMessage and processMessages TracingService: - OSS upload failure inside backgroundTask catch block - logTrace outer catch block when backgroundTaskHelper throws Co-Authored-By: Claude Sonnet 4.6 --- .../test/ClaudeAgentTracer.test.ts | 119 +++++++++++++++++- .../agent-tracing/test/TracingService.test.ts | 55 ++++++++ 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts index fb149614e5..ed148af4e3 100644 --- a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts +++ b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; -import { describe, it } from 'vitest'; +import { afterEach, beforeEach, describe, it } from 'vitest'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; import { RunStatus } from '../src/types.ts'; @@ -10,8 +10,6 @@ import { createMockLogger, createCapturingTracingService } from './TestUtils.ts' // ---------- Shared setup ---------- function createTestEnv() { - process.env.FAAS_ENV = 'dev'; - const { tracingService, capturedRuns } = createCapturingTracingService(); const mockLogger = createMockLogger(); @@ -158,6 +156,21 @@ function createMockStreamEvent(): SDKMessage { // ---------- Tests ---------- describe('test/ClaudeAgentTracer.test.ts', () => { + let originalFaasEnv: string | undefined; + + beforeEach(() => { + originalFaasEnv = process.env.FAAS_ENV; + process.env.FAAS_ENV = 'dev'; + }); + + afterEach(() => { + if (originalFaasEnv === undefined) { + delete process.env.FAAS_ENV; + } else { + process.env.FAAS_ENV = originalFaasEnv; + } + }); + describe('Streaming mode + tool use', () => { it('should trace tool execution with session.processMessage', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); @@ -316,4 +329,104 @@ describe('test/ClaudeAgentTracer.test.ts', () => { assert(rootError.run, 'Root error run should exist'); }); }); + + describe('Guard clauses — messages before init', () => { + it('should warn and ignore assistant message received before init', async () => { + const { claudeTracer, capturedRuns } = createTestEnv(); + const session = claudeTracer.createSession(); + + // Send assistant message without a preceding init + await session.processMessage(createMockAssistantWithTool()); + + // Nothing should have been traced + assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured before init'); + }); + + it('should warn and ignore result message received before init', async () => { + const { claudeTracer, capturedRuns } = createTestEnv(); + const session = claudeTracer.createSession(); + + // Send result message without a preceding init + await session.processMessage(createMockResult()); + + // Nothing should have been traced + assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured before init'); + }); + }); + + describe('Pending tool runs cleanup on result', () => { + it('should log ERROR for pending tool runs that never received a result', async () => { + const { claudeTracer, capturedRuns } = createTestEnv(); + const session = claudeTracer.createSession(); + + // Init → assistant calls a tool → result arrives WITHOUT the tool_result + const messages: SDKMessage[] = [ + createMockInit(), + createMockAssistantWithTool(), // creates a pending tool run (tu_1) + createMockResult(), // result arrives before tool_result + ]; + + for (const msg of messages) { + await session.processMessage(msg); + } + + // The pending tool run should have been force-closed with ERROR status + const toolErrors = capturedRuns.filter((e) => e.run.run_type === 'tool' && e.status === RunStatus.ERROR); + assert(toolErrors.length >= 1, `Should have at least one tool ERROR entry, got ${toolErrors.length}`); + }); + }); + + describe('processMessages edge cases', () => { + it('should warn and return early for empty message array', async () => { + const { claudeTracer, capturedRuns } = createTestEnv(); + + await claudeTracer.processMessages([]); + + assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured for empty input'); + }); + + it('should warn and return early when no init message is present', async () => { + const { claudeTracer, capturedRuns } = createTestEnv(); + + // Only assistant and result, no system/init + const messages: SDKMessage[] = [createMockAssistantTextOnly(), createMockResult()]; + + await claudeTracer.processMessages(messages); + + assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured without an init message'); + }); + }); + + describe('Internal error handling', () => { + it('should catch errors thrown inside processMessage without propagating', async () => { + const { claudeTracer } = createTestEnv(); + const session = claudeTracer.createSession(); + + // Force an error by replacing logTrace with a throwing stub + (claudeTracer as any).tracingService = { + logTrace: () => { + throw new Error('unexpected logTrace error'); + }, + }; + + // Should NOT throw — error is swallowed by the catch block + await assert.doesNotReject(async () => { + await session.processMessage(createMockInit()); + }); + }); + + it('should catch errors thrown inside processMessages without propagating', async () => { + const { claudeTracer } = createTestEnv(); + + // Replace createSession with a throwing stub to trigger the outer catch + (claudeTracer as any).createSession = () => { + throw new Error('unexpected createSession error'); + }; + + // Should NOT throw — error is swallowed by the catch block + await assert.doesNotReject(async () => { + await claudeTracer.processMessages([createMockInit()]); + }); + }); + }); }); diff --git a/tegg/core/agent-tracing/test/TracingService.test.ts b/tegg/core/agent-tracing/test/TracingService.test.ts index 92beb6ec61..8a6117454b 100644 --- a/tegg/core/agent-tracing/test/TracingService.test.ts +++ b/tegg/core/agent-tracing/test/TracingService.test.ts @@ -296,5 +296,60 @@ describe('test/TracingService.test.ts', () => { const parsed = JSON.parse(runJson!); assert.deepStrictEqual(parsed.child_run_ids, ['child-001']); }); + + it('should warn when OSS upload fails inside backgroundTask', async () => { + process.env.FAAS_ENV = 'dev'; + const { service, warnLogs } = makeTracingService({ withOss: false }); + + // Make the OSS client throw on put + (service as any).ossClient = { + put: async () => { + throw new Error('oss upload failed'); + }, + }; + + // Track background tasks so we can await them + const pendingTasks: Array> = []; + (service as any).backgroundTaskHelper = { + run: (fn: () => Promise) => { + pendingTasks.push(fn()); + }, + }; + + const run = makeRun({ outputs: { result: 'data' } }); + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + + // Wait for all background tasks (the catch block inside fn() calls logger.warn) + await Promise.allSettled(pendingTasks); + + assert( + warnLogs.some((log) => log.includes('Failed to upload run data to OSS')), + 'Should warn about OSS upload failure', + ); + }); + + it('should catch and warn when logTrace itself throws', () => { + process.env.FAAS_ENV = 'dev'; + const { service, warnLogs } = makeTracingService(); + + // Make backgroundTaskHelper.run throw synchronously to trigger the outer catch + (service as any).backgroundTaskHelper = { + run: () => { + throw new Error('backgroundTask error'); + }, + }; + + const run = makeRun({ outputs: { result: 'data' } }); + + // Should NOT throw — the outer catch block in logTrace swallows the error + assert.doesNotThrow(() => { + service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); + }); + + assert( + warnLogs.some((log) => log.includes('logTrace error')), + 'Should warn about logTrace error', + ); + }); }); }); From 81ddf29a6db8b6c1f4db862d22ead62ac1bf75b9 Mon Sep 17 00:00:00 2001 From: jerry Date: Tue, 10 Mar 2026 15:27:43 +0800 Subject: [PATCH 11/18] style(agent-tracing): fix oxfmt formatting in test file Co-Authored-By: Claude Opus 4.6 --- tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts index ed148af4e3..eeaad70759 100644 --- a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts +++ b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts @@ -363,7 +363,7 @@ describe('test/ClaudeAgentTracer.test.ts', () => { const messages: SDKMessage[] = [ createMockInit(), createMockAssistantWithTool(), // creates a pending tool run (tu_1) - createMockResult(), // result arrives before tool_result + createMockResult(), // result arrives before tool_result ]; for (const msg of messages) { From 3c535cd2cc9d0141598c1733863a51222f6e1c85 Mon Sep 17 00:00:00 2001 From: jerry Date: Tue, 10 Mar 2026 15:32:14 +0800 Subject: [PATCH 12/18] refactor(agent-tracing): address PR review feedback - Rename ILogServiceClient/IOssClient to AbstractLogServiceClient/AbstractOssClient to follow tegg naming conventions (use Abstract prefix for abstract classes) - Remove empty AgentTracingConfig interface and unused configure() method - Add threadId to log prefix (from run.extra.metadata.thread_id) per reviewer request Co-Authored-By: Claude Opus 4.6 --- ...eClient.ts => AbstractLogServiceClient.ts} | 6 ++--- .../{IOssClient.ts => AbstractOssClient.ts} | 6 ++--- .../agent-tracing/src/ClaudeAgentTracer.ts | 2 +- .../core/agent-tracing/src/LangGraphTracer.ts | 2 +- tegg/core/agent-tracing/src/TracingService.ts | 26 +++++++++---------- tegg/core/agent-tracing/src/index.ts | 4 +-- tegg/core/agent-tracing/src/types.ts | 9 +------ .../core/agent-tracing/test/Configure.test.ts | 17 +----------- .../test/LangGraphTracer.test.ts | 12 ++++++++- tegg/core/agent-tracing/test/TestUtils.ts | 1 - .../agent-tracing/test/TracingService.test.ts | 9 +++++++ 11 files changed, 44 insertions(+), 50 deletions(-) rename tegg/core/agent-tracing/src/{ILogServiceClient.ts => AbstractLogServiceClient.ts} (84%) rename tegg/core/agent-tracing/src/{IOssClient.ts => AbstractOssClient.ts} (84%) diff --git a/tegg/core/agent-tracing/src/ILogServiceClient.ts b/tegg/core/agent-tracing/src/AbstractLogServiceClient.ts similarity index 84% rename from tegg/core/agent-tracing/src/ILogServiceClient.ts rename to tegg/core/agent-tracing/src/AbstractLogServiceClient.ts index 06dadbc5ed..5b3fb4de37 100644 --- a/tegg/core/agent-tracing/src/ILogServiceClient.ts +++ b/tegg/core/agent-tracing/src/AbstractLogServiceClient.ts @@ -9,11 +9,11 @@ * ```typescript * import { SingletonProto } from '@eggjs/core-decorator'; * import { AccessLevel } from '@eggjs/tegg-types'; - * import { ILogServiceClient } from '@eggjs/agent-tracing'; + * import { AbstractLogServiceClient } from '@eggjs/agent-tracing'; * * // Class name must be LogServiceClient (registers as 'logServiceClient' in the IoC container) * @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) - * export class LogServiceClient extends ILogServiceClient { + * export class LogServiceClient extends AbstractLogServiceClient { * async send(log: string): Promise { * await fetch('https://log.example.com/api', { * method: 'POST', @@ -26,6 +26,6 @@ * * If no implementation is registered, log service syncing is silently skipped. */ -export abstract class ILogServiceClient { +export abstract class AbstractLogServiceClient { abstract send(log: string): Promise; } diff --git a/tegg/core/agent-tracing/src/IOssClient.ts b/tegg/core/agent-tracing/src/AbstractOssClient.ts similarity index 84% rename from tegg/core/agent-tracing/src/IOssClient.ts rename to tegg/core/agent-tracing/src/AbstractOssClient.ts index a6baf6fb00..f0ea395ad1 100644 --- a/tegg/core/agent-tracing/src/IOssClient.ts +++ b/tegg/core/agent-tracing/src/AbstractOssClient.ts @@ -9,11 +9,11 @@ * ```typescript * import { SingletonProto } from '@eggjs/core-decorator'; * import { AccessLevel } from '@eggjs/tegg-types'; - * import { IOssClient } from '@eggjs/agent-tracing'; + * import { AbstractOssClient } from '@eggjs/agent-tracing'; * * // Class name must be OssClient (registers as 'ossClient' in the IoC container) * @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) - * export class OssClient extends IOssClient { + * export class OssClient extends AbstractOssClient { * async put(key: string, content: string | Buffer): Promise { * // your OSS implementation here * } @@ -22,6 +22,6 @@ * * If no implementation is registered, OSS uploads are silently skipped. */ -export abstract class IOssClient { +export abstract class AbstractOssClient { abstract put(key: string, content: string | Buffer): Promise; } diff --git a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts index b05d50eeb9..4b561c0481 100644 --- a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts +++ b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts @@ -206,7 +206,7 @@ export class ClaudeAgentTracer { * Configure the tracer with agent name and service credentials. */ configure(config: TracerConfig): void { - applyTracerConfig(this, this.tracingService, config); + applyTracerConfig(this, config); } /** diff --git a/tegg/core/agent-tracing/src/LangGraphTracer.ts b/tegg/core/agent-tracing/src/LangGraphTracer.ts index de52ac553e..8601ba86a0 100644 --- a/tegg/core/agent-tracing/src/LangGraphTracer.ts +++ b/tegg/core/agent-tracing/src/LangGraphTracer.ts @@ -21,7 +21,7 @@ export class LangGraphTracer extends BaseTracer { * Configure the tracer with agent name and service credentials. */ configure(config: TracerConfig): void { - applyTracerConfig(this, this.tracingService, config); + applyTracerConfig(this, config); } protected persistRun(_: Run): Promise { diff --git a/tegg/core/agent-tracing/src/TracingService.ts b/tegg/core/agent-tracing/src/TracingService.ts index cbfe6a629e..a31f32a1fe 100644 --- a/tegg/core/agent-tracing/src/TracingService.ts +++ b/tegg/core/agent-tracing/src/TracingService.ts @@ -5,9 +5,9 @@ import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; import { getCustomLogger } from 'onelogger'; -import { ILogServiceClient } from './ILogServiceClient.ts'; -import { IOssClient } from './IOssClient.ts'; -import { type AgentTracingConfig, FIELDS_TO_OSS, type IResource, RunStatus } from './types.ts'; +import { AbstractLogServiceClient } from './AbstractLogServiceClient.ts'; +import { AbstractOssClient } from './AbstractOssClient.ts'; +import { FIELDS_TO_OSS, type IResource, RunStatus } from './types.ts'; /** * TracingService - Shared service for common tracing operations. @@ -24,14 +24,10 @@ export class TracingService { private backgroundTaskHelper: BackgroundTaskHelper; @InjectOptional() - private readonly ossClient: IOssClient; + private readonly ossClient: AbstractOssClient; @InjectOptional() - private readonly logServiceClient: ILogServiceClient; - - configure(_config: AgentTracingConfig): void { - // Reserved for future configuration options - } + private readonly logServiceClient: AbstractLogServiceClient; /** * Get the current environment (local, pre, prod, gray) @@ -58,9 +54,11 @@ export class TracingService { getLogInfoPrefix(run: Run, status: RunStatus, name: string): string { const env = this.getEnv(); const envSegment = process.env.FAAS_ENV || env === 'local' ? '' : `env=${env},`; + const threadId = (run.extra as Record)?.metadata?.thread_id ?? 'unknown'; return ( `[agent_run][${name}]:` + `traceId=${run.trace_id},` + + `threadId=${threadId},` + `type=${run.parent_run_id ? 'child_run' : 'root_run'},` + `status=${status},` + `${envSegment}` + @@ -70,12 +68,12 @@ export class TracingService { } /** - * Upload content to OSS using the injected IOssClient implementation. - * Gracefully skips if no IOssClient is provided. + * Upload content to OSS using the injected AbstractOssClient implementation. + * Gracefully skips if no AbstractOssClient is provided. */ async uploadToOss(key: string, fileContent: string): Promise { if (!this.ossClient) { - this.logger.warn('[TracingService] OSS client not configured. Provide an IOssClient implementation.'); + this.logger.warn('[TracingService] OSS client not configured. Provide an AbstractOssClient implementation.'); return; } this.logger.info(`Uploading to OSS with key: ${key}`); @@ -84,8 +82,8 @@ export class TracingService { } /** - * Sync local tracing logs to the injected ILogServiceClient implementation. - * Silently skips if no ILogServiceClient is registered. + * Sync local tracing logs to the injected AbstractLogServiceClient implementation. + * Silently skips if no AbstractLogServiceClient is registered. */ async syncLocalToLogService(log: string, agentName: string): Promise { if (!this.logServiceClient) { diff --git a/tegg/core/agent-tracing/src/index.ts b/tegg/core/agent-tracing/src/index.ts index 737a85cf00..c8d08ef7e4 100644 --- a/tegg/core/agent-tracing/src/index.ts +++ b/tegg/core/agent-tracing/src/index.ts @@ -1,4 +1,4 @@ export * from './types.ts'; export { TracingService } from './TracingService.ts'; -export { IOssClient } from './IOssClient.ts'; -export { ILogServiceClient } from './ILogServiceClient.ts'; +export { AbstractOssClient } from './AbstractOssClient.ts'; +export { AbstractLogServiceClient } from './AbstractLogServiceClient.ts'; diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts index df786bb18a..ba85f16a1a 100644 --- a/tegg/core/agent-tracing/src/types.ts +++ b/tegg/core/agent-tracing/src/types.ts @@ -144,26 +144,19 @@ export const RunStatus = { } as const; export type RunStatus = (typeof RunStatus)[keyof typeof RunStatus]; -/** Internal config used by TracingService */ -export interface AgentTracingConfig { - // Reserved for future configuration options -} - /** User-facing config passed to tracer.configure() */ export interface TracerConfig { agentName?: string; } -/** Apply user-facing TracerConfig to a tracer instance and its TracingService. */ +/** Apply user-facing TracerConfig to a tracer instance. */ export function applyTracerConfig( tracer: { agentName: string }, - tracingService: { configure(config: AgentTracingConfig): void }, config: TracerConfig, ): void { if (config.agentName !== undefined) { tracer.agentName = config.agentName; } - tracingService.configure({}); } export { FIELDS_TO_OSS }; diff --git a/tegg/core/agent-tracing/test/Configure.test.ts b/tegg/core/agent-tracing/test/Configure.test.ts index 5c63ad750e..18dffd7e93 100644 --- a/tegg/core/agent-tracing/test/Configure.test.ts +++ b/tegg/core/agent-tracing/test/Configure.test.ts @@ -1,27 +1,12 @@ import assert from 'node:assert/strict'; -import { describe, it, beforeEach } from 'vitest'; +import { describe, it } from 'vitest'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer.ts'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; -import { TracingService } from '../src/TracingService.ts'; import { createMockLogger, createCapturingTracingService } from './TestUtils.ts'; describe('test/Configure.test.ts', () => { - describe('TracingService.configure()', () => { - let tracingService: TracingService; - - beforeEach(() => { - tracingService = createCapturingTracingService().tracingService; - }); - - it('should accept empty config', () => { - assert.doesNotThrow(() => { - tracingService.configure({}); - }); - }); - }); - describe('LangGraphTracer.configure()', () => { it('should set agentName and delegate to TracingService', () => { const { tracingService } = createCapturingTracingService(); diff --git a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts index f9d9bd628f..f635fe7523 100644 --- a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts +++ b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts @@ -3,7 +3,7 @@ import assert from 'node:assert/strict'; import type { Run } from '@langchain/core/tracers/base'; import { FakeLLM } from '@langchain/core/utils/testing'; import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; -import { describe, it, beforeEach } from 'vitest'; +import { afterEach, beforeEach, describe, it } from 'vitest'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; import { RunStatus } from '../src/types.ts'; @@ -42,8 +42,10 @@ const GraphState = Annotation.Root({ describe('test/LangGraphTracer.test.ts', () => { let tracer: LangGraphTracer; let capturedRuns: CapturedEntry[]; + let originalFaasEnv: string | undefined; beforeEach(() => { + originalFaasEnv = process.env.FAAS_ENV; process.env.FAAS_ENV = 'dev'; const capturing = createCapturingTracingService(); @@ -53,6 +55,14 @@ describe('test/LangGraphTracer.test.ts', () => { (tracer as any).tracingService = capturing.tracingService; }); + afterEach(() => { + if (originalFaasEnv === undefined) { + delete process.env.FAAS_ENV; + } else { + process.env.FAAS_ENV = originalFaasEnv; + } + }); + describe('Single-node StateGraph triggers chain lifecycle hooks', () => { it('should trigger onChainStart and onChainEnd via graph.invoke', async () => { const graph = new StateGraph(GraphState) diff --git a/tegg/core/agent-tracing/test/TestUtils.ts b/tegg/core/agent-tracing/test/TestUtils.ts index f21dc7c102..d6bd3fa740 100644 --- a/tegg/core/agent-tracing/test/TestUtils.ts +++ b/tegg/core/agent-tracing/test/TestUtils.ts @@ -34,7 +34,6 @@ export function createCapturingTracingService(): { } { const capturedRuns: CapturedEntry[] = []; const tracingService = { - configure: () => {}, logTrace: (run: Run, status: string, name: string, agentName: string) => { capturedRuns.push({ run, status, name, agentName }); }, diff --git a/tegg/core/agent-tracing/test/TracingService.test.ts b/tegg/core/agent-tracing/test/TracingService.test.ts index 8a6117454b..4fe287532a 100644 --- a/tegg/core/agent-tracing/test/TracingService.test.ts +++ b/tegg/core/agent-tracing/test/TracingService.test.ts @@ -167,12 +167,21 @@ describe('test/TracingService.test.ts', () => { const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); assert(prefix.includes('[agent_run][MyAgent]')); assert(prefix.includes('traceId=trace-xyz')); + assert(prefix.includes('threadId=unknown')); assert(prefix.includes('type=root_run')); assert(prefix.includes('status=start')); assert(prefix.includes('run_id=run-123')); assert(prefix.includes('parent_run_id=')); }); + it('should include threadId from run.extra.metadata when available', () => { + process.env.FAAS_ENV = 'dev'; + const { service } = makeTracingService(); + const run = makeRun({ extra: { metadata: { thread_id: 'thread-abc' } } }); + const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); + assert(prefix.includes('threadId=thread-abc')); + }); + it('should mark child run when parent_run_id is set', () => { process.env.FAAS_ENV = 'dev'; const { service } = makeTracingService(); From 0e8c85cd11245321a3544471dffa83727b8d662b Mon Sep 17 00:00:00 2001 From: jerry Date: Tue, 10 Mar 2026 16:06:03 +0800 Subject: [PATCH 13/18] fix(agent-tracing): populate thread_id from session_id in ClaudeAgentTracer Claude SDK runs are manually constructed and don't have LangChain's automatic metadata.thread_id. Use session_id as thread_id in run.extra.metadata so getLogInfoPrefix can extract it consistently across both LangGraph and Claude tracers. Co-Authored-By: Claude Opus 4.6 --- tegg/core/agent-tracing/src/ClaudeAgentTracer.ts | 3 +++ tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts index 4b561c0481..eba8ae2555 100644 --- a/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts +++ b/tegg/core/agent-tracing/src/ClaudeAgentTracer.ts @@ -341,6 +341,9 @@ export class ClaudeAgentTracer { parent_run_id: undefined, tags: [], extra: { + metadata: { + thread_id: initMsg.session_id, + }, apiKeySource: initMsg.apiKeySource, claude_code_version: initMsg.claude_code_version, output_style: initMsg.output_style, diff --git a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts index eeaad70759..cfc3d9fdb5 100644 --- a/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts +++ b/tegg/core/agent-tracing/test/ClaudeAgentTracer.test.ts @@ -218,6 +218,10 @@ describe('test/ClaudeAgentTracer.test.ts', () => { assert.strictEqual(traceIds.size, 1, `All runs should share one trace_id, got ${traceIds.size}`); assert.strictEqual([...traceIds][0], 'test-session-001', 'trace_id should match session_id'); + // Root run should carry session_id as thread_id in extra.metadata + const rootExtra = rootStart.run.extra as Record; + assert.strictEqual(rootExtra?.metadata?.thread_id, 'test-session-001', 'thread_id should match session_id'); + // Child runs reference root run as parent const childEntries = capturedRuns.filter((e) => !!e.run.parent_run_id); for (const child of childEntries) { From 3c246f67e4e3f7bbc26e678329af8ac7c4cdc78c Mon Sep 17 00:00:00 2001 From: jerry Date: Tue, 10 Mar 2026 16:49:48 +0800 Subject: [PATCH 14/18] style(agent-tracing): fix oxfmt formatting in types.ts Co-Authored-By: Claude Opus 4.6 --- tegg/core/agent-tracing/src/types.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts index ba85f16a1a..f46fd4e613 100644 --- a/tegg/core/agent-tracing/src/types.ts +++ b/tegg/core/agent-tracing/src/types.ts @@ -150,10 +150,7 @@ export interface TracerConfig { } /** Apply user-facing TracerConfig to a tracer instance. */ -export function applyTracerConfig( - tracer: { agentName: string }, - config: TracerConfig, -): void { +export function applyTracerConfig(tracer: { agentName: string }, config: TracerConfig): void { if (config.agentName !== undefined) { tracer.agentName = config.agentName; } From f2264f887bd9e551bb52a7bad9f60769edafcd26 Mon Sep 17 00:00:00 2001 From: jerry Date: Wed, 11 Mar 2026 15:32:02 +0800 Subject: [PATCH 15/18] chore(deps): sync pnpm-lock.yaml after rebase on next Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 76 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e71c0237d1..5f784e5cb2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -462,6 +462,9 @@ catalogs: onelogger: specifier: ^1.0.1 version: 1.0.1 + oss-client: + specifier: ^2.5.1 + version: 2.5.1 oxc-minify: specifier: ^0.105.0 version: 0.105.0 @@ -2056,6 +2059,25 @@ importers: specifier: 'catalog:' version: 0.105.0 + tegg/core/agent-runtime: + dependencies: + '@eggjs/tegg-types': + specifier: workspace:* + version: link:../types + egg-logger: + specifier: 'catalog:' + version: 3.6.1 + oss-client: + specifier: 'catalog:' + version: 2.5.1 + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 24.10.2 + typescript: + specifier: 'catalog:' + version: 5.9.3 + tegg/core/agent-tracing: dependencies: '@eggjs/background-task': @@ -2722,6 +2744,9 @@ importers: tegg/core/tegg: dependencies: + '@eggjs/agent-runtime': + specifier: workspace:* + version: link:../agent-runtime '@eggjs/ajv-decorator': specifier: workspace:* version: link:../ajv-decorator @@ -2997,6 +3022,9 @@ importers: tegg/plugin/controller: dependencies: + '@eggjs/agent-runtime': + specifier: workspace:* + version: link:../../core/agent-runtime '@eggjs/controller-decorator': specifier: workspace:* version: link:../../core/controller-decorator @@ -8078,6 +8106,11 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -8446,6 +8479,13 @@ packages: engines: {node: '>=0.10.0'} hasBin: true + oss-client@2.5.1: + resolution: {integrity: sha512-LSm191DdZm7E/jKhaGLBf/EjrA+6E+RjTjHjq8dvEtMb9hi4O1FWe6IwSncPHCwHXgmVOk3iHll87WqbNN4NXw==} + engines: {node: '>= 16.0.0'} + + oss-interface@1.5.0: + resolution: {integrity: sha512-NqVR42QclE/EosbfzKfrJ8OUPHsHt1T6dMoXnLz7lOtHcefMGgtaN70yRMMdhL0/bt1VF59jh+4lwdP3PrhHqg==} + osx-release@1.1.0: resolution: {integrity: sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==} engines: {node: '>=0.10.0'} @@ -8964,6 +9004,7 @@ packages: rolldown-vite@7.3.0: resolution: {integrity: sha512-5hI5NCJwKBGtzWtdKB3c2fOEpI77Iaa0z4mSzZPU1cJ/OqrGbFafm90edVCd7T9Snz+Sh09TMAv4EQqyVLzuEg==} engines: {node: ^20.19.0 || >=22.12.0} + deprecated: Use 7.3.1 for migration purposes. For the most recent updates, migrate to Vite 8 once you're ready. hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 @@ -9920,6 +9961,14 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + xss@1.0.15: resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} engines: {node: '>= 0.10.0'} @@ -14402,6 +14451,8 @@ snapshots: mime@2.6.0: {} + mime@3.0.0: {} + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -14835,6 +14886,21 @@ snapshots: osx-release: 1.1.0 win-release: 1.1.1 + oss-client@2.5.1: + dependencies: + is-type-of: 2.2.0 + mime: 3.0.0 + ms: 2.1.3 + oss-interface: 1.5.0 + stream-wormhole: 2.0.1 + urllib: 4.8.2 + utility: 2.5.0 + xml2js: 0.6.2 + + oss-interface@1.5.0: + dependencies: + type-fest: 4.41.0 + osx-release@1.1.0: dependencies: minimist: 1.2.8 @@ -15478,8 +15544,7 @@ snapshots: '@parcel/watcher': 2.5.1 optional: true - sax@1.4.3: - optional: true + sax@1.4.3: {} sdk-base@3.6.0: dependencies: @@ -16508,6 +16573,13 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 + xml2js@0.6.2: + dependencies: + sax: 1.4.3 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + xss@1.0.15: dependencies: commander: 2.20.3 From 4de046da0e1dc3106182d410c90428c3bc8cf769 Mon Sep 17 00:00:00 2001 From: jerry Date: Wed, 11 Mar 2026 19:42:52 +0800 Subject: [PATCH 16/18] chore(agent-tracing): bump version to 4.0.2-beta.3 to align with other tegg packages Co-Authored-By: Claude Opus 4.6 --- tegg/core/agent-tracing/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tegg/core/agent-tracing/package.json b/tegg/core/agent-tracing/package.json index e75705aa72..92253bb234 100644 --- a/tegg/core/agent-tracing/package.json +++ b/tegg/core/agent-tracing/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/agent-tracing", - "version": "4.0.2-beta.1", + "version": "4.0.2-beta.3", "description": "Tracing support for AI agents (LangGraph, Claude Agent SDK)", "keywords": [ "agent", From 1e52c2aa177946e84c00efdf76d4ca0ab6b992f9 Mon Sep 17 00:00:00 2001 From: jerry Date: Thu, 12 Mar 2026 17:39:30 +0800 Subject: [PATCH 17/18] refactor(agent-tracing): deduplicate mock Run factories and remove unused ILogRun Consolidate duplicate makeMockRun/makeRun helpers into a single createMockRun() in TestUtils.ts, and delete the unused ILogRun interface from types.ts. Co-Authored-By: Claude Opus 4.6 --- tegg/core/agent-tracing/src/types.ts | 12 ----- .../test/LangGraphTracer.test.ts | 32 ++----------- tegg/core/agent-tracing/test/TestUtils.ts | 22 +++++++++ .../agent-tracing/test/TracingService.test.ts | 47 +++++-------------- 4 files changed, 40 insertions(+), 73 deletions(-) diff --git a/tegg/core/agent-tracing/src/types.ts b/tegg/core/agent-tracing/src/types.ts index f46fd4e613..949a53851a 100644 --- a/tegg/core/agent-tracing/src/types.ts +++ b/tegg/core/agent-tracing/src/types.ts @@ -1,5 +1,3 @@ -import type { Run } from '@langchain/core/tracers/base'; - // Claude SDK Message Types export interface ClaudeTextContent { @@ -127,16 +125,6 @@ export interface IRunCost { const FIELDS_TO_OSS = ['inputs', 'outputs', 'attachments', 'serialized', 'events'] as const; -export interface ILogRun extends Omit { - child_run_ids?: string[]; - outputs?: IResource; - inputs?: IResource; - attachments?: IResource; - serialized?: IResource; - events?: IResource; - cost?: IRunCost; -} - export const RunStatus = { START: 'start', END: 'end', diff --git a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts index f635fe7523..a3dc9268b8 100644 --- a/tegg/core/agent-tracing/test/LangGraphTracer.test.ts +++ b/tegg/core/agent-tracing/test/LangGraphTracer.test.ts @@ -7,29 +7,7 @@ import { afterEach, beforeEach, describe, it } from 'vitest'; import { LangGraphTracer } from '../src/LangGraphTracer.ts'; import { RunStatus } from '../src/types.ts'; -import { type CapturedEntry, createCapturingTracingService } from './TestUtils.ts'; - -function makeMockRun(overrides?: Partial): Run { - return { - id: 'run-001', - name: 'TestRun', - run_type: 'chain', - inputs: {}, - outputs: {}, - start_time: Date.now(), - end_time: Date.now() + 100, - execution_order: 1, - child_execution_order: 1, - child_runs: [], - events: [], - trace_id: 'trace-001', - parent_run_id: undefined, - tags: [], - extra: {}, - error: undefined, - ...overrides, - } as Run; -} +import { type CapturedEntry, createCapturingTracingService, createMockRun } from './TestUtils.ts'; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -237,7 +215,7 @@ describe('test/LangGraphTracer.test.ts', () => { describe('Direct hook invocation (unit coverage)', () => { it('should log tool hooks: onToolStart, onToolEnd, onToolError', () => { - const run = makeMockRun({ run_type: 'tool', name: 'BashTool' }); + const run = createMockRun({ run_type: 'tool', name: 'BashTool' }); tracer.onToolStart(run); tracer.onToolEnd(run); tracer.onToolError({ ...run, error: 'tool failed' } as Run); @@ -250,7 +228,7 @@ describe('test/LangGraphTracer.test.ts', () => { }); it('should log LLM hooks: onLLMStart, onLLMEnd, onLLMError', () => { - const run = makeMockRun({ run_type: 'llm', name: 'claude-3' }); + const run = createMockRun({ run_type: 'llm', name: 'claude-3' }); tracer.onLLMStart(run); tracer.onLLMEnd(run); tracer.onLLMError({ ...run, error: 'llm error' } as Run); @@ -263,7 +241,7 @@ describe('test/LangGraphTracer.test.ts', () => { }); it('should log retriever hooks: onRetrieverStart, onRetrieverEnd, onRetrieverError', () => { - const run = makeMockRun({ run_type: 'retriever', name: 'VectorRetriever' }); + const run = createMockRun({ run_type: 'retriever', name: 'VectorRetriever' }); tracer.onRetrieverStart(run); tracer.onRetrieverEnd(run); tracer.onRetrieverError({ ...run, error: 'retriever error' } as Run); @@ -276,7 +254,7 @@ describe('test/LangGraphTracer.test.ts', () => { }); it('should log agent hooks: onAgentAction, onAgentEnd', () => { - const run = makeMockRun({ run_type: 'chain', name: 'AgentExecutor' }); + const run = createMockRun({ run_type: 'chain', name: 'AgentExecutor' }); tracer.onAgentAction(run); tracer.onAgentEnd(run); diff --git a/tegg/core/agent-tracing/test/TestUtils.ts b/tegg/core/agent-tracing/test/TestUtils.ts index d6bd3fa740..59343f2903 100644 --- a/tegg/core/agent-tracing/test/TestUtils.ts +++ b/tegg/core/agent-tracing/test/TestUtils.ts @@ -10,6 +10,28 @@ export interface CapturedEntry { agentName: string; } +export function createMockRun(overrides?: Partial): Run { + return { + id: 'run-001', + name: 'TestRun', + run_type: 'chain', + inputs: {}, + outputs: {}, + start_time: Date.now(), + end_time: Date.now() + 100, + execution_order: 1, + child_execution_order: 1, + child_runs: [], + events: [], + trace_id: 'trace-001', + parent_run_id: undefined, + tags: [], + extra: {}, + error: undefined, + ...overrides, + } as Run; +} + export function createMockLogger(logs?: string[]): Logger { return { info: (msg: string) => { diff --git a/tegg/core/agent-tracing/test/TracingService.test.ts b/tegg/core/agent-tracing/test/TracingService.test.ts index 4fe287532a..2300207ca1 100644 --- a/tegg/core/agent-tracing/test/TracingService.test.ts +++ b/tegg/core/agent-tracing/test/TracingService.test.ts @@ -1,34 +1,13 @@ import assert from 'node:assert/strict'; -import type { Run } from '@langchain/core/tracers/base'; import { afterEach, beforeEach, describe, it } from 'vitest'; import { TracingService } from '../src/TracingService.ts'; import { RunStatus } from '../src/types.ts'; +import { createMockRun } from './TestUtils.ts'; // ---------- Helpers ---------- -function makeRun(overrides?: Partial): Run { - return { - id: 'run-001', - name: 'TestRun', - run_type: 'chain', - inputs: { query: 'hello' }, - outputs: { result: 'ok' }, - start_time: 1000, - end_time: 2000, - execution_order: 1, - child_execution_order: 1, - child_runs: [], - events: [], - trace_id: 'trace-abc', - parent_run_id: undefined, - tags: [], - extra: {}, - ...overrides, - } as Run; -} - function makeTracingService({ withOss = true, withLogService = true, @@ -163,7 +142,7 @@ describe('test/TracingService.test.ts', () => { it('should format prefix for root run with FAAS_ENV set', () => { process.env.FAAS_ENV = 'prod'; const { service } = makeTracingService(); - const run = makeRun({ trace_id: 'trace-xyz', id: 'run-123', parent_run_id: undefined }); + const run = createMockRun({ trace_id: 'trace-xyz', id: 'run-123', parent_run_id: undefined }); const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); assert(prefix.includes('[agent_run][MyAgent]')); assert(prefix.includes('traceId=trace-xyz')); @@ -177,7 +156,7 @@ describe('test/TracingService.test.ts', () => { it('should include threadId from run.extra.metadata when available', () => { process.env.FAAS_ENV = 'dev'; const { service } = makeTracingService(); - const run = makeRun({ extra: { metadata: { thread_id: 'thread-abc' } } }); + const run = createMockRun({ extra: { metadata: { thread_id: 'thread-abc' } } }); const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); assert(prefix.includes('threadId=thread-abc')); }); @@ -185,7 +164,7 @@ describe('test/TracingService.test.ts', () => { it('should mark child run when parent_run_id is set', () => { process.env.FAAS_ENV = 'dev'; const { service } = makeTracingService(); - const run = makeRun({ parent_run_id: 'parent-001' }); + const run = createMockRun({ parent_run_id: 'parent-001' }); const prefix = service.getLogInfoPrefix(run, RunStatus.END, 'MyAgent'); assert(prefix.includes('type=child_run')); assert(prefix.includes('parent_run_id=parent-001')); @@ -195,7 +174,7 @@ describe('test/TracingService.test.ts', () => { delete process.env.FAAS_ENV; process.env.SERVER_ENV = 'pre'; const { service } = makeTracingService(); - const run = makeRun(); + const run = createMockRun(); const prefix = service.getLogInfoPrefix(run, RunStatus.END, 'MyAgent'); assert(prefix.includes('env=pre')); }); @@ -255,7 +234,7 @@ describe('test/TracingService.test.ts', () => { it('should log trace via logger.info when FAAS_ENV is set', () => { process.env.FAAS_ENV = 'dev'; const { service, infoLogs } = makeTracingService(); - const run = makeRun({ outputs: undefined }); + const run = createMockRun({ outputs: undefined }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); assert(infoLogs.some((log) => log.includes('[agent_run]'))); }); @@ -263,7 +242,7 @@ describe('test/TracingService.test.ts', () => { it('should skip runs tagged with langsmith:hidden', () => { process.env.FAAS_ENV = 'dev'; const { service, infoLogs } = makeTracingService(); - const run = makeRun({ tags: ['langsmith:hidden'] }); + const run = createMockRun({ tags: ['langsmith:hidden'] }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); assert.strictEqual(infoLogs.length, 0); }); @@ -271,7 +250,7 @@ describe('test/TracingService.test.ts', () => { it('should upload outputs field to OSS and replace with IResource', async () => { process.env.FAAS_ENV = 'dev'; const { service, ossPuts, infoLogs } = makeTracingService({ withOss: true }); - const run = makeRun({ outputs: { result: 'data', llmOutput: { promptTokens: 10 } } }); + const run = createMockRun({ outputs: { result: 'data', llmOutput: { promptTokens: 10 } } }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); // backgroundTaskHelper runs synchronously in mock, so OSS put should be done assert(ossPuts.length >= 1, 'Should have uploaded to OSS'); @@ -289,7 +268,7 @@ describe('test/TracingService.test.ts', () => { delete process.env.FAAS_ENV; delete process.env.SERVER_ENV; const { service, logServiceSends } = makeTracingService({ withLogService: true }); - const run = makeRun({ outputs: undefined }); + const run = createMockRun({ outputs: undefined }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); assert(logServiceSends.length >= 1, 'Should have synced to log service in local env'); }); @@ -297,8 +276,8 @@ describe('test/TracingService.test.ts', () => { it('should include child run ids in logged json', () => { process.env.FAAS_ENV = 'dev'; const { service, infoLogs } = makeTracingService(); - const childRun = makeRun({ id: 'child-001' }); - const run = makeRun({ child_runs: [childRun], outputs: undefined }); + const childRun = createMockRun({ id: 'child-001' }); + const run = createMockRun({ child_runs: [childRun], outputs: undefined }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); const logLine = infoLogs.find((log) => log.includes('[agent_run]')); const runJson = logLine?.match(/,run=({.*})$/)?.[1]; @@ -325,7 +304,7 @@ describe('test/TracingService.test.ts', () => { }, }; - const run = makeRun({ outputs: { result: 'data' } }); + const run = createMockRun({ outputs: { result: 'data' } }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); // Wait for all background tasks (the catch block inside fn() calls logger.warn) @@ -348,7 +327,7 @@ describe('test/TracingService.test.ts', () => { }, }; - const run = makeRun({ outputs: { result: 'data' } }); + const run = createMockRun({ outputs: { result: 'data' } }); // Should NOT throw — the outer catch block in logTrace swallows the error assert.doesNotThrow(() => { From e51da495690ebed905e54d98f4e9bef2e5cae9b1 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 13 Mar 2026 16:59:47 +0800 Subject: [PATCH 18/18] refactor(agent-tracing): make sub-entries self-contained with re-exports Remove internal TracingService from public index exports and re-export shared types/clients from claude and langgraph entries so users only need a single import path. Co-Authored-By: Claude Opus 4.6 --- tegg/core/agent-tracing/src/claude.ts | 1 + tegg/core/agent-tracing/src/index.ts | 1 - tegg/core/agent-tracing/src/langgraph.ts | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tegg/core/agent-tracing/src/claude.ts b/tegg/core/agent-tracing/src/claude.ts index 56edead0c7..b519ebeb27 100644 --- a/tegg/core/agent-tracing/src/claude.ts +++ b/tegg/core/agent-tracing/src/claude.ts @@ -1 +1,2 @@ +export * from './index.ts'; export { ClaudeAgentTracer, TraceSession } from './ClaudeAgentTracer.ts'; diff --git a/tegg/core/agent-tracing/src/index.ts b/tegg/core/agent-tracing/src/index.ts index c8d08ef7e4..c3b07e4719 100644 --- a/tegg/core/agent-tracing/src/index.ts +++ b/tegg/core/agent-tracing/src/index.ts @@ -1,4 +1,3 @@ export * from './types.ts'; -export { TracingService } from './TracingService.ts'; export { AbstractOssClient } from './AbstractOssClient.ts'; export { AbstractLogServiceClient } from './AbstractLogServiceClient.ts'; diff --git a/tegg/core/agent-tracing/src/langgraph.ts b/tegg/core/agent-tracing/src/langgraph.ts index a696438096..3bd67ac4bc 100644 --- a/tegg/core/agent-tracing/src/langgraph.ts +++ b/tegg/core/agent-tracing/src/langgraph.ts @@ -1 +1,2 @@ +export * from './index.ts'; export { LangGraphTracer } from './LangGraphTracer.ts';