claudia-orchestrator 0.1.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/LICENSE +201 -0
- package/README.md +109 -0
- package/dist/cli-parser.d.ts +11 -0
- package/dist/cli-parser.d.ts.map +1 -0
- package/dist/cli-parser.js +57 -0
- package/dist/cli-parser.js.map +1 -0
- package/dist/cui-server.d.ts +69 -0
- package/dist/cui-server.d.ts.map +1 -0
- package/dist/cui-server.js +705 -0
- package/dist/cui-server.js.map +1 -0
- package/dist/mcp-server/claudia-tools.d.ts +15 -0
- package/dist/mcp-server/claudia-tools.d.ts.map +1 -0
- package/dist/mcp-server/claudia-tools.js +366 -0
- package/dist/mcp-server/claudia-tools.js.map +1 -0
- package/dist/mcp-server/index.d.ts +3 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +176 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +18 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +136 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors-setup.d.ts +7 -0
- package/dist/middleware/cors-setup.d.ts.map +1 -0
- package/dist/middleware/cors-setup.js +8 -0
- package/dist/middleware/cors-setup.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +4 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +27 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/query-parser.d.ts +11 -0
- package/dist/middleware/query-parser.d.ts.map +1 -0
- package/dist/middleware/query-parser.js +68 -0
- package/dist/middleware/query-parser.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +4 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +29 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/process-daemon/index.d.ts +14 -0
- package/dist/process-daemon/index.d.ts.map +1 -0
- package/dist/process-daemon/index.js +51 -0
- package/dist/process-daemon/index.js.map +1 -0
- package/dist/process-daemon/process-daemon.d.ts +78 -0
- package/dist/process-daemon/process-daemon.d.ts.map +1 -0
- package/dist/process-daemon/process-daemon.js +568 -0
- package/dist/process-daemon/process-daemon.js.map +1 -0
- package/dist/process-daemon/process-manager-client.d.ts +108 -0
- package/dist/process-daemon/process-manager-client.d.ts.map +1 -0
- package/dist/process-daemon/process-manager-client.js +314 -0
- package/dist/process-daemon/process-manager-client.js.map +1 -0
- package/dist/process-daemon/process-manager-interface.d.ts +47 -0
- package/dist/process-daemon/process-manager-interface.d.ts.map +1 -0
- package/dist/process-daemon/process-manager-interface.js +8 -0
- package/dist/process-daemon/process-manager-interface.js.map +1 -0
- package/dist/process-daemon/test-daemon.d.ts +12 -0
- package/dist/process-daemon/test-daemon.d.ts.map +1 -0
- package/dist/process-daemon/test-daemon.js +81 -0
- package/dist/process-daemon/test-daemon.js.map +1 -0
- package/dist/process-daemon/types.d.ts +85 -0
- package/dist/process-daemon/types.d.ts.map +1 -0
- package/dist/process-daemon/types.js +8 -0
- package/dist/process-daemon/types.js.map +1 -0
- package/dist/routes/claudia.routes.d.ts +10 -0
- package/dist/routes/claudia.routes.d.ts.map +1 -0
- package/dist/routes/claudia.routes.js +123 -0
- package/dist/routes/claudia.routes.js.map +1 -0
- package/dist/routes/config.routes.d.ts +4 -0
- package/dist/routes/config.routes.d.ts.map +1 -0
- package/dist/routes/config.routes.js +27 -0
- package/dist/routes/config.routes.js.map +1 -0
- package/dist/routes/conversation.routes.d.ts +8 -0
- package/dist/routes/conversation.routes.d.ts.map +1 -0
- package/dist/routes/conversation.routes.js +870 -0
- package/dist/routes/conversation.routes.js.map +1 -0
- package/dist/routes/filesystem.routes.d.ts +4 -0
- package/dist/routes/filesystem.routes.d.ts.map +1 -0
- package/dist/routes/filesystem.routes.js +86 -0
- package/dist/routes/filesystem.routes.js.map +1 -0
- package/dist/routes/gemini.routes.d.ts +4 -0
- package/dist/routes/gemini.routes.d.ts.map +1 -0
- package/dist/routes/gemini.routes.js +93 -0
- package/dist/routes/gemini.routes.js.map +1 -0
- package/dist/routes/insights.routes.d.ts +17 -0
- package/dist/routes/insights.routes.d.ts.map +1 -0
- package/dist/routes/insights.routes.js +417 -0
- package/dist/routes/insights.routes.js.map +1 -0
- package/dist/routes/license.routes.d.ts +3 -0
- package/dist/routes/license.routes.d.ts.map +1 -0
- package/dist/routes/license.routes.js +111 -0
- package/dist/routes/license.routes.js.map +1 -0
- package/dist/routes/log.routes.d.ts +3 -0
- package/dist/routes/log.routes.d.ts.map +1 -0
- package/dist/routes/log.routes.js +65 -0
- package/dist/routes/log.routes.js.map +1 -0
- package/dist/routes/notifications.routes.d.ts +4 -0
- package/dist/routes/notifications.routes.d.ts.map +1 -0
- package/dist/routes/notifications.routes.js +71 -0
- package/dist/routes/notifications.routes.js.map +1 -0
- package/dist/routes/permission.routes.d.ts +4 -0
- package/dist/routes/permission.routes.d.ts.map +1 -0
- package/dist/routes/permission.routes.js +116 -0
- package/dist/routes/permission.routes.js.map +1 -0
- package/dist/routes/question.routes.d.ts +8 -0
- package/dist/routes/question.routes.d.ts.map +1 -0
- package/dist/routes/question.routes.js +82 -0
- package/dist/routes/question.routes.js.map +1 -0
- package/dist/routes/streaming.routes.d.ts +4 -0
- package/dist/routes/streaming.routes.d.ts.map +1 -0
- package/dist/routes/streaming.routes.js +28 -0
- package/dist/routes/streaming.routes.js.map +1 -0
- package/dist/routes/system.routes.d.ts +5 -0
- package/dist/routes/system.routes.d.ts.map +1 -0
- package/dist/routes/system.routes.js +103 -0
- package/dist/routes/system.routes.js.map +1 -0
- package/dist/routes/working-directories.routes.d.ts +4 -0
- package/dist/routes/working-directories.routes.d.ts.map +1 -0
- package/dist/routes/working-directories.routes.js +25 -0
- package/dist/routes/working-directories.routes.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/services/ToolMetricsService.d.ts +53 -0
- package/dist/services/ToolMetricsService.d.ts.map +1 -0
- package/dist/services/ToolMetricsService.js +230 -0
- package/dist/services/ToolMetricsService.js.map +1 -0
- package/dist/services/anthropic-service.d.ts +186 -0
- package/dist/services/anthropic-service.d.ts.map +1 -0
- package/dist/services/anthropic-service.js +1132 -0
- package/dist/services/anthropic-service.js.map +1 -0
- package/dist/services/claude-history-reader.d.ts +126 -0
- package/dist/services/claude-history-reader.d.ts.map +1 -0
- package/dist/services/claude-history-reader.js +717 -0
- package/dist/services/claude-history-reader.js.map +1 -0
- package/dist/services/claude-process-manager.d.ts +108 -0
- package/dist/services/claude-process-manager.d.ts.map +1 -0
- package/dist/services/claude-process-manager.js +909 -0
- package/dist/services/claude-process-manager.js.map +1 -0
- package/dist/services/claude-router-service.d.ts +19 -0
- package/dist/services/claude-router-service.d.ts.map +1 -0
- package/dist/services/claude-router-service.js +160 -0
- package/dist/services/claude-router-service.js.map +1 -0
- package/dist/services/claudia-service.d.ts +77 -0
- package/dist/services/claudia-service.d.ts.map +1 -0
- package/dist/services/claudia-service.js +194 -0
- package/dist/services/claudia-service.js.map +1 -0
- package/dist/services/commands-service.d.ts +18 -0
- package/dist/services/commands-service.d.ts.map +1 -0
- package/dist/services/commands-service.js +76 -0
- package/dist/services/commands-service.js.map +1 -0
- package/dist/services/config-service.d.ts +68 -0
- package/dist/services/config-service.d.ts.map +1 -0
- package/dist/services/config-service.js +429 -0
- package/dist/services/config-service.js.map +1 -0
- package/dist/services/conversation-cache.d.ts +86 -0
- package/dist/services/conversation-cache.d.ts.map +1 -0
- package/dist/services/conversation-cache.js +235 -0
- package/dist/services/conversation-cache.js.map +1 -0
- package/dist/services/conversation-status-manager.d.ts +98 -0
- package/dist/services/conversation-status-manager.d.ts.map +1 -0
- package/dist/services/conversation-status-manager.js +295 -0
- package/dist/services/conversation-status-manager.js.map +1 -0
- package/dist/services/cost-tracker.d.ts +87 -0
- package/dist/services/cost-tracker.d.ts.map +1 -0
- package/dist/services/cost-tracker.js +335 -0
- package/dist/services/cost-tracker.js.map +1 -0
- package/dist/services/file-system-service.d.ts +61 -0
- package/dist/services/file-system-service.d.ts.map +1 -0
- package/dist/services/file-system-service.js +348 -0
- package/dist/services/file-system-service.js.map +1 -0
- package/dist/services/gemini-service.d.ts +72 -0
- package/dist/services/gemini-service.d.ts.map +1 -0
- package/dist/services/gemini-service.js +431 -0
- package/dist/services/gemini-service.js.map +1 -0
- package/dist/services/insight-queue.d.ts +99 -0
- package/dist/services/insight-queue.d.ts.map +1 -0
- package/dist/services/insight-queue.js +277 -0
- package/dist/services/insight-queue.js.map +1 -0
- package/dist/services/insights-service.d.ts +102 -0
- package/dist/services/insights-service.d.ts.map +1 -0
- package/dist/services/insights-service.js +1152 -0
- package/dist/services/insights-service.js.map +1 -0
- package/dist/services/json-lines-parser.d.ts +19 -0
- package/dist/services/json-lines-parser.d.ts.map +1 -0
- package/dist/services/json-lines-parser.js +56 -0
- package/dist/services/json-lines-parser.js.map +1 -0
- package/dist/services/license-service.d.ts +69 -0
- package/dist/services/license-service.d.ts.map +1 -0
- package/dist/services/license-service.js +330 -0
- package/dist/services/license-service.js.map +1 -0
- package/dist/services/log-formatter.d.ts +5 -0
- package/dist/services/log-formatter.d.ts.map +1 -0
- package/dist/services/log-formatter.js +77 -0
- package/dist/services/log-formatter.js.map +1 -0
- package/dist/services/log-stream-buffer.d.ts +11 -0
- package/dist/services/log-stream-buffer.d.ts.map +1 -0
- package/dist/services/log-stream-buffer.js +36 -0
- package/dist/services/log-stream-buffer.js.map +1 -0
- package/dist/services/logger.d.ts +71 -0
- package/dist/services/logger.d.ts.map +1 -0
- package/dist/services/logger.js +215 -0
- package/dist/services/logger.js.map +1 -0
- package/dist/services/mcp-config-generator.d.ts +32 -0
- package/dist/services/mcp-config-generator.d.ts.map +1 -0
- package/dist/services/mcp-config-generator.js +126 -0
- package/dist/services/mcp-config-generator.js.map +1 -0
- package/dist/services/message-filter.d.ts +22 -0
- package/dist/services/message-filter.d.ts.map +1 -0
- package/dist/services/message-filter.js +57 -0
- package/dist/services/message-filter.js.map +1 -0
- package/dist/services/notification-service.d.ts +45 -0
- package/dist/services/notification-service.d.ts.map +1 -0
- package/dist/services/notification-service.js +184 -0
- package/dist/services/notification-service.js.map +1 -0
- package/dist/services/permission-tracker.d.ts +67 -0
- package/dist/services/permission-tracker.d.ts.map +1 -0
- package/dist/services/permission-tracker.js +161 -0
- package/dist/services/permission-tracker.js.map +1 -0
- package/dist/services/process-manager-factory.d.ts +81 -0
- package/dist/services/process-manager-factory.d.ts.map +1 -0
- package/dist/services/process-manager-factory.js +211 -0
- package/dist/services/process-manager-factory.js.map +1 -0
- package/dist/services/question-tracker.d.ts +47 -0
- package/dist/services/question-tracker.d.ts.map +1 -0
- package/dist/services/question-tracker.js +105 -0
- package/dist/services/question-tracker.js.map +1 -0
- package/dist/services/session-activity-watcher.d.ts +33 -0
- package/dist/services/session-activity-watcher.d.ts.map +1 -0
- package/dist/services/session-activity-watcher.js +194 -0
- package/dist/services/session-activity-watcher.js.map +1 -0
- package/dist/services/session-info-service.d.ts +228 -0
- package/dist/services/session-info-service.d.ts.map +1 -0
- package/dist/services/session-info-service.js +920 -0
- package/dist/services/session-info-service.js.map +1 -0
- package/dist/services/session-insights-service.d.ts +119 -0
- package/dist/services/session-insights-service.d.ts.map +1 -0
- package/dist/services/session-insights-service.js +889 -0
- package/dist/services/session-insights-service.js.map +1 -0
- package/dist/services/stream-manager.d.ts +62 -0
- package/dist/services/stream-manager.d.ts.map +1 -0
- package/dist/services/stream-manager.js +239 -0
- package/dist/services/stream-manager.js.map +1 -0
- package/dist/services/web-push-service.d.ts +48 -0
- package/dist/services/web-push-service.d.ts.map +1 -0
- package/dist/services/web-push-service.js +186 -0
- package/dist/services/web-push-service.js.map +1 -0
- package/dist/services/working-directories-service.d.ts +19 -0
- package/dist/services/working-directories-service.d.ts.map +1 -0
- package/dist/services/working-directories-service.js +103 -0
- package/dist/services/working-directories-service.js.map +1 -0
- package/dist/types/config.d.ts +111 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +14 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/express.d.ts +5 -0
- package/dist/types/express.d.ts.map +1 -0
- package/dist/types/express.js +2 -0
- package/dist/types/express.js.map +1 -0
- package/dist/types/index.d.ts +325 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/insights.d.ts +99 -0
- package/dist/types/insights.d.ts.map +1 -0
- package/dist/types/insights.js +7 -0
- package/dist/types/insights.js.map +1 -0
- package/dist/types/license.d.ts +70 -0
- package/dist/types/license.d.ts.map +1 -0
- package/dist/types/license.js +5 -0
- package/dist/types/license.js.map +1 -0
- package/dist/types/router-config.d.ts +13 -0
- package/dist/types/router-config.d.ts.map +1 -0
- package/dist/types/router-config.js +2 -0
- package/dist/types/router-config.js.map +1 -0
- package/dist/utils/constants.d.ts +26 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +28 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/machine-id.d.ts +7 -0
- package/dist/utils/machine-id.d.ts.map +1 -0
- package/dist/utils/machine-id.js +76 -0
- package/dist/utils/machine-id.js.map +1 -0
- package/dist/utils/server-startup.d.ts +13 -0
- package/dist/utils/server-startup.d.ts.map +1 -0
- package/dist/utils/server-startup.js +20 -0
- package/dist/utils/server-startup.js.map +1 -0
- package/dist/web/assets/main-DAc2rjJ2.css +1 -0
- package/dist/web/assets/main-DvlZ02mT.js +137 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/favicon.svg +22 -0
- package/dist/web/icon-192x192.png +0 -0
- package/dist/web/icon-512x512.png +0 -0
- package/dist/web/index.html +36 -0
- package/dist/web/manifest.json +61 -0
- package/package.json +174 -0
- package/scripts/postinstall.js +30 -0
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
import { CONFIG_DIR, CONFIG_DIR_NAME } from '../utils/constants.js';
|
|
5
|
+
import { createLogger } from './logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* SessionInfoService manages session information using SQLite backend
|
|
8
|
+
* Stores session metadata including custom names in ~/.cui/session-info.db
|
|
9
|
+
* Provides fast lookups and updates for session-specific data
|
|
10
|
+
*/
|
|
11
|
+
export class SessionInfoService {
|
|
12
|
+
static instance;
|
|
13
|
+
logger;
|
|
14
|
+
dbPath;
|
|
15
|
+
configDir;
|
|
16
|
+
isInitialized = false;
|
|
17
|
+
db;
|
|
18
|
+
getSessionStmt;
|
|
19
|
+
insertSessionStmt;
|
|
20
|
+
updateSessionStmt;
|
|
21
|
+
deleteSessionStmt;
|
|
22
|
+
getAllStmt;
|
|
23
|
+
countStmt;
|
|
24
|
+
archiveAllStmt;
|
|
25
|
+
setMetadataStmt;
|
|
26
|
+
getMetadataStmt;
|
|
27
|
+
// Insights statements
|
|
28
|
+
getInsightsStmt;
|
|
29
|
+
upsertInsightsStmt;
|
|
30
|
+
getAllInsightsStmt;
|
|
31
|
+
markInsightsStaleStmt;
|
|
32
|
+
getStaleInsightsStmt;
|
|
33
|
+
constructor(customConfigDir) {
|
|
34
|
+
this.logger = createLogger('SessionInfoService');
|
|
35
|
+
this.initializePaths(customConfigDir);
|
|
36
|
+
}
|
|
37
|
+
static getInstance() {
|
|
38
|
+
if (!SessionInfoService.instance) {
|
|
39
|
+
SessionInfoService.instance = new SessionInfoService();
|
|
40
|
+
}
|
|
41
|
+
return SessionInfoService.instance;
|
|
42
|
+
}
|
|
43
|
+
static resetInstance() {
|
|
44
|
+
if (SessionInfoService.instance) {
|
|
45
|
+
SessionInfoService.instance.isInitialized = false;
|
|
46
|
+
}
|
|
47
|
+
SessionInfoService.instance = null;
|
|
48
|
+
}
|
|
49
|
+
initializePaths(customConfigDir) {
|
|
50
|
+
if (customConfigDir) {
|
|
51
|
+
if (customConfigDir === ':memory:') {
|
|
52
|
+
this.configDir = ':memory:';
|
|
53
|
+
this.dbPath = ':memory:';
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.configDir = path.join(customConfigDir, CONFIG_DIR_NAME);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.configDir = CONFIG_DIR;
|
|
60
|
+
}
|
|
61
|
+
this.dbPath = path.join(this.configDir, 'session-info.db');
|
|
62
|
+
this.logger.debug('Initializing paths', {
|
|
63
|
+
configDir: this.configDir,
|
|
64
|
+
dbPath: this.dbPath
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async initialize() {
|
|
68
|
+
if (this.isInitialized) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
if (this.dbPath !== ':memory:' && !fs.existsSync(this.configDir)) {
|
|
73
|
+
fs.mkdirSync(this.configDir, { recursive: true });
|
|
74
|
+
this.logger.debug('Created config directory', { dir: this.configDir });
|
|
75
|
+
}
|
|
76
|
+
this.db = new Database(this.dbPath);
|
|
77
|
+
this.db.pragma('journal_mode = WAL');
|
|
78
|
+
this.db.exec(`
|
|
79
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
80
|
+
session_id TEXT PRIMARY KEY,
|
|
81
|
+
custom_name TEXT NOT NULL DEFAULT '',
|
|
82
|
+
created_at TEXT NOT NULL,
|
|
83
|
+
updated_at TEXT NOT NULL,
|
|
84
|
+
version INTEGER NOT NULL,
|
|
85
|
+
pinned INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
archived INTEGER NOT NULL DEFAULT 0,
|
|
87
|
+
continuation_session_id TEXT NOT NULL DEFAULT '',
|
|
88
|
+
initial_commit_head TEXT NOT NULL DEFAULT '',
|
|
89
|
+
permission_mode TEXT NOT NULL DEFAULT 'default'
|
|
90
|
+
);
|
|
91
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
92
|
+
key TEXT PRIMARY KEY,
|
|
93
|
+
value TEXT NOT NULL
|
|
94
|
+
);
|
|
95
|
+
CREATE TABLE IF NOT EXISTS session_insights (
|
|
96
|
+
session_id TEXT PRIMARY KEY,
|
|
97
|
+
description TEXT,
|
|
98
|
+
purposes TEXT NOT NULL DEFAULT '[]',
|
|
99
|
+
milestones TEXT NOT NULL DEFAULT '[]',
|
|
100
|
+
recent_actions TEXT NOT NULL DEFAULT '[]',
|
|
101
|
+
progress_completed INTEGER NOT NULL DEFAULT 0,
|
|
102
|
+
progress_total INTEGER NOT NULL DEFAULT 0,
|
|
103
|
+
outstanding_tasks TEXT NOT NULL DEFAULT '[]',
|
|
104
|
+
completed_tasks TEXT NOT NULL DEFAULT '[]',
|
|
105
|
+
current_task TEXT,
|
|
106
|
+
theme TEXT,
|
|
107
|
+
computed_at TEXT NOT NULL,
|
|
108
|
+
stale INTEGER NOT NULL DEFAULT 0
|
|
109
|
+
);
|
|
110
|
+
`);
|
|
111
|
+
// Migration: Add new columns if they don't exist (for existing databases)
|
|
112
|
+
this.migrateSessionsColumns();
|
|
113
|
+
this.migrateInsightsColumns();
|
|
114
|
+
this.prepareStatements();
|
|
115
|
+
this.ensureMetadata();
|
|
116
|
+
this.isInitialized = true;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
this.logger.error('Failed to initialize session info database', error);
|
|
120
|
+
throw new Error(`Session info database initialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
migrateSessionsColumns() {
|
|
124
|
+
// Check if new columns exist on sessions table and add them if not
|
|
125
|
+
try {
|
|
126
|
+
const tableInfo = this.db.pragma('table_info(sessions)');
|
|
127
|
+
const columnNames = tableInfo.map(col => col.name);
|
|
128
|
+
if (!columnNames.includes('identity_image')) {
|
|
129
|
+
this.db.exec("ALTER TABLE sessions ADD COLUMN identity_image TEXT DEFAULT NULL");
|
|
130
|
+
this.logger.info('Added identity_image column to sessions');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
this.logger.debug('Sessions migration check failed', { error });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
migrateInsightsColumns() {
|
|
138
|
+
// Check if new columns exist and add them if not
|
|
139
|
+
try {
|
|
140
|
+
const tableInfo = this.db.pragma('table_info(session_insights)');
|
|
141
|
+
const columnNames = tableInfo.map(col => col.name);
|
|
142
|
+
if (!columnNames.includes('purposes')) {
|
|
143
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN purposes TEXT NOT NULL DEFAULT '[]'");
|
|
144
|
+
this.logger.info('Added purposes column to session_insights');
|
|
145
|
+
}
|
|
146
|
+
if (!columnNames.includes('milestones')) {
|
|
147
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN milestones TEXT NOT NULL DEFAULT '[]'");
|
|
148
|
+
this.logger.info('Added milestones column to session_insights');
|
|
149
|
+
}
|
|
150
|
+
if (!columnNames.includes('recent_actions')) {
|
|
151
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN recent_actions TEXT NOT NULL DEFAULT '[]'");
|
|
152
|
+
this.logger.info('Added recent_actions column to session_insights');
|
|
153
|
+
}
|
|
154
|
+
// New ontology columns
|
|
155
|
+
if (!columnNames.includes('context')) {
|
|
156
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN context TEXT DEFAULT NULL");
|
|
157
|
+
this.logger.info('Added context column to session_insights');
|
|
158
|
+
}
|
|
159
|
+
if (!columnNames.includes('notable')) {
|
|
160
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN notable TEXT NOT NULL DEFAULT '[]'");
|
|
161
|
+
this.logger.info('Added notable column to session_insights');
|
|
162
|
+
}
|
|
163
|
+
if (!columnNames.includes('tags')) {
|
|
164
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN tags TEXT DEFAULT NULL");
|
|
165
|
+
this.logger.info('Added tags column to session_insights');
|
|
166
|
+
}
|
|
167
|
+
if (!columnNames.includes('panels')) {
|
|
168
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN panels TEXT NOT NULL DEFAULT '[]'");
|
|
169
|
+
this.logger.info('Added panels column to session_insights');
|
|
170
|
+
}
|
|
171
|
+
// Delta detection metadata columns
|
|
172
|
+
if (!columnNames.includes('message_count')) {
|
|
173
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN message_count INTEGER DEFAULT NULL");
|
|
174
|
+
this.logger.info('Added message_count column to session_insights');
|
|
175
|
+
}
|
|
176
|
+
if (!columnNames.includes('last_delta_check_at')) {
|
|
177
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN last_delta_check_at TEXT DEFAULT NULL");
|
|
178
|
+
this.logger.info('Added last_delta_check_at column to session_insights');
|
|
179
|
+
}
|
|
180
|
+
if (!columnNames.includes('patched_at')) {
|
|
181
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN patched_at TEXT DEFAULT NULL");
|
|
182
|
+
this.logger.info('Added patched_at column to session_insights');
|
|
183
|
+
}
|
|
184
|
+
if (!columnNames.includes('current_state')) {
|
|
185
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN current_state TEXT DEFAULT NULL");
|
|
186
|
+
this.logger.info('Added current_state column to session_insights');
|
|
187
|
+
}
|
|
188
|
+
if (!columnNames.includes('last_patch_trace_id')) {
|
|
189
|
+
this.db.exec("ALTER TABLE session_insights ADD COLUMN last_patch_trace_id TEXT DEFAULT NULL");
|
|
190
|
+
this.logger.info('Added last_patch_trace_id column to session_insights');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
this.logger.debug('Migration check failed (table may not exist yet)', { error });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
prepareStatements() {
|
|
198
|
+
this.getSessionStmt = this.db.prepare('SELECT * FROM sessions WHERE session_id = ?');
|
|
199
|
+
this.insertSessionStmt = this.db.prepare(`
|
|
200
|
+
INSERT INTO sessions (
|
|
201
|
+
session_id,
|
|
202
|
+
custom_name,
|
|
203
|
+
created_at,
|
|
204
|
+
updated_at,
|
|
205
|
+
version,
|
|
206
|
+
pinned,
|
|
207
|
+
archived,
|
|
208
|
+
continuation_session_id,
|
|
209
|
+
initial_commit_head,
|
|
210
|
+
permission_mode
|
|
211
|
+
) VALUES (
|
|
212
|
+
@session_id,
|
|
213
|
+
@custom_name,
|
|
214
|
+
@created_at,
|
|
215
|
+
@updated_at,
|
|
216
|
+
@version,
|
|
217
|
+
@pinned,
|
|
218
|
+
@archived,
|
|
219
|
+
@continuation_session_id,
|
|
220
|
+
@initial_commit_head,
|
|
221
|
+
@permission_mode
|
|
222
|
+
)
|
|
223
|
+
`);
|
|
224
|
+
this.updateSessionStmt = this.db.prepare(`
|
|
225
|
+
UPDATE sessions SET
|
|
226
|
+
custom_name=@custom_name,
|
|
227
|
+
updated_at=@updated_at,
|
|
228
|
+
pinned=@pinned,
|
|
229
|
+
archived=@archived,
|
|
230
|
+
continuation_session_id=@continuation_session_id,
|
|
231
|
+
initial_commit_head=@initial_commit_head,
|
|
232
|
+
permission_mode=@permission_mode,
|
|
233
|
+
version=@version
|
|
234
|
+
WHERE session_id=@session_id
|
|
235
|
+
`);
|
|
236
|
+
this.deleteSessionStmt = this.db.prepare('DELETE FROM sessions WHERE session_id = ?');
|
|
237
|
+
this.getAllStmt = this.db.prepare('SELECT * FROM sessions');
|
|
238
|
+
this.countStmt = this.db.prepare('SELECT COUNT(*) as count FROM sessions');
|
|
239
|
+
this.archiveAllStmt = this.db.prepare('UPDATE sessions SET archived=1, updated_at=@updated_at WHERE archived=0');
|
|
240
|
+
this.setMetadataStmt = this.db.prepare('INSERT INTO metadata (key, value) VALUES (@key, @value) ON CONFLICT(key) DO UPDATE SET value=excluded.value');
|
|
241
|
+
this.getMetadataStmt = this.db.prepare('SELECT value FROM metadata WHERE key = ?');
|
|
242
|
+
// Insights statements
|
|
243
|
+
this.getInsightsStmt = this.db.prepare('SELECT * FROM session_insights WHERE session_id = ?');
|
|
244
|
+
this.upsertInsightsStmt = this.db.prepare(`
|
|
245
|
+
INSERT INTO session_insights (
|
|
246
|
+
session_id, description, context, current_state, last_patch_trace_id, purposes, milestones, notable, tags, recent_actions, panels,
|
|
247
|
+
progress_completed, progress_total,
|
|
248
|
+
outstanding_tasks, completed_tasks, current_task, theme, computed_at, stale,
|
|
249
|
+
message_count, last_delta_check_at, patched_at
|
|
250
|
+
) VALUES (
|
|
251
|
+
@session_id, @description, @context, @current_state, @last_patch_trace_id, @purposes, @milestones, @notable, @tags, @recent_actions, @panels,
|
|
252
|
+
@progress_completed, @progress_total,
|
|
253
|
+
@outstanding_tasks, @completed_tasks, @current_task, @theme, @computed_at, @stale,
|
|
254
|
+
@message_count, @last_delta_check_at, @patched_at
|
|
255
|
+
) ON CONFLICT(session_id) DO UPDATE SET
|
|
256
|
+
description=excluded.description,
|
|
257
|
+
context=excluded.context,
|
|
258
|
+
current_state=excluded.current_state,
|
|
259
|
+
last_patch_trace_id=excluded.last_patch_trace_id,
|
|
260
|
+
purposes=excluded.purposes,
|
|
261
|
+
milestones=excluded.milestones,
|
|
262
|
+
notable=excluded.notable,
|
|
263
|
+
tags=excluded.tags,
|
|
264
|
+
recent_actions=excluded.recent_actions,
|
|
265
|
+
panels=excluded.panels,
|
|
266
|
+
progress_completed=excluded.progress_completed,
|
|
267
|
+
progress_total=excluded.progress_total,
|
|
268
|
+
outstanding_tasks=excluded.outstanding_tasks,
|
|
269
|
+
completed_tasks=excluded.completed_tasks,
|
|
270
|
+
current_task=excluded.current_task,
|
|
271
|
+
theme=excluded.theme,
|
|
272
|
+
computed_at=excluded.computed_at,
|
|
273
|
+
stale=excluded.stale,
|
|
274
|
+
message_count=excluded.message_count,
|
|
275
|
+
last_delta_check_at=excluded.last_delta_check_at,
|
|
276
|
+
patched_at=excluded.patched_at
|
|
277
|
+
`);
|
|
278
|
+
this.getAllInsightsStmt = this.db.prepare('SELECT * FROM session_insights');
|
|
279
|
+
this.markInsightsStaleStmt = this.db.prepare('UPDATE session_insights SET stale=1 WHERE session_id = ?');
|
|
280
|
+
this.getStaleInsightsStmt = this.db.prepare('SELECT session_id FROM session_insights WHERE stale = 1');
|
|
281
|
+
}
|
|
282
|
+
ensureMetadata() {
|
|
283
|
+
const now = new Date().toISOString();
|
|
284
|
+
const schema = this.getMetadataStmt.get('schema_version');
|
|
285
|
+
if (!schema) {
|
|
286
|
+
this.setMetadataStmt.run({ key: 'schema_version', value: '3' });
|
|
287
|
+
this.setMetadataStmt.run({ key: 'created_at', value: now });
|
|
288
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
mapRow(row) {
|
|
292
|
+
return {
|
|
293
|
+
custom_name: row.custom_name,
|
|
294
|
+
created_at: row.created_at,
|
|
295
|
+
updated_at: row.updated_at,
|
|
296
|
+
version: row.version,
|
|
297
|
+
pinned: !!row.pinned,
|
|
298
|
+
archived: !!row.archived,
|
|
299
|
+
continuation_session_id: row.continuation_session_id,
|
|
300
|
+
initial_commit_head: row.initial_commit_head,
|
|
301
|
+
permission_mode: row.permission_mode,
|
|
302
|
+
identity_image: row.identity_image ?? undefined
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
async getSessionInfo(sessionId) {
|
|
306
|
+
try {
|
|
307
|
+
const row = this.getSessionStmt.get(sessionId);
|
|
308
|
+
if (row) {
|
|
309
|
+
return this.mapRow(row);
|
|
310
|
+
}
|
|
311
|
+
const now = new Date().toISOString();
|
|
312
|
+
const defaultSession = {
|
|
313
|
+
custom_name: '',
|
|
314
|
+
created_at: now,
|
|
315
|
+
updated_at: now,
|
|
316
|
+
version: 3,
|
|
317
|
+
pinned: false,
|
|
318
|
+
archived: false,
|
|
319
|
+
continuation_session_id: '',
|
|
320
|
+
initial_commit_head: '',
|
|
321
|
+
permission_mode: 'default'
|
|
322
|
+
};
|
|
323
|
+
this.insertSessionStmt.run({
|
|
324
|
+
session_id: sessionId,
|
|
325
|
+
custom_name: '',
|
|
326
|
+
created_at: now,
|
|
327
|
+
updated_at: now,
|
|
328
|
+
version: 3,
|
|
329
|
+
pinned: 0,
|
|
330
|
+
archived: 0,
|
|
331
|
+
continuation_session_id: '',
|
|
332
|
+
initial_commit_head: '',
|
|
333
|
+
permission_mode: 'default'
|
|
334
|
+
});
|
|
335
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
336
|
+
return defaultSession;
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
this.logger.error('Failed to get session info', { sessionId, error });
|
|
340
|
+
const now = new Date().toISOString();
|
|
341
|
+
return {
|
|
342
|
+
custom_name: '',
|
|
343
|
+
created_at: now,
|
|
344
|
+
updated_at: now,
|
|
345
|
+
version: 3,
|
|
346
|
+
pinned: false,
|
|
347
|
+
archived: false,
|
|
348
|
+
continuation_session_id: '',
|
|
349
|
+
initial_commit_head: '',
|
|
350
|
+
permission_mode: 'default'
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async updateSessionInfo(sessionId, updates) {
|
|
355
|
+
try {
|
|
356
|
+
const existingRow = this.getSessionStmt.get(sessionId);
|
|
357
|
+
const now = new Date().toISOString();
|
|
358
|
+
if (existingRow) {
|
|
359
|
+
const updatedSession = {
|
|
360
|
+
...this.mapRow(existingRow),
|
|
361
|
+
...updates,
|
|
362
|
+
updated_at: now
|
|
363
|
+
};
|
|
364
|
+
this.updateSessionStmt.run({
|
|
365
|
+
session_id: sessionId,
|
|
366
|
+
custom_name: updatedSession.custom_name,
|
|
367
|
+
updated_at: updatedSession.updated_at,
|
|
368
|
+
pinned: updatedSession.pinned ? 1 : 0,
|
|
369
|
+
archived: updatedSession.archived ? 1 : 0,
|
|
370
|
+
continuation_session_id: updatedSession.continuation_session_id,
|
|
371
|
+
initial_commit_head: updatedSession.initial_commit_head,
|
|
372
|
+
permission_mode: updatedSession.permission_mode,
|
|
373
|
+
version: updatedSession.version
|
|
374
|
+
});
|
|
375
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
376
|
+
return updatedSession;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
const newSession = {
|
|
380
|
+
custom_name: '',
|
|
381
|
+
created_at: now,
|
|
382
|
+
updated_at: now,
|
|
383
|
+
version: 3,
|
|
384
|
+
pinned: false,
|
|
385
|
+
archived: false,
|
|
386
|
+
continuation_session_id: '',
|
|
387
|
+
initial_commit_head: '',
|
|
388
|
+
permission_mode: 'default',
|
|
389
|
+
...updates
|
|
390
|
+
};
|
|
391
|
+
this.insertSessionStmt.run({
|
|
392
|
+
session_id: sessionId,
|
|
393
|
+
custom_name: newSession.custom_name,
|
|
394
|
+
created_at: newSession.created_at,
|
|
395
|
+
updated_at: newSession.updated_at,
|
|
396
|
+
version: newSession.version,
|
|
397
|
+
pinned: newSession.pinned ? 1 : 0,
|
|
398
|
+
archived: newSession.archived ? 1 : 0,
|
|
399
|
+
continuation_session_id: newSession.continuation_session_id,
|
|
400
|
+
initial_commit_head: newSession.initial_commit_head,
|
|
401
|
+
permission_mode: newSession.permission_mode
|
|
402
|
+
});
|
|
403
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
404
|
+
return newSession;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
this.logger.error('Failed to update session info', { sessionId, updates, error });
|
|
409
|
+
throw new Error(`Failed to update session info: ${error instanceof Error ? error.message : String(error)}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async updateCustomName(sessionId, customName) {
|
|
413
|
+
await this.updateSessionInfo(sessionId, { custom_name: customName });
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Set the identity image for a session. This is a one-time operation
|
|
417
|
+
* that doesn't update the session's updated_at timestamp.
|
|
418
|
+
*/
|
|
419
|
+
async setIdentityImage(sessionId, imageData) {
|
|
420
|
+
try {
|
|
421
|
+
const stmt = this.db.prepare('UPDATE sessions SET identity_image = ? WHERE session_id = ?');
|
|
422
|
+
stmt.run(imageData, sessionId);
|
|
423
|
+
this.logger.debug('Identity image set for session', { sessionId });
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
this.logger.error('Failed to set identity image', { sessionId, error });
|
|
427
|
+
throw new Error(`Failed to set identity image: ${error instanceof Error ? error.message : String(error)}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Check if a session already has an identity image.
|
|
432
|
+
*/
|
|
433
|
+
async hasIdentityImage(sessionId) {
|
|
434
|
+
try {
|
|
435
|
+
const row = this.getSessionStmt.get(sessionId);
|
|
436
|
+
return row?.identity_image != null;
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
this.logger.error('Failed to check identity image', { sessionId, error });
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
async deleteSession(sessionId) {
|
|
444
|
+
this.logger.info('Deleting session info', { sessionId });
|
|
445
|
+
try {
|
|
446
|
+
const result = this.deleteSessionStmt.run(sessionId);
|
|
447
|
+
if (result.changes > 0) {
|
|
448
|
+
const now = new Date().toISOString();
|
|
449
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
450
|
+
this.logger.info('Session info deleted successfully', { sessionId });
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
this.logger.debug('Session info not found for deletion', { sessionId });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
this.logger.error('Failed to delete session info', { sessionId, error });
|
|
458
|
+
throw new Error(`Failed to delete session info: ${error instanceof Error ? error.message : String(error)}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async getAllSessionInfo() {
|
|
462
|
+
this.logger.debug('Getting all session info');
|
|
463
|
+
try {
|
|
464
|
+
const rows = this.getAllStmt.all();
|
|
465
|
+
const result = {};
|
|
466
|
+
for (const row of rows) {
|
|
467
|
+
result[row.session_id] = this.mapRow(row);
|
|
468
|
+
}
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
this.logger.error('Failed to get all session info', error);
|
|
473
|
+
return {};
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async getStats() {
|
|
477
|
+
try {
|
|
478
|
+
const countRow = this.countStmt.get();
|
|
479
|
+
let dbSize = 0;
|
|
480
|
+
if (this.dbPath !== ':memory:') {
|
|
481
|
+
try {
|
|
482
|
+
const stats = fs.statSync(this.dbPath);
|
|
483
|
+
dbSize = stats.size;
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
dbSize = 0;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const lastUpdatedRow = this.getMetadataStmt.get('last_updated');
|
|
490
|
+
return {
|
|
491
|
+
sessionCount: countRow.count,
|
|
492
|
+
dbSize,
|
|
493
|
+
lastUpdated: lastUpdatedRow?.value || new Date().toISOString()
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
this.logger.error('Failed to get database stats', error);
|
|
498
|
+
return {
|
|
499
|
+
sessionCount: 0,
|
|
500
|
+
dbSize: 0,
|
|
501
|
+
lastUpdated: new Date().toISOString()
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
reinitializePaths(customConfigDir) {
|
|
506
|
+
this.initializePaths(customConfigDir);
|
|
507
|
+
}
|
|
508
|
+
getDbPath() {
|
|
509
|
+
return this.dbPath;
|
|
510
|
+
}
|
|
511
|
+
getConfigDir() {
|
|
512
|
+
return this.configDir;
|
|
513
|
+
}
|
|
514
|
+
async archiveAllSessions() {
|
|
515
|
+
this.logger.info('Archiving all sessions');
|
|
516
|
+
try {
|
|
517
|
+
const now = new Date().toISOString();
|
|
518
|
+
const transaction = this.db.transaction(() => {
|
|
519
|
+
const info = this.archiveAllStmt.run({ updated_at: now });
|
|
520
|
+
if (info.changes > 0) {
|
|
521
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
522
|
+
}
|
|
523
|
+
return info.changes;
|
|
524
|
+
});
|
|
525
|
+
const archivedCount = transaction();
|
|
526
|
+
this.logger.info('Sessions archived successfully', { archivedCount });
|
|
527
|
+
return archivedCount;
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
this.logger.error('Failed to archive all sessions', error);
|
|
531
|
+
throw new Error(`Failed to archive all sessions: ${error instanceof Error ? error.message : String(error)}`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async syncMissingSessions(sessionIds) {
|
|
535
|
+
try {
|
|
536
|
+
const now = new Date().toISOString();
|
|
537
|
+
const insert = this.db.prepare(`
|
|
538
|
+
INSERT OR IGNORE INTO sessions (
|
|
539
|
+
session_id,
|
|
540
|
+
custom_name,
|
|
541
|
+
created_at,
|
|
542
|
+
updated_at,
|
|
543
|
+
version,
|
|
544
|
+
pinned,
|
|
545
|
+
archived,
|
|
546
|
+
continuation_session_id,
|
|
547
|
+
initial_commit_head,
|
|
548
|
+
permission_mode
|
|
549
|
+
) VALUES (
|
|
550
|
+
@session_id,
|
|
551
|
+
'',
|
|
552
|
+
@now,
|
|
553
|
+
@now,
|
|
554
|
+
3,
|
|
555
|
+
0,
|
|
556
|
+
0,
|
|
557
|
+
'',
|
|
558
|
+
'',
|
|
559
|
+
'default'
|
|
560
|
+
)
|
|
561
|
+
`);
|
|
562
|
+
const transaction = this.db.transaction((ids) => {
|
|
563
|
+
let inserted = 0;
|
|
564
|
+
for (const id of ids) {
|
|
565
|
+
const info = insert.run({ session_id: id, now });
|
|
566
|
+
if (info.changes > 0)
|
|
567
|
+
inserted++;
|
|
568
|
+
}
|
|
569
|
+
if (inserted > 0) {
|
|
570
|
+
this.setMetadataStmt.run({ key: 'last_updated', value: now });
|
|
571
|
+
}
|
|
572
|
+
return inserted;
|
|
573
|
+
});
|
|
574
|
+
return transaction(sessionIds);
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
this.logger.error('Failed to sync missing sessions', error);
|
|
578
|
+
throw new Error(`Failed to sync missing sessions: ${error instanceof Error ? error.message : String(error)}`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// ==================== INSIGHTS METHODS ====================
|
|
582
|
+
mapInsightsRow(row) {
|
|
583
|
+
return {
|
|
584
|
+
session_id: row.session_id,
|
|
585
|
+
description: row.description,
|
|
586
|
+
context: row.context ? JSON.parse(row.context) : null,
|
|
587
|
+
current_state: row.current_state ? JSON.parse(row.current_state) : null,
|
|
588
|
+
last_patch_trace_id: row.last_patch_trace_id ?? null,
|
|
589
|
+
purposes: JSON.parse(row.purposes || '[]'),
|
|
590
|
+
milestones: JSON.parse(row.milestones || '[]'),
|
|
591
|
+
notable: JSON.parse(row.notable || '[]'),
|
|
592
|
+
tags: row.tags ? JSON.parse(row.tags) : null,
|
|
593
|
+
recent_actions: JSON.parse(row.recent_actions || '[]'),
|
|
594
|
+
panels: JSON.parse(row.panels || '[]'),
|
|
595
|
+
progress_completed: row.progress_completed,
|
|
596
|
+
progress_total: row.progress_total,
|
|
597
|
+
outstanding_tasks: JSON.parse(row.outstanding_tasks || '[]'),
|
|
598
|
+
completed_tasks: JSON.parse(row.completed_tasks || '[]'),
|
|
599
|
+
current_task: row.current_task,
|
|
600
|
+
theme: row.theme,
|
|
601
|
+
computed_at: row.computed_at,
|
|
602
|
+
stale: !!row.stale,
|
|
603
|
+
message_count: row.message_count ?? undefined,
|
|
604
|
+
last_delta_check_at: row.last_delta_check_at ?? undefined,
|
|
605
|
+
patched_at: row.patched_at ?? undefined
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
async getInsights(sessionId) {
|
|
609
|
+
try {
|
|
610
|
+
const row = this.getInsightsStmt.get(sessionId);
|
|
611
|
+
if (row) {
|
|
612
|
+
return this.mapInsightsRow(row);
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
this.logger.error('Failed to get insights', { sessionId, error });
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
async setInsights(insights) {
|
|
622
|
+
try {
|
|
623
|
+
this.upsertInsightsStmt.run({
|
|
624
|
+
session_id: insights.session_id,
|
|
625
|
+
description: insights.description,
|
|
626
|
+
context: insights.context ? JSON.stringify(insights.context) : null,
|
|
627
|
+
current_state: insights.current_state ? JSON.stringify(insights.current_state) : null,
|
|
628
|
+
last_patch_trace_id: insights.last_patch_trace_id ?? null,
|
|
629
|
+
purposes: JSON.stringify(insights.purposes || []),
|
|
630
|
+
milestones: JSON.stringify(insights.milestones || []),
|
|
631
|
+
notable: JSON.stringify(insights.notable || []),
|
|
632
|
+
tags: insights.tags ? JSON.stringify(insights.tags) : null,
|
|
633
|
+
recent_actions: JSON.stringify(insights.recent_actions || []),
|
|
634
|
+
panels: JSON.stringify(insights.panels || []),
|
|
635
|
+
progress_completed: insights.progress_completed,
|
|
636
|
+
progress_total: insights.progress_total,
|
|
637
|
+
outstanding_tasks: JSON.stringify(insights.outstanding_tasks),
|
|
638
|
+
completed_tasks: JSON.stringify(insights.completed_tasks),
|
|
639
|
+
current_task: insights.current_task,
|
|
640
|
+
theme: insights.theme,
|
|
641
|
+
computed_at: insights.computed_at,
|
|
642
|
+
stale: insights.stale ? 1 : 0,
|
|
643
|
+
message_count: insights.message_count ?? null,
|
|
644
|
+
last_delta_check_at: insights.last_delta_check_at ?? null,
|
|
645
|
+
patched_at: insights.patched_at ?? null
|
|
646
|
+
});
|
|
647
|
+
this.logger.debug('Insights cached', { sessionId: insights.session_id });
|
|
648
|
+
}
|
|
649
|
+
catch (error) {
|
|
650
|
+
this.logger.error('Failed to set insights', { sessionId: insights.session_id, error });
|
|
651
|
+
throw error;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async getAllInsights() {
|
|
655
|
+
try {
|
|
656
|
+
const rows = this.getAllInsightsStmt.all();
|
|
657
|
+
const result = new Map();
|
|
658
|
+
for (const row of rows) {
|
|
659
|
+
result.set(row.session_id, this.mapInsightsRow(row));
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
this.logger.error('Failed to get all insights', error);
|
|
665
|
+
return new Map();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async markInsightsStale(sessionId) {
|
|
669
|
+
try {
|
|
670
|
+
this.markInsightsStaleStmt.run(sessionId);
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
this.logger.error('Failed to mark insights stale', { sessionId, error });
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
async getStaleSessionIds() {
|
|
677
|
+
try {
|
|
678
|
+
const rows = this.getStaleInsightsStmt.all();
|
|
679
|
+
return rows.map(r => r.session_id);
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
this.logger.error('Failed to get stale session IDs', error);
|
|
683
|
+
return [];
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
async getMissingInsightsSessionIds(sessionIds) {
|
|
687
|
+
try {
|
|
688
|
+
const existing = await this.getAllInsights();
|
|
689
|
+
return sessionIds.filter(id => !existing.has(id));
|
|
690
|
+
}
|
|
691
|
+
catch (error) {
|
|
692
|
+
this.logger.error('Failed to get missing insights session IDs', error);
|
|
693
|
+
return sessionIds;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Get all non-archived session IDs (for refresh operations)
|
|
698
|
+
*/
|
|
699
|
+
getNonArchivedSessionIds() {
|
|
700
|
+
try {
|
|
701
|
+
const rows = this.db.prepare('SELECT session_id FROM sessions WHERE archived = 0').all();
|
|
702
|
+
return rows.map(r => r.session_id);
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
this.logger.error('Failed to get non-archived session IDs', error);
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Audit insight regeneration for debugging (legacy method)
|
|
711
|
+
*/
|
|
712
|
+
auditRegeneration(params) {
|
|
713
|
+
try {
|
|
714
|
+
// Ensure audit table exists
|
|
715
|
+
this.db.exec(`
|
|
716
|
+
CREATE TABLE IF NOT EXISTS insight_audit (
|
|
717
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
718
|
+
session_id TEXT NOT NULL,
|
|
719
|
+
action TEXT NOT NULL,
|
|
720
|
+
reason TEXT,
|
|
721
|
+
old_mission TEXT,
|
|
722
|
+
new_mission TEXT,
|
|
723
|
+
old_theme TEXT,
|
|
724
|
+
new_theme TEXT,
|
|
725
|
+
message_count INTEGER,
|
|
726
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
727
|
+
)
|
|
728
|
+
`);
|
|
729
|
+
this.db.prepare(`
|
|
730
|
+
INSERT INTO insight_audit (session_id, action, reason, old_mission, new_mission, old_theme, new_theme, message_count)
|
|
731
|
+
VALUES (@sessionId, @action, @reason, @oldMission, @newMission, @oldTheme, @newTheme, @messageCount)
|
|
732
|
+
`).run({
|
|
733
|
+
sessionId: params.sessionId,
|
|
734
|
+
action: params.action,
|
|
735
|
+
reason: params.reason,
|
|
736
|
+
oldMission: params.oldMission || null,
|
|
737
|
+
newMission: params.newMission || null,
|
|
738
|
+
oldTheme: params.oldTheme || null,
|
|
739
|
+
newTheme: params.newTheme || null,
|
|
740
|
+
messageCount: params.messageCount || null
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
this.logger.debug('Failed to audit regeneration', { error });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Audit event-driven insight operations with full context for debugging.
|
|
749
|
+
* This captures trace IDs, action content, and before/after state snapshots.
|
|
750
|
+
*/
|
|
751
|
+
auditEventInsight(params) {
|
|
752
|
+
try {
|
|
753
|
+
// Ensure extended audit table exists with new columns
|
|
754
|
+
this.db.exec(`
|
|
755
|
+
CREATE TABLE IF NOT EXISTS insight_event_audit (
|
|
756
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
757
|
+
trace_id TEXT NOT NULL,
|
|
758
|
+
session_id TEXT NOT NULL,
|
|
759
|
+
event_type TEXT NOT NULL,
|
|
760
|
+
trigger TEXT NOT NULL,
|
|
761
|
+
action_content TEXT,
|
|
762
|
+
before_state TEXT,
|
|
763
|
+
after_state TEXT,
|
|
764
|
+
llm_response TEXT,
|
|
765
|
+
patched_fields TEXT,
|
|
766
|
+
duration_ms INTEGER,
|
|
767
|
+
skipped_reason TEXT,
|
|
768
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
769
|
+
)
|
|
770
|
+
`);
|
|
771
|
+
// Create index on trace_id for fast lookups
|
|
772
|
+
this.db.exec(`
|
|
773
|
+
CREATE INDEX IF NOT EXISTS idx_insight_event_audit_trace_id ON insight_event_audit(trace_id)
|
|
774
|
+
`);
|
|
775
|
+
this.db.exec(`
|
|
776
|
+
CREATE INDEX IF NOT EXISTS idx_insight_event_audit_session_id ON insight_event_audit(session_id)
|
|
777
|
+
`);
|
|
778
|
+
this.db.prepare(`
|
|
779
|
+
INSERT INTO insight_event_audit (
|
|
780
|
+
trace_id, session_id, event_type, trigger,
|
|
781
|
+
action_content, before_state, after_state,
|
|
782
|
+
llm_response, patched_fields, duration_ms, skipped_reason
|
|
783
|
+
) VALUES (
|
|
784
|
+
@traceId, @sessionId, @eventType, @trigger,
|
|
785
|
+
@actionContent, @beforeState, @afterState,
|
|
786
|
+
@llmResponse, @patchedFields, @durationMs, @skippedReason
|
|
787
|
+
)
|
|
788
|
+
`).run({
|
|
789
|
+
traceId: params.traceId,
|
|
790
|
+
sessionId: params.sessionId,
|
|
791
|
+
eventType: params.eventType,
|
|
792
|
+
trigger: params.trigger,
|
|
793
|
+
actionContent: params.actionContent ? JSON.stringify(params.actionContent) : null,
|
|
794
|
+
beforeState: params.beforeState ? JSON.stringify(params.beforeState) : null,
|
|
795
|
+
afterState: params.afterState ? JSON.stringify(params.afterState) : null,
|
|
796
|
+
llmResponse: params.llmResponse || null,
|
|
797
|
+
patchedFields: params.patchedFields ? JSON.stringify(params.patchedFields) : null,
|
|
798
|
+
durationMs: params.durationMs || null,
|
|
799
|
+
skippedReason: params.skippedReason || null
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
catch (error) {
|
|
803
|
+
this.logger.debug('Failed to audit event insight', { error });
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Get event audit history by trace ID (for debugging a specific event chain)
|
|
808
|
+
*/
|
|
809
|
+
getEventAuditByTraceId(traceId) {
|
|
810
|
+
try {
|
|
811
|
+
const rows = this.db.prepare(`
|
|
812
|
+
SELECT * FROM insight_event_audit
|
|
813
|
+
WHERE trace_id = ?
|
|
814
|
+
ORDER BY created_at ASC
|
|
815
|
+
`).all(traceId);
|
|
816
|
+
return rows.map(r => ({
|
|
817
|
+
traceId: r.trace_id,
|
|
818
|
+
sessionId: r.session_id,
|
|
819
|
+
eventType: r.event_type,
|
|
820
|
+
trigger: r.trigger,
|
|
821
|
+
actionContent: r.action_content ? JSON.parse(r.action_content) : null,
|
|
822
|
+
beforeState: r.before_state ? JSON.parse(r.before_state) : null,
|
|
823
|
+
afterState: r.after_state ? JSON.parse(r.after_state) : null,
|
|
824
|
+
llmResponse: r.llm_response,
|
|
825
|
+
patchedFields: r.patched_fields ? JSON.parse(r.patched_fields) : null,
|
|
826
|
+
durationMs: r.duration_ms,
|
|
827
|
+
skippedReason: r.skipped_reason,
|
|
828
|
+
createdAt: r.created_at
|
|
829
|
+
}));
|
|
830
|
+
}
|
|
831
|
+
catch (error) {
|
|
832
|
+
this.logger.debug('Failed to get event audit by trace ID', { error });
|
|
833
|
+
return [];
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Get recent event audit history for a session
|
|
838
|
+
*/
|
|
839
|
+
getRecentEventAudit(sessionId, limit = 50) {
|
|
840
|
+
try {
|
|
841
|
+
const rows = this.db.prepare(`
|
|
842
|
+
SELECT trace_id, event_type, trigger, patched_fields, duration_ms, skipped_reason, created_at
|
|
843
|
+
FROM insight_event_audit
|
|
844
|
+
WHERE session_id = ?
|
|
845
|
+
ORDER BY created_at DESC
|
|
846
|
+
LIMIT ?
|
|
847
|
+
`).all(sessionId, limit);
|
|
848
|
+
return rows.map(r => ({
|
|
849
|
+
traceId: r.trace_id,
|
|
850
|
+
eventType: r.event_type,
|
|
851
|
+
trigger: r.trigger,
|
|
852
|
+
patchedFields: r.patched_fields ? JSON.parse(r.patched_fields) : null,
|
|
853
|
+
durationMs: r.duration_ms,
|
|
854
|
+
skippedReason: r.skipped_reason,
|
|
855
|
+
createdAt: r.created_at
|
|
856
|
+
}));
|
|
857
|
+
}
|
|
858
|
+
catch (error) {
|
|
859
|
+
this.logger.debug('Failed to get recent event audit', { error });
|
|
860
|
+
return [];
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Get all audit history across all sessions
|
|
865
|
+
*/
|
|
866
|
+
getAllAuditHistory(limit = 100) {
|
|
867
|
+
try {
|
|
868
|
+
const rows = this.db.prepare(`
|
|
869
|
+
SELECT session_id, action, reason, old_mission, new_mission, old_theme, new_theme, message_count, created_at
|
|
870
|
+
FROM insight_audit
|
|
871
|
+
ORDER BY created_at DESC
|
|
872
|
+
LIMIT ?
|
|
873
|
+
`).all(limit);
|
|
874
|
+
return rows.map(r => ({
|
|
875
|
+
sessionId: r.session_id,
|
|
876
|
+
action: r.action,
|
|
877
|
+
reason: r.reason,
|
|
878
|
+
oldMission: r.old_mission,
|
|
879
|
+
newMission: r.new_mission,
|
|
880
|
+
oldTheme: r.old_theme,
|
|
881
|
+
newTheme: r.new_theme,
|
|
882
|
+
messageCount: r.message_count,
|
|
883
|
+
createdAt: r.created_at
|
|
884
|
+
}));
|
|
885
|
+
}
|
|
886
|
+
catch (error) {
|
|
887
|
+
this.logger.debug('Failed to get all audit history', { error });
|
|
888
|
+
return [];
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Get audit history for a session
|
|
893
|
+
*/
|
|
894
|
+
getAuditHistory(sessionId, limit = 50) {
|
|
895
|
+
try {
|
|
896
|
+
const rows = this.db.prepare(`
|
|
897
|
+
SELECT action, reason, old_mission, new_mission, old_theme, new_theme, message_count, created_at
|
|
898
|
+
FROM insight_audit
|
|
899
|
+
WHERE session_id = ?
|
|
900
|
+
ORDER BY created_at DESC
|
|
901
|
+
LIMIT ?
|
|
902
|
+
`).all(sessionId, limit);
|
|
903
|
+
return rows.map(r => ({
|
|
904
|
+
action: r.action,
|
|
905
|
+
reason: r.reason,
|
|
906
|
+
oldMission: r.old_mission,
|
|
907
|
+
newMission: r.new_mission,
|
|
908
|
+
oldTheme: r.old_theme,
|
|
909
|
+
newTheme: r.new_theme,
|
|
910
|
+
messageCount: r.message_count,
|
|
911
|
+
createdAt: r.created_at
|
|
912
|
+
}));
|
|
913
|
+
}
|
|
914
|
+
catch (error) {
|
|
915
|
+
this.logger.debug('Failed to get audit history', { error });
|
|
916
|
+
return [];
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
//# sourceMappingURL=session-info-service.js.map
|