@winspan/claude-forge 9.2.0 → 9.12.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/DEVELOPMENT.md +80 -19
- package/README.md +13 -6
- package/dist/catalogs/agents.json +19 -24
- package/dist/catalogs/skills.json +139 -27
- package/dist/claudemd/templates/swarm-protocol.md +1 -1
- package/dist/cli/commands/agent.d.ts +169 -0
- package/dist/cli/commands/agent.d.ts.map +1 -0
- package/dist/cli/commands/agent.js +690 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/codegraph.d.ts +17 -0
- package/dist/cli/commands/codegraph.d.ts.map +1 -0
- package/dist/cli/commands/codegraph.js +263 -0
- package/dist/cli/commands/codegraph.js.map +1 -0
- package/dist/cli/commands/decisions.d.ts.map +1 -1
- package/dist/cli/commands/decisions.js +46 -9
- package/dist/cli/commands/decisions.js.map +1 -1
- package/dist/cli/commands/executions.d.ts.map +1 -1
- package/dist/cli/commands/executions.js +2 -1
- package/dist/cli/commands/executions.js.map +1 -1
- package/dist/cli/commands/insights-goal-check.d.ts +5 -1
- package/dist/cli/commands/insights-goal-check.d.ts.map +1 -1
- package/dist/cli/commands/insights-goal-check.js +15 -15
- package/dist/cli/commands/insights-goal-check.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts +51 -0
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +202 -29
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/loop.d.ts +95 -0
- package/dist/cli/commands/loop.d.ts.map +1 -0
- package/dist/cli/commands/loop.js +408 -0
- package/dist/cli/commands/loop.js.map +1 -0
- package/dist/cli/commands/maintenance.d.ts +33 -0
- package/dist/cli/commands/maintenance.d.ts.map +1 -0
- package/dist/cli/commands/maintenance.js +75 -0
- package/dist/cli/commands/maintenance.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +23 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +82 -0
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/skills.d.ts +131 -0
- package/dist/cli/commands/skills.d.ts.map +1 -1
- package/dist/cli/commands/skills.js +409 -9
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/stats.d.ts.map +1 -1
- package/dist/cli/commands/stats.js +9 -2
- package/dist/cli/commands/stats.js.map +1 -1
- package/dist/cli/index.js +8 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/constants.d.ts +37 -0
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +43 -0
- package/dist/core/constants.js.map +1 -1
- package/dist/core/diagnostics/checks.d.ts.map +1 -1
- package/dist/core/diagnostics/checks.js +2 -1
- package/dist/core/diagnostics/checks.js.map +1 -1
- package/dist/core/diagnostics/daemon-status.d.ts +1 -1
- package/dist/core/diagnostics/daemon-status.d.ts.map +1 -1
- package/dist/core/diagnostics/daemon-status.js +1 -1
- package/dist/core/diagnostics/daemon-status.js.map +1 -1
- package/dist/core/diagnostics/entropy-checks.d.ts +3 -2
- package/dist/core/diagnostics/entropy-checks.d.ts.map +1 -1
- package/dist/core/diagnostics/entropy-checks.js +10 -5
- package/dist/core/diagnostics/entropy-checks.js.map +1 -1
- package/dist/core/diagnostics/heartbeat-reader.d.ts +28 -0
- package/dist/core/diagnostics/heartbeat-reader.d.ts.map +1 -0
- package/dist/core/diagnostics/heartbeat-reader.js +50 -0
- package/dist/core/diagnostics/heartbeat-reader.js.map +1 -0
- package/dist/core/event-fields.d.ts +27 -0
- package/dist/core/event-fields.d.ts.map +1 -1
- package/dist/core/event-fields.js +43 -0
- package/dist/core/event-fields.js.map +1 -1
- package/dist/core/governance/global-inject.d.ts +34 -4
- package/dist/core/governance/global-inject.d.ts.map +1 -1
- package/dist/core/governance/global-inject.js +80 -25
- package/dist/core/governance/global-inject.js.map +1 -1
- package/dist/core/insights/agent-anchor-guard.d.ts +77 -0
- package/dist/core/insights/agent-anchor-guard.d.ts.map +1 -0
- package/dist/core/insights/agent-anchor-guard.js +119 -0
- package/dist/core/insights/agent-anchor-guard.js.map +1 -0
- package/dist/core/insights/agent-distill-context.d.ts +55 -0
- package/dist/core/insights/agent-distill-context.d.ts.map +1 -0
- package/dist/core/insights/agent-distill-context.js +146 -0
- package/dist/core/insights/agent-distill-context.js.map +1 -0
- package/dist/core/insights/agent-distill-spawn.d.ts +56 -0
- package/dist/core/insights/agent-distill-spawn.d.ts.map +1 -0
- package/dist/core/insights/agent-distill-spawn.js +179 -0
- package/dist/core/insights/agent-distill-spawn.js.map +1 -0
- package/dist/core/insights/agent-drift.d.ts +66 -0
- package/dist/core/insights/agent-drift.d.ts.map +1 -0
- package/dist/core/insights/agent-drift.js +109 -0
- package/dist/core/insights/agent-drift.js.map +1 -0
- package/dist/core/insights/agent-evolution-sources.d.ts +21 -0
- package/dist/core/insights/agent-evolution-sources.d.ts.map +1 -0
- package/dist/core/insights/agent-evolution-sources.js +36 -0
- package/dist/core/insights/agent-evolution-sources.js.map +1 -0
- package/dist/core/insights/agent-health.d.ts +142 -0
- package/dist/core/insights/agent-health.d.ts.map +1 -0
- package/dist/core/insights/agent-health.js +296 -0
- package/dist/core/insights/agent-health.js.map +1 -0
- package/dist/core/insights/agent-patch-apply.d.ts +45 -0
- package/dist/core/insights/agent-patch-apply.d.ts.map +1 -0
- package/dist/core/insights/agent-patch-apply.js +165 -0
- package/dist/core/insights/agent-patch-apply.js.map +1 -0
- package/dist/core/insights/agent-suggest.d.ts +128 -0
- package/dist/core/insights/agent-suggest.d.ts.map +1 -0
- package/dist/core/insights/agent-suggest.js +284 -0
- package/dist/core/insights/agent-suggest.js.map +1 -0
- package/dist/core/insights/coverage-tiers.d.ts +46 -0
- package/dist/core/insights/coverage-tiers.d.ts.map +1 -0
- package/dist/core/insights/coverage-tiers.js +95 -0
- package/dist/core/insights/coverage-tiers.js.map +1 -0
- package/dist/{daemon/services → core/insights}/experience-extractor.d.ts +0 -7
- package/dist/core/insights/experience-extractor.d.ts.map +1 -0
- package/dist/{daemon/services → core/insights}/experience-extractor.js +5 -9
- package/dist/core/insights/experience-extractor.js.map +1 -0
- package/dist/{daemon/services → core/insights}/violation-reporter.d.ts +20 -1
- package/dist/core/insights/violation-reporter.d.ts.map +1 -0
- package/dist/{daemon/services → core/insights}/violation-reporter.js +56 -4
- package/dist/core/insights/violation-reporter.js.map +1 -0
- package/dist/core/loop/loop-engine.d.ts +140 -0
- package/dist/core/loop/loop-engine.d.ts.map +1 -0
- package/dist/core/loop/loop-engine.js +266 -0
- package/dist/core/loop/loop-engine.js.map +1 -0
- package/dist/core/queue/index.d.ts.map +1 -1
- package/dist/core/queue/index.js +2 -1
- package/dist/core/queue/index.js.map +1 -1
- package/dist/core/storage/base.d.ts +159 -0
- package/dist/core/storage/base.d.ts.map +1 -1
- package/dist/core/storage/base.js +523 -0
- package/dist/core/storage/base.js.map +1 -1
- package/dist/core/storage/codegraph-types.d.ts +79 -0
- package/dist/core/storage/codegraph-types.d.ts.map +1 -0
- package/dist/core/storage/codegraph-types.js +14 -0
- package/dist/core/storage/codegraph-types.js.map +1 -0
- package/dist/core/storage/codegraph.d.ts +186 -0
- package/dist/core/storage/codegraph.d.ts.map +1 -0
- package/dist/core/storage/codegraph.js +452 -0
- package/dist/core/storage/codegraph.js.map +1 -0
- package/dist/core/storage/decisions.d.ts +30 -5
- package/dist/core/storage/decisions.d.ts.map +1 -1
- package/dist/core/storage/decisions.js +45 -13
- package/dist/core/storage/decisions.js.map +1 -1
- package/dist/core/storage/events.d.ts +127 -0
- package/dist/core/storage/events.d.ts.map +1 -1
- package/dist/core/storage/events.js +318 -3
- package/dist/core/storage/events.js.map +1 -1
- package/dist/core/storage/feedback.d.ts +3 -23
- package/dist/core/storage/feedback.d.ts.map +1 -1
- package/dist/core/storage/feedback.js +37 -38
- package/dist/core/storage/feedback.js.map +1 -1
- package/dist/core/storage/injections.d.ts +40 -0
- package/dist/core/storage/injections.d.ts.map +1 -1
- package/dist/core/storage/injections.js +69 -0
- package/dist/core/storage/injections.js.map +1 -1
- package/dist/core/storage/knowledge.d.ts +226 -0
- package/dist/core/storage/knowledge.d.ts.map +1 -1
- package/dist/core/storage/knowledge.js +391 -4
- package/dist/core/storage/knowledge.js.map +1 -1
- package/dist/core/storage/pipeline-rollup.d.ts +1 -7
- package/dist/core/storage/pipeline-rollup.d.ts.map +1 -1
- package/dist/core/storage/pipeline-rollup.js +18 -57
- package/dist/core/storage/pipeline-rollup.js.map +1 -1
- package/dist/core/storage/routing.d.ts +34 -0
- package/dist/core/storage/routing.d.ts.map +1 -1
- package/dist/core/storage/routing.js +92 -2
- package/dist/core/storage/routing.js.map +1 -1
- package/dist/core/storage/rows.d.ts +5 -25
- package/dist/core/storage/rows.d.ts.map +1 -1
- package/dist/core/storage/schema.sql +92 -27
- package/dist/core/storage/sessions.d.ts.map +1 -1
- package/dist/core/storage/sessions.js +2 -1
- package/dist/core/storage/sessions.js.map +1 -1
- package/dist/core/storage/skills.d.ts +159 -0
- package/dist/core/storage/skills.d.ts.map +1 -1
- package/dist/core/storage/skills.js +350 -4
- package/dist/core/storage/skills.js.map +1 -1
- package/dist/core/storage/sqlite.d.ts +81 -25
- package/dist/core/storage/sqlite.d.ts.map +1 -1
- package/dist/core/storage/sqlite.js +143 -45
- package/dist/core/storage/sqlite.js.map +1 -1
- package/dist/core/storage/tasks.d.ts +270 -0
- package/dist/core/storage/tasks.d.ts.map +1 -1
- package/dist/core/storage/tasks.js +495 -16
- package/dist/core/storage/tasks.js.map +1 -1
- package/dist/core/storage/tool-intercepts.d.ts +1 -1
- package/dist/core/storage/tool-intercepts.js +1 -1
- package/dist/core/types.d.ts +26 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/utils/binary-paths.d.ts +32 -0
- package/dist/core/utils/binary-paths.d.ts.map +1 -1
- package/dist/core/utils/binary-paths.js +52 -0
- package/dist/core/utils/binary-paths.js.map +1 -1
- package/dist/core/utils/claude-cli-resolver.d.ts.map +1 -0
- package/dist/{skills/distill → core/utils}/claude-cli-resolver.js +1 -1
- package/dist/core/utils/claude-cli-resolver.js.map +1 -0
- package/dist/core/utils/claude-cli-spawn.d.ts +1 -1
- package/dist/core/utils/claude-cli-spawn.js +2 -2
- package/dist/core/utils/claude-cli-spawn.js.map +1 -1
- package/dist/core/utils/noise-prompt.d.ts +1 -1
- package/dist/core/utils/noise-prompt.js +1 -1
- package/dist/core/utils/time.d.ts +26 -0
- package/dist/core/utils/time.d.ts.map +1 -1
- package/dist/core/utils/time.js +33 -0
- package/dist/core/utils/time.js.map +1 -1
- package/dist/daemon/config-store.d.ts.map +1 -1
- package/dist/daemon/config-store.js +14 -5
- package/dist/daemon/config-store.js.map +1 -1
- package/dist/daemon/event-parser.d.ts.map +1 -1
- package/dist/daemon/event-parser.js +5 -0
- package/dist/daemon/event-parser.js.map +1 -1
- package/dist/daemon/handlers/post-tool-use.d.ts +24 -16
- package/dist/daemon/handlers/post-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use.js +76 -116
- package/dist/daemon/handlers/post-tool-use.js.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.d.ts +35 -10
- package/dist/daemon/handlers/pre-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.js +71 -38
- package/dist/daemon/handlers/pre-tool-use.js.map +1 -1
- package/dist/daemon/handlers/stop.d.ts +20 -0
- package/dist/daemon/handlers/stop.d.ts.map +1 -1
- package/dist/daemon/handlers/stop.js +96 -8
- package/dist/daemon/handlers/stop.js.map +1 -1
- package/dist/daemon/handlers/user-prompt.d.ts +16 -1
- package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt.js +97 -56
- package/dist/daemon/handlers/user-prompt.js.map +1 -1
- package/dist/daemon/handlers/violation-content-backfill.d.ts +76 -0
- package/dist/daemon/handlers/violation-content-backfill.d.ts.map +1 -0
- package/dist/daemon/handlers/violation-content-backfill.js +167 -0
- package/dist/daemon/handlers/violation-content-backfill.js.map +1 -0
- package/dist/daemon/index.d.ts +19 -0
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +125 -200
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/rules/defaults.d.ts.map +1 -1
- package/dist/daemon/rules/defaults.js +151 -64
- package/dist/daemon/rules/defaults.js.map +1 -1
- package/dist/daemon/rules/types.d.ts +28 -22
- package/dist/daemon/rules/types.d.ts.map +1 -1
- package/dist/daemon/rules/workflow-defaults.js +9 -9
- package/dist/daemon/rules/workflow-defaults.js.map +1 -1
- package/dist/daemon/services/codegraph-sync.d.ts +94 -0
- package/dist/daemon/services/codegraph-sync.d.ts.map +1 -0
- package/dist/daemon/services/codegraph-sync.js +159 -0
- package/dist/daemon/services/codegraph-sync.js.map +1 -0
- package/dist/daemon/services/decision-hint.d.ts +47 -10
- package/dist/daemon/services/decision-hint.d.ts.map +1 -1
- package/dist/daemon/services/decision-hint.js +99 -24
- package/dist/daemon/services/decision-hint.js.map +1 -1
- package/dist/daemon/services/event-ttl-sweep.d.ts.map +1 -1
- package/dist/daemon/services/event-ttl-sweep.js +3 -2
- package/dist/daemon/services/event-ttl-sweep.js.map +1 -1
- package/dist/daemon/services/feedback-aggregator.d.ts +14 -26
- package/dist/daemon/services/feedback-aggregator.d.ts.map +1 -1
- package/dist/daemon/services/feedback-aggregator.js +23 -63
- package/dist/daemon/services/feedback-aggregator.js.map +1 -1
- package/dist/daemon/services/heartbeat-writer.d.ts +6 -15
- package/dist/daemon/services/heartbeat-writer.d.ts.map +1 -1
- package/dist/daemon/services/heartbeat-writer.js +7 -36
- package/dist/daemon/services/heartbeat-writer.js.map +1 -1
- package/dist/daemon/services/kb-injector.d.ts +1 -1
- package/dist/daemon/services/kb-injector.d.ts.map +1 -1
- package/dist/daemon/services/kb-injector.js +10 -2
- package/dist/daemon/services/kb-injector.js.map +1 -1
- package/dist/daemon/services/kb-rebuild-scheduler.d.ts +95 -0
- package/dist/daemon/services/kb-rebuild-scheduler.d.ts.map +1 -0
- package/dist/daemon/services/kb-rebuild-scheduler.js +149 -0
- package/dist/daemon/services/kb-rebuild-scheduler.js.map +1 -0
- package/dist/daemon/services/loop-hint.d.ts +139 -0
- package/dist/daemon/services/loop-hint.d.ts.map +1 -0
- package/dist/daemon/services/loop-hint.js +272 -0
- package/dist/daemon/services/loop-hint.js.map +1 -0
- package/dist/daemon/services/outcome-classification-service.js +1 -1
- package/dist/daemon/services/outcome-classification-service.js.map +1 -1
- package/dist/daemon/services/task-segmenter.d.ts +11 -0
- package/dist/daemon/services/task-segmenter.d.ts.map +1 -1
- package/dist/daemon/services/task-segmenter.js +48 -2
- package/dist/daemon/services/task-segmenter.js.map +1 -1
- package/dist/daemon/startup/maintenance-schedulers.d.ts +68 -0
- package/dist/daemon/startup/maintenance-schedulers.d.ts.map +1 -0
- package/dist/daemon/startup/maintenance-schedulers.js +294 -0
- package/dist/daemon/startup/maintenance-schedulers.js.map +1 -0
- package/dist/daemon/templates/agents/agent-retro-distiller.md +106 -0
- package/dist/daemon/templates/agents/claudemd-writer.md +1 -0
- package/dist/daemon/templates/agents/coder.md +165 -8
- package/dist/daemon/templates/agents/decision-maker.md +107 -21
- package/dist/daemon/templates/agents/doc-reviewer.md +4 -1
- package/dist/daemon/templates/agents/harness-debug-full.md +85 -3
- package/dist/daemon/templates/agents/knowledge-builder.md +1 -0
- package/dist/daemon/templates/agents/patch-applier.md +1 -0
- package/dist/daemon/templates/agents/planner.md +55 -3
- package/dist/daemon/templates/agents/safety-net-implementer.md +278 -0
- package/dist/daemon/templates/agents/skill-distiller.md +1 -0
- package/dist/daemon/templates/agents/task-boundary-classifier.md +1 -0
- package/dist/daemon/templates/agents/verify-agent.md +128 -5
- package/dist/hooks/stop.sh +7 -1
- package/dist/knowledge/builder.js +36 -7
- package/dist/knowledge/builder.js.map +1 -1
- package/dist/knowledge/constants.d.ts +10 -5
- package/dist/knowledge/constants.d.ts.map +1 -1
- package/dist/knowledge/constants.js +10 -5
- package/dist/knowledge/constants.js.map +1 -1
- package/dist/knowledge/graph/edge-extractor.d.ts +45 -0
- package/dist/knowledge/graph/edge-extractor.d.ts.map +1 -0
- package/dist/knowledge/graph/edge-extractor.js +242 -0
- package/dist/knowledge/graph/edge-extractor.js.map +1 -0
- package/dist/knowledge/graph/impact.d.ts +73 -0
- package/dist/knowledge/graph/impact.d.ts.map +1 -0
- package/dist/knowledge/graph/impact.js +94 -0
- package/dist/knowledge/graph/impact.js.map +1 -0
- package/dist/knowledge/graph/types.d.ts +22 -0
- package/dist/knowledge/graph/types.d.ts.map +1 -0
- package/dist/knowledge/graph/types.js +13 -0
- package/dist/knowledge/graph/types.js.map +1 -0
- package/dist/knowledge/prompt.d.ts +9 -0
- package/dist/knowledge/prompt.d.ts.map +1 -1
- package/dist/knowledge/prompt.js +17 -5
- package/dist/knowledge/prompt.js.map +1 -1
- package/dist/knowledge/query.d.ts +13 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +107 -10
- package/dist/knowledge/query.js.map +1 -1
- package/dist/knowledge/repo-map.d.ts +11 -5
- package/dist/knowledge/repo-map.d.ts.map +1 -1
- package/dist/knowledge/repo-map.js +42 -3
- package/dist/knowledge/repo-map.js.map +1 -1
- package/dist/knowledge/validator.d.ts.map +1 -1
- package/dist/knowledge/validator.js +69 -2
- package/dist/knowledge/validator.js.map +1 -1
- package/dist/mcp/server.d.ts +64 -8
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +233 -18
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills/distill/distiller.js +1 -1
- package/dist/skills/distill/distiller.js.map +1 -1
- package/dist/skills/distilled/distilled-api-design.md +4 -0
- package/dist/skills/distilled/distilled-brainstorming.md +79 -0
- package/dist/skills/distilled/distilled-brand-guidelines.md +86 -0
- package/dist/skills/distilled/distilled-canvas-design.md +128 -0
- package/dist/skills/distilled/distilled-claude-api.md +185 -0
- package/dist/skills/distilled/distilled-creator.md +5 -2
- package/dist/skills/distilled/distilled-dispatching-parallel-agents.md +136 -0
- package/dist/skills/distilled/distilled-doc-coauthoring.md +144 -0
- package/dist/skills/distilled/distilled-docx.md +231 -0
- package/dist/skills/distilled/distilled-executing-plans.md +85 -50
- package/dist/skills/distilled/distilled-finishing-a-development-branch.md +213 -0
- package/dist/skills/distilled/distilled-frontend-design.md +118 -0
- package/dist/skills/distilled/distilled-harness-engineering.md +1 -1
- package/dist/skills/distilled/distilled-receiving-code-review.md +185 -0
- package/dist/skills/distilled/distilled-subagent-driven-development.md +124 -0
- package/dist/skills/distilled/distilled-systematic-debugging.md +108 -260
- package/dist/skills/distilled/distilled-test-driven-development.md +432 -0
- package/dist/skills/distilled/distilled-using-superpowers.md +134 -0
- package/dist/skills/distilled/distilled-verification-before-completion.md +88 -78
- package/dist/skills/distilled/distilled-writing-skills.md +175 -0
- package/dist/skills/registry.d.ts +10 -50
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +7 -118
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/tools/pipeline-suggest.js +2 -2
- package/dist/skills/tools/pipeline-suggest.js.map +1 -1
- package/dist/skills/tools/skill-invoke.d.ts +2 -1
- package/dist/skills/tools/skill-invoke.d.ts.map +1 -1
- package/dist/skills/tools/skill-invoke.js +3 -1
- package/dist/skills/tools/skill-invoke.js.map +1 -1
- package/dist/web/analytics/anti-pattern-detector.d.ts.map +1 -1
- package/dist/web/analytics/anti-pattern-detector.js +6 -1
- package/dist/web/analytics/anti-pattern-detector.js.map +1 -1
- package/dist/web/analytics/drift-detector.d.ts +6 -0
- package/dist/web/analytics/drift-detector.d.ts.map +1 -1
- package/dist/web/analytics/drift-detector.js +15 -8
- package/dist/web/analytics/drift-detector.js.map +1 -1
- package/dist/web/analytics/weekly-report.d.ts +13 -0
- package/dist/web/analytics/weekly-report.d.ts.map +1 -1
- package/dist/web/analytics/weekly-report.js +17 -3
- package/dist/web/analytics/weekly-report.js.map +1 -1
- package/dist/web/routes/_helpers.d.ts +31 -0
- package/dist/web/routes/_helpers.d.ts.map +1 -1
- package/dist/web/routes/_helpers.js +33 -0
- package/dist/web/routes/_helpers.js.map +1 -1
- package/dist/web/routes/agent-distill.d.ts +49 -0
- package/dist/web/routes/agent-distill.d.ts.map +1 -0
- package/dist/web/routes/agent-distill.js +526 -0
- package/dist/web/routes/agent-distill.js.map +1 -0
- package/dist/web/routes/config.d.ts +56 -0
- package/dist/web/routes/config.d.ts.map +1 -0
- package/dist/web/routes/config.js +243 -0
- package/dist/web/routes/config.js.map +1 -0
- package/dist/web/routes/decisions.js +1 -1
- package/dist/web/routes/decisions.js.map +1 -1
- package/dist/web/routes/error-handler.d.ts +0 -4
- package/dist/web/routes/error-handler.d.ts.map +1 -1
- package/dist/web/routes/error-handler.js +0 -8
- package/dist/web/routes/error-handler.js.map +1 -1
- package/dist/web/routes/events.d.ts.map +1 -1
- package/dist/web/routes/events.js +2 -1
- package/dist/web/routes/events.js.map +1 -1
- package/dist/web/routes/insights.d.ts.map +1 -1
- package/dist/web/routes/insights.js +0 -0
- package/dist/web/routes/insights.js.map +1 -1
- package/dist/web/routes/knowledge.d.ts +43 -2
- package/dist/web/routes/knowledge.d.ts.map +1 -1
- package/dist/web/routes/knowledge.js +117 -6
- package/dist/web/routes/knowledge.js.map +1 -1
- package/dist/web/routes/pipeline.d.ts +0 -9
- package/dist/web/routes/pipeline.d.ts.map +1 -1
- package/dist/web/routes/pipeline.js +0 -4
- package/dist/web/routes/pipeline.js.map +1 -1
- package/dist/web/routes/rules.d.ts.map +1 -1
- package/dist/web/routes/rules.js +20 -6
- package/dist/web/routes/rules.js.map +1 -1
- package/dist/web/routes/sessions.d.ts.map +1 -1
- package/dist/web/routes/sessions.js +8 -7
- package/dist/web/routes/sessions.js.map +1 -1
- package/dist/web/routes/skill-stats.d.ts.map +1 -1
- package/dist/web/routes/skill-stats.js +153 -16
- package/dist/web/routes/skill-stats.js.map +1 -1
- package/dist/web/routes/skills-distill.js +1 -1
- package/dist/web/routes/skills-distill.js.map +1 -1
- package/dist/web/routes/stats.d.ts.map +1 -1
- package/dist/web/routes/stats.js +2 -1
- package/dist/web/routes/stats.js.map +1 -1
- package/dist/web/routes/task-timeline.d.ts +95 -19
- package/dist/web/routes/task-timeline.d.ts.map +1 -1
- package/dist/web/routes/task-timeline.js +344 -88
- package/dist/web/routes/task-timeline.js.map +1 -1
- package/dist/web/routes/tasks.d.ts.map +1 -1
- package/dist/web/routes/tasks.js +52 -30
- package/dist/web/routes/tasks.js.map +1 -1
- package/dist/web/routes/violations.d.ts +1 -1
- package/dist/web/routes/violations.d.ts.map +1 -1
- package/dist/web/routes/violations.js +3 -2
- package/dist/web/routes/violations.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +20 -0
- package/dist/web/server.js.map +1 -1
- package/dist/web/services/agent-distill-manager.d.ts +122 -0
- package/dist/web/services/agent-distill-manager.d.ts.map +1 -0
- package/dist/web/services/agent-distill-manager.js +397 -0
- package/dist/web/services/agent-distill-manager.js.map +1 -0
- package/dist/web/services/distill-manager.d.ts +47 -0
- package/dist/web/services/distill-manager.d.ts.map +1 -1
- package/dist/web/services/distill-manager.js +103 -0
- package/dist/web/services/distill-manager.js.map +1 -1
- package/dist/web/static/assets/AgentDetailPage-DlUeA1sX.js +2 -0
- package/dist/web/static/assets/AgentDetailPage-DlUeA1sX.js.map +1 -0
- package/dist/web/static/assets/AgentDistillRunPage-Cybo4bii.js +3 -0
- package/dist/web/static/assets/AgentDistillRunPage-Cybo4bii.js.map +1 -0
- package/dist/web/static/assets/AgentsPage-Qd9FExLG.js +2 -0
- package/dist/web/static/assets/AgentsPage-Qd9FExLG.js.map +1 -0
- package/dist/web/static/assets/DaemonHealthPage-DTSVqtrI.js +2 -0
- package/dist/web/static/assets/DaemonHealthPage-DTSVqtrI.js.map +1 -0
- package/dist/web/static/assets/DecisionDetailPage-b4BA8dhc.js +2 -0
- package/dist/web/static/assets/DecisionDetailPage-b4BA8dhc.js.map +1 -0
- package/dist/web/static/assets/DecisionsPage-a3NRo_T7.js +2 -0
- package/dist/web/static/assets/DecisionsPage-a3NRo_T7.js.map +1 -0
- package/dist/web/static/assets/DiagnosticsPage-DIVdiIQG.js +2 -0
- package/dist/web/static/assets/DiagnosticsPage-DIVdiIQG.js.map +1 -0
- package/dist/web/static/assets/DistillDetailPage-U6a3l2iP.js +4 -0
- package/dist/web/static/assets/DistillDetailPage-U6a3l2iP.js.map +1 -0
- package/dist/web/static/assets/DistillPage-O7BHtRN8.js +2 -0
- package/dist/web/static/assets/DistillPage-O7BHtRN8.js.map +1 -0
- package/dist/web/static/assets/DistillRunPage-D1JuRWWr.js +2 -0
- package/dist/web/static/assets/DistillRunPage-D1JuRWWr.js.map +1 -0
- package/dist/web/static/assets/GlobalScopeHint-Q3wTJx3F.js +2 -0
- package/dist/web/static/assets/GlobalScopeHint-Q3wTJx3F.js.map +1 -0
- package/dist/web/static/assets/IssueDetailPage-BDfrtk2C.js +2 -0
- package/dist/web/static/assets/IssueDetailPage-BDfrtk2C.js.map +1 -0
- package/dist/web/static/assets/IssuesPage-SKmhlCrw.js +2 -0
- package/dist/web/static/assets/IssuesPage-SKmhlCrw.js.map +1 -0
- package/dist/web/static/assets/KbDetailPage-Yna86Na8.js +2 -0
- package/dist/web/static/assets/KbDetailPage-Yna86Na8.js.map +1 -0
- package/dist/web/static/assets/KbHitsPage-Cljl7H9p.js +2 -0
- package/dist/web/static/assets/KbHitsPage-Cljl7H9p.js.map +1 -0
- package/dist/web/static/assets/{MarkdownRenderer-DZmTl-8J.js → MarkdownRenderer-DlDQNihj.js} +2 -2
- package/dist/web/static/assets/{MarkdownRenderer-DZmTl-8J.js.map → MarkdownRenderer-DlDQNihj.js.map} +1 -1
- package/dist/web/static/assets/NotFound-LMzbP51V.js +2 -0
- package/dist/web/static/assets/{NotFound-BQPh0vaF.js.map → NotFound-LMzbP51V.js.map} +1 -1
- package/dist/web/static/assets/SettingsPage-DzoK4PKg.js +2 -0
- package/dist/web/static/assets/SettingsPage-DzoK4PKg.js.map +1 -0
- package/dist/web/static/assets/SkillDetailPage-BuBJJ_NX.js +2 -0
- package/dist/web/static/assets/SkillDetailPage-BuBJJ_NX.js.map +1 -0
- package/dist/web/static/assets/SkillsPage-aojkJpBc.js +2 -0
- package/dist/web/static/assets/SkillsPage-aojkJpBc.js.map +1 -0
- package/dist/web/static/assets/TaskDetailPage-1ckxnGhw.js +4 -0
- package/dist/web/static/assets/TaskDetailPage-1ckxnGhw.js.map +1 -0
- package/dist/web/static/assets/TasksHubPage-C2PLh3eg.js +6 -0
- package/dist/web/static/assets/TasksHubPage-C2PLh3eg.js.map +1 -0
- package/dist/web/static/assets/WorkplacePage-DHrp5VxS.js +2 -0
- package/dist/web/static/assets/WorkplacePage-DHrp5VxS.js.map +1 -0
- package/dist/web/static/assets/arco-DV6xCLhr.js +14 -0
- package/dist/web/static/assets/arco-DV6xCLhr.js.map +1 -0
- package/dist/web/static/assets/charts-BSV4cyC4.js +37 -0
- package/dist/web/static/assets/charts-BSV4cyC4.js.map +1 -0
- package/dist/web/static/assets/{index-7bl3kbcx.css → index-B_v_MKlb.css} +1 -1
- package/dist/web/static/assets/index-DileOOE4.js +4 -0
- package/dist/web/static/assets/index-DileOOE4.js.map +1 -0
- package/dist/web/static/assets/markdown-CA7ePUts.js +30 -0
- package/dist/web/static/assets/markdown-CA7ePUts.js.map +1 -0
- package/dist/web/static/assets/{outcome-DUn1NjlC.js → outcome-BKGy9azt.js} +2 -2
- package/dist/web/static/assets/{outcome-DUn1NjlC.js.map → outcome-BKGy9azt.js.map} +1 -1
- package/dist/web/static/assets/{query-S6X1S7K9.js → query-CgCOpYWf.js} +2 -2
- package/dist/web/static/assets/{query-S6X1S7K9.js.map → query-CgCOpYWf.js.map} +1 -1
- package/dist/web/static/assets/{react-router-JVUrkhdd.js → react-router-Cxmg8RuL.js} +3 -3
- package/dist/web/static/assets/{react-router-JVUrkhdd.js.map → react-router-Cxmg8RuL.js.map} +1 -1
- package/dist/web/static/assets/{syntax-highlighter-BkZfCDsz.js → syntax-highlighter-BDYycNja.js} +3 -3
- package/dist/web/static/assets/{syntax-highlighter-BkZfCDsz.js.map → syntax-highlighter-BDYycNja.js.map} +1 -1
- package/dist/web/static/assets/task-title-BhOcemuR.js +2 -0
- package/dist/web/static/assets/task-title-BhOcemuR.js.map +1 -0
- package/dist/web/static/assets/useAgentStats-B-uTgqBd.js +2 -0
- package/dist/web/static/assets/useAgentStats-B-uTgqBd.js.map +1 -0
- package/dist/web/static/assets/useDecisions-D-G2Ft5T.js +2 -0
- package/dist/web/static/assets/useDecisions-D-G2Ft5T.js.map +1 -0
- package/dist/web/static/assets/useDistill-21dZkXlT.js +3 -0
- package/dist/web/static/assets/useDistill-21dZkXlT.js.map +1 -0
- package/dist/web/static/assets/useEffectiveProject-DQiyX54y.js +2 -0
- package/dist/web/static/assets/useEffectiveProject-DQiyX54y.js.map +1 -0
- package/dist/web/static/assets/useIssuesFeed-CFiyQkAL.js +2 -0
- package/dist/web/static/assets/useIssuesFeed-CFiyQkAL.js.map +1 -0
- package/dist/web/static/assets/useKbHits-xKXWgqh9.js +2 -0
- package/dist/web/static/assets/useKbHits-xKXWgqh9.js.map +1 -0
- package/dist/web/static/assets/useSkillStats-B5hbIwdf.js +2 -0
- package/dist/web/static/assets/useSkillStats-B5hbIwdf.js.map +1 -0
- package/dist/web/static/assets/vendor-DS-q4Eyc.js +36 -0
- package/dist/web/static/assets/vendor-DS-q4Eyc.js.map +1 -0
- package/dist/web/static/index.html +6 -6
- package/package.json +5 -3
- package/dist/core/storage/workflow-recommendations.d.ts +0 -124
- package/dist/core/storage/workflow-recommendations.d.ts.map +0 -1
- package/dist/core/storage/workflow-recommendations.js +0 -274
- package/dist/core/storage/workflow-recommendations.js.map +0 -1
- package/dist/daemon/services/experience-extractor.d.ts.map +0 -1
- package/dist/daemon/services/experience-extractor.js.map +0 -1
- package/dist/daemon/services/violation-reporter.d.ts.map +0 -1
- package/dist/daemon/services/violation-reporter.js.map +0 -1
- package/dist/daemon/templates/agents/harness-hotfix.md +0 -99
- package/dist/daemon/templates/agents/hybrid-feature-with-safety.md +0 -104
- package/dist/daemon/templates/agents/refactor-specialist.md +0 -98
- package/dist/skills/distill/claude-cli-resolver.d.ts.map +0 -1
- package/dist/skills/distill/claude-cli-resolver.js.map +0 -1
- package/dist/skills/distilled/distilled-defi-amm-security.md +0 -293
- package/dist/skills/keyword-score.d.ts +0 -29
- package/dist/skills/keyword-score.d.ts.map +0 -1
- package/dist/skills/keyword-score.js +0 -54
- package/dist/skills/keyword-score.js.map +0 -1
- package/dist/web/static/assets/AgentContentPage-DkeRNxok.js +0 -2
- package/dist/web/static/assets/AgentContentPage-DkeRNxok.js.map +0 -1
- package/dist/web/static/assets/AgentDelegationTable-ByBa0x1l.js +0 -2
- package/dist/web/static/assets/AgentDelegationTable-ByBa0x1l.js.map +0 -1
- package/dist/web/static/assets/ContextInsightsPage-oUk7_I8u.js +0 -3
- package/dist/web/static/assets/ContextInsightsPage-oUk7_I8u.js.map +0 -1
- package/dist/web/static/assets/DaemonHealthPage-DG2fyOP7.js +0 -2
- package/dist/web/static/assets/DaemonHealthPage-DG2fyOP7.js.map +0 -1
- package/dist/web/static/assets/DecisionsPage-CMAPEnKb.js +0 -2
- package/dist/web/static/assets/DecisionsPage-CMAPEnKb.js.map +0 -1
- package/dist/web/static/assets/DiagnosticsPage-DQd-Zm4r.js +0 -2
- package/dist/web/static/assets/DiagnosticsPage-DQd-Zm4r.js.map +0 -1
- package/dist/web/static/assets/DriftTab-DqpepOhI.js +0 -2
- package/dist/web/static/assets/DriftTab-DqpepOhI.js.map +0 -1
- package/dist/web/static/assets/HealthHomePage-CN6zNIie.js +0 -3
- package/dist/web/static/assets/HealthHomePage-CN6zNIie.js.map +0 -1
- package/dist/web/static/assets/KbHitRateTable-ByEIWujF.js +0 -2
- package/dist/web/static/assets/KbHitRateTable-ByEIWujF.js.map +0 -1
- package/dist/web/static/assets/NotFound-BQPh0vaF.js +0 -2
- package/dist/web/static/assets/ProjectSwitcher-D3lZMFd3.js +0 -2
- package/dist/web/static/assets/ProjectSwitcher-D3lZMFd3.js.map +0 -1
- package/dist/web/static/assets/SettingsPage-oLJBNzQj.js +0 -2
- package/dist/web/static/assets/SettingsPage-oLJBNzQj.js.map +0 -1
- package/dist/web/static/assets/SkillContentPage-DK5rgfgw.js +0 -2
- package/dist/web/static/assets/SkillContentPage-DK5rgfgw.js.map +0 -1
- package/dist/web/static/assets/SkillStatsTable-DYMzjEUV.js +0 -2
- package/dist/web/static/assets/SkillStatsTable-DYMzjEUV.js.map +0 -1
- package/dist/web/static/assets/SkillsDistillTab-C7qaG8q3.js +0 -2
- package/dist/web/static/assets/SkillsDistillTab-C7qaG8q3.js.map +0 -1
- package/dist/web/static/assets/TasksHubPage-03wsRRsJ.js +0 -6
- package/dist/web/static/assets/TasksHubPage-03wsRRsJ.js.map +0 -1
- package/dist/web/static/assets/ViolationsPage-DSiLr-9O.js +0 -3
- package/dist/web/static/assets/ViolationsPage-DSiLr-9O.js.map +0 -1
- package/dist/web/static/assets/arco-Bhi3a6Qp.js +0 -14
- package/dist/web/static/assets/arco-Bhi3a6Qp.js.map +0 -1
- package/dist/web/static/assets/charts-BuHQWDbQ.js +0 -37
- package/dist/web/static/assets/charts-BuHQWDbQ.js.map +0 -1
- package/dist/web/static/assets/index-BIYnq1Dx.js +0 -4
- package/dist/web/static/assets/index-BIYnq1Dx.js.map +0 -1
- package/dist/web/static/assets/useTabsParam-k8qte_0C.js +0 -2
- package/dist/web/static/assets/useTabsParam-k8qte_0C.js.map +0 -1
- package/dist/web/static/assets/vendor-DWgdB1eY.js +0 -65
- package/dist/web/static/assets/vendor-DWgdB1eY.js.map +0 -1
- /package/dist/{skills/distill → core/utils}/claude-cli-resolver.d.ts +0 -0
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* - 一次性 sessions 回填
|
|
9
9
|
*
|
|
10
10
|
* 不涉及业务读写。所有业务读写由 *Operations 类组合 db 实例实现。
|
|
11
|
+
*
|
|
12
|
+
* 迁移机制(无编号 .sql runner):schema.sql 是当前 schema 的唯一真相
|
|
13
|
+
* (全 idempotent CREATE ... IF NOT EXISTS,每次启动 db.exec 重放)+
|
|
14
|
+
* 本文件 runColumnMigrations()/runIndexMigrations()/runPostMigrations()
|
|
15
|
+
* 对旧库就地补列/补索引/补 CHECK。migrations/ 目录下不存在被读取的编号 .sql。
|
|
16
|
+
* 详见 migrations/README.md。
|
|
11
17
|
*/
|
|
12
18
|
import Database from 'better-sqlite3';
|
|
13
19
|
import { readFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
|
|
@@ -15,6 +21,48 @@ import { dirname, join } from 'node:path';
|
|
|
15
21
|
import { fileURLToPath } from 'node:url';
|
|
16
22
|
import { DATABASE } from '../constants.js';
|
|
17
23
|
import { logger } from '../utils/logger.js';
|
|
24
|
+
import { extractSpawnFields } from '../event-fields.js';
|
|
25
|
+
import { decodeToolInput } from './codec/tool-input-codec.js';
|
|
26
|
+
/**
|
|
27
|
+
* Backfill window for events.decision_id / subagent_type (decision c425f26f,
|
|
28
|
+
* spec 1544 § 1.3, user ruling #4). The spec aligns the window to existing
|
|
29
|
+
* retention; the only retention constant that bounds recoverability is the
|
|
30
|
+
* event-ttl-sweep WARM TTL (default 7 days), past which `tool_input` is NULLed
|
|
31
|
+
* (gzip blob gone — nothing left to decode). We use 10 days (spec default) as a
|
|
32
|
+
* conservative-but-safe ceiling: rows older than the warm TTL simply have NULL
|
|
33
|
+
* tool_input and are skipped by the WHERE (`tool_input IS NOT NULL` implied by a
|
|
34
|
+
* successful decode). base.ts is in the leaf-most `core` layer and MUST NOT
|
|
35
|
+
* depend on the daemon ConfigStore (decision ed98c8e8), so this is a static
|
|
36
|
+
* constant, not a config read.
|
|
37
|
+
*/
|
|
38
|
+
export const SPAWN_FIELD_BACKFILL_WINDOW = "-10 days";
|
|
39
|
+
/**
|
|
40
|
+
* Backfill window for the one-shot tasks.parent_task_id historical linkage
|
|
41
|
+
* (decision f47c2c90, spec 1711 § Phase 1.1, user ruling Q2 = -10 days). Aligned
|
|
42
|
+
* with SPAWN_FIELD_BACKFILL_WINDOW so the two boot-time backfills cover the same
|
|
43
|
+
* recent slice. Historical rows cannot read the in-memory agentJustReturned flag,
|
|
44
|
+
* so the backfill uses a WEAKER pure-SQL heuristic (ack-title + same session +
|
|
45
|
+
* ≤30min prior non-ack user task); anything ambiguous stays NULL (宁缺毋滥).
|
|
46
|
+
*/
|
|
47
|
+
export const TASK_PARENT_BACKFILL_WINDOW = "-10 days";
|
|
48
|
+
/**
|
|
49
|
+
* Resume window in minutes (mirrors TASK_RESUME_WINDOW_MS=30min in the daemon
|
|
50
|
+
* TaskSegmenter). The parent backfill only links a follow-up ack task to a prior
|
|
51
|
+
* user task started within this window. Kept as a static constant here because
|
|
52
|
+
* base.ts is leaf-most core and MUST NOT depend on the daemon config (ed98c8e8).
|
|
53
|
+
*/
|
|
54
|
+
const TASK_PARENT_BACKFILL_WINDOW_MIN = 30;
|
|
55
|
+
/**
|
|
56
|
+
* Conservative ack-title set for the parent backfill (decision f47c2c90). A
|
|
57
|
+
* follow-up task whose title is one of these short acknowledgements is treated
|
|
58
|
+
* as an approval/continue turn that should chain back to the prior user task.
|
|
59
|
+
* Mirrors the segmenter's ACK_CONTINUE_KEYWORDS spirit but is a SQL-side
|
|
60
|
+
* lowercase exact-match list (titles are short ack tokens, not free text).
|
|
61
|
+
*/
|
|
62
|
+
const TASK_PARENT_ACK_TITLES = [
|
|
63
|
+
'批准', '同意', 'approve', '好', '好的', '嗯', 'ok', 'okay',
|
|
64
|
+
'可以', '是', '是的', '继续', 'yes', '对', '行', '没问题',
|
|
65
|
+
];
|
|
18
66
|
export class SQLiteBase {
|
|
19
67
|
db;
|
|
20
68
|
dbPath;
|
|
@@ -212,10 +260,49 @@ export class SQLiteBase {
|
|
|
212
260
|
this.addColumnIfMissing('sessions', 'first_prompt', 'TEXT');
|
|
213
261
|
this.addColumnIfMissing('routing_events', 'skill_confidence', 'REAL');
|
|
214
262
|
this.addColumnIfMissing('routing_events', 'skill_source', 'TEXT');
|
|
263
|
+
// R9 (decision 1544, 2026-06-10): routing row provenance. NULL for normal
|
|
264
|
+
// hook-driven events; 'programmatic' for rows minted by code paths. Nullable,
|
|
265
|
+
// DEFAULT NULL keeps every legacy row untouched.
|
|
266
|
+
this.addColumnIfMissing('routing_events', 'source', 'TEXT');
|
|
215
267
|
this.addColumnIfMissing('skill_invocations', 'workflow', 'TEXT');
|
|
216
268
|
this.addColumnIfMissing('skill_invocations', 'phase', 'TEXT');
|
|
217
269
|
this.addColumnIfMissing('skill_invocations', 'feature_slug', 'TEXT');
|
|
218
270
|
this.addColumnIfMissing('skill_invocations', 'artifact_path', 'TEXT');
|
|
271
|
+
// audit #2 (decision f61b8a0c, 2026-06-09): distinguish "injected" rows from
|
|
272
|
+
// "really invoked" rows. The UserPromptSubmit auto-match path writes a row
|
|
273
|
+
// with success=1 the moment a skill is INJECTED — it never observes whether
|
|
274
|
+
// the skill was actually followed. That made every web "skill hit /
|
|
275
|
+
// effectiveness" metric (weekly-report / drift-detector / skill-stats) read
|
|
276
|
+
// injection counts as if they were real-call counts.
|
|
277
|
+
//
|
|
278
|
+
// Fix without breaking the old `success` semantics (5+ readers depend on the
|
|
279
|
+
// old column): add a NULLABLE marker `recorded_at_inject`. Auto-match
|
|
280
|
+
// injection rows stamp it 1; real-call rows (MCP skill_invoke /
|
|
281
|
+
// events.tool_name='Skill') leave it NULL. Readers filter on it to separate
|
|
282
|
+
// "injected" from "really invoked". DEFAULT NULL keeps legacy rows untouched.
|
|
283
|
+
this.addColumnIfMissing('skill_invocations', 'recorded_at_inject', 'INTEGER');
|
|
284
|
+
// Skill usefulness feedback (decision d24cd3a2 P0, spec
|
|
285
|
+
// docs/design/2026-06-11/1524-skill-usefulness-feedback-spec.md). Agent
|
|
286
|
+
// POST-USE self-assessment of whether a pulled skill's CONTENT was actionable
|
|
287
|
+
// on THIS project's task. DISTINCT from `success` (which only means the
|
|
288
|
+
// invoke call didn't throw) — usefulness measures content transfer.
|
|
289
|
+
// usefulness = 'helped' | 'partial' | 'not-actionable' (advisory enum,
|
|
290
|
+
// stored as free TEXT, nullable, DEFAULT NULL).
|
|
291
|
+
// usefulness_note = one-line "what was/wasn't applicable", nullable.
|
|
292
|
+
// Nullable + no backfill → every legacy row reads usefulness=NULL untouched,
|
|
293
|
+
// verbatim mirror of the reason/workflow/recorded_at_inject additive columns.
|
|
294
|
+
this.addColumnIfMissing('skill_invocations', 'usefulness', 'TEXT');
|
|
295
|
+
this.addColumnIfMissing('skill_invocations', 'usefulness_note', 'TEXT');
|
|
296
|
+
// Project scope (decision 39839e30 Part 2, 2026-06-13): the project a skill
|
|
297
|
+
// pull belongs to, so the workplace "Skill 调用" KPI can filter by project
|
|
298
|
+
// like the other 3 KPIs already do. Mirror of kb_query_log.project_path.
|
|
299
|
+
// Nullable, DEFAULT NULL → every legacy row reads project_path=NULL untouched;
|
|
300
|
+
// aggregations only add `AND project_path = ?` when a project is passed
|
|
301
|
+
// (additive, back-compat with Phase 2c / Skill 详情页 / cf skill usefulness).
|
|
302
|
+
// Historical rows are one-shot back-filled from sessions.project_path in
|
|
303
|
+
// runPostMigrations → backfillSkillInvocationsProjectPathIfNeeded (join miss
|
|
304
|
+
// → stays NULL).
|
|
305
|
+
this.addColumnIfMissing('skill_invocations', 'project_path', 'TEXT');
|
|
219
306
|
// Outcome instrumentation (v9.x). NOTE: SQLite ALTER TABLE ADD COLUMN can
|
|
220
307
|
// only add CHECK constraints that don't reference other rows — the
|
|
221
308
|
// outcome CHECK is therefore embedded in the column type clause below.
|
|
@@ -260,6 +347,17 @@ export class SQLiteBase {
|
|
|
260
347
|
// to their granular kind via title-prefix sniff. The CHECK constraint
|
|
261
348
|
// here is the same one schema.sql declares for fresh DBs.
|
|
262
349
|
this.addColumnIfMissing('tasks', 'task_kind', `TEXT NOT NULL DEFAULT 'user' CHECK(task_kind IN ('user','agent-callback','image','system'))`);
|
|
350
|
+
// decision f47c2c90 (二期B, spec 1711): parent_task_id self-referencing FK
|
|
351
|
+
// for cross-turn workflow linkage on tasks that carry NO decision_id. The
|
|
352
|
+
// TaskSegmenter fills it (conservatively, triple-gate) for approval/continue
|
|
353
|
+
// follow-up turns; timeline aggregates the parent chain only when the task
|
|
354
|
+
// has no decision_id (else-branch fallback, strictly mutually exclusive with
|
|
355
|
+
// the decision_id path). Nullable, DEFAULT NULL → every legacy row untouched.
|
|
356
|
+
// SQLite ALTER ADD COLUMN cannot carry inline REFERENCES, so legacy DBs get
|
|
357
|
+
// a plain TEXT column with no FK enforcement — the app layer never relies on
|
|
358
|
+
// SQLite FK enforce (logical FK only). The one-shot backfill runs in
|
|
359
|
+
// runPostMigrations (backfillTasksParentIfNeeded).
|
|
360
|
+
this.addColumnIfMissing('tasks', 'parent_task_id', 'TEXT');
|
|
263
361
|
// C1 (spec 0854, 2026-06-02): the pending_specs table was retired and is
|
|
264
362
|
// DROPped by schema.sql. Its former `source` column migration + spec_path
|
|
265
363
|
// index migration were removed here — schema.sql runs AFTER these column
|
|
@@ -268,13 +366,49 @@ export class SQLiteBase {
|
|
|
268
366
|
// v9.x MVP1: KB injection metadata column. Stores JSON array of KB hits
|
|
269
367
|
// (page name + score + refs) for the 'UserPromptSubmitHandler:kb' source.
|
|
270
368
|
this.addColumnIfMissing('injections', 'metadata_json', 'TEXT');
|
|
369
|
+
// KB 采纳 telemetry(decision 148f7a0a, 2026-06-10 审计):注入后同会话
|
|
370
|
+
// 出现主动 KB MCP 调用(mcp__claude-forge__knowledge*)时,PostToolUse
|
|
371
|
+
// handler 给该会话所有 source_handler LIKE '%:kb' 且 adopted_at IS NULL
|
|
372
|
+
// 的注入行盖 epoch-ms 时间戳。4 周后用一条 SQL 即可回答「注入的 KB 被
|
|
373
|
+
// 用了没」(adopted/total)。Nullable INTEGER;NULL = 从未被采纳。
|
|
374
|
+
this.addColumnIfMissing('injections', 'adopted_at', 'INTEGER');
|
|
271
375
|
// F3 (audit-20260522 migration 009): writer-side tool_output 50KB truncation
|
|
272
376
|
// marker. Legacy rows default to 0 (full content preserved).
|
|
273
377
|
this.addColumnIfMissing('events', 'tool_output_truncated', 'INTEGER DEFAULT 0');
|
|
378
|
+
// decision c425f26f (spec 1544): cross-task execution-chain aggregation.
|
|
379
|
+
// decision_id + subagent_type are extracted to PLAINTEXT columns so a SQL
|
|
380
|
+
// GROUP BY decision_id can stitch a workflow fragmented across per-prompt
|
|
381
|
+
// tasks. Both nullable, DEFAULT NULL → every legacy row untouched.
|
|
382
|
+
// CRITICAL: these are plaintext TEXT — they NEVER flow through the gzip
|
|
383
|
+
// codec (gzip one-way-door); the go-forward writer (events.ts) extracts them
|
|
384
|
+
// pre-codec, the one-shot backfill (runPostMigrations) decodes gzip once to
|
|
385
|
+
// fill historical rows. ALTER ADD COLUMN nullable is an O(1) metadata op
|
|
386
|
+
// (no row rewrite) so this is cheap even on the 295 MB events table.
|
|
387
|
+
this.addColumnIfMissing('events', 'decision_id', 'TEXT');
|
|
388
|
+
this.addColumnIfMissing('events', 'subagent_type', 'TEXT');
|
|
274
389
|
// decision-flow-overhaul (2026-05-28): link routing rows back to the decision
|
|
275
390
|
// that triggered the spawn. Both columns default NULL (additive).
|
|
276
391
|
this.addColumnIfMissing('routing_events', 'decision_id', 'TEXT');
|
|
277
392
|
this.addColumnIfMissing('routing_events', 'spawn_id', 'TEXT');
|
|
393
|
+
// decision d24cd3a2 Task B (2026-06-11): make agent KB pulls attributable.
|
|
394
|
+
// kb_query_log gains the same attribution fields as skill_invocations so a
|
|
395
|
+
// deliberate `cf knowledge query --reason` pull (source='cli') is
|
|
396
|
+
// distinguishable from the daemon's automatic per-prompt probe
|
|
397
|
+
// (source='daemon-probe'). All nullable — legacy rows stay NULL, no backfill.
|
|
398
|
+
this.addColumnIfMissing('kb_query_log', 'reason', 'TEXT');
|
|
399
|
+
this.addColumnIfMissing('kb_query_log', 'source', 'TEXT');
|
|
400
|
+
this.addColumnIfMissing('kb_query_log', 'agent_id', 'TEXT');
|
|
401
|
+
this.addColumnIfMissing('kb_query_log', 'workflow', 'TEXT');
|
|
402
|
+
this.addColumnIfMissing('kb_query_log', 'phase', 'TEXT');
|
|
403
|
+
// decision 096309e4 batch 1 item ① (2026-06-13): KB post-use usefulness
|
|
404
|
+
// self-assessment, mirroring skill_invocations.usefulness. An agent that
|
|
405
|
+
// pulled KB via `cf knowledge query/fetch --reason` rates whether the CONTENT
|
|
406
|
+
// helped on THIS task. Nullable + no backfill → legacy / daemon-probe rows
|
|
407
|
+
// read usefulness=NULL untouched. This is the real "KB was used AND helped"
|
|
408
|
+
// signal that the dead injections.adopted_at path (MCP-only, subagents have
|
|
409
|
+
// zero MCP) could never capture.
|
|
410
|
+
this.addColumnIfMissing('kb_query_log', 'usefulness', 'TEXT');
|
|
411
|
+
this.addColumnIfMissing('kb_query_log', 'usefulness_note', 'TEXT');
|
|
278
412
|
// decision-flow-overhaul (2026-05-28): update pending_specs.source CHECK to
|
|
279
413
|
// include 'decision'. SQLite does NOT support altering CHECK constraints on
|
|
280
414
|
// existing columns via ALTER TABLE, so for existing DBs the guard lives in
|
|
@@ -322,6 +456,8 @@ export class SQLiteBase {
|
|
|
322
456
|
runIndexMigrations() {
|
|
323
457
|
this.createIndexIfMissing('idx_skill_invocations_workflow', `CREATE INDEX IF NOT EXISTS idx_skill_invocations_workflow ON skill_invocations(workflow, phase)`);
|
|
324
458
|
this.createIndexIfMissing('idx_skill_invocations_feature', `CREATE INDEX IF NOT EXISTS idx_skill_invocations_feature ON skill_invocations(feature_slug)`);
|
|
459
|
+
// decision 39839e30 Part 2: project-scoped skill KPI filtering.
|
|
460
|
+
this.createIndexIfMissing('idx_skill_invocations_project', `CREATE INDEX IF NOT EXISTS idx_skill_invocations_project ON skill_invocations(project_path)`);
|
|
325
461
|
this.createIndexIfMissing('idx_sessions_start_time', `CREATE INDEX IF NOT EXISTS idx_sessions_start_time ON sessions(start_time DESC)`);
|
|
326
462
|
// Composite indexes for high-frequency session-scoped queries
|
|
327
463
|
this.createIndexIfMissing('idx_events_session_ts', `CREATE INDEX IF NOT EXISTS idx_events_session_ts ON events(session_id, timestamp DESC)`);
|
|
@@ -329,9 +465,21 @@ export class SQLiteBase {
|
|
|
329
465
|
this.createIndexIfMissing('idx_skill_invocations_session_ts', `CREATE INDEX IF NOT EXISTS idx_skill_invocations_session_ts ON skill_invocations(session_id, timestamp DESC)`);
|
|
330
466
|
// spec b1480935 (2026-06-01): task_kind index for hot WHERE filters.
|
|
331
467
|
this.createIndexIfMissing('idx_tasks_kind', `CREATE INDEX IF NOT EXISTS idx_tasks_kind ON tasks(task_kind)`);
|
|
468
|
+
// decision f47c2c90 (二期B): index on the parent FK column so the timeline
|
|
469
|
+
// chain query's `WHERE parent_task_id = ?` (向下收子 task) hits an index.
|
|
470
|
+
this.createIndexIfMissing('idx_tasks_parent', `CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id)`);
|
|
332
471
|
// Phase 1 Refactor: Additional performance indexes
|
|
333
472
|
this.createIndexIfMissing('idx_routing_events_obeyed_ts', `CREATE INDEX IF NOT EXISTS idx_routing_events_obeyed_ts ON routing_events(obeyed, ts DESC)`);
|
|
334
473
|
this.createIndexIfMissing('idx_events_session_hook', `CREATE INDEX IF NOT EXISTS idx_events_session_hook ON events(session_id, hook_type, timestamp DESC)`);
|
|
474
|
+
// decision 7a3d07b5: covering index for the agent-board top_tools join.
|
|
475
|
+
// (event_id, hook_type, tool_name) lets the per-row events lookup satisfy the
|
|
476
|
+
// PostToolUse + tool_name filter and project tool_name without a table hit;
|
|
477
|
+
// paired with `INDEXED BY idx_task_events_task` in tasks.queryAgentBoardTasks
|
|
478
|
+
// it flips the join driver off the full PostToolUse scan (485ms → ~10ms).
|
|
479
|
+
this.createIndexIfMissing('idx_events_eid_hook_tool', `CREATE INDEX IF NOT EXISTS idx_events_eid_hook_tool ON events(event_id, hook_type, tool_name)`);
|
|
480
|
+
// decision c425f26f (spec 1544): cross-task decision_id aggregation indexes.
|
|
481
|
+
this.createIndexIfMissing('idx_events_decision_id', `CREATE INDEX IF NOT EXISTS idx_events_decision_id ON events(decision_id)`);
|
|
482
|
+
this.createIndexIfMissing('idx_events_decision_spawn', `CREATE INDEX IF NOT EXISTS idx_events_decision_spawn ON events(decision_id, hook_type, tool_name, timestamp)`);
|
|
335
483
|
this.createIndexIfMissing('idx_injections_session_handler', `CREATE INDEX IF NOT EXISTS idx_injections_session_handler ON injections(session_id, source_handler)`);
|
|
336
484
|
this.createIndexIfMissing('idx_routing_events_type_ts', `CREATE INDEX IF NOT EXISTS idx_routing_events_type_ts ON routing_events(routed_to_type, ts DESC)`);
|
|
337
485
|
// Outcome instrumentation indexes (v9.x)
|
|
@@ -360,12 +508,17 @@ export class SQLiteBase {
|
|
|
360
508
|
this.backfillSessionsIfNeeded();
|
|
361
509
|
this.rebuildWorkflowRecommendationsCheckIfNeeded();
|
|
362
510
|
this.rebuildOutcomeCheckIfNeeded();
|
|
511
|
+
this.widenDecisionsStatusCheckIfNeeded();
|
|
512
|
+
this.dropDecisionsWorkflowTypeIfNeeded();
|
|
363
513
|
this.dropDeadKbQueryLogColumnsIfNeeded();
|
|
364
514
|
this.dropTokenUsageIfNeeded();
|
|
365
515
|
this.dropRoutingIsForcedIfNeeded();
|
|
366
516
|
this.backfillRoutingRoutedToTypePending();
|
|
367
517
|
this.backfillTasksIsNoiseIfNeeded();
|
|
368
518
|
this.backfillTasksTaskKindIfNeeded();
|
|
519
|
+
this.backfillSkillInvocationsProjectPathIfNeeded();
|
|
520
|
+
this.backfillEventsDecisionIdIfNeeded();
|
|
521
|
+
this.backfillTasksParentIfNeeded();
|
|
369
522
|
const deprecatedTables = [
|
|
370
523
|
'quality_issues', 'distill_results', 'v2_decisions',
|
|
371
524
|
'v2_tool_events', 'experiment_assignments', 'routing_rule_states',
|
|
@@ -577,6 +730,228 @@ export class SQLiteBase {
|
|
|
577
730
|
logger.warn(`[SQLiteStorage] migration: backfill routed_to_type='pending' failed: ${err}`);
|
|
578
731
|
}
|
|
579
732
|
}
|
|
733
|
+
/**
|
|
734
|
+
* One-shot back-fill of skill_invocations.project_path from
|
|
735
|
+
* sessions.project_path (decision 39839e30 Part 2). A skill pull's session_id
|
|
736
|
+
* JOINs sessions to recover the project it belonged to. Idempotent: the WHERE
|
|
737
|
+
* clause restricts to `project_path IS NULL AND session_id IS NOT NULL`, so a
|
|
738
|
+
* second startup touches 0 rows. Rows whose session can't be joined (sentinel
|
|
739
|
+
* session 'cli-no-session', or a session not in the sessions table) stay NULL
|
|
740
|
+
* — we deliberately do NOT invent a project for them.
|
|
741
|
+
*
|
|
742
|
+
* Errors are warn-logged and swallowed — daemon startup must not fail on a
|
|
743
|
+
* data back-fill; worst case some legacy rows stay project_path=NULL and only
|
|
744
|
+
* the all-projects view counts them.
|
|
745
|
+
*/
|
|
746
|
+
backfillSkillInvocationsProjectPathIfNeeded() {
|
|
747
|
+
if (!this.hasTable('skill_invocations'))
|
|
748
|
+
return;
|
|
749
|
+
if (!this.hasColumn('skill_invocations', 'project_path'))
|
|
750
|
+
return;
|
|
751
|
+
try {
|
|
752
|
+
const res = this.db.prepare(`
|
|
753
|
+
UPDATE skill_invocations
|
|
754
|
+
SET project_path = (
|
|
755
|
+
SELECT s.project_path FROM sessions s
|
|
756
|
+
WHERE s.session_id = skill_invocations.session_id
|
|
757
|
+
)
|
|
758
|
+
WHERE project_path IS NULL
|
|
759
|
+
AND session_id IS NOT NULL
|
|
760
|
+
AND EXISTS (
|
|
761
|
+
SELECT 1 FROM sessions s2
|
|
762
|
+
WHERE s2.session_id = skill_invocations.session_id
|
|
763
|
+
AND s2.project_path IS NOT NULL
|
|
764
|
+
)
|
|
765
|
+
`).run();
|
|
766
|
+
if (res.changes > 0) {
|
|
767
|
+
logger.info(`[SQLiteStorage] migration: back-filled skill_invocations.project_path on ${res.changes} rows from sessions`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
catch (err) {
|
|
771
|
+
logger.warn(`[SQLiteStorage] migration: backfill skill_invocations.project_path failed: ${err}`);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* decision f47c2c90 (二期B, spec 1711 § Phase 1.1): one-shot, idempotent,
|
|
776
|
+
* gzip-free backfill of `tasks.parent_task_id` for recent ack/continue-class
|
|
777
|
+
* follow-up tasks that have no parent linked yet.
|
|
778
|
+
*
|
|
779
|
+
* Why gzip-free + weaker than go-forward: this runs over HISTORY, which has no
|
|
780
|
+
* in-memory `agentJustReturned` flag to read (R2). It uses a pure-SQL heuristic
|
|
781
|
+
* over the plaintext tasks columns ONLY (title / start_time / session_id —
|
|
782
|
+
* never the gzip events.tool_input, R7). Conservatism over recall:
|
|
783
|
+
* - child candidate: task_kind='user', parent_task_id IS NULL, title is a
|
|
784
|
+
* short ack token (TASK_PARENT_ACK_TITLES), start_time within the window.
|
|
785
|
+
* - parent: the MOST RECENT same-session task_kind='user' task whose
|
|
786
|
+
* start_time is STRICTLY earlier than the child AND within 30min AND whose
|
|
787
|
+
* title is NOT itself an ack token (avoid ack→ack chains, prefer a
|
|
788
|
+
* substantive originating task). No qualifying parent → stays NULL.
|
|
789
|
+
* - never self-links (parent.id != child.id is implied by strict earlier
|
|
790
|
+
* start_time + the explicit guard below).
|
|
791
|
+
*
|
|
792
|
+
* Idempotent gate: only runs when at least one un-linked recent ack child
|
|
793
|
+
* exists; the UPDATE itself only touches `parent_task_id IS NULL` rows, so a
|
|
794
|
+
* second boot updates 0 rows (success criterion: `backfilled 0 rows`).
|
|
795
|
+
*
|
|
796
|
+
* Wrapped in a transaction. Errors are warn-logged and swallowed — daemon
|
|
797
|
+
* startup must not fail on a data backfill. Time math uses
|
|
798
|
+
* `datetime('now', ?)` + ISO lexical comparison (tasks.start_time is ISO TEXT;
|
|
799
|
+
* NO numeric probe — DB column type landmine, MEMORY).
|
|
800
|
+
*/
|
|
801
|
+
backfillTasksParentIfNeeded() {
|
|
802
|
+
if (!this.hasTable('tasks'))
|
|
803
|
+
return;
|
|
804
|
+
if (!this.hasColumn('tasks', 'parent_task_id'))
|
|
805
|
+
return;
|
|
806
|
+
try {
|
|
807
|
+
const ackPh = TASK_PARENT_ACK_TITLES.map(() => '?').join(',');
|
|
808
|
+
// Idempotent gate: any un-linked recent ack child left to consider?
|
|
809
|
+
const pending = this.db.prepare(`SELECT 1 FROM tasks
|
|
810
|
+
WHERE task_kind = 'user'
|
|
811
|
+
AND parent_task_id IS NULL
|
|
812
|
+
AND LOWER(trim(title)) IN (${ackPh})
|
|
813
|
+
AND start_time >= datetime('now', ?)
|
|
814
|
+
LIMIT 1`).get(...TASK_PARENT_ACK_TITLES, TASK_PARENT_BACKFILL_WINDOW);
|
|
815
|
+
if (!pending)
|
|
816
|
+
return;
|
|
817
|
+
// Correlated UPDATE: for each eligible ack child, pick the most recent
|
|
818
|
+
// same-session non-ack user task strictly earlier and within 30min.
|
|
819
|
+
const upd = this.db.prepare(`UPDATE tasks AS child
|
|
820
|
+
SET parent_task_id = (
|
|
821
|
+
SELECT p.id FROM tasks p
|
|
822
|
+
WHERE p.session_id = child.session_id
|
|
823
|
+
AND p.task_kind = 'user'
|
|
824
|
+
AND p.id != child.id
|
|
825
|
+
AND p.start_time < child.start_time
|
|
826
|
+
AND p.start_time >= datetime(child.start_time, '-${TASK_PARENT_BACKFILL_WINDOW_MIN} minutes')
|
|
827
|
+
AND LOWER(trim(p.title)) NOT IN (${ackPh})
|
|
828
|
+
ORDER BY p.start_time DESC
|
|
829
|
+
LIMIT 1
|
|
830
|
+
)
|
|
831
|
+
WHERE child.task_kind = 'user'
|
|
832
|
+
AND child.parent_task_id IS NULL
|
|
833
|
+
AND LOWER(trim(child.title)) IN (${ackPh})
|
|
834
|
+
AND child.start_time >= datetime('now', ?)
|
|
835
|
+
AND EXISTS (
|
|
836
|
+
SELECT 1 FROM tasks p2
|
|
837
|
+
WHERE p2.session_id = child.session_id
|
|
838
|
+
AND p2.task_kind = 'user'
|
|
839
|
+
AND p2.id != child.id
|
|
840
|
+
AND p2.start_time < child.start_time
|
|
841
|
+
AND p2.start_time >= datetime(child.start_time, '-${TASK_PARENT_BACKFILL_WINDOW_MIN} minutes')
|
|
842
|
+
AND LOWER(trim(p2.title)) NOT IN (${ackPh})
|
|
843
|
+
)`);
|
|
844
|
+
let changes = 0;
|
|
845
|
+
const tx = this.db.transaction(() => {
|
|
846
|
+
const res = upd.run(...TASK_PARENT_ACK_TITLES, // parent NOT IN (subquery)
|
|
847
|
+
...TASK_PARENT_ACK_TITLES, // child IN (WHERE)
|
|
848
|
+
TASK_PARENT_BACKFILL_WINDOW, ...TASK_PARENT_ACK_TITLES);
|
|
849
|
+
changes = res.changes;
|
|
850
|
+
});
|
|
851
|
+
tx();
|
|
852
|
+
if (changes > 0) {
|
|
853
|
+
logger.info(`[SQLiteStorage] migration: backfilled tasks.parent_task_id on ${changes} rows`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
catch (err) {
|
|
857
|
+
logger.warn(`[SQLiteStorage] migration: backfillTasksParentIfNeeded failed: ${err}`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* decision c425f26f (spec 1544 § 1.3): one-shot, idempotent backfill of the
|
|
862
|
+
* PLAINTEXT `events.decision_id` / `events.subagent_type` columns for recent
|
|
863
|
+
* Task/Agent spawn rows whose values still live only inside the gzip
|
|
864
|
+
* `tool_input` blob.
|
|
865
|
+
*
|
|
866
|
+
* Idempotent gate: only spawn rows in the backfill window where BOTH new
|
|
867
|
+
* columns are still NULL are touched, so a second daemon boot updates 0 rows
|
|
868
|
+
* (success criterion: `backfilled 0 rows`).
|
|
869
|
+
*
|
|
870
|
+
* gzip one-way-door: we DECODE `tool_input` via the codec (sniffs gzip magic),
|
|
871
|
+
* run `extractSpawnFields` on the decoded object, then write the extracted
|
|
872
|
+
* strings to the plaintext columns. The columns themselves never see gzip.
|
|
873
|
+
* Rows whose `tool_input` was already NULLed by the warm-TTL sweep have
|
|
874
|
+
* nothing to decode and are skipped (decode → undefined → both NULL, but the
|
|
875
|
+
* WHERE already excludes most via `tool_input IS NOT NULL`).
|
|
876
|
+
*
|
|
877
|
+
* Per-row try/catch: one malformed gzip blob never aborts the batch. Wrapped
|
|
878
|
+
* in a single transaction. Errors are warn-logged and swallowed — daemon
|
|
879
|
+
* startup must not fail on a data backfill.
|
|
880
|
+
*
|
|
881
|
+
* This is the boot-time wrapper (gated). The re-runnable engine lives in
|
|
882
|
+
* {@link backfillEventsDecisionId} so `cf maintenance backfill-spawn-fields`
|
|
883
|
+
* can force a full re-scan of the window.
|
|
884
|
+
*/
|
|
885
|
+
backfillEventsDecisionIdIfNeeded() {
|
|
886
|
+
if (!this.hasTable('events'))
|
|
887
|
+
return;
|
|
888
|
+
if (!this.hasColumn('events', 'decision_id') || !this.hasColumn('events', 'subagent_type'))
|
|
889
|
+
return;
|
|
890
|
+
try {
|
|
891
|
+
// Idempotent gate: any un-backfilled recent spawn row left?
|
|
892
|
+
const pending = this.db.prepare(`SELECT 1 FROM events
|
|
893
|
+
WHERE hook_type = 'PreToolUse'
|
|
894
|
+
AND tool_name IN ('Task','Agent')
|
|
895
|
+
AND tool_input IS NOT NULL
|
|
896
|
+
AND decision_id IS NULL AND subagent_type IS NULL
|
|
897
|
+
AND timestamp >= datetime('now', ?)
|
|
898
|
+
LIMIT 1`).get(SPAWN_FIELD_BACKFILL_WINDOW);
|
|
899
|
+
if (!pending)
|
|
900
|
+
return;
|
|
901
|
+
const n = this.backfillEventsDecisionId();
|
|
902
|
+
if (n > 0) {
|
|
903
|
+
logger.info(`[SQLiteStorage] migration: backfilled events.decision_id/subagent_type on ${n} rows`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
catch (err) {
|
|
907
|
+
logger.warn(`[SQLiteStorage] migration: backfillEventsDecisionIdIfNeeded failed: ${err}`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Re-runnable engine for the decision_id / subagent_type backfill (decision
|
|
912
|
+
* c425f26f, user ruling #3 manual fallback). Scans the backfill window for
|
|
913
|
+
* Pre/Post Task/Agent spawn rows that still have NULL plaintext columns,
|
|
914
|
+
* decodes their gzip `tool_input`, and writes the extracted fields. Returns
|
|
915
|
+
* the number of rows updated. Safe to call repeatedly (idempotent: only NULL
|
|
916
|
+
* rows are touched). Exposed via the SQLiteStorage facade for the
|
|
917
|
+
* `cf maintenance backfill-spawn-fields` command.
|
|
918
|
+
*/
|
|
919
|
+
backfillEventsDecisionId() {
|
|
920
|
+
if (!this.hasTable('events'))
|
|
921
|
+
return 0;
|
|
922
|
+
if (!this.hasColumn('events', 'decision_id') || !this.hasColumn('events', 'subagent_type'))
|
|
923
|
+
return 0;
|
|
924
|
+
const rows = this.db.prepare(`SELECT event_id, tool_name, tool_input FROM events
|
|
925
|
+
WHERE hook_type IN ('PreToolUse','PostToolUse')
|
|
926
|
+
AND tool_name IN ('Task','Agent')
|
|
927
|
+
AND tool_input IS NOT NULL
|
|
928
|
+
AND decision_id IS NULL AND subagent_type IS NULL
|
|
929
|
+
AND timestamp >= datetime('now', ?)`).all(SPAWN_FIELD_BACKFILL_WINDOW);
|
|
930
|
+
if (rows.length === 0)
|
|
931
|
+
return 0;
|
|
932
|
+
const upd = this.db.prepare(`UPDATE events SET decision_id = ?, subagent_type = ? WHERE event_id = ?`);
|
|
933
|
+
let updated = 0;
|
|
934
|
+
const tx = this.db.transaction(() => {
|
|
935
|
+
for (const row of rows) {
|
|
936
|
+
try {
|
|
937
|
+
const decoded = decodeToolInput(row.tool_input);
|
|
938
|
+
const { decisionId, subagentType } = extractSpawnFields(row.tool_name, decoded);
|
|
939
|
+
// Only write when we actually extracted something — leaving both NULL
|
|
940
|
+
// for unparseable / field-less rows keeps the idempotent gate honest
|
|
941
|
+
// (they'd otherwise be re-scanned but never spuriously marked done).
|
|
942
|
+
if (decisionId === null && subagentType === null)
|
|
943
|
+
continue;
|
|
944
|
+
upd.run(decisionId, subagentType, row.event_id);
|
|
945
|
+
updated++;
|
|
946
|
+
}
|
|
947
|
+
catch (err) {
|
|
948
|
+
logger.warn(`[SQLiteStorage] backfillEventsDecisionId: skip row ${row.event_id}: ${err}`);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
tx();
|
|
953
|
+
return updated;
|
|
954
|
+
}
|
|
580
955
|
/**
|
|
581
956
|
* Migration 010: widen workflow_recommendations.outcome CHECK to include
|
|
582
957
|
* 'partial_accepted'. SQLite cannot ALTER a CHECK constraint in place — we
|
|
@@ -745,6 +1120,154 @@ export class SQLiteBase {
|
|
|
745
1120
|
}
|
|
746
1121
|
}
|
|
747
1122
|
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Migration 012 (decision 0fc1883c, 2026-06-08): DROP the dead
|
|
1125
|
+
* `decisions.workflow_type` column. It was hard-coded to the literal
|
|
1126
|
+
* 'pending' by decision-hint.ts (daemon no longer pre-classifies) and had
|
|
1127
|
+
* ZERO consumers — no UPDATE path, no business logic branch. (The
|
|
1128
|
+
* `intent.workflow_type` read by rules/pipeline-rollup is a DIFFERENT field
|
|
1129
|
+
* sourced from injection metadata, NOT this column.)
|
|
1130
|
+
*
|
|
1131
|
+
* SQLite cannot reliably DROP COLUMN on older engines, so we mirror the
|
|
1132
|
+
* migration-010/011 table-rebuild pattern: read the table's stored CREATE
|
|
1133
|
+
* statement, strip the `workflow_type` line, CREATE the temp table, copy
|
|
1134
|
+
* every OTHER column by EXPLICIT name (never `SELECT *` — the live column
|
|
1135
|
+
* ORDER differs from schema.sql because migration-added columns are appended),
|
|
1136
|
+
* DROP old, RENAME, replay indexes.
|
|
1137
|
+
*
|
|
1138
|
+
* Robustness choices:
|
|
1139
|
+
* - **Idempotent**: guarded by hasColumn — if workflow_type is already gone
|
|
1140
|
+
* (fresh DB from current schema.sql, or a prior boot) this is a no-op.
|
|
1141
|
+
* - **Data-preserving**: copies all non-workflow_type columns by name, so
|
|
1142
|
+
* every decision row keeps its other fields verbatim.
|
|
1143
|
+
* - **Index-preserving**: snapshots user indexes before the drop and replays
|
|
1144
|
+
* each `sql` after rename (made IF NOT EXISTS so schema.sql's recreate on
|
|
1145
|
+
* the same boot doesn't clash).
|
|
1146
|
+
*
|
|
1147
|
+
* Errors are warn-logged, never thrown — daemon startup must not fail on a
|
|
1148
|
+
* migration. Worst case the dead column lingers (harmless — nothing reads it).
|
|
1149
|
+
*/
|
|
1150
|
+
/**
|
|
1151
|
+
* Migration 013 (decision 93e8fff1, 2026-06-08): widen the
|
|
1152
|
+
* `decisions.status` CHECK to include 'advisory'. The advisory mint default
|
|
1153
|
+
* (writeDecision) writes status='advisory', which the legacy CHECK
|
|
1154
|
+
* (`status IN ('pending','resolved','bypassed','expired')`) would REJECT on
|
|
1155
|
+
* existing live DBs — SQLite cannot ALTER a CHECK in place, so we mirror the
|
|
1156
|
+
* migration-011/012 table-rebuild pattern: read the stored CREATE, string-
|
|
1157
|
+
* replace ONLY the status CHECK fragment, CREATE temp → INSERT SELECT * →
|
|
1158
|
+
* DROP → RENAME → replay indexes.
|
|
1159
|
+
*
|
|
1160
|
+
* Robustness:
|
|
1161
|
+
* - **Idempotent**: detected by sniffing the stored CREATE for the legacy
|
|
1162
|
+
* status enum WITHOUT 'advisory'. New DBs (schema.sql already carries
|
|
1163
|
+
* 'advisory') and a second boot are no-ops.
|
|
1164
|
+
* - **Data-preserving**: copies every column verbatim via `SELECT *`.
|
|
1165
|
+
* - **Index-preserving**: snapshots non-autoindexes before the drop, replays
|
|
1166
|
+
* each (made IF NOT EXISTS so schema.sql's recreate on the same boot is
|
|
1167
|
+
* harmless).
|
|
1168
|
+
*
|
|
1169
|
+
* Errors are warn-logged, never thrown — daemon startup must not fail on a
|
|
1170
|
+
* migration. Worst case the old CHECK lingers and a fresh advisory write is
|
|
1171
|
+
* rejected at write time (writeDecision callers fail-silent), and the operator
|
|
1172
|
+
* can re-run after the next build.
|
|
1173
|
+
*/
|
|
1174
|
+
widenDecisionsStatusCheckIfNeeded() {
|
|
1175
|
+
try {
|
|
1176
|
+
if (!this.hasTable('decisions'))
|
|
1177
|
+
return;
|
|
1178
|
+
const sqlRow = this.db
|
|
1179
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='decisions'`)
|
|
1180
|
+
.get();
|
|
1181
|
+
if (!sqlRow || !sqlRow.sql)
|
|
1182
|
+
return;
|
|
1183
|
+
// Already widened? The new CHECK contains 'advisory'. Skip if present.
|
|
1184
|
+
if (/status\s+IN\s*\([^)]*'advisory'/i.test(sqlRow.sql))
|
|
1185
|
+
return;
|
|
1186
|
+
// Only act when the legacy status CHECK enum is present.
|
|
1187
|
+
if (!/status\s+IN\s*\(\s*'pending'/i.test(sqlRow.sql))
|
|
1188
|
+
return;
|
|
1189
|
+
logger.info('[SQLiteStorage] migration 013: widening decisions.status CHECK to include advisory via table rebuild');
|
|
1190
|
+
const newTableName = 'decisions_st_new';
|
|
1191
|
+
const newCreate = sqlRow.sql
|
|
1192
|
+
.replace(
|
|
1193
|
+
// Match the status CHECK fragment regardless of internal whitespace.
|
|
1194
|
+
/status\s+IN\s*\(\s*'pending'\s*,\s*'resolved'\s*,\s*'bypassed'\s*,\s*'expired'\s*\)/i, "status IN ('advisory','pending','resolved','bypassed','expired')")
|
|
1195
|
+
.replace(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`]?decisions["'`]?/i, `CREATE TABLE ${newTableName}`);
|
|
1196
|
+
if (newCreate === sqlRow.sql || !/'advisory'/i.test(newCreate)) {
|
|
1197
|
+
logger.warn('[SQLiteStorage] migration 013: could not rewrite decisions.status CHECK (unexpected DDL shape) — skipping');
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const indexRows = this.db
|
|
1201
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='decisions' AND sql IS NOT NULL`)
|
|
1202
|
+
.all();
|
|
1203
|
+
const rebuild = this.db.transaction(() => {
|
|
1204
|
+
this.db.exec(newCreate);
|
|
1205
|
+
this.db.exec(`INSERT INTO ${newTableName} SELECT * FROM decisions;`);
|
|
1206
|
+
this.db.exec(`DROP TABLE decisions;`);
|
|
1207
|
+
this.db.exec(`ALTER TABLE ${newTableName} RENAME TO decisions;`);
|
|
1208
|
+
for (const idx of indexRows) {
|
|
1209
|
+
const ddl = idx.sql.replace(/CREATE\s+(UNIQUE\s+)?INDEX\s+/i, (m) => /IF\s+NOT\s+EXISTS/i.test(idx.sql) ? m : `${m}IF NOT EXISTS `);
|
|
1210
|
+
this.db.exec(ddl);
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
rebuild();
|
|
1214
|
+
logger.info('[SQLiteStorage] migration 013: decisions.status CHECK widened');
|
|
1215
|
+
}
|
|
1216
|
+
catch (err) {
|
|
1217
|
+
logger.warn(`[SQLiteStorage] migration 013: widen status CHECK failed: ${err}`);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
dropDecisionsWorkflowTypeIfNeeded() {
|
|
1221
|
+
try {
|
|
1222
|
+
if (!this.hasTable('decisions'))
|
|
1223
|
+
return;
|
|
1224
|
+
if (!this.hasColumn('decisions', 'workflow_type'))
|
|
1225
|
+
return; // already dropped
|
|
1226
|
+
const sqlRow = this.db
|
|
1227
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='decisions'`)
|
|
1228
|
+
.get();
|
|
1229
|
+
if (!sqlRow || !sqlRow.sql)
|
|
1230
|
+
return;
|
|
1231
|
+
logger.info('[SQLiteStorage] migration 012: dropping dead decisions.workflow_type column via table rebuild');
|
|
1232
|
+
const newTableName = 'decisions_wf_new';
|
|
1233
|
+
// Strip the entire `workflow_type ...` column line (up to and including
|
|
1234
|
+
// its trailing comma + newline). The column is declared NOT NULL with a
|
|
1235
|
+
// trailing comment, e.g.:
|
|
1236
|
+
// workflow_type TEXT NOT NULL, -- intent.workflow_type (...)
|
|
1237
|
+
let newCreate = sqlRow.sql.replace(/^[ \t]*workflow_type\b[^\n]*\n/im, '');
|
|
1238
|
+
// Point CREATE at the temp table (first occurrence of the table name).
|
|
1239
|
+
newCreate = newCreate.replace(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`]?decisions["'`]?/i, `CREATE TABLE ${newTableName}`);
|
|
1240
|
+
if (newCreate === sqlRow.sql || /\bworkflow_type\b/.test(newCreate)) {
|
|
1241
|
+
logger.warn('[SQLiteStorage] migration 012: could not strip decisions.workflow_type from DDL (unexpected shape) — skipping');
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
// Explicit column list (every column EXCEPT workflow_type), preserving the
|
|
1245
|
+
// live table's order. Never `SELECT *` — live order != schema.sql order.
|
|
1246
|
+
const liveCols = this.db.prepare(`PRAGMA table_info(decisions)`).all()
|
|
1247
|
+
.map((c) => c.name)
|
|
1248
|
+
.filter((name) => name !== 'workflow_type');
|
|
1249
|
+
const colList = liveCols.join(', ');
|
|
1250
|
+
// Snapshot user indexes (auto-indexes have NULL sql and are skipped).
|
|
1251
|
+
const indexRows = this.db
|
|
1252
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='decisions' AND sql IS NOT NULL`)
|
|
1253
|
+
.all();
|
|
1254
|
+
const rebuild = this.db.transaction(() => {
|
|
1255
|
+
this.db.exec(newCreate);
|
|
1256
|
+
this.db.exec(`INSERT INTO ${newTableName} (${colList}) SELECT ${colList} FROM decisions;`);
|
|
1257
|
+
this.db.exec(`DROP TABLE decisions;`);
|
|
1258
|
+
this.db.exec(`ALTER TABLE ${newTableName} RENAME TO decisions;`);
|
|
1259
|
+
for (const idx of indexRows) {
|
|
1260
|
+
const ddl = idx.sql.replace(/CREATE\s+(UNIQUE\s+)?INDEX\s+/i, (m) => /IF\s+NOT\s+EXISTS/i.test(idx.sql) ? m : `${m}IF NOT EXISTS `);
|
|
1261
|
+
this.db.exec(ddl);
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
rebuild();
|
|
1265
|
+
logger.info('[SQLiteStorage] migration 012: decisions.workflow_type dropped');
|
|
1266
|
+
}
|
|
1267
|
+
catch (err) {
|
|
1268
|
+
logger.warn(`[SQLiteStorage] migration 012: drop workflow_type failed: ${err}`);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
748
1271
|
/**
|
|
749
1272
|
* 将 events 表按 session_id 聚合回填到 sessions 表,仅在 sessions 尚未对齐
|
|
750
1273
|
* (没有任何一行带 first_prompt)时触发。
|