@kafca/agentdock 0.1.24 → 0.1.26
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/README.md +2 -0
- package/dist/renderer/assets/{Badge-CXcUMqb-.js → Badge-qZHTUGI3.js} +1 -1
- package/dist/renderer/assets/{Chat-BRxdPzi-.js → Chat-W35BzaZk.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-sWGBIfIb.js → ChatMarkdown-DxKT_YGp.js} +1 -1
- package/dist/renderer/assets/{Config-DqxuxIGg.js → Config-COTX6LDN.js} +1 -1
- package/dist/renderer/assets/{CronList-OgHxbti5.js → CronList-kxlfu9Iq.js} +1 -1
- package/dist/renderer/assets/{Dashboard-DmklXiH1.js → Dashboard-Y4PGxzuV.js} +2 -2
- package/dist/renderer/assets/{EmptyState-BvDmi8dq.js → EmptyState-Cjq33EOF.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-Bj59Hdpo.js → KnowledgeDetail-CztNt7i8.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-CigQ-nSo.js → KnowledgeHome-BAOyy3I9.js} +1 -1
- package/dist/renderer/assets/{Logs-Bfc7gBpY.js → Logs-PRZ1IIEi.js} +1 -1
- package/dist/renderer/assets/{Modal-CfmiFknz.js → Modal-B1rgqkBM.js} +1 -1
- package/dist/renderer/assets/{Page-BxtLEw_b.js → Page-CjzAhgaU.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-CcUphxGm.js → ProjectDetail-XGh_5XxQ.js} +1 -1
- package/dist/renderer/assets/{ProjectList-CVkvDIfy.js → ProjectList-BkPTpQRZ.js} +1 -1
- package/dist/renderer/assets/{Select-DgQMZ0MD.js → Select-BZd1QJVw.js} +1 -1
- package/dist/renderer/assets/{SessionChat-CRmGKfBx.js → SessionChat-C3sOTWGT.js} +1 -1
- package/dist/renderer/assets/{SessionList-D0E8XbYw.js → SessionList-B5WK1big.js} +1 -1
- package/dist/renderer/assets/ThreadChat-DWiBLHC7.js +23 -0
- package/dist/renderer/assets/{Workspace-Q4g4rlRT.js → Workspace-CQ64VK-o.js} +1 -1
- package/dist/renderer/assets/{arrow-left-BySN_zhF.js → arrow-left-FLdaWHZ8.js} +1 -1
- package/dist/renderer/assets/{book-open-BGlrnrTc.js → book-open-DaSioPS9.js} +1 -1
- package/dist/renderer/assets/{check-BiJogEa5.js → check-BiSmsuGR.js} +1 -1
- package/dist/renderer/assets/{chevron-down-D2fHRJv6.js → chevron-down-pZ2KjmDR.js} +1 -1
- package/dist/renderer/assets/{circle-CIcjixmI.js → circle-296S3nw9.js} +1 -1
- package/dist/renderer/assets/{desktop-DyA_NYew.js → desktop-88jgnOZV.js} +1 -1
- package/dist/renderer/assets/{desktop-DAXY9Bxv.js → desktop-BkMW-h5p.js} +2 -2
- package/dist/renderer/assets/{funnel-BUzwb-id.js → funnel-Dvz31PLy.js} +1 -1
- package/dist/renderer/assets/{heart-DZEQ1z_V.js → heart-D9mxSZVO.js} +1 -1
- package/dist/renderer/assets/{index-DgsIo4vJ.js → index-CZA4lqvM.js} +1 -1
- package/dist/renderer/assets/{index-CB_KyLOr.js → index-dOdOWAa2.js} +3 -3
- package/dist/renderer/assets/{pencil-DGL4agcl.js → pencil-B2fRbaAo.js} +1 -1
- package/dist/renderer/assets/{play-DzasCLM4.js → play-CWPRHypD.js} +1 -1
- package/dist/renderer/assets/{plug-dubNMe_m.js → plug-B7IBVwSo.js} +1 -1
- package/dist/renderer/assets/{plus-DN4kML4k.js → plus-Cn3Awirs.js} +1 -1
- package/dist/renderer/assets/projects-CmCopAi6.js +1 -0
- package/dist/renderer/assets/{save-CNIc9tjl.js → save-MVjXRqom.js} +1 -1
- package/dist/renderer/assets/{search-C0EGbF3I.js → search-Bu5c7wTX.js} +1 -1
- package/dist/renderer/assets/{server-BMxQ4zmc.js → server-aDvfqBg_.js} +1 -1
- package/dist/renderer/assets/{session-utils-Bf5O-iMi.js → session-utils-DjDpjT9r.js} +1 -1
- package/dist/renderer/assets/{sessions-BMSs6rEI.js → sessions-D7dV8h0H.js} +1 -1
- package/dist/renderer/assets/{shield-check-i3Tf1TUz.js → shield-check-DJdEhc_e.js} +1 -1
- package/dist/renderer/assets/{trash-2-SKrcp18f.js → trash-2-BF3ejnXL.js} +1 -1
- package/dist/renderer/assets/{user-cNUZddjZ.js → user-BwuWCrin.js} +1 -1
- package/dist/renderer/assets/{wifi-off-Dyb84ANR.js → wifi-off-BahJZfDs.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/electron/agent-runtime-detector.test.js +64 -0
- package/dist-electron/electron/architecture-docs.test.js +56 -0
- package/dist-electron/electron/channel-content.test.js +8 -0
- package/dist-electron/electron/lac-cli.test.js +248 -0
- package/dist-electron/electron/local-core-refactor.test.js +795 -20
- package/dist-electron/electron/plugin-kernel.test.js +108 -0
- package/dist-electron/electron/renderer-packaging.test.js +77 -0
- package/dist-electron/electron/workspace-task-store.test.js +57 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +67 -6
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js +76 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +201 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +29 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +30 -18
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +112 -210
- package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +60 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +44 -27
- package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +104 -12
- package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +13 -1
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +8 -1
- package/dist-electron/services/local-ai-core/src/cli/lac.js +3 -1
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +1 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +221 -10
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +39 -9
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +1 -0
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +439 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +208 -303
- package/dist-electron/shared/desktop.js +7 -3
- package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +187 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +1 -0
- package/dist-electron/src/pages/Threads/thread-chat-page-state.js +96 -0
- package/dist-electron/src/pages/Threads/thread-chat-permission.js +18 -0
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +277 -0
- package/package.json +4 -2
- package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +0 -23
- package/dist/renderer/assets/projects-migPtfRe.js +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyAssistantMessageChunk = applyAssistantMessageChunk;
|
|
4
|
+
exports.applyThoughtChunk = applyThoughtChunk;
|
|
5
|
+
exports.registerPendingToolCall = registerPendingToolCall;
|
|
6
|
+
exports.resolveToolCallForUpdate = resolveToolCallForUpdate;
|
|
7
|
+
exports.resolveFallbackToolCall = resolveFallbackToolCall;
|
|
8
|
+
exports.getToolCallsInOrder = getToolCallsInOrder;
|
|
9
|
+
exports.deletePendingToolCall = deletePendingToolCall;
|
|
10
|
+
exports.syncLegacyPendingToolCall = syncLegacyPendingToolCall;
|
|
11
|
+
exports.extractToolUpdateContent = extractToolUpdateContent;
|
|
12
|
+
exports.formatToolProgressMessage = formatToolProgressMessage;
|
|
13
|
+
exports.resolveToolUpdateDisplayTitle = resolveToolUpdateDisplayTitle;
|
|
14
|
+
exports.isEmptyRunningToolUpdate = isEmptyRunningToolUpdate;
|
|
15
|
+
exports.isTerminalToolStatus = isTerminalToolStatus;
|
|
16
|
+
exports.formatPlanProgress = formatPlanProgress;
|
|
17
|
+
exports.extractToolCallKey = extractToolCallKey;
|
|
18
|
+
exports.extractToolCallInput = extractToolCallInput;
|
|
19
|
+
function applyAssistantMessageChunk(currentTurn, text) {
|
|
20
|
+
if (!text) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
currentTurn.assistantText += text;
|
|
24
|
+
if (!currentTurn.previewStarted) {
|
|
25
|
+
currentTurn.previewStarted = true;
|
|
26
|
+
return {
|
|
27
|
+
bridgeType: 'preview_start',
|
|
28
|
+
previewHandle: currentTurn.previewHandle,
|
|
29
|
+
content: currentTurn.assistantText,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
bridgeType: 'update_message',
|
|
34
|
+
previewHandle: currentTurn.previewHandle,
|
|
35
|
+
content: currentTurn.assistantText,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function applyThoughtChunk(currentTurn, text) {
|
|
39
|
+
if (!text) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
currentTurn.thoughtText = mergeStreamingText(currentTurn.thoughtText, text);
|
|
43
|
+
const content = `💭 ${currentTurn.thoughtText.trim()}`;
|
|
44
|
+
if (!currentTurn.thoughtPreviewStarted) {
|
|
45
|
+
currentTurn.thoughtPreviewStarted = true;
|
|
46
|
+
return {
|
|
47
|
+
bridgeType: 'preview_start',
|
|
48
|
+
previewHandle: currentTurn.thoughtPreviewHandle,
|
|
49
|
+
messageId: currentTurn.thoughtMessageId,
|
|
50
|
+
content,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
bridgeType: 'update_message',
|
|
55
|
+
previewHandle: currentTurn.thoughtPreviewHandle,
|
|
56
|
+
messageId: currentTurn.thoughtMessageId,
|
|
57
|
+
content,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function mergeStreamingText(current, next) {
|
|
61
|
+
if (!current) {
|
|
62
|
+
return next;
|
|
63
|
+
}
|
|
64
|
+
if (next.startsWith(current)) {
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
if (current.endsWith(next)) {
|
|
68
|
+
return current;
|
|
69
|
+
}
|
|
70
|
+
const maxOverlap = Math.min(current.length, next.length);
|
|
71
|
+
for (let size = maxOverlap; size > 0; size -= 1) {
|
|
72
|
+
if (current.endsWith(next.slice(0, size))) {
|
|
73
|
+
return current + next.slice(size);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return current + next;
|
|
77
|
+
}
|
|
78
|
+
function registerPendingToolCall(input) {
|
|
79
|
+
const title = String(input.update.title || 'Running tool').trim();
|
|
80
|
+
const nextSequence = (input.currentTurn.toolCallSequence || 0) + 1;
|
|
81
|
+
input.currentTurn.toolCallSequence = nextSequence;
|
|
82
|
+
const key = extractToolCallKey(input.update) || `sequence:${nextSequence}`;
|
|
83
|
+
const toolInput = extractToolCallInput(input.update);
|
|
84
|
+
const toolCall = {
|
|
85
|
+
key,
|
|
86
|
+
title,
|
|
87
|
+
messageId: `${input.runId}-tool-${nextSequence}`,
|
|
88
|
+
...(toolInput === undefined ? {} : { input: toolInput }),
|
|
89
|
+
sequence: nextSequence,
|
|
90
|
+
emitted: false,
|
|
91
|
+
};
|
|
92
|
+
input.currentTurn.pendingToolCalls = {
|
|
93
|
+
...(input.currentTurn.pendingToolCalls || {}),
|
|
94
|
+
[key]: toolCall,
|
|
95
|
+
};
|
|
96
|
+
input.currentTurn.pendingToolCallOrder = [
|
|
97
|
+
...(input.currentTurn.pendingToolCallOrder || []).filter((item) => item !== key),
|
|
98
|
+
key,
|
|
99
|
+
];
|
|
100
|
+
input.currentTurn.activeToolCallKey = key;
|
|
101
|
+
return toolCall;
|
|
102
|
+
}
|
|
103
|
+
function resolveToolCallForUpdate(currentTurn, update) {
|
|
104
|
+
const explicitKey = extractToolCallKey(update);
|
|
105
|
+
if (explicitKey && currentTurn.pendingToolCalls?.[explicitKey]) {
|
|
106
|
+
currentTurn.activeToolCallKey = explicitKey;
|
|
107
|
+
return currentTurn.pendingToolCalls[explicitKey];
|
|
108
|
+
}
|
|
109
|
+
return resolveFallbackToolCall(currentTurn);
|
|
110
|
+
}
|
|
111
|
+
function resolveFallbackToolCall(currentTurn) {
|
|
112
|
+
const active = currentTurn.activeToolCallKey
|
|
113
|
+
? currentTurn.pendingToolCalls?.[currentTurn.activeToolCallKey]
|
|
114
|
+
: undefined;
|
|
115
|
+
if (active) {
|
|
116
|
+
return active;
|
|
117
|
+
}
|
|
118
|
+
const ordered = getToolCallsInOrder(currentTurn);
|
|
119
|
+
return ordered[ordered.length - 1];
|
|
120
|
+
}
|
|
121
|
+
function getToolCallsInOrder(currentTurn) {
|
|
122
|
+
const toolCalls = currentTurn.pendingToolCalls || {};
|
|
123
|
+
const orderedKeys = currentTurn.pendingToolCallOrder || [];
|
|
124
|
+
return orderedKeys
|
|
125
|
+
.map((key) => toolCalls[key])
|
|
126
|
+
.filter((toolCall) => Boolean(toolCall));
|
|
127
|
+
}
|
|
128
|
+
function deletePendingToolCall(currentTurn, key) {
|
|
129
|
+
if (currentTurn.pendingToolCalls) {
|
|
130
|
+
delete currentTurn.pendingToolCalls[key];
|
|
131
|
+
}
|
|
132
|
+
currentTurn.pendingToolCallOrder = (currentTurn.pendingToolCallOrder || []).filter((item) => item !== key);
|
|
133
|
+
if (currentTurn.activeToolCallKey === key) {
|
|
134
|
+
currentTurn.activeToolCallKey = undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function syncLegacyPendingToolCall(currentTurn, toolCall) {
|
|
138
|
+
currentTurn.pendingToolCallTitle = toolCall?.title;
|
|
139
|
+
currentTurn.pendingToolCallId = toolCall?.messageId;
|
|
140
|
+
currentTurn.pendingToolCallDetail = toolCall?.detail;
|
|
141
|
+
currentTurn.activeToolCallKey = toolCall?.key;
|
|
142
|
+
}
|
|
143
|
+
function extractToolUpdateContent(content) {
|
|
144
|
+
return Array.isArray(content)
|
|
145
|
+
? content
|
|
146
|
+
.map((entry) => entry?.type === 'content' && entry?.content?.type === 'text'
|
|
147
|
+
? String(entry.content.text || '')
|
|
148
|
+
: '')
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
.join('\n')
|
|
151
|
+
: '';
|
|
152
|
+
}
|
|
153
|
+
function formatToolProgressMessage(input) {
|
|
154
|
+
const detail = [input.title, input.status, input.content].filter(Boolean).join(' - ');
|
|
155
|
+
return input.toolName ? `🔧 ${input.toolName}: ${detail || 'Tool update'}` : `🔧 ${detail || 'Tool update'}`;
|
|
156
|
+
}
|
|
157
|
+
function resolveToolUpdateDisplayTitle(input) {
|
|
158
|
+
const title = input.title.trim();
|
|
159
|
+
if (input.priorDetail && (isTerminalToolStatus(input.status) || /^tool update$/i.test(title))) {
|
|
160
|
+
return input.priorDetail;
|
|
161
|
+
}
|
|
162
|
+
if (isTerminalToolStatus(input.status) && /^tool update$/i.test(title)) {
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
return input.title;
|
|
166
|
+
}
|
|
167
|
+
function isEmptyRunningToolUpdate(input) {
|
|
168
|
+
return !input.content.trim() &&
|
|
169
|
+
/^running$/i.test(input.status) &&
|
|
170
|
+
(!input.title.trim() || /^tool update$/i.test(input.title));
|
|
171
|
+
}
|
|
172
|
+
function isTerminalToolStatus(status) {
|
|
173
|
+
return /^(completed|failed|error|cancelled|canceled)$/i.test(status.trim());
|
|
174
|
+
}
|
|
175
|
+
function formatPlanProgress(entries) {
|
|
176
|
+
const summary = entries
|
|
177
|
+
.map((entry) => String(entry?.content || '').trim())
|
|
178
|
+
.filter(Boolean)
|
|
179
|
+
.join(' | ');
|
|
180
|
+
return summary ? `💭 ${summary}` : '';
|
|
181
|
+
}
|
|
182
|
+
function extractToolCallKey(update) {
|
|
183
|
+
for (const key of ['toolCallId', 'tool_call_id', 'callId', 'call_id', 'invocationId', 'invocation_id', 'id']) {
|
|
184
|
+
const value = update[key];
|
|
185
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
186
|
+
const normalized = String(value).trim();
|
|
187
|
+
if (normalized) {
|
|
188
|
+
return normalized;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return '';
|
|
193
|
+
}
|
|
194
|
+
function extractToolCallInput(update) {
|
|
195
|
+
for (const key of ['input', 'parameters', 'arguments', 'args', 'rawInput']) {
|
|
196
|
+
if (Object.prototype.hasOwnProperty.call(update, key)) {
|
|
197
|
+
return update[key];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
@@ -68,6 +68,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
68
68
|
const created = await this.options.transport.request(session, 'session/new', {
|
|
69
69
|
cwd: config.workDir,
|
|
70
70
|
mcpServers: [],
|
|
71
|
+
_meta: this.buildSessionMeta(threadId),
|
|
71
72
|
}, 30000);
|
|
72
73
|
session.sessionId = String(created.sessionId || created.session_id || created.id || created.session?.sessionId || created.session?.session_id || created.session?.id || '').trim();
|
|
73
74
|
if (!session.sessionId) {
|
|
@@ -82,6 +83,16 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
82
83
|
}
|
|
83
84
|
return session;
|
|
84
85
|
}
|
|
86
|
+
async setThreadMode(threadId, mode) {
|
|
87
|
+
const session = this.sessions.get(threadId);
|
|
88
|
+
if (!session || session.closed || !session.sessionId) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await this.options.transport.request(session, 'session/set_mode', {
|
|
92
|
+
sessionId: session.sessionId,
|
|
93
|
+
modeId: mode,
|
|
94
|
+
}, 30000);
|
|
95
|
+
}
|
|
85
96
|
async interruptRun(runId) {
|
|
86
97
|
const threadId = this.options.runThreadMap.get(runId);
|
|
87
98
|
if (!threadId) {
|
|
@@ -154,6 +165,10 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
154
165
|
LOCAL_AI_WORKSPACE_ID: row.workspace_id,
|
|
155
166
|
LOCAL_AI_THREAD_ID: threadId,
|
|
156
167
|
};
|
|
168
|
+
const workspace = this.options.store.getWorkspaceRegistryEntry(row.workspace_id);
|
|
169
|
+
if (workspace?.path) {
|
|
170
|
+
env.LOCAL_AI_WORKSPACE_PATH = workspace.path;
|
|
171
|
+
}
|
|
157
172
|
if (binding) {
|
|
158
173
|
env.LOCAL_AI_PLATFORM = binding.platform;
|
|
159
174
|
env.LOCAL_AI_ROUTE_TYPE = 'lark_chat';
|
|
@@ -167,5 +182,19 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
167
182
|
}
|
|
168
183
|
return env;
|
|
169
184
|
}
|
|
185
|
+
buildSessionMeta(threadId) {
|
|
186
|
+
const row = this.options.store.getThreadRow(threadId);
|
|
187
|
+
const mode = String(row?.agent_mode || '').trim();
|
|
188
|
+
if (!mode || mode === 'default') {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
claudeCode: {
|
|
193
|
+
options: {
|
|
194
|
+
permissionMode: mode,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
170
199
|
}
|
|
171
200
|
exports.LocalCoreAcpSessionCoordinator = LocalCoreAcpSessionCoordinator;
|
|
@@ -31,7 +31,8 @@ class LocalCoreAcpStore {
|
|
|
31
31
|
history_count INTEGER NOT NULL DEFAULT 0,
|
|
32
32
|
excerpt TEXT NOT NULL DEFAULT '',
|
|
33
33
|
acp_session_id TEXT,
|
|
34
|
-
acp_supports_load INTEGER NOT NULL DEFAULT 0
|
|
34
|
+
acp_supports_load INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
agent_mode TEXT NOT NULL DEFAULT 'default'
|
|
35
36
|
);
|
|
36
37
|
CREATE INDEX IF NOT EXISTS idx_threads_workspace_updated ON threads (workspace_id, updated_at DESC);
|
|
37
38
|
CREATE TABLE IF NOT EXISTS messages (
|
|
@@ -39,6 +40,7 @@ class LocalCoreAcpStore {
|
|
|
39
40
|
thread_id TEXT NOT NULL,
|
|
40
41
|
role TEXT NOT NULL,
|
|
41
42
|
content TEXT NOT NULL,
|
|
43
|
+
tool_call_json TEXT,
|
|
42
44
|
timestamp TEXT NOT NULL,
|
|
43
45
|
kind TEXT NOT NULL,
|
|
44
46
|
seq INTEGER NOT NULL,
|
|
@@ -219,14 +221,16 @@ class LocalCoreAcpStore {
|
|
|
219
221
|
CREATE INDEX IF NOT EXISTS idx_audit_events_task_created ON audit_events (task_id, created_at DESC);
|
|
220
222
|
CREATE INDEX IF NOT EXISTS idx_audit_events_type_created ON audit_events (type, created_at DESC);
|
|
221
223
|
`);
|
|
224
|
+
this.ensureColumn('messages', 'tool_call_json', 'TEXT');
|
|
222
225
|
this.ensureColumn('scheduled_jobs', 'execution_mode', "TEXT NOT NULL DEFAULT 'same-thread'");
|
|
226
|
+
this.ensureColumn('threads', 'agent_mode', "TEXT NOT NULL DEFAULT 'default'");
|
|
223
227
|
}
|
|
224
228
|
close() {
|
|
225
229
|
this.db.close();
|
|
226
230
|
}
|
|
227
231
|
listThreadSummaries(workspaceId) {
|
|
228
232
|
const rows = this.db.prepare(`
|
|
229
|
-
SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt
|
|
233
|
+
SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, acp_session_id, acp_supports_load, agent_mode
|
|
230
234
|
FROM threads
|
|
231
235
|
WHERE workspace_id = ?
|
|
232
236
|
ORDER BY updated_at DESC
|
|
@@ -249,15 +253,15 @@ class LocalCoreAcpStore {
|
|
|
249
253
|
const row = this.db.prepare('SELECT COUNT(*) AS total FROM threads WHERE workspace_id = ?').get(workspaceId);
|
|
250
254
|
return Number(row?.total || 0);
|
|
251
255
|
}
|
|
252
|
-
createThread(workspaceId, title, agentType = desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
|
|
256
|
+
createThread(workspaceId, title, agentType = desktop_js_1.LOCALCORE_ACP_AGENT_TYPE, agentMode = 'default') {
|
|
253
257
|
const sessionId = (0, node_crypto_1.randomUUID)();
|
|
254
258
|
const threadId = (0, workspace_thread_id_js_1.encodeThreadId)(workspaceId, sessionId);
|
|
255
259
|
const now = new Date().toISOString();
|
|
256
260
|
const bridgeSessionKey = `${desktop_js_1.LOCALCORE_ACP_AGENT_TYPE}:${workspaceId}:${sessionId}`;
|
|
257
261
|
this.db.prepare(`
|
|
258
|
-
INSERT INTO threads (id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt)
|
|
259
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, '')
|
|
260
|
-
`).run(threadId, workspaceId, sessionId, bridgeSessionKey, title, agentType, now, now);
|
|
262
|
+
INSERT INTO threads (id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, agent_mode)
|
|
263
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, '', ?)
|
|
264
|
+
`).run(threadId, workspaceId, sessionId, bridgeSessionKey, title, agentType, now, now, agentMode || 'default');
|
|
261
265
|
return {
|
|
262
266
|
id: threadId,
|
|
263
267
|
workspaceId,
|
|
@@ -280,7 +284,7 @@ class LocalCoreAcpStore {
|
|
|
280
284
|
throw new Error(`Thread not found: ${threadId}`);
|
|
281
285
|
}
|
|
282
286
|
const messages = this.db.prepare(`
|
|
283
|
-
SELECT id, thread_id, role, content, timestamp, kind, seq
|
|
287
|
+
SELECT id, thread_id, role, content, tool_call_json, timestamp, kind, seq
|
|
284
288
|
FROM messages
|
|
285
289
|
WHERE thread_id = ?
|
|
286
290
|
ORDER BY seq ASC
|
|
@@ -300,6 +304,7 @@ class LocalCoreAcpStore {
|
|
|
300
304
|
id: message.id,
|
|
301
305
|
role: message.role,
|
|
302
306
|
content: message.content,
|
|
307
|
+
toolCall: parseJson(message.tool_call_json || 'null', null) || undefined,
|
|
303
308
|
timestamp: message.timestamp,
|
|
304
309
|
kind: message.kind,
|
|
305
310
|
})),
|
|
@@ -314,7 +319,7 @@ class LocalCoreAcpStore {
|
|
|
314
319
|
deleteThread(threadId) {
|
|
315
320
|
this.db.prepare('DELETE FROM threads WHERE id = ?').run(threadId);
|
|
316
321
|
}
|
|
317
|
-
appendMessage(threadId, role, content, kind) {
|
|
322
|
+
appendMessage(threadId, role, content, kind, toolCall) {
|
|
318
323
|
const timestamp = new Date().toISOString();
|
|
319
324
|
const nextSequenceRow = this.db.prepare('SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM messages WHERE thread_id = ?').get(threadId);
|
|
320
325
|
const nextSeq = Number(nextSequenceRow?.next_seq || 0);
|
|
@@ -323,9 +328,9 @@ class LocalCoreAcpStore {
|
|
|
323
328
|
this.db.exec('BEGIN IMMEDIATE');
|
|
324
329
|
try {
|
|
325
330
|
this.db.prepare(`
|
|
326
|
-
INSERT INTO messages (id, thread_id, role, content, timestamp, kind, seq)
|
|
327
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
328
|
-
`).run(id, threadId, role, content, timestamp, kind, nextSeq);
|
|
331
|
+
INSERT INTO messages (id, thread_id, role, content, tool_call_json, timestamp, kind, seq)
|
|
332
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
333
|
+
`).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, nextSeq);
|
|
329
334
|
this.db.prepare(`
|
|
330
335
|
UPDATE threads
|
|
331
336
|
SET updated_at = ?, history_count = history_count + 1, excerpt = ?
|
|
@@ -339,7 +344,7 @@ class LocalCoreAcpStore {
|
|
|
339
344
|
}
|
|
340
345
|
return { id, timestamp };
|
|
341
346
|
}
|
|
342
|
-
upsertMessage(threadId, id, role, content, kind) {
|
|
347
|
+
upsertMessage(threadId, id, role, content, kind, toolCall) {
|
|
343
348
|
const timestamp = new Date().toISOString();
|
|
344
349
|
const excerpt = (0, workspace_thread_mappers_js_1.normalizeMessageContent)(content);
|
|
345
350
|
const existing = this.db.prepare('SELECT id FROM messages WHERE id = ? AND thread_id = ?').get(id, threadId);
|
|
@@ -348,17 +353,17 @@ class LocalCoreAcpStore {
|
|
|
348
353
|
if (existing) {
|
|
349
354
|
this.db.prepare(`
|
|
350
355
|
UPDATE messages
|
|
351
|
-
SET content = ?, timestamp = ?, kind = ?
|
|
356
|
+
SET content = ?, tool_call_json = ?, timestamp = ?, kind = ?
|
|
352
357
|
WHERE id = ? AND thread_id = ?
|
|
353
|
-
`).run(content, timestamp, kind, id, threadId);
|
|
358
|
+
`).run(content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, id, threadId);
|
|
354
359
|
}
|
|
355
360
|
else {
|
|
356
361
|
const nextSequenceRow = this.db.prepare('SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM messages WHERE thread_id = ?').get(threadId);
|
|
357
362
|
const nextSeq = Number(nextSequenceRow?.next_seq || 0);
|
|
358
363
|
this.db.prepare(`
|
|
359
|
-
INSERT INTO messages (id, thread_id, role, content, timestamp, kind, seq)
|
|
360
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
361
|
-
`).run(id, threadId, role, content, timestamp, kind, nextSeq);
|
|
364
|
+
INSERT INTO messages (id, thread_id, role, content, tool_call_json, timestamp, kind, seq)
|
|
365
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
366
|
+
`).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, nextSeq);
|
|
362
367
|
this.db.prepare(`
|
|
363
368
|
UPDATE threads
|
|
364
369
|
SET history_count = history_count + 1
|
|
@@ -942,11 +947,18 @@ class LocalCoreAcpStore {
|
|
|
942
947
|
}
|
|
943
948
|
getThreadRow(threadId) {
|
|
944
949
|
return this.db.prepare(`
|
|
945
|
-
SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, acp_session_id, acp_supports_load
|
|
950
|
+
SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, acp_session_id, acp_supports_load, agent_mode
|
|
946
951
|
FROM threads
|
|
947
952
|
WHERE id = ?
|
|
948
953
|
`).get(threadId);
|
|
949
954
|
}
|
|
955
|
+
updateThreadAgentMode(threadId, mode) {
|
|
956
|
+
this.db.prepare(`
|
|
957
|
+
UPDATE threads
|
|
958
|
+
SET agent_mode = ?, updated_at = ?
|
|
959
|
+
WHERE id = ?
|
|
960
|
+
`).run(mode, new Date().toISOString(), threadId);
|
|
961
|
+
}
|
|
950
962
|
updateThreadSession(threadId, sessionId, supportsLoad) {
|
|
951
963
|
this.db.prepare(`
|
|
952
964
|
UPDATE threads
|