@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.
- package/README.md +5 -0
- package/dist/renderer/assets/{Badge-B-Ayib4h.js → Badge-ngf0eCzI.js} +1 -1
- package/dist/renderer/assets/{Chat-CNEOxtyz.js → Chat-tCGY5odh.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-BTd1QBj9.js → ChatMarkdown-vqDWgVNb.js} +1 -1
- package/dist/renderer/assets/{Config-ByKhcLa6.js → Config-YsVDQmIs.js} +1 -1
- package/dist/renderer/assets/{CronList-0mi8dcC5.js → CronList-DQN0T7Yc.js} +1 -1
- package/dist/renderer/assets/{Dashboard-C8-tB_qg.js → Dashboard-C2O6Yxq3.js} +2 -2
- package/dist/renderer/assets/{EmptyState-TR-9nr5e.js → EmptyState-CTkA-fBQ.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-DKAxFOXc.js → KnowledgeDetail-DjLZDItQ.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-CM-1IDr4.js → KnowledgeHome-2NHeRZxW.js} +1 -1
- package/dist/renderer/assets/{Logs-DI5RFPri.js → Logs-CQ6j-Sct.js} +1 -1
- package/dist/renderer/assets/{Modal-CQpa4NPP.js → Modal-9g1uWJfC.js} +1 -1
- package/dist/renderer/assets/{Page-BHXEAwzQ.js → Page-B4mPLn6n.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-Dn5elnHx.js → ProjectDetail-pBXOAeL4.js} +1 -1
- package/dist/renderer/assets/{ProjectList-D6yvEgb5.js → ProjectList-B9PYfZaS.js} +1 -1
- package/dist/renderer/assets/{Select-BbkcXnXL.js → Select-BWFQJ2R8.js} +1 -1
- package/dist/renderer/assets/{SessionChat-Vkf-7gvX.js → SessionChat-fBrzJs3A.js} +1 -1
- package/dist/renderer/assets/{SessionList-c5encjWd.js → SessionList-DjHOsk7X.js} +1 -1
- package/dist/renderer/assets/ThreadChat-MagPeJD0.js +23 -0
- package/dist/renderer/assets/{Workspace-BC7hWqhP.js → Workspace-nfTcxlhw.js} +1 -1
- package/dist/renderer/assets/{arrow-left-I_Y1kdN9.js → arrow-left-CIWZ6JEB.js} +1 -1
- package/dist/renderer/assets/{book-open-B3anUYvp.js → book-open-Ck6BgBhN.js} +1 -1
- package/dist/renderer/assets/{check-BPNCDKgS.js → check-CtB2ZLVE.js} +1 -1
- package/dist/renderer/assets/{chevron-down-BaxUcOIV.js → chevron-down--8lODnN0.js} +1 -1
- package/dist/renderer/assets/{circle-DJQiypFS.js → circle-CKQOJcbh.js} +1 -1
- package/dist/renderer/assets/{desktop-Cv51clcN.js → desktop-Do2I28CT.js} +1 -1
- package/dist/renderer/assets/{funnel-DqKBtiqE.js → funnel-CkZuFIwa.js} +1 -1
- package/dist/renderer/assets/{heart-CmoNjvMO.js → heart-CAX_CqFQ.js} +1 -1
- package/dist/renderer/assets/{index-BfXqhQ1Z.js → index--_L0Y77h.js} +1 -1
- package/dist/renderer/assets/{index-DXJl2HOK.js → index-DvAYflFj.js} +2 -2
- package/dist/renderer/assets/{pencil-CLUBszbw.js → pencil-sIRhtchu.js} +1 -1
- package/dist/renderer/assets/{play-l2RPZuAi.js → play-CJNRbCL5.js} +1 -1
- package/dist/renderer/assets/{plug-CeUPIjRt.js → plug-DsFe9GBx.js} +1 -1
- package/dist/renderer/assets/{plus-BjkefS2P.js → plus-KDwEI-Rs.js} +1 -1
- package/dist/renderer/assets/projects-CJUs2vaq.js +1 -0
- package/dist/renderer/assets/{save-D0yYt9tz.js → save-C-p5r3K-.js} +1 -1
- package/dist/renderer/assets/{search-B7s473Y4.js → search-BZxQJb2k.js} +1 -1
- package/dist/renderer/assets/{server-Dz_h8X_8.js → server-BV-K022I.js} +1 -1
- package/dist/renderer/assets/{session-utils-BvNRTRXA.js → session-utils-ClQTa8yS.js} +1 -1
- package/dist/renderer/assets/{sessions-KPJsgKbQ.js → sessions-B0FSCA9O.js} +1 -1
- package/dist/renderer/assets/{shield-check-Wc5wfUUs.js → shield-check-B6AjkXUd.js} +1 -1
- package/dist/renderer/assets/{trash-2-7INdaB9w.js → trash-2-41Pv4qRx.js} +1 -1
- package/dist/renderer/assets/{user-CLbCgWNR.js → user-NKKHCKm9.js} +1 -1
- package/dist/renderer/assets/{wifi-off-BfDVPXbO.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-DY4wzomi.js +0 -23
- package/dist/renderer/assets/projects-De73QY_o.js +0 -1
|
@@ -11,6 +11,8 @@ const node_os_1 = require("node:os");
|
|
|
11
11
|
const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
12
|
const local_core_acp_session_coordinator_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-session-coordinator.js");
|
|
13
13
|
const scheduler_service_js_1 = require("../services/local-ai-core/src/scheduler/scheduler-service.js");
|
|
14
|
+
const bootstrap_js_1 = require("../services/local-ai-core/src/kernel/bootstrap.js");
|
|
15
|
+
const local_core_controller_js_1 = require("../services/local-ai-core/src/runtime/local-core-controller.js");
|
|
14
16
|
(0, node_test_1.default)('workspace registry entries persist in LocalCoreAcpStore', () => {
|
|
15
17
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-registry-'));
|
|
16
18
|
try {
|
|
@@ -36,6 +38,50 @@ const scheduler_service_js_1 = require("../services/local-ai-core/src/scheduler/
|
|
|
36
38
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
37
39
|
}
|
|
38
40
|
});
|
|
41
|
+
(0, node_test_1.default)('scheduler create resolves a Lark route from the current thread binding', async () => {
|
|
42
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-binding-route-'));
|
|
43
|
+
try {
|
|
44
|
+
const runtime = (0, bootstrap_js_1.bootstrapLocalCoreRuntime)({
|
|
45
|
+
userDataPath,
|
|
46
|
+
enableKnowledge: false,
|
|
47
|
+
log: () => { },
|
|
48
|
+
});
|
|
49
|
+
const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
|
|
50
|
+
const thread = runtime.store.createThread('workspace-a', 'Lark thread');
|
|
51
|
+
const now = new Date().toISOString();
|
|
52
|
+
runtime.store.upsertPlatformThreadBinding({
|
|
53
|
+
workspace_id: 'workspace-a',
|
|
54
|
+
platform: 'lark',
|
|
55
|
+
chat_id: 'chat-1',
|
|
56
|
+
platform_user_id: 'user-1',
|
|
57
|
+
thread_id: thread.id,
|
|
58
|
+
last_platform_message_id: null,
|
|
59
|
+
created_at: now,
|
|
60
|
+
updated_at: now,
|
|
61
|
+
});
|
|
62
|
+
const job = await controller.createScheduledJob({
|
|
63
|
+
workspaceId: 'workspace-a',
|
|
64
|
+
threadId: thread.id,
|
|
65
|
+
executionMode: 'side-thread',
|
|
66
|
+
triggerType: 'cron',
|
|
67
|
+
cronExpr: '30 18 * * *',
|
|
68
|
+
promptTemplate: 'ping',
|
|
69
|
+
description: 'bound lark task',
|
|
70
|
+
enabled: true,
|
|
71
|
+
});
|
|
72
|
+
strict_1.default.equal(job.platform, 'lark');
|
|
73
|
+
strict_1.default.deepEqual(job.route, {
|
|
74
|
+
type: 'channel.chat',
|
|
75
|
+
channelId: 'chat-1',
|
|
76
|
+
participantId: 'user-1',
|
|
77
|
+
threadId: thread.id,
|
|
78
|
+
});
|
|
79
|
+
await controller.close();
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
39
85
|
(0, node_test_1.default)('ACP runtime env includes the current workspace path for file returns', async () => {
|
|
40
86
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-env-'));
|
|
41
87
|
try {
|
|
@@ -92,6 +138,72 @@ const scheduler_service_js_1 = require("../services/local-ai-core/src/scheduler/
|
|
|
92
138
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
93
139
|
}
|
|
94
140
|
});
|
|
141
|
+
(0, node_test_1.default)('ACP scheduled session can override permission mode without changing thread mode', async () => {
|
|
142
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-permission-mode-'));
|
|
143
|
+
try {
|
|
144
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
145
|
+
const thread = store.createThread('workspace-a', 'Thread');
|
|
146
|
+
const capturedSessionNewParams = [];
|
|
147
|
+
let closeCount = 0;
|
|
148
|
+
const coordinator = new local_core_acp_session_coordinator_js_1.LocalCoreAcpSessionCoordinator({
|
|
149
|
+
store,
|
|
150
|
+
transport: {
|
|
151
|
+
spawnSession(input) {
|
|
152
|
+
return {
|
|
153
|
+
threadId: input.threadId,
|
|
154
|
+
bridgeSessionKey: input.bridgeSessionKey,
|
|
155
|
+
closed: false,
|
|
156
|
+
sessionId: '',
|
|
157
|
+
supportsLoad: false,
|
|
158
|
+
pendingPermissionByRun: new Map(),
|
|
159
|
+
schedulerJobCreatedByRun: new Map(),
|
|
160
|
+
launchPermissionMode: '',
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
initializeSession: async () => { },
|
|
164
|
+
request: async (_session, method, params) => {
|
|
165
|
+
if (method === 'session/new') {
|
|
166
|
+
capturedSessionNewParams.push(params);
|
|
167
|
+
return { sessionId: 'session-1' };
|
|
168
|
+
}
|
|
169
|
+
return {};
|
|
170
|
+
},
|
|
171
|
+
closeSession: () => {
|
|
172
|
+
closeCount += 1;
|
|
173
|
+
},
|
|
174
|
+
closeSessionWithError: () => { },
|
|
175
|
+
sendRaw: () => true,
|
|
176
|
+
},
|
|
177
|
+
runThreadMap: new Map(),
|
|
178
|
+
emitBridge: () => { },
|
|
179
|
+
});
|
|
180
|
+
await coordinator.ensureSession(thread.id, 'session:thread-1', {
|
|
181
|
+
workspaceId: 'workspace-a',
|
|
182
|
+
agentType: 'claudecode',
|
|
183
|
+
command: 'claude',
|
|
184
|
+
args: [],
|
|
185
|
+
env: {},
|
|
186
|
+
workDir: '/tmp/workspace-a',
|
|
187
|
+
model: '',
|
|
188
|
+
}, { permissionMode: 'bypassPermissions' });
|
|
189
|
+
strict_1.default.equal(capturedSessionNewParams[0]?._meta?.claudeCode?.options?.permissionMode, 'bypassPermissions');
|
|
190
|
+
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'default');
|
|
191
|
+
await coordinator.ensureSession(thread.id, 'session:thread-1', {
|
|
192
|
+
workspaceId: 'workspace-a',
|
|
193
|
+
agentType: 'claudecode',
|
|
194
|
+
command: 'claude',
|
|
195
|
+
args: [],
|
|
196
|
+
env: {},
|
|
197
|
+
workDir: '/tmp/workspace-a',
|
|
198
|
+
model: '',
|
|
199
|
+
});
|
|
200
|
+
strict_1.default.equal(closeCount, 1);
|
|
201
|
+
strict_1.default.equal(capturedSessionNewParams[1]?._meta, undefined);
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
95
207
|
(0, node_test_1.default)('scheduler uses short ids for new jobs and resolves legacy full ids by short id', () => {
|
|
96
208
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-id-store-'));
|
|
97
209
|
try {
|
|
@@ -27,11 +27,11 @@ class LocalCoreAcpBackend {
|
|
|
27
27
|
});
|
|
28
28
|
this.turnCoordinator = new local_core_acp_turn_coordinator_js_1.LocalCoreAcpTurnCoordinator({
|
|
29
29
|
emitBridge: (event) => this.emitBridgeEvent(event),
|
|
30
|
-
appendMessage: (threadId, role, content, kind, toolCall) => {
|
|
31
|
-
this.options.store.appendMessage(threadId, role, content, kind, toolCall);
|
|
30
|
+
appendMessage: (threadId, role, content, kind, toolCall, bridgeKind, bridgeStatus) => {
|
|
31
|
+
this.options.store.appendMessage(threadId, role, content, kind, toolCall, bridgeKind, bridgeStatus);
|
|
32
32
|
},
|
|
33
|
-
upsertMessage: (threadId, id, role, content, kind, toolCall) => {
|
|
34
|
-
this.options.store.upsertMessage(threadId, id, role, content, kind, toolCall);
|
|
33
|
+
upsertMessage: (threadId, id, role, content, kind, toolCall, bridgeKind, bridgeStatus) => {
|
|
34
|
+
this.options.store.upsertMessage(threadId, id, role, content, kind, toolCall, bridgeKind, bridgeStatus);
|
|
35
35
|
},
|
|
36
36
|
updateRunStatus: (runId, threadId, status) => {
|
|
37
37
|
this.options.store.updateRun(runId, threadId, status);
|
|
@@ -129,7 +129,7 @@ class LocalCoreAcpBackend {
|
|
|
129
129
|
this.options.store.deleteThread(threadId);
|
|
130
130
|
return { deleted: true };
|
|
131
131
|
}
|
|
132
|
-
async sendThreadMessage(threadId, input, config) {
|
|
132
|
+
async sendThreadMessage(threadId, input, config, options = {}) {
|
|
133
133
|
if (!config) {
|
|
134
134
|
throw new Error('localcore-acp message send requires a workspace config.');
|
|
135
135
|
}
|
|
@@ -199,7 +199,7 @@ class LocalCoreAcpBackend {
|
|
|
199
199
|
sessionKey: row.bridge_session_key,
|
|
200
200
|
},
|
|
201
201
|
});
|
|
202
|
-
void this.runPrompt(threadId, runId, row.bridge_session_key, config, message).catch((error) => {
|
|
202
|
+
void this.runPrompt(threadId, runId, row.bridge_session_key, config, message, options).catch((error) => {
|
|
203
203
|
this.options.log?.(`localcore-acp prompt failed for ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
204
204
|
});
|
|
205
205
|
return { runId };
|
|
@@ -283,7 +283,7 @@ class LocalCoreAcpBackend {
|
|
|
283
283
|
async interruptRun(runId) {
|
|
284
284
|
return this.sessionCoordinator.interruptRun(runId);
|
|
285
285
|
}
|
|
286
|
-
async runPrompt(threadId, runId, bridgeSessionKey, config, input) {
|
|
286
|
+
async runPrompt(threadId, runId, bridgeSessionKey, config, input, options = {}) {
|
|
287
287
|
const row = this.options.store.getThreadRow(threadId);
|
|
288
288
|
if (!row) {
|
|
289
289
|
throw new Error(`Thread not found: ${threadId}`);
|
|
@@ -297,7 +297,9 @@ class LocalCoreAcpBackend {
|
|
|
297
297
|
const content = message.displayText;
|
|
298
298
|
let session = null;
|
|
299
299
|
try {
|
|
300
|
-
session = await this.sessionCoordinator.ensureSession(threadId, bridgeSessionKey, config
|
|
300
|
+
session = await this.sessionCoordinator.ensureSession(threadId, bridgeSessionKey, config, {
|
|
301
|
+
permissionMode: options.permissionMode,
|
|
302
|
+
});
|
|
301
303
|
session.currentRunId = runId;
|
|
302
304
|
session.currentTurn = {
|
|
303
305
|
runId,
|
|
@@ -27,12 +27,14 @@ function applyAssistantMessageChunk(currentTurn, text) {
|
|
|
27
27
|
bridgeType: 'preview_start',
|
|
28
28
|
previewHandle: currentTurn.previewHandle,
|
|
29
29
|
content: currentTurn.assistantText,
|
|
30
|
+
bridgeKind: 'assistant',
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
return {
|
|
33
34
|
bridgeType: 'update_message',
|
|
34
35
|
previewHandle: currentTurn.previewHandle,
|
|
35
36
|
content: currentTurn.assistantText,
|
|
37
|
+
bridgeKind: 'assistant',
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
function applyThoughtChunk(currentTurn, text) {
|
|
@@ -40,7 +42,7 @@ function applyThoughtChunk(currentTurn, text) {
|
|
|
40
42
|
return null;
|
|
41
43
|
}
|
|
42
44
|
currentTurn.thoughtText = mergeStreamingText(currentTurn.thoughtText, text);
|
|
43
|
-
const content =
|
|
45
|
+
const content = currentTurn.thoughtText.trim();
|
|
44
46
|
if (!currentTurn.thoughtPreviewStarted) {
|
|
45
47
|
currentTurn.thoughtPreviewStarted = true;
|
|
46
48
|
return {
|
|
@@ -48,6 +50,7 @@ function applyThoughtChunk(currentTurn, text) {
|
|
|
48
50
|
previewHandle: currentTurn.thoughtPreviewHandle,
|
|
49
51
|
messageId: currentTurn.thoughtMessageId,
|
|
50
52
|
content,
|
|
53
|
+
bridgeKind: 'thought',
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
56
|
return {
|
|
@@ -55,6 +58,7 @@ function applyThoughtChunk(currentTurn, text) {
|
|
|
55
58
|
previewHandle: currentTurn.thoughtPreviewHandle,
|
|
56
59
|
messageId: currentTurn.thoughtMessageId,
|
|
57
60
|
content,
|
|
61
|
+
bridgeKind: 'thought',
|
|
58
62
|
};
|
|
59
63
|
}
|
|
60
64
|
function mergeStreamingText(current, next) {
|
|
@@ -177,7 +181,7 @@ function formatPlanProgress(entries) {
|
|
|
177
181
|
.map((entry) => String(entry?.content || '').trim())
|
|
178
182
|
.filter(Boolean)
|
|
179
183
|
.join(' | ');
|
|
180
|
-
return summary
|
|
184
|
+
return summary;
|
|
181
185
|
}
|
|
182
186
|
function extractToolCallKey(update) {
|
|
183
187
|
for (const key of ['toolCallId', 'tool_call_id', 'callId', 'call_id', 'invocationId', 'invocation_id', 'id']) {
|
|
@@ -25,9 +25,13 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
25
25
|
this.options.transport.closeSession(session);
|
|
26
26
|
this.sessions.delete(threadId);
|
|
27
27
|
}
|
|
28
|
-
async ensureSession(threadId, bridgeSessionKey, config) {
|
|
28
|
+
async ensureSession(threadId, bridgeSessionKey, config, options = {}) {
|
|
29
29
|
const existing = this.sessions.get(threadId);
|
|
30
|
-
|
|
30
|
+
const permissionMode = this.resolveLaunchPermissionMode(threadId, options.permissionMode);
|
|
31
|
+
if (existing
|
|
32
|
+
&& !existing.closed
|
|
33
|
+
&& existing.sessionId
|
|
34
|
+
&& existing.launchPermissionMode === permissionMode) {
|
|
31
35
|
return existing;
|
|
32
36
|
}
|
|
33
37
|
if (existing) {
|
|
@@ -43,6 +47,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
43
47
|
config,
|
|
44
48
|
runtimeEnv: this.buildAgentRuntimeEnv(threadId, String(baseEnv.PATH || '')),
|
|
45
49
|
});
|
|
50
|
+
session.launchPermissionMode = permissionMode;
|
|
46
51
|
this.sessions.set(threadId, session);
|
|
47
52
|
await this.options.transport.initializeSession(session);
|
|
48
53
|
const row = this.options.store.getThreadRow(threadId);
|
|
@@ -68,7 +73,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
68
73
|
const created = await this.options.transport.request(session, 'session/new', {
|
|
69
74
|
cwd: config.workDir,
|
|
70
75
|
mcpServers: [],
|
|
71
|
-
_meta: this.buildSessionMeta(threadId),
|
|
76
|
+
_meta: this.buildSessionMeta(threadId, permissionMode),
|
|
72
77
|
}, 30000);
|
|
73
78
|
session.sessionId = String(created.sessionId || created.session_id || created.id || created.session?.sessionId || created.session?.session_id || created.session?.id || '').trim();
|
|
74
79
|
if (!session.sessionId) {
|
|
@@ -182,10 +187,14 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
182
187
|
}
|
|
183
188
|
return env;
|
|
184
189
|
}
|
|
185
|
-
|
|
190
|
+
resolveLaunchPermissionMode(threadId, permissionModeOverride = '') {
|
|
186
191
|
const row = this.options.store.getThreadRow(threadId);
|
|
187
|
-
const mode = String(row?.agent_mode || '').trim();
|
|
188
|
-
|
|
192
|
+
const mode = String(permissionModeOverride || row?.agent_mode || '').trim();
|
|
193
|
+
return !mode || mode === 'default' ? '' : mode;
|
|
194
|
+
}
|
|
195
|
+
buildSessionMeta(threadId, permissionModeOverride = '') {
|
|
196
|
+
const mode = this.resolveLaunchPermissionMode(threadId, permissionModeOverride);
|
|
197
|
+
if (!mode) {
|
|
189
198
|
return undefined;
|
|
190
199
|
}
|
|
191
200
|
return {
|
|
@@ -41,6 +41,8 @@ class LocalCoreAcpStore {
|
|
|
41
41
|
role TEXT NOT NULL,
|
|
42
42
|
content TEXT NOT NULL,
|
|
43
43
|
tool_call_json TEXT,
|
|
44
|
+
bridge_kind TEXT,
|
|
45
|
+
bridge_status TEXT,
|
|
44
46
|
timestamp TEXT NOT NULL,
|
|
45
47
|
kind TEXT NOT NULL,
|
|
46
48
|
seq INTEGER NOT NULL,
|
|
@@ -222,6 +224,8 @@ class LocalCoreAcpStore {
|
|
|
222
224
|
CREATE INDEX IF NOT EXISTS idx_audit_events_type_created ON audit_events (type, created_at DESC);
|
|
223
225
|
`);
|
|
224
226
|
this.ensureColumn('messages', 'tool_call_json', 'TEXT');
|
|
227
|
+
this.ensureColumn('messages', 'bridge_kind', 'TEXT');
|
|
228
|
+
this.ensureColumn('messages', 'bridge_status', 'TEXT');
|
|
225
229
|
this.ensureColumn('scheduled_jobs', 'execution_mode', "TEXT NOT NULL DEFAULT 'same-thread'");
|
|
226
230
|
this.ensureColumn('threads', 'agent_mode', "TEXT NOT NULL DEFAULT 'default'");
|
|
227
231
|
}
|
|
@@ -284,7 +288,7 @@ class LocalCoreAcpStore {
|
|
|
284
288
|
throw new Error(`Thread not found: ${threadId}`);
|
|
285
289
|
}
|
|
286
290
|
const messages = this.db.prepare(`
|
|
287
|
-
SELECT id, thread_id, role, content, tool_call_json, timestamp, kind, seq
|
|
291
|
+
SELECT id, thread_id, role, content, tool_call_json, bridge_kind, bridge_status, timestamp, kind, seq
|
|
288
292
|
FROM messages
|
|
289
293
|
WHERE thread_id = ?
|
|
290
294
|
ORDER BY seq ASC
|
|
@@ -305,6 +309,8 @@ class LocalCoreAcpStore {
|
|
|
305
309
|
role: message.role,
|
|
306
310
|
content: message.content,
|
|
307
311
|
toolCall: parseJson(message.tool_call_json || 'null', null) || undefined,
|
|
312
|
+
bridgeKind: normalizeBridgeKind(message.bridge_kind),
|
|
313
|
+
bridgeStatus: normalizeBridgeStatus(message.bridge_status),
|
|
308
314
|
timestamp: message.timestamp,
|
|
309
315
|
kind: message.kind,
|
|
310
316
|
})),
|
|
@@ -319,7 +325,7 @@ class LocalCoreAcpStore {
|
|
|
319
325
|
deleteThread(threadId) {
|
|
320
326
|
this.db.prepare('DELETE FROM threads WHERE id = ?').run(threadId);
|
|
321
327
|
}
|
|
322
|
-
appendMessage(threadId, role, content, kind, toolCall) {
|
|
328
|
+
appendMessage(threadId, role, content, kind, toolCall, bridgeKind, bridgeStatus) {
|
|
323
329
|
const timestamp = new Date().toISOString();
|
|
324
330
|
const nextSequenceRow = this.db.prepare('SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM messages WHERE thread_id = ?').get(threadId);
|
|
325
331
|
const nextSeq = Number(nextSequenceRow?.next_seq || 0);
|
|
@@ -328,9 +334,9 @@ class LocalCoreAcpStore {
|
|
|
328
334
|
this.db.exec('BEGIN IMMEDIATE');
|
|
329
335
|
try {
|
|
330
336
|
this.db.prepare(`
|
|
331
|
-
INSERT INTO messages (id, thread_id, role, content, tool_call_json, timestamp, kind, seq)
|
|
332
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
333
|
-
`).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, nextSeq);
|
|
337
|
+
INSERT INTO messages (id, thread_id, role, content, tool_call_json, bridge_kind, bridge_status, timestamp, kind, seq)
|
|
338
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
339
|
+
`).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, bridgeKind || null, bridgeStatus || null, timestamp, kind, nextSeq);
|
|
334
340
|
this.db.prepare(`
|
|
335
341
|
UPDATE threads
|
|
336
342
|
SET updated_at = ?, history_count = history_count + 1, excerpt = ?
|
|
@@ -344,7 +350,7 @@ class LocalCoreAcpStore {
|
|
|
344
350
|
}
|
|
345
351
|
return { id, timestamp };
|
|
346
352
|
}
|
|
347
|
-
upsertMessage(threadId, id, role, content, kind, toolCall) {
|
|
353
|
+
upsertMessage(threadId, id, role, content, kind, toolCall, bridgeKind, bridgeStatus) {
|
|
348
354
|
const timestamp = new Date().toISOString();
|
|
349
355
|
const excerpt = (0, workspace_thread_mappers_js_1.normalizeMessageContent)(content);
|
|
350
356
|
const existing = this.db.prepare('SELECT id FROM messages WHERE id = ? AND thread_id = ?').get(id, threadId);
|
|
@@ -353,17 +359,17 @@ class LocalCoreAcpStore {
|
|
|
353
359
|
if (existing) {
|
|
354
360
|
this.db.prepare(`
|
|
355
361
|
UPDATE messages
|
|
356
|
-
SET content = ?, tool_call_json = ?, timestamp = ?, kind = ?
|
|
362
|
+
SET content = ?, tool_call_json = ?, bridge_kind = ?, bridge_status = ?, timestamp = ?, kind = ?
|
|
357
363
|
WHERE id = ? AND thread_id = ?
|
|
358
|
-
`).run(content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, id, threadId);
|
|
364
|
+
`).run(content, toolCall ? JSON.stringify(toolCall) : null, bridgeKind || null, bridgeStatus || null, timestamp, kind, id, threadId);
|
|
359
365
|
}
|
|
360
366
|
else {
|
|
361
367
|
const nextSequenceRow = this.db.prepare('SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM messages WHERE thread_id = ?').get(threadId);
|
|
362
368
|
const nextSeq = Number(nextSequenceRow?.next_seq || 0);
|
|
363
369
|
this.db.prepare(`
|
|
364
|
-
INSERT INTO messages (id, thread_id, role, content, tool_call_json, timestamp, kind, seq)
|
|
365
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
366
|
-
`).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, nextSeq);
|
|
370
|
+
INSERT INTO messages (id, thread_id, role, content, tool_call_json, bridge_kind, bridge_status, timestamp, kind, seq)
|
|
371
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
372
|
+
`).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, bridgeKind || null, bridgeStatus || null, timestamp, kind, nextSeq);
|
|
367
373
|
this.db.prepare(`
|
|
368
374
|
UPDATE threads
|
|
369
375
|
SET history_count = history_count + 1
|
|
@@ -830,6 +836,9 @@ class LocalCoreAcpStore {
|
|
|
830
836
|
return row ? this.toScheduledJob(row) : undefined;
|
|
831
837
|
}
|
|
832
838
|
createScheduledJob(input) {
|
|
839
|
+
if (!input.platform || !input.route) {
|
|
840
|
+
throw new Error('Scheduled job creation requires a resolved platform and route.');
|
|
841
|
+
}
|
|
833
842
|
let id = (0, job_id_js_1.createScheduledJobId)();
|
|
834
843
|
for (let attempt = 0; attempt < 5 && this.getScheduledJob(id); attempt += 1) {
|
|
835
844
|
id = (0, job_id_js_1.createScheduledJobId)();
|
|
@@ -1301,6 +1310,22 @@ function parseJson(value, fallback) {
|
|
|
1301
1310
|
return fallback;
|
|
1302
1311
|
}
|
|
1303
1312
|
}
|
|
1313
|
+
function normalizeBridgeKind(value) {
|
|
1314
|
+
switch (value) {
|
|
1315
|
+
case 'assistant':
|
|
1316
|
+
case 'thought':
|
|
1317
|
+
case 'plan':
|
|
1318
|
+
case 'tool':
|
|
1319
|
+
case 'status':
|
|
1320
|
+
case 'permission':
|
|
1321
|
+
return value;
|
|
1322
|
+
default:
|
|
1323
|
+
return undefined;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
function normalizeBridgeStatus(value) {
|
|
1327
|
+
return value === 'awaiting_input' ? value : undefined;
|
|
1328
|
+
}
|
|
1304
1329
|
function defaultPermissions() {
|
|
1305
1330
|
return {
|
|
1306
1331
|
'workspace.read': 'allow',
|
|
@@ -160,11 +160,13 @@ class LocalCoreAcpTurnCoordinator {
|
|
|
160
160
|
});
|
|
161
161
|
this.options.updateRunStatus(currentRunId, session.threadId, 'awaiting_input');
|
|
162
162
|
const permissionPrompt = (0, local_core_acp_permission_lifecycle_js_1.createPermissionPrompt)(toolTitle);
|
|
163
|
-
this.options.appendMessage(session.threadId, 'assistant', permissionPrompt, 'progress');
|
|
163
|
+
this.options.appendMessage(session.threadId, 'assistant', permissionPrompt, 'progress', undefined, 'permission', 'awaiting_input');
|
|
164
164
|
this.options.emitBridge({
|
|
165
165
|
type: 'buttons',
|
|
166
166
|
sessionKey: session.bridgeSessionKey,
|
|
167
167
|
replyCtx: currentRunId,
|
|
168
|
+
bridgeKind: 'permission',
|
|
169
|
+
bridgeStatus: 'awaiting_input',
|
|
168
170
|
content: permissionPrompt,
|
|
169
171
|
buttonRows,
|
|
170
172
|
});
|
|
@@ -194,6 +196,7 @@ class LocalCoreAcpTurnCoordinator {
|
|
|
194
196
|
sessionKey: session.bridgeSessionKey,
|
|
195
197
|
replyCtx: currentRunId,
|
|
196
198
|
previewHandle: projection.previewHandle,
|
|
199
|
+
bridgeKind: projection.bridgeKind,
|
|
197
200
|
content: projection.content,
|
|
198
201
|
});
|
|
199
202
|
return;
|
|
@@ -208,13 +211,14 @@ class LocalCoreAcpTurnCoordinator {
|
|
|
208
211
|
return;
|
|
209
212
|
}
|
|
210
213
|
if (this.options.upsertMessage) {
|
|
211
|
-
this.options.upsertMessage(session.threadId, projection.messageId, 'assistant', projection.content, 'progress');
|
|
214
|
+
this.options.upsertMessage(session.threadId, projection.messageId, 'assistant', projection.content, 'progress', undefined, projection.bridgeKind);
|
|
212
215
|
}
|
|
213
216
|
this.options.emitBridge({
|
|
214
217
|
type: projection.bridgeType,
|
|
215
218
|
sessionKey: session.bridgeSessionKey,
|
|
216
219
|
replyCtx: currentRunId,
|
|
217
220
|
previewHandle: projection.previewHandle,
|
|
221
|
+
bridgeKind: projection.bridgeKind,
|
|
218
222
|
content: projection.content,
|
|
219
223
|
});
|
|
220
224
|
return;
|
|
@@ -290,11 +294,12 @@ class LocalCoreAcpTurnCoordinator {
|
|
|
290
294
|
if (!content) {
|
|
291
295
|
return;
|
|
292
296
|
}
|
|
293
|
-
this.options.appendMessage(session.threadId, 'assistant', content, 'progress');
|
|
297
|
+
this.options.appendMessage(session.threadId, 'assistant', content, 'progress', undefined, 'plan');
|
|
294
298
|
this.options.emitBridge({
|
|
295
299
|
type: 'reply',
|
|
296
300
|
sessionKey: session.bridgeSessionKey,
|
|
297
301
|
replyCtx: currentRunId,
|
|
302
|
+
bridgeKind: 'plan',
|
|
298
303
|
content,
|
|
299
304
|
});
|
|
300
305
|
return;
|
|
@@ -305,16 +310,17 @@ class LocalCoreAcpTurnCoordinator {
|
|
|
305
310
|
}
|
|
306
311
|
emitProgress(session, currentRunId, content, messageId, toolCall) {
|
|
307
312
|
if (messageId && this.options.upsertMessage) {
|
|
308
|
-
this.options.upsertMessage(session.threadId, messageId, 'assistant', content, 'progress', toolCall);
|
|
313
|
+
this.options.upsertMessage(session.threadId, messageId, 'assistant', content, 'progress', toolCall, toolCall ? 'tool' : undefined);
|
|
309
314
|
}
|
|
310
315
|
else {
|
|
311
|
-
this.options.appendMessage(session.threadId, 'assistant', content, 'progress', toolCall);
|
|
316
|
+
this.options.appendMessage(session.threadId, 'assistant', content, 'progress', toolCall, toolCall ? 'tool' : undefined);
|
|
312
317
|
}
|
|
313
318
|
this.options.emitBridge({
|
|
314
319
|
type: 'reply',
|
|
315
320
|
sessionKey: session.bridgeSessionKey,
|
|
316
321
|
replyCtx: currentRunId,
|
|
317
322
|
messageId,
|
|
323
|
+
bridgeKind: toolCall ? 'tool' : undefined,
|
|
318
324
|
content,
|
|
319
325
|
toolCall,
|
|
320
326
|
});
|
|
@@ -31,31 +31,25 @@ function createLarkTurnState(sessionKey, sourceMessageId) {
|
|
|
31
31
|
return turn;
|
|
32
32
|
}
|
|
33
33
|
function foldToolResultForLark(content) {
|
|
34
|
-
|
|
35
|
-
if (!normalized.startsWith('🔧 '))
|
|
36
|
-
return normalized;
|
|
37
|
-
const parts = normalized.split(' - ');
|
|
38
|
-
if (parts.length <= 2)
|
|
39
|
-
return normalized;
|
|
40
|
-
return parts.slice(0, 2).join(' - ');
|
|
34
|
+
return String(content || '').trim();
|
|
41
35
|
}
|
|
42
36
|
function summarizeToolCallForLark(event, content) {
|
|
43
37
|
const toolCall = event.toolCall;
|
|
44
38
|
if (toolCall) {
|
|
45
39
|
const name = String(toolCall.name || '').trim() || summarizeToolContentForLark(content);
|
|
46
40
|
const input = formatToolInputForLark(toolCall.input);
|
|
47
|
-
return [
|
|
41
|
+
return [`🔧 ${name}`, input ? `参数:${input}` : '']
|
|
48
42
|
.filter(Boolean)
|
|
49
43
|
.join('\n\n');
|
|
50
44
|
}
|
|
51
45
|
return summarizeToolContentForLark(content);
|
|
52
46
|
}
|
|
53
|
-
function isPendingToolProgressForLark(
|
|
54
|
-
|
|
55
|
-
return normalized.startsWith('🔧 ') && normalized.includes(' - pending');
|
|
47
|
+
function isPendingToolProgressForLark(event) {
|
|
48
|
+
return event.toolCall?.status.trim().toLowerCase() === 'pending';
|
|
56
49
|
}
|
|
57
50
|
function consumeLarkBridgeEvent(turn, event, options) {
|
|
58
51
|
const content = foldToolResultForLark(String(event.content || '').trim());
|
|
52
|
+
const bridgeKind = resolveBridgeEventKind(event);
|
|
59
53
|
if (event.type === 'typing_start') {
|
|
60
54
|
turn.messageId = undefined;
|
|
61
55
|
turn.finalMessageId = undefined;
|
|
@@ -80,10 +74,10 @@ function consumeLarkBridgeEvent(turn, event, options) {
|
|
|
80
74
|
return;
|
|
81
75
|
}
|
|
82
76
|
if (event.type === 'preview_start' || event.type === 'update_message') {
|
|
83
|
-
if (
|
|
77
|
+
if (bridgeKind === 'thought') {
|
|
84
78
|
turn.pendingThoughtKey = event.previewHandle || 'thinking-preview';
|
|
85
79
|
turn.pendingThoughtText = content;
|
|
86
|
-
pushUniqueLarkTurnLine(turn.thinkingSteps, content
|
|
80
|
+
pushUniqueLarkTurnLine(turn.thinkingSteps, content);
|
|
87
81
|
return;
|
|
88
82
|
}
|
|
89
83
|
turn.previewText = content;
|
|
@@ -106,17 +100,17 @@ function consumeLarkBridgeEvent(turn, event, options) {
|
|
|
106
100
|
if (!content) {
|
|
107
101
|
return;
|
|
108
102
|
}
|
|
109
|
-
if (
|
|
103
|
+
if (bridgeKind === 'thought' || bridgeKind === 'plan') {
|
|
110
104
|
turn.pendingThoughtKey = progressKey('thinking', event);
|
|
111
105
|
turn.pendingThoughtText = content;
|
|
112
|
-
pushUniqueLarkTurnLine(turn.thinkingSteps, content
|
|
106
|
+
pushUniqueLarkTurnLine(turn.thinkingSteps, content);
|
|
113
107
|
return;
|
|
114
108
|
}
|
|
115
|
-
if (
|
|
109
|
+
if (bridgeKind === 'tool') {
|
|
116
110
|
pushUniqueLarkTurnLine(turn.toolCalls, content.slice(3).trim());
|
|
117
111
|
return;
|
|
118
112
|
}
|
|
119
|
-
if (
|
|
113
|
+
if (bridgeKind === 'status') {
|
|
120
114
|
pushUniqueLarkTurnLine(turn.statusLines, content.slice(3).trim());
|
|
121
115
|
return;
|
|
122
116
|
}
|
|
@@ -125,12 +119,13 @@ function consumeLarkBridgeEvent(turn, event, options) {
|
|
|
125
119
|
}
|
|
126
120
|
function renderLarkBridgeEventMessage(turn, event) {
|
|
127
121
|
const content = foldToolResultForLark(String(event.content || '').trim());
|
|
122
|
+
const bridgeKind = resolveBridgeEventKind(event);
|
|
128
123
|
if (event.type === 'preview_start' || event.type === 'update_message') {
|
|
129
|
-
if (
|
|
124
|
+
if (bridgeKind === 'thought') {
|
|
130
125
|
return { key: 'noop', text: '', buttonRows: [], isFinal: false };
|
|
131
126
|
}
|
|
132
|
-
if (
|
|
133
|
-
if (isPendingToolProgressForLark(
|
|
127
|
+
if (bridgeKind === 'tool') {
|
|
128
|
+
if (isPendingToolProgressForLark(event)) {
|
|
134
129
|
return { key: 'noop', text: '', buttonRows: [], isFinal: false };
|
|
135
130
|
}
|
|
136
131
|
return renderToolMessage(progressKey('tool', event), summarizeToolCallForLark(event, content));
|
|
@@ -138,19 +133,19 @@ function renderLarkBridgeEventMessage(turn, event) {
|
|
|
138
133
|
return { key: 'noop', text: '', buttonRows: [], isFinal: false };
|
|
139
134
|
}
|
|
140
135
|
if (event.type === 'status') {
|
|
141
|
-
return renderProgressMessage(progressKey('status', event),
|
|
136
|
+
return renderProgressMessage(progressKey('status', event), `⏳ ${content}`);
|
|
142
137
|
}
|
|
143
138
|
if (event.type === 'reply') {
|
|
144
|
-
if (
|
|
145
|
-
return
|
|
139
|
+
if (bridgeKind === 'thought' || bridgeKind === 'plan') {
|
|
140
|
+
return renderProgressMessage(progressKey(bridgeKind, event), renderProcessText(bridgeKind, content));
|
|
146
141
|
}
|
|
147
|
-
if (
|
|
148
|
-
if (isPendingToolProgressForLark(
|
|
142
|
+
if (bridgeKind === 'tool') {
|
|
143
|
+
if (isPendingToolProgressForLark(event)) {
|
|
149
144
|
return { key: 'noop', text: '', buttonRows: [], isFinal: false };
|
|
150
145
|
}
|
|
151
146
|
return renderToolMessage(progressKey('tool', event), summarizeToolCallForLark(event, content));
|
|
152
147
|
}
|
|
153
|
-
if (
|
|
148
|
+
if (bridgeKind === 'status') {
|
|
154
149
|
return renderProgressMessage(progressKey('status', event), content);
|
|
155
150
|
}
|
|
156
151
|
return {
|
|
@@ -186,7 +181,7 @@ function takePendingLarkThoughtRender(turn) {
|
|
|
186
181
|
}
|
|
187
182
|
turn.thoughtSegmentSequence += 1;
|
|
188
183
|
const baseKey = turn.pendingThoughtKey || 'thinking-preview';
|
|
189
|
-
const rendered = renderProgressMessage(`thinking:${baseKey}:${turn.thoughtSegmentSequence}`, text);
|
|
184
|
+
const rendered = renderProgressMessage(`thinking:${baseKey}:${turn.thoughtSegmentSequence}`, renderProcessText('thought', text));
|
|
190
185
|
turn.pendingThoughtKey = undefined;
|
|
191
186
|
turn.pendingThoughtText = undefined;
|
|
192
187
|
return rendered;
|
|
@@ -229,21 +224,21 @@ function progressKey(prefix, event) {
|
|
|
229
224
|
return `${prefix}:${content.slice(0, 120)}`;
|
|
230
225
|
}
|
|
231
226
|
function isThoughtBridgeEvent(event) {
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
const kind = resolveBridgeEventKind(event);
|
|
228
|
+
return kind === 'thought' || kind === 'plan';
|
|
229
|
+
}
|
|
230
|
+
function resolveBridgeEventKind(event) {
|
|
231
|
+
if (event.bridgeKind) {
|
|
232
|
+
return event.bridgeKind;
|
|
234
233
|
}
|
|
235
|
-
return
|
|
234
|
+
return event.type === 'status' ? 'status' : 'assistant';
|
|
235
|
+
}
|
|
236
|
+
function renderProcessText(kind, content) {
|
|
237
|
+
const text = content.trim();
|
|
238
|
+
return kind === 'plan' ? `计划\n${text}` : text;
|
|
236
239
|
}
|
|
237
240
|
function summarizeToolContentForLark(content) {
|
|
238
|
-
|
|
239
|
-
if (!normalized.startsWith('🔧 ')) {
|
|
240
|
-
return normalized;
|
|
241
|
-
}
|
|
242
|
-
const withoutOutput = normalized.split(' - ')[0]?.trim() || normalized;
|
|
243
|
-
return withoutOutput
|
|
244
|
-
.replace(/\s+-\s*(running|pending|completed|failed|error|cancelled|canceled)\s*$/i, '')
|
|
245
|
-
.replace(/:\s*(running|pending|completed|failed|error|cancelled|canceled)\s*$/i, '')
|
|
246
|
-
.trim();
|
|
241
|
+
return String(content || '').trim();
|
|
247
242
|
}
|
|
248
243
|
function formatToolInputForLark(input) {
|
|
249
244
|
if (input == null) {
|