@codemcp/workflows-core 3.1.19 → 3.1.21-fix-build-after-monorepo.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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/conversation-manager.js +1 -1
- package/dist/conversation-manager.js.map +1 -1
- package/dist/database.d.ts +24 -34
- package/dist/database.js +185 -420
- package/dist/database.js.map +1 -1
- package/dist/project-docs-manager.js +5 -4
- package/dist/project-docs-manager.js.map +1 -1
- package/dist/state-machine-loader.d.ts +2 -1
- package/dist/state-machine-loader.js +45 -7
- package/dist/state-machine-loader.js.map +1 -1
- package/dist/template-manager.js +9 -7
- package/dist/template-manager.js.map +1 -1
- package/dist/workflow-manager.js +9 -7
- package/dist/workflow-manager.js.map +1 -1
- package/package.json +3 -2
- package/src/conversation-manager.ts +1 -4
- package/src/database.ts +266 -578
- package/src/project-docs-manager.ts +5 -4
- package/src/state-machine-loader.ts +71 -14
- package/src/template-manager.ts +15 -8
- package/src/workflow-manager.ts +12 -7
package/dist/database.js
CHANGED
@@ -1,500 +1,265 @@
|
|
1
1
|
/**
|
2
|
-
* Database
|
2
|
+
* Database Manager
|
3
3
|
*
|
4
|
-
*
|
5
|
-
*
|
6
|
-
* Also stores interaction logs for auditing and debugging.
|
4
|
+
* Handles SQLite database operations for conversation state persistence.
|
5
|
+
* Uses better-sqlite3 for reliable cross-platform native bindings.
|
7
6
|
*/
|
8
|
-
import
|
7
|
+
import BetterSqlite3 from 'better-sqlite3';
|
9
8
|
import { mkdir } from 'node:fs/promises';
|
10
9
|
import { dirname } from 'node:path';
|
11
|
-
import { join } from 'node:path';
|
12
10
|
import { createLogger } from './logger.js';
|
13
11
|
const logger = createLogger('Database');
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
return value;
|
18
|
-
}
|
19
|
-
throw new Error(`Database field '${fieldName}' expected string but got ${typeof value}: ${value}`);
|
20
|
-
}
|
21
|
-
function parseJsonSafely(value, fieldName) {
|
22
|
-
if (!value) {
|
23
|
-
return undefined;
|
24
|
-
}
|
25
|
-
const stringValue = validateString(value, fieldName);
|
26
|
-
try {
|
27
|
-
return JSON.parse(stringValue);
|
28
|
-
}
|
29
|
-
catch (error) {
|
30
|
-
throw new Error(`Failed to parse JSON in field '${fieldName}': ${error instanceof Error ? error.message : String(error)}`);
|
31
|
-
}
|
32
|
-
}
|
33
|
-
function mapRowToInteractionLog(row) {
|
34
|
-
return {
|
35
|
-
id: typeof row.id === 'number' ? row.id : undefined,
|
36
|
-
conversationId: validateString(row.conversationId, 'conversationId'),
|
37
|
-
toolName: validateString(row.toolName, 'toolName'),
|
38
|
-
inputParams: validateString(row.inputParams, 'inputParams'),
|
39
|
-
responseData: validateString(row.responseData, 'responseData'),
|
40
|
-
currentPhase: validateString(row.currentPhase, 'currentPhase'),
|
41
|
-
timestamp: validateString(row.timestamp, 'timestamp'),
|
42
|
-
isReset: typeof row.isReset === 'number' ? Boolean(row.isReset) : undefined,
|
43
|
-
resetAt: row.resetAt ? validateString(row.resetAt, 'resetAt') : undefined,
|
44
|
-
};
|
45
|
-
}
|
12
|
+
/**
|
13
|
+
* Database connection and operations manager
|
14
|
+
*/
|
46
15
|
export class Database {
|
47
16
|
db = null;
|
48
17
|
dbPath;
|
49
|
-
constructor(
|
50
|
-
|
51
|
-
const vibeDir = join(projectPath, '.vibe');
|
52
|
-
this.dbPath = join(vibeDir, 'conversation-state.sqlite');
|
53
|
-
logger.debug('Database path configured', {
|
54
|
-
projectPath,
|
55
|
-
dbPath: this.dbPath,
|
56
|
-
});
|
18
|
+
constructor(dbPath) {
|
19
|
+
this.dbPath = dbPath;
|
57
20
|
}
|
58
21
|
/**
|
59
22
|
* Initialize database connection and create tables
|
60
23
|
*/
|
61
24
|
async initialize() {
|
62
|
-
logger.debug('Initializing database', { dbPath: this.dbPath });
|
63
25
|
try {
|
64
26
|
// Ensure directory exists
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
});
|
27
|
+
const dbDir = dirname(this.dbPath);
|
28
|
+
await mkdir(dbDir, { recursive: true });
|
29
|
+
logger.debug('Database directory ensured', { dbDir });
|
69
30
|
// Create database connection
|
70
|
-
this.db = new
|
31
|
+
this.db = new BetterSqlite3(this.dbPath);
|
71
32
|
logger.debug('Database connection established');
|
72
|
-
// Create
|
73
|
-
await this.
|
74
|
-
CREATE TABLE IF NOT EXISTS conversation_states (
|
75
|
-
conversation_id TEXT PRIMARY KEY,
|
76
|
-
project_path TEXT NOT NULL,
|
77
|
-
git_branch TEXT NOT NULL,
|
78
|
-
current_phase TEXT NOT NULL,
|
79
|
-
plan_file_path TEXT NOT NULL,
|
80
|
-
workflow_name TEXT DEFAULT 'waterfall',
|
81
|
-
git_commit_config TEXT, -- JSON string for GitCommitConfig
|
82
|
-
created_at TEXT NOT NULL,
|
83
|
-
updated_at TEXT NOT NULL
|
84
|
-
)
|
85
|
-
`);
|
86
|
-
// Create index for efficient lookups
|
87
|
-
await this.runQuery(`
|
88
|
-
CREATE INDEX IF NOT EXISTS idx_project_branch
|
89
|
-
ON conversation_states(project_path, git_branch)
|
90
|
-
`);
|
91
|
-
// Create interaction_logs table
|
92
|
-
await this.runQuery(`
|
93
|
-
CREATE TABLE IF NOT EXISTS interaction_logs (
|
94
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
95
|
-
conversation_id TEXT NOT NULL,
|
96
|
-
tool_name TEXT NOT NULL,
|
97
|
-
input_params TEXT NOT NULL,
|
98
|
-
response_data TEXT NOT NULL,
|
99
|
-
current_phase TEXT NOT NULL,
|
100
|
-
timestamp TEXT NOT NULL,
|
101
|
-
is_reset BOOLEAN DEFAULT FALSE,
|
102
|
-
reset_at TEXT,
|
103
|
-
FOREIGN KEY (conversation_id) REFERENCES conversation_states(conversation_id)
|
104
|
-
)
|
105
|
-
`);
|
106
|
-
// Create index for efficient lookups of interaction logs
|
107
|
-
await this.runQuery(`
|
108
|
-
CREATE INDEX IF NOT EXISTS idx_interaction_conversation_id
|
109
|
-
ON interaction_logs(conversation_id)
|
110
|
-
`);
|
111
|
-
// Run migrations to add any missing columns
|
112
|
-
await this.runMigrations();
|
33
|
+
// Create tables
|
34
|
+
await this.createTables();
|
113
35
|
logger.info('Database initialized successfully', { dbPath: this.dbPath });
|
114
36
|
}
|
115
37
|
catch (error) {
|
116
|
-
logger.error('Failed to initialize database', error
|
117
|
-
dbPath: this.dbPath,
|
118
|
-
});
|
38
|
+
logger.error('Failed to initialize database', error);
|
119
39
|
throw error;
|
120
40
|
}
|
121
41
|
}
|
122
42
|
/**
|
123
|
-
*
|
124
|
-
*/
|
125
|
-
runQuery(sql, params = []) {
|
126
|
-
return new Promise((resolve, reject) => {
|
127
|
-
if (!this.db) {
|
128
|
-
reject(new Error('Database not initialized'));
|
129
|
-
return;
|
130
|
-
}
|
131
|
-
this.db.run(sql, params, function (err) {
|
132
|
-
if (err) {
|
133
|
-
reject(err);
|
134
|
-
}
|
135
|
-
else {
|
136
|
-
resolve();
|
137
|
-
}
|
138
|
-
});
|
139
|
-
});
|
140
|
-
}
|
141
|
-
/**
|
142
|
-
* Helper method to get single row with promises
|
43
|
+
* Create database tables if they don't exist
|
143
44
|
*/
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
45
|
+
async createTables() {
|
46
|
+
if (!this.db) {
|
47
|
+
throw new Error('Database not initialized');
|
48
|
+
}
|
49
|
+
const createConversationStateTable = `
|
50
|
+
CREATE TABLE IF NOT EXISTS conversation_state (
|
51
|
+
conversationId TEXT PRIMARY KEY,
|
52
|
+
projectPath TEXT NOT NULL,
|
53
|
+
gitBranch TEXT NOT NULL,
|
54
|
+
currentPhase TEXT NOT NULL,
|
55
|
+
planFilePath TEXT NOT NULL,
|
56
|
+
workflowName TEXT NOT NULL,
|
57
|
+
gitCommitConfig TEXT,
|
58
|
+
requireReviewsBeforePhaseTransition INTEGER NOT NULL DEFAULT 0,
|
59
|
+
createdAt TEXT NOT NULL,
|
60
|
+
updatedAt TEXT NOT NULL
|
61
|
+
)
|
62
|
+
`;
|
63
|
+
const createInteractionLogTable = `
|
64
|
+
CREATE TABLE IF NOT EXISTS interaction_log (
|
65
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
66
|
+
conversationId TEXT NOT NULL,
|
67
|
+
toolName TEXT NOT NULL,
|
68
|
+
inputParams TEXT NOT NULL,
|
69
|
+
responseData TEXT NOT NULL,
|
70
|
+
currentPhase TEXT NOT NULL,
|
71
|
+
timestamp TEXT NOT NULL,
|
72
|
+
isReset INTEGER DEFAULT 0,
|
73
|
+
resetAt TEXT,
|
74
|
+
FOREIGN KEY (conversationId) REFERENCES conversation_state(conversationId)
|
75
|
+
)
|
76
|
+
`;
|
77
|
+
this.db.exec(createConversationStateTable);
|
78
|
+
this.db.exec(createInteractionLogTable);
|
79
|
+
logger.debug('Tables created successfully');
|
159
80
|
}
|
160
81
|
/**
|
161
|
-
*
|
82
|
+
* Save conversation state to database
|
162
83
|
*/
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
84
|
+
async saveConversationState(state) {
|
85
|
+
if (!this.db) {
|
86
|
+
throw new Error('Database not initialized');
|
87
|
+
}
|
88
|
+
const stmt = this.db.prepare(`
|
89
|
+
INSERT OR REPLACE INTO conversation_state
|
90
|
+
(conversationId, projectPath, gitBranch, currentPhase, planFilePath, workflowName,
|
91
|
+
gitCommitConfig, requireReviewsBeforePhaseTransition, createdAt, updatedAt)
|
92
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
93
|
+
`);
|
94
|
+
stmt.run(state.conversationId, state.projectPath, state.gitBranch, state.currentPhase, state.planFilePath, state.workflowName, state.gitCommitConfig ? JSON.stringify(state.gitCommitConfig) : null, state.requireReviewsBeforePhaseTransition ? 1 : 0, state.createdAt, state.updatedAt);
|
95
|
+
logger.debug('Conversation state saved', {
|
96
|
+
conversationId: state.conversationId,
|
97
|
+
workflowName: state.workflowName,
|
98
|
+
currentPhase: state.currentPhase,
|
177
99
|
});
|
178
100
|
}
|
179
101
|
/**
|
180
|
-
*
|
102
|
+
* Load conversation state from database
|
181
103
|
*/
|
182
|
-
async
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
}
|
104
|
+
async loadConversationState(conversationId) {
|
105
|
+
if (!this.db) {
|
106
|
+
throw new Error('Database not initialized');
|
107
|
+
}
|
108
|
+
const stmt = this.db.prepare('SELECT * FROM conversation_state WHERE conversationId = ?');
|
109
|
+
const row = stmt.get(conversationId);
|
110
|
+
if (row) {
|
190
111
|
const state = {
|
191
|
-
conversationId:
|
192
|
-
projectPath:
|
193
|
-
gitBranch:
|
194
|
-
currentPhase:
|
195
|
-
planFilePath:
|
196
|
-
workflowName:
|
197
|
-
gitCommitConfig:
|
198
|
-
|
199
|
-
|
200
|
-
|
112
|
+
conversationId: row.conversationId,
|
113
|
+
projectPath: row.projectPath,
|
114
|
+
gitBranch: row.gitBranch,
|
115
|
+
currentPhase: row.currentPhase,
|
116
|
+
planFilePath: row.planFilePath,
|
117
|
+
workflowName: row.workflowName,
|
118
|
+
gitCommitConfig: row.gitCommitConfig
|
119
|
+
? JSON.parse(row.gitCommitConfig)
|
120
|
+
: undefined,
|
121
|
+
requireReviewsBeforePhaseTransition: row.requireReviewsBeforePhaseTransition === 1,
|
122
|
+
createdAt: row.createdAt,
|
123
|
+
updatedAt: row.updatedAt,
|
201
124
|
};
|
202
|
-
logger.debug('Conversation state
|
125
|
+
logger.debug('Conversation state loaded', {
|
203
126
|
conversationId,
|
127
|
+
workflowName: state.workflowName,
|
204
128
|
currentPhase: state.currentPhase,
|
205
|
-
projectPath: state.projectPath,
|
206
129
|
});
|
207
130
|
return state;
|
208
131
|
}
|
209
|
-
|
210
|
-
|
211
|
-
conversationId,
|
212
|
-
});
|
213
|
-
throw error;
|
214
|
-
}
|
132
|
+
logger.debug('No conversation state found', { conversationId });
|
133
|
+
return null;
|
215
134
|
}
|
216
135
|
/**
|
217
|
-
*
|
136
|
+
* Get conversation state by ID (alias for loadConversationState)
|
218
137
|
*/
|
219
|
-
async
|
220
|
-
|
221
|
-
conversationId: state.conversationId,
|
222
|
-
currentPhase: state.currentPhase,
|
223
|
-
projectPath: state.projectPath,
|
224
|
-
workflowName: state.workflowName,
|
225
|
-
});
|
226
|
-
try {
|
227
|
-
await this.runQuery(`INSERT OR REPLACE INTO conversation_states (
|
228
|
-
conversation_id, project_path, git_branch, current_phase,
|
229
|
-
plan_file_path, workflow_name, git_commit_config, require_reviews_before_phase_transition, created_at, updated_at
|
230
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
231
|
-
state.conversationId,
|
232
|
-
state.projectPath,
|
233
|
-
state.gitBranch,
|
234
|
-
state.currentPhase,
|
235
|
-
state.planFilePath,
|
236
|
-
state.workflowName,
|
237
|
-
state.gitCommitConfig ? JSON.stringify(state.gitCommitConfig) : null,
|
238
|
-
state.requireReviewsBeforePhaseTransition,
|
239
|
-
state.createdAt,
|
240
|
-
state.updatedAt,
|
241
|
-
]);
|
242
|
-
logger.info('Conversation state saved successfully', {
|
243
|
-
conversationId: state.conversationId,
|
244
|
-
currentPhase: state.currentPhase,
|
245
|
-
});
|
246
|
-
}
|
247
|
-
catch (error) {
|
248
|
-
logger.error('Failed to save conversation state', error, {
|
249
|
-
conversationId: state.conversationId,
|
250
|
-
});
|
251
|
-
throw error;
|
252
|
-
}
|
138
|
+
async getConversationState(conversationId) {
|
139
|
+
return this.loadConversationState(conversationId);
|
253
140
|
}
|
254
141
|
/**
|
255
|
-
*
|
142
|
+
* List all conversation states
|
256
143
|
*/
|
257
|
-
async
|
258
|
-
|
259
|
-
|
260
|
-
return null;
|
144
|
+
async listConversationStates() {
|
145
|
+
if (!this.db) {
|
146
|
+
throw new Error('Database not initialized');
|
261
147
|
}
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
148
|
+
const stmt = this.db.prepare('SELECT * FROM conversation_state ORDER BY updatedAt DESC');
|
149
|
+
const rows = stmt.all();
|
150
|
+
const states = rows.map(row => ({
|
151
|
+
conversationId: row.conversationId,
|
152
|
+
projectPath: row.projectPath,
|
153
|
+
gitBranch: row.gitBranch,
|
154
|
+
currentPhase: row.currentPhase,
|
155
|
+
planFilePath: row.planFilePath,
|
156
|
+
workflowName: row.workflowName,
|
157
|
+
gitCommitConfig: row.gitCommitConfig
|
158
|
+
? JSON.parse(row.gitCommitConfig)
|
159
|
+
: undefined,
|
160
|
+
requireReviewsBeforePhaseTransition: row.requireReviewsBeforePhaseTransition === 1,
|
161
|
+
createdAt: row.createdAt,
|
162
|
+
updatedAt: row.updatedAt,
|
163
|
+
}));
|
164
|
+
logger.debug('Listed conversation states', { count: states.length });
|
165
|
+
return states;
|
274
166
|
}
|
275
167
|
/**
|
276
168
|
* Delete conversation state
|
277
169
|
*/
|
278
170
|
async deleteConversationState(conversationId) {
|
279
|
-
|
171
|
+
if (!this.db) {
|
172
|
+
throw new Error('Database not initialized');
|
173
|
+
}
|
174
|
+
const stmt = this.db.prepare('DELETE FROM conversation_state WHERE conversationId = ?');
|
175
|
+
const result = stmt.run(conversationId);
|
176
|
+
const deleted = result.changes > 0;
|
177
|
+
logger.debug('Conversation state deletion', { conversationId, deleted });
|
178
|
+
return deleted;
|
280
179
|
}
|
281
180
|
/**
|
282
181
|
* Log an interaction to the database
|
283
182
|
*/
|
284
183
|
async logInteraction(log) {
|
285
|
-
|
184
|
+
if (!this.db) {
|
185
|
+
throw new Error('Database not initialized');
|
186
|
+
}
|
187
|
+
const stmt = this.db.prepare(`
|
188
|
+
INSERT INTO interaction_log
|
189
|
+
(conversationId, toolName, inputParams, responseData, currentPhase, timestamp, isReset, resetAt)
|
190
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
191
|
+
`);
|
192
|
+
stmt.run(log.conversationId, log.toolName, log.inputParams, log.responseData, log.currentPhase, log.timestamp, log.isReset ? 1 : 0, log.resetAt || null);
|
193
|
+
logger.debug('Interaction logged', {
|
286
194
|
conversationId: log.conversationId,
|
287
195
|
toolName: log.toolName,
|
196
|
+
timestamp: log.timestamp,
|
288
197
|
});
|
289
|
-
try {
|
290
|
-
await this.runQuery(`INSERT INTO interaction_logs (
|
291
|
-
conversation_id, tool_name, input_params, response_data,
|
292
|
-
current_phase, timestamp
|
293
|
-
) VALUES (?, ?, ?, ?, ?, ?)`, [
|
294
|
-
log.conversationId,
|
295
|
-
log.toolName,
|
296
|
-
log.inputParams,
|
297
|
-
log.responseData,
|
298
|
-
log.currentPhase,
|
299
|
-
log.timestamp,
|
300
|
-
]);
|
301
|
-
logger.debug('Interaction logged successfully', {
|
302
|
-
conversationId: log.conversationId,
|
303
|
-
toolName: log.toolName,
|
304
|
-
timestamp: log.timestamp,
|
305
|
-
});
|
306
|
-
}
|
307
|
-
catch (error) {
|
308
|
-
logger.error('Failed to log interaction', error, {
|
309
|
-
conversationId: log.conversationId,
|
310
|
-
});
|
311
|
-
throw error;
|
312
|
-
}
|
313
198
|
}
|
314
199
|
/**
|
315
|
-
* Get
|
200
|
+
* Get interactions by conversation ID
|
316
201
|
*/
|
317
202
|
async getInteractionsByConversationId(conversationId) {
|
318
|
-
|
319
|
-
|
320
|
-
const rows = await this.getAllRows('SELECT * FROM interaction_logs WHERE conversation_id = ? ORDER BY timestamp ASC', [conversationId]);
|
321
|
-
const logs = rows.map(row => ({
|
322
|
-
id: typeof row.id === 'number' ? row.id : undefined,
|
323
|
-
conversationId: validateString(row.conversation_id, 'conversation_id'),
|
324
|
-
toolName: validateString(row.tool_name, 'tool_name'),
|
325
|
-
inputParams: validateString(row.input_params, 'input_params'),
|
326
|
-
responseData: validateString(row.response_data, 'response_data'),
|
327
|
-
currentPhase: validateString(row.current_phase, 'current_phase'),
|
328
|
-
timestamp: validateString(row.timestamp, 'timestamp'),
|
329
|
-
}));
|
330
|
-
logger.debug('Retrieved interaction logs', {
|
331
|
-
conversationId,
|
332
|
-
count: logs.length,
|
333
|
-
});
|
334
|
-
return logs;
|
335
|
-
}
|
336
|
-
catch (error) {
|
337
|
-
logger.error('Failed to get interaction logs', error, {
|
338
|
-
conversationId,
|
339
|
-
});
|
340
|
-
throw error;
|
341
|
-
}
|
342
|
-
}
|
343
|
-
/**
|
344
|
-
* Run database migrations to add new columns
|
345
|
-
*/
|
346
|
-
async runMigrations() {
|
347
|
-
logger.debug('Running database migrations');
|
348
|
-
try {
|
349
|
-
// Check if interaction_logs table exists first
|
350
|
-
const tables = await this.getAllRows("SELECT name FROM sqlite_master WHERE type='table' AND name='interaction_logs'");
|
351
|
-
if (tables.length > 0) {
|
352
|
-
// Table exists, check for missing columns
|
353
|
-
const tableInfo = await this.getAllRows('PRAGMA table_info(interaction_logs)');
|
354
|
-
const hasIsReset = tableInfo.some((col) => col.name === 'is_reset');
|
355
|
-
const hasResetAt = tableInfo.some((col) => col.name === 'reset_at');
|
356
|
-
if (!hasIsReset) {
|
357
|
-
logger.info('Adding is_reset column to interaction_logs table');
|
358
|
-
await this.runQuery('ALTER TABLE interaction_logs ADD COLUMN is_reset BOOLEAN DEFAULT FALSE');
|
359
|
-
}
|
360
|
-
if (!hasResetAt) {
|
361
|
-
logger.info('Adding reset_at column to interaction_logs table');
|
362
|
-
await this.runQuery('ALTER TABLE interaction_logs ADD COLUMN reset_at TEXT');
|
363
|
-
}
|
364
|
-
}
|
365
|
-
// Check if conversation_states table exists and has workflow_name column
|
366
|
-
const conversationTables = await this.getAllRows("SELECT name FROM sqlite_master WHERE type='table' AND name='conversation_states'");
|
367
|
-
if (conversationTables.length > 0) {
|
368
|
-
const conversationTableInfo = (await this.getAllRows('PRAGMA table_info(conversation_states)'));
|
369
|
-
const hasWorkflowName = conversationTableInfo.some((col) => col.name === 'workflow_name');
|
370
|
-
const hasGitCommitConfig = conversationTableInfo.some((col) => col.name === 'git_commit_config');
|
371
|
-
const hasRequireReviews = conversationTableInfo.some((col) => col.name === 'require_reviews_before_phase_transition');
|
372
|
-
if (!hasWorkflowName) {
|
373
|
-
logger.info('Adding workflow_name column to conversation_states table');
|
374
|
-
await this.runQuery("ALTER TABLE conversation_states ADD COLUMN workflow_name TEXT DEFAULT 'waterfall'");
|
375
|
-
}
|
376
|
-
if (!hasGitCommitConfig) {
|
377
|
-
logger.info('Adding git_commit_config column to conversation_states table');
|
378
|
-
await this.runQuery('ALTER TABLE conversation_states ADD COLUMN git_commit_config TEXT');
|
379
|
-
}
|
380
|
-
if (!hasRequireReviews) {
|
381
|
-
logger.info('Adding require_reviews_before_phase_transition column to conversation_states table');
|
382
|
-
await this.runQuery('ALTER TABLE conversation_states ADD COLUMN require_reviews_before_phase_transition BOOLEAN DEFAULT FALSE');
|
383
|
-
}
|
384
|
-
}
|
385
|
-
logger.debug('Database migrations completed successfully');
|
386
|
-
}
|
387
|
-
catch (error) {
|
388
|
-
logger.error('Failed to run database migrations', error);
|
389
|
-
throw error;
|
390
|
-
}
|
391
|
-
}
|
392
|
-
/**
|
393
|
-
* Soft delete interaction logs for a conversation
|
394
|
-
*/
|
395
|
-
async softDeleteInteractionLogs(conversationId, reason) {
|
396
|
-
logger.debug('Soft deleting interaction logs', { conversationId, reason });
|
397
|
-
try {
|
398
|
-
const resetAt = new Date().toISOString();
|
399
|
-
await this.runQuery('UPDATE interaction_logs SET is_reset = TRUE, reset_at = ? WHERE conversation_id = ? AND is_reset = FALSE', [resetAt, conversationId]);
|
400
|
-
logger.info('Interaction logs soft deleted successfully', {
|
401
|
-
conversationId,
|
402
|
-
reason,
|
403
|
-
resetAt,
|
404
|
-
});
|
405
|
-
}
|
406
|
-
catch (error) {
|
407
|
-
logger.error('Failed to soft delete interaction logs', error, {
|
408
|
-
conversationId,
|
409
|
-
});
|
410
|
-
throw error;
|
203
|
+
if (!this.db) {
|
204
|
+
throw new Error('Database not initialized');
|
411
205
|
}
|
206
|
+
const stmt = this.db.prepare(`
|
207
|
+
SELECT * FROM interaction_log
|
208
|
+
WHERE conversationId = ? AND (isReset = 0 OR isReset IS NULL)
|
209
|
+
ORDER BY timestamp ASC
|
210
|
+
`);
|
211
|
+
const rows = stmt.all(conversationId);
|
212
|
+
const logs = rows.map(row => ({
|
213
|
+
id: row.id,
|
214
|
+
conversationId: row.conversationId,
|
215
|
+
toolName: row.toolName,
|
216
|
+
inputParams: row.inputParams,
|
217
|
+
responseData: row.responseData,
|
218
|
+
currentPhase: row.currentPhase,
|
219
|
+
timestamp: row.timestamp,
|
220
|
+
isReset: row.isReset === 1,
|
221
|
+
resetAt: row.resetAt,
|
222
|
+
}));
|
223
|
+
logger.debug('Retrieved interaction logs', {
|
224
|
+
conversationId,
|
225
|
+
count: logs.length,
|
226
|
+
});
|
227
|
+
return logs;
|
412
228
|
}
|
413
229
|
/**
|
414
|
-
*
|
230
|
+
* Soft delete interaction logs by marking them as reset
|
415
231
|
*/
|
416
|
-
async
|
417
|
-
|
418
|
-
|
419
|
-
const rows = await this.getAllRows('SELECT * FROM interaction_logs WHERE conversation_id = ? AND (is_reset = FALSE OR is_reset IS NULL) ORDER BY timestamp ASC', [conversationId]);
|
420
|
-
const logs = rows.map(row => mapRowToInteractionLog({
|
421
|
-
id: row.id,
|
422
|
-
conversationId: row.conversation_id,
|
423
|
-
toolName: row.tool_name,
|
424
|
-
inputParams: row.input_params,
|
425
|
-
responseData: row.response_data,
|
426
|
-
currentPhase: row.current_phase,
|
427
|
-
timestamp: row.timestamp,
|
428
|
-
}));
|
429
|
-
logger.debug('Retrieved active interaction logs', {
|
430
|
-
conversationId,
|
431
|
-
count: logs.length,
|
432
|
-
});
|
433
|
-
return logs;
|
232
|
+
async softDeleteInteractionLogs(conversationId) {
|
233
|
+
if (!this.db) {
|
234
|
+
throw new Error('Database not initialized');
|
434
235
|
}
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
* Get all interaction logs including reset ones for a conversation
|
444
|
-
*/
|
445
|
-
async getAllInteractionLogsIncludingReset(conversationId) {
|
446
|
-
logger.debug('Getting all interaction logs including reset', {
|
236
|
+
const resetAt = new Date().toISOString();
|
237
|
+
const stmt = this.db.prepare(`
|
238
|
+
UPDATE interaction_log
|
239
|
+
SET isReset = 1, resetAt = ?
|
240
|
+
WHERE conversationId = ? AND (isReset = 0 OR isReset IS NULL)
|
241
|
+
`);
|
242
|
+
const result = stmt.run(resetAt, conversationId);
|
243
|
+
logger.debug('Soft deleted interaction logs', {
|
447
244
|
conversationId,
|
245
|
+
affectedRows: result.changes,
|
448
246
|
});
|
449
|
-
try {
|
450
|
-
const rows = await this.getAllRows('SELECT * FROM interaction_logs WHERE conversation_id = ? ORDER BY timestamp ASC', [conversationId]);
|
451
|
-
const logs = rows.map(row => mapRowToInteractionLog({
|
452
|
-
id: row.id,
|
453
|
-
conversationId: row.conversation_id,
|
454
|
-
toolName: row.tool_name,
|
455
|
-
inputParams: row.input_params,
|
456
|
-
responseData: row.response_data,
|
457
|
-
currentPhase: row.current_phase,
|
458
|
-
timestamp: row.timestamp,
|
459
|
-
isReset: row.is_reset,
|
460
|
-
resetAt: row.reset_at,
|
461
|
-
}));
|
462
|
-
logger.debug('Retrieved all interaction logs including reset', {
|
463
|
-
conversationId,
|
464
|
-
count: logs.length,
|
465
|
-
resetCount: logs.filter(log => log.isReset).length,
|
466
|
-
});
|
467
|
-
return logs;
|
468
|
-
}
|
469
|
-
catch (error) {
|
470
|
-
logger.error('Failed to get all interaction logs including reset', error, { conversationId });
|
471
|
-
throw error;
|
472
|
-
}
|
473
247
|
}
|
474
248
|
/**
|
475
249
|
* Close database connection
|
476
250
|
*/
|
477
251
|
async close() {
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
resolve();
|
490
|
-
}
|
491
|
-
});
|
492
|
-
}
|
493
|
-
else {
|
494
|
-
logger.debug('Database connection already closed');
|
495
|
-
resolve();
|
496
|
-
}
|
497
|
-
});
|
252
|
+
if (this.db) {
|
253
|
+
this.db.close();
|
254
|
+
this.db = null;
|
255
|
+
logger.debug('Database connection closed');
|
256
|
+
}
|
257
|
+
}
|
258
|
+
/**
|
259
|
+
* Check if database is initialized
|
260
|
+
*/
|
261
|
+
isInitialized() {
|
262
|
+
return this.db !== null;
|
498
263
|
}
|
499
264
|
}
|
500
265
|
//# sourceMappingURL=database.js.map
|