all-hands-cli 0.1.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/.allhands/README.md +75 -0
- package/.allhands/agents/compounder.yaml +15 -0
- package/.allhands/agents/coordinator.yaml +17 -0
- package/.allhands/agents/documentor.yaml +15 -0
- package/.allhands/agents/e2e-test-planner.yaml +17 -0
- package/.allhands/agents/emergent.yaml +22 -0
- package/.allhands/agents/executor.yaml +14 -0
- package/.allhands/agents/ideation.yaml +11 -0
- package/.allhands/agents/initiative-steering.yaml +19 -0
- package/.allhands/agents/judge.yaml +13 -0
- package/.allhands/agents/planner.yaml +19 -0
- package/.allhands/agents/pr-reviewer.yaml +15 -0
- package/.allhands/docs.json +5 -0
- package/.allhands/docs.local.json +26 -0
- package/.allhands/flows/COMPOUNDING.md +203 -0
- package/.allhands/flows/COORDINATION.md +89 -0
- package/.allhands/flows/CORE.md +87 -0
- package/.allhands/flows/DOCUMENTATION.md +218 -0
- package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
- package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
- package/.allhands/flows/IDEATION_SCOPING.md +154 -0
- package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
- package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
- package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
- package/.allhands/flows/PR_REVIEWING.md +43 -0
- package/.allhands/flows/SPEC_PLANNING.md +216 -0
- package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
- package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
- package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
- package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
- package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
- package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
- package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
- package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
- package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
- package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
- package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
- package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
- package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
- package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
- package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
- package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
- package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
- package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
- package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
- package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
- package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
- package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
- package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
- package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
- package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
- package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
- package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
- package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
- package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
- package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
- package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
- package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
- package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
- package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
- package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
- package/.allhands/harness/ah +131 -0
- package/.allhands/harness/package-lock.json +5292 -0
- package/.allhands/harness/package.json +52 -0
- package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
- package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
- package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
- package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
- package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
- package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
- package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
- package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
- package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
- package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
- package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
- package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
- package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
- package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
- package/.allhands/harness/src/cli.ts +36 -0
- package/.allhands/harness/src/commands/complexity.ts +177 -0
- package/.allhands/harness/src/commands/context7.ts +202 -0
- package/.allhands/harness/src/commands/docs.ts +557 -0
- package/.allhands/harness/src/commands/hooks.ts +24 -0
- package/.allhands/harness/src/commands/index.ts +51 -0
- package/.allhands/harness/src/commands/knowledge.ts +382 -0
- package/.allhands/harness/src/commands/memories.ts +302 -0
- package/.allhands/harness/src/commands/notify.ts +61 -0
- package/.allhands/harness/src/commands/oracle.ts +158 -0
- package/.allhands/harness/src/commands/perplexity.ts +220 -0
- package/.allhands/harness/src/commands/planning.ts +245 -0
- package/.allhands/harness/src/commands/schema.ts +73 -0
- package/.allhands/harness/src/commands/skills.ts +128 -0
- package/.allhands/harness/src/commands/solutions.ts +353 -0
- package/.allhands/harness/src/commands/spawn.ts +158 -0
- package/.allhands/harness/src/commands/specs.ts +532 -0
- package/.allhands/harness/src/commands/tavily.ts +226 -0
- package/.allhands/harness/src/commands/tools.ts +579 -0
- package/.allhands/harness/src/commands/trace.ts +327 -0
- package/.allhands/harness/src/commands/tui.ts +960 -0
- package/.allhands/harness/src/commands/validate.ts +143 -0
- package/.allhands/harness/src/commands/validation-tools.ts +108 -0
- package/.allhands/harness/src/hooks/context.ts +1442 -0
- package/.allhands/harness/src/hooks/enforcement.ts +170 -0
- package/.allhands/harness/src/hooks/index.ts +54 -0
- package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
- package/.allhands/harness/src/hooks/notification.ts +104 -0
- package/.allhands/harness/src/hooks/observability.ts +551 -0
- package/.allhands/harness/src/hooks/session.ts +88 -0
- package/.allhands/harness/src/hooks/shared.ts +815 -0
- package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
- package/.allhands/harness/src/hooks/validation.ts +617 -0
- package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
- package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
- package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
- package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
- package/.allhands/harness/src/lib/base-command.ts +198 -0
- package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
- package/.allhands/harness/src/lib/compaction.ts +313 -0
- package/.allhands/harness/src/lib/ctags.ts +497 -0
- package/.allhands/harness/src/lib/docs-validation.ts +907 -0
- package/.allhands/harness/src/lib/event-loop.ts +662 -0
- package/.allhands/harness/src/lib/flows.ts +155 -0
- package/.allhands/harness/src/lib/git.ts +276 -0
- package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
- package/.allhands/harness/src/lib/knowledge.ts +810 -0
- package/.allhands/harness/src/lib/llm.ts +255 -0
- package/.allhands/harness/src/lib/mcp-client.ts +432 -0
- package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
- package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
- package/.allhands/harness/src/lib/notification.ts +115 -0
- package/.allhands/harness/src/lib/opencode/index.ts +70 -0
- package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
- package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
- package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
- package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
- package/.allhands/harness/src/lib/oracle.ts +926 -0
- package/.allhands/harness/src/lib/planning-utils.ts +150 -0
- package/.allhands/harness/src/lib/planning.ts +605 -0
- package/.allhands/harness/src/lib/pr-review.ts +225 -0
- package/.allhands/harness/src/lib/prompts.ts +522 -0
- package/.allhands/harness/src/lib/schema.ts +418 -0
- package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
- package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
- package/.allhands/harness/src/lib/session.ts +164 -0
- package/.allhands/harness/src/lib/specs.ts +348 -0
- package/.allhands/harness/src/lib/tldr.ts +829 -0
- package/.allhands/harness/src/lib/tmux.ts +1051 -0
- package/.allhands/harness/src/lib/trace-store.ts +714 -0
- package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
- package/.allhands/harness/src/mcp/_template.ts +47 -0
- package/.allhands/harness/src/mcp/filesystem.ts +33 -0
- package/.allhands/harness/src/mcp/index.ts +69 -0
- package/.allhands/harness/src/mcp/playwright.ts +34 -0
- package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
- package/.allhands/harness/src/schemas/docs.schema.json +44 -0
- package/.allhands/harness/src/schemas/settings.schema.json +214 -0
- package/.allhands/harness/src/tui/actions.ts +227 -0
- package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
- package/.allhands/harness/src/tui/index.ts +1574 -0
- package/.allhands/harness/src/tui/modal.ts +232 -0
- package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
- package/.allhands/harness/src/tui/status-pane.ts +434 -0
- package/.allhands/harness/tsconfig.json +22 -0
- package/.allhands/harness/vitest.config.ts +13 -0
- package/.allhands/pillars.md +33 -0
- package/.allhands/principles.md +88 -0
- package/.allhands/schemas/alignment.yaml +51 -0
- package/.allhands/schemas/documentation.yaml +10 -0
- package/.allhands/schemas/prompt.yaml +92 -0
- package/.allhands/schemas/skill.yaml +34 -0
- package/.allhands/schemas/solution.yaml +131 -0
- package/.allhands/schemas/spec.yaml +67 -0
- package/.allhands/schemas/validation-suite.yaml +49 -0
- package/.allhands/schemas/workflow.yaml +51 -0
- package/.allhands/settings.json +57 -0
- package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
- package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
- package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
- package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
- package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
- package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
- package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
- package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
- package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
- package/.allhands/validation/browser-automation.md +109 -0
- package/.allhands/validation/xcode-automation.md +195 -0
- package/.allhands/workflows/documentation.md +86 -0
- package/.allhands/workflows/investigation.md +81 -0
- package/.allhands/workflows/milestone.md +91 -0
- package/.allhands/workflows/optimization.md +85 -0
- package/.allhands/workflows/refactor.md +99 -0
- package/.allhands/workflows/triage.md +81 -0
- package/.claude/README.md +1 -0
- package/.claude/agents/explorer.md +10 -0
- package/.claude/agents/researcher.md +11 -0
- package/.claude/agents/task-runner.md +8 -0
- package/.claude/settings.json +231 -0
- package/.env.ai.example +7 -0
- package/.github/workflows/npm-publish.yml +69 -0
- package/.internal.json +45 -0
- package/.tldr/config.json +11 -0
- package/.tldrignore +90 -0
- package/CLAUDE.md +6 -0
- package/README.md +98 -0
- package/bin/sync-cli.js +7552 -0
- package/concerns.md +7 -0
- package/docs/README.md +41 -0
- package/docs/agents/README.md +24 -0
- package/docs/agents/agent-configuration-system.md +86 -0
- package/docs/agents/execution-agents.md +50 -0
- package/docs/agents/knowledge-agents.md +61 -0
- package/docs/agents/orchestration-agent.md +57 -0
- package/docs/agents/planning-agents.md +84 -0
- package/docs/agents/quality-review-agents.md +67 -0
- package/docs/agents/workflow-agent-orchestration.md +69 -0
- package/docs/flows/README.md +44 -0
- package/docs/flows/compounding.md +126 -0
- package/docs/flows/coordination.md +72 -0
- package/docs/flows/core-harness-integration.md +63 -0
- package/docs/flows/documentation-orchestration.md +98 -0
- package/docs/flows/e2e-test-plan-building.md +83 -0
- package/docs/flows/emergent-refinement.md +104 -0
- package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
- package/docs/flows/judge-reviewing.md +112 -0
- package/docs/flows/plan-deepening-and-research.md +107 -0
- package/docs/flows/plan-review-jury.md +114 -0
- package/docs/flows/pr-reviewing.md +54 -0
- package/docs/flows/prompt-task-execution.md +119 -0
- package/docs/flows/spec-planning.md +162 -0
- package/docs/flows/type-specific-scoping-flows.md +49 -0
- package/docs/flows/validation-and-skills-integration.md +145 -0
- package/docs/flows/wip/wip-flows.md +102 -0
- package/docs/harness/README.md +23 -0
- package/docs/harness/agent-profiles.md +84 -0
- package/docs/harness/cli/README.md +24 -0
- package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
- package/docs/harness/cli/docs-command.md +87 -0
- package/docs/harness/cli/knowledge-command.md +91 -0
- package/docs/harness/cli/minor-cli-commands.md +65 -0
- package/docs/harness/cli/oracle-command.md +113 -0
- package/docs/harness/cli/planning-command.md +95 -0
- package/docs/harness/cli/schema-and-validation-commands.md +154 -0
- package/docs/harness/cli/search-commands.md +97 -0
- package/docs/harness/cli/spawn-command.md +136 -0
- package/docs/harness/cli/specs-command.md +102 -0
- package/docs/harness/cli/tools-command.md +122 -0
- package/docs/harness/cli/trace-command.md +122 -0
- package/docs/harness/cli-daemon.md +92 -0
- package/docs/harness/event-loop.md +184 -0
- package/docs/harness/hooks/README.md +15 -0
- package/docs/harness/hooks/context-hooks.md +96 -0
- package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
- package/docs/harness/hooks/validation-hooks.md +97 -0
- package/docs/harness/test-harness.md +149 -0
- package/docs/harness/tui.md +176 -0
- package/docs/memories.md +20 -0
- package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
- package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
- package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
- package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
- package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
- package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
- package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
- package/docs/sync-cli/README.md +19 -0
- package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
- package/docs/sync-cli/commands/README.md +11 -0
- package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
- package/docs/sync-cli/commands/push-command.md +84 -0
- package/docs/sync-cli/commands/sync-command.md +71 -0
- package/docs/sync-cli/systems/README.md +14 -0
- package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
- package/docs/sync-cli/systems/interactive-ui.md +43 -0
- package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
- package/docs/sync-cli/systems/path-resolution.md +42 -0
- package/package.json +46 -0
- package/scripts/install-shim.sh +40 -0
- package/scripts/pre-pack.sh +25 -0
- package/specs/harness-maintenance-skill.spec.md +138 -0
- package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
- package/specs/sync-init-flag.spec.md +117 -0
- package/specs/unified-workflow-orchestration.spec.md +250 -0
- package/specs/validation-tooling-practice.spec.md +98 -0
- package/specs/workflow-domain-configuration.spec.md +265 -0
- package/src/commands/pull-manifest.ts +31 -0
- package/src/commands/push.ts +344 -0
- package/src/commands/sync.ts +289 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/dotfiles.ts +36 -0
- package/src/lib/fs-utils.ts +18 -0
- package/src/lib/gh.ts +40 -0
- package/src/lib/git.ts +63 -0
- package/src/lib/gitignore.ts +167 -0
- package/src/lib/manifest.ts +121 -0
- package/src/lib/marker-sync.ts +39 -0
- package/src/lib/paths.ts +38 -0
- package/src/lib/target-lines.ts +66 -0
- package/src/lib/ui.ts +78 -0
- package/src/sync-cli.ts +120 -0
- package/target-lines.json +23 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace Store - Dual storage for observability events
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - SQLite database for structured queries
|
|
6
|
+
* - JSONL file for greppable backup
|
|
7
|
+
* - Payload trimming to prevent log bloat
|
|
8
|
+
*
|
|
9
|
+
* All events include agent context from environment variables:
|
|
10
|
+
* - AGENT_ID, AGENT_TYPE, PROMPT_NUMBER, SPEC_NAME, BRANCH
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { mkdirSync, appendFileSync, existsSync, writeFileSync } from 'fs';
|
|
14
|
+
import { join, dirname } from 'path';
|
|
15
|
+
import { createRequire } from 'module';
|
|
16
|
+
import type BetterSqlite3 from 'better-sqlite3';
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
const Database = require('better-sqlite3') as typeof BetterSqlite3;
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Configuration (env-configurable)
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
const MAX_STRING_LENGTH = parseInt(process.env.TRACE_MAX_STRING_LENGTH ?? '200', 10);
|
|
25
|
+
const MAX_DEPTH = parseInt(process.env.TRACE_MAX_DEPTH ?? '3', 10);
|
|
26
|
+
const MAX_ARRAY_ITEMS = parseInt(process.env.TRACE_MAX_ARRAY_ITEMS ?? '5', 10);
|
|
27
|
+
const MAX_OBJECT_KEYS = parseInt(process.env.TRACE_MAX_OBJECT_KEYS ?? '8', 10);
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Types
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
export type TraceEventType =
|
|
34
|
+
| 'session.start'
|
|
35
|
+
| 'session.end'
|
|
36
|
+
| 'prompt.submit'
|
|
37
|
+
| 'tool.pre'
|
|
38
|
+
| 'tool.post'
|
|
39
|
+
| 'tool.failure' // Tool returned an error result
|
|
40
|
+
| 'tool.denied' // Hook denied the tool use
|
|
41
|
+
| 'bash.error' // Bash command non-zero exit
|
|
42
|
+
| 'hook.start' // Hook execution started
|
|
43
|
+
| 'hook.success' // Hook execution succeeded
|
|
44
|
+
| 'hook.error' // Hook execution failed
|
|
45
|
+
| 'harness.error' // Internal harness error
|
|
46
|
+
| 'tui.action' // TUI user action
|
|
47
|
+
| 'tui.lifecycle' // TUI lifecycle event
|
|
48
|
+
| 'tui.error' // TUI runtime error
|
|
49
|
+
| 'command.start' // CLI command started
|
|
50
|
+
| 'command.success' // CLI command succeeded
|
|
51
|
+
| 'command.error' // CLI command failed
|
|
52
|
+
| 'agent.spawn'
|
|
53
|
+
| 'agent.stop'
|
|
54
|
+
| 'agent.compact';
|
|
55
|
+
|
|
56
|
+
/** Error event types for filtering */
|
|
57
|
+
export const ERROR_EVENT_TYPES: TraceEventType[] = [
|
|
58
|
+
'tool.failure',
|
|
59
|
+
'tool.denied',
|
|
60
|
+
'bash.error',
|
|
61
|
+
'hook.error',
|
|
62
|
+
'harness.error',
|
|
63
|
+
'tui.error',
|
|
64
|
+
'command.error',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
export interface TraceEvent {
|
|
68
|
+
id?: number;
|
|
69
|
+
timestamp: string;
|
|
70
|
+
eventType: TraceEventType;
|
|
71
|
+
agentId: string | null;
|
|
72
|
+
agentType: string | null;
|
|
73
|
+
promptNumber: string | null;
|
|
74
|
+
specName: string | null;
|
|
75
|
+
branch: string | null;
|
|
76
|
+
toolName: string | null;
|
|
77
|
+
viaDaemon: boolean; // true if executed via CLI daemon, false if via tsx
|
|
78
|
+
payload: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface TraceQueryOptions {
|
|
82
|
+
agentId?: string;
|
|
83
|
+
agentType?: string;
|
|
84
|
+
eventType?: TraceEventType;
|
|
85
|
+
toolName?: string;
|
|
86
|
+
since?: string; // ISO timestamp or relative like '1h', '30m'
|
|
87
|
+
limit?: number;
|
|
88
|
+
offset?: number;
|
|
89
|
+
errorsOnly?: boolean; // Only return error events
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface TraceStats {
|
|
93
|
+
totalEvents: number;
|
|
94
|
+
totalErrors: number;
|
|
95
|
+
byEventType: Record<string, number>;
|
|
96
|
+
byAgentType: Record<string, number>;
|
|
97
|
+
byToolName: Record<string, number>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ============================================================================
|
|
101
|
+
// Trimming Logic (similar to envoy observability)
|
|
102
|
+
// ============================================================================
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Truncate strings that exceed MAX_STRING_LENGTH
|
|
106
|
+
*/
|
|
107
|
+
function trimStrings(obj: unknown, seen = new WeakSet()): unknown {
|
|
108
|
+
if (obj === null || obj === undefined) return obj;
|
|
109
|
+
|
|
110
|
+
if (typeof obj === 'string') {
|
|
111
|
+
if (obj.length > MAX_STRING_LENGTH) {
|
|
112
|
+
return obj.slice(0, MAX_STRING_LENGTH) + '...';
|
|
113
|
+
}
|
|
114
|
+
return obj;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof obj !== 'object') return obj;
|
|
118
|
+
|
|
119
|
+
// Circular reference protection
|
|
120
|
+
if (seen.has(obj as object)) return '[Circular]';
|
|
121
|
+
seen.add(obj as object);
|
|
122
|
+
|
|
123
|
+
if (Array.isArray(obj)) {
|
|
124
|
+
return obj.map(item => trimStrings(item, seen));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result: Record<string, unknown> = {};
|
|
128
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
129
|
+
result[key] = trimStrings(value, seen);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Truncate structure depth and breadth
|
|
136
|
+
*/
|
|
137
|
+
function truncateStructure(obj: unknown, depth = 0, seen = new WeakSet()): unknown {
|
|
138
|
+
if (obj === null || obj === undefined) return obj;
|
|
139
|
+
if (typeof obj !== 'object') return obj;
|
|
140
|
+
|
|
141
|
+
// Circular reference protection
|
|
142
|
+
if (seen.has(obj as object)) return '[Circular]';
|
|
143
|
+
seen.add(obj as object);
|
|
144
|
+
|
|
145
|
+
if (depth >= MAX_DEPTH) {
|
|
146
|
+
if (Array.isArray(obj)) {
|
|
147
|
+
return `[Array(${obj.length})]`;
|
|
148
|
+
}
|
|
149
|
+
return `[Object(${Object.keys(obj).length} keys)]`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (Array.isArray(obj)) {
|
|
153
|
+
const truncated = obj.slice(0, MAX_ARRAY_ITEMS).map(
|
|
154
|
+
item => truncateStructure(item, depth + 1, seen)
|
|
155
|
+
);
|
|
156
|
+
if (obj.length > MAX_ARRAY_ITEMS) {
|
|
157
|
+
truncated.push(`... +${obj.length - MAX_ARRAY_ITEMS} more`);
|
|
158
|
+
}
|
|
159
|
+
return truncated;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const keys = Object.keys(obj);
|
|
163
|
+
const result: Record<string, unknown> = {};
|
|
164
|
+
const selectedKeys = keys.slice(0, MAX_OBJECT_KEYS);
|
|
165
|
+
|
|
166
|
+
for (const key of selectedKeys) {
|
|
167
|
+
result[key] = truncateStructure((obj as Record<string, unknown>)[key], depth + 1, seen);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (keys.length > MAX_OBJECT_KEYS) {
|
|
171
|
+
result['...'] = `+${keys.length - MAX_OBJECT_KEYS} more keys`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Sanitize payload for logging
|
|
179
|
+
*/
|
|
180
|
+
export function sanitizePayload(payload: unknown): Record<string, unknown> {
|
|
181
|
+
const truncated = truncateStructure(payload);
|
|
182
|
+
const trimmed = trimStrings(truncated);
|
|
183
|
+
return (trimmed as Record<string, unknown>) ?? {};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Agent Context
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get agent context from environment variables
|
|
192
|
+
*/
|
|
193
|
+
export function getAgentContext(): Pick<TraceEvent, 'agentId' | 'agentType' | 'promptNumber' | 'specName' | 'branch' | 'viaDaemon'> {
|
|
194
|
+
return {
|
|
195
|
+
agentId: process.env.AGENT_ID || null,
|
|
196
|
+
agentType: process.env.AGENT_TYPE || null,
|
|
197
|
+
promptNumber: process.env.PROMPT_NUMBER || null,
|
|
198
|
+
specName: process.env.SPEC_NAME || null,
|
|
199
|
+
branch: process.env.BRANCH || null,
|
|
200
|
+
viaDaemon: process.env.AH_VIA_DAEMON === '1',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// Storage Paths
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get the project root directory.
|
|
210
|
+
* Uses CLAUDE_PROJECT_DIR if set (by Claude Code), otherwise falls back to cwd.
|
|
211
|
+
*/
|
|
212
|
+
function getProjectRoot(): string {
|
|
213
|
+
return process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function getStoragePaths(cwd?: string): { dbPath: string; jsonlPath: string } {
|
|
217
|
+
const base = cwd || getProjectRoot();
|
|
218
|
+
const cacheDir = join(base, '.allhands', 'harness', '.cache', 'trace');
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
dbPath: join(cacheDir, 'trace.db'),
|
|
222
|
+
jsonlPath: join(cacheDir, 'trace.jsonl'),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// SQLite Database
|
|
228
|
+
// ============================================================================
|
|
229
|
+
|
|
230
|
+
// Cache databases by path to support multiple projects in same process
|
|
231
|
+
const dbCache = new Map<string, BetterSqlite3.Database>();
|
|
232
|
+
|
|
233
|
+
function getDb(cwd?: string): BetterSqlite3.Database {
|
|
234
|
+
const { dbPath } = getStoragePaths(cwd);
|
|
235
|
+
|
|
236
|
+
// Return cached connection for this path
|
|
237
|
+
const cached = dbCache.get(dbPath);
|
|
238
|
+
if (cached) return cached;
|
|
239
|
+
|
|
240
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
241
|
+
|
|
242
|
+
const db = new Database(dbPath);
|
|
243
|
+
|
|
244
|
+
// Create tables
|
|
245
|
+
db.exec(`
|
|
246
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
247
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
248
|
+
timestamp TEXT NOT NULL,
|
|
249
|
+
event_type TEXT NOT NULL,
|
|
250
|
+
agent_id TEXT,
|
|
251
|
+
agent_type TEXT,
|
|
252
|
+
prompt_number TEXT,
|
|
253
|
+
spec_name TEXT,
|
|
254
|
+
branch TEXT,
|
|
255
|
+
tool_name TEXT,
|
|
256
|
+
is_error INTEGER DEFAULT 0,
|
|
257
|
+
via_daemon INTEGER DEFAULT 0,
|
|
258
|
+
payload TEXT NOT NULL
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
262
|
+
CREATE INDEX IF NOT EXISTS idx_events_event_type ON events(event_type);
|
|
263
|
+
CREATE INDEX IF NOT EXISTS idx_events_agent_type ON events(agent_type);
|
|
264
|
+
CREATE INDEX IF NOT EXISTS idx_events_tool_name ON events(tool_name);
|
|
265
|
+
CREATE INDEX IF NOT EXISTS idx_events_is_error ON events(is_error);
|
|
266
|
+
`);
|
|
267
|
+
|
|
268
|
+
dbCache.set(dbPath, db);
|
|
269
|
+
return db;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// Write Operations
|
|
274
|
+
// ============================================================================
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Check if an event type is an error type
|
|
278
|
+
*/
|
|
279
|
+
function isErrorEvent(eventType: TraceEventType): boolean {
|
|
280
|
+
return ERROR_EVENT_TYPES.includes(eventType);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Log a trace event to both SQLite and JSONL
|
|
285
|
+
*/
|
|
286
|
+
export function logEvent(
|
|
287
|
+
eventType: TraceEventType,
|
|
288
|
+
payload: Record<string, unknown>,
|
|
289
|
+
toolName?: string,
|
|
290
|
+
cwd?: string
|
|
291
|
+
): void {
|
|
292
|
+
const context = getAgentContext();
|
|
293
|
+
const timestamp = new Date().toISOString();
|
|
294
|
+
const sanitizedPayload = sanitizePayload(payload);
|
|
295
|
+
const isError = isErrorEvent(eventType) ? 1 : 0;
|
|
296
|
+
|
|
297
|
+
const event = {
|
|
298
|
+
timestamp,
|
|
299
|
+
eventType,
|
|
300
|
+
...context,
|
|
301
|
+
toolName: toolName || null,
|
|
302
|
+
isError: isError === 1,
|
|
303
|
+
payload: sanitizedPayload,
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// Write to SQLite
|
|
307
|
+
try {
|
|
308
|
+
const database = getDb(cwd);
|
|
309
|
+
const stmt = database.prepare(`
|
|
310
|
+
INSERT INTO events (timestamp, event_type, agent_id, agent_type, prompt_number, spec_name, branch, tool_name, is_error, via_daemon, payload)
|
|
311
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
312
|
+
`);
|
|
313
|
+
stmt.run(
|
|
314
|
+
event.timestamp,
|
|
315
|
+
event.eventType,
|
|
316
|
+
event.agentId,
|
|
317
|
+
event.agentType,
|
|
318
|
+
event.promptNumber,
|
|
319
|
+
event.specName,
|
|
320
|
+
event.branch,
|
|
321
|
+
event.toolName,
|
|
322
|
+
isError,
|
|
323
|
+
event.viaDaemon ? 1 : 0,
|
|
324
|
+
JSON.stringify(event.payload)
|
|
325
|
+
);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
// Silent failure - don't break the hook
|
|
328
|
+
console.error(`[trace-store] SQLite write error: ${err}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Write to JSONL
|
|
332
|
+
try {
|
|
333
|
+
const { jsonlPath } = getStoragePaths(cwd);
|
|
334
|
+
mkdirSync(dirname(jsonlPath), { recursive: true });
|
|
335
|
+
appendFileSync(jsonlPath, JSON.stringify(event) + '\n');
|
|
336
|
+
} catch (err) {
|
|
337
|
+
// Silent failure
|
|
338
|
+
console.error(`[trace-store] JSONL write error: ${err}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Log a harness internal error
|
|
344
|
+
* Use this for errors that occur within the harness itself (not tool failures)
|
|
345
|
+
*/
|
|
346
|
+
export function logHarnessError(
|
|
347
|
+
error: Error | string,
|
|
348
|
+
context: Record<string, unknown> = {},
|
|
349
|
+
cwd?: string
|
|
350
|
+
): void {
|
|
351
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
352
|
+
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
353
|
+
|
|
354
|
+
logEvent('harness.error', {
|
|
355
|
+
error: errorMessage,
|
|
356
|
+
stack: errorStack,
|
|
357
|
+
...context,
|
|
358
|
+
}, undefined, cwd);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Log a hook execution error
|
|
363
|
+
*/
|
|
364
|
+
export function logHookError(
|
|
365
|
+
hookName: string,
|
|
366
|
+
error: Error | string,
|
|
367
|
+
input?: Record<string, unknown>,
|
|
368
|
+
cwd?: string
|
|
369
|
+
): void {
|
|
370
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
371
|
+
|
|
372
|
+
logEvent('hook.error', {
|
|
373
|
+
hook: hookName,
|
|
374
|
+
error: errorMessage,
|
|
375
|
+
input: input ? sanitizePayload(input) : undefined,
|
|
376
|
+
}, undefined, cwd);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Log a TUI runtime error
|
|
381
|
+
* Use this for errors that occur within the TUI (e.g., rendering, modal handling, agent spawning)
|
|
382
|
+
*/
|
|
383
|
+
export function logTuiError(
|
|
384
|
+
component: string,
|
|
385
|
+
error: Error | string,
|
|
386
|
+
context: Record<string, unknown> = {},
|
|
387
|
+
cwd?: string
|
|
388
|
+
): void {
|
|
389
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
390
|
+
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
391
|
+
|
|
392
|
+
logEvent('tui.error', {
|
|
393
|
+
component,
|
|
394
|
+
error: errorMessage,
|
|
395
|
+
stack: errorStack,
|
|
396
|
+
...context,
|
|
397
|
+
}, undefined, cwd);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Log CLI command start
|
|
402
|
+
* Use this at the beginning of important CLI commands (specs, prompts, etc.)
|
|
403
|
+
*/
|
|
404
|
+
export function logCommandStart(
|
|
405
|
+
command: string,
|
|
406
|
+
args: Record<string, unknown> = {},
|
|
407
|
+
cwd?: string
|
|
408
|
+
): void {
|
|
409
|
+
logEvent('command.start', {
|
|
410
|
+
command,
|
|
411
|
+
args: sanitizePayload(args),
|
|
412
|
+
}, undefined, cwd);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Log CLI command success
|
|
417
|
+
* Use this when a CLI command completes successfully
|
|
418
|
+
*/
|
|
419
|
+
export function logCommandSuccess(
|
|
420
|
+
command: string,
|
|
421
|
+
result: Record<string, unknown> = {},
|
|
422
|
+
cwd?: string
|
|
423
|
+
): void {
|
|
424
|
+
logEvent('command.success', {
|
|
425
|
+
command,
|
|
426
|
+
result: sanitizePayload(result),
|
|
427
|
+
}, undefined, cwd);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Log CLI command error
|
|
432
|
+
* Use this when a CLI command fails
|
|
433
|
+
*/
|
|
434
|
+
export function logCommandError(
|
|
435
|
+
command: string,
|
|
436
|
+
error: Error | string,
|
|
437
|
+
args: Record<string, unknown> = {},
|
|
438
|
+
cwd?: string
|
|
439
|
+
): void {
|
|
440
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
441
|
+
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
442
|
+
|
|
443
|
+
logEvent('command.error', {
|
|
444
|
+
command,
|
|
445
|
+
error: errorMessage,
|
|
446
|
+
stack: errorStack,
|
|
447
|
+
args: sanitizePayload(args),
|
|
448
|
+
}, undefined, cwd);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ============================================================================
|
|
452
|
+
// Hook Logging
|
|
453
|
+
// ============================================================================
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Log hook execution start
|
|
457
|
+
* Use this at the beginning of hook handlers
|
|
458
|
+
*/
|
|
459
|
+
export function logHookStart(
|
|
460
|
+
hookName: string,
|
|
461
|
+
input: Record<string, unknown> = {},
|
|
462
|
+
cwd?: string
|
|
463
|
+
): void {
|
|
464
|
+
logEvent('hook.start', {
|
|
465
|
+
hook: hookName,
|
|
466
|
+
input: sanitizePayload(input),
|
|
467
|
+
}, undefined, cwd);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Log hook execution success
|
|
472
|
+
* Use this when a hook completes successfully
|
|
473
|
+
*/
|
|
474
|
+
export function logHookSuccess(
|
|
475
|
+
hookName: string,
|
|
476
|
+
result: Record<string, unknown> = {},
|
|
477
|
+
cwd?: string
|
|
478
|
+
): void {
|
|
479
|
+
logEvent('hook.success', {
|
|
480
|
+
hook: hookName,
|
|
481
|
+
result: sanitizePayload(result),
|
|
482
|
+
}, undefined, cwd);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ============================================================================
|
|
486
|
+
// TUI Logging
|
|
487
|
+
// ============================================================================
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Log TUI user action
|
|
491
|
+
* Use this when user performs an action in the TUI
|
|
492
|
+
*/
|
|
493
|
+
export function logTuiAction(
|
|
494
|
+
action: string,
|
|
495
|
+
data: Record<string, unknown> = {},
|
|
496
|
+
cwd?: string
|
|
497
|
+
): void {
|
|
498
|
+
logEvent('tui.action', {
|
|
499
|
+
action,
|
|
500
|
+
...sanitizePayload(data),
|
|
501
|
+
}, undefined, cwd);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Log TUI lifecycle event
|
|
506
|
+
* Use this for TUI state changes and lifecycle events
|
|
507
|
+
*/
|
|
508
|
+
export function logTuiLifecycle(
|
|
509
|
+
event: string,
|
|
510
|
+
data: Record<string, unknown> = {},
|
|
511
|
+
cwd?: string
|
|
512
|
+
): void {
|
|
513
|
+
logEvent('tui.lifecycle', {
|
|
514
|
+
event,
|
|
515
|
+
...sanitizePayload(data),
|
|
516
|
+
}, undefined, cwd);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ============================================================================
|
|
520
|
+
// Read Operations
|
|
521
|
+
// ============================================================================
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Parse relative time strings like '1h', '30m', '2d'
|
|
525
|
+
*/
|
|
526
|
+
function parseRelativeTime(input: string): Date {
|
|
527
|
+
const now = new Date();
|
|
528
|
+
const match = input.match(/^(\d+)([smhd])$/);
|
|
529
|
+
|
|
530
|
+
if (!match) {
|
|
531
|
+
// Try parsing as ISO timestamp
|
|
532
|
+
const date = new Date(input);
|
|
533
|
+
if (!isNaN(date.getTime())) return date;
|
|
534
|
+
throw new Error(`Invalid time format: ${input}`);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const [, num, unit] = match;
|
|
538
|
+
const value = parseInt(num, 10);
|
|
539
|
+
|
|
540
|
+
switch (unit) {
|
|
541
|
+
case 's': return new Date(now.getTime() - value * 1000);
|
|
542
|
+
case 'm': return new Date(now.getTime() - value * 60 * 1000);
|
|
543
|
+
case 'h': return new Date(now.getTime() - value * 60 * 60 * 1000);
|
|
544
|
+
case 'd': return new Date(now.getTime() - value * 24 * 60 * 60 * 1000);
|
|
545
|
+
default: throw new Error(`Invalid time unit: ${unit}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Query events with filters
|
|
551
|
+
*/
|
|
552
|
+
export function queryEvents(options: TraceQueryOptions = {}, cwd?: string): (TraceEvent & { isError?: boolean })[] {
|
|
553
|
+
const database = getDb(cwd);
|
|
554
|
+
|
|
555
|
+
const conditions: string[] = [];
|
|
556
|
+
const params: unknown[] = [];
|
|
557
|
+
|
|
558
|
+
if (options.agentId) {
|
|
559
|
+
conditions.push('agent_id = ?');
|
|
560
|
+
params.push(options.agentId);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (options.agentType) {
|
|
564
|
+
conditions.push('agent_type = ?');
|
|
565
|
+
params.push(options.agentType);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (options.eventType) {
|
|
569
|
+
conditions.push('event_type = ?');
|
|
570
|
+
params.push(options.eventType);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (options.toolName) {
|
|
574
|
+
conditions.push('tool_name = ?');
|
|
575
|
+
params.push(options.toolName);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (options.since) {
|
|
579
|
+
const sinceDate = parseRelativeTime(options.since);
|
|
580
|
+
conditions.push('timestamp >= ?');
|
|
581
|
+
params.push(sinceDate.toISOString());
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (options.errorsOnly) {
|
|
585
|
+
conditions.push('is_error = 1');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
589
|
+
const limit = options.limit ?? 100;
|
|
590
|
+
const offset = options.offset ?? 0;
|
|
591
|
+
|
|
592
|
+
const sql = `
|
|
593
|
+
SELECT * FROM events
|
|
594
|
+
${whereClause}
|
|
595
|
+
ORDER BY timestamp DESC
|
|
596
|
+
LIMIT ? OFFSET ?
|
|
597
|
+
`;
|
|
598
|
+
|
|
599
|
+
params.push(limit, offset);
|
|
600
|
+
|
|
601
|
+
const rows = database.prepare(sql).all(...params) as Array<{
|
|
602
|
+
id: number;
|
|
603
|
+
timestamp: string;
|
|
604
|
+
event_type: string;
|
|
605
|
+
agent_id: string | null;
|
|
606
|
+
agent_type: string | null;
|
|
607
|
+
prompt_number: string | null;
|
|
608
|
+
spec_name: string | null;
|
|
609
|
+
branch: string | null;
|
|
610
|
+
tool_name: string | null;
|
|
611
|
+
is_error: number;
|
|
612
|
+
via_daemon: number;
|
|
613
|
+
payload: string;
|
|
614
|
+
}>;
|
|
615
|
+
|
|
616
|
+
return rows.map(row => ({
|
|
617
|
+
id: row.id,
|
|
618
|
+
timestamp: row.timestamp,
|
|
619
|
+
eventType: row.event_type as TraceEventType,
|
|
620
|
+
agentId: row.agent_id,
|
|
621
|
+
agentType: row.agent_type,
|
|
622
|
+
promptNumber: row.prompt_number,
|
|
623
|
+
specName: row.spec_name,
|
|
624
|
+
branch: row.branch,
|
|
625
|
+
toolName: row.tool_name,
|
|
626
|
+
isError: row.is_error === 1,
|
|
627
|
+
viaDaemon: row.via_daemon === 1,
|
|
628
|
+
payload: JSON.parse(row.payload),
|
|
629
|
+
}));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Get aggregate statistics
|
|
634
|
+
*/
|
|
635
|
+
export function getStats(since?: string, cwd?: string): TraceStats {
|
|
636
|
+
const database = getDb(cwd);
|
|
637
|
+
|
|
638
|
+
let whereClause = '';
|
|
639
|
+
const params: unknown[] = [];
|
|
640
|
+
|
|
641
|
+
if (since) {
|
|
642
|
+
const sinceDate = parseRelativeTime(since);
|
|
643
|
+
whereClause = 'WHERE timestamp >= ?';
|
|
644
|
+
params.push(sinceDate.toISOString());
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Total count
|
|
648
|
+
const totalRow = database.prepare(`SELECT COUNT(*) as count FROM events ${whereClause}`).get(...params) as { count: number };
|
|
649
|
+
|
|
650
|
+
// Error count
|
|
651
|
+
const errorWhereClause = whereClause ? `${whereClause} AND is_error = 1` : 'WHERE is_error = 1';
|
|
652
|
+
const errorRow = database.prepare(`SELECT COUNT(*) as count FROM events ${errorWhereClause}`).get(...params) as { count: number };
|
|
653
|
+
|
|
654
|
+
// By event type
|
|
655
|
+
const eventTypeRows = database.prepare(`
|
|
656
|
+
SELECT event_type, COUNT(*) as count FROM events ${whereClause} GROUP BY event_type
|
|
657
|
+
`).all(...params) as Array<{ event_type: string; count: number }>;
|
|
658
|
+
|
|
659
|
+
// By agent type
|
|
660
|
+
const agentTypeWhereClause = whereClause
|
|
661
|
+
? `${whereClause} AND agent_type IS NOT NULL`
|
|
662
|
+
: 'WHERE agent_type IS NOT NULL';
|
|
663
|
+
const agentTypeRows = database.prepare(`
|
|
664
|
+
SELECT agent_type, COUNT(*) as count FROM events ${agentTypeWhereClause} GROUP BY agent_type
|
|
665
|
+
`).all(...params) as Array<{ agent_type: string; count: number }>;
|
|
666
|
+
|
|
667
|
+
// By tool name
|
|
668
|
+
const toolNameWhereClause = whereClause
|
|
669
|
+
? `${whereClause} AND tool_name IS NOT NULL`
|
|
670
|
+
: 'WHERE tool_name IS NOT NULL';
|
|
671
|
+
const toolNameRows = database.prepare(`
|
|
672
|
+
SELECT tool_name, COUNT(*) as count FROM events ${toolNameWhereClause} GROUP BY tool_name
|
|
673
|
+
`).all(...params) as Array<{ tool_name: string; count: number }>;
|
|
674
|
+
|
|
675
|
+
return {
|
|
676
|
+
totalEvents: totalRow.count,
|
|
677
|
+
totalErrors: errorRow.count,
|
|
678
|
+
byEventType: Object.fromEntries(eventTypeRows.map(r => [r.event_type, r.count])),
|
|
679
|
+
byAgentType: Object.fromEntries(agentTypeRows.map(r => [r.agent_type, r.count])),
|
|
680
|
+
byToolName: Object.fromEntries(toolNameRows.map(r => [r.tool_name, r.count])),
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Close all database connections (for cleanup)
|
|
686
|
+
*/
|
|
687
|
+
export function closeDb(): void {
|
|
688
|
+
Array.from(dbCache.values()).forEach((db) => db.close());
|
|
689
|
+
dbCache.clear();
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Clear all trace logs (both SQLite database and JSONL file)
|
|
694
|
+
*/
|
|
695
|
+
export function clearLogs(cwd?: string): void {
|
|
696
|
+
const { jsonlPath } = getStoragePaths(cwd);
|
|
697
|
+
|
|
698
|
+
// Clear SQLite database
|
|
699
|
+
try {
|
|
700
|
+
const database = getDb(cwd);
|
|
701
|
+
database.exec('DELETE FROM events');
|
|
702
|
+
} catch (err) {
|
|
703
|
+
console.error(`[trace-store] SQLite clear error: ${err}`);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Clear JSONL file by truncating it
|
|
707
|
+
try {
|
|
708
|
+
if (existsSync(jsonlPath)) {
|
|
709
|
+
writeFileSync(jsonlPath, '');
|
|
710
|
+
}
|
|
711
|
+
} catch (err) {
|
|
712
|
+
console.error(`[trace-store] JSONL clear error: ${err}`);
|
|
713
|
+
}
|
|
714
|
+
}
|