@kafca/agentdock 0.1.27 → 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.
- package/README.md +5 -0
- package/dist/renderer/assets/{Badge-DQ08JYYn.js → Badge-ngf0eCzI.js} +1 -1
- package/dist/renderer/assets/{Chat-BIiYTa43.js → Chat-tCGY5odh.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-ZDQtUMi8.js → ChatMarkdown-vqDWgVNb.js} +1 -1
- package/dist/renderer/assets/{Config-fKb09gkV.js → Config-YsVDQmIs.js} +1 -1
- package/dist/renderer/assets/{CronList-CN53SBez.js → CronList-DQN0T7Yc.js} +1 -1
- package/dist/renderer/assets/{Dashboard-B0Yap4m6.js → Dashboard-C2O6Yxq3.js} +2 -2
- package/dist/renderer/assets/{EmptyState-D5ZGA0rk.js → EmptyState-CTkA-fBQ.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-CE5Ajepr.js → KnowledgeDetail-DjLZDItQ.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-5a_kCXgU.js → KnowledgeHome-2NHeRZxW.js} +1 -1
- package/dist/renderer/assets/{Logs-uKKWMLFZ.js → Logs-CQ6j-Sct.js} +1 -1
- package/dist/renderer/assets/{Modal-Dd9JhtQ0.js → Modal-9g1uWJfC.js} +1 -1
- package/dist/renderer/assets/{Page-BKLPuVQM.js → Page-B4mPLn6n.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-C8gFzvH_.js → ProjectDetail-pBXOAeL4.js} +1 -1
- package/dist/renderer/assets/{ProjectList-CXk3dMu_.js → ProjectList-B9PYfZaS.js} +1 -1
- package/dist/renderer/assets/{Select-DB5PGz8e.js → Select-BWFQJ2R8.js} +1 -1
- package/dist/renderer/assets/{SessionChat-DumN9Ty3.js → SessionChat-fBrzJs3A.js} +1 -1
- package/dist/renderer/assets/{SessionList-Cu5kPOxP.js → SessionList-DjHOsk7X.js} +1 -1
- package/dist/renderer/assets/ThreadChat-MagPeJD0.js +23 -0
- package/dist/renderer/assets/{Workspace-DWcMBl5r.js → Workspace-nfTcxlhw.js} +1 -1
- package/dist/renderer/assets/{arrow-left-Q025lHNq.js → arrow-left-CIWZ6JEB.js} +1 -1
- package/dist/renderer/assets/{book-open-mUQUDLh5.js → book-open-Ck6BgBhN.js} +1 -1
- package/dist/renderer/assets/{check-ByINMPna.js → check-CtB2ZLVE.js} +1 -1
- package/dist/renderer/assets/{chevron-down-Blmwp-nN.js → chevron-down--8lODnN0.js} +1 -1
- package/dist/renderer/assets/{circle-BkvRVWGX.js → circle-CKQOJcbh.js} +1 -1
- package/dist/renderer/assets/{desktop-BzWqZMbk.js → desktop-Do2I28CT.js} +1 -1
- package/dist/renderer/assets/{funnel-Cuwc5I2B.js → funnel-CkZuFIwa.js} +1 -1
- package/dist/renderer/assets/{heart-2tbx0mrd.js → heart-CAX_CqFQ.js} +1 -1
- package/dist/renderer/assets/{index-DqU6J2AY.js → index--_L0Y77h.js} +1 -1
- package/dist/renderer/assets/{index-BTAl8hYv.js → index-DvAYflFj.js} +2 -2
- package/dist/renderer/assets/{pencil-BWKBGhOm.js → pencil-sIRhtchu.js} +1 -1
- package/dist/renderer/assets/{play-OezKUkaH.js → play-CJNRbCL5.js} +1 -1
- package/dist/renderer/assets/{plug-DyNXq963.js → plug-DsFe9GBx.js} +1 -1
- package/dist/renderer/assets/{plus-CvLYqMSE.js → plus-KDwEI-Rs.js} +1 -1
- package/dist/renderer/assets/projects-CJUs2vaq.js +1 -0
- package/dist/renderer/assets/{save-59vRfz-l.js → save-C-p5r3K-.js} +1 -1
- package/dist/renderer/assets/{search-D1h9ECCh.js → search-BZxQJb2k.js} +1 -1
- package/dist/renderer/assets/{server-BXnqzaxz.js → server-BV-K022I.js} +1 -1
- package/dist/renderer/assets/{session-utils-BNlJyXg0.js → session-utils-ClQTa8yS.js} +1 -1
- package/dist/renderer/assets/{sessions-Ctb3Ta0D.js → sessions-B0FSCA9O.js} +1 -1
- package/dist/renderer/assets/{shield-check-CRHdsNpf.js → shield-check-B6AjkXUd.js} +1 -1
- package/dist/renderer/assets/{trash-2-BVq2TEtl.js → trash-2-41Pv4qRx.js} +1 -1
- package/dist/renderer/assets/{user-CRRZk-V5.js → user-NKKHCKm9.js} +1 -1
- package/dist/renderer/assets/{wifi-off-LF_KkdXM.js → wifi-off-E1ZNhLG5.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/electron/agent-runtime-detector.test.js +20 -0
- package/dist-electron/electron/lac-cli.test.js +2 -13
- package/dist-electron/electron/local-core-refactor.test.js +127 -50
- package/dist-electron/electron/workspace-task-store.test.js +112 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +10 -8
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +6 -2
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +15 -6
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +36 -11
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +1 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +11 -5
- package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +34 -39
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +31 -18
- package/dist-electron/services/local-ai-core/src/cli/lac.js +1 -17
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +2 -2
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +1 -1
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +31 -1
- package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +4 -1
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +4 -1
- package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +4 -114
- package/dist-electron/src/pages/Threads/thread-chat-model.js +14 -23
- package/dist-electron/src/pages/Threads/thread-chat-page-state.js +1 -1
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +58 -31
- package/package.json +1 -1
- package/dist/renderer/assets/ThreadChat-ru807VqH.js +0 -23
- package/dist/renderer/assets/projects-C4VoB4se.js +0 -1
package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js
CHANGED
|
@@ -130,16 +130,16 @@ function truncateTextByUtf8Bytes(text, maxBytes) {
|
|
|
130
130
|
}
|
|
131
131
|
return `${result.trim()}${suffix}`;
|
|
132
132
|
}
|
|
133
|
-
function
|
|
134
|
-
const
|
|
135
|
-
if (!
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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 =
|
|
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 (
|
|
1114
|
-
this.pushUnique(turn.thinkingSteps, content
|
|
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(
|
|
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.
|
|
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
|
|
1148
|
-
if (
|
|
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
|
-
|
|
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]
|
package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js
CHANGED
|
@@ -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(
|
|
166
|
-
|
|
167
|
-
|
|
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.
|
|
5
|
-
exports.
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
44
|
-
|
|
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 && !
|
|
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) => !
|
|
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
|
|
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: '
|
|
174
|
-
streamTargetContent: '
|
|
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: '
|
|
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: '
|
|
218
|
-
streamTargetContent: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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.
|
|
393
|
-
|
|
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: '
|
|
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)({
|