@agentuity/opencode 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/lead.d.ts +1 -1
- package/dist/agents/lead.d.ts.map +1 -1
- package/dist/agents/lead.js +9 -0
- package/dist/agents/lead.js.map +1 -1
- package/dist/agents/monitor.d.ts +1 -1
- package/dist/agents/monitor.d.ts.map +1 -1
- package/dist/agents/monitor.js +13 -0
- package/dist/agents/monitor.js.map +1 -1
- package/dist/background/manager.d.ts +4 -1
- package/dist/background/manager.d.ts.map +1 -1
- package/dist/background/manager.js +161 -3
- package/dist/background/manager.js.map +1 -1
- package/dist/background/types.d.ts +21 -0
- package/dist/background/types.d.ts.map +1 -1
- package/dist/plugin/hooks/cadence.d.ts +2 -1
- package/dist/plugin/hooks/cadence.d.ts.map +1 -1
- package/dist/plugin/hooks/cadence.js +57 -1
- package/dist/plugin/hooks/cadence.js.map +1 -1
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +196 -7
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/sqlite/index.d.ts +3 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +2 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/queries.d.ts +18 -0
- package/dist/sqlite/queries.d.ts.map +1 -0
- package/dist/sqlite/queries.js +41 -0
- package/dist/sqlite/queries.js.map +1 -0
- package/dist/sqlite/reader.d.ts +44 -0
- package/dist/sqlite/reader.d.ts.map +1 -0
- package/dist/sqlite/reader.js +526 -0
- package/dist/sqlite/reader.js.map +1 -0
- package/dist/sqlite/types.d.ts +110 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/package.json +3 -3
- package/src/agents/lead.ts +9 -0
- package/src/agents/monitor.ts +13 -0
- package/src/background/manager.ts +174 -3
- package/src/background/types.ts +10 -0
- package/src/plugin/hooks/cadence.ts +72 -1
- package/src/plugin/plugin.ts +271 -23
- package/src/sqlite/index.ts +16 -0
- package/src/sqlite/queries.ts +50 -0
- package/src/sqlite/reader.ts +677 -0
- package/src/sqlite/types.ts +121 -0
package/src/plugin/plugin.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { PluginInput, Hooks } from '@opencode-ai/plugin';
|
|
2
2
|
import { tool } from '@opencode-ai/plugin';
|
|
3
3
|
import { StructuredError } from '@agentuity/core';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { homedir, platform } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
4
7
|
import { z } from 'zod';
|
|
5
8
|
import type { AgentConfig, CommandDefinition } from '../types';
|
|
6
9
|
import { loadAllSkills, type LoadedSkill } from '../skills';
|
|
@@ -14,6 +17,8 @@ import { createCadenceHooks } from './hooks/cadence';
|
|
|
14
17
|
import { createSessionMemoryHooks } from './hooks/session-memory';
|
|
15
18
|
import type { AgentRole } from '../types';
|
|
16
19
|
import { BackgroundManager } from '../background';
|
|
20
|
+
import type { SessionTreeNode } from '../sqlite';
|
|
21
|
+
import { OpenCodeDBReader } from '../sqlite';
|
|
17
22
|
import { TmuxSessionManager } from '../tmux';
|
|
18
23
|
import { checkAuth } from '../services/auth';
|
|
19
24
|
|
|
@@ -40,6 +45,11 @@ const MemoryShareError = StructuredError(
|
|
|
40
45
|
'Failed to create public memory share'
|
|
41
46
|
)<{ reason: string }>();
|
|
42
47
|
|
|
48
|
+
const OpenCodeDashboardUnavailableError = StructuredError(
|
|
49
|
+
'OpenCodeDashboardUnavailableError',
|
|
50
|
+
'OpenCode SQLite database is not available. Requires OpenCode v1.2.0+ with SQLite storage.'
|
|
51
|
+
);
|
|
52
|
+
|
|
43
53
|
// Sandbox environment detection
|
|
44
54
|
const SANDBOX_ID = process.env.AGENTUITY_SANDBOX_ID;
|
|
45
55
|
const IN_SANDBOX = !!SANDBOX_ID;
|
|
@@ -79,6 +89,8 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
79
89
|
|
|
80
90
|
const userConfig = await loadCoderConfig();
|
|
81
91
|
const coderConfig = mergeConfig(getDefaultConfig(), userConfig);
|
|
92
|
+
const resolvedDbPath = resolveOpenCodeDBPath();
|
|
93
|
+
const dbReader = new OpenCodeDBReader(resolvedDbPath ? { dbPath: resolvedDbPath } : undefined);
|
|
82
94
|
|
|
83
95
|
const sessionHooks = createSessionHooks(ctx, coderConfig);
|
|
84
96
|
const toolHooks = createToolHooks(ctx, coderConfig);
|
|
@@ -96,23 +108,28 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
96
108
|
}),
|
|
97
109
|
})
|
|
98
110
|
: undefined;
|
|
99
|
-
const backgroundManager = new BackgroundManager(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
const backgroundManager = new BackgroundManager(
|
|
112
|
+
ctx,
|
|
113
|
+
coderConfig.background,
|
|
114
|
+
{
|
|
115
|
+
onSubagentSessionCreated: tmuxManager
|
|
116
|
+
? (event) => {
|
|
117
|
+
void tmuxManager.onSessionCreated(event);
|
|
118
|
+
}
|
|
119
|
+
: undefined,
|
|
120
|
+
onSubagentSessionDeleted: tmuxManager
|
|
121
|
+
? (event) => {
|
|
122
|
+
void tmuxManager.onSessionDeleted(event);
|
|
123
|
+
}
|
|
124
|
+
: undefined,
|
|
125
|
+
onShutdown: tmuxManager
|
|
126
|
+
? () => {
|
|
127
|
+
void tmuxManager.cleanup();
|
|
128
|
+
}
|
|
129
|
+
: undefined,
|
|
130
|
+
},
|
|
131
|
+
dbReader
|
|
132
|
+
);
|
|
116
133
|
|
|
117
134
|
// Recover any background tasks from previous sessions
|
|
118
135
|
// This allows tasks to survive plugin restarts
|
|
@@ -140,7 +157,7 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
140
157
|
});
|
|
141
158
|
|
|
142
159
|
// Create hooks that need backgroundManager for task reference injection during compaction
|
|
143
|
-
const cadenceHooks = createCadenceHooks(ctx, coderConfig, backgroundManager);
|
|
160
|
+
const cadenceHooks = createCadenceHooks(ctx, coderConfig, backgroundManager, dbReader);
|
|
144
161
|
|
|
145
162
|
// Session memory hooks handle checkpointing and compaction for non-Cadence sessions
|
|
146
163
|
// Orchestration (deciding which module handles which session) happens below in the hooks
|
|
@@ -149,7 +166,7 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
149
166
|
const configHandler = createConfigHandler(coderConfig);
|
|
150
167
|
|
|
151
168
|
// Create plugin tools using the @opencode-ai/plugin tool helper
|
|
152
|
-
const tools = createTools(backgroundManager);
|
|
169
|
+
const tools = createTools(backgroundManager, dbReader);
|
|
153
170
|
|
|
154
171
|
// Create a logger for shutdown handler
|
|
155
172
|
const shutdownLogger = (message: string) =>
|
|
@@ -161,7 +178,7 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
161
178
|
},
|
|
162
179
|
});
|
|
163
180
|
|
|
164
|
-
registerShutdownHandler(backgroundManager, tmuxManager, shutdownLogger);
|
|
181
|
+
registerShutdownHandler(backgroundManager, tmuxManager, shutdownLogger, dbReader);
|
|
165
182
|
|
|
166
183
|
// Show startup toast (fire and forget, don't block)
|
|
167
184
|
try {
|
|
@@ -192,6 +209,9 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
192
209
|
out.env ??= {} as Record<string, string>;
|
|
193
210
|
out.env.AGENTUITY_PROFILE = profile;
|
|
194
211
|
out.env.AGENTUITY_AGENT_MODE = 'opencode';
|
|
212
|
+
if (resolvedDbPath) {
|
|
213
|
+
out.env.OPENCODE_DB_PATH = resolvedDbPath;
|
|
214
|
+
}
|
|
195
215
|
const sessionId = process.env.AGENTUITY_OPENCODE_SESSION;
|
|
196
216
|
if (sessionId) {
|
|
197
217
|
out.env.AGENTUITY_OPENCODE_SESSION = sessionId;
|
|
@@ -610,7 +630,10 @@ function normalizeBaseDir(path: string): string {
|
|
|
610
630
|
return path.replace(/[\\/]+$/, '');
|
|
611
631
|
}
|
|
612
632
|
|
|
613
|
-
function createTools(
|
|
633
|
+
function createTools(
|
|
634
|
+
backgroundManager: BackgroundManager,
|
|
635
|
+
dbReader?: OpenCodeDBReader
|
|
636
|
+
): Hooks['tool'] {
|
|
614
637
|
// Use the schema from @opencode-ai/plugin's tool helper to avoid Zod version mismatches
|
|
615
638
|
const s = tool.schema;
|
|
616
639
|
|
|
@@ -722,8 +745,30 @@ IMPORTANT: Use this tool instead of the 'task' tool when:
|
|
|
722
745
|
});
|
|
723
746
|
}
|
|
724
747
|
|
|
725
|
-
// Extract last few messages for summary
|
|
726
748
|
const messages = inspection.messages ?? [];
|
|
749
|
+
const enhanced =
|
|
750
|
+
inspection.messageCount !== undefined ||
|
|
751
|
+
inspection.activeTools !== undefined ||
|
|
752
|
+
inspection.todos !== undefined ||
|
|
753
|
+
inspection.costSummary !== undefined ||
|
|
754
|
+
inspection.childSessionCount !== undefined;
|
|
755
|
+
|
|
756
|
+
if (enhanced) {
|
|
757
|
+
return JSON.stringify({
|
|
758
|
+
taskId: inspection.taskId,
|
|
759
|
+
status: inspection.status,
|
|
760
|
+
found: true,
|
|
761
|
+
messageCount: inspection.messageCount ?? messages.length,
|
|
762
|
+
messages,
|
|
763
|
+
lastActivity: inspection.lastActivity,
|
|
764
|
+
activeTools: inspection.activeTools,
|
|
765
|
+
todos: inspection.todos,
|
|
766
|
+
costSummary: inspection.costSummary,
|
|
767
|
+
childSessionCount: inspection.childSessionCount,
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Extract last few messages for summary (fallback)
|
|
727
772
|
const lastMessages = messages
|
|
728
773
|
.slice(-3)
|
|
729
774
|
.map((m) => {
|
|
@@ -749,6 +794,34 @@ IMPORTANT: Use this tool instead of the 'task' tool when:
|
|
|
749
794
|
},
|
|
750
795
|
});
|
|
751
796
|
|
|
797
|
+
const sessionDashboard = tool({
|
|
798
|
+
description:
|
|
799
|
+
'Inspect a parent session dashboard from the local OpenCode SQLite database. Useful for Lead-of-Leads monitoring and nested session visibility.',
|
|
800
|
+
args: {
|
|
801
|
+
session_id: s.string().describe('Parent session ID to inspect'),
|
|
802
|
+
},
|
|
803
|
+
async execute(args) {
|
|
804
|
+
if (!dbReader || !dbReader.isAvailable()) {
|
|
805
|
+
const err = new OpenCodeDashboardUnavailableError();
|
|
806
|
+
return JSON.stringify({
|
|
807
|
+
success: false,
|
|
808
|
+
error: err._tag,
|
|
809
|
+
message: err.message,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const dashboard = dbReader.getSessionDashboard(args.session_id);
|
|
814
|
+
const builtTree = buildDashboardTree(dbReader, dashboard.sessions);
|
|
815
|
+
return JSON.stringify({
|
|
816
|
+
success: true,
|
|
817
|
+
sessionId: args.session_id,
|
|
818
|
+
totalCost: dashboard.totalCost,
|
|
819
|
+
summary: computeHealthSummary(builtTree),
|
|
820
|
+
sessions: builtTree,
|
|
821
|
+
});
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
|
|
752
825
|
const memoryShare = tool({
|
|
753
826
|
description: `Share memory content publicly via Agentuity Cloud Streams.
|
|
754
827
|
|
|
@@ -894,6 +967,7 @@ Returns the public URL that can be copied and used anywhere.`,
|
|
|
894
967
|
agentuity_background_output: backgroundOutput,
|
|
895
968
|
agentuity_background_cancel: backgroundCancel,
|
|
896
969
|
agentuity_background_inspect: backgroundInspect,
|
|
970
|
+
agentuity_session_dashboard: sessionDashboard,
|
|
897
971
|
agentuity_memory_share: memoryShare,
|
|
898
972
|
};
|
|
899
973
|
}
|
|
@@ -928,10 +1002,174 @@ function extractEventFromInput(
|
|
|
928
1002
|
return { type: inp.event.type, properties: inp.event.properties };
|
|
929
1003
|
}
|
|
930
1004
|
|
|
1005
|
+
function parseDisplayTitle(rawTitle: string): string {
|
|
1006
|
+
try {
|
|
1007
|
+
const parsed = JSON.parse(rawTitle);
|
|
1008
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
1009
|
+
if (typeof parsed.description === 'string') return parsed.description;
|
|
1010
|
+
if (typeof parsed.title === 'string') return parsed.title;
|
|
1011
|
+
if (typeof parsed.name === 'string') return parsed.name;
|
|
1012
|
+
}
|
|
1013
|
+
// Parsed but no useful field — return as-is
|
|
1014
|
+
return rawTitle;
|
|
1015
|
+
} catch {
|
|
1016
|
+
// Not JSON, return as-is
|
|
1017
|
+
return rawTitle;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function getCurrentActivity(reader: OpenCodeDBReader, sessionId: string): string | null {
|
|
1022
|
+
// First check active tools
|
|
1023
|
+
const activeTools = reader.getActiveToolCalls(sessionId);
|
|
1024
|
+
const firstTool = activeTools[0];
|
|
1025
|
+
if (firstTool) {
|
|
1026
|
+
return `Running ${firstTool.tool}`;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// Fall back to latest message
|
|
1030
|
+
const latestMsg = reader.getLatestMessage(sessionId);
|
|
1031
|
+
if (!latestMsg) return null;
|
|
1032
|
+
|
|
1033
|
+
if (latestMsg.error) return `Error: ${latestMsg.error.substring(0, 100)}`;
|
|
1034
|
+
|
|
1035
|
+
// Try to get latest text part for a snippet
|
|
1036
|
+
const textParts = reader.getTextParts(sessionId, { limit: 1 });
|
|
1037
|
+
const firstPart = textParts[0];
|
|
1038
|
+
if (firstPart) {
|
|
1039
|
+
const text = firstPart.text.trim();
|
|
1040
|
+
if (text.length > 80) return text.substring(0, 77) + '...';
|
|
1041
|
+
return text;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
type DashboardNode = {
|
|
1048
|
+
session: {
|
|
1049
|
+
id: string;
|
|
1050
|
+
title: string;
|
|
1051
|
+
displayTitle: string;
|
|
1052
|
+
parentId?: string | null;
|
|
1053
|
+
timeUpdated: number;
|
|
1054
|
+
timeUpdatedISO: string;
|
|
1055
|
+
};
|
|
1056
|
+
status: string;
|
|
1057
|
+
lastActivity: number;
|
|
1058
|
+
lastActivityISO: string;
|
|
1059
|
+
currentActivity: string | null;
|
|
1060
|
+
messageCount: number;
|
|
1061
|
+
activeToolCount: number;
|
|
1062
|
+
activeTools: Array<{ tool: string; status: string }>;
|
|
1063
|
+
todoSummary: { total: number; pending: number; completed: number };
|
|
1064
|
+
costSummary?: SessionTreeNode['costSummary'];
|
|
1065
|
+
children: DashboardNode[];
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
function buildDashboardTree(
|
|
1069
|
+
reader: OpenCodeDBReader,
|
|
1070
|
+
sessions: SessionTreeNode[]
|
|
1071
|
+
): DashboardNode[] {
|
|
1072
|
+
return sessions.map((node) => {
|
|
1073
|
+
const status = reader.getSessionStatus(node.session.id);
|
|
1074
|
+
const activeTools = reader.getActiveToolCalls(node.session.id);
|
|
1075
|
+
return {
|
|
1076
|
+
session: {
|
|
1077
|
+
id: node.session.id,
|
|
1078
|
+
title: node.session.title,
|
|
1079
|
+
displayTitle: parseDisplayTitle(node.session.title),
|
|
1080
|
+
parentId: node.session.parentId,
|
|
1081
|
+
timeUpdated: node.session.timeUpdated,
|
|
1082
|
+
timeUpdatedISO: new Date(node.session.timeUpdated).toISOString(),
|
|
1083
|
+
},
|
|
1084
|
+
status: status.status,
|
|
1085
|
+
lastActivity: status.lastActivity,
|
|
1086
|
+
lastActivityISO: new Date(status.lastActivity).toISOString(),
|
|
1087
|
+
currentActivity: getCurrentActivity(reader, node.session.id),
|
|
1088
|
+
messageCount: node.messageCount,
|
|
1089
|
+
activeToolCount: node.activeToolCount,
|
|
1090
|
+
activeTools: activeTools.map((t) => ({ tool: t.tool, status: t.status })),
|
|
1091
|
+
todoSummary: node.todoSummary ?? { total: 0, pending: 0, completed: 0 },
|
|
1092
|
+
costSummary: node.costSummary,
|
|
1093
|
+
children: buildDashboardTree(reader, node.children),
|
|
1094
|
+
};
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
type HealthSummary = {
|
|
1099
|
+
active: number;
|
|
1100
|
+
idle: number;
|
|
1101
|
+
error: number;
|
|
1102
|
+
archived: number;
|
|
1103
|
+
compacting: number;
|
|
1104
|
+
total: number;
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
function computeHealthSummary(nodes: DashboardNode[]): HealthSummary {
|
|
1108
|
+
const summary: HealthSummary = {
|
|
1109
|
+
active: 0,
|
|
1110
|
+
idle: 0,
|
|
1111
|
+
error: 0,
|
|
1112
|
+
archived: 0,
|
|
1113
|
+
compacting: 0,
|
|
1114
|
+
total: 0,
|
|
1115
|
+
};
|
|
1116
|
+
function walk(nodeList: DashboardNode[]): void {
|
|
1117
|
+
for (const node of nodeList) {
|
|
1118
|
+
summary.total++;
|
|
1119
|
+
const status = node.status as keyof Omit<HealthSummary, 'total'>;
|
|
1120
|
+
if (status in summary) {
|
|
1121
|
+
summary[status]++;
|
|
1122
|
+
}
|
|
1123
|
+
if (node.children.length > 0) walk(node.children);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
walk(nodes);
|
|
1127
|
+
return summary;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
function resolveOpenCodeDBPath(): string | null {
|
|
1131
|
+
const envPath = process.env.OPENCODE_DB_PATH;
|
|
1132
|
+
if (envPath) {
|
|
1133
|
+
if (isMemoryPath(envPath)) return envPath;
|
|
1134
|
+
if (existsSync(envPath)) return envPath;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
const home = homedir();
|
|
1138
|
+
const candidates: string[] = [];
|
|
1139
|
+
const currentPlatform = platform();
|
|
1140
|
+
|
|
1141
|
+
if (currentPlatform === 'darwin') {
|
|
1142
|
+
candidates.push(join(home, 'Library', 'Application Support', 'opencode', 'opencode.db'));
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (currentPlatform === 'win32') {
|
|
1146
|
+
const appData = process.env.APPDATA ?? join(home, 'AppData', 'Roaming');
|
|
1147
|
+
const localAppData = process.env.LOCALAPPDATA ?? join(home, 'AppData', 'Local');
|
|
1148
|
+
candidates.push(join(appData, 'opencode', 'opencode.db'));
|
|
1149
|
+
candidates.push(join(localAppData, 'opencode', 'opencode.db'));
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Linux default
|
|
1153
|
+
candidates.push(join(home, '.local', 'share', 'opencode', 'opencode.db'));
|
|
1154
|
+
|
|
1155
|
+
for (const candidate of candidates) {
|
|
1156
|
+
if (existsSync(candidate)) {
|
|
1157
|
+
return candidate;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
return null;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
function isMemoryPath(path: string): boolean {
|
|
1165
|
+
return path === ':memory:' || path.includes('mode=memory');
|
|
1166
|
+
}
|
|
1167
|
+
|
|
931
1168
|
function registerShutdownHandler(
|
|
932
1169
|
manager: BackgroundManager,
|
|
933
1170
|
tmuxManager?: TmuxSessionManager,
|
|
934
|
-
logger?: (msg: string) => void
|
|
1171
|
+
logger?: (msg: string) => void,
|
|
1172
|
+
dbReader?: OpenCodeDBReader
|
|
935
1173
|
): void {
|
|
936
1174
|
if (typeof process === 'undefined') {
|
|
937
1175
|
logger?.('[shutdown] process is undefined, cannot register handlers');
|
|
@@ -975,6 +1213,16 @@ function registerShutdownHandler(
|
|
|
975
1213
|
}
|
|
976
1214
|
}
|
|
977
1215
|
|
|
1216
|
+
if (dbReader) {
|
|
1217
|
+
try {
|
|
1218
|
+
log('Closing OpenCode DB reader...');
|
|
1219
|
+
dbReader.close();
|
|
1220
|
+
log('OpenCode DB reader closed');
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
log(`OpenCode DB reader error: ${error}`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
978
1226
|
log('Shutdown complete');
|
|
979
1227
|
};
|
|
980
1228
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { OpenCodeDBReader } from './reader';
|
|
2
|
+
export type {
|
|
3
|
+
DBMessage,
|
|
4
|
+
DBPart,
|
|
5
|
+
DBSession,
|
|
6
|
+
DBTextPart,
|
|
7
|
+
DBTodo,
|
|
8
|
+
DBToolCall,
|
|
9
|
+
MessageTokens,
|
|
10
|
+
OpenCodeDBConfig,
|
|
11
|
+
SessionCostSummary,
|
|
12
|
+
SessionStatus,
|
|
13
|
+
SessionSummary,
|
|
14
|
+
SessionTreeNode,
|
|
15
|
+
TodoSummary,
|
|
16
|
+
} from './types';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const QUERIES = {
|
|
2
|
+
CHECK_TABLES:
|
|
3
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name IN ('session', 'message', 'part', 'todo')",
|
|
4
|
+
|
|
5
|
+
GET_SESSION: 'SELECT * FROM session WHERE id = ?',
|
|
6
|
+
GET_CHILD_SESSIONS: 'SELECT * FROM session WHERE parent_id = ? ORDER BY time_created DESC',
|
|
7
|
+
GET_SESSIONS_BY_PROJECT: 'SELECT * FROM session WHERE project_id = ? ORDER BY time_created DESC',
|
|
8
|
+
GET_DESCENDANT_SESSIONS: `WITH RECURSIVE descendants AS (
|
|
9
|
+
SELECT * FROM session WHERE parent_id = ?
|
|
10
|
+
UNION ALL
|
|
11
|
+
SELECT s.* FROM session s JOIN descendants d ON s.parent_id = d.id
|
|
12
|
+
) SELECT * FROM descendants ORDER BY time_created DESC`,
|
|
13
|
+
|
|
14
|
+
GET_MESSAGES:
|
|
15
|
+
'SELECT * FROM message WHERE session_id = ? ORDER BY time_created DESC LIMIT ? OFFSET ?',
|
|
16
|
+
GET_LATEST_MESSAGE:
|
|
17
|
+
'SELECT * FROM message WHERE session_id = ? ORDER BY time_created DESC LIMIT 1',
|
|
18
|
+
GET_MESSAGE_COUNT: 'SELECT COUNT(*) as count FROM message WHERE session_id = ?',
|
|
19
|
+
|
|
20
|
+
GET_ACTIVE_TOOLS: `SELECT * FROM part WHERE session_id = ?
|
|
21
|
+
AND json_valid(data)
|
|
22
|
+
AND json_extract(data, '$.type') IN ('tool', 'tool-invocation')
|
|
23
|
+
AND json_extract(data, '$.state.status') IN ('pending', 'running')
|
|
24
|
+
ORDER BY time_created DESC`,
|
|
25
|
+
GET_TOOL_HISTORY: `SELECT * FROM part WHERE session_id = ?
|
|
26
|
+
AND json_valid(data)
|
|
27
|
+
AND json_extract(data, '$.type') IN ('tool', 'tool-invocation')
|
|
28
|
+
ORDER BY time_created DESC LIMIT ?`,
|
|
29
|
+
GET_TEXT_PARTS: `SELECT * FROM part WHERE session_id = ?
|
|
30
|
+
AND json_valid(data)
|
|
31
|
+
AND json_extract(data, '$.type') = 'text'
|
|
32
|
+
ORDER BY time_created DESC LIMIT ?`,
|
|
33
|
+
|
|
34
|
+
GET_TODOS: 'SELECT * FROM todo WHERE session_id = ? ORDER BY position ASC',
|
|
35
|
+
|
|
36
|
+
GET_SESSION_COST: `SELECT
|
|
37
|
+
COALESCE(SUM(json_extract(data, '$.cost')), 0) as total_cost,
|
|
38
|
+
COALESCE(SUM(json_extract(data, '$.tokens.total')), 0) as total_tokens,
|
|
39
|
+
COALESCE(SUM(json_extract(data, '$.tokens.input')), 0) as input_tokens,
|
|
40
|
+
COALESCE(SUM(json_extract(data, '$.tokens.output')), 0) as output_tokens,
|
|
41
|
+
COALESCE(SUM(json_extract(data, '$.tokens.reasoning')), 0) as reasoning_tokens,
|
|
42
|
+
COALESCE(SUM(json_extract(data, '$.tokens.cache.read')), 0) as cache_read,
|
|
43
|
+
COALESCE(SUM(json_extract(data, '$.tokens.cache.write')), 0) as cache_write,
|
|
44
|
+
COUNT(*) as message_count
|
|
45
|
+
FROM message WHERE session_id = ? AND json_valid(data) AND json_extract(data, '$.role') = 'assistant'`,
|
|
46
|
+
|
|
47
|
+
SEARCH_SESSIONS: `SELECT id, project_id, parent_id, slug, directory, title, version, share_url, summary_additions, summary_deletions, summary_files, summary_diffs, time_created, time_updated, time_compacting, time_archived FROM session WHERE title LIKE ? COLLATE NOCASE ORDER BY time_updated DESC`,
|
|
48
|
+
|
|
49
|
+
SEARCH_SESSIONS_LIMITED: `SELECT id, project_id, parent_id, slug, directory, title, version, share_url, summary_additions, summary_deletions, summary_files, summary_diffs, time_created, time_updated, time_compacting, time_archived FROM session WHERE title LIKE ? COLLATE NOCASE ORDER BY time_updated DESC LIMIT ?`,
|
|
50
|
+
} as const;
|