@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.
- package/dist/ai-gateway/model-selector.d.ts.map +1 -1
- package/dist/ai-gateway/model-selector.js +42 -6
- package/dist/ai-gateway/model-selector.js.map +1 -1
- package/dist/autopilot/intent-engine.d.ts +0 -4
- package/dist/autopilot/intent-engine.d.ts.map +1 -1
- package/dist/autopilot/intent-engine.js +32 -102
- package/dist/autopilot/intent-engine.js.map +1 -1
- package/dist/autopilot/issue-tracker.d.ts +48 -0
- package/dist/autopilot/issue-tracker.d.ts.map +1 -0
- package/dist/autopilot/issue-tracker.js +94 -0
- package/dist/autopilot/issue-tracker.js.map +1 -0
- package/dist/autopilot/phase-gap-detector.d.ts +25 -0
- package/dist/autopilot/phase-gap-detector.d.ts.map +1 -0
- package/dist/autopilot/phase-gap-detector.js +80 -0
- package/dist/autopilot/phase-gap-detector.js.map +1 -0
- package/dist/autopilot/quality-gate.d.ts +22 -0
- package/dist/autopilot/quality-gate.d.ts.map +1 -1
- package/dist/autopilot/quality-gate.js +108 -25
- package/dist/autopilot/quality-gate.js.map +1 -1
- package/dist/cli/commands/pattern.d.ts.map +1 -1
- package/dist/cli/commands/pattern.js +44 -38
- package/dist/cli/commands/pattern.js.map +1 -1
- package/dist/daemon/context-injector.d.ts +11 -1
- package/dist/daemon/context-injector.d.ts.map +1 -1
- package/dist/daemon/context-injector.js +27 -4
- package/dist/daemon/context-injector.js.map +1 -1
- package/dist/daemon/engine-registry.d.ts.map +1 -1
- package/dist/daemon/engine-registry.js +4 -1
- package/dist/daemon/engine-registry.js.map +1 -1
- package/dist/daemon/handler-context.d.ts +4 -2
- package/dist/daemon/handler-context.d.ts.map +1 -1
- package/dist/daemon/handlers/orchestration-context.d.ts +2 -0
- package/dist/daemon/handlers/orchestration-context.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use-handler.js +26 -4
- package/dist/daemon/handlers/post-tool-use-handler.js.map +1 -1
- package/dist/daemon/handlers/stages/09-pipeline-active.d.ts +1 -1
- package/dist/daemon/handlers/stages/09-pipeline-active.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/09-pipeline-active.js +22 -6
- package/dist/daemon/handlers/stages/09-pipeline-active.js.map +1 -1
- package/dist/daemon/handlers/stages/12-strategy-advice.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/12-strategy-advice.js +1 -4
- package/dist/daemon/handlers/stages/12-strategy-advice.js.map +1 -1
- package/dist/daemon/handlers/stages/17-simple-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/17-simple-task.js +1 -1
- package/dist/daemon/handlers/stages/17-simple-task.js.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.js +2 -2
- package/dist/daemon/handlers/stages/18-complex-task.js.map +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.js +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.js.map +1 -1
- package/dist/daemon/handlers/stop-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/stop-handler.js +5 -0
- package/dist/daemon/handlers/stop-handler.js.map +1 -1
- package/dist/daemon/handlers/user-prompt-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt-handler.js +95 -48
- package/dist/daemon/handlers/user-prompt-handler.js.map +1 -1
- package/dist/distill/writer.d.ts.map +1 -1
- package/dist/distill/writer.js +27 -11
- package/dist/distill/writer.js.map +1 -1
- package/dist/knowledge/graph.d.ts +13 -0
- package/dist/knowledge/graph.d.ts.map +1 -1
- package/dist/knowledge/graph.js +62 -0
- package/dist/knowledge/graph.js.map +1 -1
- package/dist/pipeline/index.d.ts +9 -0
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +49 -3
- package/dist/pipeline/index.js.map +1 -1
- package/dist/{pattern-engine/types.d.ts → pipeline/pattern-types.d.ts} +1 -1
- package/dist/pipeline/pattern-types.d.ts.map +1 -0
- package/dist/{pattern-engine/types.js → pipeline/pattern-types.js} +1 -1
- package/dist/pipeline/pattern-types.js.map +1 -0
- package/dist/pipeline/phase-manager.d.ts +5 -0
- package/dist/pipeline/phase-manager.d.ts.map +1 -1
- package/dist/pipeline/phase-manager.js +67 -25
- package/dist/pipeline/phase-manager.js.map +1 -1
- package/dist/pipeline/report-generator.d.ts +20 -0
- package/dist/pipeline/report-generator.d.ts.map +1 -0
- package/dist/pipeline/report-generator.js +123 -0
- package/dist/pipeline/report-generator.js.map +1 -0
- package/dist/prompts/intent-analysis.md +62 -0
- package/dist/prompts/registry.d.ts +17 -0
- package/dist/prompts/registry.d.ts.map +1 -0
- package/dist/prompts/registry.js +40 -0
- package/dist/prompts/registry.js.map +1 -0
- package/dist/skill-registry/evolver/index.d.ts +1 -1
- package/dist/skill-registry/evolver/index.d.ts.map +1 -1
- package/dist/{pattern-engine → skill-registry}/pattern-evolver.d.ts +6 -2
- package/dist/skill-registry/pattern-evolver.d.ts.map +1 -0
- package/dist/{pattern-engine → skill-registry}/pattern-evolver.js +30 -6
- package/dist/skill-registry/pattern-evolver.js.map +1 -0
- package/dist/storage/repositories/distill-repository.d.ts +2 -1
- package/dist/storage/repositories/distill-repository.d.ts.map +1 -1
- package/dist/storage/repositories/distill-repository.js +6 -0
- package/dist/storage/repositories/distill-repository.js.map +1 -1
- package/dist/storage/sqlite.d.ts +96 -157
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +123 -896
- package/dist/storage/sqlite.js.map +1 -1
- package/package.json +2 -2
- package/dist/daemon/engine-registry/init-pattern-engine.d.ts +0 -9
- package/dist/daemon/engine-registry/init-pattern-engine.d.ts.map +0 -1
- package/dist/daemon/engine-registry/init-pattern-engine.js +0 -22
- package/dist/daemon/engine-registry/init-pattern-engine.js.map +0 -1
- package/dist/pattern-engine/convention-pattern-generator.d.ts +0 -14
- package/dist/pattern-engine/convention-pattern-generator.d.ts.map +0 -1
- package/dist/pattern-engine/convention-pattern-generator.js +0 -120
- package/dist/pattern-engine/convention-pattern-generator.js.map +0 -1
- package/dist/pattern-engine/execution-tracker.d.ts +0 -45
- package/dist/pattern-engine/execution-tracker.d.ts.map +0 -1
- package/dist/pattern-engine/execution-tracker.js +0 -136
- package/dist/pattern-engine/execution-tracker.js.map +0 -1
- package/dist/pattern-engine/index.d.ts +0 -75
- package/dist/pattern-engine/index.d.ts.map +0 -1
- package/dist/pattern-engine/index.js +0 -247
- package/dist/pattern-engine/index.js.map +0 -1
- package/dist/pattern-engine/pattern-evolver.d.ts.map +0 -1
- package/dist/pattern-engine/pattern-evolver.js.map +0 -1
- package/dist/pattern-engine/pattern-executor.d.ts +0 -43
- package/dist/pattern-engine/pattern-executor.d.ts.map +0 -1
- package/dist/pattern-engine/pattern-executor.js +0 -228
- package/dist/pattern-engine/pattern-executor.js.map +0 -1
- package/dist/pattern-engine/pattern-loader.d.ts +0 -14
- package/dist/pattern-engine/pattern-loader.d.ts.map +0 -1
- package/dist/pattern-engine/pattern-loader.js +0 -111
- package/dist/pattern-engine/pattern-loader.js.map +0 -1
- package/dist/pattern-engine/pattern-router.d.ts +0 -31
- package/dist/pattern-engine/pattern-router.d.ts.map +0 -1
- package/dist/pattern-engine/pattern-router.js +0 -160
- package/dist/pattern-engine/pattern-router.js.map +0 -1
- package/dist/pattern-engine/types.d.ts.map +0 -1
- package/dist/pattern-engine/types.js.map +0 -1
package/dist/storage/sqlite.js
CHANGED
|
@@ -496,1001 +496,228 @@ export class SQLiteStorage {
|
|
|
496
496
|
`);
|
|
497
497
|
}
|
|
498
498
|
}
|
|
499
|
+
// ── Events ────────────────────────────────────────────────────────────────
|
|
499
500
|
writeEvent(event) {
|
|
500
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
676
|
-
|
|
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
|
-
|
|
687
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
553
|
+
// ── Pattern Executions ────────────────────────────────────────────────────
|
|
739
554
|
writePatternExecution(record) {
|
|
740
|
-
this.
|
|
741
|
-
|
|
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
|
-
|
|
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
|
-
|
|
779
|
-
|
|
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
|
-
|
|
809
|
-
this.
|
|
563
|
+
updatePatternExecutionOutcome(execId, outcome, taskSessionId) {
|
|
564
|
+
this.repositories.distill.updatePatternExecutionOutcome(execId, outcome, taskSessionId);
|
|
810
565
|
}
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
825
|
-
|
|
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
|
-
|
|
832
|
-
|
|
572
|
+
insertEvolutionHistory(record) {
|
|
573
|
+
this.repositories.distill.insertEvolutionHistory(record);
|
|
833
574
|
}
|
|
834
|
-
|
|
835
|
-
|
|
575
|
+
updateEvolutionValidation(historyId, status, successRate) {
|
|
576
|
+
this.repositories.distill.updateEvolutionValidation(historyId, status, successRate);
|
|
836
577
|
}
|
|
837
|
-
|
|
838
|
-
this.
|
|
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
|
-
|
|
846
|
-
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
872
|
-
|
|
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
|
-
|
|
879
|
-
* 注意:调用方需自行管理事务和错误处理
|
|
880
|
-
*/
|
|
881
|
-
getDatabase() {
|
|
882
|
-
return this.db;
|
|
591
|
+
getTaskSession(id) {
|
|
592
|
+
return this.repositories.tasks.getTaskSession(id);
|
|
883
593
|
}
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
911
|
-
|
|
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
|
-
|
|
924
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1253
|
-
|
|
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
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
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
|
-
|
|
1270
|
-
|
|
665
|
+
getCostByModel(days) {
|
|
666
|
+
return this.repositories.apiUsage.getCostByModel(days);
|
|
667
|
+
}
|
|
668
|
+
// ── Latency ───────────────────────────────────────────────────────────────
|
|
1271
669
|
insertLatencyTrace(record) {
|
|
1272
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
1440
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
return null;
|
|
1482
|
-
}
|
|
711
|
+
// ── Direct DB Access ──────────────────────────────────────────────────────
|
|
712
|
+
/**
|
|
713
|
+
* 获取底层数据库连接(供需要直接操作的模块使用,如 PipelineStore)
|
|
714
|
+
* 注意:调用方需自行管理事务和错误处理
|
|
715
|
+
*/
|
|
716
|
+
getDatabase() {
|
|
717
|
+
return this.db;
|
|
1483
718
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
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
|