@winspan/claude-forge 0.7.1 → 0.7.3

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.
Files changed (133) hide show
  1. package/dist/ai-gateway/model-selector.d.ts.map +1 -1
  2. package/dist/ai-gateway/model-selector.js +42 -6
  3. package/dist/ai-gateway/model-selector.js.map +1 -1
  4. package/dist/autopilot/intent-engine.d.ts +0 -4
  5. package/dist/autopilot/intent-engine.d.ts.map +1 -1
  6. package/dist/autopilot/intent-engine.js +32 -102
  7. package/dist/autopilot/intent-engine.js.map +1 -1
  8. package/dist/autopilot/issue-tracker.d.ts +48 -0
  9. package/dist/autopilot/issue-tracker.d.ts.map +1 -0
  10. package/dist/autopilot/issue-tracker.js +94 -0
  11. package/dist/autopilot/issue-tracker.js.map +1 -0
  12. package/dist/autopilot/phase-gap-detector.d.ts +25 -0
  13. package/dist/autopilot/phase-gap-detector.d.ts.map +1 -0
  14. package/dist/autopilot/phase-gap-detector.js +80 -0
  15. package/dist/autopilot/phase-gap-detector.js.map +1 -0
  16. package/dist/autopilot/quality-gate.d.ts +22 -0
  17. package/dist/autopilot/quality-gate.d.ts.map +1 -1
  18. package/dist/autopilot/quality-gate.js +108 -25
  19. package/dist/autopilot/quality-gate.js.map +1 -1
  20. package/dist/cli/commands/pattern.d.ts.map +1 -1
  21. package/dist/cli/commands/pattern.js +44 -38
  22. package/dist/cli/commands/pattern.js.map +1 -1
  23. package/dist/daemon/context-injector.d.ts +11 -1
  24. package/dist/daemon/context-injector.d.ts.map +1 -1
  25. package/dist/daemon/context-injector.js +27 -4
  26. package/dist/daemon/context-injector.js.map +1 -1
  27. package/dist/daemon/engine-registry.d.ts.map +1 -1
  28. package/dist/daemon/engine-registry.js +4 -1
  29. package/dist/daemon/engine-registry.js.map +1 -1
  30. package/dist/daemon/handler-context.d.ts +4 -2
  31. package/dist/daemon/handler-context.d.ts.map +1 -1
  32. package/dist/daemon/handlers/orchestration-context.d.ts +2 -0
  33. package/dist/daemon/handlers/orchestration-context.d.ts.map +1 -1
  34. package/dist/daemon/handlers/post-tool-use-handler.d.ts.map +1 -1
  35. package/dist/daemon/handlers/post-tool-use-handler.js +26 -4
  36. package/dist/daemon/handlers/post-tool-use-handler.js.map +1 -1
  37. package/dist/daemon/handlers/stages/09-pipeline-active.d.ts +1 -1
  38. package/dist/daemon/handlers/stages/09-pipeline-active.d.ts.map +1 -1
  39. package/dist/daemon/handlers/stages/09-pipeline-active.js +22 -6
  40. package/dist/daemon/handlers/stages/09-pipeline-active.js.map +1 -1
  41. package/dist/daemon/handlers/stages/12-strategy-advice.d.ts.map +1 -1
  42. package/dist/daemon/handlers/stages/12-strategy-advice.js +1 -4
  43. package/dist/daemon/handlers/stages/12-strategy-advice.js.map +1 -1
  44. package/dist/daemon/handlers/stages/17-simple-task.d.ts.map +1 -1
  45. package/dist/daemon/handlers/stages/17-simple-task.js +1 -1
  46. package/dist/daemon/handlers/stages/17-simple-task.js.map +1 -1
  47. package/dist/daemon/handlers/stages/18-complex-task.d.ts.map +1 -1
  48. package/dist/daemon/handlers/stages/18-complex-task.js +2 -2
  49. package/dist/daemon/handlers/stages/18-complex-task.js.map +1 -1
  50. package/dist/daemon/handlers/stages/19-moderate-task.d.ts.map +1 -1
  51. package/dist/daemon/handlers/stages/19-moderate-task.js +1 -1
  52. package/dist/daemon/handlers/stages/19-moderate-task.js.map +1 -1
  53. package/dist/daemon/handlers/stop-handler.d.ts.map +1 -1
  54. package/dist/daemon/handlers/stop-handler.js +5 -0
  55. package/dist/daemon/handlers/stop-handler.js.map +1 -1
  56. package/dist/daemon/handlers/user-prompt-handler.d.ts.map +1 -1
  57. package/dist/daemon/handlers/user-prompt-handler.js +95 -48
  58. package/dist/daemon/handlers/user-prompt-handler.js.map +1 -1
  59. package/dist/distill/writer.d.ts.map +1 -1
  60. package/dist/distill/writer.js +27 -11
  61. package/dist/distill/writer.js.map +1 -1
  62. package/dist/knowledge/graph.d.ts +13 -0
  63. package/dist/knowledge/graph.d.ts.map +1 -1
  64. package/dist/knowledge/graph.js +62 -0
  65. package/dist/knowledge/graph.js.map +1 -1
  66. package/dist/pipeline/index.d.ts +9 -0
  67. package/dist/pipeline/index.d.ts.map +1 -1
  68. package/dist/pipeline/index.js +49 -3
  69. package/dist/pipeline/index.js.map +1 -1
  70. package/dist/{pattern-engine/types.d.ts → pipeline/pattern-types.d.ts} +1 -1
  71. package/dist/pipeline/pattern-types.d.ts.map +1 -0
  72. package/dist/{pattern-engine/types.js → pipeline/pattern-types.js} +1 -1
  73. package/dist/pipeline/pattern-types.js.map +1 -0
  74. package/dist/pipeline/phase-manager.d.ts +5 -0
  75. package/dist/pipeline/phase-manager.d.ts.map +1 -1
  76. package/dist/pipeline/phase-manager.js +67 -25
  77. package/dist/pipeline/phase-manager.js.map +1 -1
  78. package/dist/pipeline/report-generator.d.ts +20 -0
  79. package/dist/pipeline/report-generator.d.ts.map +1 -0
  80. package/dist/pipeline/report-generator.js +123 -0
  81. package/dist/pipeline/report-generator.js.map +1 -0
  82. package/dist/prompts/intent-analysis.md +62 -0
  83. package/dist/prompts/registry.d.ts +17 -0
  84. package/dist/prompts/registry.d.ts.map +1 -0
  85. package/dist/prompts/registry.js +40 -0
  86. package/dist/prompts/registry.js.map +1 -0
  87. package/dist/skill-registry/evolver/index.d.ts +1 -1
  88. package/dist/skill-registry/evolver/index.d.ts.map +1 -1
  89. package/dist/{pattern-engine → skill-registry}/pattern-evolver.d.ts +6 -2
  90. package/dist/skill-registry/pattern-evolver.d.ts.map +1 -0
  91. package/dist/{pattern-engine → skill-registry}/pattern-evolver.js +30 -6
  92. package/dist/skill-registry/pattern-evolver.js.map +1 -0
  93. package/dist/storage/repositories/distill-repository.d.ts +2 -1
  94. package/dist/storage/repositories/distill-repository.d.ts.map +1 -1
  95. package/dist/storage/repositories/distill-repository.js +6 -0
  96. package/dist/storage/repositories/distill-repository.js.map +1 -1
  97. package/dist/storage/sqlite.d.ts +96 -157
  98. package/dist/storage/sqlite.d.ts.map +1 -1
  99. package/dist/storage/sqlite.js +123 -896
  100. package/dist/storage/sqlite.js.map +1 -1
  101. package/package.json +2 -2
  102. package/dist/daemon/engine-registry/init-pattern-engine.d.ts +0 -9
  103. package/dist/daemon/engine-registry/init-pattern-engine.d.ts.map +0 -1
  104. package/dist/daemon/engine-registry/init-pattern-engine.js +0 -22
  105. package/dist/daemon/engine-registry/init-pattern-engine.js.map +0 -1
  106. package/dist/pattern-engine/convention-pattern-generator.d.ts +0 -14
  107. package/dist/pattern-engine/convention-pattern-generator.d.ts.map +0 -1
  108. package/dist/pattern-engine/convention-pattern-generator.js +0 -120
  109. package/dist/pattern-engine/convention-pattern-generator.js.map +0 -1
  110. package/dist/pattern-engine/execution-tracker.d.ts +0 -45
  111. package/dist/pattern-engine/execution-tracker.d.ts.map +0 -1
  112. package/dist/pattern-engine/execution-tracker.js +0 -136
  113. package/dist/pattern-engine/execution-tracker.js.map +0 -1
  114. package/dist/pattern-engine/index.d.ts +0 -75
  115. package/dist/pattern-engine/index.d.ts.map +0 -1
  116. package/dist/pattern-engine/index.js +0 -247
  117. package/dist/pattern-engine/index.js.map +0 -1
  118. package/dist/pattern-engine/pattern-evolver.d.ts.map +0 -1
  119. package/dist/pattern-engine/pattern-evolver.js.map +0 -1
  120. package/dist/pattern-engine/pattern-executor.d.ts +0 -43
  121. package/dist/pattern-engine/pattern-executor.d.ts.map +0 -1
  122. package/dist/pattern-engine/pattern-executor.js +0 -228
  123. package/dist/pattern-engine/pattern-executor.js.map +0 -1
  124. package/dist/pattern-engine/pattern-loader.d.ts +0 -14
  125. package/dist/pattern-engine/pattern-loader.d.ts.map +0 -1
  126. package/dist/pattern-engine/pattern-loader.js +0 -111
  127. package/dist/pattern-engine/pattern-loader.js.map +0 -1
  128. package/dist/pattern-engine/pattern-router.d.ts +0 -31
  129. package/dist/pattern-engine/pattern-router.d.ts.map +0 -1
  130. package/dist/pattern-engine/pattern-router.js +0 -160
  131. package/dist/pattern-engine/pattern-router.js.map +0 -1
  132. package/dist/pattern-engine/types.d.ts.map +0 -1
  133. package/dist/pattern-engine/types.js.map +0 -1
