@opensearch-project/agent-health 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -5
- package/cli/dist/index.js +8292 -2938
- package/deployment/cloudformation/agent-health-observability.yaml +223 -32
- package/dist/assets/index-BfxtxmKc.css +1 -0
- package/dist/assets/index-CrjAfDHu.js +243 -0
- package/dist/index.html +2 -2
- package/docs/ARCHITECTURE.md +450 -0
- package/docs/BACKEND_JOB_QUEUE.md +405 -0
- package/docs/CLAUDE_CODE_TELEMETRY.md +283 -0
- package/docs/CLI.md +474 -0
- package/docs/CODING_AGENT_ANALYTICS.md +298 -0
- package/docs/CONFIGURATION.md +388 -0
- package/docs/CONNECTORS.md +536 -0
- package/docs/INSTRUMENT_WITH_OTEL.md +397 -0
- package/docs/ML-COMMONS-SETUP.md +289 -0
- package/docs/NPX_PACKAGING.md +195 -0
- package/docs/PERFORMANCE-MONITORING.md +200 -0
- package/docs/PERFORMANCE.md +390 -0
- package/docs/PI_PROFILING.md +169 -0
- package/docs/PLAN-non-agui-agent-support.md +525 -0
- package/docs/SDK.md +626 -0
- package/docs/SKILLS.md +264 -0
- package/docs/STORAGE_INDEX_FIELD_LIMITS.md +216 -0
- package/docs/blogs/2026-02-28-opensearch-agent-health.md +200 -0
- package/docs/blogs/getting-started-blog.md +608 -0
- package/docs/diagrams/Agent-health.excalidraw +5656 -0
- package/docs/diagrams/architecture.png +0 -0
- package/docs/plans/field-redesign.md +468 -0
- package/docs/rfcs/001-coding-agent-analytics.md +374 -0
- package/docs/rfcs/002-enterprise-leaderboard.md +267 -0
- package/docs/rfcs/003-remote-aggregation.md +146 -0
- package/docs/rfcs/004-test-sdk-v2.md +599 -0
- package/docs/skills/AGENT_HEALTH.md +652 -0
- package/docs/skills/AGENT_PROFILE.md +191 -0
- package/docs/skills/add-connector/SKILL.md +68 -0
- package/docs/skills/agent-health-profile/SKILL.md +40 -0
- package/docs/skills/config-auth/SKILL.md +194 -0
- package/docs/skills/config-auth/evals/evals.json +35 -0
- package/docs/skills/create-pr/SKILL.md +73 -0
- package/docs/skills/instrument-otel/SKILL.md +84 -0
- package/docs/skills/write-test/SKILL.md +124 -0
- package/docs/ui prd.md +376 -0
- package/examples/README.md +53 -0
- package/examples/config/agent-health.config.example.ts +155 -0
- package/examples/connectors/echo-connector.ts +131 -0
- package/examples/eval-files/demo.eval.js +128 -0
- package/examples/eval-files/ops-rca-classification.eval.js +71 -0
- package/examples/eval-files/ops-rca-evaluator.json +15 -0
- package/examples/eval-files/sdk-demo.eval.js +72 -0
- package/examples/eval-files/sdk-describe-demo.eval.js +50 -0
- package/examples/eval-files/sdk-hooks-demo.eval.js +99 -0
- package/examples/pi-profiling/README.md +77 -0
- package/examples/pi-profiling/agent-health-profile.ts +417 -0
- package/lib/dist/lib/agentUtils.d.ts +29 -0
- package/lib/dist/lib/agentUtils.d.ts.map +1 -0
- package/lib/dist/lib/agentUtils.js +43 -0
- package/lib/dist/lib/agentUtils.js.map +1 -0
- package/lib/dist/lib/bedrockCompat.d.ts +27 -0
- package/lib/dist/lib/bedrockCompat.d.ts.map +1 -0
- package/lib/dist/lib/bedrockCompat.js +83 -0
- package/lib/dist/lib/bedrockCompat.js.map +1 -0
- package/lib/dist/lib/benchmarkExport.d.ts +14 -0
- package/lib/dist/lib/benchmarkExport.d.ts.map +1 -0
- package/lib/dist/lib/benchmarkExport.js +41 -0
- package/lib/dist/lib/benchmarkExport.js.map +1 -0
- package/lib/dist/lib/benchmarkImage.d.ts +52 -0
- package/lib/dist/lib/benchmarkImage.d.ts.map +1 -0
- package/lib/dist/lib/benchmarkImage.js +113 -0
- package/lib/dist/lib/benchmarkImage.js.map +1 -0
- package/lib/dist/lib/benchmarkVersionUtils.d.ts +50 -0
- package/lib/dist/lib/benchmarkVersionUtils.d.ts.map +1 -0
- package/lib/dist/lib/benchmarkVersionUtils.js +89 -0
- package/lib/dist/lib/benchmarkVersionUtils.js.map +1 -0
- package/lib/dist/lib/chunkedFetch.d.ts +18 -0
- package/lib/dist/lib/chunkedFetch.d.ts.map +1 -0
- package/lib/dist/lib/chunkedFetch.js +40 -0
- package/lib/dist/lib/chunkedFetch.js.map +1 -0
- package/lib/dist/lib/comparisonInsights.d.ts +104 -0
- package/lib/dist/lib/comparisonInsights.d.ts.map +1 -0
- package/lib/dist/lib/comparisonInsights.js +212 -0
- package/lib/dist/lib/comparisonInsights.js.map +1 -0
- package/lib/dist/lib/config/defineConfig.d.ts +27 -0
- package/lib/dist/lib/config/defineConfig.d.ts.map +1 -0
- package/lib/dist/lib/config/defineConfig.js +28 -0
- package/lib/dist/lib/config/defineConfig.js.map +1 -0
- package/lib/dist/lib/config/index.d.ts +9 -0
- package/lib/dist/lib/config/index.d.ts.map +1 -0
- package/lib/dist/lib/config/index.js +8 -0
- package/lib/dist/lib/config/index.js.map +1 -0
- package/lib/dist/lib/config/loader.d.ts +39 -0
- package/lib/dist/lib/config/loader.d.ts.map +1 -0
- package/lib/dist/lib/config/loader.js +263 -0
- package/lib/dist/lib/config/loader.js.map +1 -0
- package/lib/dist/lib/config/statePaths.d.ts +61 -0
- package/lib/dist/lib/config/statePaths.d.ts.map +1 -0
- package/lib/dist/lib/config/statePaths.js +188 -0
- package/lib/dist/lib/config/statePaths.js.map +1 -0
- package/lib/dist/lib/config/types.d.ts +245 -0
- package/lib/dist/lib/config/types.d.ts.map +1 -0
- package/lib/dist/lib/config/types.js +6 -0
- package/lib/dist/lib/config/types.js.map +1 -0
- package/lib/dist/lib/config.d.ts +39 -0
- package/lib/dist/lib/config.d.ts.map +1 -0
- package/lib/dist/lib/config.js +118 -0
- package/lib/dist/lib/config.js.map +1 -0
- package/lib/dist/lib/constants.d.ts +81 -0
- package/lib/dist/lib/constants.d.ts.map +1 -0
- package/lib/dist/lib/constants.js +374 -0
- package/lib/dist/lib/constants.js.map +1 -0
- package/lib/dist/lib/contextFormat.d.ts +26 -0
- package/lib/dist/lib/contextFormat.d.ts.map +1 -0
- package/lib/dist/lib/contextFormat.js +28 -0
- package/lib/dist/lib/contextFormat.js.map +1 -0
- package/lib/dist/lib/contextUtilization.d.ts +23 -0
- package/lib/dist/lib/contextUtilization.d.ts.map +1 -0
- package/lib/dist/lib/contextUtilization.js +72 -0
- package/lib/dist/lib/contextUtilization.js.map +1 -0
- package/lib/dist/lib/dashboardMetrics.d.ts +87 -0
- package/lib/dist/lib/dashboardMetrics.d.ts.map +1 -0
- package/lib/dist/lib/dashboardMetrics.js +242 -0
- package/lib/dist/lib/dashboardMetrics.js.map +1 -0
- package/lib/dist/lib/dataSourceConfig.d.ts +108 -0
- package/lib/dist/lib/dataSourceConfig.d.ts.map +1 -0
- package/lib/dist/lib/dataSourceConfig.js +166 -0
- package/lib/dist/lib/dataSourceConfig.js.map +1 -0
- package/lib/dist/lib/debug.d.ts +26 -0
- package/lib/dist/lib/debug.d.ts.map +1 -0
- package/lib/dist/lib/debug.js +132 -0
- package/lib/dist/lib/debug.js.map +1 -0
- package/lib/dist/lib/diagnostics.d.ts +28 -0
- package/lib/dist/lib/diagnostics.d.ts.map +1 -0
- package/lib/dist/lib/diagnostics.js +65 -0
- package/lib/dist/lib/diagnostics.js.map +1 -0
- package/lib/dist/lib/envCompat.d.ts +27 -0
- package/lib/dist/lib/envCompat.d.ts.map +1 -0
- package/lib/dist/lib/envCompat.js +82 -0
- package/lib/dist/lib/envCompat.js.map +1 -0
- package/lib/dist/lib/evaluationRerun.d.ts +63 -0
- package/lib/dist/lib/evaluationRerun.d.ts.map +1 -0
- package/lib/dist/lib/evaluationRerun.js +85 -0
- package/lib/dist/lib/evaluationRerun.js.map +1 -0
- package/lib/dist/lib/findPackageRoot.d.ts +7 -0
- package/lib/dist/lib/findPackageRoot.d.ts.map +1 -0
- package/lib/dist/lib/findPackageRoot.js +57 -0
- package/lib/dist/lib/findPackageRoot.js.map +1 -0
- package/lib/dist/lib/hooks.d.ts +36 -0
- package/lib/dist/lib/hooks.d.ts.map +1 -0
- package/lib/dist/lib/hooks.js +112 -0
- package/lib/dist/lib/hooks.js.map +1 -0
- package/lib/dist/lib/index.d.ts +47 -0
- package/lib/dist/lib/index.d.ts.map +1 -0
- package/lib/dist/lib/index.js +62 -0
- package/lib/dist/lib/index.js.map +1 -0
- package/lib/dist/lib/labels.d.ts +90 -0
- package/lib/dist/lib/labels.d.ts.map +1 -0
- package/lib/dist/lib/labels.js +158 -0
- package/lib/dist/lib/labels.js.map +1 -0
- package/lib/dist/lib/markdown.d.ts +16 -0
- package/lib/dist/lib/markdown.d.ts.map +1 -0
- package/lib/dist/lib/markdown.js +42 -0
- package/lib/dist/lib/markdown.js.map +1 -0
- package/lib/dist/lib/matchers/expect.d.ts +3 -0
- package/lib/dist/lib/matchers/expect.d.ts.map +1 -0
- package/lib/dist/lib/matchers/expect.js +225 -0
- package/lib/dist/lib/matchers/expect.js.map +1 -0
- package/lib/dist/lib/matchers/index.d.ts +8 -0
- package/lib/dist/lib/matchers/index.d.ts.map +1 -0
- package/lib/dist/lib/matchers/index.js +9 -0
- package/lib/dist/lib/matchers/index.js.map +1 -0
- package/lib/dist/lib/matchers/judgeAccessor.d.ts +113 -0
- package/lib/dist/lib/matchers/judgeAccessor.d.ts.map +1 -0
- package/lib/dist/lib/matchers/judgeAccessor.js +183 -0
- package/lib/dist/lib/matchers/judgeAccessor.js.map +1 -0
- package/lib/dist/lib/matchers/session.d.ts +39 -0
- package/lib/dist/lib/matchers/session.d.ts.map +1 -0
- package/lib/dist/lib/matchers/session.js +116 -0
- package/lib/dist/lib/matchers/session.js.map +1 -0
- package/lib/dist/lib/matchers/traces.d.ts +70 -0
- package/lib/dist/lib/matchers/traces.d.ts.map +1 -0
- package/lib/dist/lib/matchers/traces.js +236 -0
- package/lib/dist/lib/matchers/traces.js.map +1 -0
- package/lib/dist/lib/matchers/tracesPricing.d.ts +38 -0
- package/lib/dist/lib/matchers/tracesPricing.d.ts.map +1 -0
- package/lib/dist/lib/matchers/tracesPricing.js +64 -0
- package/lib/dist/lib/matchers/tracesPricing.js.map +1 -0
- package/lib/dist/lib/matchers/types.d.ts +75 -0
- package/lib/dist/lib/matchers/types.d.ts.map +1 -0
- package/lib/dist/lib/matchers/types.js +6 -0
- package/lib/dist/lib/matchers/types.js.map +1 -0
- package/lib/dist/lib/packagePaths.d.ts +29 -0
- package/lib/dist/lib/packagePaths.d.ts.map +1 -0
- package/lib/dist/lib/packagePaths.js +63 -0
- package/lib/dist/lib/packagePaths.js.map +1 -0
- package/lib/dist/lib/performance.d.ts +51 -0
- package/lib/dist/lib/performance.d.ts.map +1 -0
- package/lib/dist/lib/performance.js +159 -0
- package/lib/dist/lib/performance.js.map +1 -0
- package/lib/dist/lib/portConfig.d.ts +29 -0
- package/lib/dist/lib/portConfig.d.ts.map +1 -0
- package/lib/dist/lib/portConfig.js +64 -0
- package/lib/dist/lib/portConfig.js.map +1 -0
- package/lib/dist/lib/preferences.d.ts +63 -0
- package/lib/dist/lib/preferences.d.ts.map +1 -0
- package/lib/dist/lib/preferences.js +117 -0
- package/lib/dist/lib/preferences.js.map +1 -0
- package/lib/dist/lib/resolveAgentModel.d.ts +22 -0
- package/lib/dist/lib/resolveAgentModel.d.ts.map +1 -0
- package/lib/dist/lib/resolveAgentModel.js +37 -0
- package/lib/dist/lib/resolveAgentModel.js.map +1 -0
- package/lib/dist/lib/runStats.d.ts +116 -0
- package/lib/dist/lib/runStats.d.ts.map +1 -0
- package/lib/dist/lib/runStats.js +192 -0
- package/lib/dist/lib/runStats.js.map +1 -0
- package/lib/dist/lib/telemetry/constants.d.ts +60 -0
- package/lib/dist/lib/telemetry/constants.d.ts.map +1 -0
- package/lib/dist/lib/telemetry/constants.js +87 -0
- package/lib/dist/lib/telemetry/constants.js.map +1 -0
- package/lib/dist/lib/telemetry/evalSpans.d.ts +61 -0
- package/lib/dist/lib/telemetry/evalSpans.d.ts.map +1 -0
- package/lib/dist/lib/telemetry/evalSpans.js +254 -0
- package/lib/dist/lib/telemetry/evalSpans.js.map +1 -0
- package/lib/dist/lib/telemetry/index.d.ts +11 -0
- package/lib/dist/lib/telemetry/index.d.ts.map +1 -0
- package/lib/dist/lib/telemetry/index.js +15 -0
- package/lib/dist/lib/telemetry/index.js.map +1 -0
- package/lib/dist/lib/telemetry/opensearchExporter.d.ts +43 -0
- package/lib/dist/lib/telemetry/opensearchExporter.d.ts.map +1 -0
- package/lib/dist/lib/telemetry/opensearchExporter.js +217 -0
- package/lib/dist/lib/telemetry/opensearchExporter.js.map +1 -0
- package/lib/dist/lib/telemetry/provider.d.ts +55 -0
- package/lib/dist/lib/telemetry/provider.d.ts.map +1 -0
- package/lib/dist/lib/telemetry/provider.js +140 -0
- package/lib/dist/lib/telemetry/provider.js.map +1 -0
- package/lib/dist/lib/testCaseLabels.d.ts +34 -0
- package/lib/dist/lib/testCaseLabels.d.ts.map +1 -0
- package/lib/dist/lib/testCaseLabels.js +88 -0
- package/lib/dist/lib/testCaseLabels.js.map +1 -0
- package/lib/dist/lib/testCaseValidation.d.ts +140 -0
- package/lib/dist/lib/testCaseValidation.d.ts.map +1 -0
- package/lib/dist/lib/testCaseValidation.js +162 -0
- package/lib/dist/lib/testCaseValidation.js.map +1 -0
- package/lib/dist/lib/testCases/agentFixture.d.ts +80 -0
- package/lib/dist/lib/testCases/agentFixture.d.ts.map +1 -0
- package/lib/dist/lib/testCases/agentFixture.js +43 -0
- package/lib/dist/lib/testCases/agentFixture.js.map +1 -0
- package/lib/dist/lib/testCases/authoringSurface.d.ts +10 -0
- package/lib/dist/lib/testCases/authoringSurface.d.ts.map +1 -0
- package/lib/dist/lib/testCases/authoringSurface.js +54 -0
- package/lib/dist/lib/testCases/authoringSurface.js.map +1 -0
- package/lib/dist/lib/testCases/codemod.d.ts +13 -0
- package/lib/dist/lib/testCases/codemod.d.ts.map +1 -0
- package/lib/dist/lib/testCases/codemod.js +169 -0
- package/lib/dist/lib/testCases/codemod.js.map +1 -0
- package/lib/dist/lib/testCases/define.d.ts +114 -0
- package/lib/dist/lib/testCases/define.d.ts.map +1 -0
- package/lib/dist/lib/testCases/define.js +253 -0
- package/lib/dist/lib/testCases/define.js.map +1 -0
- package/lib/dist/lib/testCases/evaluators.d.ts +80 -0
- package/lib/dist/lib/testCases/evaluators.d.ts.map +1 -0
- package/lib/dist/lib/testCases/evaluators.js +105 -0
- package/lib/dist/lib/testCases/evaluators.js.map +1 -0
- package/lib/dist/lib/testCases/index.d.ts +14 -0
- package/lib/dist/lib/testCases/index.d.ts.map +1 -0
- package/lib/dist/lib/testCases/index.js +12 -0
- package/lib/dist/lib/testCases/index.js.map +1 -0
- package/lib/dist/lib/testCases/judge.d.ts +165 -0
- package/lib/dist/lib/testCases/judge.d.ts.map +1 -0
- package/lib/dist/lib/testCases/judge.js +359 -0
- package/lib/dist/lib/testCases/judge.js.map +1 -0
- package/lib/dist/lib/testCases/loader.d.ts +36 -0
- package/lib/dist/lib/testCases/loader.d.ts.map +1 -0
- package/lib/dist/lib/testCases/loader.js +179 -0
- package/lib/dist/lib/testCases/loader.js.map +1 -0
- package/lib/dist/lib/testCases/types.d.ts +242 -0
- package/lib/dist/lib/testCases/types.d.ts.map +1 -0
- package/lib/dist/lib/testCases/types.js +6 -0
- package/lib/dist/lib/testCases/types.js.map +1 -0
- package/lib/dist/lib/theme.d.ts +6 -0
- package/lib/dist/lib/theme.d.ts.map +1 -0
- package/lib/dist/lib/theme.js +36 -0
- package/lib/dist/lib/theme.js.map +1 -0
- package/lib/dist/lib/uiTelemetry.d.ts +7 -0
- package/lib/dist/lib/uiTelemetry.d.ts.map +1 -0
- package/lib/dist/lib/uiTelemetry.js +25 -0
- package/lib/dist/lib/uiTelemetry.js.map +1 -0
- package/lib/dist/lib/utils.d.ts +111 -0
- package/lib/dist/lib/utils.d.ts.map +1 -0
- package/lib/dist/lib/utils.js +254 -0
- package/lib/dist/lib/utils.js.map +1 -0
- package/lib/dist/lib/workflow/consolidate.d.ts +12 -0
- package/lib/dist/lib/workflow/consolidate.d.ts.map +1 -0
- package/lib/dist/lib/workflow/consolidate.js +33 -0
- package/lib/dist/lib/workflow/consolidate.js.map +1 -0
- package/lib/dist/lib/workflow/index.d.ts +13 -0
- package/lib/dist/lib/workflow/index.d.ts.map +1 -0
- package/lib/dist/lib/workflow/index.js +12 -0
- package/lib/dist/lib/workflow/index.js.map +1 -0
- package/lib/dist/lib/workflow/ledger.d.ts +30 -0
- package/lib/dist/lib/workflow/ledger.d.ts.map +1 -0
- package/lib/dist/lib/workflow/ledger.js +41 -0
- package/lib/dist/lib/workflow/ledger.js.map +1 -0
- package/lib/dist/lib/workflow/pool.d.ts +13 -0
- package/lib/dist/lib/workflow/pool.d.ts.map +1 -0
- package/lib/dist/lib/workflow/pool.js +44 -0
- package/lib/dist/lib/workflow/pool.js.map +1 -0
- package/lib/dist/lib/workflow/source.d.ts +22 -0
- package/lib/dist/lib/workflow/source.d.ts.map +1 -0
- package/lib/dist/lib/workflow/source.js +29 -0
- package/lib/dist/lib/workflow/source.js.map +1 -0
- package/lib/dist/lib/workflow/stepB.d.ts +71 -0
- package/lib/dist/lib/workflow/stepB.d.ts.map +1 -0
- package/lib/dist/lib/workflow/stepB.js +99 -0
- package/lib/dist/lib/workflow/stepB.js.map +1 -0
- package/lib/dist/lib/workflow/types.d.ts +86 -0
- package/lib/dist/lib/workflow/types.d.ts.map +1 -0
- package/lib/dist/lib/workflow/types.js +6 -0
- package/lib/dist/lib/workflow/types.js.map +1 -0
- package/lib/dist/lib/workflow/workflow.d.ts +119 -0
- package/lib/dist/lib/workflow/workflow.d.ts.map +1 -0
- package/lib/dist/lib/workflow/workflow.js +195 -0
- package/lib/dist/lib/workflow/workflow.js.map +1 -0
- package/lib/dist/services/agent/aguiConverter.d.ts +50 -0
- package/lib/dist/services/agent/aguiConverter.d.ts.map +1 -0
- package/lib/dist/services/agent/aguiConverter.js +449 -0
- package/lib/dist/services/agent/aguiConverter.js.map +1 -0
- package/lib/dist/services/agent/index.d.ts +10 -0
- package/lib/dist/services/agent/index.d.ts.map +1 -0
- package/lib/dist/services/agent/index.js +12 -0
- package/lib/dist/services/agent/index.js.map +1 -0
- package/lib/dist/services/agent/payloadBuilder.d.ts +33 -0
- package/lib/dist/services/agent/payloadBuilder.d.ts.map +1 -0
- package/lib/dist/services/agent/payloadBuilder.js +75 -0
- package/lib/dist/services/agent/payloadBuilder.js.map +1 -0
- package/lib/dist/services/agent/sseStream.d.ts +43 -0
- package/lib/dist/services/agent/sseStream.d.ts.map +1 -0
- package/lib/dist/services/agent/sseStream.js +223 -0
- package/lib/dist/services/agent/sseStream.js.map +1 -0
- package/lib/dist/services/connectors/agui/AGUIStreamingConnector.d.ts +44 -0
- package/lib/dist/services/connectors/agui/AGUIStreamingConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/agui/AGUIStreamingConnector.js +95 -0
- package/lib/dist/services/connectors/agui/AGUIStreamingConnector.js.map +1 -0
- package/lib/dist/services/connectors/base/BaseConnector.d.ts +81 -0
- package/lib/dist/services/connectors/base/BaseConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/base/BaseConnector.js +170 -0
- package/lib/dist/services/connectors/base/BaseConnector.js.map +1 -0
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.d.ts +126 -0
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.js +417 -0
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.js.map +1 -0
- package/lib/dist/services/connectors/index.d.ts +13 -0
- package/lib/dist/services/connectors/index.d.ts.map +1 -0
- package/lib/dist/services/connectors/index.js +32 -0
- package/lib/dist/services/connectors/index.js.map +1 -0
- package/lib/dist/services/connectors/kiro/KiroConnector.d.ts +48 -0
- package/lib/dist/services/connectors/kiro/KiroConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/kiro/KiroConnector.js +158 -0
- package/lib/dist/services/connectors/kiro/KiroConnector.js.map +1 -0
- package/lib/dist/services/connectors/langgraph/LangGraphConnector.d.ts +36 -0
- package/lib/dist/services/connectors/langgraph/LangGraphConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/langgraph/LangGraphConnector.js +175 -0
- package/lib/dist/services/connectors/langgraph/LangGraphConnector.js.map +1 -0
- package/lib/dist/services/connectors/mock/MockConnector.d.ts +37 -0
- package/lib/dist/services/connectors/mock/MockConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/mock/MockConnector.js +120 -0
- package/lib/dist/services/connectors/mock/MockConnector.js.map +1 -0
- package/lib/dist/services/connectors/openai-compatible/OpenAICompatibleConnector.d.ts +42 -0
- package/lib/dist/services/connectors/openai-compatible/OpenAICompatibleConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/openai-compatible/OpenAICompatibleConnector.js +133 -0
- package/lib/dist/services/connectors/openai-compatible/OpenAICompatibleConnector.js.map +1 -0
- package/lib/dist/services/connectors/pi/PiConnector.d.ts +87 -0
- package/lib/dist/services/connectors/pi/PiConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/pi/PiConnector.js +274 -0
- package/lib/dist/services/connectors/pi/PiConnector.js.map +1 -0
- package/lib/dist/services/connectors/registry.d.ts +57 -0
- package/lib/dist/services/connectors/registry.d.ts.map +1 -0
- package/lib/dist/services/connectors/registry.js +106 -0
- package/lib/dist/services/connectors/registry.js.map +1 -0
- package/lib/dist/services/connectors/rest/RESTConnector.d.ts +38 -0
- package/lib/dist/services/connectors/rest/RESTConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/rest/RESTConnector.js +117 -0
- package/lib/dist/services/connectors/rest/RESTConnector.js.map +1 -0
- package/lib/dist/services/connectors/server.d.ts +13 -0
- package/lib/dist/services/connectors/server.d.ts.map +1 -0
- package/lib/dist/services/connectors/server.js +34 -0
- package/lib/dist/services/connectors/server.js.map +1 -0
- package/lib/dist/services/connectors/strands/StrandsConnector.d.ts +48 -0
- package/lib/dist/services/connectors/strands/StrandsConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/strands/StrandsConnector.js +221 -0
- package/lib/dist/services/connectors/strands/StrandsConnector.js.map +1 -0
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.d.ts +88 -0
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.d.ts.map +1 -0
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.js +426 -0
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.js.map +1 -0
- package/lib/dist/services/connectors/types.d.ts +213 -0
- package/lib/dist/services/connectors/types.d.ts.map +1 -0
- package/lib/dist/services/connectors/types.js +6 -0
- package/lib/dist/services/connectors/types.js.map +1 -0
- package/lib/dist/services/evaluation/bedrockJudge.d.ts +71 -0
- package/lib/dist/services/evaluation/bedrockJudge.d.ts.map +1 -0
- package/lib/dist/services/evaluation/bedrockJudge.js +169 -0
- package/lib/dist/services/evaluation/bedrockJudge.js.map +1 -0
- package/lib/dist/services/evaluation/evaluatorError.d.ts +56 -0
- package/lib/dist/services/evaluation/evaluatorError.d.ts.map +1 -0
- package/lib/dist/services/evaluation/evaluatorError.js +56 -0
- package/lib/dist/services/evaluation/evaluatorError.js.map +1 -0
- package/lib/dist/services/evaluation/index.d.ts +106 -0
- package/lib/dist/services/evaluation/index.d.ts.map +1 -0
- package/lib/dist/services/evaluation/index.js +684 -0
- package/lib/dist/services/evaluation/index.js.map +1 -0
- package/lib/dist/services/evaluation/mockTrajectory.d.ts +3 -0
- package/lib/dist/services/evaluation/mockTrajectory.d.ts.map +1 -0
- package/lib/dist/services/evaluation/mockTrajectory.js +72 -0
- package/lib/dist/services/evaluation/mockTrajectory.js.map +1 -0
- package/lib/dist/services/opensearch/client.d.ts +26 -0
- package/lib/dist/services/opensearch/client.d.ts.map +1 -0
- package/lib/dist/services/opensearch/client.js +131 -0
- package/lib/dist/services/opensearch/client.js.map +1 -0
- package/lib/dist/services/opensearch/index.d.ts +16 -0
- package/lib/dist/services/opensearch/index.d.ts.map +1 -0
- package/lib/dist/services/opensearch/index.js +25 -0
- package/lib/dist/services/opensearch/index.js.map +1 -0
- package/lib/dist/services/storage/asyncBenchmarkStorage.d.ts +123 -0
- package/lib/dist/services/storage/asyncBenchmarkStorage.d.ts.map +1 -0
- package/lib/dist/services/storage/asyncBenchmarkStorage.js +440 -0
- package/lib/dist/services/storage/asyncBenchmarkStorage.js.map +1 -0
- package/lib/dist/services/storage/asyncRunStorage.d.ts +135 -0
- package/lib/dist/services/storage/asyncRunStorage.d.ts.map +1 -0
- package/lib/dist/services/storage/asyncRunStorage.js +524 -0
- package/lib/dist/services/storage/asyncRunStorage.js.map +1 -0
- package/lib/dist/services/storage/asyncTestCaseStorage.d.ts +170 -0
- package/lib/dist/services/storage/asyncTestCaseStorage.d.ts.map +1 -0
- package/lib/dist/services/storage/asyncTestCaseStorage.js +301 -0
- package/lib/dist/services/storage/asyncTestCaseStorage.js.map +1 -0
- package/lib/dist/services/storage/index.d.ts +17 -0
- package/lib/dist/services/storage/index.d.ts.map +1 -0
- package/lib/dist/services/storage/index.js +20 -0
- package/lib/dist/services/storage/index.js.map +1 -0
- package/lib/dist/services/storage/migration.d.ts +54 -0
- package/lib/dist/services/storage/migration.d.ts.map +1 -0
- package/lib/dist/services/storage/migration.js +296 -0
- package/lib/dist/services/storage/migration.js.map +1 -0
- package/lib/dist/services/storage/opensearchClient.d.ts +947 -0
- package/lib/dist/services/storage/opensearchClient.d.ts.map +1 -0
- package/lib/dist/services/storage/opensearchClient.js +442 -0
- package/lib/dist/services/storage/opensearchClient.js.map +1 -0
- package/lib/dist/services/traces/browserRecovery.d.ts +37 -0
- package/lib/dist/services/traces/browserRecovery.d.ts.map +1 -0
- package/lib/dist/services/traces/browserRecovery.js +108 -0
- package/lib/dist/services/traces/browserRecovery.js.map +1 -0
- package/lib/dist/services/traces/categoryStyles.d.ts +21 -0
- package/lib/dist/services/traces/categoryStyles.d.ts.map +1 -0
- package/lib/dist/services/traces/categoryStyles.js +56 -0
- package/lib/dist/services/traces/categoryStyles.js.map +1 -0
- package/lib/dist/services/traces/executionOrderTransform.d.ts +35 -0
- package/lib/dist/services/traces/executionOrderTransform.d.ts.map +1 -0
- package/lib/dist/services/traces/executionOrderTransform.js +313 -0
- package/lib/dist/services/traces/executionOrderTransform.js.map +1 -0
- package/lib/dist/services/traces/fetchSpansForRun.d.ts +86 -0
- package/lib/dist/services/traces/fetchSpansForRun.d.ts.map +1 -0
- package/lib/dist/services/traces/fetchSpansForRun.js +69 -0
- package/lib/dist/services/traces/fetchSpansForRun.js.map +1 -0
- package/lib/dist/services/traces/flowTransform.d.ts +24 -0
- package/lib/dist/services/traces/flowTransform.d.ts.map +1 -0
- package/lib/dist/services/traces/flowTransform.js +228 -0
- package/lib/dist/services/traces/flowTransform.js.map +1 -0
- package/lib/dist/services/traces/index.d.ts +121 -0
- package/lib/dist/services/traces/index.d.ts.map +1 -0
- package/lib/dist/services/traces/index.js +255 -0
- package/lib/dist/services/traces/index.js.map +1 -0
- package/lib/dist/services/traces/intentTransform.d.ts +20 -0
- package/lib/dist/services/traces/intentTransform.d.ts.map +1 -0
- package/lib/dist/services/traces/intentTransform.js +131 -0
- package/lib/dist/services/traces/intentTransform.js.map +1 -0
- package/lib/dist/services/traces/judgeAgentsHints.d.ts +63 -0
- package/lib/dist/services/traces/judgeAgentsHints.d.ts.map +1 -0
- package/lib/dist/services/traces/judgeAgentsHints.js +89 -0
- package/lib/dist/services/traces/judgeAgentsHints.js.map +1 -0
- package/lib/dist/services/traces/messageExtraction.d.ts +15 -0
- package/lib/dist/services/traces/messageExtraction.d.ts.map +1 -0
- package/lib/dist/services/traces/messageExtraction.js +313 -0
- package/lib/dist/services/traces/messageExtraction.js.map +1 -0
- package/lib/dist/services/traces/spanCategorization.d.ts +63 -0
- package/lib/dist/services/traces/spanCategorization.d.ts.map +1 -0
- package/lib/dist/services/traces/spanCategorization.js +276 -0
- package/lib/dist/services/traces/spanCategorization.js.map +1 -0
- package/lib/dist/services/traces/spanPreprocessing.d.ts +37 -0
- package/lib/dist/services/traces/spanPreprocessing.d.ts.map +1 -0
- package/lib/dist/services/traces/spanPreprocessing.js +102 -0
- package/lib/dist/services/traces/spanPreprocessing.js.map +1 -0
- package/lib/dist/services/traces/spansToTrajectory.d.ts +36 -0
- package/lib/dist/services/traces/spansToTrajectory.d.ts.map +1 -0
- package/lib/dist/services/traces/spansToTrajectory.js +425 -0
- package/lib/dist/services/traces/spansToTrajectory.js.map +1 -0
- package/lib/dist/services/traces/toolSimilarity.d.ts +35 -0
- package/lib/dist/services/traces/toolSimilarity.d.ts.map +1 -0
- package/lib/dist/services/traces/toolSimilarity.js +203 -0
- package/lib/dist/services/traces/toolSimilarity.js.map +1 -0
- package/lib/dist/services/traces/traceComparison.d.ts +31 -0
- package/lib/dist/services/traces/traceComparison.d.ts.map +1 -0
- package/lib/dist/services/traces/traceComparison.js +318 -0
- package/lib/dist/services/traces/traceComparison.js.map +1 -0
- package/lib/dist/services/traces/traceGrouping.d.ts +19 -0
- package/lib/dist/services/traces/traceGrouping.d.ts.map +1 -0
- package/lib/dist/services/traces/traceGrouping.js +107 -0
- package/lib/dist/services/traces/traceGrouping.js.map +1 -0
- package/lib/dist/services/traces/tracePoller.d.ts +108 -0
- package/lib/dist/services/traces/tracePoller.d.ts.map +1 -0
- package/lib/dist/services/traces/tracePoller.js +475 -0
- package/lib/dist/services/traces/tracePoller.js.map +1 -0
- package/lib/dist/services/traces/traceStats.d.ts +45 -0
- package/lib/dist/services/traces/traceStats.d.ts.map +1 -0
- package/lib/dist/services/traces/traceStats.js +114 -0
- package/lib/dist/services/traces/traceStats.js.map +1 -0
- package/lib/dist/services/traces/traceSummary.d.ts +47 -0
- package/lib/dist/services/traces/traceSummary.d.ts.map +1 -0
- package/lib/dist/services/traces/traceSummary.js +68 -0
- package/lib/dist/services/traces/traceSummary.js.map +1 -0
- package/lib/dist/services/traces/utils.d.ts +33 -0
- package/lib/dist/services/traces/utils.d.ts.map +1 -0
- package/lib/dist/services/traces/utils.js +114 -0
- package/lib/dist/services/traces/utils.js.map +1 -0
- package/lib/dist/types/agui.d.ts +13 -0
- package/lib/dist/types/agui.d.ts.map +1 -0
- package/lib/dist/types/agui.js +16 -0
- package/lib/dist/types/agui.js.map +1 -0
- package/lib/dist/types/index.d.ts +1229 -0
- package/lib/dist/types/index.d.ts.map +1 -0
- package/lib/dist/types/index.js +12 -0
- package/lib/dist/types/index.js.map +1 -0
- package/lib/dist/types/skills.d.ts +146 -0
- package/lib/dist/types/skills.d.ts.map +1 -0
- package/lib/dist/types/skills.js +6 -0
- package/lib/dist/types/skills.js.map +1 -0
- package/observio-sample-agent/pi-package/README.md +112 -0
- package/observio-sample-agent/pi-package/extensions/agent-health.ts +373 -0
- package/observio-sample-agent/pi-package/package.json +17 -0
- package/observio-sample-agent/pi-package/prompts/agent-health.md +37 -0
- package/observio-sample-agent/pi-package/skills/create-pr/SKILL.md +88 -0
- package/observio-sample-agent/pi-package/skills/fix-bug/SKILL.md +71 -0
- package/observio-sample-agent/pi-package/skills/implement-feature/SKILL.md +156 -0
- package/observio-sample-agent/pi-package/skills/instrument-otel/SKILL.md +208 -0
- package/observio-sample-agent/pi-package/skills/setup-collector/SKILL.md +146 -0
- package/observio-sample-agent/pi-package/skills/write-test/SKILL.md +115 -0
- package/package.json +45 -9
- package/server/dist/app.js +34031 -18731
- package/server/dist/index.js +30737 -15338
- package/tsconfig.lib.json +71 -0
- package/dist/assets/index-BOIP5L7h.js +0 -246
- package/dist/assets/index-CU9YKpAL.css +0 -1
- package/lib/dist/config/index.js +0 -452
- package/lib/dist/index.js +0 -1725
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright OpenSearch Contributors
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* SDK lifecycle hooks demo — shows beforeEach/afterEach, beforeAll/afterAll,
|
|
8
|
+
* and the `provide()` mechanism for surfacing per-test provisioned values
|
|
9
|
+
* to the body and the teardown hook.
|
|
10
|
+
*
|
|
11
|
+
* The example uses a temp directory as the "out-of-band resource" — the
|
|
12
|
+
* exact thing connectors can't do today (no lifecycle, no teardown). Each
|
|
13
|
+
* test gets its own directory; concurrent runs are isolated automatically.
|
|
14
|
+
*
|
|
15
|
+
* Run with:
|
|
16
|
+
* AH_PORT=4002 npx @opensearch-project/agent-health benchmark \
|
|
17
|
+
* -f examples/eval-files/sdk-hooks-demo.eval.js -a demo
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
test,
|
|
26
|
+
beforeAll,
|
|
27
|
+
afterAll,
|
|
28
|
+
beforeEach,
|
|
29
|
+
afterEach,
|
|
30
|
+
expect,
|
|
31
|
+
} = require('@opensearch-project/agent-health');
|
|
32
|
+
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
// Suite-scoped: runs once before any test, once after them all.
|
|
35
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
let suiteRoot;
|
|
38
|
+
|
|
39
|
+
beforeAll(() => {
|
|
40
|
+
suiteRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-hooks-demo-'));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterAll(() => {
|
|
44
|
+
if (suiteRoot && fs.existsSync(suiteRoot)) {
|
|
45
|
+
fs.rmSync(suiteRoot, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
// Per-test: each test gets its own subdirectory, surfaced via `provide()`.
|
|
51
|
+
// `afterEach` reads the same key from `provisioned` to clean up, even when
|
|
52
|
+
// the body throws.
|
|
53
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
beforeEach(({ provide, testInfo }) => {
|
|
56
|
+
const safe = testInfo.name.replace(/[^a-z0-9-]+/gi, '_');
|
|
57
|
+
const dir = fs.mkdtempSync(path.join(suiteRoot, `${safe}-`));
|
|
58
|
+
provide('workspaceDir', dir);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach(({ provisioned }) => {
|
|
62
|
+
const dir = provisioned.workspaceDir;
|
|
63
|
+
if (typeof dir === 'string' && fs.existsSync(dir)) {
|
|
64
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
// Tests — read the provisioned value via `fixtures.provisioned`. No prompt
|
|
70
|
+
// is set so the runner skips the agent and runs the body directly; the
|
|
71
|
+
// hooks still run because they don't depend on the agent step.
|
|
72
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
test('hook-demo-uses-workspace', {
|
|
75
|
+
description: 'beforeEach provisioned a fresh temp dir for this test',
|
|
76
|
+
labels: ['category:Smoke', 'feature:hooks'],
|
|
77
|
+
}, ({ provisioned, testInfo }) => {
|
|
78
|
+
expect(provisioned.workspaceDir).to.be.a('string');
|
|
79
|
+
expect(fs.existsSync(provisioned.workspaceDir)).to.equal(true);
|
|
80
|
+
|
|
81
|
+
// The dir's basename must include the test name, proving testInfo flowed
|
|
82
|
+
// through `provide()` correctly.
|
|
83
|
+
expect(path.basename(provisioned.workspaceDir)).to.include(
|
|
84
|
+
testInfo.name.replace(/[^a-z0-9-]+/gi, '_')
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Write a file inside, just to show it's a real, writable workspace.
|
|
88
|
+
fs.writeFileSync(path.join(provisioned.workspaceDir, 'marker'), 'hello');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('hook-demo-isolation', {
|
|
92
|
+
description: 'A second test gets its own isolated workspace',
|
|
93
|
+
labels: ['category:Smoke', 'feature:hooks'],
|
|
94
|
+
}, ({ provisioned }) => {
|
|
95
|
+
expect(provisioned.workspaceDir).to.be.a('string');
|
|
96
|
+
// Concurrent tests would each see their own dir; this one in particular
|
|
97
|
+
// must NOT see the marker file from the other test.
|
|
98
|
+
expect(fs.existsSync(path.join(provisioned.workspaceDir, 'marker'))).to.equal(false);
|
|
99
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* Copyright OpenSearch Contributors
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Pi session profiling — drop-in extension
|
|
7
|
+
|
|
8
|
+
Profile your **pi.dev** coding sessions with Agent Health, the way a CPU profiler
|
|
9
|
+
profiles a running process: instrument a real session, then get back what to fix
|
|
10
|
+
(scored against an evaluator rubric) to improve the agent and your own
|
|
11
|
+
productivity.
|
|
12
|
+
|
|
13
|
+
pi has **no native OpenTelemetry telemetry**, so `agent-health profile` has
|
|
14
|
+
nothing to read for a live pi session. This single file supplies that missing
|
|
15
|
+
half: it instruments the running session (emitting OTel spans) **and** registers
|
|
16
|
+
the `/agent-health-profile` command. It is **zero-dependency** — it speaks OTLP
|
|
17
|
+
over the built-in `fetch`, so there is nothing to `npm install`.
|
|
18
|
+
|
|
19
|
+
## Install (out of the box)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. Drop the extension in (global = every pi session; or .pi/extensions/ per project)
|
|
23
|
+
mkdir -p ~/.pi/agent/extensions
|
|
24
|
+
cp agent-health-profile.ts ~/.pi/agent/extensions/
|
|
25
|
+
|
|
26
|
+
# 2. Point telemetry at a trace store BEFORE starting pi.
|
|
27
|
+
# Simplest: Agent Health in FILE mode (no OpenSearch) — its embedded receiver
|
|
28
|
+
# persists spans and matches sessions out of the box:
|
|
29
|
+
agent-health serve # or any command that boots the server on :4001
|
|
30
|
+
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4001 # /v1/traces is appended
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That's it. Use pi normally, then run `/agent-health-profile` (or
|
|
34
|
+
`/agent-health-profile -e <evaluator-id> -f "focus on X"`).
|
|
35
|
+
|
|
36
|
+
## How it routes (important for OpenSearch users)
|
|
37
|
+
|
|
38
|
+
The extension exports OTLP/JSON to `OTEL_EXPORTER_OTLP_ENDPOINT`:
|
|
39
|
+
|
|
40
|
+
- **File mode (recommended for getting started):** point it at Agent Health's
|
|
41
|
+
embedded `:4001/v1/traces`. Spans persist to disk and `profile` matches them by
|
|
42
|
+
`session.id` directly. Zero infra.
|
|
43
|
+
- **OpenSearch-backed Agent Health:** the embedded `:4001` receiver *drops*
|
|
44
|
+
payloads when a cluster is configured (no split-brain). Point the extension at
|
|
45
|
+
the **same OTLP ingest the cluster reads from** (your OTel Collector / OSIS
|
|
46
|
+
pipeline). The extension emits a standard OTLP `session.id` attribute — it is
|
|
47
|
+
schema-agnostic; what matters is that your ingest and `agent-health profile`'s
|
|
48
|
+
session query agree on where `session.id` lands. Agent Health is standardizing
|
|
49
|
+
on the plain/nested schema (`attributes.session.id`; OSIS
|
|
50
|
+
`trace-analytics-plain-raw`), moving off the legacy `@`-flattened
|
|
51
|
+
`span.attributes.session@id` layout.
|
|
52
|
+
|
|
53
|
+
## Config (env)
|
|
54
|
+
|
|
55
|
+
| Var | Default | Meaning |
|
|
56
|
+
|-----|---------|---------|
|
|
57
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4001` | OTLP traces target (`/v1/traces` appended) |
|
|
58
|
+
| `OTEL_SERVICE_NAME` | `pi-agent` | service.name on spans (keep `pi-agent` for `--service` defaults) |
|
|
59
|
+
| `OTEL_ENABLED` | `true` | set `false` to disable telemetry |
|
|
60
|
+
| `AGENT_HEALTH_REDACT` | unset | `1` → redact prompt / tool I/O |
|
|
61
|
+
| `AGENT_HEALTH_CLI` | `npx @opensearch-project/agent-health` | how the command invokes the CLI |
|
|
62
|
+
|
|
63
|
+
## What it emits
|
|
64
|
+
|
|
65
|
+
| Span | `gen_ai.operation.name` | Notes |
|
|
66
|
+
|------|-------------------------|-------|
|
|
67
|
+
| `invoke_agent pi` (root) | `invoke_agent` | one per session; carries `session.id` |
|
|
68
|
+
| `chat <model>` (per turn) | `chat` | token usage + `llm.request`/`llm.response` events |
|
|
69
|
+
| `execute_tool <name>` | `execute_tool` | `gen_ai.tool.input`/`output`; ERROR status on failure |
|
|
70
|
+
|
|
71
|
+
Every span carries `session.id`; the resource carries `service.name=pi-agent`.
|
|
72
|
+
|
|
73
|
+
## Caveat
|
|
74
|
+
|
|
75
|
+
Profiling only sees sessions that ran **while the extension was loaded and
|
|
76
|
+
telemetry was on** — you can't profile a session retroactively. Run a session,
|
|
77
|
+
then `/agent-health-profile`.
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright OpenSearch Contributors
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Agent Health — pi session profiling extension (single-file, zero-dependency).
|
|
8
|
+
*
|
|
9
|
+
* Drop this one file into `~/.pi/agent/extensions/` (global) or
|
|
10
|
+
* `.pi/extensions/` (project) and pi will auto-load it. No `npm install` — it
|
|
11
|
+
* speaks OTLP/HTTP JSON over the built-in `fetch`, so there are no
|
|
12
|
+
* `@opentelemetry/*` dependencies to resolve.
|
|
13
|
+
*
|
|
14
|
+
* Why it exists: pi has no native OpenTelemetry telemetry (unlike Claude Code's
|
|
15
|
+
* CLAUDE_CODE_ENABLE_TELEMETRY), so `agent-health profile` has nothing to read
|
|
16
|
+
* for a live pi session. This extension supplies that missing half — it
|
|
17
|
+
* instruments the running session and registers the profiling command.
|
|
18
|
+
*
|
|
19
|
+
* What it does:
|
|
20
|
+
* 1. Emits OTel spans for the session (root `invoke_agent pi`, one
|
|
21
|
+
* `chat <model>` span per turn with token usage + prompt/completion, and an
|
|
22
|
+
* `execute_tool <name>` span per tool call). Every span carries
|
|
23
|
+
* `session.id`; the resource carries `service.name=pi-agent`.
|
|
24
|
+
* 2. Records the session id to `.pi/agent-health/current-session` so
|
|
25
|
+
* `agent-health profile` resolves it deterministically.
|
|
26
|
+
* 3. Registers `/agent-health-profile -e <id> [-f "<feedback>"]`, which runs
|
|
27
|
+
* the CLI and feeds the rubric + trajectory + signals back into the chat.
|
|
28
|
+
*
|
|
29
|
+
* Config (env):
|
|
30
|
+
* - OTEL_EXPORTER_OTLP_ENDPOINT where to export (default http://localhost:4001;
|
|
31
|
+
* `/v1/traces` is appended if missing). Point at
|
|
32
|
+
* Agent Health's embedded receiver (file mode),
|
|
33
|
+
* an OTel Collector, or an OSIS pipeline.
|
|
34
|
+
* - OTEL_SERVICE_NAME default `pi-agent`.
|
|
35
|
+
* - OTEL_ENABLED=false disable telemetry (command still explains how).
|
|
36
|
+
* - AGENT_HEALTH_REDACT=1 stamp redaction placeholders instead of capturing
|
|
37
|
+
* prompt / tool I/O.
|
|
38
|
+
* - AGENT_HEALTH_CLI CLI invocation (default `npx @opensearch-project/agent-health`).
|
|
39
|
+
*
|
|
40
|
+
* See docs/skills/AGENT_PROFILE.md.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
44
|
+
import { randomBytes } from 'node:crypto';
|
|
45
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
46
|
+
import { join } from 'node:path';
|
|
47
|
+
|
|
48
|
+
// ─── Pure, exported helpers (unit-tested; no pi/OTel imports) ────────────────
|
|
49
|
+
|
|
50
|
+
export const PI_SERVICE_NAME = 'pi-agent';
|
|
51
|
+
export const PI_SCOPE_NAME = 'agent-health-pi';
|
|
52
|
+
export const DEFAULT_ENDPOINT = 'http://localhost:4001';
|
|
53
|
+
export const DEFAULT_CLI = 'npx @opensearch-project/agent-health';
|
|
54
|
+
export const DEFAULT_EVALUATOR = 'system-rca-default';
|
|
55
|
+
export const REDACTED = '[redacted]';
|
|
56
|
+
|
|
57
|
+
export type OtlpValue = { stringValue: string } | { intValue: string } | { boolValue: boolean };
|
|
58
|
+
export interface OtlpKeyValue { key: string; value: OtlpValue; }
|
|
59
|
+
export interface OtlpEvent { name: string; timeUnixNano: string; attributes: OtlpKeyValue[]; }
|
|
60
|
+
export interface OtlpSpan {
|
|
61
|
+
traceId: string;
|
|
62
|
+
spanId: string;
|
|
63
|
+
parentSpanId?: string;
|
|
64
|
+
name: string;
|
|
65
|
+
kind: number;
|
|
66
|
+
startTimeUnixNano: string;
|
|
67
|
+
endTimeUnixNano: string;
|
|
68
|
+
attributes: OtlpKeyValue[];
|
|
69
|
+
events: OtlpEvent[];
|
|
70
|
+
status: { code: number };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function randomHex(bytes: number): string {
|
|
74
|
+
return randomBytes(bytes).toString('hex');
|
|
75
|
+
}
|
|
76
|
+
export const genTraceId = () => randomHex(16); // 32 hex chars
|
|
77
|
+
export const genSpanId = () => randomHex(8); // 16 hex chars
|
|
78
|
+
|
|
79
|
+
/** Parse a W3C `traceparent` (`00-<trace>-<span>-<flags>`). */
|
|
80
|
+
export function parseTraceparent(tp?: string): { traceId: string; spanId: string } | null {
|
|
81
|
+
if (!tp) return null;
|
|
82
|
+
const m = tp.trim().match(/^[0-9a-f]{2}-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/i);
|
|
83
|
+
return m ? { traceId: m[1].toLowerCase(), spanId: m[2].toLowerCase() } : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Resolve the full OTLP traces URL from a base endpoint. */
|
|
87
|
+
export function resolveEndpoint(base?: string): string {
|
|
88
|
+
const b = (base || DEFAULT_ENDPOINT).replace(/\/$/, '');
|
|
89
|
+
return b.endsWith('/v1/traces') ? b : `${b}/v1/traces`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Stable session id from pi's session file path (basename minus extension). */
|
|
93
|
+
export function sessionIdFromFile(file?: string | null): string | undefined {
|
|
94
|
+
if (!file) return undefined;
|
|
95
|
+
const base = file.split(/[\\/]/).pop() ?? file;
|
|
96
|
+
const stem = base.replace(/\.[^.]+$/, '').trim();
|
|
97
|
+
return stem || undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function numOrUndef(v: unknown): number | undefined {
|
|
101
|
+
const n = Number(v);
|
|
102
|
+
return Number.isFinite(n) ? n : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function truncate(s: unknown, max = 5000): string {
|
|
106
|
+
const str = typeof s === 'string' ? s : safeStringify(s);
|
|
107
|
+
return str.length > max ? `${str.slice(0, max)}…` : str;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function safeStringify(v: unknown): string {
|
|
111
|
+
if (v == null) return '';
|
|
112
|
+
if (typeof v === 'string') return v;
|
|
113
|
+
try { return JSON.stringify(v); } catch { return String(v); }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Pull plain text out of a pi assistant message `content` (string or blocks). */
|
|
117
|
+
export function extractText(content: unknown): string | undefined {
|
|
118
|
+
if (typeof content === 'string') return content || undefined;
|
|
119
|
+
if (!Array.isArray(content)) return undefined;
|
|
120
|
+
const parts = content
|
|
121
|
+
.filter((b): b is { type: string; text: string } =>
|
|
122
|
+
!!b && (b as any).type === 'text' && typeof (b as any).text === 'string')
|
|
123
|
+
.map((b) => b.text);
|
|
124
|
+
return parts.length ? parts.join('\n') : undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Parse `<flag> <value>` (quoted or bare) from a raw command arg string. */
|
|
128
|
+
export function parseFlag(args: string, flags: string[]): string | undefined {
|
|
129
|
+
const names = flags.map((f) => f.replace(/^-+/, '')).join('|');
|
|
130
|
+
const re = new RegExp(`(?:^|\\s)(?:-{1,2})(?:${names})\\s+("([^"]*)"|'([^']*)'|(\\S+))`);
|
|
131
|
+
const m = (args || '').match(re);
|
|
132
|
+
return m ? (m[2] ?? m[3] ?? m[4]) : undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Build the `agent-health profile …` argv (binary + args) for a pi session. */
|
|
136
|
+
export function buildProfileInvocation(opts: {
|
|
137
|
+
cli?: string; sessionId: string; evaluator?: string; feedback?: string; service?: string;
|
|
138
|
+
}): { bin: string; args: string[] } {
|
|
139
|
+
const [bin, ...binArgs] = (opts.cli || DEFAULT_CLI).trim().split(/\s+/);
|
|
140
|
+
const args = [
|
|
141
|
+
...binArgs, 'profile',
|
|
142
|
+
'-e', opts.evaluator || DEFAULT_EVALUATOR,
|
|
143
|
+
'--session', opts.sessionId,
|
|
144
|
+
'--service', opts.service || PI_SERVICE_NAME,
|
|
145
|
+
'--output', 'json',
|
|
146
|
+
];
|
|
147
|
+
if (opts.feedback) args.push('--feedback', opts.feedback);
|
|
148
|
+
return { bin, args };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Extract the JSON object from CLI stdout (tolerant of leading log lines). */
|
|
152
|
+
export function extractJson(stdout: string): unknown | null {
|
|
153
|
+
if (!stdout) return null;
|
|
154
|
+
const start = stdout.indexOf('{');
|
|
155
|
+
const end = stdout.lastIndexOf('}');
|
|
156
|
+
if (start === -1 || end === -1 || end < start) return null;
|
|
157
|
+
try { return JSON.parse(stdout.slice(start, end + 1)); } catch { return null; }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function kv(attributes: Record<string, string | number | boolean>): OtlpKeyValue[] {
|
|
161
|
+
return Object.entries(attributes).map(([key, value]) => ({
|
|
162
|
+
key,
|
|
163
|
+
value: typeof value === 'number'
|
|
164
|
+
? { intValue: String(value) }
|
|
165
|
+
: typeof value === 'boolean'
|
|
166
|
+
? { boolValue: value }
|
|
167
|
+
: { stringValue: String(value) },
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Wrap finished spans into an OTLP/JSON ExportTraceServiceRequest. */
|
|
172
|
+
export function buildOtlpPayload(serviceName: string, spans: OtlpSpan[]) {
|
|
173
|
+
return {
|
|
174
|
+
resourceSpans: [{
|
|
175
|
+
resource: { attributes: kv({ 'service.name': serviceName }) },
|
|
176
|
+
scopeSpans: [{ scope: { name: PI_SCOPE_NAME }, spans }],
|
|
177
|
+
}],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const SPAN_KIND_SERVER = 2;
|
|
182
|
+
const SPAN_KIND_CLIENT = 3;
|
|
183
|
+
const STATUS_OK = 1;
|
|
184
|
+
const STATUS_ERROR = 2;
|
|
185
|
+
|
|
186
|
+
export interface SpanTimes { startNs: string; endNs: string; }
|
|
187
|
+
export function msToNanos(ms: number): string { return `${Math.round(ms)}000000`; }
|
|
188
|
+
|
|
189
|
+
/** Root session span (`invoke_agent pi`). */
|
|
190
|
+
export function buildRootSpan(p: { traceId: string; spanId: string; parentSpanId?: string; sessionId: string; times: SpanTimes }): OtlpSpan {
|
|
191
|
+
return {
|
|
192
|
+
traceId: p.traceId, spanId: p.spanId, ...(p.parentSpanId ? { parentSpanId: p.parentSpanId } : {}),
|
|
193
|
+
name: 'invoke_agent pi', kind: SPAN_KIND_SERVER,
|
|
194
|
+
startTimeUnixNano: p.times.startNs, endTimeUnixNano: p.times.endNs,
|
|
195
|
+
attributes: kv({ 'gen_ai.operation.name': 'invoke_agent', 'gen_ai.system': 'pi', 'gen_ai.agent.name': 'pi', 'session.id': p.sessionId }),
|
|
196
|
+
events: [], status: { code: STATUS_OK },
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Chat (LLM) span for one turn. */
|
|
201
|
+
export function buildChatSpan(p: {
|
|
202
|
+
traceId: string; spanId: string; parentSpanId: string; sessionId: string; times: SpanTimes;
|
|
203
|
+
model: string; inputTokens?: number; outputTokens?: number; userPrompt?: string; completion?: string; redact?: boolean;
|
|
204
|
+
}): OtlpSpan {
|
|
205
|
+
const attrs: Record<string, string | number | boolean> = {
|
|
206
|
+
'gen_ai.operation.name': 'chat', 'gen_ai.system': 'pi', 'gen_ai.request.model': p.model || 'unknown', 'session.id': p.sessionId,
|
|
207
|
+
};
|
|
208
|
+
if (typeof p.inputTokens === 'number') attrs['gen_ai.usage.input_tokens'] = p.inputTokens;
|
|
209
|
+
if (typeof p.outputTokens === 'number') attrs['gen_ai.usage.output_tokens'] = p.outputTokens;
|
|
210
|
+
const events: OtlpEvent[] = [];
|
|
211
|
+
if (p.userPrompt != null) {
|
|
212
|
+
events.push({ name: 'llm.request', timeUnixNano: p.times.startNs, attributes: kv({ 'gen_ai.prompt': p.redact ? REDACTED : truncate(p.userPrompt) }) });
|
|
213
|
+
}
|
|
214
|
+
if (p.completion != null) {
|
|
215
|
+
events.push({ name: 'llm.response', timeUnixNano: p.times.endNs, attributes: kv({ 'llm.completion': p.redact ? REDACTED : truncate(p.completion) }) });
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
traceId: p.traceId, spanId: p.spanId, parentSpanId: p.parentSpanId,
|
|
219
|
+
name: `chat ${p.model || 'pi'}`, kind: SPAN_KIND_CLIENT,
|
|
220
|
+
startTimeUnixNano: p.times.startNs, endTimeUnixNano: p.times.endNs,
|
|
221
|
+
attributes: kv(attrs), events, status: { code: STATUS_OK },
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Tool execution span (`execute_tool <name>`). */
|
|
226
|
+
export function buildToolSpan(p: {
|
|
227
|
+
traceId: string; spanId: string; parentSpanId: string; sessionId: string; times: SpanTimes;
|
|
228
|
+
toolName: string; input?: unknown; output?: unknown; isError?: boolean; redact?: boolean;
|
|
229
|
+
}): OtlpSpan {
|
|
230
|
+
const attrs: Record<string, string | number | boolean> = {
|
|
231
|
+
'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': p.toolName, 'session.id': p.sessionId,
|
|
232
|
+
};
|
|
233
|
+
if (p.input != null) attrs['gen_ai.tool.input'] = p.redact ? REDACTED : truncate(p.input, 3000);
|
|
234
|
+
if (p.output != null) attrs['gen_ai.tool.output'] = p.redact ? REDACTED : truncate(p.output, 3000);
|
|
235
|
+
return {
|
|
236
|
+
traceId: p.traceId, spanId: p.spanId, parentSpanId: p.parentSpanId,
|
|
237
|
+
name: `execute_tool ${p.toolName}`, kind: SPAN_KIND_CLIENT,
|
|
238
|
+
startTimeUnixNano: p.times.startNs, endTimeUnixNano: p.times.endNs,
|
|
239
|
+
attributes: kv(attrs), events: [], status: { code: p.isError ? STATUS_ERROR : STATUS_OK },
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ─── The extension (I/O wiring; not unit-tested) ─────────────────────────────
|
|
244
|
+
|
|
245
|
+
const SESSION_DIR = join('.pi', 'agent-health');
|
|
246
|
+
const SESSION_FILE = join(SESSION_DIR, 'current-session');
|
|
247
|
+
|
|
248
|
+
export default function (pi: ExtensionAPI) {
|
|
249
|
+
let sessionId: string | null = null;
|
|
250
|
+
let traceId: string | null = null;
|
|
251
|
+
let rootSpanId: string | null = null;
|
|
252
|
+
let rootStartNs: string | null = null;
|
|
253
|
+
const buffer: OtlpSpan[] = [];
|
|
254
|
+
|
|
255
|
+
// Per-turn / per-tool scratch state.
|
|
256
|
+
let turnStartMs = 0;
|
|
257
|
+
let pendingUserPrompt: string | undefined;
|
|
258
|
+
let firstTurnOfInvocation = false;
|
|
259
|
+
let lastUsage: { inputTokens?: number; outputTokens?: number } = {};
|
|
260
|
+
let lastCompletion: string | undefined;
|
|
261
|
+
let lastModel = 'unknown';
|
|
262
|
+
const toolStart = new Map<string, { startMs: number; toolName: string; input: unknown }>();
|
|
263
|
+
|
|
264
|
+
const redact = process.env.AGENT_HEALTH_REDACT === '1';
|
|
265
|
+
const endpoint = resolveEndpoint(process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
|
|
266
|
+
const serviceName = process.env.OTEL_SERVICE_NAME || PI_SERVICE_NAME;
|
|
267
|
+
// Opt-in: only emit when an endpoint is explicitly configured, so a
|
|
268
|
+
// globally-installed copy never POSTs unexpectedly for unconfigured sessions.
|
|
269
|
+
const telemetryEnabled = (): boolean =>
|
|
270
|
+
process.env.OTEL_ENABLED !== 'false' && !!process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
271
|
+
|
|
272
|
+
async function flush(): Promise<void> {
|
|
273
|
+
if (!telemetryEnabled() || buffer.length === 0) return;
|
|
274
|
+
const batch = buffer.splice(0, buffer.length);
|
|
275
|
+
try {
|
|
276
|
+
await fetch(endpoint, {
|
|
277
|
+
method: 'POST',
|
|
278
|
+
headers: { 'Content-Type': 'application/json' },
|
|
279
|
+
body: JSON.stringify(buildOtlpPayload(serviceName, batch)),
|
|
280
|
+
});
|
|
281
|
+
} catch {
|
|
282
|
+
// Best-effort: drop the batch rather than block the session.
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function recordSessionId(id: string): void {
|
|
287
|
+
try { mkdirSync(SESSION_DIR, { recursive: true }); writeFileSync(SESSION_FILE, id); } catch { /* best-effort */ }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
pi.on('session_start', async (_e, ctx) => {
|
|
291
|
+
sessionId = sessionIdFromFile(ctx.sessionManager.getSessionFile()) ?? null;
|
|
292
|
+
if (!sessionId) return; // ephemeral session
|
|
293
|
+
recordSessionId(sessionId);
|
|
294
|
+
if (!telemetryEnabled()) return;
|
|
295
|
+
|
|
296
|
+
const parent = parseTraceparent(process.env.TRACEPARENT);
|
|
297
|
+
traceId = parent?.traceId ?? genTraceId();
|
|
298
|
+
rootSpanId = genSpanId();
|
|
299
|
+
rootStartNs = msToNanos(Date.now());
|
|
300
|
+
// Root span is finalized at shutdown; record start now.
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
pi.on('model_select', async (event) => {
|
|
304
|
+
const id = (event as { model?: { id?: string } }).model?.id;
|
|
305
|
+
if (id) lastModel = String(id);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
pi.on('before_agent_start', async (event) => {
|
|
309
|
+
const prompt = (event as { prompt?: unknown }).prompt;
|
|
310
|
+
pendingUserPrompt = typeof prompt === 'string' ? prompt : undefined;
|
|
311
|
+
firstTurnOfInvocation = true;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
pi.on('turn_start', async () => {
|
|
315
|
+
turnStartMs = Date.now();
|
|
316
|
+
lastUsage = {};
|
|
317
|
+
lastCompletion = undefined;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
pi.on('message_end', async (event) => {
|
|
321
|
+
const msg = (event as { message?: any }).message;
|
|
322
|
+
if (!msg || msg.role !== 'assistant') return;
|
|
323
|
+
const usage = msg.usage || {};
|
|
324
|
+
lastUsage = {
|
|
325
|
+
inputTokens: numOrUndef(usage.inputTokens ?? usage.input_tokens ?? usage.promptTokens),
|
|
326
|
+
outputTokens: numOrUndef(usage.outputTokens ?? usage.output_tokens ?? usage.completionTokens),
|
|
327
|
+
};
|
|
328
|
+
lastCompletion = extractText(msg.content);
|
|
329
|
+
if (msg.model) lastModel = String(msg.model);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
pi.on('turn_end', async () => {
|
|
333
|
+
if (!traceId || !rootSpanId || !sessionId) return;
|
|
334
|
+
buffer.push(buildChatSpan({
|
|
335
|
+
traceId, spanId: genSpanId(), parentSpanId: rootSpanId, sessionId,
|
|
336
|
+
times: { startNs: msToNanos(turnStartMs || Date.now()), endNs: msToNanos(Date.now()) },
|
|
337
|
+
model: lastModel, inputTokens: lastUsage.inputTokens, outputTokens: lastUsage.outputTokens,
|
|
338
|
+
userPrompt: firstTurnOfInvocation ? pendingUserPrompt : undefined, completion: lastCompletion, redact,
|
|
339
|
+
}));
|
|
340
|
+
firstTurnOfInvocation = false;
|
|
341
|
+
await flush();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
pi.on('tool_execution_start', async (event) => {
|
|
345
|
+
const e = event as { toolCallId?: string; toolName?: string; args?: unknown };
|
|
346
|
+
toolStart.set(String(e.toolCallId), { startMs: Date.now(), toolName: String(e.toolName ?? 'tool'), input: e.args });
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
pi.on('tool_execution_end', async (event) => {
|
|
350
|
+
if (!traceId || !rootSpanId || !sessionId) return;
|
|
351
|
+
const e = event as { toolCallId?: string; result?: unknown; isError?: boolean };
|
|
352
|
+
const started = toolStart.get(String(e.toolCallId));
|
|
353
|
+
toolStart.delete(String(e.toolCallId));
|
|
354
|
+
buffer.push(buildToolSpan({
|
|
355
|
+
traceId, spanId: genSpanId(), parentSpanId: rootSpanId, sessionId,
|
|
356
|
+
times: { startNs: msToNanos(started?.startMs ?? Date.now()), endNs: msToNanos(Date.now()) },
|
|
357
|
+
toolName: started?.toolName ?? 'tool', input: started?.input, output: e.result, isError: !!e.isError, redact,
|
|
358
|
+
}));
|
|
359
|
+
await flush();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
pi.on('session_shutdown', async () => {
|
|
363
|
+
if (traceId && rootSpanId && sessionId && rootStartNs) {
|
|
364
|
+
buffer.push(buildRootSpan({ traceId, spanId: rootSpanId, parentSpanId: parseTraceparent(process.env.TRACEPARENT)?.spanId, sessionId, times: { startNs: rootStartNs, endNs: msToNanos(Date.now()) } }));
|
|
365
|
+
}
|
|
366
|
+
await flush();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
pi.registerCommand('agent-health-profile', {
|
|
370
|
+
description: 'Profile this pi session against an evaluator rubric and propose fixes',
|
|
371
|
+
handler: async (args, ctx) => {
|
|
372
|
+
const id = sessionId ?? sessionIdFromFile(ctx.sessionManager.getSessionFile());
|
|
373
|
+
if (!id) {
|
|
374
|
+
ctx.ui.notify('No session id (ephemeral session) — start pi without --no-session to profile.', 'warning');
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (!telemetryEnabled()) {
|
|
378
|
+
ctx.ui.notify('Telemetry is off — set OTEL_EXPORTER_OTLP_ENDPOINT (e.g. http://localhost:4001 for file-mode Agent Health) and re-run the session so its traces are captured.', 'warning');
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
await flush(); // ensure the just-finished turns are queryable
|
|
382
|
+
|
|
383
|
+
const { bin, args: cmdArgs } = buildProfileInvocation({
|
|
384
|
+
cli: process.env.AGENT_HEALTH_CLI || DEFAULT_CLI,
|
|
385
|
+
sessionId: id,
|
|
386
|
+
evaluator: parseFlag(args, ['-e', '--evaluator']),
|
|
387
|
+
feedback: parseFlag(args, ['-f', '--feedback']),
|
|
388
|
+
service: serviceName,
|
|
389
|
+
});
|
|
390
|
+
ctx.ui.notify(`Profiling pi session ${id}…`, 'info');
|
|
391
|
+
const result = await pi.exec(bin, cmdArgs, { timeout: 120_000 });
|
|
392
|
+
if (result.code !== 0) {
|
|
393
|
+
ctx.ui.notify(`profile failed (exit ${result.code}): ${truncate(result.stderr || result.stdout, 400)}`, 'error');
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const profile = extractJson(result.stdout);
|
|
397
|
+
if (!profile) {
|
|
398
|
+
ctx.ui.notify('profile produced no JSON — is telemetry reaching the trace store?', 'error');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
pi.sendUserMessage([
|
|
402
|
+
'I ran `agent-health profile` on THIS pi session. Below is the JSON profile:',
|
|
403
|
+
'the evaluator rubric (`evaluator.systemPrompt`), the reconstructed `trajectory`,',
|
|
404
|
+
'deterministic `signals`, and my `userFeedback` (if any).',
|
|
405
|
+
'',
|
|
406
|
+
'Using `evaluator.systemPrompt` as the rubric, review the trajectory + signals',
|
|
407
|
+
'+ THIS conversation (including any corrections I made) + the codebase here.',
|
|
408
|
+
'Produce a prioritized list of concrete edits (file, change, why — cite the',
|
|
409
|
+
'signal/`traceIds`, priority) and apply them on a NEW branch for review.',
|
|
410
|
+
'',
|
|
411
|
+
'```json',
|
|
412
|
+
truncate(JSON.stringify(profile), 60_000),
|
|
413
|
+
'```',
|
|
414
|
+
].join('\n'));
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AgentConfig } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Determines if an agent can be run from the browser UI.
|
|
4
|
+
*
|
|
5
|
+
* Some agents (like Claude Code) require CLI execution because they:
|
|
6
|
+
* - Spawn local processes via child_process.spawn() (Node.js only)
|
|
7
|
+
* - Access local filesystem or environment variables
|
|
8
|
+
* - Cannot operate within browser sandbox security constraints
|
|
9
|
+
*
|
|
10
|
+
* @param agent - The agent configuration to check
|
|
11
|
+
* @returns true if the agent can be run from the browser, false if CLI-only
|
|
12
|
+
*/
|
|
13
|
+
export declare function isBrowserCompatible(agent: AgentConfig): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Gets a user-friendly explanation for why an agent cannot run from the UI.
|
|
16
|
+
*
|
|
17
|
+
* @param agent - The agent configuration to check
|
|
18
|
+
* @returns Error message if agent requires CLI, null if browser-compatible
|
|
19
|
+
*/
|
|
20
|
+
export declare function getAgentUnavailableReason(agent: AgentConfig): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the first browser-compatible agent from a list.
|
|
23
|
+
* Useful for setting default selection in UI components.
|
|
24
|
+
*
|
|
25
|
+
* @param agents - List of agent configurations
|
|
26
|
+
* @returns The first browser-compatible agent, or undefined if none found
|
|
27
|
+
*/
|
|
28
|
+
export declare function getFirstBrowserCompatibleAgent(agents: AgentConfig[]): AgentConfig | undefined;
|
|
29
|
+
//# sourceMappingURL=agentUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentUtils.d.ts","sourceRoot":"","sources":["../../agentUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAG/D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAM3E;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,GAAG,SAAS,CAE7F"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright OpenSearch Contributors
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { BROWSER_SAFE_CONNECTORS } from '../lib/constants.js';
|
|
6
|
+
/**
|
|
7
|
+
* Determines if an agent can be run from the browser UI.
|
|
8
|
+
*
|
|
9
|
+
* Some agents (like Claude Code) require CLI execution because they:
|
|
10
|
+
* - Spawn local processes via child_process.spawn() (Node.js only)
|
|
11
|
+
* - Access local filesystem or environment variables
|
|
12
|
+
* - Cannot operate within browser sandbox security constraints
|
|
13
|
+
*
|
|
14
|
+
* @param agent - The agent configuration to check
|
|
15
|
+
* @returns true if the agent can be run from the browser, false if CLI-only
|
|
16
|
+
*/
|
|
17
|
+
export function isBrowserCompatible(agent) {
|
|
18
|
+
const connectorType = agent.connectorType || 'agui-streaming';
|
|
19
|
+
return BROWSER_SAFE_CONNECTORS.includes(connectorType);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Gets a user-friendly explanation for why an agent cannot run from the UI.
|
|
23
|
+
*
|
|
24
|
+
* @param agent - The agent configuration to check
|
|
25
|
+
* @returns Error message if agent requires CLI, null if browser-compatible
|
|
26
|
+
*/
|
|
27
|
+
export function getAgentUnavailableReason(agent) {
|
|
28
|
+
if (isBrowserCompatible(agent)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return `This agent requires the CLI. Run with: npx @opensearch-project/agent-health run -t <test-case> -a ${agent.key}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Gets the first browser-compatible agent from a list.
|
|
35
|
+
* Useful for setting default selection in UI components.
|
|
36
|
+
*
|
|
37
|
+
* @param agents - List of agent configurations
|
|
38
|
+
* @returns The first browser-compatible agent, or undefined if none found
|
|
39
|
+
*/
|
|
40
|
+
export function getFirstBrowserCompatibleAgent(agents) {
|
|
41
|
+
return agents.find(agent => isBrowserCompatible(agent));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=agentUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentUtils.js","sourceRoot":"","sources":["../../agentUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkB;IACpD,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,gBAAgB,CAAC;IAC9D,OAAO,uBAAuB,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAkB;IAC1D,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,qGAAqG,KAAK,CAAC,GAAG,EAAE,CAAC;AAC1H,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAAC,MAAqB;IAClE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1D,CAAC"}
|