autosnippet 2.1.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +189 -113
  2. package/bin/api-server.js +1 -4
  3. package/bin/cli.js +1 -50
  4. package/config/constitution.yaml +33 -107
  5. package/dashboard/dist/assets/{index-DbkbX1c-.js → index-B9py3ybr.js} +32 -32
  6. package/dashboard/dist/index.html +1 -1
  7. package/lib/bootstrap.js +5 -31
  8. package/lib/cli/SetupService.js +16 -14
  9. package/lib/core/capability/CapabilityProbe.js +8 -6
  10. package/lib/core/constitution/Constitution.js +13 -4
  11. package/lib/core/constitution/ConstitutionValidator.js +106 -211
  12. package/lib/core/gateway/Gateway.js +34 -98
  13. package/lib/core/gateway/GatewayActionRegistry.js +12 -1
  14. package/lib/core/permission/PermissionManager.js +2 -2
  15. package/lib/external/mcp/McpServer.js +4 -7
  16. package/lib/external/mcp/handlers/bootstrap.js +13 -1
  17. package/lib/external/mcp/handlers/browse.js +0 -7
  18. package/lib/external/mcp/handlers/candidate.js +1 -1
  19. package/lib/external/mcp/handlers/guard.js +11 -0
  20. package/lib/external/mcp/handlers/skill.js +186 -18
  21. package/lib/external/mcp/tools.js +40 -1
  22. package/lib/http/middleware/roleResolver.js +1 -1
  23. package/lib/http/routes/auth.js +2 -2
  24. package/lib/http/routes/monitoring.js +4 -4
  25. package/lib/http/routes/search.js +0 -17
  26. package/lib/injection/ServiceContainer.js +21 -40
  27. package/lib/service/candidate/CandidateService.js +12 -1
  28. package/lib/service/chat/ChatAgent.js +139 -18
  29. package/lib/service/chat/Memory.js +104 -0
  30. package/lib/service/chat/tools.js +244 -10
  31. package/lib/service/guard/GuardCheckEngine.js +9 -1
  32. package/lib/service/recipe/RecipeService.js +8 -0
  33. package/lib/service/skills/SkillHooks.js +126 -0
  34. package/package.json +1 -1
  35. package/scripts/init-db.js +1 -2
  36. package/templates/constitution.yaml +29 -85
  37. package/lib/core/session/SessionManager.js +0 -232
  38. package/lib/infrastructure/logging/ReasoningLogger.js +0 -269
  39. package/lib/infrastructure/monitoring/RoleDriftMonitor.js +0 -259
  40. package/lib/infrastructure/quality/ComplianceEvaluator.js +0 -326
