@kafca/agentdock 0.1.28 → 0.1.29

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-ngf0eCzI.js} +1 -1
  3. package/dist/renderer/assets/{Chat-CNEOxtyz.js → Chat-tCGY5odh.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-BTd1QBj9.js → ChatMarkdown-vqDWgVNb.js} +1 -1
  5. package/dist/renderer/assets/{Config-ByKhcLa6.js → Config-YsVDQmIs.js} +1 -1
  6. package/dist/renderer/assets/{CronList-0mi8dcC5.js → CronList-DQN0T7Yc.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-C8-tB_qg.js → Dashboard-C2O6Yxq3.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-TR-9nr5e.js → EmptyState-CTkA-fBQ.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-DKAxFOXc.js → KnowledgeDetail-DjLZDItQ.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-CM-1IDr4.js → KnowledgeHome-2NHeRZxW.js} +1 -1
  11. package/dist/renderer/assets/{Logs-DI5RFPri.js → Logs-CQ6j-Sct.js} +1 -1
  12. package/dist/renderer/assets/{Modal-CQpa4NPP.js → Modal-9g1uWJfC.js} +1 -1
  13. package/dist/renderer/assets/{Page-BHXEAwzQ.js → Page-B4mPLn6n.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-Dn5elnHx.js → ProjectDetail-pBXOAeL4.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-D6yvEgb5.js → ProjectList-B9PYfZaS.js} +1 -1
  16. package/dist/renderer/assets/{Select-BbkcXnXL.js → Select-BWFQJ2R8.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-Vkf-7gvX.js → SessionChat-fBrzJs3A.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-c5encjWd.js → SessionList-DjHOsk7X.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-MagPeJD0.js +23 -0
  20. package/dist/renderer/assets/{Workspace-BC7hWqhP.js → Workspace-nfTcxlhw.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-I_Y1kdN9.js → arrow-left-CIWZ6JEB.js} +1 -1
  22. package/dist/renderer/assets/{book-open-B3anUYvp.js → book-open-Ck6BgBhN.js} +1 -1
  23. package/dist/renderer/assets/{check-BPNCDKgS.js → check-CtB2ZLVE.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-BaxUcOIV.js → chevron-down--8lODnN0.js} +1 -1
  25. package/dist/renderer/assets/{circle-DJQiypFS.js → circle-CKQOJcbh.js} +1 -1
  26. package/dist/renderer/assets/{desktop-Cv51clcN.js → desktop-Do2I28CT.js} +1 -1
  27. package/dist/renderer/assets/{funnel-DqKBtiqE.js → funnel-CkZuFIwa.js} +1 -1
  28. package/dist/renderer/assets/{heart-CmoNjvMO.js → heart-CAX_CqFQ.js} +1 -1
  29. package/dist/renderer/assets/{index-BfXqhQ1Z.js → index--_L0Y77h.js} +1 -1
  30. package/dist/renderer/assets/{index-DXJl2HOK.js → index-DvAYflFj.js} +2 -2
  31. package/dist/renderer/assets/{pencil-CLUBszbw.js → pencil-sIRhtchu.js} +1 -1
  32. package/dist/renderer/assets/{play-l2RPZuAi.js → play-CJNRbCL5.js} +1 -1
  33. package/dist/renderer/assets/{plug-CeUPIjRt.js → plug-DsFe9GBx.js} +1 -1
  34. package/dist/renderer/assets/{plus-BjkefS2P.js → plus-KDwEI-Rs.js} +1 -1
  35. package/dist/renderer/assets/projects-CJUs2vaq.js +1 -0
  36. package/dist/renderer/assets/{save-D0yYt9tz.js → save-C-p5r3K-.js} +1 -1
  37. package/dist/renderer/assets/{search-B7s473Y4.js → search-BZxQJb2k.js} +1 -1
  38. package/dist/renderer/assets/{server-Dz_h8X_8.js → server-BV-K022I.js} +1 -1
  39. package/dist/renderer/assets/{session-utils-BvNRTRXA.js → session-utils-ClQTa8yS.js} +1 -1
  40. package/dist/renderer/assets/{sessions-KPJsgKbQ.js → sessions-B0FSCA9O.js} +1 -1
  41. package/dist/renderer/assets/{shield-check-Wc5wfUUs.js → shield-check-B6AjkXUd.js} +1 -1
  42. package/dist/renderer/assets/{trash-2-7INdaB9w.js → trash-2-41Pv4qRx.js} +1 -1
  43. package/dist/renderer/assets/{user-CLbCgWNR.js → user-NKKHCKm9.js} +1 -1
  44. package/dist/renderer/assets/{wifi-off-BfDVPXbO.js → wifi-off-E1ZNhLG5.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 +127 -50
  49. package/dist-electron/electron/workspace-task-store.test.js +112 -0
  50. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +10 -8
  51. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +6 -2
  52. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +15 -6
  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 +1 -0
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +11 -5
  56. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +34 -39
  57. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +31 -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
@@ -130,16 +130,16 @@ function truncateTextByUtf8Bytes(text, maxBytes) {
130
130
  }
131
131
  return `${result.trim()}${suffix}`;
132
132
  }
133
- function stripToolResultForWeixin(content) {
134
- const normalized = String(content || '').trim();
135
- if (!normalized.startsWith('🔧 '))
136
- return normalized;
137
- const parts = normalized.split(' - ');
138
- if (parts[0] === '🔧 Tool update' && parts[1] === 'completed')
133
+ function renderBridgeContentForWeixin(event) {
134
+ const toolCall = event.toolCall;
135
+ if (!toolCall) {
136
+ return String(event.content || '').trim();
137
+ }
138
+ const name = String(toolCall.name || '').trim() || 'Tool update';
139
+ const status = String(toolCall.status || '').trim();
140
+ if (name === 'Tool update' && status === 'completed')
139
141
  return '';
140
- if (parts.length <= 2)
141
- return normalized;
142
- return parts.slice(0, 2).join(' - ');
142
+ return [name, status].filter(Boolean).join(' - ');
143
143
  }
144
144
  // ==================== Gateway Class ====================
145
145
  class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
@@ -1070,7 +1070,8 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
1070
1070
  return this.outboundTurns.get(sessionKey) || this.createTurnState(sessionKey);
1071
1071
  }
1072
1072
  consumeBridgeEvent(turn, event) {
1073
- const content = stripToolResultForWeixin(String(event.content || '').trim());
1073
+ const content = renderBridgeContentForWeixin(event);
1074
+ const bridgeKind = this.resolveBridgeEventKind(event);
1074
1075
  if (event.type === 'typing_start') {
1075
1076
  turn.processing = true;
1076
1077
  turn.previewText = '';
@@ -1085,6 +1086,10 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
1085
1086
  return;
1086
1087
  }
1087
1088
  if (event.type === 'preview_start' || event.type === 'update_message') {
1089
+ if (bridgeKind === 'thought') {
1090
+ this.pushUnique(turn.thinkingSteps, content);
1091
+ return;
1092
+ }
1088
1093
  turn.previewText = content;
1089
1094
  return;
1090
1095
  }
@@ -1110,8 +1115,12 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
1110
1115
  }
1111
1116
  if (!content)
1112
1117
  return;
1113
- if (content.startsWith('💭 ')) {
1114
- this.pushUnique(turn.thinkingSteps, content.slice(3).trim());
1118
+ if (bridgeKind === 'thought' || bridgeKind === 'plan') {
1119
+ this.pushUnique(turn.thinkingSteps, content);
1120
+ return;
1121
+ }
1122
+ if (bridgeKind === 'tool' || bridgeKind === 'status') {
1123
+ this.pushUnique(turn.statusLines, content);
1115
1124
  return;
1116
1125
  }
1117
1126
  turn.finalText = content;
@@ -1120,7 +1129,7 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
1120
1129
  renderTurnText(turn) {
1121
1130
  const sections = [];
1122
1131
  if (turn.thinkingSteps.length > 0) {
1123
- sections.push(`**思考过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
1132
+ sections.push(`**中间过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
1124
1133
  }
1125
1134
  if (turn.finalText) {
1126
1135
  sections.push(turn.finalText);
@@ -1128,7 +1137,7 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
1128
1137
  else if (turn.previewText) {
1129
1138
  sections.push(turn.previewText);
1130
1139
  }
1131
- else if (turn.processing && turn.statusLines.length > 0) {
1140
+ else if (turn.statusLines.length > 0) {
1132
1141
  sections.push(`**处理中**\n${turn.statusLines.slice(-3).map((l) => `• ${l.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
1133
1142
  }
1134
1143
  else if (turn.processing) {
@@ -1144,16 +1153,20 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
1144
1153
  return true;
1145
1154
  if (event.type !== 'reply')
1146
1155
  return false;
1147
- const eventContent = String(event.content || '').trim();
1148
- if (eventContent.startsWith('🔧 ') || eventContent.startsWith('💭 '))
1156
+ const bridgeKind = this.resolveBridgeEventKind(event);
1157
+ if (bridgeKind === 'tool' || bridgeKind === 'thought' || bridgeKind === 'plan' || bridgeKind === 'status')
1149
1158
  return false;
1150
1159
  const normalized = rendered.trim();
1151
1160
  if (!normalized)
1152
1161
  return false;
1153
- if (normalized.startsWith('🔧 ') || normalized.startsWith('💭 '))
1154
- return false;
1155
1162
  return true;
1156
1163
  }
1164
+ resolveBridgeEventKind(event) {
1165
+ if (event.bridgeKind) {
1166
+ return event.bridgeKind;
1167
+ }
1168
+ return event.type === 'status' ? 'status' : 'assistant';
1169
+ }
1157
1170
  // ==================== Private: Helpers ====================
1158
1171
  pushUnique(target, value) {
1159
1172
  const normalized = value.trim();
@@ -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.29",
4
4
  "type": "module",
5
5
  "main": "dist-electron/electron/main.js",
6
6
  "bin": {