@kafca/agentdock 0.1.28 → 0.1.30

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 (70) hide show
  1. package/README.md +5 -0
  2. package/dist/renderer/assets/{Badge-B-Ayib4h.js → Badge-DlVJI1Ys.js} +1 -1
  3. package/dist/renderer/assets/{Chat-CNEOxtyz.js → Chat-oa8OjaHQ.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-BTd1QBj9.js → ChatMarkdown-C-N8KS5C.js} +1 -1
  5. package/dist/renderer/assets/{Config-ByKhcLa6.js → Config-D9sL_d4D.js} +1 -1
  6. package/dist/renderer/assets/{CronList-0mi8dcC5.js → CronList-Z5JU3DHk.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-C8-tB_qg.js → Dashboard-D3BwbG43.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-TR-9nr5e.js → EmptyState-QOj765Ul.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-DKAxFOXc.js → KnowledgeDetail-J7n01mPu.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-CM-1IDr4.js → KnowledgeHome-BFnH0NVk.js} +1 -1
  11. package/dist/renderer/assets/{Logs-DI5RFPri.js → Logs-DfSUifXc.js} +1 -1
  12. package/dist/renderer/assets/{Modal-CQpa4NPP.js → Modal-QjEGhyru.js} +1 -1
  13. package/dist/renderer/assets/{Page-BHXEAwzQ.js → Page-qdHFSw7x.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-Dn5elnHx.js → ProjectDetail-DS_rfvhh.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-D6yvEgb5.js → ProjectList-COtyZd7H.js} +1 -1
  16. package/dist/renderer/assets/{Select-BbkcXnXL.js → Select-BQ8mncVu.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-Vkf-7gvX.js → SessionChat-DjVnjuQy.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-c5encjWd.js → SessionList-88dgGZJ8.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-BCdbTxBo.js +23 -0
  20. package/dist/renderer/assets/{Workspace-BC7hWqhP.js → Workspace-D_hzSJjX.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-I_Y1kdN9.js → arrow-left-BMpknulW.js} +1 -1
  22. package/dist/renderer/assets/{book-open-B3anUYvp.js → book-open-CiaOeQbU.js} +1 -1
  23. package/dist/renderer/assets/{check-BPNCDKgS.js → check-CgwYH4GF.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-BaxUcOIV.js → chevron-down-BzD2S1OO.js} +1 -1
  25. package/dist/renderer/assets/{circle-DJQiypFS.js → circle-BDHD7g7l.js} +1 -1
  26. package/dist/renderer/assets/{desktop-Cv51clcN.js → desktop-BiR5LPL5.js} +1 -1
  27. package/dist/renderer/assets/{funnel-DqKBtiqE.js → funnel-Bruf5FYA.js} +1 -1
  28. package/dist/renderer/assets/{heart-CmoNjvMO.js → heart-D7Mz04B0.js} +1 -1
  29. package/dist/renderer/assets/{index-DXJl2HOK.js → index-CL1e8m3U.js} +2 -2
  30. package/dist/renderer/assets/{index-BfXqhQ1Z.js → index-gZIRr4_o.js} +1 -1
  31. package/dist/renderer/assets/{pencil-CLUBszbw.js → pencil-C6gzDMse.js} +1 -1
  32. package/dist/renderer/assets/{play-l2RPZuAi.js → play-DqTXX-Pr.js} +1 -1
  33. package/dist/renderer/assets/{plug-CeUPIjRt.js → plug-ykmbeaO3.js} +1 -1
  34. package/dist/renderer/assets/{plus-BjkefS2P.js → plus-DHE60vMX.js} +1 -1
  35. package/dist/renderer/assets/projects-Bx5p10th.js +1 -0
  36. package/dist/renderer/assets/{save-D0yYt9tz.js → save-CGIvrdQI.js} +1 -1
  37. package/dist/renderer/assets/{search-B7s473Y4.js → search-D7IFAwyE.js} +1 -1
  38. package/dist/renderer/assets/{server-Dz_h8X_8.js → server-aOsirp7I.js} +1 -1
  39. package/dist/renderer/assets/{session-utils-BvNRTRXA.js → session-utils-Dxfp4ohQ.js} +1 -1
  40. package/dist/renderer/assets/{sessions-KPJsgKbQ.js → sessions-Db1XPbG2.js} +1 -1
  41. package/dist/renderer/assets/{shield-check-Wc5wfUUs.js → shield-check-ChXeQK-d.js} +1 -1
  42. package/dist/renderer/assets/{trash-2-7INdaB9w.js → trash-2-CfN-c1zw.js} +1 -1
  43. package/dist/renderer/assets/{user-CLbCgWNR.js → user-C7OHaBp4.js} +1 -1
  44. package/dist/renderer/assets/{wifi-off-BfDVPXbO.js → wifi-off-CpaJigye.js} +1 -1
  45. package/dist/renderer/index.html +1 -1
  46. package/dist-electron/electron/agent-runtime-detector.test.js +20 -0
  47. package/dist-electron/electron/lac-cli.test.js +2 -13
  48. package/dist-electron/electron/local-core-refactor.test.js +424 -64
  49. package/dist-electron/electron/workspace-task-store.test.js +118 -0
  50. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +17 -10
  51. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +245 -9
  52. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +24 -11
  53. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +36 -11
  54. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +2 -0
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +90 -7
  56. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +35 -40
  57. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +43 -18
  58. package/dist-electron/services/local-ai-core/src/cli/lac.js +1 -17
  59. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +2 -2
  60. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +1 -1
  61. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +31 -1
  62. package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +4 -1
  63. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +4 -1
  64. package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +4 -114
  65. package/dist-electron/src/pages/Threads/thread-chat-model.js +14 -23
  66. package/dist-electron/src/pages/Threads/thread-chat-page-state.js +1 -1
  67. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +58 -31
  68. package/package.json +1 -1
  69. package/dist/renderer/assets/ThreadChat-DY4wzomi.js +0 -23
  70. package/dist/renderer/assets/projects-De73QY_o.js +0 -1
@@ -91,25 +91,9 @@ async function handleAdd(flags, env, io, json) {
91
91
  if (!context.workspaceId) {
92
92
  throw new Error('scheduler add requires a workspace context. Set LOCAL_AI_WORKSPACE_ID or pass --workspace.');
93
93
  }
94
- const isLarkRoute = context.platform === 'lark'
95
- && ['channel.chat', 'lark_chat'].includes(context.routeType)
96
- && context.chatId
97
- && context.platformUserId;
98
94
  const job = await request(context.baseUrl, 'POST', '/scheduler/jobs', {
99
95
  workspaceId: context.workspaceId,
100
- platform: isLarkRoute ? 'lark' : 'local',
101
- route: isLarkRoute
102
- ? {
103
- type: 'channel.chat',
104
- channelId: context.chatId,
105
- participantId: context.platformUserId,
106
- ...(context.threadId ? { threadId: context.threadId } : {}),
107
- }
108
- : {
109
- type: 'local.thread',
110
- channelId: context.workspaceId,
111
- ...(context.threadId ? { threadId: context.threadId } : {}),
112
- },
96
+ ...(context.threadId ? { threadId: context.threadId } : {}),
113
97
  executionMode,
114
98
  triggerType: 'cron',
115
99
  cronExpr,
@@ -201,10 +201,10 @@ class WorkspaceRouter {
201
201
  await this.options.knowledgeAttachments.deleteThreadKnowledgeBaseLinks(threadId);
202
202
  return { deleted: true };
203
203
  }
204
- async sendThreadMessage(threadId, content) {
204
+ async sendThreadMessage(threadId, content, options) {
205
205
  const { workspaceId } = (0, workspace_thread_id_js_1.decodeThreadId)(threadId);
206
206
  const route = await this.getWorkspaceRoute(workspaceId);
207
- return this.localCoreAcp.sendThreadMessage(threadId, content, route.config);
207
+ return this.localCoreAcp.sendThreadMessage(threadId, content, route.config, options);
208
208
  }
209
209
  async sendThreadAction(threadId, content) {
210
210
  const { workspaceId } = (0, workspace_thread_id_js_1.decodeThreadId)(threadId);
@@ -21,7 +21,7 @@ const COMMAND_CANDIDATES = {
21
21
  pi: ['pi'],
22
22
  opencode: ['opencode'],
23
23
  codex: ['codex-acp', 'codex'],
24
- claudecode: ['claude-agent-acp'],
24
+ claudecode: ['claude-agent-acp', 'claude'],
25
25
  cursor: ['cursor-agent', 'cursor'],
26
26
  gemini: ['gemini'],
27
27
  qoder: ['qoder'],
@@ -191,7 +191,37 @@ class LocalCoreController extends node_events_1.EventEmitter {
191
191
  return job;
192
192
  }
193
193
  async createScheduledJob(input) {
194
- return this.scheduler.createJob(input);
194
+ return this.scheduler.createJob(this.resolveScheduledJobCreateInput(input));
195
+ }
196
+ resolveScheduledJobCreateInput(input) {
197
+ if (input.platform && input.route) {
198
+ return input;
199
+ }
200
+ const threadId = String(input.threadId || input.route?.threadId || '').trim();
201
+ if (threadId) {
202
+ const binding = this.runtime.store.getPlatformThreadBindingByThreadId(threadId);
203
+ if (binding && binding.workspace_id === input.workspaceId) {
204
+ return {
205
+ ...input,
206
+ platform: binding.platform,
207
+ route: {
208
+ type: 'channel.chat',
209
+ channelId: binding.chat_id,
210
+ participantId: binding.platform_user_id,
211
+ threadId,
212
+ },
213
+ };
214
+ }
215
+ }
216
+ return {
217
+ ...input,
218
+ platform: 'local',
219
+ route: {
220
+ type: 'local.thread',
221
+ channelId: input.workspaceId,
222
+ ...(threadId ? { threadId } : {}),
223
+ },
224
+ };
195
225
  }
196
226
  async updateScheduledJob(jobId, input) {
197
227
  return this.scheduler.updateJob(jobId, input);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalScheduleAdapter = void 0;
4
4
  const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'interrupted']);
5
+ const SCHEDULED_RUN_PERMISSION_MODE = 'bypassPermissions';
5
6
  class LocalScheduleAdapter {
6
7
  options;
7
8
  deliveryTargets = ['local'];
@@ -15,7 +16,9 @@ class LocalScheduleAdapter {
15
16
  const { job } = context;
16
17
  const workspaceRouter = this.options.getWorkspaceRouter();
17
18
  const threadId = await this.resolveThread(job);
18
- const sendResult = await workspaceRouter.sendThreadMessage(threadId, job.promptTemplate);
19
+ const sendResult = await workspaceRouter.sendThreadMessage(threadId, job.promptTemplate, {
20
+ permissionMode: SCHEDULED_RUN_PERMISSION_MODE,
21
+ });
19
22
  await this.waitForRun(sendResult.runId);
20
23
  const thread = await workspaceRouter.getThread(threadId);
21
24
  const replyText = [...thread.messages]
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ScheduledConversationExecutor = void 0;
4
4
  const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'interrupted']);
5
+ const SCHEDULED_RUN_PERMISSION_MODE = 'bypassPermissions';
5
6
  class ScheduledConversationExecutor {
6
7
  options;
7
8
  constructor(options) {
@@ -11,7 +12,9 @@ class ScheduledConversationExecutor {
11
12
  const target = await policy.resolveTarget(job);
12
13
  await policy.beforeExecute?.(target, job);
13
14
  try {
14
- const sendResult = await this.options.workspaceRouter.sendThreadMessage(target.threadId, prompt);
15
+ const sendResult = await this.options.workspaceRouter.sendThreadMessage(target.threadId, prompt, {
16
+ permissionMode: SCHEDULED_RUN_PERMISSION_MODE,
17
+ });
15
18
  await this.waitForRun(sendResult.runId, timeoutMs);
16
19
  const thread = await this.options.workspaceRouter.getThread(target.threadId);
17
20
  const replyText = [...thread.messages]
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isInteractivePermissionMessage = isInteractivePermissionMessage;
4
- exports.parseToolResultCard = parseToolResultCard;
5
4
  exports.toolCallToResultCard = toolCallToResultCard;
6
5
  exports.isEmptyRunningToolUpdateContent = isEmptyRunningToolUpdateContent;
7
6
  exports.isHiddenProgressMessage = isHiddenProgressMessage;
@@ -19,100 +18,6 @@ function isInteractivePermissionMessage(message, pendingPermissionRequest) {
19
18
  actionInteractive: message.actionInteractive,
20
19
  }, pendingPermissionRequest);
21
20
  }
22
- function parseToolResultCard(content) {
23
- const statusFirstMatch = content.match(/^\s*🔧\s*(.+?)\s*:\s*(running|completed|failed|error|cancelled|canceled)(?:\s*-\s*([\s\S]+?))?\s*$/i);
24
- if (statusFirstMatch) {
25
- const [, toolName, status, payload = ''] = statusFirstMatch;
26
- return {
27
- title: toolName.trim() || 'Tool call',
28
- status: status.trim(),
29
- output: parseToolResultPayload(payload),
30
- label: /^running$/i.test(status) ? '工具调用' : '工具结果',
31
- };
32
- }
33
- const namedStatusMatch = content.match(/^\s*🔧\s*(.+?)\s*:\s*([\s\S]*?)\s*-\s*(running|completed|failed|error|cancelled|canceled)(?:\s*-\s*([\s\S]+?))?\s*$/i);
34
- if (namedStatusMatch) {
35
- const [, toolName, detail, status, payload = ''] = namedStatusMatch;
36
- const trimmedDetail = detail.trim();
37
- return {
38
- title: toolName.trim() || 'Tool call',
39
- status: status.trim(),
40
- output: parseToolResultPayload(payload),
41
- label: /^running$/i.test(status) ? '工具调用' : '工具结果',
42
- subtitle: trimmedDetail || undefined,
43
- };
44
- }
45
- const namedUpdateMatch = content.match(/^\s*🔧\s*(.+?)\s*:\s*(Tool update)\s*-\s*([^-]+?)\s*-\s*([\s\S]+?)\s*$/i);
46
- if (namedUpdateMatch) {
47
- const [, toolName, updateTitle, status, payload] = namedUpdateMatch;
48
- const trimmedPayload = payload.trim();
49
- try {
50
- const parsed = JSON.parse(trimmedPayload);
51
- const value = typeof parsed.output === 'string'
52
- ? parsed.output
53
- : parsed.output ?? parsed.error ?? parsed;
54
- return {
55
- title: toolName.trim() || updateTitle,
56
- status: status.trim(),
57
- output: typeof value === 'string' ? value : JSON.stringify(value, null, 2),
58
- label: '工具结果',
59
- };
60
- }
61
- catch {
62
- return {
63
- title: toolName.trim() || updateTitle,
64
- status: status.trim(),
65
- output: trimmedPayload,
66
- label: '工具结果',
67
- };
68
- }
69
- }
70
- const updateMatch = content.match(/^\s*(?:🔧\s*)?(Tool update)\s*-\s*([^-]+?)\s*-\s*([\s\S]+?)\s*$/i);
71
- if (updateMatch) {
72
- const [, title, status, payload] = updateMatch;
73
- const trimmedPayload = payload.trim();
74
- if (isEmptyRunningToolUpdateContent(title, status.trim(), trimmedPayload)) {
75
- return null;
76
- }
77
- try {
78
- const parsed = JSON.parse(trimmedPayload);
79
- const value = typeof parsed.output === 'string'
80
- ? parsed.output
81
- : parsed.output ?? parsed.error ?? parsed;
82
- return {
83
- title,
84
- status: status.trim(),
85
- output: typeof value === 'string' ? value : JSON.stringify(value, null, 2),
86
- label: '工具结果',
87
- };
88
- }
89
- catch {
90
- return {
91
- title,
92
- status: status.trim(),
93
- output: trimmedPayload,
94
- label: '工具结果',
95
- };
96
- }
97
- }
98
- const callMatch = content.match(/^\s*🔧\s*(.+?)\s*$/);
99
- if (callMatch) {
100
- const rawTitle = callMatch[1].trim();
101
- if (/^Tool update\s*-\s*running(?:\s*-\s*)?$/i.test(rawTitle)) {
102
- return null;
103
- }
104
- const [name, ...rest] = rawTitle.split(':');
105
- const output = rest.join(':').trim();
106
- const statusMatch = output.match(/^(.*?)\s*-\s*(running|completed|failed|error|cancelled|canceled)\s*$/i);
107
- return {
108
- title: name.trim() || 'Tool call',
109
- status: statusMatch?.[2]?.trim() || 'running',
110
- output: statusMatch?.[1]?.trim() || output,
111
- label: statusMatch && !/^running$/i.test(statusMatch[2]) ? '工具结果' : '工具调用',
112
- };
113
- }
114
- return null;
115
- }
116
21
  function toolCallToResultCard(toolCall) {
117
22
  if (!toolCall) {
118
23
  return null;
@@ -143,28 +48,13 @@ function summarizeToolCallInput(input) {
143
48
  .map(([key, value]) => `${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`);
144
49
  return entries.length > 0 ? entries.join(', ') : undefined;
145
50
  }
146
- function parseToolResultPayload(payload) {
147
- const trimmedPayload = payload.trim();
148
- if (!trimmedPayload) {
149
- return '';
150
- }
151
- try {
152
- const parsed = JSON.parse(trimmedPayload);
153
- const value = typeof parsed.output === 'string'
154
- ? parsed.output
155
- : parsed.output ?? parsed.error ?? parsed;
156
- return typeof value === 'string' ? value : JSON.stringify(value, null, 2);
157
- }
158
- catch {
159
- return trimmedPayload;
160
- }
161
- }
162
51
  function isEmptyRunningToolUpdateContent(title, status, payload) {
163
52
  return /^Tool update$/i.test(title.trim()) && /^running$/i.test(status.trim()) && !payload.trim();
164
53
  }
165
- function isHiddenProgressMessage(content) {
166
- const normalized = content.trim();
167
- return /^🔧\s*Tool update\s*-\s*running(?:\s*-\s*)?$/i.test(normalized);
54
+ function isHiddenProgressMessage(message) {
55
+ return message.bridgeKind === 'tool' &&
56
+ message.toolCall?.status.trim().toLowerCase() === 'running' &&
57
+ !message.toolCall.output.trim();
168
58
  }
169
59
  function shouldCollapseToolResultByDefault(card) {
170
60
  return card.label === '工具结果' && Boolean(card.output.trim());
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.upsertSessionGroup = exports.ASSISTANT_REPLY_TIMEOUT_MS = void 0;
4
- exports.isInternalProgressMessage = isInternalProgressMessage;
5
- exports.isAwaitingInputMessage = isAwaitingInputMessage;
4
+ exports.isInternalProgressBridgeKind = isInternalProgressBridgeKind;
5
+ exports.isAwaitingInputBridgeStatus = isAwaitingInputBridgeStatus;
6
6
  exports.findStreamingPreviewMessage = findStreamingPreviewMessage;
7
7
  exports.shouldReplacePreviewWithReply = shouldReplacePreviewWithReply;
8
8
  exports.advancePreviewContent = advancePreviewContent;
@@ -31,26 +31,15 @@ exports.canStreamingPromoteTaskState = canStreamingPromoteTaskState;
31
31
  const desktop_1 = require("../../../shared/desktop");
32
32
  const session_utils_1 = require("../../lib/session-utils");
33
33
  exports.ASSISTANT_REPLY_TIMEOUT_MS = 90000;
34
- function isInternalProgressMessage(content) {
35
- if (!content) {
36
- return false;
37
- }
38
- return (content.startsWith('💭 ') ||
39
- content.startsWith('🔧 ') ||
40
- content.startsWith('📤 ') ||
41
- content.startsWith('⏳ '));
34
+ function isInternalProgressBridgeKind(bridgeKind) {
35
+ return bridgeKind === 'thought' ||
36
+ bridgeKind === 'plan' ||
37
+ bridgeKind === 'tool' ||
38
+ bridgeKind === 'status' ||
39
+ bridgeKind === 'permission';
42
40
  }
43
- function isAwaitingInputMessage(content) {
44
- if (!content) {
45
- return false;
46
- }
47
- const normalized = content.replace(/\s+/g, ' ').trim();
48
- return (/^Agent 提问(?:\s*\(\d+\/\d+\))?/i.test(normalized) ||
49
- normalized.includes('请回复选项编号') ||
50
- normalized.includes('直接输入你的回答') ||
51
- normalized.includes('等待你的回复') ||
52
- normalized.includes('请直接回复') ||
53
- normalized.includes('请输入你的回答'));
41
+ function isAwaitingInputBridgeStatus(bridgeStatus) {
42
+ return bridgeStatus === 'awaiting_input';
54
43
  }
55
44
  function findStreamingPreviewMessage(messages, previewHandle, replyCtx) {
56
45
  if (previewHandle) {
@@ -66,7 +55,7 @@ function shouldReplacePreviewWithReply(message, replyContent, replyCtx) {
66
55
  return false;
67
56
  }
68
57
  const content = message.streamTargetContent ?? message.content;
69
- return Boolean(replyContent && content === replyContent && !isInternalProgressMessage(content));
58
+ return Boolean(replyContent && content === replyContent && !isInternalProgressBridgeKind(message.bridgeKind));
70
59
  }
71
60
  function advancePreviewContent(content, target) {
72
61
  if (!target) {
@@ -110,7 +99,7 @@ function finalizeTurnMessageKinds(messages, turnKey) {
110
99
  if (candidates.length === 0) {
111
100
  return messages;
112
101
  }
113
- const finalMessage = [...candidates].reverse().find((message) => !isInternalProgressMessage(message.content));
102
+ const finalMessage = [...candidates].reverse().find((message) => !isInternalProgressBridgeKind(message.bridgeKind));
114
103
  const finalId = finalMessage?.id;
115
104
  return messages.map((message) => {
116
105
  if (message.turnKey !== turnKey || message.preview) {
@@ -151,6 +140,8 @@ function toMessagesFromThread(history) {
151
140
  role: message.role === 'user' ? 'user' : message.role === 'system' ? 'system' : 'assistant',
152
141
  content: message.role === 'user' ? extractVisibleMessageContent(message.content) : message.content,
153
142
  toolCall: message.toolCall,
143
+ bridgeKind: message.bridgeKind,
144
+ bridgeStatus: message.bridgeStatus,
154
145
  kind: message.kind === 'progress'
155
146
  ? 'progress'
156
147
  : message.kind === 'system'
@@ -67,7 +67,7 @@ function toComposerPermissionCard(pendingPermissionRequest) {
67
67
  };
68
68
  }
69
69
  function shouldRenderThreadChatMessage(message, composerPermissionCard) {
70
- if (message.kind === 'progress' && (0, thread_chat_message_blocks_1.isHiddenProgressMessage)(message.content)) {
70
+ if (message.kind === 'progress' && (0, thread_chat_message_blocks_1.isHiddenProgressMessage)(message)) {
71
71
  return false;
72
72
  }
73
73
  return !(0, thread_chat_message_blocks_1.isInteractivePermissionMessage)(message, composerPermissionCard);
@@ -170,9 +170,10 @@ function createKnowledgeBase(overrides) {
170
170
  {
171
171
  id: 'thought-preview',
172
172
  role: 'assistant',
173
- content: '💭 checking',
174
- streamTargetContent: '💭 checking the request',
173
+ content: 'checking',
174
+ streamTargetContent: 'checking the request',
175
175
  kind: 'progress',
176
+ bridgeKind: 'thought',
176
177
  order: 1,
177
178
  turnKey: 'run-1',
178
179
  preview: true,
@@ -198,8 +199,9 @@ function createKnowledgeBase(overrides) {
198
199
  {
199
200
  id: 'thought-preview',
200
201
  role: 'assistant',
201
- content: '💭 checking',
202
+ content: 'checking',
202
203
  kind: 'progress',
204
+ bridgeKind: 'thought',
203
205
  order: 0,
204
206
  turnKey: 'run-1',
205
207
  preview: true,
@@ -214,9 +216,10 @@ function createKnowledgeBase(overrides) {
214
216
  const thoughtPreview = {
215
217
  id: 'thought-preview',
216
218
  role: 'assistant',
217
- content: '💭 checking',
218
- streamTargetContent: '💭 checking the request',
219
+ content: 'checking',
220
+ streamTargetContent: 'checking the request',
219
221
  kind: 'progress',
222
+ bridgeKind: 'thought',
220
223
  order: 0,
221
224
  turnKey: 'run-1',
222
225
  preview: true,
@@ -241,16 +244,23 @@ function createKnowledgeBase(overrides) {
241
244
  {
242
245
  id: 'thought',
243
246
  role: 'assistant',
244
- content: '💭 checking',
247
+ content: 'checking',
245
248
  kind: 'final',
249
+ bridgeKind: 'thought',
246
250
  order: 1,
247
251
  turnKey: 'run-1',
248
252
  },
249
253
  {
250
254
  id: 'tool',
251
255
  role: 'assistant',
252
- content: '🔧 Read file',
256
+ content: 'Read file',
253
257
  kind: 'final',
258
+ bridgeKind: 'tool',
259
+ toolCall: {
260
+ name: 'Read',
261
+ status: 'running',
262
+ output: '',
263
+ },
254
264
  order: 2,
255
265
  turnKey: 'run-1',
256
266
  },
@@ -275,24 +285,39 @@ function createKnowledgeBase(overrides) {
275
285
  {
276
286
  id: 'thought',
277
287
  role: 'assistant',
278
- content: '💭 checking the request',
288
+ content: 'checking the request',
279
289
  kind: 'final',
290
+ bridgeKind: 'thought',
280
291
  order: 1,
281
292
  turnKey: 'run-1',
282
293
  },
283
294
  {
284
295
  id: 'tool-progress',
285
296
  role: 'assistant',
286
- content: '🔧 Read: package.json - running',
297
+ content: 'Read package.json',
287
298
  kind: 'final',
299
+ bridgeKind: 'tool',
300
+ toolCall: {
301
+ name: 'Read',
302
+ status: 'running',
303
+ detail: 'package.json',
304
+ output: '',
305
+ },
288
306
  order: 2,
289
307
  turnKey: 'run-1',
290
308
  },
291
309
  {
292
310
  id: 'tool-result',
293
311
  role: 'assistant',
294
- content: '🔧 Read: package.json - completed - {"output":"ok"}',
312
+ content: 'Read package.json',
295
313
  kind: 'final',
314
+ bridgeKind: 'tool',
315
+ toolCall: {
316
+ name: 'Read',
317
+ status: 'completed',
318
+ detail: 'package.json',
319
+ output: 'ok',
320
+ },
296
321
  order: 3,
297
322
  turnKey: 'run-1',
298
323
  },
@@ -356,24 +381,6 @@ function createKnowledgeBase(overrides) {
356
381
  strict_1.default.equal((0, thread_chat_model_1.advancePreviewContent)('Hel', 'Hello'), 'Hello');
357
382
  strict_1.default.equal((0, thread_chat_model_1.advancePreviewContent)('Mismatch', 'Hello'), 'H');
358
383
  });
359
- (0, node_test_1.default)('tool result card parsing preserves tool name and decoded output', () => {
360
- const card = (0, thread_chat_message_blocks_1.parseToolResultCard)('🔧 Read: package.json - completed - {"output":"ok"}');
361
- strict_1.default.ok(card);
362
- strict_1.default.equal(card.title, 'Read');
363
- strict_1.default.equal(card.status, 'completed');
364
- strict_1.default.equal(card.output, 'ok');
365
- strict_1.default.equal(card.label, '工具结果');
366
- strict_1.default.equal(card.subtitle, 'package.json');
367
- strict_1.default.equal((0, thread_chat_message_blocks_1.shouldCollapseToolResultByDefault)(card), true);
368
- });
369
- (0, node_test_1.default)('tool result card parsing accepts status-first tool updates', () => {
370
- const card = (0, thread_chat_message_blocks_1.parseToolResultCard)('🔧 bash: completed - total 32\n-rw-r--r-- file.md');
371
- strict_1.default.ok(card);
372
- strict_1.default.equal(card.title, 'bash');
373
- strict_1.default.equal(card.status, 'completed');
374
- strict_1.default.equal(card.output, 'total 32\n-rw-r--r-- file.md');
375
- strict_1.default.equal(card.label, '工具结果');
376
- });
377
384
  (0, node_test_1.default)('tool result card prefers structured tool call fields', () => {
378
385
  const card = (0, thread_chat_message_blocks_1.toolCallToResultCard)({
379
386
  name: 'bash',
@@ -389,8 +396,22 @@ function createKnowledgeBase(overrides) {
389
396
  strict_1.default.equal(card.output, 'total 32');
390
397
  });
391
398
  (0, node_test_1.default)('running empty tool update stays hidden instead of rendering an empty card', () => {
392
- strict_1.default.equal((0, thread_chat_message_blocks_1.parseToolResultCard)('🔧 Tool update - running - '), null);
393
- strict_1.default.equal((0, thread_chat_message_blocks_1.isHiddenProgressMessage)('🔧 Tool update - running - '), true);
399
+ strict_1.default.equal((0, thread_chat_message_blocks_1.isHiddenProgressMessage)({
400
+ bridgeKind: 'tool',
401
+ toolCall: {
402
+ name: 'Tool update',
403
+ status: 'running',
404
+ output: '',
405
+ },
406
+ }), true);
407
+ strict_1.default.equal((0, thread_chat_message_blocks_1.isHiddenProgressMessage)({
408
+ bridgeKind: 'tool',
409
+ toolCall: {
410
+ name: 'Tool update',
411
+ status: 'completed',
412
+ output: '',
413
+ },
414
+ }), false);
394
415
  });
395
416
  (0, node_test_1.default)('permission card content hides fallback transport instructions', () => {
396
417
  const parsed = (0, thread_chat_message_blocks_1.parsePermissionCardContent)([
@@ -462,8 +483,14 @@ function createKnowledgeBase(overrides) {
462
483
  strict_1.default.equal((0, thread_chat_page_state_1.shouldRenderThreadChatMessage)({
463
484
  id: 'empty-tool',
464
485
  role: 'assistant',
465
- content: '🔧 Tool update - running - ',
486
+ content: 'Tool update',
466
487
  kind: 'progress',
488
+ bridgeKind: 'tool',
489
+ toolCall: {
490
+ name: 'Tool update',
491
+ status: 'running',
492
+ output: '',
493
+ },
467
494
  order: 2,
468
495
  }, pendingCard), false);
469
496
  strict_1.default.equal((0, thread_chat_page_state_1.shouldRenderThreadChatMessage)({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kafca/agentdock",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "main": "dist-electron/electron/main.js",
6
6
  "bin": {