@@ -496,1001 +496,228 @@ export class SQLiteStorage {
496
496
  `);
497
497
  }
498
498
  }
499
+ // ── Events ────────────────────────────────────────────────────────────────
499
500
  writeEvent(event) {
500
- const writeEventTx = this.db.transaction((ev) => {
501
- const toolInputStr = ev.tool_input ? JSON.stringify(ev.tool_input) : null;
502
- const changes = this.db.prepare(`
503
- INSERT OR IGNORE INTO events (
504
- event_id, session_id, project_path, timestamp,
505
- hook_type, tool_name, tool_input, tool_output,
506
- user_prompt, ai_response, distilled
507
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
508
- `).run(ev.event_id, ev.session_id, ev.project_path, ev.timestamp, ev.hook_type, ev.tool_name || null, toolInputStr, ev.tool_output ? JSON.stringify(ev.tool_output) : null, ev.user_prompt || null, ev.ai_response || null, ev.distilled ? 1 : 0);
509
- // FTS5 同步(只在新记录插入时同步,避免重复)
510
- if (changes.changes > 0) {
511
- try {
512
- const rowResult = this.db.prepare('SELECT rowid FROM events WHERE event_id = ?').get(ev.event_id);
513
- if (rowResult) {
514
- this.db.prepare(`
515
- INSERT OR IGNORE INTO events_fts(rowid, event_id, session_id, project_path, tool_name, tool_input, user_prompt)
516
- VALUES (?, ?, ?, ?, ?, ?, ?)
517
- `).run(rowResult.rowid, ev.event_id, ev.session_id, ev.project_path, ev.tool_name || null, toolInputStr, ev.user_prompt || null);
518
- }
519
- }
520
- catch {
521
- // FTS5 表可能尚未创建(迁移 v2 前的旧数据库),静默忽略
522
- }
523
- }
524
- // 更新或创建会话(在同一事务内)
525
- this.upsertSession(ev);
526
- });
527
- writeEventTx(event);
528
- }
529
- upsertSession(event) {
530
- const existing = this.db.prepare('SELECT session_id FROM sessions WHERE session_id = ? AND project_path = ?').get(event.session_id, event.project_path);
531
- if (!existing) {
532
- // 用 INSERT OR REPLACE 处理同 session_id 不同 project_path 的情况
533
- this.db.prepare(`
534
- INSERT INTO sessions (session_id, project_path, status, start_time, last_event_time, event_count)
535
- VALUES (?, ?, 'active', ?, ?, 1)
536
- ON CONFLICT(session_id) DO UPDATE SET
537
- project_path = excluded.project_path,
538
- last_event_time = excluded.last_event_time,
539
- event_count = event_count + 1,
540
- updated_at = datetime('now')
541
- `).run(event.session_id, event.project_path, event.timestamp, event.timestamp);
542
- }
543
- else {
544
- this.db.prepare(`
545
- UPDATE sessions
546
- SET last_event_time = ?, event_count = event_count + 1, updated_at = datetime('now')
547
- WHERE session_id = ? AND project_path = ?
548
- `).run(event.timestamp, event.session_id, event.project_path);
549
- }
550
- // Stop Hook → 标记会话为 completed
551
- if (event.hook_type === 'Stop') {
552
- this.db.prepare(`
553
- UPDATE sessions SET status = 'completed', end_time = ?, updated_at = datetime('now')
554
- WHERE session_id = ? AND project_path = ?
555
- `).run(event.timestamp, event.session_id, event.project_path);
556
- }
501
+ this.repositories.events.writeEvent(event);
557
502
  }
558
503
  queryEvents(filters) {
559
- let sql = 'SELECT * FROM events WHERE 1=1';
560
- const params = [];
561
- if (filters.session_id) {
562
- sql += ' AND session_id = ?';
563
- params.push(filters.session_id);
564
- }
565
- if (filters.project_path) {
566
- sql += ' AND project_path = ?';
567
- params.push(filters.project_path);
568
- }
569
- if (filters.start_time) {
570
- sql += ' AND timestamp >= ?';
571
- params.push(filters.start_time);
572
- }
573
- if (filters.end_time) {
574
- sql += ' AND timestamp <= ?';
575
- params.push(filters.end_time);
576
- }
577
- if (filters.tool_name) {
578
- sql += ' AND tool_name = ?';
579
- params.push(filters.tool_name);
580
- }
581
- if (filters.hook_type) {
582
- sql += ' AND hook_type = ?';
583
- params.push(filters.hook_type);
584
- }
585
- if (filters.distilled !== undefined) {
586
- sql += ' AND distilled = ?';
587
- params.push(filters.distilled ? 1 : 0);
588
- }
589
- sql += ' ORDER BY timestamp DESC';
590
- if (filters.limit) {
591
- sql += ' LIMIT ?';
592
- params.push(filters.limit);
593
- }
594
- if (filters.offset) {
595
- sql += ' OFFSET ?';
596
- params.push(filters.offset);
597
- }
598
- const rows = this.db.prepare(sql).all(...params);
599
- return rows.map(row => this.rowToEvent(row));
504
+ return this.repositories.events.queryEvents(filters);
600
505
  }
601
506
  countEvents(filters) {
602
- let sql = 'SELECT COUNT(*) as count FROM events WHERE 1=1';
603
- const params = [];
604
- if (filters.session_id) {
605
- sql += ' AND session_id = ?';
606
- params.push(filters.session_id);
607
- }
608
- if (filters.project_path) {
609
- sql += ' AND project_path = ?';
610
- params.push(filters.project_path);
611
- }
612
- if (filters.distilled !== undefined) {
613
- sql += ' AND distilled = ?';
614
- params.push(filters.distilled ? 1 : 0);
615
- }
616
- const row = this.db.prepare(sql).get(...params);
617
- return row.count;
507
+ return this.repositories.events.countEvents(filters);
618
508
  }
619
509
  markEventsDistilled(eventIds) {
620
- const stmt = this.db.prepare('UPDATE events SET distilled = 1 WHERE event_id = ?');
621
- const markAll = this.db.transaction((ids) => {
622
- for (const id of ids) {
623
- stmt.run(id);
624
- }
625
- });
626
- const t0 = Date.now();
627
- markAll(eventIds);
628
- const elapsed = Date.now() - t0;
629
- if (elapsed > 50) {
630
- logger.warn(`[SQLite] markEventsDistilled 耗时 ${elapsed}ms(${eventIds.length} 条)`);
631
- }
510
+ this.repositories.events.markEventsDistilled(eventIds);
511
+ }
512
+ getUndistilledEvents(sessionId, projectPath) {
513
+ return this.repositories.events.getUndistilledEvents(sessionId, projectPath);
514
+ }
515
+ searchByKeywords(keywords, projectPath, limit = 20) {
516
+ return this.repositories.events.searchByKeywords(keywords, projectPath, limit);
632
517
  }
518
+ // ── Sessions ──────────────────────────────────────────────────────────────
633
519
  getSession(sessionId, projectPath) {
634
- let sql = 'SELECT * FROM sessions WHERE session_id = ?';
635
- const params = [sessionId];
636
- if (projectPath) {
637
- sql += ' AND project_path = ?';
638
- params.push(projectPath);
639
- }
640
- const row = this.db.prepare(sql).get(...params);
641
- return row ? this.rowToSession(row) : null;
520
+ return this.repositories.sessions.getSession(sessionId, projectPath);
642
521
  }
643
522
  querySessions(filters) {
644
- let sql = 'SELECT * FROM sessions WHERE 1=1';
645
- const params = [];
646
- if (filters.project_path) {
647
- sql += ' AND project_path = ?';
648
- params.push(filters.project_path);
649
- }
650
- if (filters.status) {
651
- sql += ' AND status = ?';
652
- params.push(filters.status);
653
- }
654
- sql += ' ORDER BY start_time DESC';
655
- if (filters.limit) {
656
- sql += ' LIMIT ?';
657
- params.push(filters.limit);
658
- }
659
- const rows = this.db.prepare(sql).all(...params);
660
- return rows.map(this.rowToSession);
523
+ return this.repositories.sessions.querySessions(filters);
661
524
  }
662
525
  updateSessionStatus(sessionId, status, endTime, projectPath) {
663
- const pathFilter = projectPath ? ' AND project_path = ?' : '';
664
- if (endTime) {
665
- const sql = `UPDATE sessions SET status = ?, end_time = ?, updated_at = datetime('now') WHERE session_id = ?${pathFilter}`;
666
- const params = projectPath ? [status, endTime, sessionId, projectPath] : [status, endTime, sessionId];
667
- this.db.prepare(sql).run(...params);
668
- }
669
- else {
670
- const sql = `UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE session_id = ?${pathFilter}`;
671
- const params = projectPath ? [status, sessionId, projectPath] : [status, sessionId];
672
- this.db.prepare(sql).run(...params);
673
- }
526
+ this.repositories.sessions.updateSessionStatus(sessionId, status, endTime, projectPath);
674
527
  }
675
- getUndistilledEvents(sessionId, projectPath) {
676
- const filters = {
677
- session_id: sessionId,
678
- distilled: false,
679
- };
680
- if (projectPath) {
681
- filters.project_path = projectPath;
682
- }
683
- return this.queryEvents(filters);
528
+ getLastCompletedTaskSession(projectPath) {
529
+ return this.repositories.sessions.getLastCompletedTaskSession(projectPath);
684
530
  }
531
+ // ── Maintenance ───────────────────────────────────────────────────────────
685
532
  getStorageSize() {
686
- const row = this.db.prepare("SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()").get();
687
- return row.size;
533
+ return this.repositories.maintenance.getStorageSize();
534
+ }
535
+ getDbSizeMb() {
536
+ return this.repositories.maintenance.getDbSizeMb();
688
537
  }
689
- /**
690
- * 清理已蒸馏且超过 retentionDays 天的事件
691
- * 返回删除的条数
692
- */
693
538
  purgeDistilledEvents(retentionDays = 7) {
694
- const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
695
- const result = this.db.prepare('DELETE FROM events WHERE distilled = 1 AND timestamp < ?').run(cutoff);
696
- return result.changes;
539
+ return this.repositories.maintenance.purgeDistilledEvents(retentionDays);
697
540
  }
698
- /**
699
- * 清理已完成/放弃且超过 retentionDays 天的会话
700
- */
701
541
  purgeOldSessions(retentionDays = 30) {
702
- const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
703
- const result = this.db.prepare("DELETE FROM sessions WHERE status IN ('completed', 'abandoned') AND updated_at < ?").run(cutoff);
704
- return result.changes;
542
+ return this.repositories.maintenance.purgeOldSessions(retentionDays);
705
543
  }
706
- /**
707
- * 执行数据库维护:清理过期数据 + VACUUM
708
- * 建议在 daemon 启动时和每日凌晨 3 点调用
709
- * @param maxSizeMb 超过此大小时输出警告(来自配置 storage.max_size_mb)
710
- */
711
544
  runMaintenance(maxSizeMb = 1024) {
712
- // 清理 30 天前已蒸馏的事件
713
- const purgedEvents = this.purgeDistilledEvents(30);
714
- // 清理 30 天前已完成的会话
715
- const purgedSessions = this.purgeOldSessions(30);
716
- // 清理 7 天前已处理的蒸馏队列
717
- const queueResult = this.db.prepare("DELETE FROM distill_queue WHERE status = 'processed' AND processed_at < datetime('now', '-7 days')").run();
718
- const purgedQueue = queueResult.changes;
719
- // VACUUM 回收空间
720
- this.db.exec('VACUUM');
721
- const dbSizeMb = this.getDbSizeMb();
722
- if (dbSizeMb > maxSizeMb) {
723
- logger.warn(`[存储] 数据库大小 ${dbSizeMb.toFixed(1)}MB 超过上限 ${maxSizeMb}MB,建议检查数据增长`);
724
- }
725
- logger.info(`[存储] 维护完成:清理事件 ${purgedEvents} 条,会话 ${purgedSessions} 条,队列 ${purgedQueue} 条,当前大小 ${dbSizeMb.toFixed(1)}MB`);
726
- return { purgedEvents, purgedSessions, purgedQueue, dbSizeMb };
545
+ return this.repositories.maintenance.runMaintenance(maxSizeMb);
727
546
  }
728
- /** 返回数据库文件大小(MB) */
729
- getDbSizeMb() {
730
- try {
731
- const stat = fs.statSync(this.dbPath);
732
- return stat.size / (1024 * 1024);
733
- }
734
- catch {
735
- return 0;
736
- }
547
+ getDaemonState(key) {
548
+ return this.repositories.maintenance.getDaemonState(key);
549
+ }
550
+ setDaemonState(key, value) {
551
+ this.repositories.maintenance.setDaemonState(key, value);
737
552
  }
738
- /** 写入或更新 Pattern 执行记录(INSERT OR REPLACE 支持 running→completed 状态更新) */
553
+ // ── Pattern Executions ────────────────────────────────────────────────────
739
554
  writePatternExecution(record) {
740
- this.db.prepare(`
741
- INSERT OR REPLACE INTO pattern_executions
742
- (id, pattern_id, session_id, project_path, phase_id, status,
743
- started_at, completed_at, duration_ms, output, user_satisfaction, created_at)
744
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
745
- `).run(record.id, record.pattern_id, record.session_id, record.project_path, record.phase_id, record.status, record.started_at ?? null, record.completed_at ?? null, record.duration_ms ?? null, record.output ?? null, record.user_satisfaction ?? null, record.created_at);
746
- }
747
- /** 查询指定 Pattern 的最近执行记录 */
555
+ this.repositories.distill.writePatternExecution(record);
556
+ }
748
557
  queryPatternExecutions(patternId, limit = 50) {
749
- const rows = this.db.prepare(`
750
- SELECT * FROM pattern_executions
751
- WHERE pattern_id = ?
752
- ORDER BY created_at DESC
753
- LIMIT ?
754
- `).all(patternId, limit);
755
- return rows.map((r) => ({
756
- id: r.id,
757
- pattern_id: r.pattern_id,
758
- session_id: r.session_id,
759
- project_path: r.project_path,
760
- phase_id: r.phase_id,
761
- status: r.status,
762
- started_at: r.started_at,
763
- completed_at: r.completed_at,
764
- duration_ms: r.duration_ms,
765
- output: r.output,
766
- user_satisfaction: r.user_satisfaction,
767
- task_session_id: r.task_session_id, // v5 新增
768
- outcome: r.outcome, // v5 新增
769
- created_at: r.created_at,
770
- }));
771
- }
772
- /** 更新执行记录的用户满意度(+1 正向 / -1 负向 / 0 中性) */
773
- updatePatternSatisfaction(executionId, satisfaction) {
774
- this.db.prepare(`
775
- UPDATE pattern_executions SET user_satisfaction = ? WHERE id = ?
776
- `).run(satisfaction, executionId);
558
+ return this.repositories.distill.queryPatternExecutions(patternId, limit);
777
559
  }
778
- /** FTS5 全文检索:按关键词搜索相关历史事件,返回最近 limit 条 */
779
- searchByKeywords(keywords, projectPath, limit = 20) {
780
- if (keywords.length === 0)
781
- return [];
782
- try {
783
- // 关键词清理:过滤停用词 + 特殊字符,截取最多 5 个
784
- const cleaned = keywords
785
- .map(kw => kw.replace(/['"*\[\](){}]/g, '').trim())
786
- .filter(kw => kw.length >= 2)
787
- .filter(kw => !SQLiteStorage.STOPWORDS.has(kw) && !SQLiteStorage.STOPWORDS.has(kw.toLowerCase()))
788
- .slice(0, 5);
789
- if (cleaned.length === 0)
790
- return [];
791
- const matchExpr = cleaned.join(' OR ');
792
- const rows = this.db.prepare(`
793
- SELECT e.*
794
- FROM events e
795
- INNER JOIN events_fts fts ON e.rowid = fts.rowid
796
- WHERE events_fts MATCH ?
797
- AND e.project_path = ?
798
- ORDER BY e.timestamp DESC
799
- LIMIT ?
800
- `).all(matchExpr, projectPath, limit);
801
- return rows.map(row => this.rowToEvent(row));
802
- }
803
- catch {
804
- // FTS5 表不存在或查询失败,静默返回空结果
805
- return [];
806
- }
560
+ updatePatternSatisfaction(executionId, satisfaction) {
561
+ this.repositories.distill.updatePatternSatisfaction(executionId, satisfaction);
807
562
  }
808
- close() {
809
- this.db.close();
563
+ updatePatternExecutionOutcome(execId, outcome, taskSessionId) {
564
+ this.repositories.distill.updatePatternExecutionOutcome(execId, outcome, taskSessionId);
810
565
  }
811
- // ── 任务复盘分析器查询方法 ──────────────────────────────────────────────────
812
- insertTaskSession(data) {
813
- this.db.prepare(`
814
- INSERT INTO task_sessions (
815
- id, session_id, project_path, pipeline_id, requirement, task_type, complexity,
816
- total_rounds, total_tool_calls, write_edit_count, fix_attempt_count,
817
- quality_fail_count, quality_pass_count, failure_signal_count,
818
- phase_distribution, outcome, duration_ms, started_at, ended_at
819
- ) VALUES (
820
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
821
- )
822
- `).run(data.id, data.session_id, data.project_path, data.pipeline_id ?? null, data.requirement, data.task_type, data.complexity, data.total_rounds, data.total_tool_calls, data.write_edit_count, data.fix_attempt_count, data.quality_fail_count, data.quality_pass_count, data.failure_signal_count, data.phase_distribution ? JSON.stringify(data.phase_distribution) : null, data.outcome, data.duration_ms ?? null, data.started_at, data.ended_at ?? null);
566
+ queryPatternExecutionsBySession(sessionId, projectPath) {
567
+ return this.repositories.distill.queryPatternExecutionsBySession(sessionId, projectPath);
823
568
  }
824
- updateTaskSession(id, updates) {
825
- const fields = Object.keys(updates).map(k => `${k} = ?`).join(', ');
826
- if (!fields)
827
- return;
828
- this.db.prepare(`UPDATE task_sessions SET ${fields} WHERE id = ?`)
829
- .run(...Object.values(updates), id);
569
+ getPatternSuccessRates() {
570
+ return this.repositories.distill.getPatternSuccessRates();
830
571
  }
831
- getTaskSession(id) {
832
- return this.db.prepare('SELECT * FROM task_sessions WHERE id = ?').get(id) ?? null;
572
+ insertEvolutionHistory(record) {
573
+ this.repositories.distill.insertEvolutionHistory(record);
833
574
  }
834
- getTaskSessionsByType(taskType, limit = 20) {
835
- return this.db.prepare('SELECT * FROM task_sessions WHERE task_type = ? ORDER BY started_at DESC LIMIT ?').all(taskType, limit);
575
+ updateEvolutionValidation(historyId, status, successRate) {
576
+ this.repositories.distill.updateEvolutionValidation(historyId, status, successRate);
836
577
  }
837
- insertFailureSignal(data) {
838
- this.db.prepare(`
839
- INSERT INTO failure_signals (
840
- id, session_id, project_path, task_session_id, signal_type, raw_prompt,
841
- matched_pattern, context_phase, is_iteration, preceding_tool_count, timestamp
842
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
843
- `).run(data.id, data.session_id, data.project_path, data.task_session_id ?? null, data.signal_type, data.raw_prompt, data.matched_pattern ?? null, data.context_phase ?? null, data.is_iteration, data.preceding_tool_count, data.timestamp);
578
+ getPendingEvolutions() {
579
+ return this.repositories.distill.getPendingEvolutions();
844
580
  }
845
- getFailureSignals(query) {
846
- const conditions = ['1=1'];
847
- const params = [];
848
- if (query.session_id) {
849
- conditions.push('session_id = ?');
850
- params.push(query.session_id);
851
- }
852
- if (query.project_path) {
853
- conditions.push('project_path = ?');
854
- params.push(query.project_path);
855
- }
856
- params.push(query.limit ?? 100);
857
- return this.db.prepare(`SELECT * FROM failure_signals WHERE ${conditions.join(' AND ')} ORDER BY timestamp ASC LIMIT ?`).all(...params);
581
+ getLatestEvolution(patternId) {
582
+ return this.repositories.distill.getLatestEvolution(patternId);
858
583
  }
859
- upsertTaskPattern(data) {
860
- this.db.prepare(`
861
- INSERT INTO task_patterns (id, task_type, failure_pattern, prevention_strategy, confidence_score, sample_count)
862
- VALUES (?, ?, ?, ?, ?, ?)
863
- ON CONFLICT(id) DO UPDATE SET
864
- failure_pattern = excluded.failure_pattern,
865
- prevention_strategy = excluded.prevention_strategy,
866
- confidence_score = excluded.confidence_score,
867
- sample_count = excluded.sample_count,
868
- last_updated = datetime('now')
869
- `).run(data.id, data.task_type, data.failure_pattern, data.prevention_strategy, data.confidence_score, data.sample_count);
584
+ // ── Tasks ─────────────────────────────────────────────────────────────────
585
+ insertTaskSession(data) {
586
+ this.repositories.tasks.insertTaskSession(data);
870
587
  }
871
- getTaskPatterns(taskType) {
872
- if (taskType) {
873
- return this.db.prepare('SELECT * FROM task_patterns WHERE task_type = ? ORDER BY confidence_score DESC').all(taskType);
874
- }
875
- return this.db.prepare('SELECT * FROM task_patterns ORDER BY confidence_score DESC').all();
588
+ updateTaskSession(id, updates) {
589
+ this.repositories.tasks.updateTaskSession(id, updates);
876
590
  }
877
- /**
878
- * 获取底层数据库连接(供需要直接操作的模块使用,如 PipelineStore)
879
- * 注意:调用方需自行管理事务和错误处理
880
- */
881
- getDatabase() {
882
- return this.db;
591
+ getTaskSession(id) {
592
+ return this.repositories.tasks.getTaskSession(id);
883
593
  }
884
- // 行数据转换为 ForgeEvent
885
- rowToEvent(row) {
886
- const safeJsonParse = (s) => {
887
- if (!s)
888
- return undefined;
889
- try {
890
- return JSON.parse(s);
891
- }
892
- catch {
893
- return undefined;
894
- }
895
- };
896
- return {
897
- event_id: row.event_id,
898
- session_id: row.session_id,
899
- project_path: row.project_path,
900
- timestamp: row.timestamp,
901
- hook_type: row.hook_type,
902
- tool_name: row.tool_name,
903
- tool_input: safeJsonParse(row.tool_input),
904
- tool_output: safeJsonParse(row.tool_output),
905
- user_prompt: row.user_prompt,
906
- ai_response: row.ai_response,
907
- distilled: row.distilled === 1,
908
- };
594
+ getTaskSessionsByType(taskType, limit = 20) {
595
+ return this.repositories.tasks.getTaskSessionsByType(taskType, limit);
909
596
  }
910
- // ── Schema v4 新增方法 ──────────────────────────────────────────────────────
911
- /** 写入一条 API 调用记录 */
912
- insertApiUsage(data) {
913
- try {
914
- this.db.prepare(`
915
- INSERT INTO api_usage (id, session_id, label, model, tokens_in, tokens_out, cost_usd, latency_ms, timestamp)
916
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
917
- `).run(data.id, data.session_id, data.label, data.model, data.tokens_in, data.tokens_out, data.cost_usd, data.latency_ms, data.timestamp);
918
- }
919
- catch {
920
- // api_usage 表可能尚未迁移(旧数据库),静默忽略
921
- }
597
+ getSessionMetrics(sessionId) {
598
+ return this.repositories.tasks.getSessionMetrics(sessionId);
922
599
  }
923
- /** 查询 API 使用汇总(按 label 分组) */
924
- getApiUsageSummary(sessionId, days) {
925
- const conditions = ['1=1'];
926
- const params = [];
927
- if (sessionId) {
928
- conditions.push('session_id = ?');
929
- params.push(sessionId);
930
- }
931
- if (days) {
932
- conditions.push(`timestamp >= datetime('now', '-${days} days')`);
933
- }
934
- try {
935
- return this.db.prepare(`
936
- SELECT label,
937
- COUNT(*) as calls,
938
- SUM(tokens_in) as tokens_in,
939
- SUM(tokens_out) as tokens_out,
940
- SUM(cost_usd) as cost_usd,
941
- AVG(latency_ms) as avg_latency_ms
942
- FROM api_usage
943
- WHERE ${conditions.join(' AND ')}
944
- GROUP BY label
945
- ORDER BY calls DESC
946
- `).all(...params);
947
- }
948
- catch {
949
- return [];
950
- }
600
+ getRecentSessions(projectPath, limit = 10) {
601
+ return this.repositories.tasks.getRecentSessions(projectPath, limit);
951
602
  }
952
- /** 更新 task_session 的满意度评分 */
953
603
  updateTaskSessionSatisfaction(id, score) {
954
- try {
955
- this.db.prepare(`UPDATE task_sessions SET satisfaction_score = ? WHERE id = ?`).run(score, id);
956
- }
957
- catch { /* 列可能尚未迁移 */ }
604
+ this.repositories.tasks.updateTaskSessionSatisfaction(id, score);
958
605
  }
959
- /** 获取满意度平均分(可按项目和天数过滤) */
960
606
  getAverageSatisfaction(projectPath, days) {
961
- const conditions = ['satisfaction_score IS NOT NULL'];
962
- const params = [];
963
- if (projectPath) {
964
- conditions.push('project_path = ?');
965
- params.push(projectPath);
966
- }
967
- if (days) {
968
- conditions.push(`started_at >= datetime('now', '-${days} days')`);
969
- }
970
- try {
971
- const row = this.db.prepare(`SELECT AVG(satisfaction_score) as avg FROM task_sessions WHERE ${conditions.join(' AND ')}`).get(...params);
972
- return row?.avg ?? 0;
973
- }
974
- catch {
975
- return 0;
976
- }
977
- }
978
- /** 获取总成本(可按天数过滤) */
979
- getTotalCost(days) {
980
- const conditions = ['1=1'];
981
- if (days) {
982
- conditions.push(`timestamp >= datetime('now', '-${days} days')`);
983
- }
984
- try {
985
- const row = this.db.prepare(`
986
- SELECT SUM(cost_usd) as total FROM api_usage WHERE ${conditions.join(' AND ')}
987
- `).get();
988
- return row?.total ?? 0;
989
- }
990
- catch {
991
- return 0;
992
- }
607
+ return this.repositories.tasks.getAverageSatisfaction(projectPath, days);
993
608
  }
994
- /** 获取按模型分组的成本(可按天数过滤) */
995
- getCostByModel(days) {
996
- const conditions = ['1=1'];
997
- if (days) {
998
- conditions.push(`timestamp >= datetime('now', '-${days} days')`);
999
- }
1000
- try {
1001
- return this.db.prepare(`
1002
- SELECT model, SUM(cost_usd) as cost
1003
- FROM api_usage
1004
- WHERE ${conditions.join(' AND ')}
1005
- GROUP BY model
1006
- ORDER BY cost DESC
1007
- `).all();
1008
- }
1009
- catch {
1010
- return [];
1011
- }
609
+ upsertTaskPattern(data) {
610
+ this.repositories.tasks.upsertTaskPattern(data);
1012
611
  }
1013
- /** 获取最近的会话列表(可按项目过滤) */
1014
- getRecentSessions(projectPath, limit = 10) {
1015
- const conditions = ['1=1'];
1016
- const params = [];
1017
- if (projectPath) {
1018
- conditions.push('project_path = ?');
1019
- params.push(projectPath);
1020
- }
1021
- try {
1022
- return this.db.prepare(`
1023
- SELECT id, session_id, requirement, outcome, satisfaction_score, started_at
1024
- FROM task_sessions
1025
- WHERE ${conditions.join(' AND ')}
1026
- ORDER BY started_at DESC
1027
- LIMIT ?
1028
- `).all(...params, limit);
1029
- }
1030
- catch {
1031
- return [];
1032
- }
612
+ getTaskPatterns(taskType) {
613
+ return this.repositories.tasks.getTaskPatterns(taskType);
1033
614
  }
1034
- /** 更新 task_pattern 的效果评分(指数移动平均) */
1035
615
  updatePatternEffectiveness(id, success) {
1036
- try {
1037
- const row = this.db.prepare('SELECT effectiveness_score FROM task_patterns WHERE id = ?').get(id);
1038
- if (!row)
1039
- return;
1040
- const newScore = row.effectiveness_score * 0.8 + (success ? 1.0 : 0.0) * 0.2;
1041
- this.db.prepare(`UPDATE task_patterns SET effectiveness_score = ?, last_used = datetime('now') WHERE id = ?`).run(newScore, id);
1042
- }
1043
- catch { /* 列可能尚未迁移 */ }
616
+ this.repositories.tasks.updatePatternEffectiveness(id, success);
1044
617
  }
1045
- /** 递增 task_pattern 的采用次数 */
1046
618
  incrementPatternAdoption(id) {
1047
- try {
1048
- this.db.prepare(`UPDATE task_patterns SET adoption_count = adoption_count + 1, last_used = datetime('now') WHERE id = ?`).run(id);
1049
- }
1050
- catch { /* 列可能尚未迁移 */ }
619
+ this.repositories.tasks.incrementPatternAdoption(id);
1051
620
  }
1052
- /** 获取效果最好的 patterns(按 effectiveness_score 降序)
1053
- * 如果 taskType 为空字符串,返回所有 patterns
1054
- */
1055
621
  getTopPatterns(taskTypeOrId, limit = 5) {
1056
- try {
1057
- if (taskTypeOrId === '') {
1058
- // 空字符串:返回所有 patterns
1059
- return this.db.prepare(`SELECT * FROM task_patterns ORDER BY effectiveness_score DESC, adoption_count DESC LIMIT ?`).all(limit);
1060
- }
1061
- else {
1062
- // 非空:先尝试按 id 查询,如果没有结果则按 task_type 查询
1063
- const byId = this.db.prepare(`SELECT * FROM task_patterns WHERE id = ? LIMIT ?`).all(taskTypeOrId, limit);
1064
- if (byId.length > 0) {
1065
- return byId;
1066
- }
1067
- // 降级:按 task_type 查询
1068
- return this.db.prepare(`SELECT * FROM task_patterns WHERE task_type = ? ORDER BY effectiveness_score DESC, adoption_count DESC LIMIT ?`).all(taskTypeOrId, limit);
1069
- }
1070
- }
1071
- catch {
1072
- return [];
1073
- }
1074
- }
1075
- /** 获取指定 session 的指标(用于主动干预检测) */
1076
- getSessionMetrics(sessionId) {
1077
- try {
1078
- return this.db.prepare('SELECT * FROM task_sessions WHERE session_id = ? ORDER BY started_at DESC LIMIT 1').get(sessionId) ?? null;
1079
- }
1080
- catch {
1081
- return null;
1082
- }
1083
- }
1084
- // 行数据转换为 Session
1085
- rowToSession(row) {
1086
- return {
1087
- session_id: row.session_id,
1088
- project_path: row.project_path,
1089
- status: row.status,
1090
- start_time: row.start_time,
1091
- end_time: row.end_time,
1092
- last_event_time: row.last_event_time,
1093
- event_count: row.event_count,
1094
- };
1095
- }
1096
- // ==================== v5 新增方法:学习闭环 ====================
1097
- /** 更新 pattern_execution 的 outcome 和 task_session_id(批量) */
1098
- updatePatternExecutionOutcome(execId, outcome, taskSessionId) {
1099
- try {
1100
- this.db.prepare(`UPDATE pattern_executions SET outcome = ?, task_session_id = ? WHERE id = ?`).run(outcome, taskSessionId, execId);
1101
- }
1102
- catch (err) {
1103
- logger.warn(`[SQLite] 更新 pattern_execution outcome 失败:${err}`);
1104
- }
1105
- }
1106
- /** 查询指定 session 的所有 pattern_executions */
1107
- queryPatternExecutionsBySession(sessionId, projectPath) {
1108
- try {
1109
- return this.db.prepare(`SELECT id, pattern_id FROM pattern_executions WHERE session_id = ? AND project_path = ?`).all(sessionId, projectPath);
1110
- }
1111
- catch {
1112
- return [];
1113
- }
622
+ return this.repositories.tasks.getTopPatterns(taskTypeOrId, limit);
1114
623
  }
1115
- /** 基于真实成功率更新 effectiveness_score(使用 EMA) */
1116
624
  updatePatternEffectivenessScore(patternId, successRate) {
1117
- try {
1118
- const row = this.db.prepare(`SELECT effectiveness_score FROM task_patterns WHERE id = ?`).get(patternId);
1119
- if (!row)
1120
- return;
1121
- // EMA: 新分数 = 旧分数 × 0.7 + 成功率 × 0.3(提高新数据权重)
1122
- const newScore = row.effectiveness_score * 0.7 + successRate * 0.3;
1123
- this.db.prepare(`UPDATE task_patterns SET effectiveness_score = ?, last_used = datetime('now') WHERE id = ?`).run(newScore, patternId);
1124
- }
1125
- catch (err) {
1126
- logger.warn(`[SQLite] 更新 effectiveness_score 失败:${err}`);
1127
- }
1128
- }
1129
- /** 查询所有有 outcome 的 pattern_executions,按 pattern_id 分组统计成功率 */
1130
- getPatternSuccessRates() {
1131
- try {
1132
- return this.db.prepare(`
1133
- SELECT
1134
- pattern_id,
1135
- SUM(CASE WHEN outcome = 'success' THEN 1 ELSE 0 END) * 1.0 / COUNT(*) as success_rate,
1136
- COUNT(*) as total_count
1137
- FROM pattern_executions
1138
- WHERE outcome IS NOT NULL
1139
- GROUP BY pattern_id
1140
- HAVING COUNT(*) >= 5
1141
- `).all();
1142
- }
1143
- catch {
1144
- return [];
1145
- }
625
+ this.repositories.tasks.updatePatternEffectivenessScore(patternId, successRate);
1146
626
  }
1147
- // ==================== v5 新增方法:质量门禁反馈 ====================
1148
- /** 递增抑制计数,3 次后设置过期时间 */
1149
627
  incrementSuppressionCount(category, projectPath) {
1150
- try {
1151
- const existing = this.db.prepare(`SELECT id, suppression_count FROM quality_gate_suppressions WHERE category = ? AND project_path = ?`).get(category, projectPath);
1152
- if (existing) {
1153
- const newCount = existing.suppression_count + 1;
1154
- const expiresAt = newCount >= 3 ? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() : null;
1155
- this.db.prepare(`UPDATE quality_gate_suppressions SET suppression_count = ?, last_suppressed_at = datetime('now'), expires_at = ? WHERE id = ?`).run(newCount, expiresAt, existing.id);
1156
- }
1157
- else {
1158
- const id = `qgs_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
1159
- this.db.prepare(`INSERT INTO quality_gate_suppressions (id, category, project_path, first_suppressed_at, last_suppressed_at)
1160
- VALUES (?, ?, ?, datetime('now'), datetime('now'))`).run(id, category, projectPath);
1161
- }
1162
- }
1163
- catch (err) {
1164
- logger.warn(`[SQLite] 递增抑制计数失败:${err}`);
1165
- }
628
+ this.repositories.tasks.incrementSuppressionCount(category, projectPath);
1166
629
  }
