@devflow-tools/database 0.17.8 → 0.18.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/dist/data/LocalDataProvider.d.ts +63 -0
  3. package/dist/data/LocalDataProvider.js +332 -0
  4. package/dist/data/default-enforcer-rules.d.ts +3 -0
  5. package/dist/data/default-enforcer-rules.js +170 -0
  6. package/dist/data/devflow-schema.d.ts +2 -0
  7. package/dist/data/devflow-schema.js +80 -0
  8. package/dist/database.d.ts +45 -7
  9. package/dist/database.js +583 -125
  10. package/dist/index.d.ts +8 -4
  11. package/dist/index.js +11 -5
  12. package/dist/retrieval-ledger.d.ts +16 -1
  13. package/dist/retrieval-ledger.js +6 -0
  14. package/dist/retrieval-runtime.d.ts +39 -0
  15. package/dist/retrieval-runtime.js +2 -0
  16. package/dist/task-aggregate.d.ts +49 -0
  17. package/dist/task-aggregate.js +2 -0
  18. package/dist/task-semantic-control.d.ts +8 -1
  19. package/dist/task-semantic-control.js +2 -4
  20. package/package.json +15 -3
  21. package/CHANGELOG.md +0 -844
  22. package/__tests__/database.failure-category.test.ts +0 -44
  23. package/__tests__/database.host-actions.test.ts +0 -405
  24. package/__tests__/database.learning-candidates.test.ts +0 -93
  25. package/__tests__/database.memory-turn-receipts.test.ts +0 -98
  26. package/__tests__/database.retrieval-ledger.test.ts +0 -68
  27. package/__tests__/database.retrieval-sessions.test.ts +0 -79
  28. package/__tests__/database.semantic-resolution.test.ts +0 -87
  29. package/__tests__/database.skill-executions.test.ts +0 -456
  30. package/__tests__/database.task-runtime.test.ts +0 -73
  31. package/__tests__/database.test.ts +0 -177
  32. package/__tests__/database.work-queue.test.ts +0 -274
  33. package/__tests__/database.workflow-workers.test.ts +0 -60
  34. package/__tests__/node-sqlite.test.ts +0 -68
  35. package/src/database.ts +0 -5853
  36. package/src/host-actions.ts +0 -211
  37. package/src/index.ts +0 -153
  38. package/src/learning-candidates.ts +0 -181
  39. package/src/node-sqlite.ts +0 -60
  40. package/src/obligation-ledger.ts +0 -57
  41. package/src/retrieval-ledger.ts +0 -35
  42. package/src/retrieval-sessions.ts +0 -196
  43. package/src/semantic-resolution.ts +0 -181
  44. package/src/task-runtime.ts +0 -169
  45. package/src/task-semantic-control.ts +0 -270
  46. package/src/types.ts +0 -17
  47. package/src/work-queue.ts +0 -123
  48. package/src/workflow-workers.ts +0 -198
  49. package/tsconfig.json +0 -19
  50. package/tsconfig.tsbuildinfo +0 -1
  51. package/vitest.config.ts +0 -41
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 shilongfeicool
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,63 @@
1
+ import type { SQLInputValue } from "node:sqlite";
2
+ import type { IDataProvider, VectorResult, VectorPoint, GraphNode, GraphEdge, SessionEntry, SymbolEntry, SymbolKind, FileMeta, ImportEntry, AuditEntry, ContextReceipt, Rule } from "@devflow-tools/sdk";
3
+ export declare class LocalDataProvider implements IDataProvider {
4
+ private db;
5
+ constructor(_root: string, options?: {
6
+ dbPath?: string;
7
+ });
8
+ private initSchema;
9
+ close(): void;
10
+ vectorSearch(_collection: string, _query: number[], _limit: number): Promise<VectorResult[]>;
11
+ vectorUpsert(_collection: string, _points: VectorPoint[]): Promise<void>;
12
+ vectorDelete(_collection: string, _ids: string[]): Promise<void>;
13
+ graphGetAll(): Promise<{
14
+ nodes: GraphNode[];
15
+ edges: GraphEdge[];
16
+ }>;
17
+ graphGetNode(_id: string): Promise<GraphNode | null>;
18
+ graphGetEdges(_nodeId: string, _direction: "in" | "out"): Promise<GraphEdge[]>;
19
+ graphUpsertNodes(_nodes: GraphNode[]): Promise<void>;
20
+ graphUpsertEdges(_edges: GraphEdge[]): Promise<void>;
21
+ graphClear(): Promise<void>;
22
+ kvGet(key: string): Promise<unknown>;
23
+ kvSet(key: string, value: unknown): Promise<void>;
24
+ kvDelete(key: string): Promise<void>;
25
+ kvGetPrefix(prefix: string): Promise<Record<string, unknown>>;
26
+ sessionGet(sessionId: string): Promise<SessionEntry[]>;
27
+ sessionAppend(sessionId: string, entry: SessionEntry): Promise<void>;
28
+ sessionCreate(sessionId: string, task: string): Promise<void>;
29
+ sessionClose(sessionId: string): Promise<void>;
30
+ sqlQuery<T>(sql: string, params?: SQLInputValue[]): Promise<T[]>;
31
+ sqlExecute(sql: string, params?: SQLInputValue[]): Promise<void>;
32
+ recordToolCall(tool: string, args: Record<string, unknown>, durationMs: number, error?: string): Promise<void>;
33
+ getToolCallStats(): Promise<{
34
+ total: number;
35
+ byTool: Record<string, number>;
36
+ recent: Array<{
37
+ tool: string;
38
+ timestamp: number;
39
+ durationMs: number;
40
+ error?: string;
41
+ }>;
42
+ }>;
43
+ private _auditSecret;
44
+ auditGetSecret(): Promise<string>;
45
+ auditAppend(entry: AuditEntry): Promise<void>;
46
+ auditGetAll(): Promise<AuditEntry[]>;
47
+ ruleSave(rule: Rule): Promise<void>;
48
+ ruleGetAll(options?: {
49
+ includeDisabled?: boolean;
50
+ }): Promise<Rule[]>;
51
+ ruleDelete(id: string): Promise<void>;
52
+ contextReceiptPut(receipt: ContextReceipt): Promise<void>;
53
+ contextReceiptGet(projectRoot: string, sessionId: string, executionId: string): Promise<ContextReceipt | null>;
54
+ contextReceiptDelete(projectRoot: string, sessionId: string, executionId?: string): Promise<void>;
55
+ insertSymbols(_symbols: SymbolEntry[]): Promise<void>;
56
+ insertFiles(_files: FileMeta[]): Promise<void>;
57
+ insertImports(_imports: ImportEntry[]): Promise<void>;
58
+ findSymbols(_name: string): Promise<SymbolEntry[]>;
59
+ findSymbolsByKind(_kind: SymbolKind): Promise<SymbolEntry[]>;
60
+ getSymbolsInFile(_file: string): Promise<SymbolEntry[]>;
61
+ getFileCount(): Promise<number>;
62
+ getSymbolCount(): Promise<number>;
63
+ }
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalDataProvider = void 0;
4
+ const node_sqlite_1 = require("node:sqlite");
5
+ const node_path_1 = require("node:path");
6
+ const node_fs_1 = require("node:fs");
7
+ const database_js_1 = require("../database.js");
8
+ const node_crypto_1 = require("node:crypto");
9
+ const devflow_schema_js_1 = require("./devflow-schema.js");
10
+ function parseStringArray(value) {
11
+ if (typeof value !== 'string')
12
+ return [];
13
+ try {
14
+ const parsed = JSON.parse(value);
15
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === 'string') : [];
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ }
21
+ function parseJsonObject(value) {
22
+ if (typeof value !== 'string' || !value)
23
+ return undefined;
24
+ try {
25
+ const parsed = JSON.parse(value);
26
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : undefined;
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ function parseJsonArray(value) {
33
+ if (typeof value !== 'string' || !value)
34
+ return [];
35
+ try {
36
+ const parsed = JSON.parse(value);
37
+ return Array.isArray(parsed) ? parsed : [];
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ }
43
+ class LocalDataProvider {
44
+ constructor(_root, options) {
45
+ // --- Audit chain ---
46
+ this._auditSecret = null;
47
+ const dbPath = options?.dbPath ?? (0, database_js_1.getGlobalDevFlowDbPath)();
48
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(dbPath), { recursive: true });
49
+ this.db = new node_sqlite_1.DatabaseSync(dbPath);
50
+ this.db.exec("PRAGMA journal_mode = WAL");
51
+ this.initSchema();
52
+ }
53
+ initSchema() {
54
+ this.db.exec(devflow_schema_js_1.DEVFLOW_SCHEMA_SQL);
55
+ try {
56
+ this.db.exec("ALTER TABLE devflow_workflow_runs ADD COLUMN failure_category TEXT");
57
+ }
58
+ catch { }
59
+ try {
60
+ this.db.exec("ALTER TABLE devflow_workflow_runs ADD COLUMN failure_reason TEXT");
61
+ }
62
+ catch { }
63
+ try {
64
+ this.db.exec("ALTER TABLE devflow_workflow_runs ADD COLUMN failure_evidence TEXT");
65
+ }
66
+ catch { }
67
+ try {
68
+ this.db.exec("ALTER TABLE devflow_rules ADD COLUMN gate INTEGER NOT NULL DEFAULT 1");
69
+ }
70
+ catch { }
71
+ try {
72
+ this.db.exec("ALTER TABLE devflow_rules ADD COLUMN updated_at INTEGER");
73
+ }
74
+ catch { }
75
+ try {
76
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN selected_files TEXT NOT NULL DEFAULT '[]'");
77
+ }
78
+ catch { }
79
+ try {
80
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN memory_ids TEXT NOT NULL DEFAULT '[]'");
81
+ }
82
+ catch { }
83
+ try {
84
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN canonical_next_action TEXT");
85
+ }
86
+ catch { }
87
+ try {
88
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN canonical_action_json TEXT");
89
+ }
90
+ catch { }
91
+ try {
92
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN action_attempts TEXT NOT NULL DEFAULT '[]'");
93
+ }
94
+ catch { }
95
+ try {
96
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN action_satisfied_at INTEGER");
97
+ }
98
+ catch { }
99
+ try {
100
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN action_degradation_json TEXT");
101
+ }
102
+ catch { }
103
+ try {
104
+ this.db.exec("ALTER TABLE devflow_context_receipts ADD COLUMN request_id TEXT");
105
+ }
106
+ catch { }
107
+ this.db.exec("UPDATE devflow_rules SET updated_at = created_at WHERE updated_at IS NULL");
108
+ }
109
+ close() {
110
+ if (this.db.isOpen)
111
+ this.db.close();
112
+ }
113
+ // --- Vector (P0 stubs) ---
114
+ async vectorSearch(_collection, _query, _limit) {
115
+ return [];
116
+ }
117
+ async vectorUpsert(_collection, _points) { }
118
+ async vectorDelete(_collection, _ids) { }
119
+ // --- Graph (stubs — CodeGraph owns this now) ---
120
+ async graphGetAll() { return { nodes: [], edges: [] }; }
121
+ async graphGetNode(_id) { return null; }
122
+ async graphGetEdges(_nodeId, _direction) { return []; }
123
+ async graphUpsertNodes(_nodes) { }
124
+ async graphUpsertEdges(_edges) { }
125
+ async graphClear() { }
126
+ // --- KV ---
127
+ async kvGet(key) {
128
+ const row = this.db.prepare("SELECT value FROM devflow_kv WHERE key = ?").get(key);
129
+ if (!row)
130
+ return null;
131
+ return JSON.parse(row.value);
132
+ }
133
+ async kvSet(key, value) {
134
+ this.db.prepare("INSERT OR REPLACE INTO devflow_kv (key, value) VALUES (?, ?)").run(key, JSON.stringify(value));
135
+ }
136
+ async kvDelete(key) {
137
+ this.db.prepare("DELETE FROM devflow_kv WHERE key = ?").run(key);
138
+ }
139
+ async kvGetPrefix(prefix) {
140
+ const rows = this.db.prepare("SELECT key, value FROM devflow_kv WHERE key LIKE ?").all(prefix + "%");
141
+ const result = {};
142
+ for (const row of rows) {
143
+ result[row.key] = JSON.parse(row.value);
144
+ }
145
+ return result;
146
+ }
147
+ // --- Session ---
148
+ async sessionGet(sessionId) {
149
+ const rows = this.db.prepare("SELECT timestamp, event, data FROM devflow_sessions WHERE session_id = ? ORDER BY timestamp").all(sessionId);
150
+ return rows.map((r) => ({
151
+ timestamp: r.timestamp,
152
+ action: r.event,
153
+ summary: r.event,
154
+ ...(r.data ? JSON.parse(r.data) : {}),
155
+ }));
156
+ }
157
+ async sessionAppend(sessionId, entry) {
158
+ this.db.prepare("INSERT INTO devflow_sessions (session_id, timestamp, event, data) VALUES (?, ?, ?, ?)").run(sessionId, entry.timestamp, entry.action, JSON.stringify({ summary: entry.summary, metadata: entry.metadata }));
159
+ }
160
+ async sessionCreate(sessionId, task) {
161
+ await this.sessionAppend(sessionId, {
162
+ timestamp: Date.now(),
163
+ action: "session:create",
164
+ summary: task,
165
+ });
166
+ }
167
+ async sessionClose(sessionId) {
168
+ await this.sessionAppend(sessionId, {
169
+ timestamp: Date.now(),
170
+ action: "session:close",
171
+ summary: "Session closed",
172
+ });
173
+ }
174
+ // --- SQL ---
175
+ async sqlQuery(sql, params) {
176
+ const stmt = this.db.prepare(sql);
177
+ return (params ? stmt.all(...params) : stmt.all());
178
+ }
179
+ async sqlExecute(sql, params) {
180
+ const stmt = this.db.prepare(sql);
181
+ params ? stmt.run(...params) : stmt.run();
182
+ }
183
+ // --- Tool call telemetry (Layer 4: local mode) ---
184
+ async recordToolCall(tool, args, durationMs, error) {
185
+ this.db.prepare("INSERT INTO devflow_tool_calls (tool, args, timestamp, duration_ms, error) VALUES (?, ?, ?, ?, ?)").run(tool, JSON.stringify(args), Date.now(), durationMs, error ?? null);
186
+ }
187
+ async getToolCallStats() {
188
+ const total = this.db.prepare("SELECT COUNT(*) as c FROM devflow_tool_calls").get().c;
189
+ const byToolRows = this.db.prepare("SELECT tool, COUNT(*) as c FROM devflow_tool_calls GROUP BY tool").all();
190
+ const byTool = {};
191
+ for (const r of byToolRows)
192
+ byTool[r.tool] = r.c;
193
+ const recent = this.db.prepare("SELECT tool, timestamp, duration_ms, error FROM devflow_tool_calls ORDER BY timestamp DESC LIMIT 20").all();
194
+ return {
195
+ total,
196
+ byTool,
197
+ recent: recent.map((r) => ({ tool: r.tool, timestamp: r.timestamp, durationMs: r.duration_ms, ...(r.error ? { error: r.error } : {}) })),
198
+ };
199
+ }
200
+ async auditGetSecret() {
201
+ if (this._auditSecret)
202
+ return this._auditSecret;
203
+ const row = this.db.prepare("SELECT value FROM devflow_kv WHERE key = ?").get("audit_secret");
204
+ if (row) {
205
+ this._auditSecret = row.value;
206
+ return this._auditSecret;
207
+ }
208
+ const secret = (0, node_crypto_1.randomBytes)(32).toString("hex");
209
+ this.db.prepare("INSERT OR REPLACE INTO devflow_kv (key, value) VALUES ('audit_secret', ?)").run(secret);
210
+ this._auditSecret = secret;
211
+ return secret;
212
+ }
213
+ async auditAppend(entry) {
214
+ this.db.prepare("INSERT INTO devflow_audit_chain (ts, tool, args_hash, decision, rule_id, session_id, prev_hash, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(entry.ts, entry.tool, entry.argsHash, entry.decision, entry.ruleId, entry.sessionId, entry.prevHash, entry.signature);
215
+ }
216
+ async auditGetAll() {
217
+ const rows = this.db.prepare("SELECT seq, ts, tool, args_hash, decision, rule_id, session_id, prev_hash, signature FROM devflow_audit_chain ORDER BY seq").all();
218
+ return rows.map((r) => ({
219
+ seq: r.seq,
220
+ ts: r.ts,
221
+ tool: r.tool,
222
+ argsHash: r.args_hash,
223
+ decision: r.decision,
224
+ ruleId: r.rule_id,
225
+ sessionId: r.session_id,
226
+ prevHash: r.prev_hash,
227
+ signature: r.signature,
228
+ }));
229
+ }
230
+ // --- Rules ---
231
+ async ruleSave(rule) {
232
+ const now = Date.now();
233
+ this.db.prepare(`INSERT INTO devflow_rules
234
+ (id, name, gate, level, condition_json, action, message, enabled, created_at, updated_at)
235
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
236
+ ON CONFLICT(id) DO UPDATE SET
237
+ name = excluded.name, gate = excluded.gate, level = excluded.level,
238
+ condition_json = excluded.condition_json, action = excluded.action,
239
+ message = excluded.message, enabled = excluded.enabled,
240
+ updated_at = excluded.updated_at`).run(rule.id, rule.name, rule.gate, rule.level, JSON.stringify(rule.condition), rule.action, rule.message, rule.enabled ? 1 : 0, rule.createdAt ?? now, now);
241
+ }
242
+ async ruleGetAll(options) {
243
+ const where = options?.includeDisabled ? "" : "WHERE enabled = 1";
244
+ const rows = this.db.prepare(`SELECT * FROM devflow_rules ${where} ORDER BY gate, id`).all();
245
+ return rows.map((r) => ({
246
+ id: r.id,
247
+ name: r.name,
248
+ gate: r.gate,
249
+ level: r.level,
250
+ condition: JSON.parse(r.condition_json),
251
+ action: r.action,
252
+ message: r.message,
253
+ enabled: r.enabled === 1,
254
+ createdAt: r.created_at,
255
+ updatedAt: r.updated_at,
256
+ }));
257
+ }
258
+ async ruleDelete(id) {
259
+ this.db.prepare("DELETE FROM devflow_rules WHERE id = ?").run(id);
260
+ }
261
+ async contextReceiptPut(receipt) {
262
+ this.db.prepare(`
263
+ INSERT INTO devflow_context_receipts
264
+ (project_root, session_id, execution_id, context_hash, issued_at, expires_at,
265
+ selected_files, memory_ids, canonical_next_action, canonical_action_json,
266
+ action_attempts, action_satisfied_at, action_degradation_json, request_id)
267
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
268
+ ON CONFLICT(project_root, session_id, execution_id) DO UPDATE SET
269
+ context_hash = excluded.context_hash,
270
+ issued_at = excluded.issued_at,
271
+ expires_at = excluded.expires_at,
272
+ selected_files = excluded.selected_files,
273
+ memory_ids = excluded.memory_ids,
274
+ canonical_next_action = excluded.canonical_next_action,
275
+ canonical_action_json = excluded.canonical_action_json,
276
+ action_attempts = excluded.action_attempts,
277
+ action_satisfied_at = excluded.action_satisfied_at,
278
+ action_degradation_json = excluded.action_degradation_json,
279
+ request_id = excluded.request_id
280
+ `).run(receipt.projectRoot, receipt.sessionId, receipt.executionId, receipt.contextHash, receipt.issuedAt, receipt.expiresAt, JSON.stringify(receipt.selectedFiles ?? []), JSON.stringify(receipt.memoryIds ?? []), receipt.canonicalNextAction ?? null, receipt.canonicalAction ? JSON.stringify(receipt.canonicalAction) : null, JSON.stringify(receipt.actionAttempts ?? []), receipt.actionSatisfiedAt ?? null, receipt.actionDegradation ? JSON.stringify(receipt.actionDegradation) : null, receipt.requestId ?? null);
281
+ }
282
+ async contextReceiptGet(projectRoot, sessionId, executionId) {
283
+ const row = this.db.prepare(`
284
+ SELECT project_root, session_id, execution_id, context_hash, issued_at, expires_at,
285
+ selected_files, memory_ids, canonical_next_action, canonical_action_json,
286
+ action_attempts, action_satisfied_at, action_degradation_json, request_id
287
+ FROM devflow_context_receipts
288
+ WHERE project_root = ? AND session_id = ? AND execution_id = ?
289
+ `).get(projectRoot, sessionId, executionId);
290
+ if (!row)
291
+ return null;
292
+ return {
293
+ projectRoot: row.project_root,
294
+ sessionId: row.session_id,
295
+ executionId: row.execution_id,
296
+ contextHash: row.context_hash,
297
+ issuedAt: row.issued_at,
298
+ expiresAt: row.expires_at,
299
+ selectedFiles: parseStringArray(row.selected_files),
300
+ memoryIds: parseStringArray(row.memory_ids),
301
+ canonicalNextAction: typeof row.canonical_next_action === 'string'
302
+ ? row.canonical_next_action
303
+ : undefined,
304
+ canonicalAction: parseJsonObject(row.canonical_action_json),
305
+ actionAttempts: parseJsonArray(row.action_attempts),
306
+ actionSatisfiedAt: typeof row.action_satisfied_at === 'number' ? row.action_satisfied_at : undefined,
307
+ actionDegradation: parseJsonObject(row.action_degradation_json),
308
+ requestId: typeof row.request_id === 'string' ? row.request_id : undefined,
309
+ };
310
+ }
311
+ async contextReceiptDelete(projectRoot, sessionId, executionId) {
312
+ if (executionId) {
313
+ this.db.prepare(`DELETE FROM devflow_context_receipts
314
+ WHERE project_root = ? AND session_id = ? AND execution_id = ?`)
315
+ .run(projectRoot, sessionId, executionId);
316
+ }
317
+ else {
318
+ this.db.prepare(`DELETE FROM devflow_context_receipts
319
+ WHERE project_root = ? AND session_id = ?`).run(projectRoot, sessionId);
320
+ }
321
+ }
322
+ // --- File index (stubs — CodeGraph owns this now) ---
323
+ async insertSymbols(_symbols) { }
324
+ async insertFiles(_files) { }
325
+ async insertImports(_imports) { }
326
+ async findSymbols(_name) { return []; }
327
+ async findSymbolsByKind(_kind) { return []; }
328
+ async getSymbolsInFile(_file) { return []; }
329
+ async getFileCount() { return 0; }
330
+ async getSymbolCount() { return 0; }
331
+ }
332
+ exports.LocalDataProvider = LocalDataProvider;
@@ -0,0 +1,3 @@
1
+ import type { IDataProvider, Rule } from "@devflow-tools/sdk";
2
+ export declare const DEFAULT_ENFORCER_RULES: readonly Rule[];
3
+ export declare function seedDefaultEnforcerRules(provider: IDataProvider): Promise<void>;
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_ENFORCER_RULES = void 0;
4
+ exports.seedDefaultEnforcerRules = seedDefaultEnforcerRules;
5
+ const constitutional = (rule) => ({
6
+ ...rule,
7
+ gate: 1,
8
+ level: "constitutional",
9
+ enabled: true,
10
+ });
11
+ exports.DEFAULT_ENFORCER_RULES = [
12
+ constitutional({
13
+ id: "C001",
14
+ name: "Session Token 缺失",
15
+ condition: { requireFields: ["_devflow_token"] },
16
+ action: "warn",
17
+ message: "缺少 Session Token。当前调用处于降级认证模式。",
18
+ }),
19
+ constitutional({
20
+ id: "C002",
21
+ name: "Session Token 过期",
22
+ condition: { paramPatterns: { "_devflow_token.expired": "^true$" } },
23
+ action: "warn",
24
+ message: "Session Token 已过期,DevFlow 将自动续期。",
25
+ }),
26
+ constitutional({
27
+ id: "C003",
28
+ name: "调用速率限制",
29
+ condition: { maxCallsPerMinute: 60 },
30
+ action: "warn",
31
+ message: "调用频率超过每分钟 60 次,请降低请求速率。",
32
+ }),
33
+ constitutional({
34
+ id: "C004",
35
+ name: "危险 Shell 命令",
36
+ condition: {
37
+ toolPatterns: ["^(bash|shell|run_command|execute_command)$"],
38
+ forbidPatterns: ["(^|\\s)(rm\\s+-rf|mkfs(\\.|\\s)|dd\\s+if=|shutdown|reboot)(\\s|$)"],
39
+ },
40
+ action: "deny",
41
+ message: "检测到高风险系统命令,执行已阻止。",
42
+ }),
43
+ constitutional({
44
+ id: "C005",
45
+ name: "禁止强制推送",
46
+ condition: { forbidPatterns: ["git\\s+push(?:\\s+[^\\n]*)?\\s(?:--force|-f)(?:\\s|$)"] },
47
+ action: "deny",
48
+ message: "禁止未经显式治理规则批准的 Git 强制推送。",
49
+ }),
50
+ constitutional({
51
+ id: "C006",
52
+ name: "保护敏感文件",
53
+ condition: {
54
+ toolPatterns: ["(write|edit|patch|create|delete|remove|move|rename)"],
55
+ forbidPatterns: ["(^|/)(\\.env(?:\\.[^/]*)?|id_rsa|id_ed25519|credentials(?:\\.json)?|secrets?\\.(?:json|ya?ml))$"],
56
+ },
57
+ action: "deny",
58
+ message: "目标包含敏感凭据文件,写入已阻止。",
59
+ }),
60
+ constitutional({
61
+ id: "C007",
62
+ name: "阻止明文凭据",
63
+ condition: {
64
+ toolPatterns: ["(write|edit|patch|create|save|update)"],
65
+ forbidPatterns: ["(?:sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN (?:RSA |OPENSSH )?PRIVATE KEY-----)"],
66
+ },
67
+ action: "deny",
68
+ message: "检测到疑似明文凭据或私钥,写入已阻止。",
69
+ }),
70
+ constitutional({
71
+ id: "C008",
72
+ name: "Token 预算上限",
73
+ condition: { maxNumericFields: { maxTokens: 200000, max_tokens: 200000, tokenBudget: 200000, token_budget: 200000 } },
74
+ action: "warn",
75
+ message: "请求的 Token 预算超过 200000,请拆分任务或降低预算。",
76
+ }),
77
+ constitutional({
78
+ id: "C009",
79
+ name: "项目根目录必填",
80
+ condition: {
81
+ toolPatterns: ["^(get_project_context|search_symbol|get_dependency_graph|react_|vue_|nest_|next_|express_)"],
82
+ requireFields: ["projectRoot"],
83
+ },
84
+ action: "deny",
85
+ message: "该工具必须显式提供 projectRoot,禁止使用进程 cwd 推断。",
86
+ }),
87
+ constitutional({
88
+ id: "C010",
89
+ name: "破坏性 SQL",
90
+ condition: {
91
+ toolPatterns: ["(sql|query|database|db)"],
92
+ forbidPatterns: ["\\b(?:DROP\\s+(?:DATABASE|TABLE)|TRUNCATE\\s+TABLE|DELETE\\s+FROM\\s+[^;]+(?:;|$))"],
93
+ },
94
+ action: "deny",
95
+ message: "检测到破坏性 SQL,执行已阻止。",
96
+ }),
97
+ {
98
+ id: "S001",
99
+ name: "领域工具先获取项目上下文",
100
+ gate: 2,
101
+ level: "project",
102
+ condition: {
103
+ toolPatterns: ["^(react|vue|nest|next|nextjs|graphql|express|typescript|testing|security|database)_"],
104
+ requirePriorTools: ["get_project_context"],
105
+ },
106
+ action: "deny",
107
+ message: "Call get_project_context successfully before this domain tool.",
108
+ enabled: true,
109
+ },
110
+ {
111
+ id: "S002",
112
+ name: "代码生成先获取项目上下文",
113
+ gate: 2,
114
+ level: "project",
115
+ condition: {
116
+ toolPatterns: ["(generate|scaffold|refactor|code_write)"],
117
+ requirePriorTools: ["get_project_context"],
118
+ },
119
+ action: "warn",
120
+ message: "Gate 2:生成或重构代码前必须先获取项目上下文。",
121
+ enabled: true,
122
+ },
123
+ {
124
+ id: "I001",
125
+ name: "上下文告警拦截写入",
126
+ gate: 3,
127
+ level: "session",
128
+ condition: {
129
+ toolPatterns: ["(write|edit|patch|create|generate|refactor|code_write)"],
130
+ requiresContextWarning: true,
131
+ },
132
+ action: "warn",
133
+ message: "Gate 3:最近的项目上下文包含告警或低置信度,请先补充检索再写代码。",
134
+ enabled: true,
135
+ },
136
+ {
137
+ id: "I002",
138
+ name: "低置信度上下文拦截工作流",
139
+ gate: 3,
140
+ level: "session",
141
+ condition: {
142
+ toolPatterns: ["(workflow|execute|run)_"],
143
+ requiresContextWarning: true,
144
+ },
145
+ action: "warn",
146
+ message: "Gate 3:上下文质量不足,执行工作流前应确认告警并缩小范围。",
147
+ enabled: true,
148
+ },
149
+ {
150
+ id: "R001",
151
+ name: "失败后自动纠正",
152
+ gate: 4,
153
+ level: "session",
154
+ condition: { tools: ["*"], requiresPreviousFailure: true },
155
+ action: "warn",
156
+ message: "Gate 4:上一工具调用失败。请根据失败信息调整参数、缩小操作范围后重试。",
157
+ enabled: true,
158
+ },
159
+ ];
160
+ async function seedDefaultEnforcerRules(provider) {
161
+ const existing = new Map((await provider.ruleGetAll({ includeDisabled: true })).map((rule) => [rule.id, rule]));
162
+ for (const rule of exports.DEFAULT_ENFORCER_RULES) {
163
+ const stored = existing.get(rule.id);
164
+ const staleS001 = rule.id === "S001" && stored && (stored.action !== rule.action
165
+ || stored.enabled !== rule.enabled
166
+ || JSON.stringify(stored.condition) !== JSON.stringify(rule.condition));
167
+ if (!stored || staleS001)
168
+ await provider.ruleSave(rule);
169
+ }
170
+ }
@@ -0,0 +1,2 @@
1
+ export declare const DEVFLOW_TABLE_NAMES: readonly ["devflow_kv", "devflow_sessions", "devflow_telemetry_runs", "devflow_benchmark_reports", "devflow_workflow_runs", "devflow_knowledge_entries", "devflow_tool_calls", "devflow_rules", "devflow_audit_chain", "devflow_context_receipts"];
2
+ export declare const DEVFLOW_SCHEMA_SQL = "\n CREATE TABLE IF NOT EXISTS devflow_kv (\n key TEXT PRIMARY KEY, value TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_sessions (\n session_id TEXT, timestamp INTEGER, event TEXT, data TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_telemetry_runs (\n id TEXT PRIMARY KEY, agent TEXT, mode TEXT, status TEXT,\n started_at INTEGER, finished_at INTEGER, token_used INTEGER, data TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_benchmark_reports (\n id TEXT PRIMARY KEY, task TEXT, score REAL, started_at INTEGER,\n finished_at INTEGER, data TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_workflow_runs (\n run_id TEXT PRIMARY KEY, type TEXT, status TEXT, input TEXT,\n steps TEXT, started_at INTEGER, finished_at INTEGER,\n failure_category TEXT, failure_reason TEXT, failure_evidence TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_knowledge_entries (\n id TEXT PRIMARY KEY, source TEXT, title TEXT, content TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_tool_calls (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n tool TEXT NOT NULL,\n args TEXT,\n timestamp INTEGER NOT NULL,\n duration_ms INTEGER NOT NULL,\n error TEXT\n );\n CREATE TABLE IF NOT EXISTS devflow_rules (\n id TEXT PRIMARY KEY, name TEXT NOT NULL, level TEXT NOT NULL DEFAULT 'project',\n gate INTEGER NOT NULL DEFAULT 1,\n condition_json TEXT NOT NULL, action TEXT NOT NULL DEFAULT 'warn',\n message TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS devflow_audit_chain (\n seq INTEGER PRIMARY KEY AUTOINCREMENT, ts INTEGER NOT NULL,\n tool TEXT NOT NULL, args_hash TEXT NOT NULL,\n decision TEXT NOT NULL, rule_id TEXT,\n session_id TEXT NOT NULL, prev_hash TEXT NOT NULL,\n signature TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS devflow_context_receipts (\n project_root TEXT NOT NULL,\n session_id TEXT NOT NULL,\n execution_id TEXT NOT NULL,\n context_hash TEXT NOT NULL,\n issued_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n selected_files TEXT NOT NULL DEFAULT '[]',\n memory_ids TEXT NOT NULL DEFAULT '[]',\n canonical_next_action TEXT,\n canonical_action_json TEXT,\n action_attempts TEXT NOT NULL DEFAULT '[]',\n action_satisfied_at INTEGER,\n action_degradation_json TEXT,\n request_id TEXT,\n PRIMARY KEY (project_root, session_id, execution_id)\n );\n CREATE INDEX IF NOT EXISTS idx_context_receipts_expiry\n ON devflow_context_receipts(expires_at);\n";
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEVFLOW_SCHEMA_SQL = exports.DEVFLOW_TABLE_NAMES = void 0;
4
+ exports.DEVFLOW_TABLE_NAMES = [
5
+ 'devflow_kv',
6
+ 'devflow_sessions',
7
+ 'devflow_telemetry_runs',
8
+ 'devflow_benchmark_reports',
9
+ 'devflow_workflow_runs',
10
+ 'devflow_knowledge_entries',
11
+ 'devflow_tool_calls',
12
+ 'devflow_rules',
13
+ 'devflow_audit_chain',
14
+ 'devflow_context_receipts',
15
+ ];
16
+ exports.DEVFLOW_SCHEMA_SQL = `
17
+ CREATE TABLE IF NOT EXISTS devflow_kv (
18
+ key TEXT PRIMARY KEY, value TEXT
19
+ );
20
+ CREATE TABLE IF NOT EXISTS devflow_sessions (
21
+ session_id TEXT, timestamp INTEGER, event TEXT, data TEXT
22
+ );
23
+ CREATE TABLE IF NOT EXISTS devflow_telemetry_runs (
24
+ id TEXT PRIMARY KEY, agent TEXT, mode TEXT, status TEXT,
25
+ started_at INTEGER, finished_at INTEGER, token_used INTEGER, data TEXT
26
+ );
27
+ CREATE TABLE IF NOT EXISTS devflow_benchmark_reports (
28
+ id TEXT PRIMARY KEY, task TEXT, score REAL, started_at INTEGER,
29
+ finished_at INTEGER, data TEXT
30
+ );
31
+ CREATE TABLE IF NOT EXISTS devflow_workflow_runs (
32
+ run_id TEXT PRIMARY KEY, type TEXT, status TEXT, input TEXT,
33
+ steps TEXT, started_at INTEGER, finished_at INTEGER,
34
+ failure_category TEXT, failure_reason TEXT, failure_evidence TEXT
35
+ );
36
+ CREATE TABLE IF NOT EXISTS devflow_knowledge_entries (
37
+ id TEXT PRIMARY KEY, source TEXT, title TEXT, content TEXT
38
+ );
39
+ CREATE TABLE IF NOT EXISTS devflow_tool_calls (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ tool TEXT NOT NULL,
42
+ args TEXT,
43
+ timestamp INTEGER NOT NULL,
44
+ duration_ms INTEGER NOT NULL,
45
+ error TEXT
46
+ );
47
+ CREATE TABLE IF NOT EXISTS devflow_rules (
48
+ id TEXT PRIMARY KEY, name TEXT NOT NULL, level TEXT NOT NULL DEFAULT 'project',
49
+ gate INTEGER NOT NULL DEFAULT 1,
50
+ condition_json TEXT NOT NULL, action TEXT NOT NULL DEFAULT 'warn',
51
+ message TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1,
52
+ created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
53
+ );
54
+ CREATE TABLE IF NOT EXISTS devflow_audit_chain (
55
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, ts INTEGER NOT NULL,
56
+ tool TEXT NOT NULL, args_hash TEXT NOT NULL,
57
+ decision TEXT NOT NULL, rule_id TEXT,
58
+ session_id TEXT NOT NULL, prev_hash TEXT NOT NULL,
59
+ signature TEXT NOT NULL
60
+ );
61
+ CREATE TABLE IF NOT EXISTS devflow_context_receipts (
62
+ project_root TEXT NOT NULL,
63
+ session_id TEXT NOT NULL,
64
+ execution_id TEXT NOT NULL,
65
+ context_hash TEXT NOT NULL,
66
+ issued_at INTEGER NOT NULL,
67
+ expires_at INTEGER NOT NULL,
68
+ selected_files TEXT NOT NULL DEFAULT '[]',
69
+ memory_ids TEXT NOT NULL DEFAULT '[]',
70
+ canonical_next_action TEXT,
71
+ canonical_action_json TEXT,
72
+ action_attempts TEXT NOT NULL DEFAULT '[]',
73
+ action_satisfied_at INTEGER,
74
+ action_degradation_json TEXT,
75
+ request_id TEXT,
76
+ PRIMARY KEY (project_root, session_id, execution_id)
77
+ );
78
+ CREATE INDEX IF NOT EXISTS idx_context_receipts_expiry
79
+ ON devflow_context_receipts(expires_at);
80
+ `;