@jamesaphoenix/tx-core 0.8.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db.d.ts +6 -6
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +29 -2
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/internal/cycle-scan-service-impl.d.ts +28 -0
- package/dist/internal/cycle-scan-service-impl.d.ts.map +1 -0
- package/dist/internal/cycle-scan-service-impl.js +877 -0
- package/dist/internal/cycle-scan-service-impl.js.map +1 -0
- package/dist/internal/doc-service-impl.d.ts +42 -0
- package/dist/internal/doc-service-impl.d.ts.map +1 -0
- package/dist/internal/doc-service-impl.js +812 -0
- package/dist/internal/doc-service-impl.js.map +1 -0
- package/dist/internal/embedding-service-impl.d.ts +202 -0
- package/dist/internal/embedding-service-impl.d.ts.map +1 -0
- package/dist/internal/embedding-service-impl.js +466 -0
- package/dist/internal/embedding-service-impl.js.map +1 -0
- package/dist/internal/memory-service-impl.d.ts +49 -0
- package/dist/internal/memory-service-impl.d.ts.map +1 -0
- package/dist/internal/memory-service-impl.js +1061 -0
- package/dist/internal/memory-service-impl.js.map +1 -0
- package/dist/internal/spec-trace-service-impl.d.ts +50 -0
- package/dist/internal/spec-trace-service-impl.d.ts.map +1 -0
- package/dist/internal/spec-trace-service-impl.js +707 -0
- package/dist/internal/spec-trace-service-impl.js.map +1 -0
- package/dist/internal/sync/service-impl.d.ts +41 -0
- package/dist/internal/sync/service-impl.d.ts.map +1 -0
- package/dist/{services/sync-service.js → internal/sync/service-impl.js} +954 -499
- package/dist/internal/sync/service-impl.js.map +1 -0
- package/dist/layer.d.ts +8 -5
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +22 -10
- package/dist/layer.js.map +1 -1
- package/dist/mappers/anchor.d.ts.map +1 -1
- package/dist/mappers/anchor.js +5 -4
- package/dist/mappers/anchor.js.map +1 -1
- package/dist/mappers/attempt.d.ts.map +1 -1
- package/dist/mappers/attempt.js +2 -1
- package/dist/mappers/attempt.js.map +1 -1
- package/dist/mappers/candidate.d.ts.map +1 -1
- package/dist/mappers/candidate.js +2 -1
- package/dist/mappers/candidate.js.map +1 -1
- package/dist/mappers/claim.d.ts.map +1 -1
- package/dist/mappers/claim.js +2 -1
- package/dist/mappers/claim.js.map +1 -1
- package/dist/mappers/doc.d.ts.map +1 -1
- package/dist/mappers/doc.js +9 -8
- package/dist/mappers/doc.js.map +1 -1
- package/dist/mappers/edge.d.ts.map +1 -1
- package/dist/mappers/edge.js +4 -3
- package/dist/mappers/edge.js.map +1 -1
- package/dist/mappers/file-learning.d.ts.map +1 -1
- package/dist/mappers/file-learning.js +2 -1
- package/dist/mappers/file-learning.js.map +1 -1
- package/dist/mappers/index.d.ts +1 -0
- package/dist/mappers/index.d.ts.map +1 -1
- package/dist/mappers/index.js +2 -0
- package/dist/mappers/index.js.map +1 -1
- package/dist/mappers/learning.d.ts.map +1 -1
- package/dist/mappers/learning.js +4 -3
- package/dist/mappers/learning.js.map +1 -1
- package/dist/mappers/memory.d.ts.map +1 -1
- package/dist/mappers/memory.js +7 -6
- package/dist/mappers/memory.js.map +1 -1
- package/dist/mappers/orchestrator-state.d.ts.map +1 -1
- package/dist/mappers/orchestrator-state.js +2 -1
- package/dist/mappers/orchestrator-state.js.map +1 -1
- package/dist/mappers/pin.d.ts.map +1 -1
- package/dist/mappers/pin.js +2 -1
- package/dist/mappers/pin.js.map +1 -1
- package/dist/mappers/run.d.ts.map +1 -1
- package/dist/mappers/run.js +4 -3
- package/dist/mappers/run.js.map +1 -1
- package/dist/mappers/spec-trace.d.ts +11 -0
- package/dist/mappers/spec-trace.d.ts.map +1 -0
- package/dist/mappers/spec-trace.js +59 -0
- package/dist/mappers/spec-trace.js.map +1 -0
- package/dist/mappers/task.d.ts.map +1 -1
- package/dist/mappers/task.js +4 -4
- package/dist/mappers/task.js.map +1 -1
- package/dist/mappers/worker.d.ts.map +1 -1
- package/dist/mappers/worker.js +2 -1
- package/dist/mappers/worker.js.map +1 -1
- package/dist/migrations-embedded.d.ts.map +1 -1
- package/dist/migrations-embedded.js +15 -0
- package/dist/migrations-embedded.js.map +1 -1
- package/dist/repo/anchor-repo.d.ts +2 -2
- package/dist/repo/anchor-repo.d.ts.map +1 -1
- package/dist/repo/anchor-repo.js +18 -17
- package/dist/repo/anchor-repo.js.map +1 -1
- package/dist/repo/attempt-repo.d.ts.map +1 -1
- package/dist/repo/attempt-repo.js +11 -10
- package/dist/repo/attempt-repo.js.map +1 -1
- package/dist/repo/candidate-repo.d.ts.map +1 -1
- package/dist/repo/candidate-repo.js +8 -7
- package/dist/repo/candidate-repo.js.map +1 -1
- package/dist/repo/claim-repo.d.ts +4 -4
- package/dist/repo/claim-repo.d.ts.map +1 -1
- package/dist/repo/claim-repo.js +14 -13
- package/dist/repo/claim-repo.js.map +1 -1
- package/dist/repo/compaction-repo.d.ts +4 -4
- package/dist/repo/compaction-repo.d.ts.map +1 -1
- package/dist/repo/compaction-repo.js +7 -6
- package/dist/repo/compaction-repo.js.map +1 -1
- package/dist/repo/deduplication-repo.d.ts.map +1 -1
- package/dist/repo/deduplication-repo.js +80 -57
- package/dist/repo/deduplication-repo.js.map +1 -1
- package/dist/repo/dep-repo.d.ts.map +1 -1
- package/dist/repo/dep-repo.js +90 -88
- package/dist/repo/dep-repo.js.map +1 -1
- package/dist/repo/doc-repo.d.ts +2 -63
- package/dist/repo/doc-repo.d.ts.map +1 -1
- package/dist/repo/doc-repo.js +6 -4
- package/dist/repo/doc-repo.js.map +1 -1
- package/dist/repo/doc-repo.types.d.ts +64 -0
- package/dist/repo/doc-repo.types.d.ts.map +1 -0
- package/dist/repo/doc-repo.types.js +2 -0
- package/dist/repo/doc-repo.types.js.map +1 -0
- package/dist/repo/edge-repo.d.ts.map +1 -1
- package/dist/repo/edge-repo.js +91 -81
- package/dist/repo/edge-repo.js.map +1 -1
- package/dist/repo/file-learning-repo.d.ts.map +1 -1
- package/dist/repo/file-learning-repo.js +7 -6
- package/dist/repo/file-learning-repo.js.map +1 -1
- package/dist/repo/guard-repo.d.ts +4 -4
- package/dist/repo/guard-repo.d.ts.map +1 -1
- package/dist/repo/guard-repo.js +8 -7
- package/dist/repo/guard-repo.js.map +1 -1
- package/dist/repo/index.d.ts +1 -0
- package/dist/repo/index.d.ts.map +1 -1
- package/dist/repo/index.js +1 -0
- package/dist/repo/index.js.map +1 -1
- package/dist/repo/label-repo.d.ts +4 -4
- package/dist/repo/label-repo.d.ts.map +1 -1
- package/dist/repo/label-repo.js +9 -8
- package/dist/repo/label-repo.js.map +1 -1
- package/dist/repo/learning-repo.d.ts +2 -2
- package/dist/repo/learning-repo.d.ts.map +1 -1
- package/dist/repo/learning-repo.js +37 -31
- package/dist/repo/learning-repo.js.map +1 -1
- package/dist/repo/memory-repo/document.d.ts +4 -0
- package/dist/repo/memory-repo/document.d.ts.map +1 -0
- package/dist/repo/memory-repo/document.js +205 -0
- package/dist/repo/memory-repo/document.js.map +1 -0
- package/dist/repo/memory-repo/link.d.ts +4 -0
- package/dist/repo/memory-repo/link.d.ts.map +1 -0
- package/dist/repo/memory-repo/link.js +124 -0
- package/dist/repo/memory-repo/link.js.map +1 -0
- package/dist/repo/memory-repo/property.d.ts +4 -0
- package/dist/repo/memory-repo/property.d.ts.map +1 -0
- package/dist/repo/memory-repo/property.js +56 -0
- package/dist/repo/memory-repo/property.js.map +1 -0
- package/dist/repo/memory-repo/shared.d.ts +28 -0
- package/dist/repo/memory-repo/shared.d.ts.map +1 -0
- package/dist/repo/memory-repo/shared.js +63 -0
- package/dist/repo/memory-repo/shared.js.map +1 -0
- package/dist/repo/memory-repo/source.d.ts +4 -0
- package/dist/repo/memory-repo/source.d.ts.map +1 -0
- package/dist/repo/memory-repo/source.js +58 -0
- package/dist/repo/memory-repo/source.js.map +1 -0
- package/dist/repo/memory-repo.d.ts +14 -10
- package/dist/repo/memory-repo.d.ts.map +1 -1
- package/dist/repo/memory-repo.js +8 -527
- package/dist/repo/memory-repo.js.map +1 -1
- package/dist/repo/message-repo.d.ts.map +1 -1
- package/dist/repo/message-repo.js +9 -8
- package/dist/repo/message-repo.js.map +1 -1
- package/dist/repo/orchestrator-state-repo.d.ts.map +1 -1
- package/dist/repo/orchestrator-state-repo.js +2 -1
- package/dist/repo/orchestrator-state-repo.js.map +1 -1
- package/dist/repo/pin-repo.d.ts.map +1 -1
- package/dist/repo/pin-repo.js +5 -4
- package/dist/repo/pin-repo.js.map +1 -1
- package/dist/repo/run-repo.d.ts.map +1 -1
- package/dist/repo/run-repo.js +11 -10
- package/dist/repo/run-repo.js.map +1 -1
- package/dist/repo/spec-trace-repo.d.ts +9 -0
- package/dist/repo/spec-trace-repo.d.ts.map +1 -0
- package/dist/repo/spec-trace-repo.filter.d.ts +3 -0
- package/dist/repo/spec-trace-repo.filter.d.ts.map +1 -0
- package/dist/repo/spec-trace-repo.filter.js +13 -0
- package/dist/repo/spec-trace-repo.filter.js.map +1 -0
- package/dist/repo/spec-trace-repo.js +323 -0
- package/dist/repo/spec-trace-repo.js.map +1 -0
- package/dist/repo/spec-trace-repo.types.d.ts +60 -0
- package/dist/repo/spec-trace-repo.types.d.ts.map +1 -0
- package/dist/repo/spec-trace-repo.types.js +2 -0
- package/dist/repo/spec-trace-repo.types.js.map +1 -0
- package/dist/repo/task-repo/factory.d.ts +4 -0
- package/dist/repo/task-repo/factory.d.ts.map +1 -0
- package/dist/repo/task-repo/factory.js +7 -0
- package/dist/repo/task-repo/factory.js.map +1 -0
- package/dist/repo/task-repo/read.d.ts +6 -0
- package/dist/repo/task-repo/read.d.ts.map +1 -0
- package/dist/repo/task-repo/read.js +332 -0
- package/dist/repo/task-repo/read.js.map +1 -0
- package/dist/repo/task-repo/shared.d.ts +10 -0
- package/dist/repo/task-repo/shared.d.ts.map +1 -0
- package/dist/repo/task-repo/shared.js +29 -0
- package/dist/repo/task-repo/shared.js.map +1 -0
- package/dist/repo/task-repo/write.d.ts +6 -0
- package/dist/repo/task-repo/write.d.ts.map +1 -0
- package/dist/repo/task-repo/write.js +182 -0
- package/dist/repo/task-repo/write.js.map +1 -0
- package/dist/repo/task-repo.d.ts +5 -4
- package/dist/repo/task-repo.d.ts.map +1 -1
- package/dist/repo/task-repo.js +2 -520
- package/dist/repo/task-repo.js.map +1 -1
- package/dist/repo/tracked-project-repo.d.ts.map +1 -1
- package/dist/repo/tracked-project-repo.js +6 -5
- package/dist/repo/tracked-project-repo.js.map +1 -1
- package/dist/repo/worker-repo.d.ts.map +1 -1
- package/dist/repo/worker-repo.js +60 -47
- package/dist/repo/worker-repo.js.map +1 -1
- package/dist/schemas/index.d.ts +4 -2
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -1
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/sync-events.d.ts +25 -0
- package/dist/schemas/sync-events.d.ts.map +1 -0
- package/dist/schemas/sync-events.js +23 -0
- package/dist/schemas/sync-events.js.map +1 -0
- package/dist/schemas/sync.d.ts +20 -10
- package/dist/schemas/sync.d.ts.map +1 -1
- package/dist/schemas/sync.js +10 -4
- package/dist/schemas/sync.js.map +1 -1
- package/dist/services/agent-service.d.ts +4 -4
- package/dist/services/agent-service.d.ts.map +1 -1
- package/dist/services/agent-service.js.map +1 -1
- package/dist/services/anchor/anchor-service-core-ops.d.ts +125 -0
- package/dist/services/anchor/anchor-service-core-ops.d.ts.map +1 -0
- package/dist/services/anchor/anchor-service-core-ops.js +41 -0
- package/dist/services/anchor/anchor-service-core-ops.js.map +1 -0
- package/dist/services/anchor/anchor-service-deps.d.ts +10 -0
- package/dist/services/anchor/anchor-service-deps.d.ts.map +1 -0
- package/dist/services/anchor/anchor-service-deps.js +2 -0
- package/dist/services/anchor/anchor-service-deps.js.map +1 -0
- package/dist/services/anchor/anchor-service-ops.d.ts +296 -0
- package/dist/services/anchor/anchor-service-ops.d.ts.map +1 -0
- package/dist/services/anchor/anchor-service-ops.js +9 -0
- package/dist/services/anchor/anchor-service-ops.js.map +1 -0
- package/dist/services/anchor/anchor-service-state-ops.d.ts +116 -0
- package/dist/services/anchor/anchor-service-state-ops.d.ts.map +1 -0
- package/dist/services/anchor/anchor-service-state-ops.js +150 -0
- package/dist/services/anchor/anchor-service-state-ops.js.map +1 -0
- package/dist/services/anchor/anchor-service-validation.d.ts +7 -0
- package/dist/services/anchor/anchor-service-validation.d.ts.map +1 -0
- package/dist/services/anchor/anchor-service-validation.js +114 -0
- package/dist/services/anchor/anchor-service-validation.js.map +1 -0
- package/dist/services/anchor/anchor-service-verification-ops.d.ts +71 -0
- package/dist/services/anchor/anchor-service-verification-ops.d.ts.map +1 -0
- package/dist/services/anchor/anchor-service-verification-ops.js +169 -0
- package/dist/services/anchor/anchor-service-verification-ops.js.map +1 -0
- package/dist/services/anchor/anchor-verification-batch.d.ts +12 -0
- package/dist/services/anchor/anchor-verification-batch.d.ts.map +1 -0
- package/dist/services/anchor/anchor-verification-batch.js +109 -0
- package/dist/services/anchor/anchor-verification-batch.js.map +1 -0
- package/dist/services/anchor/anchor-verification-single.d.ts +7 -0
- package/dist/services/anchor/anchor-verification-single.d.ts.map +1 -0
- package/dist/services/anchor/anchor-verification-single.js +407 -0
- package/dist/services/anchor/anchor-verification-single.js.map +1 -0
- package/dist/services/anchor/anchor-verification-utils.d.ts +19 -0
- package/dist/services/anchor/anchor-verification-utils.d.ts.map +1 -0
- package/dist/services/anchor/anchor-verification-utils.js +107 -0
- package/dist/services/anchor/anchor-verification-utils.js.map +1 -0
- package/dist/services/anchor-service.d.ts +12 -90
- package/dist/services/anchor-service.d.ts.map +1 -1
- package/dist/services/anchor-service.js +5 -530
- package/dist/services/anchor-service.js.map +1 -1
- package/dist/services/anchor-verification.d.ts +9 -60
- package/dist/services/anchor-verification.d.ts.map +1 -1
- package/dist/services/anchor-verification.js +5 -796
- package/dist/services/anchor-verification.js.map +1 -1
- package/dist/services/ast-grep-service/patterns.d.ts +90 -0
- package/dist/services/ast-grep-service/patterns.d.ts.map +1 -0
- package/dist/services/ast-grep-service/patterns.js +261 -0
- package/dist/services/ast-grep-service/patterns.js.map +1 -0
- package/dist/services/ast-grep-service.d.ts +2 -13
- package/dist/services/ast-grep-service.d.ts.map +1 -1
- package/dist/services/ast-grep-service.js +3 -261
- package/dist/services/ast-grep-service.js.map +1 -1
- package/dist/services/auto-sync-service.d.ts +3 -3
- package/dist/services/auto-sync-service.d.ts.map +1 -1
- package/dist/services/auto-sync-service.js +12 -13
- package/dist/services/auto-sync-service.js.map +1 -1
- package/dist/services/compaction-service.d.ts +6 -6
- package/dist/services/compaction-service.d.ts.map +1 -1
- package/dist/services/compaction-service.js +11 -7
- package/dist/services/compaction-service.js.map +1 -1
- package/dist/services/cycle-scan-service.d.ts +1 -27
- package/dist/services/cycle-scan-service.d.ts.map +1 -1
- package/dist/services/cycle-scan-service.js +1 -876
- package/dist/services/cycle-scan-service.js.map +1 -1
- package/dist/services/daemon-service/process.d.ts +93 -0
- package/dist/services/daemon-service/process.d.ts.map +1 -0
- package/dist/services/daemon-service/process.js +325 -0
- package/dist/services/daemon-service/process.js.map +1 -0
- package/dist/services/daemon-service/templates.d.ts +88 -0
- package/dist/services/daemon-service/templates.d.ts.map +1 -0
- package/dist/services/daemon-service/templates.js +119 -0
- package/dist/services/daemon-service/templates.js.map +1 -0
- package/dist/services/daemon-service.d.ts +8 -160
- package/dist/services/daemon-service.d.ts.map +1 -1
- package/dist/services/daemon-service.js +4 -440
- package/dist/services/daemon-service.js.map +1 -1
- package/dist/services/doc-service.d.ts +1 -41
- package/dist/services/doc-service.d.ts.map +1 -1
- package/dist/services/doc-service.js +1 -780
- package/dist/services/doc-service.js.map +1 -1
- package/dist/services/edge-service.d.ts +6 -6
- package/dist/services/edge-service.d.ts.map +1 -1
- package/dist/services/edge-service.js.map +1 -1
- package/dist/services/embedding-service.d.ts +1 -201
- package/dist/services/embedding-service.d.ts.map +1 -1
- package/dist/services/embedding-service.js +1 -465
- package/dist/services/embedding-service.js.map +1 -1
- package/dist/services/feedback-tracker.d.ts +2 -2
- package/dist/services/feedback-tracker.d.ts.map +1 -1
- package/dist/services/feedback-tracker.js.map +1 -1
- package/dist/services/file-watcher-service/shared.d.ts +76 -0
- package/dist/services/file-watcher-service/shared.d.ts.map +1 -0
- package/dist/services/file-watcher-service/shared.js +43 -0
- package/dist/services/file-watcher-service/shared.js.map +1 -0
- package/dist/services/file-watcher-service.d.ts +2 -42
- package/dist/services/file-watcher-service.d.ts.map +1 -1
- package/dist/services/file-watcher-service.js +1 -46
- package/dist/services/file-watcher-service.js.map +1 -1
- package/dist/services/graph-expansion/from-files.d.ts +14 -0
- package/dist/services/graph-expansion/from-files.d.ts.map +1 -0
- package/dist/services/graph-expansion/from-files.js +187 -0
- package/dist/services/graph-expansion/from-files.js.map +1 -0
- package/dist/services/graph-expansion/live.d.ts +11 -0
- package/dist/services/graph-expansion/live.d.ts.map +1 -0
- package/dist/services/graph-expansion/live.js +263 -0
- package/dist/services/graph-expansion/live.js.map +1 -0
- package/dist/services/graph-expansion.d.ts +17 -20
- package/dist/services/graph-expansion.d.ts.map +1 -1
- package/dist/services/graph-expansion.js +2 -439
- package/dist/services/graph-expansion.js.map +1 -1
- package/dist/services/guard-service.d.ts +2 -2
- package/dist/services/guard-service.d.ts.map +1 -1
- package/dist/services/guard-service.js.map +1 -1
- package/dist/services/index.d.ts +3 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/learning-service.d.ts +4 -4
- package/dist/services/learning-service.d.ts.map +1 -1
- package/dist/services/learning-service.js.map +1 -1
- package/dist/services/llm-service.d.ts +4 -4
- package/dist/services/llm-service.d.ts.map +1 -1
- package/dist/services/llm-service.js.map +1 -1
- package/dist/services/memory-retriever-service.d.ts.map +1 -1
- package/dist/services/memory-retriever-service.js.map +1 -1
- package/dist/services/memory-service.d.ts +1 -48
- package/dist/services/memory-service.d.ts.map +1 -1
- package/dist/services/memory-service.js +1 -1060
- package/dist/services/memory-service.js.map +1 -1
- package/dist/services/migration-service.d.ts +6 -6
- package/dist/services/migration-service.d.ts.map +1 -1
- package/dist/services/migration-service.js +6 -2
- package/dist/services/migration-service.js.map +1 -1
- package/dist/services/orchestrator-service.d.ts +2 -2
- package/dist/services/orchestrator-service.d.ts.map +1 -1
- package/dist/services/orchestrator-service.js.map +1 -1
- package/dist/services/pin-service.d.ts.map +1 -1
- package/dist/services/pin-service.js +21 -28
- package/dist/services/pin-service.js.map +1 -1
- package/dist/services/promotion-service.d.ts +4 -4
- package/dist/services/promotion-service.d.ts.map +1 -1
- package/dist/services/promotion-service.js.map +1 -1
- package/dist/services/query-expansion-service.d.ts +2 -2
- package/dist/services/query-expansion-service.d.ts.map +1 -1
- package/dist/services/query-expansion-service.js.map +1 -1
- package/dist/services/reflect-service.d.ts +6 -6
- package/dist/services/reflect-service.d.ts.map +1 -1
- package/dist/services/reflect-service.js.map +1 -1
- package/dist/services/reranker-service.d.ts +2 -2
- package/dist/services/reranker-service.d.ts.map +1 -1
- package/dist/services/reranker-service.js.map +1 -1
- package/dist/services/retriever-scoring.d.ts +52 -0
- package/dist/services/retriever-scoring.d.ts.map +1 -0
- package/dist/services/retriever-scoring.js +146 -0
- package/dist/services/retriever-scoring.js.map +1 -0
- package/dist/services/retriever-service.d.ts.map +1 -1
- package/dist/services/retriever-service.js +1 -191
- package/dist/services/retriever-service.js.map +1 -1
- package/dist/services/run-heartbeat-service.d.ts +10 -10
- package/dist/services/run-heartbeat-service.d.ts.map +1 -1
- package/dist/services/run-heartbeat-service.js.map +1 -1
- package/dist/services/score-service.d.ts +2 -2
- package/dist/services/score-service.d.ts.map +1 -1
- package/dist/services/score-service.js.map +1 -1
- package/dist/services/spec-trace-service.d.ts +2 -0
- package/dist/services/spec-trace-service.d.ts.map +1 -0
- package/dist/services/spec-trace-service.js +2 -0
- package/dist/services/spec-trace-service.js.map +1 -0
- package/dist/services/stream-service.d.ts +31 -0
- package/dist/services/stream-service.d.ts.map +1 -0
- package/dist/services/stream-service.js +162 -0
- package/dist/services/stream-service.js.map +1 -0
- package/dist/services/swarm-verification/shared.d.ts +71 -0
- package/dist/services/swarm-verification/shared.d.ts.map +1 -0
- package/dist/services/swarm-verification/shared.js +108 -0
- package/dist/services/swarm-verification/shared.js.map +1 -0
- package/dist/services/swarm-verification.d.ts +4 -68
- package/dist/services/swarm-verification.d.ts.map +1 -1
- package/dist/services/swarm-verification.js +2 -122
- package/dist/services/swarm-verification.js.map +1 -1
- package/dist/services/sync/converters.d.ts +63 -0
- package/dist/services/sync/converters.d.ts.map +1 -0
- package/dist/services/sync/converters.js +253 -0
- package/dist/services/sync/converters.js.map +1 -0
- package/dist/services/sync/entity-export.d.ts +22 -0
- package/dist/services/sync/entity-export.d.ts.map +1 -0
- package/dist/services/sync/entity-export.js +15 -0
- package/dist/services/sync/entity-export.js.map +1 -0
- package/dist/services/sync/entity-import.d.ts +22 -0
- package/dist/services/sync/entity-import.d.ts.map +1 -0
- package/dist/services/sync/entity-import.js +15 -0
- package/dist/services/sync/entity-import.js.map +1 -0
- package/dist/services/sync/file-utils.d.ts +23 -0
- package/dist/services/sync/file-utils.d.ts.map +1 -0
- package/dist/services/sync/file-utils.js +97 -0
- package/dist/services/sync/file-utils.js.map +1 -0
- package/dist/services/sync/index.d.ts +3 -0
- package/dist/services/sync/index.d.ts.map +1 -0
- package/dist/services/sync/index.js +2 -0
- package/dist/services/sync/index.js.map +1 -0
- package/dist/services/sync/service.d.ts +2 -0
- package/dist/services/sync/service.d.ts.map +1 -0
- package/dist/services/sync/service.js +2 -0
- package/dist/services/sync/service.js.map +1 -0
- package/dist/services/sync/sync-helpers.d.ts +54 -0
- package/dist/services/sync/sync-helpers.d.ts.map +1 -0
- package/dist/services/sync/sync-helpers.js +245 -0
- package/dist/services/sync/sync-helpers.js.map +1 -0
- package/dist/services/sync/types.d.ts +104 -0
- package/dist/services/sync/types.d.ts.map +1 -0
- package/dist/services/sync/types.js +5 -0
- package/dist/services/sync/types.js.map +1 -0
- package/dist/services/task-service/internals.d.ts +36 -0
- package/dist/services/task-service/internals.d.ts.map +1 -0
- package/dist/services/task-service/internals.js +270 -0
- package/dist/services/task-service/internals.js.map +1 -0
- package/dist/services/task-service.d.ts +2 -1
- package/dist/services/task-service.d.ts.map +1 -1
- package/dist/services/task-service.js +28 -236
- package/dist/services/task-service.js.map +1 -1
- package/dist/services/tracing-service.d.ts +2 -2
- package/dist/services/tracing-service.d.ts.map +1 -1
- package/dist/services/tracing-service.js.map +1 -1
- package/dist/services/transcript-adapter.d.ts +6 -6
- package/dist/services/transcript-adapter.d.ts.map +1 -1
- package/dist/services/transcript-adapter.js +3 -8
- package/dist/services/transcript-adapter.js.map +1 -1
- package/dist/services/validation-service.d.ts +8 -8
- package/dist/services/validation-service.d.ts.map +1 -1
- package/dist/services/validation-service.js.map +1 -1
- package/dist/services/verify-service.d.ts +2 -2
- package/dist/services/verify-service.d.ts.map +1 -1
- package/dist/services/verify-service.js.map +1 -1
- package/dist/services/worker-process/runtime.d.ts +51 -0
- package/dist/services/worker-process/runtime.d.ts.map +1 -0
- package/dist/services/worker-process/runtime.js +150 -0
- package/dist/services/worker-process/runtime.js.map +1 -0
- package/dist/services/worker-process.d.ts +3 -18
- package/dist/services/worker-process.d.ts.map +1 -1
- package/dist/services/worker-process.js +3 -173
- package/dist/services/worker-process.js.map +1 -1
- package/dist/services/worker-service.d.ts +6 -6
- package/dist/services/worker-service.d.ts.map +1 -1
- package/dist/services/worker-service.js.map +1 -1
- package/dist/utils/db-result.d.ts +14 -0
- package/dist/utils/db-result.d.ts.map +1 -0
- package/dist/utils/db-result.js +37 -0
- package/dist/utils/db-result.js.map +1 -0
- package/dist/utils/doc-renderer.d.ts +10 -10
- package/dist/utils/doc-renderer.d.ts.map +1 -1
- package/dist/utils/doc-renderer.js.map +1 -1
- package/dist/utils/ears-validator.d.ts +2 -2
- package/dist/utils/ears-validator.d.ts.map +1 -1
- package/dist/utils/ears-validator.js.map +1 -1
- package/dist/utils/file-path.d.ts +27 -0
- package/dist/utils/file-path.d.ts.map +1 -0
- package/dist/utils/file-path.js +77 -0
- package/dist/utils/file-path.js.map +1 -0
- package/dist/utils/glob.d.ts +2 -11
- package/dist/utils/glob.d.ts.map +1 -1
- package/dist/utils/glob.js +22 -13
- package/dist/utils/glob.js.map +1 -1
- package/dist/utils/spec-discovery.d.ts +34 -0
- package/dist/utils/spec-discovery.d.ts.map +1 -0
- package/dist/utils/spec-discovery.js +344 -0
- package/dist/utils/spec-discovery.js.map +1 -0
- package/dist/utils/toml-config.d.ts +7 -2
- package/dist/utils/toml-config.d.ts.map +1 -1
- package/dist/utils/toml-config.js +106 -2
- package/dist/utils/toml-config.js.map +1 -1
- package/dist/utils/ulid.d.ts +8 -0
- package/dist/utils/ulid.d.ts.map +1 -0
- package/dist/utils/ulid.js +30 -0
- package/dist/utils/ulid.js.map +1 -0
- package/dist/worker/hooks.d.ts +10 -10
- package/dist/worker/hooks.d.ts.map +1 -1
- package/dist/worker/run-worker.d.ts.map +1 -1
- package/dist/worker/run-worker.js.map +1 -1
- package/migrations/033_sync_events.sql +33 -0
- package/migrations/034_spec_test_traceability.sql +51 -0
- package/migrations/035_anchor_schema_repair.sql +82 -0
- package/package.json +4 -3
- package/dist/services/sync-service.d.ts +0 -247
- package/dist/services/sync-service.d.ts.map +0 -1
- package/dist/services/sync-service.js.map +0 -1
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CycleScanService — Cycle-Based Issue Discovery
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates sub-agent swarms via AgentService to scan for codebase issues,
|
|
5
|
+
* deduplicates findings against known issues using an LLM-as-judge, and
|
|
6
|
+
* optionally fixes them. Repeats in rounds within each cycle until convergence.
|
|
7
|
+
*/
|
|
8
|
+
import { Context, Effect, Layer } from "effect";
|
|
9
|
+
import { mkdirSync } from "node:fs";
|
|
10
|
+
import { appendFile } from "node:fs/promises";
|
|
11
|
+
import { resolve } from "node:path";
|
|
12
|
+
import { randomBytes } from "node:crypto";
|
|
13
|
+
import { readFileSync, existsSync, statSync } from "node:fs";
|
|
14
|
+
import { LOSS_WEIGHTS } from "@jamesaphoenix/tx-types";
|
|
15
|
+
import { CycleScanError } from "../errors.js";
|
|
16
|
+
import { AgentService } from "../services/agent-service.js";
|
|
17
|
+
import { SqliteClient } from "../db.js";
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// JSON Schemas for Structured Output
|
|
20
|
+
// =============================================================================
|
|
21
|
+
const FINDINGS_SCHEMA = {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
findings: {
|
|
25
|
+
type: "array",
|
|
26
|
+
items: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
title: { type: "string", description: "Short, descriptive issue title" },
|
|
30
|
+
description: { type: "string", description: "Detailed explanation of the issue and why it matters" },
|
|
31
|
+
severity: { type: "string", enum: ["high", "medium", "low"] },
|
|
32
|
+
issueType: { type: "string", description: "Category: bug, anti-pattern, security, performance, testing, ddd, etc." },
|
|
33
|
+
file: { type: "string", description: "File path relative to project root" },
|
|
34
|
+
line: { type: "number", description: "Approximate line number" },
|
|
35
|
+
},
|
|
36
|
+
required: ["title", "description", "severity", "issueType", "file", "line"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ["findings"],
|
|
41
|
+
};
|
|
42
|
+
const DEDUP_SCHEMA = {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
newIssues: {
|
|
46
|
+
type: "array",
|
|
47
|
+
items: {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
title: { type: "string" },
|
|
51
|
+
description: { type: "string" },
|
|
52
|
+
severity: { type: "string", enum: ["high", "medium", "low"] },
|
|
53
|
+
issueType: { type: "string" },
|
|
54
|
+
file: { type: "string" },
|
|
55
|
+
line: { type: "number" },
|
|
56
|
+
},
|
|
57
|
+
required: ["title", "description", "severity", "issueType", "file", "line"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
duplicates: {
|
|
61
|
+
type: "array",
|
|
62
|
+
items: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
findingIdx: { type: "number", description: "Index into the findings array" },
|
|
66
|
+
existingIssueId: { type: "string", description: "Task ID of the existing issue this duplicates" },
|
|
67
|
+
reason: { type: "string", description: "Why this is considered a duplicate" },
|
|
68
|
+
},
|
|
69
|
+
required: ["findingIdx", "existingIssueId", "reason"],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ["newIssues", "duplicates"],
|
|
74
|
+
};
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Helpers
|
|
77
|
+
// =============================================================================
|
|
78
|
+
function composeScanPrompt(task, scan) {
|
|
79
|
+
return `## Context
|
|
80
|
+
${task}
|
|
81
|
+
|
|
82
|
+
## Your Mission
|
|
83
|
+
${scan}
|
|
84
|
+
|
|
85
|
+
## Instructions
|
|
86
|
+
Explore the codebase thoroughly using Read, Glob, and Grep.
|
|
87
|
+
Look for real issues — not style nits. Focus on bugs, logic errors,
|
|
88
|
+
missing error handling, security vulnerabilities, and structural problems.
|
|
89
|
+
|
|
90
|
+
Return your findings as structured JSON. Only report issues you are
|
|
91
|
+
confident about after reading the actual code.`;
|
|
92
|
+
}
|
|
93
|
+
function composeDedupPrompt(findings, existingIssues) {
|
|
94
|
+
return `## Task
|
|
95
|
+
Compare these new findings against known issues. Return only genuinely new issues.
|
|
96
|
+
|
|
97
|
+
## New Findings (${findings.length} total)
|
|
98
|
+
${JSON.stringify(findings, null, 2)}
|
|
99
|
+
|
|
100
|
+
## Known Issues (${existingIssues.length} total)
|
|
101
|
+
${JSON.stringify(existingIssues, null, 2)}
|
|
102
|
+
|
|
103
|
+
## Instructions
|
|
104
|
+
For each new finding, check if it describes the same problem as any known issue.
|
|
105
|
+
Use semantic understanding — the same issue might be worded differently or reference
|
|
106
|
+
a slightly different line number in the same file. Be conservative: if in doubt,
|
|
107
|
+
treat it as new rather than duplicate.
|
|
108
|
+
|
|
109
|
+
Return your analysis as structured JSON.`;
|
|
110
|
+
}
|
|
111
|
+
function composeFixPrompt(issues) {
|
|
112
|
+
const issuesSummary = issues
|
|
113
|
+
.map((i, idx) => `${idx + 1}. [${i.severity.toUpperCase()}] ${i.title} (${i.file}:${i.line})\n ${i.description}`)
|
|
114
|
+
.join("\n\n");
|
|
115
|
+
return `## Task
|
|
116
|
+
Fix these ${issues.length} issues found in the codebase:
|
|
117
|
+
|
|
118
|
+
${issuesSummary}
|
|
119
|
+
|
|
120
|
+
## Instructions
|
|
121
|
+
Work through the issues above. For each one:
|
|
122
|
+
1. Read the relevant file to understand context
|
|
123
|
+
2. Make the minimal fix needed
|
|
124
|
+
3. Move to the next issue
|
|
125
|
+
|
|
126
|
+
Focus on correctness. Skip issues you're not confident about fixing safely.`;
|
|
127
|
+
}
|
|
128
|
+
function resolvePrompt(value) {
|
|
129
|
+
const filePath = resolve(value);
|
|
130
|
+
if (existsSync(filePath) && statSync(filePath).isFile()) {
|
|
131
|
+
return readFileSync(filePath, "utf-8").trim();
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
function generateId(prefix) {
|
|
136
|
+
return `${prefix}-${randomBytes(4).toString("hex")}`;
|
|
137
|
+
}
|
|
138
|
+
// =============================================================================
|
|
139
|
+
// Transcript Logging
|
|
140
|
+
// =============================================================================
|
|
141
|
+
const LOGS_DIR = resolve(".tx", "logs");
|
|
142
|
+
const RUNS_DIR = resolve(".tx", "runs");
|
|
143
|
+
function ensureLogsDir() {
|
|
144
|
+
mkdirSync(LOGS_DIR, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
function logPath(runId) {
|
|
147
|
+
return resolve(LOGS_DIR, `${runId}.jsonl`);
|
|
148
|
+
}
|
|
149
|
+
function writeTranscriptLine(runId, message) {
|
|
150
|
+
try {
|
|
151
|
+
const line = JSON.stringify(message) + "\n";
|
|
152
|
+
// Use async appendFile to avoid blocking the event loop during concurrent scans
|
|
153
|
+
appendFile(logPath(runId), line).catch(() => {
|
|
154
|
+
// Don't let transcript writing break the scan
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Don't let transcript writing break the scan
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function writeOrchestratorLog(runId, text) {
|
|
162
|
+
writeTranscriptLine(runId, {
|
|
163
|
+
type: "assistant",
|
|
164
|
+
message: {
|
|
165
|
+
role: "assistant",
|
|
166
|
+
content: [{ type: "text", text }],
|
|
167
|
+
},
|
|
168
|
+
timestamp: new Date().toISOString(),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function asNonEmptyPath(value) {
|
|
172
|
+
if (typeof value !== "string")
|
|
173
|
+
return null;
|
|
174
|
+
const trimmed = value.trim();
|
|
175
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
176
|
+
}
|
|
177
|
+
function extractRunLogPathHints(message) {
|
|
178
|
+
if (!message || typeof message !== "object") {
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
181
|
+
const queue = [{ value: message, depth: 0 }];
|
|
182
|
+
const seen = new Set();
|
|
183
|
+
let stdoutPath = null;
|
|
184
|
+
let stderrPath = null;
|
|
185
|
+
while (queue.length > 0 && (!stdoutPath || !stderrPath)) {
|
|
186
|
+
const next = queue.shift();
|
|
187
|
+
if (!next)
|
|
188
|
+
break;
|
|
189
|
+
const { value, depth } = next;
|
|
190
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
seen.add(value);
|
|
194
|
+
const record = value;
|
|
195
|
+
if (!stdoutPath) {
|
|
196
|
+
stdoutPath = asNonEmptyPath(record.stdout_path) ?? asNonEmptyPath(record.stdoutPath);
|
|
197
|
+
}
|
|
198
|
+
if (!stderrPath) {
|
|
199
|
+
stderrPath = asNonEmptyPath(record.stderr_path) ?? asNonEmptyPath(record.stderrPath);
|
|
200
|
+
}
|
|
201
|
+
if (depth >= 2)
|
|
202
|
+
continue;
|
|
203
|
+
for (const child of Object.values(record)) {
|
|
204
|
+
if (child && typeof child === "object") {
|
|
205
|
+
queue.push({ value: child, depth: depth + 1 });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
...(stdoutPath ? { stdoutPath } : {}),
|
|
211
|
+
...(stderrPath ? { stderrPath } : {}),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function buildRunLogPathCandidates(runId, stream) {
|
|
215
|
+
const suffixes = stream === "stdout" ? ["stdout", "stdout.log"] : ["stderr", "stderr.log"];
|
|
216
|
+
return [
|
|
217
|
+
resolve(LOGS_DIR, `${runId}.${suffixes[0]}`),
|
|
218
|
+
resolve(LOGS_DIR, `${runId}.${suffixes[1]}`),
|
|
219
|
+
resolve(RUNS_DIR, `${runId}.${suffixes[0]}`),
|
|
220
|
+
resolve(RUNS_DIR, `${runId}.${suffixes[1]}`),
|
|
221
|
+
];
|
|
222
|
+
}
|
|
223
|
+
function discoverRunLogPath(runId, stream) {
|
|
224
|
+
const candidates = buildRunLogPathCandidates(runId, stream);
|
|
225
|
+
for (const candidate of candidates) {
|
|
226
|
+
if (existsSync(candidate)) {
|
|
227
|
+
return candidate;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
function parseRunMetadata(raw) {
|
|
233
|
+
if (!raw)
|
|
234
|
+
return {};
|
|
235
|
+
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
236
|
+
return raw;
|
|
237
|
+
}
|
|
238
|
+
if (typeof raw !== "string")
|
|
239
|
+
return {};
|
|
240
|
+
try {
|
|
241
|
+
const parsed = JSON.parse(raw);
|
|
242
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
243
|
+
? parsed
|
|
244
|
+
: {};
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return {};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// =============================================================================
|
|
251
|
+
// Service Definition
|
|
252
|
+
// =============================================================================
|
|
253
|
+
/**
|
|
254
|
+
* CycleScanService orchestrates cycle-based issue discovery.
|
|
255
|
+
*/
|
|
256
|
+
export class CycleScanService extends Context.Tag("CycleScanService")() {
|
|
257
|
+
}
|
|
258
|
+
// =============================================================================
|
|
259
|
+
// Live Implementation
|
|
260
|
+
// =============================================================================
|
|
261
|
+
/**
|
|
262
|
+
* Live implementation of CycleScanService.
|
|
263
|
+
* Depends on AgentService for sub-agent dispatch and SqliteClient for DB ops.
|
|
264
|
+
*/
|
|
265
|
+
export const CycleScanServiceLive = Layer.effect(CycleScanService, Effect.gen(function* () {
|
|
266
|
+
const agentService = yield* AgentService;
|
|
267
|
+
const sqliteClient = yield* SqliteClient;
|
|
268
|
+
const computeLoss = (findings) => {
|
|
269
|
+
let loss = 0;
|
|
270
|
+
for (const f of findings) {
|
|
271
|
+
loss += LOSS_WEIGHTS[f.severity] ?? 1;
|
|
272
|
+
}
|
|
273
|
+
return loss;
|
|
274
|
+
};
|
|
275
|
+
const extractErrorReason = (error, fallback) => {
|
|
276
|
+
if (error && typeof error === "object") {
|
|
277
|
+
const record = error;
|
|
278
|
+
if (typeof record.reason === "string" && record.reason.trim().length > 0) {
|
|
279
|
+
return record.reason.trim();
|
|
280
|
+
}
|
|
281
|
+
if (typeof record.message === "string" && record.message.trim().length > 0) {
|
|
282
|
+
return record.message.trim();
|
|
283
|
+
}
|
|
284
|
+
if (typeof record.error === "string" && record.error.trim().length > 0) {
|
|
285
|
+
return record.error.trim();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const text = String(error);
|
|
289
|
+
if (text && text !== "[object Object]") {
|
|
290
|
+
return text;
|
|
291
|
+
}
|
|
292
|
+
return fallback;
|
|
293
|
+
};
|
|
294
|
+
const countBySeverity = (findings) => {
|
|
295
|
+
let high = 0, medium = 0, low = 0;
|
|
296
|
+
for (const f of findings) {
|
|
297
|
+
if (f.severity === "high")
|
|
298
|
+
high++;
|
|
299
|
+
else if (f.severity === "medium")
|
|
300
|
+
medium++;
|
|
301
|
+
else
|
|
302
|
+
low++;
|
|
303
|
+
}
|
|
304
|
+
return { high, medium, low };
|
|
305
|
+
};
|
|
306
|
+
// DB helpers
|
|
307
|
+
const db = sqliteClient;
|
|
308
|
+
const dbReadRunPathRow = (runId) => db.prepare(`SELECT transcript_path, stdout_path, stderr_path, metadata
|
|
309
|
+
FROM runs
|
|
310
|
+
WHERE id = ?`).get(runId);
|
|
311
|
+
const classifyRunLogPath = (path) => {
|
|
312
|
+
if (!path) {
|
|
313
|
+
return {
|
|
314
|
+
state: "not_reported",
|
|
315
|
+
reason: "path_not_reported",
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (existsSync(path)) {
|
|
319
|
+
return {
|
|
320
|
+
state: "captured",
|
|
321
|
+
reason: "path_reported_file_exists",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
state: "missing_file",
|
|
326
|
+
reason: "path_reported_file_missing",
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
const buildLogCaptureMetadata = (row, failureReason) => {
|
|
330
|
+
const stdout = classifyRunLogPath(row.stdout_path);
|
|
331
|
+
const stderr = classifyRunLogPath(row.stderr_path);
|
|
332
|
+
const mode = (() => {
|
|
333
|
+
if (stdout.state === "captured" || stderr.state === "captured")
|
|
334
|
+
return "stdio_captured";
|
|
335
|
+
if (stdout.state === "missing_file" || stderr.state === "missing_file")
|
|
336
|
+
return "stdio_path_missing_file";
|
|
337
|
+
if (row.transcript_path)
|
|
338
|
+
return "transcript_only";
|
|
339
|
+
return "no_capture";
|
|
340
|
+
})();
|
|
341
|
+
const reason = (() => {
|
|
342
|
+
if (mode === "stdio_captured")
|
|
343
|
+
return "stdio_paths_reported";
|
|
344
|
+
if (mode === "stdio_path_missing_file")
|
|
345
|
+
return "reported_stdio_path_missing_file";
|
|
346
|
+
if (mode === "transcript_only" && failureReason)
|
|
347
|
+
return "failed_without_stdio_capture";
|
|
348
|
+
if (mode === "transcript_only")
|
|
349
|
+
return "stdio_paths_not_reported";
|
|
350
|
+
return "no_capture_paths_reported";
|
|
351
|
+
})();
|
|
352
|
+
return {
|
|
353
|
+
mode,
|
|
354
|
+
reason,
|
|
355
|
+
transcriptPath: row.transcript_path,
|
|
356
|
+
stdout: {
|
|
357
|
+
path: row.stdout_path,
|
|
358
|
+
state: stdout.state,
|
|
359
|
+
reason: stdout.reason,
|
|
360
|
+
},
|
|
361
|
+
stderr: {
|
|
362
|
+
path: row.stderr_path,
|
|
363
|
+
state: stderr.state,
|
|
364
|
+
reason: stderr.reason,
|
|
365
|
+
},
|
|
366
|
+
failureReason,
|
|
367
|
+
updatedAt: new Date().toISOString(),
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
const dbUpdateRunMetadata = (runId, metadataPatch) => {
|
|
371
|
+
const existing = dbReadRunPathRow(runId);
|
|
372
|
+
const currentMetadata = parseRunMetadata(existing?.metadata);
|
|
373
|
+
const mergedMetadata = {
|
|
374
|
+
...currentMetadata,
|
|
375
|
+
...metadataPatch,
|
|
376
|
+
};
|
|
377
|
+
db.prepare(`UPDATE runs SET metadata = ? WHERE id = ?`).run(JSON.stringify(mergedMetadata), runId);
|
|
378
|
+
};
|
|
379
|
+
const dbSyncRunLogPaths = (runId, hints = {}) => {
|
|
380
|
+
const current = dbReadRunPathRow(runId);
|
|
381
|
+
if (!current)
|
|
382
|
+
return null;
|
|
383
|
+
const hintedStdoutPath = asNonEmptyPath(hints.stdoutPath);
|
|
384
|
+
const hintedStderrPath = asNonEmptyPath(hints.stderrPath);
|
|
385
|
+
const nextStdoutPath = current.stdout_path
|
|
386
|
+
?? hintedStdoutPath
|
|
387
|
+
?? discoverRunLogPath(runId, "stdout");
|
|
388
|
+
const nextStderrPath = current.stderr_path
|
|
389
|
+
?? hintedStderrPath
|
|
390
|
+
?? discoverRunLogPath(runId, "stderr");
|
|
391
|
+
if (nextStdoutPath !== current.stdout_path || nextStderrPath !== current.stderr_path) {
|
|
392
|
+
db.prepare(`UPDATE runs
|
|
393
|
+
SET stdout_path = ?, stderr_path = ?
|
|
394
|
+
WHERE id = ?`).run(nextStdoutPath, nextStderrPath, runId);
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
transcript_path: current.transcript_path,
|
|
398
|
+
stdout_path: nextStdoutPath,
|
|
399
|
+
stderr_path: nextStderrPath,
|
|
400
|
+
metadata: current.metadata,
|
|
401
|
+
};
|
|
402
|
+
};
|
|
403
|
+
const dbRecordRunLogPathHints = (runId, hints) => {
|
|
404
|
+
const row = dbSyncRunLogPaths(runId, hints);
|
|
405
|
+
if (!row)
|
|
406
|
+
return;
|
|
407
|
+
dbUpdateRunMetadata(runId, {
|
|
408
|
+
logCapture: buildLogCaptureMetadata(row, null),
|
|
409
|
+
});
|
|
410
|
+
};
|
|
411
|
+
const dbCreateRun = (agent) => {
|
|
412
|
+
const id = generateId("run");
|
|
413
|
+
const now = new Date().toISOString();
|
|
414
|
+
const transcriptPath = logPath(id);
|
|
415
|
+
const discoveredStdoutPath = discoverRunLogPath(id, "stdout");
|
|
416
|
+
const discoveredStderrPath = discoverRunLogPath(id, "stderr");
|
|
417
|
+
db.prepare(`INSERT INTO runs (id, agent, started_at, status, metadata, transcript_path, stdout_path, stderr_path)
|
|
418
|
+
VALUES (?, ?, ?, 'running', '{}', ?, ?, ?)`).run(id, agent, now, transcriptPath, discoveredStdoutPath, discoveredStderrPath);
|
|
419
|
+
dbUpdateRunMetadata(id, {
|
|
420
|
+
logCapture: buildLogCaptureMetadata({
|
|
421
|
+
transcript_path: transcriptPath,
|
|
422
|
+
stdout_path: discoveredStdoutPath,
|
|
423
|
+
stderr_path: discoveredStderrPath,
|
|
424
|
+
metadata: "{}",
|
|
425
|
+
}, null),
|
|
426
|
+
});
|
|
427
|
+
return id;
|
|
428
|
+
};
|
|
429
|
+
const dbUpdateRun = (runId, updates) => {
|
|
430
|
+
const sets = [];
|
|
431
|
+
const params = [];
|
|
432
|
+
if (updates.status) {
|
|
433
|
+
sets.push("status = ?");
|
|
434
|
+
params.push(updates.status);
|
|
435
|
+
if (updates.status === "completed" || updates.status === "failed") {
|
|
436
|
+
sets.push("ended_at = datetime('now')");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (updates.summary) {
|
|
440
|
+
sets.push("summary = ?");
|
|
441
|
+
params.push(updates.summary);
|
|
442
|
+
}
|
|
443
|
+
if (updates.errorMessage) {
|
|
444
|
+
sets.push("error_message = ?");
|
|
445
|
+
params.push(updates.errorMessage);
|
|
446
|
+
}
|
|
447
|
+
if (sets.length > 0) {
|
|
448
|
+
params.push(runId);
|
|
449
|
+
db.prepare(`UPDATE runs SET ${sets.join(", ")} WHERE id = ?`).run(...params);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const dbFinalizeRun = (runId, updates, failureReason = null) => {
|
|
453
|
+
dbUpdateRun(runId, updates);
|
|
454
|
+
const row = dbSyncRunLogPaths(runId);
|
|
455
|
+
if (!row)
|
|
456
|
+
return;
|
|
457
|
+
dbUpdateRunMetadata(runId, {
|
|
458
|
+
logCapture: buildLogCaptureMetadata(row, failureReason),
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
const dbCreateTask = (data) => {
|
|
462
|
+
const id = generateId("tx");
|
|
463
|
+
const now = new Date().toISOString();
|
|
464
|
+
db.prepare(`INSERT INTO tasks (id, title, description, status, parent_id, score, created_at, updated_at, completed_at, metadata)
|
|
465
|
+
VALUES (?, ?, ?, 'backlog', NULL, ?, ?, ?, NULL, ?)`).run(id, data.title, data.description, data.score, now, now, JSON.stringify(data.metadata));
|
|
466
|
+
return id;
|
|
467
|
+
};
|
|
468
|
+
const emitMetric = (runId, metricName, metadata) => {
|
|
469
|
+
db.prepare(`INSERT INTO events (timestamp, event_type, run_id, content, metadata)
|
|
470
|
+
VALUES (datetime('now'), 'metric', ?, ?, ?)`).run(runId, metricName, JSON.stringify(metadata));
|
|
471
|
+
};
|
|
472
|
+
// Agent dispatch helpers
|
|
473
|
+
const runScanAgent = (task, scan, agentModel, agentRuntime, runId) => Effect.gen(function* () {
|
|
474
|
+
const prompt = composeScanPrompt(task, scan);
|
|
475
|
+
const config = {
|
|
476
|
+
prompt,
|
|
477
|
+
options: {
|
|
478
|
+
tools: ["Read", "Glob", "Grep"],
|
|
479
|
+
permissionMode: "bypassPermissions",
|
|
480
|
+
allowDangerouslySkipPermissions: true,
|
|
481
|
+
model: agentModel,
|
|
482
|
+
runtime: agentRuntime,
|
|
483
|
+
maxTurns: 20,
|
|
484
|
+
persistSession: false,
|
|
485
|
+
outputFormat: { type: "json_schema", schema: FINDINGS_SCHEMA },
|
|
486
|
+
},
|
|
487
|
+
};
|
|
488
|
+
const result = yield* agentService
|
|
489
|
+
.run(config, (msg) => {
|
|
490
|
+
try {
|
|
491
|
+
const logPathHints = extractRunLogPathHints(msg);
|
|
492
|
+
if (logPathHints.stdoutPath || logPathHints.stderrPath) {
|
|
493
|
+
dbRecordRunLogPathHints(runId, logPathHints);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// Never let path-hint parsing block cycle execution.
|
|
498
|
+
}
|
|
499
|
+
writeTranscriptLine(runId, { ...msg, timestamp: new Date().toISOString() });
|
|
500
|
+
})
|
|
501
|
+
.pipe(Effect.mapError((e) => new CycleScanError({
|
|
502
|
+
phase: "scan",
|
|
503
|
+
reason: extractErrorReason(e, "Scan agent failed"),
|
|
504
|
+
cause: e,
|
|
505
|
+
})));
|
|
506
|
+
// Try structured output first, then parse text
|
|
507
|
+
const output = result.structuredOutput;
|
|
508
|
+
if (output?.findings) {
|
|
509
|
+
return output.findings;
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
const parsed = JSON.parse(result.text);
|
|
513
|
+
return parsed.findings ?? [];
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
return [];
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
const runDedupAgent = (findings, issuesMap, agentModel, agentRuntime, runId) => Effect.gen(function* () {
|
|
520
|
+
if (issuesMap.size === 0) {
|
|
521
|
+
return { newIssues: findings, duplicates: [] };
|
|
522
|
+
}
|
|
523
|
+
if (findings.length === 0) {
|
|
524
|
+
return { newIssues: [], duplicates: [] };
|
|
525
|
+
}
|
|
526
|
+
const existingIssues = Array.from(issuesMap.entries()).map(([id, issue]) => ({
|
|
527
|
+
id,
|
|
528
|
+
title: issue.title,
|
|
529
|
+
description: issue.description,
|
|
530
|
+
severity: issue.severity,
|
|
531
|
+
file: issue.file,
|
|
532
|
+
line: issue.line,
|
|
533
|
+
}));
|
|
534
|
+
const prompt = composeDedupPrompt(findings, existingIssues);
|
|
535
|
+
const config = {
|
|
536
|
+
prompt,
|
|
537
|
+
options: {
|
|
538
|
+
tools: [],
|
|
539
|
+
model: agentModel,
|
|
540
|
+
runtime: agentRuntime,
|
|
541
|
+
maxTurns: 1,
|
|
542
|
+
persistSession: false,
|
|
543
|
+
outputFormat: { type: "json_schema", schema: DEDUP_SCHEMA },
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
const result = yield* agentService
|
|
547
|
+
.run(config, (msg) => {
|
|
548
|
+
try {
|
|
549
|
+
const logPathHints = extractRunLogPathHints(msg);
|
|
550
|
+
if (logPathHints.stdoutPath || logPathHints.stderrPath) {
|
|
551
|
+
dbRecordRunLogPathHints(runId, logPathHints);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
// Never let path-hint parsing block cycle execution.
|
|
556
|
+
}
|
|
557
|
+
writeTranscriptLine(runId, { ...msg, timestamp: new Date().toISOString() });
|
|
558
|
+
})
|
|
559
|
+
.pipe(Effect.mapError((e) => new CycleScanError({
|
|
560
|
+
phase: "dedup",
|
|
561
|
+
reason: extractErrorReason(e, "Dedup agent failed"),
|
|
562
|
+
cause: e,
|
|
563
|
+
})));
|
|
564
|
+
const output = result.structuredOutput;
|
|
565
|
+
if (output?.newIssues) {
|
|
566
|
+
return output;
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
return JSON.parse(result.text);
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
return { newIssues: findings, duplicates: [] };
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
const runFixAgent = (issues, agentModel, agentRuntime, runId) => Effect.gen(function* () {
|
|
576
|
+
const prompt = composeFixPrompt(issues);
|
|
577
|
+
const config = {
|
|
578
|
+
prompt,
|
|
579
|
+
options: {
|
|
580
|
+
tools: ["Read", "Edit", "Write", "Bash", "Glob", "Grep"],
|
|
581
|
+
permissionMode: "acceptEdits",
|
|
582
|
+
model: agentModel,
|
|
583
|
+
runtime: agentRuntime,
|
|
584
|
+
maxTurns: 50,
|
|
585
|
+
persistSession: false,
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
yield* agentService
|
|
589
|
+
.run(config, (msg) => {
|
|
590
|
+
try {
|
|
591
|
+
const logPathHints = extractRunLogPathHints(msg);
|
|
592
|
+
if (logPathHints.stdoutPath || logPathHints.stderrPath) {
|
|
593
|
+
dbRecordRunLogPathHints(runId, logPathHints);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
// Never let path-hint parsing block cycle execution.
|
|
598
|
+
}
|
|
599
|
+
writeTranscriptLine(runId, { ...msg, timestamp: new Date().toISOString() });
|
|
600
|
+
})
|
|
601
|
+
.pipe(Effect.mapError((e) => new CycleScanError({
|
|
602
|
+
phase: "fix",
|
|
603
|
+
reason: extractErrorReason(e, "Fix agent failed"),
|
|
604
|
+
cause: e,
|
|
605
|
+
})));
|
|
606
|
+
});
|
|
607
|
+
return {
|
|
608
|
+
computeLoss,
|
|
609
|
+
runCycles: (config, onProgress) => Effect.gen(function* () {
|
|
610
|
+
ensureLogsDir();
|
|
611
|
+
const taskPrompt = resolvePrompt(config.taskPrompt);
|
|
612
|
+
const scanPrompt = config.scanPrompt
|
|
613
|
+
? resolvePrompt(config.scanPrompt)
|
|
614
|
+
: "Find bugs, anti-patterns, missing error handling, security vulnerabilities, and untested code paths.";
|
|
615
|
+
const cycleCount = config.cycles ?? 1;
|
|
616
|
+
const maxRounds = config.maxRounds ?? 10;
|
|
617
|
+
const agentCount = config.agents ?? 3;
|
|
618
|
+
const model = config.model ?? "claude-opus-4-6";
|
|
619
|
+
const runtime = config.runtime ?? "auto";
|
|
620
|
+
const scanOnly = config.scanOnly ?? false;
|
|
621
|
+
const dryRun = config.dryRun ?? false;
|
|
622
|
+
const doFix = (config.fix ?? false) && !scanOnly && !dryRun;
|
|
623
|
+
const effectiveMaxRounds = scanOnly || (!doFix && !dryRun) ? 1 : maxRounds;
|
|
624
|
+
const baseScore = config.score ?? 500;
|
|
625
|
+
const cycleName = config.name ?? taskPrompt.slice(0, 60).replace(/\n/g, " ");
|
|
626
|
+
const cycleDescription = config.description ?? scanPrompt.slice(0, 200).replace(/\n/g, " ");
|
|
627
|
+
const results = [];
|
|
628
|
+
// Query max existing cycle number for globally unique numbering
|
|
629
|
+
const maxCycleRow = db
|
|
630
|
+
.prepare(`SELECT MAX(CAST(json_extract(metadata, '$.cycle') AS INTEGER)) as maxCycle
|
|
631
|
+
FROM runs WHERE agent = 'cycle-scanner'`)
|
|
632
|
+
.get();
|
|
633
|
+
const cycleOffset = maxCycleRow?.maxCycle ?? 0;
|
|
634
|
+
for (let i = 1; i <= cycleCount; i++) {
|
|
635
|
+
const cycle = cycleOffset + i;
|
|
636
|
+
onProgress?.({ type: "cycle_start", cycle, totalCycles: cycleCount, name: cycleName });
|
|
637
|
+
// Create cycle group run
|
|
638
|
+
const cycleRunId = dbCreateRun("cycle-scanner");
|
|
639
|
+
dbUpdateRunMetadata(cycleRunId, { type: "cycle", cycle, name: cycleName, description: cycleDescription });
|
|
640
|
+
writeOrchestratorLog(cycleRunId, `Starting cycle ${cycle}/${cycleCount}: "${cycleName}" with ${agentCount} agents, model: ${model}, runtime: ${runtime}`);
|
|
641
|
+
// Load existing issues for this cycle
|
|
642
|
+
const issuesMap = new Map();
|
|
643
|
+
const existingRows = db
|
|
644
|
+
.prepare(`SELECT id, title, description, metadata FROM tasks
|
|
645
|
+
WHERE json_extract(metadata, '$.foundByScan') = 1
|
|
646
|
+
AND json_extract(metadata, '$.cycleId') = ?`)
|
|
647
|
+
.all(cycleRunId);
|
|
648
|
+
for (const row of existingRows) {
|
|
649
|
+
const meta = JSON.parse(row.metadata || "{}");
|
|
650
|
+
issuesMap.set(row.id, {
|
|
651
|
+
id: row.id,
|
|
652
|
+
title: row.title,
|
|
653
|
+
description: row.description,
|
|
654
|
+
severity: meta.severity ?? "low",
|
|
655
|
+
file: meta.file ?? "",
|
|
656
|
+
line: meta.line ?? 0,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
let totalNewIssues = 0;
|
|
660
|
+
let finalLoss = 0;
|
|
661
|
+
let converged = false;
|
|
662
|
+
let roundCount = 0;
|
|
663
|
+
for (let round = 1; round <= effectiveMaxRounds; round++) {
|
|
664
|
+
roundCount = round;
|
|
665
|
+
// SCAN PHASE — N agents in parallel
|
|
666
|
+
writeOrchestratorLog(cycleRunId, `Round ${round}: Dispatching ${agentCount} scan agents...`);
|
|
667
|
+
const scanStart = Date.now();
|
|
668
|
+
const scanEffects = Array.from({ length: agentCount }, (_, i) => Effect.gen(function* () {
|
|
669
|
+
const scanRunId = dbCreateRun(`scan-agent-${i + 1}`);
|
|
670
|
+
dbUpdateRunMetadata(scanRunId, { type: "scan", cycle, round, cycleRunId });
|
|
671
|
+
return yield* runScanAgent(taskPrompt, scanPrompt, model, runtime, scanRunId).pipe(Effect.map((findings) => ({
|
|
672
|
+
findings,
|
|
673
|
+
failed: false,
|
|
674
|
+
errorReason: null,
|
|
675
|
+
})), Effect.tap(() => Effect.sync(() => dbFinalizeRun(scanRunId, { status: "completed" }))), Effect.catchAll((error) => {
|
|
676
|
+
const reason = extractErrorReason(error, "Scan agent failed");
|
|
677
|
+
return Effect.sync(() => {
|
|
678
|
+
dbFinalizeRun(scanRunId, { status: "failed", errorMessage: reason }, reason);
|
|
679
|
+
return {
|
|
680
|
+
findings: [],
|
|
681
|
+
failed: true,
|
|
682
|
+
errorReason: reason,
|
|
683
|
+
};
|
|
684
|
+
});
|
|
685
|
+
}));
|
|
686
|
+
}));
|
|
687
|
+
const scanResults = yield* Effect.all(scanEffects, { concurrency: agentCount });
|
|
688
|
+
const failedScans = scanResults.filter((result) => result.failed);
|
|
689
|
+
const allFindings = scanResults.flatMap((result) => result.findings);
|
|
690
|
+
const scanDuration = Date.now() - scanStart;
|
|
691
|
+
if (failedScans.length > 0) {
|
|
692
|
+
const reasonList = Array.from(new Set(failedScans
|
|
693
|
+
.map((result) => result.errorReason)
|
|
694
|
+
.filter((reason) => typeof reason === "string" && reason.length > 0)));
|
|
695
|
+
const reasonText = reasonList.length > 0 ? ` Reasons: ${reasonList.join(" | ")}` : "";
|
|
696
|
+
writeOrchestratorLog(cycleRunId, `Round ${round}: ${failedScans.length}/${agentCount} scan agent(s) failed.${reasonText}`);
|
|
697
|
+
}
|
|
698
|
+
if (failedScans.length === agentCount) {
|
|
699
|
+
const reasonList = Array.from(new Set(failedScans
|
|
700
|
+
.map((result) => result.errorReason)
|
|
701
|
+
.filter((reason) => typeof reason === "string" && reason.length > 0)));
|
|
702
|
+
const reasonText = reasonList.length > 0 ? reasonList.join(" | ") : "No failure reason reported";
|
|
703
|
+
const fatalReason = `All ${agentCount} scan agents failed in cycle ${cycle} round ${round}: ${reasonText}`;
|
|
704
|
+
writeOrchestratorLog(cycleRunId, fatalReason);
|
|
705
|
+
dbFinalizeRun(cycleRunId, {
|
|
706
|
+
status: "failed",
|
|
707
|
+
errorMessage: fatalReason,
|
|
708
|
+
}, fatalReason);
|
|
709
|
+
return yield* Effect.fail(new CycleScanError({ phase: "scan", reason: fatalReason }));
|
|
710
|
+
}
|
|
711
|
+
writeOrchestratorLog(cycleRunId, `Round ${round}: Scan complete — ${allFindings.length} findings in ${(scanDuration / 1000).toFixed(1)}s`);
|
|
712
|
+
onProgress?.({ type: "scan_complete", cycle, round, findings: allFindings.length, durationMs: scanDuration });
|
|
713
|
+
if (allFindings.length === 0) {
|
|
714
|
+
writeOrchestratorLog(cycleRunId, `Round ${round}: No findings — converged!`);
|
|
715
|
+
emitMetric(cycleRunId, "cycle.round.loss", {
|
|
716
|
+
metric: "cycle.round.loss",
|
|
717
|
+
cycleId: cycleRunId,
|
|
718
|
+
cycle,
|
|
719
|
+
round,
|
|
720
|
+
loss: 0,
|
|
721
|
+
newIssues: 0,
|
|
722
|
+
existingIssues: issuesMap.size,
|
|
723
|
+
duplicates: 0,
|
|
724
|
+
high: 0,
|
|
725
|
+
medium: 0,
|
|
726
|
+
low: 0,
|
|
727
|
+
});
|
|
728
|
+
onProgress?.({ type: "converged", cycle, round });
|
|
729
|
+
converged = true;
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
// DEDUP PHASE
|
|
733
|
+
const dedupRunId = dbCreateRun("dedup-agent");
|
|
734
|
+
dbUpdateRunMetadata(dedupRunId, { type: "dedup", cycle, round, cycleRunId });
|
|
735
|
+
const dedupResult = yield* runDedupAgent(allFindings, issuesMap, model, runtime, dedupRunId).pipe(Effect.tap(() => Effect.sync(() => dbFinalizeRun(dedupRunId, { status: "completed" }))), Effect.tapError((error) => Effect.sync(() => {
|
|
736
|
+
const reason = extractErrorReason(error, "Dedup agent failed");
|
|
737
|
+
dbFinalizeRun(dedupRunId, { status: "failed", errorMessage: reason }, reason);
|
|
738
|
+
})), Effect.catchAll(() => Effect.succeed({ newIssues: allFindings, duplicates: [] })));
|
|
739
|
+
writeOrchestratorLog(cycleRunId, `Round ${round}: Dedup complete — ${dedupResult.newIssues.length} new, ${dedupResult.duplicates.length} duplicates`);
|
|
740
|
+
onProgress?.({
|
|
741
|
+
type: "dedup_complete",
|
|
742
|
+
cycle,
|
|
743
|
+
round,
|
|
744
|
+
newIssues: dedupResult.newIssues.length,
|
|
745
|
+
duplicates: dedupResult.duplicates.length,
|
|
746
|
+
});
|
|
747
|
+
// CREATE TASKS for new issues
|
|
748
|
+
if (!dryRun && dedupResult.newIssues.length > 0) {
|
|
749
|
+
for (const issue of dedupResult.newIssues) {
|
|
750
|
+
const taskId = dbCreateTask({
|
|
751
|
+
title: `${issue.severity.toUpperCase()}: ${issue.title}`,
|
|
752
|
+
description: issue.description,
|
|
753
|
+
score: baseScore,
|
|
754
|
+
metadata: {
|
|
755
|
+
foundByScan: true,
|
|
756
|
+
cycleId: cycleRunId,
|
|
757
|
+
cycle,
|
|
758
|
+
round,
|
|
759
|
+
severity: issue.severity,
|
|
760
|
+
issueType: issue.issueType,
|
|
761
|
+
file: issue.file,
|
|
762
|
+
line: issue.line,
|
|
763
|
+
},
|
|
764
|
+
});
|
|
765
|
+
issuesMap.set(taskId, {
|
|
766
|
+
id: taskId,
|
|
767
|
+
title: issue.title,
|
|
768
|
+
description: issue.description,
|
|
769
|
+
severity: issue.severity,
|
|
770
|
+
file: issue.file,
|
|
771
|
+
line: issue.line,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
else if (dryRun && dedupResult.newIssues.length > 0) {
|
|
776
|
+
// Still add to in-memory map for dedup in subsequent rounds
|
|
777
|
+
for (const issue of dedupResult.newIssues) {
|
|
778
|
+
const fakeId = `dry-${cycle}-${round}-${issuesMap.size}`;
|
|
779
|
+
issuesMap.set(fakeId, {
|
|
780
|
+
id: fakeId,
|
|
781
|
+
title: issue.title,
|
|
782
|
+
description: issue.description,
|
|
783
|
+
severity: issue.severity,
|
|
784
|
+
file: issue.file,
|
|
785
|
+
line: issue.line,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
totalNewIssues += dedupResult.newIssues.length;
|
|
790
|
+
// COMPUTE ROUND LOSS
|
|
791
|
+
const { high, medium, low } = countBySeverity(dedupResult.newIssues);
|
|
792
|
+
const loss = computeLoss(dedupResult.newIssues);
|
|
793
|
+
finalLoss = loss;
|
|
794
|
+
writeOrchestratorLog(cycleRunId, `Round ${round}: Loss = ${loss} (${high}H x3 + ${medium}M x2 + ${low}L x1)`);
|
|
795
|
+
onProgress?.({ type: "round_loss", cycle, round, loss, high, medium, low });
|
|
796
|
+
// FIX PHASE
|
|
797
|
+
if (doFix && dedupResult.newIssues.length > 0) {
|
|
798
|
+
const fixRunId = dbCreateRun("fix-agent");
|
|
799
|
+
dbUpdateRunMetadata(fixRunId, { type: "fix", cycle, round, cycleRunId });
|
|
800
|
+
yield* runFixAgent(dedupResult.newIssues, model, runtime, fixRunId).pipe(Effect.tap(() => Effect.sync(() => dbFinalizeRun(fixRunId, { status: "completed" }))), Effect.tapError((error) => Effect.sync(() => {
|
|
801
|
+
const reason = extractErrorReason(error, "Fix agent failed");
|
|
802
|
+
dbFinalizeRun(fixRunId, { status: "failed", errorMessage: reason }, reason);
|
|
803
|
+
})), Effect.catchAll(() => Effect.void));
|
|
804
|
+
}
|
|
805
|
+
// EMIT ROUND METRICS
|
|
806
|
+
emitMetric(cycleRunId, "cycle.round.loss", {
|
|
807
|
+
metric: "cycle.round.loss",
|
|
808
|
+
cycleId: cycleRunId,
|
|
809
|
+
cycle,
|
|
810
|
+
round,
|
|
811
|
+
loss,
|
|
812
|
+
newIssues: dedupResult.newIssues.length,
|
|
813
|
+
existingIssues: issuesMap.size,
|
|
814
|
+
duplicates: dedupResult.duplicates.length,
|
|
815
|
+
high,
|
|
816
|
+
medium,
|
|
817
|
+
low,
|
|
818
|
+
});
|
|
819
|
+
// CHECK CONVERGENCE
|
|
820
|
+
if (dedupResult.newIssues.length === 0) {
|
|
821
|
+
converged = true;
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
// EMIT CYCLE-LEVEL METRIC
|
|
826
|
+
emitMetric(cycleRunId, "cycle.complete", {
|
|
827
|
+
metric: "cycle.complete",
|
|
828
|
+
cycleId: cycleRunId,
|
|
829
|
+
cycle,
|
|
830
|
+
name: cycleName,
|
|
831
|
+
description: cycleDescription,
|
|
832
|
+
rounds: roundCount,
|
|
833
|
+
totalNewIssues,
|
|
834
|
+
existingIssues: issuesMap.size,
|
|
835
|
+
finalLoss,
|
|
836
|
+
converged,
|
|
837
|
+
});
|
|
838
|
+
// UPDATE CYCLE RUN
|
|
839
|
+
dbFinalizeRun(cycleRunId, {
|
|
840
|
+
status: "completed",
|
|
841
|
+
summary: `Cycle ${cycle}: ${roundCount} rounds, ${totalNewIssues} new issues, loss ${finalLoss}${converged ? " (converged)" : ""}`,
|
|
842
|
+
});
|
|
843
|
+
dbUpdateRunMetadata(cycleRunId, {
|
|
844
|
+
type: "cycle",
|
|
845
|
+
cycle,
|
|
846
|
+
name: cycleName,
|
|
847
|
+
description: cycleDescription,
|
|
848
|
+
rounds: roundCount,
|
|
849
|
+
totalNewIssues,
|
|
850
|
+
existingIssues: issuesMap.size,
|
|
851
|
+
finalLoss,
|
|
852
|
+
converged,
|
|
853
|
+
});
|
|
854
|
+
writeOrchestratorLog(cycleRunId, `Cycle ${cycle} complete: ${roundCount} rounds, ${totalNewIssues} new issues, final loss ${finalLoss}${converged ? " — CONVERGED" : ""}`);
|
|
855
|
+
const cycleResult = {
|
|
856
|
+
cycleRunId,
|
|
857
|
+
cycle,
|
|
858
|
+
name: cycleName,
|
|
859
|
+
description: cycleDescription,
|
|
860
|
+
rounds: roundCount,
|
|
861
|
+
totalNewIssues,
|
|
862
|
+
existingIssues: issuesMap.size,
|
|
863
|
+
finalLoss,
|
|
864
|
+
converged,
|
|
865
|
+
};
|
|
866
|
+
onProgress?.({ type: "cycle_complete", result: cycleResult });
|
|
867
|
+
results.push(cycleResult);
|
|
868
|
+
}
|
|
869
|
+
return results;
|
|
870
|
+
}).pipe(Effect.catchAllDefect((defect) => Effect.fail(new CycleScanError({
|
|
871
|
+
phase: "unknown",
|
|
872
|
+
reason: `Unexpected defect: ${defect instanceof Error ? defect.message : String(defect)}`,
|
|
873
|
+
cause: defect,
|
|
874
|
+
})))),
|
|
875
|
+
};
|
|
876
|
+
}));
|
|
877
|
+
//# sourceMappingURL=cycle-scan-service-impl.js.map
|