@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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* 拆分自 sqlite.ts。
|
|
5
5
|
*/
|
|
6
6
|
import { decodeToolInput, decodeToolOutput } from './codec/tool-input-codec.js';
|
|
7
|
+
import { DAY_MS } from '../utils/time.js';
|
|
7
8
|
const ALL_TASK_KINDS = ['user', 'agent-callback', 'image', 'system'];
|
|
8
9
|
export class TaskOperations {
|
|
9
10
|
db;
|
|
@@ -27,9 +28,212 @@ export class TaskOperations {
|
|
|
27
28
|
// assumption that pre-classification rows ARE user-driven work.
|
|
28
29
|
const kind = task.task_kind ?? 'user';
|
|
29
30
|
this.db.prepare(`
|
|
30
|
-
INSERT INTO tasks (id, session_id, title, start_time, is_noise, task_kind)
|
|
31
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
32
|
-
`).run(task.id, task.session_id, task.title, task.start_time, task.is_noise ? 1 : 0, kind);
|
|
31
|
+
INSERT INTO tasks (id, session_id, title, start_time, is_noise, task_kind, parent_task_id)
|
|
32
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
33
|
+
`).run(task.id, task.session_id, task.title, task.start_time, task.is_noise ? 1 : 0, kind, task.parent_task_id ?? null);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* decision f47c2c90 (二期B, spec 1711 § Phase 2): link a task to its parent
|
|
37
|
+
* user task, but ONLY when no parent is set yet (idempotent — never overwrites
|
|
38
|
+
* an existing link). Also refuses to self-link (parentTaskId === taskId). This
|
|
39
|
+
* is the resume-path补链 hook; go-forward创建路径 passes parent_task_id directly
|
|
40
|
+
* to writeTask instead. Defense-in-depth for R4 (no cycles / no self-link).
|
|
41
|
+
*/
|
|
42
|
+
setTaskParent(taskId, parentTaskId) {
|
|
43
|
+
if (!taskId || !parentTaskId || taskId === parentTaskId)
|
|
44
|
+
return;
|
|
45
|
+
this.db.prepare(`UPDATE tasks SET parent_task_id = ? WHERE id = ? AND parent_task_id IS NULL`).run(parentTaskId, taskId);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* decision f47c2c90 (二期B, spec 1711 § Phase 2): find the most recent prior
|
|
49
|
+
* user task in the same session for the triple-gate parent linkage.
|
|
50
|
+
*
|
|
51
|
+
* Conservative selection (mirrors the backfill heuristic):
|
|
52
|
+
* - same session, task_kind='user'
|
|
53
|
+
* - start_time STRICTLY earlier than `beforeIso` (parent must precede child)
|
|
54
|
+
* - start_time within `windowMs` of `beforeIso` (default 30min)
|
|
55
|
+
* - PREFERS a non-ack-titled task as the substantive originator; falls back
|
|
56
|
+
* to the most recent qualifying user task if none is non-ack.
|
|
57
|
+
*
|
|
58
|
+
* Returns the task id, or null when no qualifying prior task exists.
|
|
59
|
+
* Time math uses ISO lexical comparison (start_time is ISO TEXT — no numeric
|
|
60
|
+
* probe, DB column type landmine). `ackTitles` is the lowercase ack-token set
|
|
61
|
+
* supplied by the caller (single source of truth lives in the segmenter).
|
|
62
|
+
*/
|
|
63
|
+
getMostRecentUserTaskBefore(opts) {
|
|
64
|
+
const sinceIso = new Date(Date.parse(opts.beforeIso) - opts.windowMs).toISOString();
|
|
65
|
+
const ackPh = opts.ackTitles.length > 0 ? opts.ackTitles.map(() => '?').join(',') : `''`;
|
|
66
|
+
// Prefer a non-ack-titled originator (avoid ack→ack chains).
|
|
67
|
+
const nonAckSql = `
|
|
68
|
+
SELECT id FROM tasks
|
|
69
|
+
WHERE session_id = ?
|
|
70
|
+
AND task_kind = 'user'
|
|
71
|
+
AND start_time < ?
|
|
72
|
+
AND start_time >= ?
|
|
73
|
+
${opts.ackTitles.length > 0 ? `AND LOWER(trim(title)) NOT IN (${ackPh})` : ''}
|
|
74
|
+
ORDER BY start_time DESC
|
|
75
|
+
LIMIT 1`;
|
|
76
|
+
const nonAckParams = [opts.session_id, opts.beforeIso, sinceIso];
|
|
77
|
+
if (opts.ackTitles.length > 0)
|
|
78
|
+
nonAckParams.push(...opts.ackTitles);
|
|
79
|
+
const nonAck = this.db.prepare(nonAckSql).get(...nonAckParams);
|
|
80
|
+
if (nonAck?.id)
|
|
81
|
+
return nonAck.id;
|
|
82
|
+
// Fallback: most recent qualifying user task regardless of title.
|
|
83
|
+
const any = this.db.prepare(`
|
|
84
|
+
SELECT id FROM tasks
|
|
85
|
+
WHERE session_id = ?
|
|
86
|
+
AND task_kind = 'user'
|
|
87
|
+
AND start_time < ?
|
|
88
|
+
AND start_time >= ?
|
|
89
|
+
ORDER BY start_time DESC
|
|
90
|
+
LIMIT 1
|
|
91
|
+
`).get(opts.session_id, opts.beforeIso, sinceIso);
|
|
92
|
+
return any?.id ?? null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* decision f47c2c90 (二期B, spec 1711 § Phase 3.1): aggregate ALL Task/Agent
|
|
96
|
+
* spawns across the parent_task_id chain that `anchorTaskId` belongs to —
|
|
97
|
+
* the timeline's fallback for workflows that have NO decision_id.
|
|
98
|
+
*
|
|
99
|
+
* Algorithm (two bounded recursive CTEs, R4 cycle-safe):
|
|
100
|
+
* 1. 向上找根: walk parent_task_id up from the anchor to the root
|
|
101
|
+
* (parent_task_id IS NULL), depth ≤ 8.
|
|
102
|
+
* 2. 向下收子: from that root, collect all descendants (depth ≤ 8).
|
|
103
|
+
* The union of both walks is the chain's task-id set. SQLite recursive CTEs
|
|
104
|
+
* with UNION (set semantics) dedup automatically; the explicit depth column
|
|
105
|
+
* guard caps a pathological cycle (a.parent=b, b.parent=a) at 8 hops so the
|
|
106
|
+
* query always terminates with a finite result.
|
|
107
|
+
*
|
|
108
|
+
* Output shape is IDENTICAL to EventOperations.queryWorkflowSpawnsByDecisionId
|
|
109
|
+
* so task-timeline.ts can feed both through the same buildAgentSpawnEvents
|
|
110
|
+
* helper. subagent_type is read from the PLAINTEXT column (no gzip);
|
|
111
|
+
* tool_input / tool_output are decoded via the codec (same path as the A query)
|
|
112
|
+
* for the prompt / output previews. task_count = distinct chain task ids that
|
|
113
|
+
* actually carry ≥1 spawn (the "跨 N 轮" N for the B path).
|
|
114
|
+
*
|
|
115
|
+
* gzip note: only tool_input/tool_output flow through the codec; the chain walk
|
|
116
|
+
* itself touches plaintext tasks columns only (R7-safe).
|
|
117
|
+
*/
|
|
118
|
+
queryParentTaskChainSpawns(anchorTaskId) {
|
|
119
|
+
if (!anchorTaskId)
|
|
120
|
+
return { spawns: [], task_count: 0 };
|
|
121
|
+
// Collect the full chain id-set (up to root + down to descendants), depth≤8,
|
|
122
|
+
// cycle-safe via the depth guard + UNION dedup.
|
|
123
|
+
const chainRows = this.db.prepare(`
|
|
124
|
+
WITH RECURSIVE
|
|
125
|
+
ancestors(id, depth) AS (
|
|
126
|
+
SELECT id, 0 FROM tasks WHERE id = ?
|
|
127
|
+
UNION
|
|
128
|
+
SELECT t.parent_task_id, a.depth + 1
|
|
129
|
+
FROM tasks t JOIN ancestors a ON t.id = a.id
|
|
130
|
+
WHERE t.parent_task_id IS NOT NULL AND a.depth < 8
|
|
131
|
+
),
|
|
132
|
+
root(id) AS (
|
|
133
|
+
-- the topmost ancestor reached (smallest remaining parent); we take
|
|
134
|
+
-- ALL ancestors as roots-of-subtree to be safe, then collect descendants
|
|
135
|
+
SELECT id FROM ancestors
|
|
136
|
+
),
|
|
137
|
+
descendants(id, depth) AS (
|
|
138
|
+
SELECT id, 0 FROM root
|
|
139
|
+
UNION
|
|
140
|
+
SELECT t.id, d.depth + 1
|
|
141
|
+
FROM tasks t JOIN descendants d ON t.parent_task_id = d.id
|
|
142
|
+
WHERE d.depth < 8
|
|
143
|
+
)
|
|
144
|
+
SELECT DISTINCT id FROM (
|
|
145
|
+
SELECT id FROM ancestors
|
|
146
|
+
UNION
|
|
147
|
+
SELECT id FROM descendants
|
|
148
|
+
) WHERE id IS NOT NULL
|
|
149
|
+
`).all(anchorTaskId);
|
|
150
|
+
const chainIds = chainRows.map((r) => r.id);
|
|
151
|
+
if (chainIds.length === 0)
|
|
152
|
+
return { spawns: [], task_count: 0 };
|
|
153
|
+
const ph = chainIds.map(() => '?').join(',');
|
|
154
|
+
const rows = this.db.prepare(`
|
|
155
|
+
SELECT e.event_id, e.session_id, e.timestamp, e.hook_type, e.subagent_type,
|
|
156
|
+
e.tool_input, e.tool_output, te.task_id AS task_id
|
|
157
|
+
FROM task_events te
|
|
158
|
+
JOIN events e ON e.event_id = te.event_id
|
|
159
|
+
WHERE te.task_id IN (${ph})
|
|
160
|
+
AND e.tool_name IN ('Task','Agent')
|
|
161
|
+
AND e.hook_type IN ('PreToolUse','PostToolUse')
|
|
162
|
+
ORDER BY e.timestamp ASC
|
|
163
|
+
`).all(...chainIds);
|
|
164
|
+
const taskIds = new Set();
|
|
165
|
+
const spawns = rows.map((row) => {
|
|
166
|
+
if (row.task_id)
|
|
167
|
+
taskIds.add(row.task_id);
|
|
168
|
+
let toolInput;
|
|
169
|
+
if (row.tool_input) {
|
|
170
|
+
try {
|
|
171
|
+
toolInput = decodeToolInput(row.tool_input);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
toolInput = undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
let toolOutput;
|
|
178
|
+
if (row.tool_output) {
|
|
179
|
+
try {
|
|
180
|
+
toolOutput = decodeToolOutput(row.tool_output);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
toolOutput = undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
event_id: row.event_id,
|
|
188
|
+
session_id: row.session_id,
|
|
189
|
+
task_id: row.task_id ?? null,
|
|
190
|
+
timestamp: row.timestamp,
|
|
191
|
+
hook_type: row.hook_type,
|
|
192
|
+
subagent_type: row.subagent_type,
|
|
193
|
+
tool_input: toolInput,
|
|
194
|
+
tool_output: toolOutput,
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
return { spawns, task_count: taskIds.size };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Read the plaintext `user_prompt` of every `UserPromptSubmit` event linked to
|
|
201
|
+
* any task in `taskIds` (decision ed5c3e93, spec 0909 per-turn-human-input).
|
|
202
|
+
*
|
|
203
|
+
* Used by the timeline route's two cross-task aggregation arms to collect the
|
|
204
|
+
* per-turn human inputs that live in OTHER per-prompt tasks of the same
|
|
205
|
+
* workflow (the originating task only sees its own first turn). The caller
|
|
206
|
+
* passes the distinct task_id set already computed from the spawn rows.
|
|
207
|
+
*
|
|
208
|
+
* CRITICAL (gzip event-content landmine, MEMORY note): `events.user_prompt` is
|
|
209
|
+
* a PLAINTEXT TEXT column — read it directly. It is NOT a gzip blob; do NOT run
|
|
210
|
+
* it through `decodeToolInput`/`decodeToolOutput` (those are for tool_input /
|
|
211
|
+
* tool_output only).
|
|
212
|
+
*
|
|
213
|
+
* Ordering: `ORDER BY e.timestamp ASC` — timestamp is ISO TEXT, lexically
|
|
214
|
+
* sortable (DB time-column-type landmine: do not treat it as ms INTEGER).
|
|
215
|
+
* Empty `taskIds` → `[]` (avoid an empty `IN ()` clause).
|
|
216
|
+
*/
|
|
217
|
+
queryUserPromptsByTaskIds(taskIds) {
|
|
218
|
+
if (!taskIds || taskIds.length === 0)
|
|
219
|
+
return [];
|
|
220
|
+
const ph = taskIds.map(() => '?').join(',');
|
|
221
|
+
const rows = this.db.prepare(`
|
|
222
|
+
SELECT e.event_id, e.timestamp, e.user_prompt, te.task_id AS task_id
|
|
223
|
+
FROM task_events te
|
|
224
|
+
JOIN events e ON e.event_id = te.event_id
|
|
225
|
+
WHERE te.task_id IN (${ph})
|
|
226
|
+
AND e.hook_type = 'UserPromptSubmit'
|
|
227
|
+
AND e.user_prompt IS NOT NULL
|
|
228
|
+
ORDER BY e.timestamp ASC
|
|
229
|
+
`).all(...taskIds);
|
|
230
|
+
return rows.map((row) => ({
|
|
231
|
+
event_id: row.event_id,
|
|
232
|
+
timestamp: row.timestamp,
|
|
233
|
+
// Plaintext column — direct read, never decoded.
|
|
234
|
+
user_prompt: row.user_prompt,
|
|
235
|
+
task_id: row.task_id ?? null,
|
|
236
|
+
}));
|
|
33
237
|
}
|
|
34
238
|
updateTask(taskId, updates) {
|
|
35
239
|
const sets = [];
|
|
@@ -537,6 +741,52 @@ export class TaskOperations {
|
|
|
537
741
|
`).all(taskId, nowMs);
|
|
538
742
|
return rows;
|
|
539
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* 该任务执行窗内 agent 主动 pull 的 KB(decision 93b3075b)—— 任务详情页右栏
|
|
746
|
+
* 「用了什么 → KB 主动 pull」从「无主动 pull」降级占位接成真实数据。
|
|
747
|
+
*
|
|
748
|
+
* scope 口径(与 querySkillInvocationsByTaskWindow 同思路,但 kb_query_log
|
|
749
|
+
* **没有 session_id 列**,故纯按时间窗 + project 圈到具体任务):
|
|
750
|
+
* - 时间窗:kb_query_log.ts(INTEGER ms) BETWEEN task.start_time(→ms) 和
|
|
751
|
+
* COALESCE(task.end_time→ms+999, now_ms)。task.start/end 是 ISO TEXT,
|
|
752
|
+
* 用 strftime('%s', ...)*1000 转 ms 比较(kb_query_log.ts 地雷:是 ms 整数,
|
|
753
|
+
* 别打成 1970)。end_time IS NULL(active task)→ 取 now_ms 兜底。
|
|
754
|
+
* - **project 圈定**:JOIN tasks t LEFT JOIN sessions s 取 project_path,
|
|
755
|
+
* 仅取 kb_query_log.project_path = 该任务 project 的 pull。本 session 多任务
|
|
756
|
+
* 共享 session_id,且同一时间窗可能并发别项目的 pull(实测 64d37b1d 窗内有
|
|
757
|
+
* /prm 项目的 coder pull),故 project 过滤 + 时间窗一起把 pull 圈到本任务
|
|
758
|
+
* 的本项目,不串别项目/别任务。project_path 为 NULL(孤儿 task)→ 不串任何
|
|
759
|
+
* pull(IS NOT NULL guard)。
|
|
760
|
+
* - **只取真实 agent pull**:reason LIKE '%kb-precedent%'(coder/verify/
|
|
761
|
+
* safetynet/decision 的 pull-reason 约定),与 countAgentKbPulls 同口径,
|
|
762
|
+
* 排掉 `cf knowledge query foo` 探针(同时 NULL-safe 排 sentinel
|
|
763
|
+
* agent_id='cli-no-session',decision 2d6d59b0)。
|
|
764
|
+
*
|
|
765
|
+
* `now_ms` 通过参数注入(end_time IS NULL 时取此值),便于测试 mock。
|
|
766
|
+
*/
|
|
767
|
+
queryTaskKbPulls(taskId, opts = {}) {
|
|
768
|
+
const nowMs = opts.now_ms ?? Date.now();
|
|
769
|
+
const rows = this.db.prepare(`
|
|
770
|
+
SELECT k.ts AS ts, k.query AS query, k.reason AS reason,
|
|
771
|
+
k.workflow AS workflow, k.phase AS phase,
|
|
772
|
+
k.usefulness AS usefulness, k.usefulness_note AS usefulness_note,
|
|
773
|
+
k.result_count AS result_count
|
|
774
|
+
FROM kb_query_log k
|
|
775
|
+
JOIN tasks t ON t.id = ?
|
|
776
|
+
LEFT JOIN sessions s ON s.session_id = t.session_id
|
|
777
|
+
WHERE s.project_path IS NOT NULL
|
|
778
|
+
AND k.project_path = s.project_path
|
|
779
|
+
AND k.reason LIKE '%kb-precedent%'
|
|
780
|
+
AND (k.agent_id IS NULL OR k.agent_id != 'cli-no-session')
|
|
781
|
+
AND k.ts >= CAST(strftime('%s', t.start_time) AS INTEGER) * 1000
|
|
782
|
+
AND k.ts <= COALESCE(
|
|
783
|
+
CAST(strftime('%s', t.end_time) AS INTEGER) * 1000 + 999,
|
|
784
|
+
?
|
|
785
|
+
)
|
|
786
|
+
ORDER BY k.ts ASC
|
|
787
|
+
`).all(taskId, nowMs);
|
|
788
|
+
return rows;
|
|
789
|
+
}
|
|
540
790
|
// ── Agent Board v3 (task-centric, 2026-05-22) ─────────────────────────
|
|
541
791
|
//
|
|
542
792
|
// 每张卡片 = 一个 task 行(TaskSegmenter 自动按 user prompt 切分)。
|
|
@@ -572,7 +822,7 @@ export class TaskOperations {
|
|
|
572
822
|
const limit = Math.min(Math.max(opts.limit ?? 120, 1), 300);
|
|
573
823
|
const offset = Math.max(opts.offset ?? 0, 0);
|
|
574
824
|
const nowMs = opts.now_ms ?? Date.now();
|
|
575
|
-
const sinceMs = nowMs - opts.windowDays *
|
|
825
|
+
const sinceMs = nowMs - opts.windowDays * DAY_MS;
|
|
576
826
|
const since = new Date(sinceMs).toISOString();
|
|
577
827
|
const nowIso = new Date(nowMs).toISOString();
|
|
578
828
|
let projectFilter = '';
|
|
@@ -643,7 +893,8 @@ export class TaskOperations {
|
|
|
643
893
|
const sessionPh = sessionIds.map(() => '?').join(',');
|
|
644
894
|
const toolRows = this.db.prepare(`
|
|
645
895
|
SELECT te.task_id AS task_id, e.tool_name AS tool, COUNT(*) AS cnt
|
|
646
|
-
FROM task_events te
|
|
896
|
+
FROM task_events te INDEXED BY idx_task_events_task
|
|
897
|
+
JOIN events e ON e.event_id = te.event_id
|
|
647
898
|
WHERE te.task_id IN (${taskPh})
|
|
648
899
|
AND e.hook_type = 'PostToolUse'
|
|
649
900
|
AND e.tool_name IS NOT NULL
|
|
@@ -867,6 +1118,95 @@ export class TaskOperations {
|
|
|
867
1118
|
: `AND t.task_kind IN (${effectiveKinds.map(() => '?').join(',')})`;
|
|
868
1119
|
return { effectiveKinds, kindFilter };
|
|
869
1120
|
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Shared outcome-bucket filter resolution (decision 0c1549ec, 2026-06-11).
|
|
1123
|
+
*
|
|
1124
|
+
* The TasksHub「我的任务」list view's outcome chips push their selection to
|
|
1125
|
+
* the backend so filtering happens over the WHOLE window (not the page-local
|
|
1126
|
+
* ~120 rows). The bucket classification here MUST mirror the CASE expression
|
|
1127
|
+
* in queryAgentBoardPromptWindowAggregates so the chip COUNTS and the chip
|
|
1128
|
+
* FILTER agree exactly — otherwise we re-introduce the count/filter 口径
|
|
1129
|
+
* split that commit 40e03283 caused.
|
|
1130
|
+
*
|
|
1131
|
+
* Bucket → predicate over the `t` (tasks) alias:
|
|
1132
|
+
* - 'all' / undefined → '' (no filter, backward compatible)
|
|
1133
|
+
* - 'success'|'partial'|'failed' → t.outcome = '<bucket>'
|
|
1134
|
+
* - 'abandoned' → t.outcome='abandoned' OR (t.outcome IS NULL AND status='abandoned')
|
|
1135
|
+
* - 'answered' → t.outcome='answered'
|
|
1136
|
+
* - 'none' → residual catch-all (the in-progress / no-terminal-outcome
|
|
1137
|
+
* bucket + any unknown/legacy outcome string), exactly the
|
|
1138
|
+
* complement of the five buckets above.
|
|
1139
|
+
* Returns the bare predicate (no leading AND); caller wraps in `AND (...)`.
|
|
1140
|
+
* The `t` alias is parameterless (no bind values), so callers don't push args.
|
|
1141
|
+
*/
|
|
1142
|
+
resolveOutcomeFilter(outcome) {
|
|
1143
|
+
switch (outcome) {
|
|
1144
|
+
case 'success':
|
|
1145
|
+
case 'partial':
|
|
1146
|
+
case 'failed':
|
|
1147
|
+
return `t.outcome = '${outcome}'`;
|
|
1148
|
+
case 'abandoned':
|
|
1149
|
+
return `(t.outcome = 'abandoned' OR (t.outcome IS NULL AND t.status = 'abandoned'))`;
|
|
1150
|
+
case 'answered':
|
|
1151
|
+
return `t.outcome = 'answered'`;
|
|
1152
|
+
case 'none':
|
|
1153
|
+
// Complement of the five named buckets: NULL-and-not-abandoned (active /
|
|
1154
|
+
// pending / in-progress) plus any unknown/legacy outcome string.
|
|
1155
|
+
return `(
|
|
1156
|
+
t.outcome NOT IN ('success','partial','failed','abandoned','answered')
|
|
1157
|
+
OR (t.outcome IS NULL AND t.status <> 'abandoned')
|
|
1158
|
+
OR (t.outcome IS NULL AND t.status IS NULL)
|
|
1159
|
+
)`;
|
|
1160
|
+
case 'all':
|
|
1161
|
+
case undefined:
|
|
1162
|
+
default:
|
|
1163
|
+
return '';
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Shared free-text search filter for the TasksHub「我的任务」list view
|
|
1168
|
+
* (decision 540ed063, 2026-06-11). Same downsink shape as resolveOutcomeFilter
|
|
1169
|
+
* above: the search box pushes its term to the backend so matching happens
|
|
1170
|
+
* over the WHOLE window (every page), not just the page-local ~120 rows a
|
|
1171
|
+
* client `.filter` could see.
|
|
1172
|
+
*
|
|
1173
|
+
* Matching 口径 (must stay in lockstep between query and count):
|
|
1174
|
+
* - case-insensitive substring (`LIKE '%term%'`, LOWER on both sides);
|
|
1175
|
+
* - OR'd across two PLAINTEXT columns — `e.user_prompt` (the prompt text)
|
|
1176
|
+
* and `t.title` (the task title).
|
|
1177
|
+
*
|
|
1178
|
+
* ⚠️ gzip One-way Door (KB landmine): `events.tool_input` / `tool_output` hold
|
|
1179
|
+
* GZIP-compressed blobs (declared TEXT but binary content) — a bare SQL LIKE
|
|
1180
|
+
* over them silently matches nothing. This predicate therefore touches ONLY
|
|
1181
|
+
* the two plaintext columns above and NEVER tool_input/tool_output.
|
|
1182
|
+
*
|
|
1183
|
+
* Empty / whitespace-only term → '' (no predicate), so absent or blank search
|
|
1184
|
+
* is backward-compatible (= no filter). Returns { predicate, params }: the
|
|
1185
|
+
* predicate has two `?` placeholders, so unlike resolveOutcomeFilter the caller
|
|
1186
|
+
* MUST push the two bind values in clause order.
|
|
1187
|
+
*/
|
|
1188
|
+
resolveSearchFilter(search) {
|
|
1189
|
+
const term = (search ?? '').trim();
|
|
1190
|
+
if (!term)
|
|
1191
|
+
return { predicate: '', params: [] };
|
|
1192
|
+
// LIKE-metacharacter hardening (decision 540ed063 follow-up): a user typing a
|
|
1193
|
+
// literal `%` or `_` must NOT get wildcard behavior (`%` matches everything,
|
|
1194
|
+
// `_` matches any single char). Escape the escape char FIRST (so `\` → `\\`),
|
|
1195
|
+
// then `%` and `_`, with backslash as the ESCAPE char declared on each LIKE.
|
|
1196
|
+
// The escaped term is still a bound `?` param (never string-concatenated into
|
|
1197
|
+
// SQL), so this is injection-safe and behavior-preserving for ordinary words
|
|
1198
|
+
// (terms with no `%`/`_`/`\` are unaffected by the replaces).
|
|
1199
|
+
const escaped = term
|
|
1200
|
+
.toLowerCase()
|
|
1201
|
+
.replace(/\\/g, '\\\\')
|
|
1202
|
+
.replace(/%/g, '\\%')
|
|
1203
|
+
.replace(/_/g, '\\_');
|
|
1204
|
+
const like = `%${escaped}%`;
|
|
1205
|
+
return {
|
|
1206
|
+
predicate: "(LOWER(e.user_prompt) LIKE ? ESCAPE '\\' OR LOWER(t.title) LIKE ? ESCAPE '\\')",
|
|
1207
|
+
params: [like, like],
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
870
1210
|
// ── Agent Board prompt-rows projection (decision b509fd91, 2026-06-08) ───
|
|
871
1211
|
//
|
|
872
1212
|
// "一行 = 一条 user prompt" view (P1 方案 B, zero schema). Each row is one
|
|
@@ -889,10 +1229,14 @@ export class TaskOperations {
|
|
|
889
1229
|
const limit = Math.min(Math.max(opts.limit ?? 120, 1), 300);
|
|
890
1230
|
const offset = Math.max(opts.offset ?? 0, 0);
|
|
891
1231
|
const nowMs = opts.now_ms ?? Date.now();
|
|
892
|
-
const sinceMs = nowMs - opts.windowDays *
|
|
1232
|
+
const sinceMs = nowMs - opts.windowDays * DAY_MS;
|
|
893
1233
|
const since = new Date(sinceMs).toISOString();
|
|
894
1234
|
const projectFilter = opts.projectPath ? 'AND s.project_path = ?' : '';
|
|
895
1235
|
const { effectiveKinds, kindFilter } = this.resolveKindFilter(opts);
|
|
1236
|
+
const outcomePred = this.resolveOutcomeFilter(opts.outcome);
|
|
1237
|
+
const outcomeFilter = outcomePred ? `AND ${outcomePred}` : '';
|
|
1238
|
+
const searchClause = this.resolveSearchFilter(opts.search);
|
|
1239
|
+
const searchFilter = searchClause.predicate ? `AND ${searchClause.predicate}` : '';
|
|
896
1240
|
const sql = `
|
|
897
1241
|
SELECT
|
|
898
1242
|
e.event_id AS event_id,
|
|
@@ -915,14 +1259,25 @@ export class TaskOperations {
|
|
|
915
1259
|
CAST(strftime('%s', 'now') AS INTEGER) - CAST(strftime('%s', t.start_time) AS INTEGER)
|
|
916
1260
|
END) AS group_duration_sec
|
|
917
1261
|
FROM events e
|
|
918
|
-
JOIN
|
|
919
|
-
|
|
920
|
-
|
|
1262
|
+
-- perf (decision 2f05b0bf, 2026-06-13): CROSS JOIN pins the join ORDER so
|
|
1263
|
+
-- the small, selective UserPromptSubmit set on events
|
|
1264
|
+
-- (idx_events_hook_type, ~hundreds of rows) drives the join. Without it the
|
|
1265
|
+
-- optimizer picks tasks USING idx_tasks_kind as the outer loop and fans
|
|
1266
|
+
-- out across EVERY task_events row (tens of thousands), ~90ms PER query,
|
|
1267
|
+
-- linear in window. CROSS JOIN is a pure join-order hint (SQLite keeps the
|
|
1268
|
+
-- written table order); the ON/WHERE predicates are unchanged so the result
|
|
1269
|
+
-- set is byte-identical. Profiled 30d window: q1 91ms to under 2ms, +project
|
|
1270
|
+
-- 48ms to 0.7ms, +search 320ms to 0.8ms (all rowsets verified identical).
|
|
1271
|
+
CROSS JOIN task_events te ON te.event_id = e.event_id
|
|
1272
|
+
CROSS JOIN tasks t ON t.id = te.task_id
|
|
1273
|
+
CROSS JOIN sessions s ON s.session_id = t.session_id
|
|
921
1274
|
WHERE e.hook_type = 'UserPromptSubmit'
|
|
922
1275
|
AND e.user_prompt IS NOT NULL
|
|
923
1276
|
AND t.start_time >= ?
|
|
924
1277
|
${projectFilter}
|
|
925
1278
|
${kindFilter}
|
|
1279
|
+
${outcomeFilter}
|
|
1280
|
+
${searchFilter}
|
|
926
1281
|
ORDER BY e.timestamp DESC
|
|
927
1282
|
LIMIT ? OFFSET ?
|
|
928
1283
|
`;
|
|
@@ -931,6 +1286,9 @@ export class TaskOperations {
|
|
|
931
1286
|
params.push(opts.projectPath);
|
|
932
1287
|
if (kindFilter)
|
|
933
1288
|
params.push(...effectiveKinds);
|
|
1289
|
+
// search placeholders come after kind, before LIMIT/OFFSET — clause order.
|
|
1290
|
+
if (searchClause.params.length)
|
|
1291
|
+
params.push(...searchClause.params);
|
|
934
1292
|
params.push(limit, offset);
|
|
935
1293
|
const rows = this.db.prepare(sql).all(...params);
|
|
936
1294
|
return rows.map(r => ({
|
|
@@ -957,30 +1315,139 @@ export class TaskOperations {
|
|
|
957
1315
|
*/
|
|
958
1316
|
countAgentBoardPromptRows(opts) {
|
|
959
1317
|
const nowMs = opts.now_ms ?? Date.now();
|
|
960
|
-
const sinceMs = nowMs - opts.windowDays *
|
|
1318
|
+
const sinceMs = nowMs - opts.windowDays * DAY_MS;
|
|
961
1319
|
const since = new Date(sinceMs).toISOString();
|
|
962
1320
|
const projectFilter = opts.projectPath ? 'AND s.project_path = ?' : '';
|
|
963
1321
|
const { effectiveKinds, kindFilter } = this.resolveKindFilter(opts);
|
|
1322
|
+
const outcomePred = this.resolveOutcomeFilter(opts.outcome);
|
|
1323
|
+
const outcomeFilter = outcomePred ? `AND ${outcomePred}` : '';
|
|
1324
|
+
// Same predicate + same bind values as queryAgentBoardPromptRows — count and
|
|
1325
|
+
// query MUST share one 口径 or the paginator denominator diverges from the rows.
|
|
1326
|
+
const searchClause = this.resolveSearchFilter(opts.search);
|
|
1327
|
+
const searchFilter = searchClause.predicate ? `AND ${searchClause.predicate}` : '';
|
|
964
1328
|
const sql = `
|
|
965
1329
|
SELECT COUNT(*) AS n
|
|
966
1330
|
FROM events e
|
|
967
|
-
JOIN
|
|
968
|
-
|
|
969
|
-
|
|
1331
|
+
-- perf (decision 2f05b0bf): CROSS JOIN pins events-first join order — see
|
|
1332
|
+
-- queryAgentBoardPromptRows for the full rationale (90ms → <2ms; pure
|
|
1333
|
+
-- join-order hint, result count unchanged).
|
|
1334
|
+
CROSS JOIN task_events te ON te.event_id = e.event_id
|
|
1335
|
+
CROSS JOIN tasks t ON t.id = te.task_id
|
|
1336
|
+
CROSS JOIN sessions s ON s.session_id = t.session_id
|
|
970
1337
|
WHERE e.hook_type = 'UserPromptSubmit'
|
|
971
1338
|
AND e.user_prompt IS NOT NULL
|
|
972
1339
|
AND t.start_time >= ?
|
|
973
1340
|
${projectFilter}
|
|
974
1341
|
${kindFilter}
|
|
1342
|
+
${outcomeFilter}
|
|
1343
|
+
${searchFilter}
|
|
975
1344
|
`;
|
|
976
1345
|
const params = [since];
|
|
977
1346
|
if (opts.projectPath)
|
|
978
1347
|
params.push(opts.projectPath);
|
|
979
1348
|
if (kindFilter)
|
|
980
1349
|
params.push(...effectiveKinds);
|
|
1350
|
+
if (searchClause.params.length)
|
|
1351
|
+
params.push(...searchClause.params);
|
|
981
1352
|
const row = this.db.prepare(sql).get(...params);
|
|
982
1353
|
return row?.n ?? 0;
|
|
983
1354
|
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Window-level task aggregates for the prompt-rows list view (2026-06-10).
|
|
1357
|
+
*
|
|
1358
|
+
* Why: the list view's KPI bar ("总任务数 / 失败 / 平均时长") and outcome tab
|
|
1359
|
+
* counts were derived client-side from `queryAgentBoardPromptRows` groups —
|
|
1360
|
+
* i.e. from ONE PAGE (LIMIT default 120) — so they showed page-local numbers
|
|
1361
|
+
* (e.g. 34) instead of true window totals (e.g. 709). This method runs the
|
|
1362
|
+
* SAME WHERE 口径 as countAgentBoardPromptRows (window / project / kind
|
|
1363
|
+
* whitelist, prompt-bearing tasks only) but dedupes to distinct tasks and
|
|
1364
|
+
* aggregates outcome distribution + average duration in SQL.
|
|
1365
|
+
*
|
|
1366
|
+
* Semantics intentionally mirror the page's previous client-side math:
|
|
1367
|
+
* - task_total = COUNT(DISTINCT task) — a task with N prompts counts once.
|
|
1368
|
+
* - outcome_abandoned = outcome='abandoned' OR (outcome IS NULL AND
|
|
1369
|
+
* status='abandoned') — same fold the outcome chips did.
|
|
1370
|
+
* - outcome_answered = outcome='answered' (spec 1100 Option C, 2026-06-02 —
|
|
1371
|
+
* a first-class fifth value; no UI tab, but live data
|
|
1372
|
+
* has hundreds of these so it must not vanish).
|
|
1373
|
+
* - outcome_none = residual catch-all: outcome IS NULL with a
|
|
1374
|
+
* non-abandoned status, OR any unknown/legacy outcome
|
|
1375
|
+
* string (the column CHECK only enforces non-empty).
|
|
1376
|
+
* - avg_duration_sec = AVG over tasks with duration > 0, where duration is
|
|
1377
|
+
* the same CASE expression queryAgentBoardPromptRows
|
|
1378
|
+
* computes for group_duration_sec (end-start, or
|
|
1379
|
+
* now-start while active). Rounded to whole seconds.
|
|
1380
|
+
*
|
|
1381
|
+
* Invariant (tested, holds for ARBITRARY outcome strings thanks to the
|
|
1382
|
+
* catch-all): success+partial+failed+abandoned+answered+none == task_total.
|
|
1383
|
+
* MUST keep the inner WHERE in sync with queryAgentBoardPromptRows /
|
|
1384
|
+
* countAgentBoardPromptRows above.
|
|
1385
|
+
*/
|
|
1386
|
+
queryAgentBoardPromptWindowAggregates(opts) {
|
|
1387
|
+
const nowMs = opts.now_ms ?? Date.now();
|
|
1388
|
+
const sinceMs = nowMs - opts.windowDays * DAY_MS;
|
|
1389
|
+
const since = new Date(sinceMs).toISOString();
|
|
1390
|
+
const projectFilter = opts.projectPath ? 'AND s.project_path = ?' : '';
|
|
1391
|
+
const { effectiveKinds, kindFilter } = this.resolveKindFilter(opts);
|
|
1392
|
+
const sql = `
|
|
1393
|
+
SELECT
|
|
1394
|
+
COUNT(*) AS task_total,
|
|
1395
|
+
SUM(CASE WHEN g.outcome = 'success' THEN 1 ELSE 0 END) AS outcome_success,
|
|
1396
|
+
SUM(CASE WHEN g.outcome = 'partial' THEN 1 ELSE 0 END) AS outcome_partial,
|
|
1397
|
+
SUM(CASE WHEN g.outcome = 'failed' THEN 1 ELSE 0 END) AS outcome_failed,
|
|
1398
|
+
SUM(CASE WHEN g.outcome = 'abandoned'
|
|
1399
|
+
OR (g.outcome IS NULL AND g.status = 'abandoned')
|
|
1400
|
+
THEN 1 ELSE 0 END) AS outcome_abandoned,
|
|
1401
|
+
SUM(CASE WHEN g.outcome = 'answered' THEN 1 ELSE 0 END) AS outcome_answered,
|
|
1402
|
+
SUM(CASE
|
|
1403
|
+
WHEN g.outcome IN ('success','partial','failed','abandoned','answered') THEN 0
|
|
1404
|
+
WHEN g.outcome IS NULL AND g.status = 'abandoned' THEN 0
|
|
1405
|
+
ELSE 1
|
|
1406
|
+
END) AS outcome_none,
|
|
1407
|
+
AVG(CASE WHEN g.duration_sec > 0 THEN g.duration_sec END) AS avg_duration_sec
|
|
1408
|
+
FROM (
|
|
1409
|
+
SELECT
|
|
1410
|
+
t.id AS id,
|
|
1411
|
+
t.outcome AS outcome,
|
|
1412
|
+
t.status AS status,
|
|
1413
|
+
(CASE
|
|
1414
|
+
WHEN t.end_time IS NOT NULL THEN
|
|
1415
|
+
CAST(strftime('%s', t.end_time) AS INTEGER) - CAST(strftime('%s', t.start_time) AS INTEGER)
|
|
1416
|
+
ELSE
|
|
1417
|
+
CAST(strftime('%s', 'now') AS INTEGER) - CAST(strftime('%s', t.start_time) AS INTEGER)
|
|
1418
|
+
END) AS duration_sec
|
|
1419
|
+
FROM events e
|
|
1420
|
+
-- perf (decision 2f05b0bf): CROSS JOIN pins events-first join order — see
|
|
1421
|
+
-- queryAgentBoardPromptRows for the full rationale (90ms → <2ms; pure
|
|
1422
|
+
-- join-order hint, aggregate result unchanged).
|
|
1423
|
+
CROSS JOIN task_events te ON te.event_id = e.event_id
|
|
1424
|
+
CROSS JOIN tasks t ON t.id = te.task_id
|
|
1425
|
+
CROSS JOIN sessions s ON s.session_id = t.session_id
|
|
1426
|
+
WHERE e.hook_type = 'UserPromptSubmit'
|
|
1427
|
+
AND e.user_prompt IS NOT NULL
|
|
1428
|
+
AND t.start_time >= ?
|
|
1429
|
+
${projectFilter}
|
|
1430
|
+
${kindFilter}
|
|
1431
|
+
GROUP BY t.id
|
|
1432
|
+
) g
|
|
1433
|
+
`;
|
|
1434
|
+
const params = [since];
|
|
1435
|
+
if (opts.projectPath)
|
|
1436
|
+
params.push(opts.projectPath);
|
|
1437
|
+
if (kindFilter)
|
|
1438
|
+
params.push(...effectiveKinds);
|
|
1439
|
+
const row = this.db.prepare(sql).get(...params);
|
|
1440
|
+
return {
|
|
1441
|
+
task_total: row?.task_total ?? 0,
|
|
1442
|
+
outcome_success: row?.outcome_success ?? 0,
|
|
1443
|
+
outcome_partial: row?.outcome_partial ?? 0,
|
|
1444
|
+
outcome_failed: row?.outcome_failed ?? 0,
|
|
1445
|
+
outcome_abandoned: row?.outcome_abandoned ?? 0,
|
|
1446
|
+
outcome_answered: row?.outcome_answered ?? 0,
|
|
1447
|
+
outcome_none: row?.outcome_none ?? 0,
|
|
1448
|
+
avg_duration_sec: row?.avg_duration_sec != null ? Math.round(row.avg_duration_sec) : null,
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
984
1451
|
// ── Agent Board v3 — count companion (2026-05-26) ─────────────────────
|
|
985
1452
|
//
|
|
986
1453
|
// Why a separate method: queryAgentBoardTasks() applies a LIMIT (default 40,
|
|
@@ -995,7 +1462,7 @@ export class TaskOperations {
|
|
|
995
1462
|
// noise filter + project filter + cap-vs-total invariant).
|
|
996
1463
|
countAgentBoardTasks(opts) {
|
|
997
1464
|
const nowMs = opts.now_ms ?? Date.now();
|
|
998
|
-
const sinceMs = nowMs - opts.windowDays *
|
|
1465
|
+
const sinceMs = nowMs - opts.windowDays * DAY_MS;
|
|
999
1466
|
const since = new Date(sinceMs).toISOString();
|
|
1000
1467
|
const projectFilter = opts.projectPath ? 'AND s.project_path = ?' : '';
|
|
1001
1468
|
// spec b1480935: same resolution order as queryAgentBoardTasks so the
|
|
@@ -1059,7 +1526,7 @@ export class TaskOperations {
|
|
|
1059
1526
|
queryCommitActivityDaily(opts) {
|
|
1060
1527
|
const days = Math.min(Math.max(opts.days, 1), 365);
|
|
1061
1528
|
const nowMs = opts.now_ms ?? Date.now();
|
|
1062
|
-
const sinceMs = nowMs - days *
|
|
1529
|
+
const sinceMs = nowMs - days * DAY_MS;
|
|
1063
1530
|
const since = new Date(sinceMs).toISOString();
|
|
1064
1531
|
const projectFilter = opts.projectPath ? 'AND s.project_path = ?' : '';
|
|
1065
1532
|
const sql = `
|
|
@@ -1093,7 +1560,7 @@ export class TaskOperations {
|
|
|
1093
1560
|
}
|
|
1094
1561
|
const out = [];
|
|
1095
1562
|
for (let i = days - 1; i >= 0; i--) {
|
|
1096
|
-
const d = new Date(nowMs - i *
|
|
1563
|
+
const d = new Date(nowMs - i * DAY_MS).toISOString().slice(0, 10);
|
|
1097
1564
|
const v = byDate.get(d);
|
|
1098
1565
|
out.push({
|
|
1099
1566
|
date: d,
|
|
@@ -1416,6 +1883,8 @@ export class TaskOperations {
|
|
|
1416
1883
|
// for some edge cases mid-migration) read as undefined → callers treat
|
|
1417
1884
|
// as 'user' via the TaskRecord default contract.
|
|
1418
1885
|
task_kind: r.task_kind ?? 'user',
|
|
1886
|
+
// decision f47c2c90: self-FK passthrough (NULL on legacy / non-linked rows).
|
|
1887
|
+
parent_task_id: r.parent_task_id ?? null,
|
|
1419
1888
|
};
|
|
1420
1889
|
}
|
|
1421
1890
|
// ── Outcome classifier (spec 1100 v2 — edit-intent signal) ────────
|
|
@@ -1457,6 +1926,15 @@ export class TaskOperations {
|
|
|
1457
1926
|
//
|
|
1458
1927
|
// Includes both 'completed' and 'abandoned' statuses (never 'active' —
|
|
1459
1928
|
// the current task gets classified separately by stop.ts existing path).
|
|
1929
|
+
//
|
|
1930
|
+
// task_kind gate (fix 2026-06-17): only real user tasks get an outcome.
|
|
1931
|
+
// Without this filter the idle sweep stamped outcome on agent-callback
|
|
1932
|
+
// (`<task-notification>`), image, and system-envelope rows, e.g. a callback
|
|
1933
|
+
// notification was wrongly labelled `success`/`answered` (128/133 rows
|
|
1934
|
+
// polluted). Mirrors getActiveUserTask's predicate exactly so the idle sweep
|
|
1935
|
+
// and the active-task path agree on what "a user task" is. The
|
|
1936
|
+
// `task_kind IS NULL AND is_noise = 0` arm preserves legacy rows written
|
|
1937
|
+
// before the task_kind backfill (spec b1480935).
|
|
1460
1938
|
queryIdleUnclassifiedTasks(sessionId) {
|
|
1461
1939
|
const rows = this.db.prepare(`
|
|
1462
1940
|
SELECT t.*, s.project_path
|
|
@@ -1465,6 +1943,7 @@ export class TaskOperations {
|
|
|
1465
1943
|
WHERE t.session_id = ?
|
|
1466
1944
|
AND t.outcome IS NULL
|
|
1467
1945
|
AND t.status IN ('completed', 'abandoned')
|
|
1946
|
+
AND (t.task_kind = 'user' OR (t.task_kind IS NULL AND t.is_noise = 0))
|
|
1468
1947
|
ORDER BY t.start_time ASC
|
|
1469
1948
|
`).all(sessionId);
|
|
1470
1949
|
return rows.map(r => this.mapRow(r));
|