@kafca/agentdock 0.1.24 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +2 -0
  2. package/dist/renderer/assets/{Badge-CXcUMqb-.js → Badge-Bn2h-Qkf.js} +1 -1
  3. package/dist/renderer/assets/{Chat-BRxdPzi-.js → Chat-Cgo9aneg.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-sWGBIfIb.js → ChatMarkdown-CYzHfSiR.js} +1 -1
  5. package/dist/renderer/assets/{Config-DqxuxIGg.js → Config-DljB2-eP.js} +1 -1
  6. package/dist/renderer/assets/{CronList-OgHxbti5.js → CronList-DeVbiY9a.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-DmklXiH1.js → Dashboard-fSqx5sm8.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BvDmi8dq.js → EmptyState-DgqKDwlq.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Bj59Hdpo.js → KnowledgeDetail-u8s2SmPV.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-CigQ-nSo.js → KnowledgeHome-BdQw8nbY.js} +1 -1
  11. package/dist/renderer/assets/{Logs-Bfc7gBpY.js → Logs-DSbndMpy.js} +1 -1
  12. package/dist/renderer/assets/{Modal-CfmiFknz.js → Modal-0d0EmAuK.js} +1 -1
  13. package/dist/renderer/assets/{Page-BxtLEw_b.js → Page-CVbOH_Kh.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-CcUphxGm.js → ProjectDetail-C91tF7uA.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-CVkvDIfy.js → ProjectList-D78Pc-BQ.js} +1 -1
  16. package/dist/renderer/assets/{Select-DgQMZ0MD.js → Select-BbKzAIuO.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-CRmGKfBx.js → SessionChat-BERvL5e4.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-D0E8XbYw.js → SessionList-Bomz335M.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-CLr3amU2.js +23 -0
  20. package/dist/renderer/assets/{Workspace-Q4g4rlRT.js → Workspace-In91z4W2.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-BySN_zhF.js → arrow-left-FBnVKKBl.js} +1 -1
  22. package/dist/renderer/assets/{book-open-BGlrnrTc.js → book-open-4_CuP6Sa.js} +1 -1
  23. package/dist/renderer/assets/{check-BiJogEa5.js → check-vsoVjvac.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-D2fHRJv6.js → chevron-down-DFpmFTJW.js} +1 -1
  25. package/dist/renderer/assets/{circle-CIcjixmI.js → circle-vRULegER.js} +1 -1
  26. package/dist/renderer/assets/{desktop-DAXY9Bxv.js → desktop-BkMW-h5p.js} +2 -2
  27. package/dist/renderer/assets/{desktop-DyA_NYew.js → desktop-ul0Tpzyc.js} +1 -1
  28. package/dist/renderer/assets/{funnel-BUzwb-id.js → funnel-Gu0E_hJ_.js} +1 -1
  29. package/dist/renderer/assets/{heart-DZEQ1z_V.js → heart-DsdofOHf.js} +1 -1
  30. package/dist/renderer/assets/{index-CB_KyLOr.js → index-BD3aCyBa.js} +3 -3
  31. package/dist/renderer/assets/{index-DgsIo4vJ.js → index-pJUFobU7.js} +1 -1
  32. package/dist/renderer/assets/{pencil-DGL4agcl.js → pencil-DbKmV8nD.js} +1 -1
  33. package/dist/renderer/assets/{play-DzasCLM4.js → play-BDMsRxSm.js} +1 -1
  34. package/dist/renderer/assets/{plug-dubNMe_m.js → plug-t2dMl78-.js} +1 -1
  35. package/dist/renderer/assets/{plus-DN4kML4k.js → plus-BUb6zOAe.js} +1 -1
  36. package/dist/renderer/assets/projects-4F6D_Q8t.js +1 -0
  37. package/dist/renderer/assets/{save-CNIc9tjl.js → save-C-_5rQdv.js} +1 -1
  38. package/dist/renderer/assets/{search-C0EGbF3I.js → search-R3ekHFPM.js} +1 -1
  39. package/dist/renderer/assets/{server-BMxQ4zmc.js → server-BTuvHf5n.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Bf5O-iMi.js → session-utils-ClX6VPFG.js} +1 -1
  41. package/dist/renderer/assets/{sessions-BMSs6rEI.js → sessions-AFKIC-ub.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-i3Tf1TUz.js → shield-check-CqMW8CHe.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-SKrcp18f.js → trash-2-BNLcHyUw.js} +1 -1
  44. package/dist/renderer/assets/{user-cNUZddjZ.js → user-BCCN8FYK.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-Dyb84ANR.js → wifi-off-BsNsx3oX.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/electron/agent-runtime-detector.test.js +64 -0
  48. package/dist-electron/electron/architecture-docs.test.js +56 -0
  49. package/dist-electron/electron/channel-content.test.js +8 -0
  50. package/dist-electron/electron/lac-cli.test.js +248 -0
  51. package/dist-electron/electron/local-core-refactor.test.js +666 -3
  52. package/dist-electron/electron/plugin-kernel.test.js +108 -0
  53. package/dist-electron/electron/renderer-packaging.test.js +77 -0
  54. package/dist-electron/electron/workspace-task-store.test.js +57 -0
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +67 -6
  56. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js +76 -0
  57. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +201 -0
  58. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +29 -0
  59. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +30 -18
  60. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +112 -210
  61. package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +60 -0
  62. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +15 -3
  63. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +11 -0
  64. package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +13 -1
  65. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +8 -1
  66. package/dist-electron/services/local-ai-core/src/cli/lac.js +3 -1
  67. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +1 -0
  68. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
  69. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +221 -10
  70. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +39 -9
  71. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -0
  72. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +1 -0
  73. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +439 -0
  74. package/dist-electron/services/local-ai-core/src/runtime/server.js +208 -303
  75. package/dist-electron/shared/desktop.js +7 -3
  76. package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +187 -0
  77. package/dist-electron/src/pages/Threads/thread-chat-model.js +1 -0
  78. package/dist-electron/src/pages/Threads/thread-chat-page-state.js +96 -0
  79. package/dist-electron/src/pages/Threads/thread-chat-permission.js +18 -0
  80. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +277 -0
  81. package/package.json +4 -2
  82. package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +0 -23
  83. 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