1167
- /** 获取未过期的抑制规则 */
1168
630
  getActiveSuppressions(projectPath) {
1169
- try {
1170
- const rows = this.db.prepare(`SELECT category FROM quality_gate_suppressions
1171
- WHERE project_path = ? AND suppression_count >= 3
1172
- AND (expires_at IS NULL OR expires_at > datetime('now'))`).all(projectPath);
1173
- return rows.map(r => r.category);
1174
- }
1175
- catch {
1176
- return [];
1177
- }
631
+ return this.repositories.tasks.getActiveSuppressions(projectPath);
1178
632
  }
1179
- /** 插入质量门禁历史记录 */
1180
633
  insertQualityGateHistory(record) {
1181
- try {
1182
- this.db.prepare(`INSERT INTO quality_gate_history (id, session_id, project_path, file_path, category, level, message)
1183
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.session_id, record.project_path, record.file_path, record.category, record.level, record.message);
1184
- }
1185
- catch (err) {
1186
- logger.warn(`[SQLite] 插入质量门禁历史失败:${err}`);
1187
- }
634
+ this.repositories.tasks.insertQualityGateHistory(record);
1188
635
  }
1189
- /** 标记历史问题为已解决 */
1190
636
  markQualityIssueResolved(filePath, category) {
1191
- try {
1192
- this.db.prepare(`UPDATE quality_gate_history SET resolved = 1, resolved_at = datetime('now')
1193
- WHERE file_path = ? AND category = ? AND resolved = 0`).run(filePath, category);
1194
- }
1195
- catch (err) {
1196
- logger.warn(`[SQLite] 标记问题已解决失败:${err}`);
1197
- }
637
+ this.repositories.tasks.markQualityIssueResolved(filePath, category);
1198
638
  }
1199
- /** 查询文件的未解决问题 */
1200
639
  getUnresolvedIssues(filePath) {
1201
- try {
1202
- return this.db.prepare(`SELECT category, message FROM quality_gate_history WHERE file_path = ? AND resolved = 0 ORDER BY created_at DESC LIMIT 10`).all(filePath);
1203
- }
1204
- catch {
1205
- return [];
1206
- }
640
+ return this.repositories.tasks.getUnresolvedIssues(filePath);
1207
641
  }
1208
- /** 加载会话的质量门禁历史(用于恢复 reviewHistory) */
1209
642
  getSessionQualityHistory(sessionId, projectPath) {
1210
- try {
1211
- return this.db.prepare(`SELECT file_path, category, level, message, resolved
1212
- FROM quality_gate_history
1213
- WHERE session_id = ? AND project_path = ?
1214
- ORDER BY created_at ASC
1215
- LIMIT 50`).all(sessionId, projectPath);
1216
- }
1217
- catch {
1218
- return [];
1219
- }
643
+ return this.repositories.tasks.getSessionQualityHistory(sessionId, projectPath);
1220
644
  }
1221
- /** 清理过期的抑制规则 */
1222
645
  cleanupExpiredSuppressions() {
1223
- try {
1224
- const result = this.db.prepare(`DELETE FROM quality_gate_suppressions WHERE expires_at IS NOT NULL AND expires_at < datetime('now')`).run();
1225
- return result.changes;
1226
- }
1227
- catch {
1228
- return 0;
1229
- }
646
+ return this.repositories.tasks.cleanupExpiredSuppressions();
1230
647
  }
1231
- // ==================== v5 新增方法:Pattern 进化验证 ====================
1232
- /** 插入 Pattern 进化历史 */
1233
- insertEvolutionHistory(record) {
1234
- try {
1235
- this.db.prepare(`INSERT INTO pattern_evolution_history (id, pattern_id, old_version, new_version, old_yaml, new_yaml, evolved_at)
1236
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.pattern_id, record.old_version, record.new_version, record.old_yaml, record.new_yaml, record.evolved_at);
1237
- }
1238
- catch (err) {
1239
- logger.warn(`[SQLite] 插入进化历史失败:${err}`);
1240
- }
648
+ // ── Failures ──────────────────────────────────────────────────────────────
649
+ insertFailureSignal(data) {
650
+ this.repositories.failures.insertFailureSignal(data);
1241
651
  }
1242
- /** 更新进化验证状态 */
1243
- updateEvolutionValidation(historyId, status, successRate) {
1244
- try {
1245
- this.db.prepare(`UPDATE pattern_evolution_history SET validation_status = ?, test_success_rate = ?, validated_at = datetime('now') WHERE id = ?`).run(status, successRate, historyId);
1246
- }
1247
- catch (err) {
1248
- logger.warn(`[SQLite] 更新进化验证状态失败:${err}`);
1249
- }
652
+ getFailureSignals(query) {
653
+ return this.repositories.failures.getFailureSignals(query);
1250
654
  }
1251
- /** 获取待验证的进化记录 */
1252
- getPendingEvolutions() {
1253
- try {
1254
- return this.db.prepare(`SELECT id, pattern_id, new_version FROM pattern_evolution_history WHERE validation_status = 'pending' ORDER BY evolved_at DESC`).all();
1255
- }
1256
- catch {
1257
- return [];
1258
- }
655
+ // ── API Usage ─────────────────────────────────────────────────────────────
656
+ insertApiUsage(data) {
657
+ this.repositories.apiUsage.insertApiUsage(data);
1259
658
  }
1260
- /** 获取 Pattern 的最新进化记录 */
1261
- getLatestEvolution(patternId) {
1262
- try {
1263
- return this.db.prepare(`SELECT old_yaml, validation_status FROM pattern_evolution_history WHERE pattern_id = ? ORDER BY evolved_at DESC LIMIT 1`).get(patternId) ?? null;
1264
- }
1265
- catch {
1266
- return null;
1267
- }
659
+ getApiUsageSummary(sessionId, days) {
660
+ return this.repositories.apiUsage.getApiUsageSummary(sessionId, days);
661
+ }
662
+ getTotalCost(days) {
663
+ return this.repositories.apiUsage.getTotalCost(days);
1268
664
  }
1269
- // ── v8 新增方法 ──────────────────────────────────────────────────────────────
1270
- /** 写入延迟追踪记录 */
665
+ getCostByModel(days) {
666
+ return this.repositories.apiUsage.getCostByModel(days);
667
+ }
668
+ // ── Latency ───────────────────────────────────────────────────────────────
1271
669
  insertLatencyTrace(record) {
1272
- try {
1273
- this.db.prepare(`
1274
- INSERT OR IGNORE INTO latency_traces (id, session_id, project_path, trace_type, engine_name, duration_ms, metadata)
1275
- VALUES (?, ?, ?, ?, ?, ?, ?)
1276
- `).run(record.id, record.session_id, record.project_path, record.trace_type, record.engine_name, record.duration_ms, record.metadata ? JSON.stringify(record.metadata) : null);
1277
- }
1278
- catch { /* 延迟追踪失败不影响主流程 */ }
670
+ this.repositories.latency.insertLatencyTrace(record);
1279
671
  }
1280
- /** 按引擎统计延迟(P50/P95/max),默认最近 7 天 */
1281
672
  getLatencyStats(traceType, days = 7) {
1282
- try {
1283
- const since = new Date(Date.now() - days * 86400_000).toISOString();
1284
- const where = traceType ? `AND trace_type = '${traceType}'` : '';
1285
- const rows = this.db.prepare(`
1286
- SELECT engine_name, trace_type,
1287
- duration_ms, COUNT(*) OVER (PARTITION BY engine_name, trace_type) as cnt
1288
- FROM latency_traces
1289
- WHERE timestamp >= ? ${where}
1290
- ORDER BY engine_name, trace_type, duration_ms
1291
- `).all(since);
1292
- const groups = new Map();
1293
- const meta = new Map();
1294
- for (const r of rows) {
1295
- const key = `${r.engine_name}::${r.trace_type}`;
1296
- if (!groups.has(key)) {
1297
- groups.set(key, []);
1298
- meta.set(key, { engine_name: r.engine_name, trace_type: r.trace_type });
1299
- }
1300
- groups.get(key).push(r.duration_ms);
1301
- }
1302
- return [...groups.entries()].map(([key, vals]) => {
1303
- vals.sort((a, b) => a - b);
1304
- const p = (pct) => vals[Math.floor(vals.length * pct)] ?? 0;
1305
- return { ...meta.get(key), p50: p(0.5), p95: p(0.95), max: vals[vals.length - 1] ?? 0, count: vals.length };
1306
- });
1307
- }
1308
- catch {
1309
- return [];
1310
- }
673
+ return this.repositories.latency.getLatencyStats(traceType, days);
1311
674
  }
1312
- /** 写入知识图谱节点 */
675
+ // ── Knowledge Graph ───────────────────────────────────────────────────────
1313
676
  insertKnowledgeNode(node) {
1314
- this.db.prepare(`
1315
- INSERT OR REPLACE INTO knowledge_nodes (id, project_path, node_type, title, content, tags, embedding_hint, last_accessed_at)
1316
- VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
1317
- `).run(node.id, node.project_path, node.node_type, node.title, node.content, node.tags ? JSON.stringify(node.tags) : null, node.embedding_hint ?? null);
677
+ this.repositories.knowledge.insertKnowledgeNode(node);
1318
678
  }
1319
- /** 写入知识图谱边 */
1320
679
  insertKnowledgeEdge(edge) {
1321
- this.db.prepare(`
1322
- INSERT OR IGNORE INTO knowledge_edges (id, from_node_id, to_node_id, edge_type, weight, metadata)
1323
- VALUES (?, ?, ?, ?, ?, ?)
1324
- `).run(edge.id, edge.from_node_id, edge.to_node_id, edge.edge_type, edge.weight ?? 1.0, edge.metadata ? JSON.stringify(edge.metadata) : null);
680
+ this.repositories.knowledge.insertKnowledgeEdge(edge);
1325
681
  }
1326
- /** FTS 检索知识图谱节点 */
1327
682
  searchKnowledgeNodes(query, projectPath, limit = 10) {
1328
- try {
1329
- const terms = query.trim().split(/\s+/).filter(Boolean).map(t => `"${t}"`).join(' OR ');
1330
- if (!terms)
1331
- return [];
1332
- return this.db.prepare(`
1333
- SELECT kn.id, kn.node_type, kn.title, kn.content, kn.tags
1334
- FROM knowledge_nodes_fts fts
1335
- JOIN knowledge_nodes kn ON kn.id = fts.id
1336
- WHERE fts.knowledge_nodes_fts MATCH ? AND kn.project_path = ? AND kn.archived = 0
1337
- ORDER BY rank LIMIT ?
1338
- `).all(terms, projectPath, limit);
1339
- }
1340
- catch {
1341
- return [];
1342
- }
683
+ return this.repositories.knowledge.searchKnowledgeNodes(query, projectPath, limit);
1343
684
  }
1344
- /** 按精确标题查找知识图谱节点(用于去重) */
1345
685
  findKnowledgeNodeByTitle(title, projectPath, nodeType) {
1346
- try {
1347
- const sql = nodeType
1348
- ? `SELECT id FROM knowledge_nodes WHERE title = ? AND project_path = ? AND node_type = ? AND archived = 0 LIMIT 1`
1349
- : `SELECT id FROM knowledge_nodes WHERE title = ? AND project_path = ? AND archived = 0 LIMIT 1`;
1350
- const params = nodeType ? [title, projectPath, nodeType] : [title, projectPath];
1351
- return this.db.prepare(sql).get(...params);
1352
- }
1353
- catch {
1354
- return null;
1355
- }
686
+ return this.repositories.knowledge.findKnowledgeNodeByTitle(title, projectPath, nodeType);
1356
687
  }
1357
- /** 查询知识图谱边(支持方向过滤) */
1358
688
  getKnowledgeEdges(nodeId, direction = 'both') {
1359
- try {
1360
- let sql;
1361
- let params;
1362
- if (direction === 'out') {
1363
- sql = `SELECT id, from_node_id, to_node_id, edge_type, weight FROM knowledge_edges WHERE from_node_id = ?`;
1364
- params = [nodeId];
1365
- }
1366
- else if (direction === 'in') {
1367
- sql = `SELECT id, from_node_id, to_node_id, edge_type, weight FROM knowledge_edges WHERE to_node_id = ?`;
1368
- params = [nodeId];
1369
- }
1370
- else {
1371
- sql = `SELECT id, from_node_id, to_node_id, edge_type, weight FROM knowledge_edges WHERE from_node_id = ? OR to_node_id = ?`;
1372
- params = [nodeId, nodeId];
1373
- }
1374
- return this.db.prepare(sql).all(...params);
1375
- }
1376
- catch {
1377
- return [];
1378
- }
689
+ return this.repositories.knowledge.getKnowledgeEdges(nodeId, direction);
1379
690
  }
1380
- /** 查询节点的邻居节点(JOIN edges + nodes,过滤 archived) */
1381
691
  getNeighborNodes(nodeId, projectPath, limit = 10) {
1382
- try {
1383
- return this.db.prepare(`
1384
- SELECT kn.id, kn.node_type, kn.title, kn.content, ke.edge_type, ke.weight
1385
- FROM knowledge_edges ke
1386
- JOIN knowledge_nodes kn ON (
1387
- CASE WHEN ke.from_node_id = ? THEN ke.to_node_id ELSE ke.from_node_id END = kn.id
1388
- )
1389
- WHERE (ke.from_node_id = ? OR ke.to_node_id = ?)
1390
- AND kn.project_path = ? AND kn.archived = 0
1391
- ORDER BY ke.weight DESC
1392
- LIMIT ?
1393
- `).all(nodeId, nodeId, nodeId, projectPath, limit);
1394
- }
1395
- catch {
1396
- return [];
1397
- }
692
+ return this.repositories.knowledge.getNeighborNodes(nodeId, projectPath, limit);
1398
693
  }
694
+ // ── Satisfaction ──────────────────────────────────────────────────────────
1399
695
  insertSatisfactionSignal(signal) {
1400
- try {
1401
- this.db.prepare(`
1402
- INSERT OR IGNORE INTO satisfaction_signals
1403
- (id, session_id, project_path, task_session_id, signal_type, signal_value, weight, raw_data)
1404
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1405
- `).run(signal.id, signal.session_id, signal.project_path, signal.task_session_id ?? null, signal.signal_type, signal.signal_value, signal.weight ?? 1.0, signal.raw_data ? JSON.stringify(signal.raw_data) : null);
1406
- }
1407
- catch { /* 信号写入失败不影响主流程 */ }
696
+ this.repositories.satisfaction.insertSatisfactionSignal(signal);
1408
697
  }
1409
- /** 查询项目近期满意度历史(含 task_sessions.satisfaction_score) */
1410
698
  getSatisfactionHistory(projectPath, limit = 10) {
1411
- try {
1412
- return this.db.prepare(`
1413
- SELECT session_id, satisfaction_score as score, task_type, complexity, ended_at
1414
- FROM task_sessions
1415
- WHERE project_path = ? AND satisfaction_score IS NOT NULL
1416
- ORDER BY ended_at DESC LIMIT ?
1417
- `).all(projectPath, limit);
1418
- }
1419
- catch {
1420
- return [];
1421
- }
1422
- }
1423
- /** 获取项目最近一次已完成的 task_session(用于负向重启回溯修正) */
1424
- getLastCompletedTaskSession(projectPath) {
1425
- try {
1426
- return this.db.prepare(`
1427
- SELECT id, session_id, satisfaction_score, ended_at
1428
- FROM task_sessions
1429
- WHERE project_path = ? AND outcome = 'success'
1430
- ORDER BY ended_at DESC LIMIT 1
1431
- `).get(projectPath) ?? null;
1432
- }
1433
- catch {
1434
- return null;
1435
- }
699
+ return this.repositories.satisfaction.getSatisfactionHistory(projectPath, limit);
1436
700
  }
1437
- /** 写入或更新意图规则 */
701
+ // ── Intent Rules ──────────────────────────────────────────────────────────
1438
702
  upsertIntentRule(rule) {
1439
- this.db.prepare(`
1440
- INSERT INTO intent_rules (id, rule_type, pattern, confidence, source)
1441
- VALUES (?, ?, ?, ?, ?)
1442
- ON CONFLICT(id) DO UPDATE SET
1443
- pattern = excluded.pattern,
1444
- confidence = excluded.confidence,
1445
- updated_at = datetime('now')
1446
- `).run(rule.id, rule.rule_type, rule.pattern, rule.confidence ?? 0.8, rule.source ?? 'distilled');
1447
- }
1448
- /** 查询意图规则库(按置信度降序) */
703
+ this.repositories.intentRules.upsertIntentRule(rule);
704
+ }
1449
705
  getIntentRules(ruleType) {
1450
- try {
1451
- const where = ruleType ? `WHERE rule_type = ? AND confidence >= 0.5` : `WHERE confidence >= 0.5`;
1452
- const args = ruleType ? [ruleType] : [];
1453
- return this.db.prepare(`
1454
- SELECT id, rule_type, pattern, confidence, hit_count, miss_count
1455
- FROM intent_rules ${where} ORDER BY confidence DESC
1456
- `).all(...args);
1457
- }
1458
- catch {
1459
- return [];
1460
- }
706
+ return this.repositories.intentRules.getIntentRules(ruleType);
1461
707
  }
1462
- /** 更新意图规则命中/未命中计数 */
1463
708
  recordIntentRuleHit(ruleId, hit) {
1464
- try {
1465
- if (hit) {
1466
- this.db.prepare(`UPDATE intent_rules SET hit_count = hit_count + 1, last_hit_at = datetime('now') WHERE id = ?`).run(ruleId);
1467
- }
1468
- else {
1469
- this.db.prepare(`UPDATE intent_rules SET miss_count = miss_count + 1 WHERE id = ?`).run(ruleId);
1470
- }
1471
- }
1472
- catch { /* 忽略 */ }
709
+ this.repositories.intentRules.recordIntentRuleHit(ruleId, hit);
1473
710
  }
1474
- /** 获取 Daemon 状态(用于持久化 stopCount 等) */
1475
- getDaemonState(key) {
1476
- try {
1477
- const row = this.db.prepare(`SELECT value FROM daemon_state WHERE key = ?`).get(key);
1478
- return row?.value ?? null;
1479
- }
1480
- catch {
1481
- return null;
1482
- }
711
+ // ── Direct DB Access ──────────────────────────────────────────────────────
712
+ /**
713
+ * 获取底层数据库连接(供需要直接操作的模块使用,如 PipelineStore)
714
+ * 注意:调用方需自行管理事务和错误处理
715
+ */
716
+ getDatabase() {
717
+ return this.db;
1483
718
  }
1484
- /** 设置 Daemon 状态 */
1485
- setDaemonState(key, value) {
1486
- try {
1487
- this.db.prepare(`
1488
- INSERT INTO daemon_state (key, value, updated_at)
1489
- VALUES (?, ?, datetime('now'))
1490
- ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = datetime('now')
1491
- `).run(key, value);
1492
- }
1493
- catch { /* 忽略 */ }
719
+ close() {
720
+ this.db.close();
1494
721
  }
1495
722
  }
1496
723
  //# sourceMappingURL=sqlite.js.map