@ai-setting/roy-agent-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +99145 -0
- package/package.json +114 -0
- package/src/config/config-component.test.ts +627 -0
- package/src/config/config-component.ts +906 -0
- package/src/config/config-parser.test.ts +319 -0
- package/src/config/config-parser.ts +203 -0
- package/src/config/decentralized-config.test.ts +740 -0
- package/src/config/env-key.ts +210 -0
- package/src/config/env-source.test.ts +252 -0
- package/src/config/env-source.ts +301 -0
- package/src/config/file-source.test.ts +357 -0
- package/src/config/file-source.ts +421 -0
- package/src/config/index.ts +24 -0
- package/src/config/protocol-resolver.test.ts +217 -0
- package/src/config/protocol-resolver.ts +228 -0
- package/src/env/agent/agent-component.abort.test.ts +511 -0
- package/src/env/agent/agent-component.record-session.test.ts +349 -0
- package/src/env/agent/agent-component.test.ts +1389 -0
- package/src/env/agent/agent-component.tool-error.test.ts +327 -0
- package/src/env/agent/agent-component.ts +1711 -0
- package/src/env/agent/agent-config-registration.test.ts +226 -0
- package/src/env/agent/agent-config-registration.ts +46 -0
- package/src/env/agent/agent-reminder-plugin.integration.test.ts +243 -0
- package/src/env/agent/index.ts +10 -0
- package/src/env/agent/summary-agent.parse-hint.test.ts +360 -0
- package/src/env/agent/summary-agent.ts +508 -0
- package/src/env/agent/types.ts +536 -0
- package/src/env/commands/commands-component.test.ts +364 -0
- package/src/env/commands/commands-component.ts +604 -0
- package/src/env/commands/commands-config-registration.test.ts +198 -0
- package/src/env/commands/commands-config-registration.ts +38 -0
- package/src/env/commands/index.ts +21 -0
- package/src/env/commands/parser.test.ts +203 -0
- package/src/env/commands/parser.ts +115 -0
- package/src/env/commands/types.ts +184 -0
- package/src/env/commands-prompt-integration.test.ts +243 -0
- package/src/env/component-env.test.ts +119 -0
- package/src/env/component.ts +335 -0
- package/src/env/constants.test.ts +72 -0
- package/src/env/constants.ts +123 -0
- package/src/env/debug/debug-component.test.ts +114 -0
- package/src/env/debug/debug-component.ts +547 -0
- package/src/env/debug/formatters/index.ts +9 -0
- package/src/env/debug/formatters/repl-formatter.test.ts +139 -0
- package/src/env/debug/formatters/repl-formatter.ts +358 -0
- package/src/env/debug/formatters/trace-formatter.test.ts +119 -0
- package/src/env/debug/formatters/trace-formatter.ts +191 -0
- package/src/env/debug/formatters/tree-formatter.test.ts +107 -0
- package/src/env/debug/formatters/tree-formatter.ts +325 -0
- package/src/env/debug/index.ts +38 -0
- package/src/env/debug/parser/regex-parser.test.ts +201 -0
- package/src/env/debug/parser/regex-parser.ts +196 -0
- package/src/env/debug/parser/span-builder.test.ts +241 -0
- package/src/env/debug/parser/span-builder.ts +386 -0
- package/src/env/debug/reader/log-reader.test.ts +170 -0
- package/src/env/debug/reader/log-reader.ts +186 -0
- package/src/env/debug/reader/span-db-reader.test.ts +118 -0
- package/src/env/debug/reader/span-db-reader.ts +201 -0
- package/src/env/debug/types.test.ts +187 -0
- package/src/env/debug/types.ts +171 -0
- package/src/env/environment-init.test.ts +183 -0
- package/src/env/environment-lifecycle.test.ts +516 -0
- package/src/env/environment-service.test.ts +332 -0
- package/src/env/environment.handle-query.test.ts +96 -0
- package/src/env/environment.test.ts +232 -0
- package/src/env/environment.ts +708 -0
- package/src/env/errors.test.ts +165 -0
- package/src/env/errors.ts +157 -0
- package/src/env/event-source/event-source-agent-handler.test.ts +193 -0
- package/src/env/event-source/event-source-agent-handler.ts +111 -0
- package/src/env/event-source/event-source-component.process-cleanup.test.ts +236 -0
- package/src/env/event-source/event-source-component.stop.test.ts +346 -0
- package/src/env/event-source/event-source-component.test.ts +1207 -0
- package/src/env/event-source/event-source-component.ts +1379 -0
- package/src/env/event-source/event-source-config-registration.test.ts +242 -0
- package/src/env/event-source/event-source-config-registration.ts +37 -0
- package/src/env/event-source/event-source-integration.test.ts +320 -0
- package/src/env/event-source/event-source-platform.test.ts +630 -0
- package/src/env/event-source/types.ts +298 -0
- package/src/env/hook/global-hook-manager.ts +162 -0
- package/src/env/hook/hook-manager.test.ts +374 -0
- package/src/env/hook/hook-manager.ts +309 -0
- package/src/env/hook/index.ts +38 -0
- package/src/env/hook/types.ts +138 -0
- package/src/env/index.ts +144 -0
- package/src/env/interface.ts +203 -0
- package/src/env/llm/hooks.test.ts +293 -0
- package/src/env/llm/hooks.ts +316 -0
- package/src/env/llm/index.ts +61 -0
- package/src/env/llm/invoke-threshold-check.test.ts +88 -0
- package/src/env/llm/invoke-timeout.test.ts +54 -0
- package/src/env/llm/invoke.test.ts +71 -0
- package/src/env/llm/invoke.ts +1039 -0
- package/src/env/llm/llm-config.test.ts +523 -0
- package/src/env/llm/llm.test.ts +233 -0
- package/src/env/llm/llm.ts +568 -0
- package/src/env/llm/provider.test.ts +182 -0
- package/src/env/llm/provider.ts +108 -0
- package/src/env/llm/transform.test.ts +251 -0
- package/src/env/llm/transform.ts +286 -0
- package/src/env/llm/types.test.ts +580 -0
- package/src/env/llm/types.ts +424 -0
- package/src/env/log-trace/decorator-otel.test.ts +182 -0
- package/src/env/log-trace/decorator.ts +230 -0
- package/src/env/log-trace/index.ts +79 -0
- package/src/env/log-trace/log-trace-component.test.ts +242 -0
- package/src/env/log-trace/log-trace-component.ts +497 -0
- package/src/env/log-trace/log-trace-config-registration.test.ts +348 -0
- package/src/env/log-trace/log-trace-config-registration.ts +45 -0
- package/src/env/log-trace/logger.test.ts +149 -0
- package/src/env/log-trace/logger.ts +522 -0
- package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +147 -0
- package/src/env/log-trace/opentelemetry/cli-propagation.ts +194 -0
- package/src/env/log-trace/opentelemetry/integration.test.ts +668 -0
- package/src/env/log-trace/opentelemetry/mod.ts +25 -0
- package/src/env/log-trace/opentelemetry/propagation-env.test.ts +181 -0
- package/src/env/log-trace/opentelemetry/propagation-env.ts +136 -0
- package/src/env/log-trace/opentelemetry/propagation.test.ts +259 -0
- package/src/env/log-trace/opentelemetry/propagation.ts +215 -0
- package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +166 -0
- package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +379 -0
- package/src/env/log-trace/opentelemetry/tracer-provider.ts +612 -0
- package/src/env/log-trace/span-storage.test.ts +145 -0
- package/src/env/log-trace/span-storage.ts +230 -0
- package/src/env/log-trace/trace-context.test.ts +187 -0
- package/src/env/log-trace/trace-context.ts +162 -0
- package/src/env/log-trace/types.test.ts +63 -0
- package/src/env/log-trace/types.ts +172 -0
- package/src/env/mcp/README.md +244 -0
- package/src/env/mcp/__integration__/mcp-component.integration.test.ts +373 -0
- package/src/env/mcp/config.test.ts +74 -0
- package/src/env/mcp/config.ts +116 -0
- package/src/env/mcp/index.ts +41 -0
- package/src/env/mcp/loader.test.ts +161 -0
- package/src/env/mcp/loader.ts +209 -0
- package/src/env/mcp/mcp-component.test.ts +111 -0
- package/src/env/mcp/mcp-component.ts +358 -0
- package/src/env/mcp/mcp-config-registration.test.ts +304 -0
- package/src/env/mcp/mcp-config-registration.ts +50 -0
- package/src/env/mcp/scanner.test.ts +170 -0
- package/src/env/mcp/scanner.ts +246 -0
- package/src/env/mcp/tool/adapter.test.ts +520 -0
- package/src/env/mcp/tool/adapter.ts +521 -0
- package/src/env/mcp/tool/index.ts +5 -0
- package/src/env/mcp/types.test.ts +171 -0
- package/src/env/mcp/types.ts +79 -0
- package/src/env/memory/README.md +177 -0
- package/src/env/memory/built-in/index.ts +59 -0
- package/src/env/memory/built-in/recall-memory.ts +103 -0
- package/src/env/memory/built-in/record-memory.ts +148 -0
- package/src/env/memory/index.ts +20 -0
- package/src/env/memory/memory-component.test.ts +239 -0
- package/src/env/memory/memory-component.ts +503 -0
- package/src/env/memory/memory-config-registration.test.ts +67 -0
- package/src/env/memory/memory-config-registration.ts +48 -0
- package/src/env/memory/memory-config.ts +45 -0
- package/src/env/memory/memory-file.test.ts +268 -0
- package/src/env/memory/plugin/index.ts +48 -0
- package/src/env/memory/plugin/memory-agent.test.ts +249 -0
- package/src/env/memory/plugin/memory-agent.ts +365 -0
- package/src/env/memory/plugin/memory-manager.ts +198 -0
- package/src/env/memory/plugin/memory-plugin-agent.test.ts +145 -0
- package/src/env/memory/plugin/memory-plugin.ts +210 -0
- package/src/env/memory/plugin/plugin-simplified.test.ts +51 -0
- package/src/env/memory/plugin/recall-memory.test.ts +106 -0
- package/src/env/memory/plugin/recall-memory.ts +53 -0
- package/src/env/memory/plugin/types.ts +101 -0
- package/src/env/memory/tools/memory-agent-tools.ts +228 -0
- package/src/env/memory/types.ts +85 -0
- package/src/env/paths.ts +118 -0
- package/src/env/prompt/index.ts +18 -0
- package/src/env/prompt/memory-prompts.test.ts +91 -0
- package/src/env/prompt/prompt-component.test.ts +491 -0
- package/src/env/prompt/prompt-component.ts +619 -0
- package/src/env/prompt/prompt-config-registration.test.ts +213 -0
- package/src/env/prompt/prompt-config-registration.ts +39 -0
- package/src/env/prompt/prompts-index.ts +504 -0
- package/src/env/prompt/renderer.ts +67 -0
- package/src/env/prompt/types.ts +136 -0
- package/src/env/session/hooks.ts +18 -0
- package/src/env/session/index.ts +37 -0
- package/src/env/session/search-query-parser.test.ts +425 -0
- package/src/env/session/search-query-parser.ts +171 -0
- package/src/env/session/session-checkpoint.test.ts +523 -0
- package/src/env/session/session-component.extract-recent-messages.test.ts +209 -0
- package/src/env/session/session-component.test.ts +132 -0
- package/src/env/session/session-component.ts +1249 -0
- package/src/env/session/session-config-registration.test.ts +138 -0
- package/src/env/session/session-config-registration.ts +52 -0
- package/src/env/session/session-message-converter.test.ts +763 -0
- package/src/env/session/session-message-converter.ts +415 -0
- package/src/env/session/session-message-e2e.test.ts +448 -0
- package/src/env/session/session-search.test.ts +391 -0
- package/src/env/session/session-store.test.ts +362 -0
- package/src/env/session/session-store.ts +141 -0
- package/src/env/session/storage/index.ts +6 -0
- package/src/env/session/storage/memory.ts +502 -0
- package/src/env/session/storage/sqlite.ts +794 -0
- package/src/env/session/types.ts +742 -0
- package/src/env/skill/config.ts +39 -0
- package/src/env/skill/index.ts +6 -0
- package/src/env/skill/parser.test.ts +116 -0
- package/src/env/skill/parser.ts +77 -0
- package/src/env/skill/scanner.test.ts +211 -0
- package/src/env/skill/scanner.ts +119 -0
- package/src/env/skill/skill-component.test.ts +234 -0
- package/src/env/skill/skill-component.ts +352 -0
- package/src/env/skill/skill-config-registration.test.ts +60 -0
- package/src/env/skill/skill-config-registration.ts +43 -0
- package/src/env/skill/tool/index.ts +1 -0
- package/src/env/skill/tool/skill-tool.test.ts +100 -0
- package/src/env/skill/tool/skill-tool.ts +72 -0
- package/src/env/skill/types.ts +64 -0
- package/src/env/task/delegate/delegate-tool.test.ts +498 -0
- package/src/env/task/delegate/delegate-tool.ts +1014 -0
- package/src/env/task/delegate/index.ts +18 -0
- package/src/env/task/delegate/stop-tool.test.ts +140 -0
- package/src/env/task/delegate/stop-tool.ts +119 -0
- package/src/env/task/delegate/task-events.test.ts +178 -0
- package/src/env/task/delegate/task-events.ts +143 -0
- package/src/env/task/hooks/contexts.test.ts +92 -0
- package/src/env/task/hooks/contexts.ts +192 -0
- package/src/env/task/hooks/index.ts +23 -0
- package/src/env/task/hooks/task-hook-points.test.ts +32 -0
- package/src/env/task/hooks/task-hook-points.ts +54 -0
- package/src/env/task/index.ts +7 -0
- package/src/env/task/plugins/index.ts +13 -0
- package/src/env/task/plugins/task-plugin.test.ts +74 -0
- package/src/env/task/plugins/task-plugin.ts +89 -0
- package/src/env/task/plugins/task-tag-plugin.test.ts +377 -0
- package/src/env/task/plugins/task-tag-plugin.ts +319 -0
- package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +226 -0
- package/src/env/task/plugins/workflow-extractor-agent.test.ts +107 -0
- package/src/env/task/plugins/workflow-extractor-agent.ts +225 -0
- package/src/env/task/storage/index.ts +6 -0
- package/src/env/task/storage/sqlite-task-store.test.ts +283 -0
- package/src/env/task/storage/sqlite-task-store.ts +903 -0
- package/src/env/task/storage/task-search.test.ts +291 -0
- package/src/env/task/tag-service.test.ts +198 -0
- package/src/env/task/tag-service.ts +264 -0
- package/src/env/task/task-component.test.ts +193 -0
- package/src/env/task/task-component.ts +658 -0
- package/src/env/task/task-config-registration.test.ts +57 -0
- package/src/env/task/task-config-registration.ts +37 -0
- package/src/env/task/task-types.test.ts +137 -0
- package/src/env/task/tools/complete-tool.ts +44 -0
- package/src/env/task/tools/create-tool.ts +49 -0
- package/src/env/task/tools/delete-tool.ts +43 -0
- package/src/env/task/tools/get-tool.ts +59 -0
- package/src/env/task/tools/index.ts +10 -0
- package/src/env/task/tools/list-tool.ts +40 -0
- package/src/env/task/tools/operation/create-tool.ts +48 -0
- package/src/env/task/tools/operation/delete-tool.ts +43 -0
- package/src/env/task/tools/operation/get-tool.ts +43 -0
- package/src/env/task/tools/operation/index.ts +9 -0
- package/src/env/task/tools/operation/list-tool.ts +40 -0
- package/src/env/task/tools/operation/operation-tools.test.ts +274 -0
- package/src/env/task/tools/operation/operation-types.ts +75 -0
- package/src/env/task/tools/operation/update-tool.ts +47 -0
- package/src/env/task/tools/task-tools.test.ts +203 -0
- package/src/env/task/tools/task-types.test.ts +75 -0
- package/src/env/task/tools/task-types.ts +68 -0
- package/src/env/task/tools/update-tool.ts +70 -0
- package/src/env/task/types.ts +160 -0
- package/src/env/tool/built-in/bash.ts +201 -0
- package/src/env/tool/built-in/echo.ts +29 -0
- package/src/env/tool/built-in/edit-file.test.ts +136 -0
- package/src/env/tool/built-in/edit-file.ts +92 -0
- package/src/env/tool/built-in/glob.test.ts +94 -0
- package/src/env/tool/built-in/glob.ts +65 -0
- package/src/env/tool/built-in/grep.test.ts +122 -0
- package/src/env/tool/built-in/grep.ts +108 -0
- package/src/env/tool/built-in/index.ts +44 -0
- package/src/env/tool/built-in/read-file.test.ts +84 -0
- package/src/env/tool/built-in/read-file.ts +75 -0
- package/src/env/tool/built-in/write-file.test.ts +119 -0
- package/src/env/tool/built-in/write-file.ts +68 -0
- package/src/env/tool/index.ts +24 -0
- package/src/env/tool/registry.test.ts +257 -0
- package/src/env/tool/registry.ts +167 -0
- package/src/env/tool/tool-component.test.ts +559 -0
- package/src/env/tool/tool-component.ts +563 -0
- package/src/env/tool/tool-config-registration.test.ts +249 -0
- package/src/env/tool/tool-config-registration.ts +46 -0
- package/src/env/tool/types.ts +267 -0
- package/src/env/tool/validator.test.ts +143 -0
- package/src/env/tool/validator.ts +44 -0
- package/src/env/types.ts +180 -0
- package/src/env/workflow/ask-user-tool-registration.test.ts +216 -0
- package/src/env/workflow/complex-workflow.integration.test.ts +1900 -0
- package/src/env/workflow/decorators/decorator-node.ts +229 -0
- package/src/env/workflow/decorators/decorator.test.ts +196 -0
- package/src/env/workflow/decorators/edge.ts +82 -0
- package/src/env/workflow/decorators/index.ts +31 -0
- package/src/env/workflow/decorators/node-as.ts +98 -0
- package/src/env/workflow/decorators/workflow.ts +54 -0
- package/src/env/workflow/engine/dag-manager.test.ts +570 -0
- package/src/env/workflow/engine/dag-manager.ts +594 -0
- package/src/env/workflow/engine/engine.ts +1422 -0
- package/src/env/workflow/engine/event-bus.test.ts +359 -0
- package/src/env/workflow/engine/event-bus.ts +156 -0
- package/src/env/workflow/engine/executor-agent-session.test.ts +84 -0
- package/src/env/workflow/engine/executor.test.ts +619 -0
- package/src/env/workflow/engine/executor.ts +593 -0
- package/src/env/workflow/engine/index.ts +24 -0
- package/src/env/workflow/engine/node-registry.test.ts +560 -0
- package/src/env/workflow/engine/node-registry.ts +289 -0
- package/src/env/workflow/engine/resume-removed.test.ts +22 -0
- package/src/env/workflow/engine/scheduler.test.ts +715 -0
- package/src/env/workflow/engine/scheduler.ts +318 -0
- package/src/env/workflow/engine/workflow-engine.test.ts +815 -0
- package/src/env/workflow/extractor/workflow-converter.ts +306 -0
- package/src/env/workflow/fixtures.ts +380 -0
- package/src/env/workflow/index.ts +38 -0
- package/src/env/workflow/integration/run-resume-unified.test.ts +186 -0
- package/src/env/workflow/integration/service-integration.test.ts +267 -0
- package/src/env/workflow/metadata/keys.ts +12 -0
- package/src/env/workflow/nodes/agent-component-adapter.test.ts +318 -0
- package/src/env/workflow/nodes/agent-component-adapter.ts +448 -0
- package/src/env/workflow/nodes/agent-node.test.ts +371 -0
- package/src/env/workflow/nodes/agent-node.ts +598 -0
- package/src/env/workflow/nodes/ask-user-node.ts +113 -0
- package/src/env/workflow/nodes/condition-node.ts +200 -0
- package/src/env/workflow/nodes/index.ts +9 -0
- package/src/env/workflow/nodes/merge-node.ts +141 -0
- package/src/env/workflow/nodes/skill-node.test.ts +253 -0
- package/src/env/workflow/nodes/skill-node.ts +393 -0
- package/src/env/workflow/nodes/tool-node.test.ts +251 -0
- package/src/env/workflow/nodes/tool-node.ts +493 -0
- package/src/env/workflow/nodes/workflow-llm-history.test.ts +455 -0
- package/src/env/workflow/nodes/workflow-node.test.ts +315 -0
- package/src/env/workflow/nodes/workflow-node.ts +311 -0
- package/src/env/workflow/service/index.ts +27 -0
- package/src/env/workflow/service/registry.test.ts +133 -0
- package/src/env/workflow/service/registry.ts +71 -0
- package/src/env/workflow/service/workflow-service.test.ts +310 -0
- package/src/env/workflow/service/workflow-service.ts +393 -0
- package/src/env/workflow/storage/index.ts +28 -0
- package/src/env/workflow/storage/mock-repositories.ts +385 -0
- package/src/env/workflow/storage/sqlite.test.ts +179 -0
- package/src/env/workflow/storage/sqlite.ts +163 -0
- package/src/env/workflow/storage/workflow-repo.test.ts +780 -0
- package/src/env/workflow/storage/workflow-repo.ts +342 -0
- package/src/env/workflow/tools/ask-user-tool.ts +82 -0
- package/src/env/workflow/tools/index.ts +26 -0
- package/src/env/workflow/tools/run-workflow.test.ts +352 -0
- package/src/env/workflow/tools/run-workflow.ts +214 -0
- package/src/env/workflow/types/context.ts +18 -0
- package/src/env/workflow/types/decorators-types.ts +198 -0
- package/src/env/workflow/types/event.test.ts +515 -0
- package/src/env/workflow/types/event.ts +193 -0
- package/src/env/workflow/types/index.ts +49 -0
- package/src/env/workflow/types/run.test.ts +437 -0
- package/src/env/workflow/types/run.ts +173 -0
- package/src/env/workflow/types/workflow-hil.ts +114 -0
- package/src/env/workflow/types/workflow-message.test.ts +138 -0
- package/src/env/workflow/types/workflow-message.ts +196 -0
- package/src/env/workflow/types/workflow-session.test.ts +95 -0
- package/src/env/workflow/types/workflow-session.ts +59 -0
- package/src/env/workflow/types/workflow.test.ts +495 -0
- package/src/env/workflow/types/workflow.ts +195 -0
- package/src/env/workflow/types_compat.ts +51 -0
- package/src/env/workflow/utils/create-workflow.ts +47 -0
- package/src/env/workflow/utils/execution-state.ts +245 -0
- package/src/env/workflow/utils/index.ts +18 -0
- package/src/env/workflow/utils/node-registry-helper.ts +58 -0
- package/src/env/workflow/utils/recovery-validator.test.ts +460 -0
- package/src/env/workflow/utils/recovery-validator.ts +377 -0
- package/src/env/workflow/utils/session-parser.test.ts +111 -0
- package/src/env/workflow/utils/session-parser.ts +94 -0
- package/src/env/workflow/utils/session-recovery.test.ts +334 -0
- package/src/env/workflow/utils/session-recovery.ts +188 -0
- package/src/env/workflow/utils/template-resolver.test.ts +258 -0
- package/src/env/workflow/utils/template-resolver.ts +436 -0
- package/src/env/workflow/utils/validation-rules.ts +149 -0
- package/src/env/workflow/workflow-component.ts +544 -0
- package/src/index.ts +422 -0
- package/src/utils/id.ts +21 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { TreeFormatter } from './tree-formatter';
|
|
3
|
+
import type { TraceTreeNode, TraceEntry } from '../types';
|
|
4
|
+
|
|
5
|
+
describe('TreeFormatter', () => {
|
|
6
|
+
const formatter = new TreeFormatter();
|
|
7
|
+
|
|
8
|
+
const mockRootNode: TraceTreeNode = {
|
|
9
|
+
traceId: 'trace-001',
|
|
10
|
+
function: 'agent.execute',
|
|
11
|
+
startTime: '2026-04-08T10:00:00.000',
|
|
12
|
+
endTime: '2026-04-08T10:00:05.000',
|
|
13
|
+
durationMs: 5000,
|
|
14
|
+
children: [
|
|
15
|
+
{
|
|
16
|
+
traceId: 'trace-001',
|
|
17
|
+
function: 'llm.component.invoke',
|
|
18
|
+
startTime: '2026-04-08T10:00:01.000',
|
|
19
|
+
endTime: '2026-04-08T10:00:03.000',
|
|
20
|
+
durationMs: 2000,
|
|
21
|
+
children: [],
|
|
22
|
+
entry: {} as TraceEntry,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
traceId: 'trace-001',
|
|
26
|
+
function: 'tool.execute',
|
|
27
|
+
startTime: '2026-04-08T10:00:03.000',
|
|
28
|
+
endTime: '2026-04-08T10:00:04.000',
|
|
29
|
+
durationMs: 1000,
|
|
30
|
+
children: [],
|
|
31
|
+
entry: {} as TraceEntry,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
entry: {} as TraceEntry,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
test('formatTree - should format tree with proper indentation', () => {
|
|
38
|
+
const result = formatter.formatTree(mockRootNode);
|
|
39
|
+
|
|
40
|
+
// Should show root function
|
|
41
|
+
expect(result).toContain('agent.execute');
|
|
42
|
+
// Should show duration (5s because it's > 1000ms)
|
|
43
|
+
expect(result).toContain('5.00s');
|
|
44
|
+
// Should have tree branch connectors
|
|
45
|
+
expect(result).toContain('├──');
|
|
46
|
+
expect(result).toContain('└──');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('formatTree - should show children with correct markers', () => {
|
|
50
|
+
const result = formatter.formatTree(mockRootNode);
|
|
51
|
+
|
|
52
|
+
// Should show children
|
|
53
|
+
expect(result).toContain('llm.component.invoke');
|
|
54
|
+
expect(result).toContain('tool.execute');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('formatTree - should handle empty children', () => {
|
|
58
|
+
const leafNode: TraceTreeNode = {
|
|
59
|
+
traceId: 'trace-001',
|
|
60
|
+
function: 'leaf.function',
|
|
61
|
+
startTime: '2026-04-08T10:00:00.000',
|
|
62
|
+
children: [],
|
|
63
|
+
entry: {} as TraceEntry,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const result = formatter.formatTree(leafNode);
|
|
67
|
+
expect(result).toContain('leaf.function');
|
|
68
|
+
expect(result).not.toContain('children');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('formatTree - should format duration', () => {
|
|
72
|
+
const result = formatter.formatTree(mockRootNode);
|
|
73
|
+
|
|
74
|
+
// Duration should be visible (formatted as seconds for values >= 1000ms)
|
|
75
|
+
expect(result).toContain('5.00s'); // 5000ms
|
|
76
|
+
expect(result).toContain('2.00s'); // 2000ms
|
|
77
|
+
expect(result).toContain('1.00s'); // 1000ms
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('formatTrees - should format multiple trees', () => {
|
|
81
|
+
const node2: TraceTreeNode = {
|
|
82
|
+
...mockRootNode,
|
|
83
|
+
traceId: 'trace-002',
|
|
84
|
+
function: 'another.function',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const result = formatter.formatTrees([mockRootNode, node2]);
|
|
88
|
+
|
|
89
|
+
// Should have two separate trees
|
|
90
|
+
expect(result.split('\n\n')).toHaveLength(2);
|
|
91
|
+
expect(result).toContain('agent.execute');
|
|
92
|
+
expect(result).toContain('another.function');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('formatTrees - should handle empty array', () => {
|
|
96
|
+
const result = formatter.formatTrees([]);
|
|
97
|
+
expect(result).toBe('No trace trees');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('toJSON - should output valid JSON', () => {
|
|
101
|
+
const result = formatter.toJSON([mockRootNode]);
|
|
102
|
+
const parsed = JSON.parse(result);
|
|
103
|
+
|
|
104
|
+
expect(parsed).toHaveLength(1);
|
|
105
|
+
expect(parsed[0].function).toBe('agent.execute');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tree Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats trace call trees for console output with:
|
|
5
|
+
* - ANSI color support
|
|
6
|
+
* - Duration display with percentage
|
|
7
|
+
* - Time bar visualization
|
|
8
|
+
* - Proper indentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { TraceTreeNode } from '../types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* ANSI color codes
|
|
15
|
+
*/
|
|
16
|
+
const Colors = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
bold: '\x1b[1m',
|
|
19
|
+
dim: '\x1b[2m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
yellow: '\x1b[33m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
white: '\x1b[37m',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Apply color to text
|
|
29
|
+
*/
|
|
30
|
+
function color(text: string, colorCode: string): string {
|
|
31
|
+
return `${colorCode}${text}${Colors.reset}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Tree drawing characters
|
|
36
|
+
*/
|
|
37
|
+
const CHARS = {
|
|
38
|
+
VERTICAL: '│',
|
|
39
|
+
VERTICAL_CONNECTOR: '├──',
|
|
40
|
+
LAST_CONNECTOR: '└──',
|
|
41
|
+
SPACE: ' ',
|
|
42
|
+
BAR: '█',
|
|
43
|
+
EMPTY: '░',
|
|
44
|
+
DASH: '─',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Options for tree formatting
|
|
49
|
+
*/
|
|
50
|
+
export interface TreeFormatOptions {
|
|
51
|
+
/** Show duration percentage (default: true) */
|
|
52
|
+
showPercent?: boolean;
|
|
53
|
+
/** Show time bar (default: false) */
|
|
54
|
+
showTimeBar?: boolean;
|
|
55
|
+
/** Width of time bar (default: 10) */
|
|
56
|
+
barWidth?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Formats trace call trees for human-readable output
|
|
61
|
+
*/
|
|
62
|
+
export class TreeFormatter {
|
|
63
|
+
private options: TreeFormatOptions;
|
|
64
|
+
|
|
65
|
+
constructor(options: TreeFormatOptions = {}) {
|
|
66
|
+
this.options = {
|
|
67
|
+
showPercent: true,
|
|
68
|
+
showTimeBar: false, // Default off, use --bar flag to enable
|
|
69
|
+
barWidth: 10,
|
|
70
|
+
...options,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Color function based on percentage
|
|
76
|
+
*/
|
|
77
|
+
private colorByPercent(percent: number): string {
|
|
78
|
+
if (percent >= 80) return Colors.red;
|
|
79
|
+
if (percent >= 50) return Colors.yellow;
|
|
80
|
+
if (percent >= 20) return Colors.cyan;
|
|
81
|
+
return Colors.dim;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Format a single trace tree
|
|
86
|
+
*/
|
|
87
|
+
formatTree(node: TraceTreeNode): string {
|
|
88
|
+
const lines: string[] = [];
|
|
89
|
+
const totalDuration = node.durationMs || 0;
|
|
90
|
+
// Use sum of direct children for percentage base (better for virtual root nodes)
|
|
91
|
+
const percentBase = node.children.reduce((sum, c) => sum + (c.durationMs || 0), 0) || totalDuration;
|
|
92
|
+
|
|
93
|
+
// Root node line
|
|
94
|
+
lines.push(this.formatNode(node, totalDuration, percentBase, '', true, true));
|
|
95
|
+
|
|
96
|
+
// Process children recursively
|
|
97
|
+
this.formatChildren(node.children, '', lines, totalDuration, percentBase);
|
|
98
|
+
|
|
99
|
+
return lines.join('\n');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Format a single node
|
|
104
|
+
*/
|
|
105
|
+
private formatNode(
|
|
106
|
+
node: TraceTreeNode,
|
|
107
|
+
totalDuration: number,
|
|
108
|
+
percentBase: number,
|
|
109
|
+
prefix: string,
|
|
110
|
+
isLast: boolean,
|
|
111
|
+
isRoot: boolean
|
|
112
|
+
): string {
|
|
113
|
+
// Tree connector
|
|
114
|
+
const connector = isRoot ? '' : (isLast ? CHARS.LAST_CONNECTOR : CHARS.VERTICAL_CONNECTOR);
|
|
115
|
+
const prefixStr = isRoot ? '' : prefix;
|
|
116
|
+
|
|
117
|
+
let line = `${prefixStr}${connector} ${node.function}`;
|
|
118
|
+
|
|
119
|
+
// Duration
|
|
120
|
+
if (node.durationMs !== undefined) {
|
|
121
|
+
line += color(` (${this.formatDuration(node.durationMs)})`, Colors.dim);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Percentage - skip for:
|
|
125
|
+
// 1. Virtual root nodes (function is '(root)')
|
|
126
|
+
// 2. Actual root of tree (isRoot=true) - root is 100% by definition
|
|
127
|
+
if (this.options.showPercent && percentBase > 0 && node.durationMs !== undefined &&
|
|
128
|
+
node.function !== '(root)' && !isRoot) {
|
|
129
|
+
const percent = Math.round((node.durationMs / percentBase) * 100);
|
|
130
|
+
const percentStr = ` [${percent}%]`;
|
|
131
|
+
line += color(percentStr, this.colorByPercent(percent));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Time bar - only show for nested spans where totalDuration is meaningful
|
|
135
|
+
if (this.options.showTimeBar && node.durationMs !== undefined && totalDuration > 0 && !isRoot) {
|
|
136
|
+
line += ' ' + this.renderTimeBar(node.durationMs, totalDuration);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return isRoot ? color(line, Colors.bold) : line;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Recursively format children
|
|
144
|
+
*/
|
|
145
|
+
private formatChildren(
|
|
146
|
+
children: TraceTreeNode[],
|
|
147
|
+
prefix: string,
|
|
148
|
+
lines: string[],
|
|
149
|
+
totalDuration: number,
|
|
150
|
+
percentBase: number
|
|
151
|
+
): void {
|
|
152
|
+
children.forEach((child, index) => {
|
|
153
|
+
const isLast = index === children.length - 1;
|
|
154
|
+
const newPrefix = prefix + (isLast ? ' ' : `${CHARS.VERTICAL} `);
|
|
155
|
+
|
|
156
|
+
lines.push(this.formatNode(child, totalDuration, percentBase, prefix, isLast, false));
|
|
157
|
+
|
|
158
|
+
if (child.children.length > 0) {
|
|
159
|
+
this.formatChildren(child.children, newPrefix, lines, totalDuration, percentBase);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Render a time bar showing relative duration
|
|
166
|
+
*/
|
|
167
|
+
private renderTimeBar(duration: number, total: number): string {
|
|
168
|
+
const width = this.options.barWidth || 10;
|
|
169
|
+
const filledWidth = Math.round((duration / total) * width);
|
|
170
|
+
const emptyWidth = width - filledWidth;
|
|
171
|
+
|
|
172
|
+
const bar = CHARS.BAR.repeat(filledWidth) + CHARS.EMPTY.repeat(emptyWidth);
|
|
173
|
+
return color(`[${bar}]`, Colors.dim);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Format duration in human-readable form
|
|
178
|
+
*/
|
|
179
|
+
private formatDuration(ms: number): string {
|
|
180
|
+
if (ms < 1000) {
|
|
181
|
+
return `${ms}ms`;
|
|
182
|
+
} else if (ms < 60000) {
|
|
183
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
184
|
+
} else if (ms < 3600000) { // < 1 hour
|
|
185
|
+
const mins = Math.floor(ms / 60000);
|
|
186
|
+
const secs = Math.round((ms % 60000) / 1000);
|
|
187
|
+
return `${mins}m ${secs}s`;
|
|
188
|
+
} else {
|
|
189
|
+
const hours = Math.floor(ms / 3600000);
|
|
190
|
+
const mins = Math.floor((ms % 3600000) / 60000);
|
|
191
|
+
return `${hours}h ${mins}m`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Format multiple trace trees
|
|
197
|
+
*/
|
|
198
|
+
formatTrees(trees: TraceTreeNode[]): string {
|
|
199
|
+
if (trees.length === 0) {
|
|
200
|
+
return 'No trace trees';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return trees.map(tree => this.formatTree(tree)).join('\n\n');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Output as JSON
|
|
208
|
+
*/
|
|
209
|
+
toJSON(trees: TraceTreeNode[]): string {
|
|
210
|
+
return JSON.stringify(trees, null, 2);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Generate a summary table of all spans sorted by duration
|
|
215
|
+
*/
|
|
216
|
+
formatSummary(node: TraceTreeNode): string {
|
|
217
|
+
const rows: { func: string; duration: number; percent: number; depth: number }[] = [];
|
|
218
|
+
// For virtual root nodes with multiple children, use the root's own duration
|
|
219
|
+
// which is calculated from children's time span
|
|
220
|
+
const total = node.durationMs || 0;
|
|
221
|
+
|
|
222
|
+
// Calculate sum of direct children durations for percentage base
|
|
223
|
+
const childrenTotal = node.children.reduce((sum, c) => sum + (c.durationMs || 0), 0);
|
|
224
|
+
const percentBase = total > 0 ? total : childrenTotal;
|
|
225
|
+
|
|
226
|
+
const collect = (n: TraceTreeNode, depth: number): void => {
|
|
227
|
+
if (n.durationMs !== undefined) {
|
|
228
|
+
rows.push({
|
|
229
|
+
func: n.function,
|
|
230
|
+
duration: n.durationMs,
|
|
231
|
+
percent: percentBase > 0 ? Math.round((n.durationMs / percentBase) * 100) : 0,
|
|
232
|
+
depth,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
n.children.forEach(child => collect(child, depth + 1));
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
collect(node, 0);
|
|
239
|
+
|
|
240
|
+
// Sort by duration descending
|
|
241
|
+
rows.sort((a, b) => b.duration - a.duration);
|
|
242
|
+
|
|
243
|
+
const lines: string[] = [];
|
|
244
|
+
lines.push(color('\n=== Duration Summary ===', Colors.bold));
|
|
245
|
+
lines.push(color('(sorted by time consumption)\n', Colors.dim));
|
|
246
|
+
|
|
247
|
+
// Table header
|
|
248
|
+
lines.push(color(` ${'─'.repeat(65)}`, Colors.dim));
|
|
249
|
+
lines.push(color(` ${'FUNCTION'.padEnd(35)} ${'TIME'.padEnd(12)} %`, Colors.dim));
|
|
250
|
+
lines.push(color(` ${'─'.repeat(65)}`, Colors.dim));
|
|
251
|
+
|
|
252
|
+
for (const row of rows) {
|
|
253
|
+
const indent = ' '.repeat(row.depth);
|
|
254
|
+
const funcDisplay = row.depth > 0 ? '└─ '.repeat(row.depth) + row.func : row.func;
|
|
255
|
+
|
|
256
|
+
const percentStr = String(row.percent).padStart(3) + '%';
|
|
257
|
+
const percentColor = row.percent >= 50 ? Colors.red :
|
|
258
|
+
row.percent >= 20 ? Colors.yellow :
|
|
259
|
+
Colors.dim;
|
|
260
|
+
|
|
261
|
+
lines.push(
|
|
262
|
+
` ${indent}${funcDisplay.substring(0, 35).padEnd(35)}${this.formatDuration(row.duration).padEnd(12)} ${color(percentStr, percentColor)}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return lines.join('\n');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Format tree with timeline visualization
|
|
271
|
+
*/
|
|
272
|
+
formatTimeline(node: TraceTreeNode): string {
|
|
273
|
+
const lines: string[] = [];
|
|
274
|
+
const totalDuration = node.durationMs || 0;
|
|
275
|
+
|
|
276
|
+
lines.push(color(`\n=== Timeline: ${node.function} ===`, Colors.bold));
|
|
277
|
+
lines.push(color(`Total: ${this.formatDuration(totalDuration)}\n`, Colors.dim));
|
|
278
|
+
|
|
279
|
+
// Build timeline
|
|
280
|
+
const timeline = this.buildTimeline(node, totalDuration, 0, 0);
|
|
281
|
+
lines.push(...timeline);
|
|
282
|
+
|
|
283
|
+
return lines.join('\n');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Build timeline visualization with absolute positioning
|
|
288
|
+
*/
|
|
289
|
+
private buildTimeline(
|
|
290
|
+
node: TraceTreeNode,
|
|
291
|
+
totalDuration: number,
|
|
292
|
+
absoluteOffset: number,
|
|
293
|
+
depth: number
|
|
294
|
+
): string[] {
|
|
295
|
+
const lines: string[] = [];
|
|
296
|
+
const width = 40;
|
|
297
|
+
|
|
298
|
+
// Calculate position based on absolute offset
|
|
299
|
+
const startPos = Math.round((absoluteOffset / totalDuration) * width);
|
|
300
|
+
const durationWidth = node.durationMs !== undefined
|
|
301
|
+
? Math.max(2, Math.round((node.durationMs / totalDuration) * width))
|
|
302
|
+
: 2;
|
|
303
|
+
|
|
304
|
+
// Build the bar with proper positioning
|
|
305
|
+
const bar = ' '.repeat(Math.max(0, startPos)) + CHARS.BAR.repeat(durationWidth);
|
|
306
|
+
|
|
307
|
+
// Format line
|
|
308
|
+
const indent = ' '.repeat(depth);
|
|
309
|
+
const funcName = node.function.length > 25 ? '...' + node.function.slice(-22) : node.function;
|
|
310
|
+
const time = node.durationMs !== undefined ? this.formatDuration(node.durationMs) : '-';
|
|
311
|
+
const percent = node.durationMs !== undefined
|
|
312
|
+
? ` [${Math.round((node.durationMs / totalDuration) * 100)}%]`
|
|
313
|
+
: '';
|
|
314
|
+
lines.push(`${indent}${funcName.padEnd(25)} ${bar} ${color(time + percent, Colors.dim)}`);
|
|
315
|
+
|
|
316
|
+
// Process children with absolute offset within this node
|
|
317
|
+
let childOffset = absoluteOffset;
|
|
318
|
+
for (const child of node.children) {
|
|
319
|
+
lines.push(...this.buildTimeline(child, totalDuration, childOffset, depth + 1));
|
|
320
|
+
childOffset += child.durationMs || 0;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return lines;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Debug Component Index
|
|
3
|
+
*
|
|
4
|
+
* Export all debug-related types and classes.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
TraceEntry,
|
|
10
|
+
TraceTreeNode,
|
|
11
|
+
ParserOptions,
|
|
12
|
+
LogDiscoveryOptions,
|
|
13
|
+
ParsedLogLine,
|
|
14
|
+
TraceIdInfo,
|
|
15
|
+
TraceSummary,
|
|
16
|
+
FormatOptions,
|
|
17
|
+
TraceAction,
|
|
18
|
+
LogLevel,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
// Data source types (defined in debug-component.ts)
|
|
22
|
+
export type { TraceDataSource, TraceParserOptions } from './debug-component';
|
|
23
|
+
|
|
24
|
+
// Components
|
|
25
|
+
export { DebugComponent } from './debug-component';
|
|
26
|
+
|
|
27
|
+
// Reader
|
|
28
|
+
export { LogReader } from './reader/log-reader';
|
|
29
|
+
export { SpanDbReader } from './reader/span-db-reader';
|
|
30
|
+
|
|
31
|
+
// Parser
|
|
32
|
+
export { RegexParser } from './parser/regex-parser';
|
|
33
|
+
export { SpanBuilder } from './parser/span-builder';
|
|
34
|
+
|
|
35
|
+
// Formatters
|
|
36
|
+
export { TraceFormatter } from './formatters/trace-formatter';
|
|
37
|
+
export { TreeFormatter } from './formatters/tree-formatter';
|
|
38
|
+
export { ReplFormatter } from './formatters/repl-formatter';
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { describe, expect, test, beforeEach } from 'bun:test';
|
|
2
|
+
import { RegexParser } from './regex-parser';
|
|
3
|
+
|
|
4
|
+
describe('RegexParser', () => {
|
|
5
|
+
let parser: RegexParser;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
parser = new RegexParser();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('parseLogLine', () => {
|
|
12
|
+
test('should parse enter trace line', () => {
|
|
13
|
+
const line = '2026-04-08 19:12:36.134 [INFO] [cli/src/bin/roy.js:80752][traced:llm.component.invoke] [TRACE] >>> llm.component.invoke enter: [{\"messages\":[{\"role\":\"user\"}]},{\"providerId\":\"minimax\"}]';
|
|
14
|
+
|
|
15
|
+
const result = parser.parseLogLine(line);
|
|
16
|
+
|
|
17
|
+
expect(result).not.toBeNull();
|
|
18
|
+
expect(result!.timestamp).toBe('2026-04-08T19:12:36.134'); // ISO format with T
|
|
19
|
+
expect(result!.level).toBe('INFO');
|
|
20
|
+
expect(result!.source).toBe('cli/src/bin/roy.js:80752');
|
|
21
|
+
expect(result!.category).toBe('traced:llm.component.invoke');
|
|
22
|
+
expect(result!.action).toBe('>>>');
|
|
23
|
+
expect(result!.method).toBe('llm.component.invoke');
|
|
24
|
+
expect(result!.actionType).toBe('enter');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('should parse quit trace line', () => {
|
|
28
|
+
const line = '2026-04-08 19:12:39.213 [INFO] [cli/src/bin/roy.js:80752][traced:llm.component.invoke] [TRACE] <<< llm.component.invoke quit: {\"output\":{\"content\":\"Hello\"}}';
|
|
29
|
+
|
|
30
|
+
const result = parser.parseLogLine(line);
|
|
31
|
+
|
|
32
|
+
expect(result).not.toBeNull();
|
|
33
|
+
expect(result!.action).toBe('<<<');
|
|
34
|
+
expect(result!.actionType).toBe('quit');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should parse error trace line', () => {
|
|
38
|
+
const line = '2026-04-08 19:12:39.213 [ERROR] [cli/src/bin/roy.js:80752][traced:llm.component.invoke] [TRACE] !!! llm.component.invoke error: Connection timeout';
|
|
39
|
+
|
|
40
|
+
const result = parser.parseLogLine(line);
|
|
41
|
+
|
|
42
|
+
expect(result).not.toBeNull();
|
|
43
|
+
expect(result!.action).toBe('!!!');
|
|
44
|
+
expect(result!.actionType).toBe('error');
|
|
45
|
+
expect(result!.level).toBe('ERROR');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('should return null for non-trace lines', () => {
|
|
49
|
+
const line = '2026-04-08 19:12:36.134 [INFO] Some regular log message';
|
|
50
|
+
|
|
51
|
+
const result = parser.parseLogLine(line);
|
|
52
|
+
|
|
53
|
+
expect(result).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('should return null for malformed trace lines', () => {
|
|
57
|
+
const line = '2026-04-08 19:12:36.134 [INFO] [TRACE] >>> invalid line';
|
|
58
|
+
|
|
59
|
+
const result = parser.parseLogLine(line);
|
|
60
|
+
|
|
61
|
+
expect(result).toBeNull();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should parse line with method name', () => {
|
|
65
|
+
// Note: The requestId is extracted separately via extractTraceId
|
|
66
|
+
const line = '2026-04-08 19:12:36.134 [INFO] [cli/src/bin/roy.js:80752][traced:llm.component.invoke] [TRACE] >>> llm.component.invoke enter: [{}]';
|
|
67
|
+
|
|
68
|
+
const result = parser.parseLogLine(line);
|
|
69
|
+
|
|
70
|
+
expect(result).not.toBeNull();
|
|
71
|
+
expect(result!.method).toBe('llm.component.invoke');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('parseArgs', () => {
|
|
76
|
+
test('should parse JSON array args', () => {
|
|
77
|
+
const raw = '[{\"messages\":[{\"role\":\"user\"}]},{\"providerId\":\"minimax\"}]';
|
|
78
|
+
|
|
79
|
+
const result = parser.parseArgs(raw);
|
|
80
|
+
|
|
81
|
+
expect(Array.isArray(result)).toBe(true);
|
|
82
|
+
expect(result).toHaveLength(2);
|
|
83
|
+
expect(result[0]).toEqual({ messages: [{ role: 'user' }] });
|
|
84
|
+
expect(result[1]).toEqual({ providerId: 'minimax' });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should parse JSON object args', () => {
|
|
88
|
+
const raw = '{\"name\":\"test\",\"value\":123}';
|
|
89
|
+
|
|
90
|
+
const result = parser.parseArgs(raw);
|
|
91
|
+
|
|
92
|
+
expect(result).toEqual({ name: 'test', value: 123 });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should parse simple string args', () => {
|
|
96
|
+
const raw = 'simple string argument';
|
|
97
|
+
|
|
98
|
+
const result = parser.parseArgs(raw);
|
|
99
|
+
|
|
100
|
+
expect(result).toBe('simple string argument');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('should handle empty args', () => {
|
|
104
|
+
const raw = '';
|
|
105
|
+
|
|
106
|
+
const result = parser.parseArgs(raw);
|
|
107
|
+
|
|
108
|
+
expect(result).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('should handle malformed JSON gracefully', () => {
|
|
112
|
+
const raw = '{invalid json}';
|
|
113
|
+
|
|
114
|
+
const result = parser.parseArgs(raw);
|
|
115
|
+
|
|
116
|
+
expect(result).toBe(raw); // Return raw string on parse failure
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('isTraceLine', () => {
|
|
121
|
+
test('should return true for trace lines', () => {
|
|
122
|
+
const line = '2026-04-08 19:12:36.134 [INFO] [TRACE] >>> method enter: [{}]';
|
|
123
|
+
expect(parser.isTraceLine(line)).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('should return false for non-trace lines', () => {
|
|
127
|
+
const line = '2026-04-08 19:12:36.134 [INFO] Regular log message';
|
|
128
|
+
expect(parser.isTraceLine(line)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('filterByLevel', () => {
|
|
133
|
+
test('should filter by info level', () => {
|
|
134
|
+
const lines = [
|
|
135
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> method enter: [{}]',
|
|
136
|
+
'2026-04-08 19:12:36.134 [DEBUG] [TRACE] >>> method enter: [{}]',
|
|
137
|
+
'2026-04-08 19:12:36.134 [ERROR] [TRACE] >>> method enter: [{}]',
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const filtered = parser.filterByLevel(lines, 'info');
|
|
141
|
+
|
|
142
|
+
expect(filtered).toHaveLength(1);
|
|
143
|
+
expect(filtered[0]).toContain('[INFO]');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('should return all lines if no level specified', () => {
|
|
147
|
+
const lines = [
|
|
148
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> method enter: [{}]',
|
|
149
|
+
'2026-04-08 19:12:36.134 [DEBUG] [TRACE] >>> method enter: [{}]',
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const filtered = parser.filterByLevel(lines);
|
|
153
|
+
|
|
154
|
+
expect(filtered).toHaveLength(2);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('filterByFunction', () => {
|
|
159
|
+
test('should filter by function name', () => {
|
|
160
|
+
const lines = [
|
|
161
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> llm.invoke enter: [{}]',
|
|
162
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> tool.execute enter: [{}]',
|
|
163
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> db.query enter: [{}]',
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const filtered = parser.filterByFunction(lines, 'llm');
|
|
167
|
+
|
|
168
|
+
expect(filtered).toHaveLength(1);
|
|
169
|
+
expect(filtered[0]).toContain('llm.invoke');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('should return all lines if no function specified', () => {
|
|
173
|
+
const lines = [
|
|
174
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> llm.invoke enter: [{}]',
|
|
175
|
+
'2026-04-08 19:12:36.134 [INFO] [TRACE] >>> tool.execute enter: [{}]',
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
const filtered = parser.filterByFunction(lines);
|
|
179
|
+
|
|
180
|
+
expect(filtered).toHaveLength(2);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('extractTraceId', () => {
|
|
185
|
+
test('should extract traceId from category', () => {
|
|
186
|
+
const line = '2026-04-08 19:12:36.134 [INFO] [source][requestId=req_123] [TRACE] >>> method enter: [{}]';
|
|
187
|
+
|
|
188
|
+
const traceId = parser.extractTraceId(line);
|
|
189
|
+
|
|
190
|
+
expect(traceId).toBe('req_123');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('should return null if no traceId found', () => {
|
|
194
|
+
const line = '2026-04-08 19:12:36.134 [INFO] [source] [TRACE] >>> method enter: [{}]';
|
|
195
|
+
|
|
196
|
+
const traceId = parser.extractTraceId(line);
|
|
197
|
+
|
|
198
|
+
expect(traceId).toBeNull();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|