@@ -1,232 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
- import Logger from '../../infrastructure/logging/Logger.js';
3
-
4
- /**
5
- * SessionManager - 会话管理器
6
- * 管理不同作用域的会话(project, target, file, developer)
7
- */
8
- export class SessionManager {
9
- static MAX_CACHED_SESSIONS = 500;
10
-
11
- constructor(db) {
12
- this.db = db.getDb();
13
- this.logger = Logger.getInstance();
14
- this.activeSessions = new Map();
15
- }
16
-
17
- /**
18
- * 创建会话
19
- */
20
- create(options) {
21
- const sessionId = uuidv4();
22
- const {
23
- scope,
24
- scopeId,
25
- context = {},
26
- metadata = {},
27
- actor = 'unknown',
28
- } = options;
29
-
30
- const now = Math.floor(Date.now() / 1000); // UNIX timestamp
31
- const expiresAt = now + this._getExpirationSeconds(scope);
32
-
33
- const session = {
34
- id: sessionId,
35
- scope, // 'project' | 'target' | 'file' | 'developer'
36
- scope_id: scopeId,
37
- context: JSON.stringify(context),
38
- metadata: JSON.stringify(metadata),
39
- actor,
40
- created_at: now,
41
- last_active_at: now,
42
- expired_at: expiresAt,
43
- };
44
-
45
- // 保存到数据库
46
- const stmt = this.db.prepare(`
47
- INSERT INTO sessions (id, scope, scope_id, context, metadata, actor, created_at, last_active_at, expired_at)
48
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
49
- `);
50
-
51
- stmt.run(
52
- session.id,
53
- session.scope,
54
- session.scope_id,
55
- session.context,
56
- session.metadata,
57
- session.actor,
58
- session.created_at,
59
- session.last_active_at,
60
- session.expired_at
61
- );
62
-
63
- // 加入内存缓存(带上限保护)
64
- if (this.activeSessions.size >= SessionManager.MAX_CACHED_SESSIONS) {
65
- // 淘汰最老的会话缓存
66
- const oldest = this.activeSessions.keys().next().value;
67
- this.activeSessions.delete(oldest);
68
- }
69
- this.activeSessions.set(sessionId, session);
70
-
71
- this.logger.info('Session created', {
72
- sessionId,
73
- scope,
74
- scopeId,
75
- });
76
-
77
- return session;
78
- }
79
-
80
- /**
81
- * 获取会话
82
- */
83
- get(sessionId) {
84
- // 先从缓存获取
85
- if (this.activeSessions.has(sessionId)) {
86
- const session = this.activeSessions.get(sessionId);
87
- if (!this.isExpired(session)) {
88
- return session;
89
- } else {
90
- this.activeSessions.delete(sessionId);
91
- }
92
- }
93
-
94
- // 从数据库获取
95
- const stmt = this.db.prepare('SELECT * FROM sessions WHERE id = ?');
96
- const session = stmt.get(sessionId);
97
-
98
- if (session && !this.isExpired(session)) {
99
- this.activeSessions.set(sessionId, session);
100
- return session;
101
- }
102
-
103
- return null;
104
- }
105
-
106
- /**
107
- * 根据作用域查询会话
108
- */
109
- findByScope(scope, scopeId) {
110
- const stmt = this.db.prepare(`
111
- SELECT * FROM sessions
112
- WHERE scope = ? AND scope_id = ?
113
- AND expired_at > ?
114
- ORDER BY created_at DESC
115
- LIMIT 1
116
- `);
117
- return stmt.get(scope, scopeId, Math.floor(Date.now() / 1000));
118
- }
119
-
120
- /**
121
- * 更新会话元数据
122
- */
123
- updateMetadata(sessionId, metadata) {
124
- const now = Math.floor(Date.now() / 1000);
125
- const stmt = this.db.prepare(`
126
- UPDATE sessions
127
- SET metadata = ?, last_active_at = ?
128
- WHERE id = ?
129
- `);
130
-
131
- stmt.run(
132
- JSON.stringify(metadata),
133
- now,
134
- sessionId
135
- );
136
-
137
- // 更新缓存
138
- if (this.activeSessions.has(sessionId)) {
139
- const session = this.activeSessions.get(sessionId);
140
- session.metadata = JSON.stringify(metadata);
141
- session.last_active_at = now;
142
- }
143
-
144
- this.logger.debug('Session metadata updated', { sessionId });
145
- }
146
-
147
- /**
148
- * 关闭会话
149
- */
150
- close(sessionId) {
151
- // 直接从数据库删除(更彻底)
152
- const stmt = this.db.prepare('DELETE FROM sessions WHERE id = ?');
153
- stmt.run(sessionId);
154
-
155
- // 从缓存移除
156
- this.activeSessions.delete(sessionId);
157
-
158
- this.logger.info('Session closed', { sessionId });
159
- }
160
-
161
- /**
162
- * 清理过期会话
163
- */
164
- cleanupExpired() {
165
- const now = Math.floor(Date.now() / 1000);
166
- const stmt = this.db.prepare('DELETE FROM sessions WHERE expired_at < ?');
167
- const result = stmt.run(now);
168
-
169
- if (result.changes > 0) {
170
- this.logger.info(`Cleaned up ${result.changes} expired sessions`);
171
- }
172
-
173
- // 清理缓存中的过期会话
174
- for (const [sessionId, session] of this.activeSessions.entries()) {
175
- if (this.isExpired(session)) {
176
- this.activeSessions.delete(sessionId);
177
- }
178
- }
179
- }
180
-
181
- /**
182
- * 判断会话是否过期
183
- */
184
- isExpired(session) {
185
- const now = Math.floor(Date.now() / 1000);
186
- return session.expired_at < now;
187
- }
188
-
189
- /**
190
- * 获取作用域的过期秒数
191
- */
192
- _getExpirationSeconds(scope) {
193
- switch (scope) {
194
- case 'project':
195
- return 7 * 24 * 60 * 60; // 7 天
196
- case 'target':
197
- return 24 * 60 * 60; // 1 天
198
- case 'file':
199
- return 60 * 60; // 1 小时
200
- case 'developer':
201
- return 30 * 24 * 60 * 60; // 30 天
202
- default:
203
- return 24 * 60 * 60; // 默认 1 天
204
- }
205
- }
206
-
207
- /**
208
- * 获取活跃会话数
209
- */
210
- getActiveCount() {
211
- const now = Math.floor(Date.now() / 1000);
212
- const stmt = this.db.prepare('SELECT COUNT(*) as count FROM sessions WHERE expired_at > ?');
213
- const result = stmt.get(now);
214
- return result.count;
215
- }
216
-
217
- /**
218
- * 获取所有活跃会话
219
- */
220
- getActiveSessions(limit = 100) {
221
- const now = Math.floor(Date.now() / 1000);
222
- const stmt = this.db.prepare(`
223
- SELECT * FROM sessions
224
- WHERE expired_at > ?
225
- ORDER BY created_at DESC
226
- LIMIT ?
227
- `);
228
- return stmt.all(now, limit);
229
- }
230
- }
231
-
232
- export default SessionManager;
@@ -1,269 +0,0 @@
1
- /**
2
- * ReasoningLogger - 透明推理日志系统
3
- *
4
- * 记录 AI 操作的完整推理过程,包括:
5
- * - Candidate 提交时的推理上下文
6
- * - Guard 检查时的规则匹配过程
7
- * - 搜索时的排名决策
8
- *
9
- * 所有推理日志不可篡改,存储在 SQLite 中
10
- */
11
-
12
- import { v4 as uuidv4 } from 'uuid';
13
- import Logger from '../logging/Logger.js';
14
-
15
- export class ReasoningLogger {
16
- constructor(db) {
17
- this.db = typeof db?.getDb === 'function' ? db.getDb() : db;
18
- this.logger = Logger.getInstance();
19
- this._ensureTable();
20
- }
21
-
22
- _ensureTable() {
23
- this.db.exec(`
24
- CREATE TABLE IF NOT EXISTS reasoning_logs (
25
- id TEXT PRIMARY KEY,
26
- type TEXT NOT NULL,
27
- actor TEXT NOT NULL,
28
- resource_type TEXT,
29
- resource_id TEXT,
30
- reasoning_json TEXT NOT NULL DEFAULT '{}',
31
- quality_score REAL DEFAULT 0,
32
- context_json TEXT DEFAULT '{}',
33
- created_at INTEGER NOT NULL
34
- )
35
- `);
36
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_reasoning_logs_type ON reasoning_logs(type)`);
37
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_reasoning_logs_actor ON reasoning_logs(actor)`);
38
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_reasoning_logs_created ON reasoning_logs(created_at)`);
39
- }
40
-
41
- /**
42
- * 记录 Candidate 推理过程
43
- */
44
- logCandidateReasoning(candidate, actor, context = {}) {
45
- const reasoning = candidate.reasoning || {};
46
- const qualityScore = this._assessReasoningQuality(reasoning);
47
-
48
- return this._insert({
49
- type: 'candidate_reasoning',
50
- actor,
51
- resourceType: 'candidate',
52
- resourceId: candidate.id,
53
- reasoning: {
54
- whyStandard: reasoning.whyStandard || '',
55
- sources: reasoning.sources || [],
56
- qualitySignals: reasoning.qualitySignals || {},
57
- alternatives: reasoning.alternatives || [],
58
- confidence: reasoning.confidence || 0,
59
- assessment: {
60
- completeness: this._assessCompleteness(reasoning),
61
- sourceQuality: this._assessSourceQuality(reasoning),
62
- overall: qualityScore,
63
- },
64
- },
65
- qualityScore,
66
- context,
67
- });
68
- }
69
-
70
- /**
71
- * 记录 Guard 规则匹配推理
72
- */
73
- logGuardReasoning(code, matchedRules, actor, context = {}) {
74
- return this._insert({
75
- type: 'guard_reasoning',
76
- actor,
77
- resourceType: 'code_check',
78
- resourceId: context.fileId || null,
79
- reasoning: {
80
- codeLength: code.length,
81
- rulesChecked: matchedRules.length,
82
- violations: matchedRules.map(r => ({
83
- ruleId: r.ruleId || r.id,
84
- ruleName: r.name,
85
- severity: r.severity,
86
- matchCount: r.matches?.length || 0,
87
- sourceRecipeId: r.sourceRecipeId,
88
- })),
89
- checkDuration: context.duration,
90
- },
91
- qualityScore: 0,
92
- context,
93
- });
94
- }
95
-
96
- /**
97
- * 记录搜索排名推理
98
- */
99
- logSearchReasoning(query, results, rankingFactors, actor, context = {}) {
100
- return this._insert({
101
- type: 'search_reasoning',
102
- actor,
103
- resourceType: 'search',
104
- resourceId: null,
105
- reasoning: {
106
- query,
107
- resultCount: results.length,
108
- topResults: results.slice(0, 5).map(r => ({
109
- id: r.id,
110
- score: r.score,
111
- title: r.title,
112
- })),
113
- rankingFactors,
114
- },
115
- qualityScore: 0,
116
- context,
117
- });
118
- }
119
-
120
- /**
121
- * 查询推理日志
122
- */
123
- query(filters = {}, pagination = { page: 1, pageSize: 20 }) {
124
- const conditions = ['1=1'];
125
- const params = [];
126
-
127
- if (filters.type) {
128
- conditions.push('type = ?');
129
- params.push(filters.type);
130
- }
131
- if (filters.actor) {
132
- conditions.push('actor = ?');
133
- params.push(filters.actor);
134
- }
135
- if (filters.resourceType) {
136
- conditions.push('resource_type = ?');
137
- params.push(filters.resourceType);
138
- }
139
- if (filters.resourceId) {
140
- conditions.push('resource_id = ?');
141
- params.push(filters.resourceId);
142
- }
143
- if (filters.minQuality !== undefined) {
144
- conditions.push('quality_score >= ?');
145
- params.push(filters.minQuality);
146
- }
147
- if (filters.since) {
148
- conditions.push('created_at >= ?');
149
- params.push(filters.since);
150
- }
151
-
152
- const offset = (pagination.page - 1) * pagination.pageSize;
153
- const where = conditions.join(' AND ');
154
-
155
- const rows = this.db.prepare(
156
- `SELECT * FROM reasoning_logs WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`
157
- ).all(...params, pagination.pageSize, offset);
158
-
159
- const countRow = this.db.prepare(
160
- `SELECT COUNT(*) as total FROM reasoning_logs WHERE ${where}`
161
- ).get(...params);
162
-
163
- return {
164
- items: rows.map(this._mapRow),
165
- total: countRow.total,
166
- page: pagination.page,
167
- pageSize: pagination.pageSize,
168
- };
169
- }
170
-
171
- /**
172
- * 获取推理质量统计
173
- */
174
- getQualityStats(since = null) {
175
- const params = [];
176
- let where = '1=1';
177
- if (since) {
178
- where = 'created_at >= ?';
179
- params.push(since);
180
- }
181
-
182
- const stats = this.db.prepare(`
183
- SELECT
184
- type,
185
- COUNT(*) as count,
186
- AVG(quality_score) as avg_quality,
187
- MIN(quality_score) as min_quality,
188
- MAX(quality_score) as max_quality
189
- FROM reasoning_logs
190
- WHERE ${where}
191
- GROUP BY type
192
- `).all(...params);
193
-
194
- return stats;
195
- }
196
-
197
- // ========== Private Methods ==========
198
-
199
- _insert({ type, actor, resourceType, resourceId, reasoning, qualityScore, context }) {
200
- const id = uuidv4();
201
- const now = Math.floor(Date.now() / 1000);
202
-
203
- this.db.prepare(`
204
- INSERT INTO reasoning_logs (id, type, actor, resource_type, resource_id, reasoning_json, quality_score, context_json, created_at)
205
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
206
- `).run(id, type, actor, resourceType, resourceId, JSON.stringify(reasoning), qualityScore, JSON.stringify(context), now);
207
-
208
- return { id, type, actor, qualityScore, createdAt: now };
209
- }
210
-
211
- _mapRow(row) {
212
- return {
213
- id: row.id,
214
- type: row.type,
215
- actor: row.actor,
216
- resourceType: row.resource_type,
217
- resourceId: row.resource_id,
218
- reasoning: JSON.parse(row.reasoning_json || '{}'),
219
- qualityScore: row.quality_score,
220
- context: JSON.parse(row.context_json || '{}'),
221
- createdAt: row.created_at,
222
- };
223
- }
224
-
225
- /**
226
- * 评估推理质量(0-1)
227
- */
228
- _assessReasoningQuality(reasoning) {
229
- if (!reasoning) return 0;
230
-
231
- const completeness = this._assessCompleteness(reasoning);
232
- const sourceQuality = this._assessSourceQuality(reasoning);
233
- const confidenceScore = typeof reasoning.confidence === 'number' ? reasoning.confidence : 0;
234
-
235
- // 权重: 完整性 40%, 来源质量 35%, 置信度 25%
236
- return Math.round((completeness * 0.4 + sourceQuality * 0.35 + confidenceScore * 0.25) * 100) / 100;
237
- }
238
-
239
- _assessCompleteness(reasoning) {
240
- if (!reasoning) return 0;
241
- let score = 0;
242
- if (reasoning.whyStandard && reasoning.whyStandard.length > 10) score += 0.3;
243
- if (Array.isArray(reasoning.sources) && reasoning.sources.length > 0) score += 0.25;
244
- if (reasoning.qualitySignals && Object.keys(reasoning.qualitySignals).length > 0) score += 0.2;
245
- if (Array.isArray(reasoning.alternatives) && reasoning.alternatives.length > 0) score += 0.15;
246
- if (typeof reasoning.confidence === 'number') score += 0.1;
247
- return Math.min(1, score);
248
- }
249
-
250
- _assessSourceQuality(reasoning) {
251
- if (!reasoning || !Array.isArray(reasoning.sources) || reasoning.sources.length === 0) return 0;
252
- // 多来源 + 来源有类型或链接 → 较高质量
253
- const count = Math.min(reasoning.sources.length, 5);
254
- return count / 5;
255
- }
256
- }
257
-
258
- let instance = null;
259
-
260
- export function initReasoningLogger(db) {
261
- instance = new ReasoningLogger(db);
262
- return instance;
263
- }
264
-
265
- export function getReasoningLogger() {
266
- return instance;
267
- }
268
-
269
- export default ReasoningLogger;