@kafca/agentdock 0.1.7

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 (79) hide show
  1. package/README.md +70 -0
  2. package/bin/agentdock.mjs +275 -0
  3. package/build/icon.png +0 -0
  4. package/dist/renderer/assets/index-BaNpzC6d.js +482 -0
  5. package/dist/renderer/assets/index-CLWJyO0E.css +11 -0
  6. package/dist/renderer/favicon.svg +19 -0
  7. package/dist/renderer/index.html +14 -0
  8. package/dist/renderer/logo-options.svg +153 -0
  9. package/dist-electron/electron/knowledge-skill-script.test.js +125 -0
  10. package/dist-electron/electron/lac-cli.test.js +221 -0
  11. package/dist-electron/electron/local-core-refactor.test.js +1075 -0
  12. package/dist-electron/electron/main.js +163 -0
  13. package/dist-electron/electron/plugin-kernel.test.js +534 -0
  14. package/dist-electron/electron/preload.js +2 -0
  15. package/dist-electron/package.json +3 -0
  16. package/dist-electron/packages/contracts/src/index.js +18 -0
  17. package/dist-electron/packages/contracts/src/local-core.js +2 -0
  18. package/dist-electron/packages/core-sdk/src/index.js +406 -0
  19. package/dist-electron/packages/knowledge-api/src/ai-vector-provider.js +388 -0
  20. package/dist-electron/packages/knowledge-api/src/index.js +146 -0
  21. package/dist-electron/packages/knowledge-api/src/sqlite-store.js +467 -0
  22. package/dist-electron/packages/knowledge-api/src/thread-knowledge-store.js +21 -0
  23. package/dist-electron/packages/knowledge-api/test/ai-vector-provider.test.js +412 -0
  24. package/dist-electron/packages/plugin-sdk/src/index.js +2 -0
  25. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +369 -0
  26. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +110 -0
  27. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +171 -0
  28. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +635 -0
  29. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +190 -0
  30. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +244 -0
  31. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +52 -0
  32. package/dist-electron/services/local-ai-core/src/cli/lac.js +290 -0
  33. package/dist-electron/services/local-ai-core/src/gateway/local-core-lark-gateway.js +976 -0
  34. package/dist-electron/services/local-ai-core/src/gateway/local-core-weixin-gateway.js +1143 -0
  35. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +291 -0
  36. package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +67 -0
  37. package/dist-electron/services/local-ai-core/src/kernel/diagnostics.js +27 -0
  38. package/dist-electron/services/local-ai-core/src/kernel/event-bus.js +27 -0
  39. package/dist-electron/services/local-ai-core/src/kernel/lifecycle-manager.js +85 -0
  40. package/dist-electron/services/local-ai-core/src/kernel/plugin-registry.js +60 -0
  41. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +114 -0
  42. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-lark-plugin.js +59 -0
  43. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-weixin-plugin.js +56 -0
  44. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +7 -0
  45. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +7 -0
  46. package/dist-electron/services/local-ai-core/src/plugins/builtin/runtime-capabilities-plugin.js +62 -0
  47. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-cron-plugin.js +49 -0
  48. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-lark-plugin.js +38 -0
  49. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-weixin-plugin.js +38 -0
  50. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +230 -0
  51. package/dist-electron/services/local-ai-core/src/router/workspace-router-types.js +2 -0
  52. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +389 -0
  53. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +315 -0
  54. package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +282 -0
  55. package/dist-electron/services/local-ai-core/src/runtime/server.js +465 -0
  56. package/dist-electron/services/local-ai-core/src/runtime/standalone.js +38 -0
  57. package/dist-electron/services/local-ai-core/src/scheduler/adapters.js +2 -0
  58. package/dist-electron/services/local-ai-core/src/scheduler/cron-command-detector.js +70 -0
  59. package/dist-electron/services/local-ai-core/src/scheduler/cron.js +53 -0
  60. package/dist-electron/services/local-ai-core/src/scheduler/execution-policy.js +2 -0
  61. package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +66 -0
  62. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +77 -0
  63. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +46 -0
  64. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-run-lifecycle.js +64 -0
  65. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +133 -0
  66. package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +66 -0
  67. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +78 -0
  68. package/dist-electron/services/local-ai-core/src/thread/workspace-thread-id.js +17 -0
  69. package/dist-electron/services/local-ai-core/src/thread/workspace-thread-mappers.js +43 -0
  70. package/dist-electron/shared/desktop.js +195 -0
  71. package/dist-electron/src/api/client.js +73 -0
  72. package/dist-electron/src/api/sessions.js +21 -0
  73. package/dist-electron/src/lib/session-utils.js +51 -0
  74. package/dist-electron/src/pages/Threads/thread-chat-model.js +246 -0
  75. package/dist-electron/src/pages/Threads/thread-chat-model.test.js +76 -0
  76. package/dist-electron/src/pages/Threads/thread-chat-permission.js +26 -0
  77. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +102 -0
  78. package/dist-electron/src/pages/Threads/thread-chat-task-state.js +65 -0
  79. package/package.json +146 -0
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalCoreAcpBackend = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const desktop_js_1 = require("../../../../shared/desktop.js");
6
+ const local_core_acp_transport_js_1 = require("./local-core-acp-transport.js");
7
+ const local_core_acp_turn_coordinator_js_1 = require("./local-core-acp-turn-coordinator.js");
8
+ const local_core_acp_session_coordinator_js_1 = require("./local-core-acp-session-coordinator.js");
9
+ const local_core_acp_response_processor_js_1 = require("./local-core-acp-response-processor.js");
10
+ const ACP_PROMPT_TIMEOUT_MS = 15 * 60 * 1000;
11
+ class LocalCoreAcpBackend {
12
+ options;
13
+ transport;
14
+ turnCoordinator;
15
+ sessionCoordinator;
16
+ responseProcessor;
17
+ constructor(options) {
18
+ this.options = options;
19
+ this.transport = new local_core_acp_transport_js_1.LocalCoreAcpTransport({
20
+ log: options.log,
21
+ onAgentRequest: (session, payload) => this.handleAgentRequest(session, payload),
22
+ onAgentNotification: (session, payload) => this.handleAgentNotification(session, payload),
23
+ onSessionClosed: (session, error) => this.handleTransportSessionClosed(session, error),
24
+ });
25
+ this.turnCoordinator = new local_core_acp_turn_coordinator_js_1.LocalCoreAcpTurnCoordinator({
26
+ emitBridge: (event) => this.emitBridgeEvent(event),
27
+ appendMessage: (threadId, role, content, kind) => {
28
+ this.options.store.appendMessage(threadId, role, content, kind);
29
+ },
30
+ updateRunStatus: (runId, threadId, status) => {
31
+ this.options.store.updateRun(runId, threadId, status);
32
+ },
33
+ sendRaw: (session, payload) => this.transport.sendRaw(session, payload),
34
+ });
35
+ this.sessionCoordinator = new local_core_acp_session_coordinator_js_1.LocalCoreAcpSessionCoordinator({
36
+ store: options.store,
37
+ transport: this.transport,
38
+ runThreadMap: options.runThreadMap,
39
+ cliBinDir: options.cliBinDir,
40
+ localCoreBase: options.localCoreBase,
41
+ emitBridge: (event) => this.emitBridgeEvent(event),
42
+ log: options.log,
43
+ });
44
+ this.responseProcessor = new local_core_acp_response_processor_js_1.LocalCoreAcpResponseProcessor({
45
+ getScheduledDeliveryBinding: (threadId) => {
46
+ const binding = this.options.store.getPlatformThreadBindingByThreadId(threadId);
47
+ if (!binding) {
48
+ return null;
49
+ }
50
+ return {
51
+ workspaceId: binding.workspace_id,
52
+ platform: binding.platform,
53
+ route: {
54
+ type: binding.platform === 'lark' ? 'channel.chat' : binding.platform,
55
+ channelId: binding.chat_id,
56
+ participantId: binding.platform_user_id,
57
+ threadId,
58
+ },
59
+ };
60
+ },
61
+ scheduler: options.scheduler,
62
+ });
63
+ }
64
+ close() {
65
+ this.sessionCoordinator.closeAll();
66
+ }
67
+ async listThreads(workspaceId) {
68
+ return this.options.store.listThreadSummaries(workspaceId);
69
+ }
70
+ async createThread(workspaceId, title, agentType = desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
71
+ return this.options.store.createThread(workspaceId, title, agentType);
72
+ }
73
+ async getThread(threadId) {
74
+ const detail = this.options.store.getThread(threadId, []);
75
+ return {
76
+ ...detail,
77
+ pendingPermissionRequest: this.turnCoordinator.getPendingPermissionRequest(this.sessionCoordinator.getSession(threadId), detail),
78
+ };
79
+ }
80
+ async renameThread(threadId, title) {
81
+ this.options.store.renameThread(threadId, title);
82
+ return this.getThread(threadId);
83
+ }
84
+ async deleteThread(threadId) {
85
+ this.sessionCoordinator.closeThreadSession(threadId);
86
+ this.options.store.deleteThread(threadId);
87
+ return { deleted: true };
88
+ }
89
+ async sendThreadMessage(threadId, content, config) {
90
+ if (!config) {
91
+ throw new Error('localcore-acp message send requires a workspace config.');
92
+ }
93
+ const row = this.options.store.getThreadRow(threadId);
94
+ if (!row) {
95
+ throw new Error(`Thread not found: ${threadId}`);
96
+ }
97
+ this.options.store.appendMessage(threadId, 'user', content, 'final');
98
+ this.options.eventBus.emit({
99
+ type: 'thread.message.accepted',
100
+ payload: {
101
+ threadId,
102
+ workspaceId: row.workspace_id,
103
+ role: 'user',
104
+ content,
105
+ kind: 'final',
106
+ source: 'user',
107
+ },
108
+ });
109
+ const runId = `run:${threadId}:${Date.now()}`;
110
+ this.options.runThreadMap.set(runId, threadId);
111
+ this.options.store.updateRun(runId, threadId, 'running');
112
+ this.options.eventBus.emit({
113
+ type: 'run.started',
114
+ payload: {
115
+ runId,
116
+ threadId,
117
+ workspaceId: row.workspace_id,
118
+ prompt: content,
119
+ sessionKey: row.bridge_session_key,
120
+ },
121
+ });
122
+ void this.runPrompt(threadId, runId, row.bridge_session_key, config, content).catch((error) => {
123
+ this.options.log?.(`localcore-acp prompt failed for ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
124
+ });
125
+ return { runId };
126
+ }
127
+ async sendThreadAction(threadId, content, config) {
128
+ const session = this.sessionCoordinator.getSession(threadId);
129
+ if (!session?.currentRunId) {
130
+ return this.sendThreadMessage(threadId, content, config);
131
+ }
132
+ const pendingPermission = session.pendingPermissionByRun.get(session.currentRunId);
133
+ if (!pendingPermission) {
134
+ return this.sendThreadMessage(threadId, content, config);
135
+ }
136
+ const action = String(content || '').trim().toLowerCase();
137
+ const matched = pendingPermission.options.find((option) => option.normalizedAction === action || option.optionId === action);
138
+ if (!matched) {
139
+ throw new Error(`Unknown permission option: ${content}`);
140
+ }
141
+ const accepted = this.transport.sendRaw(session, {
142
+ jsonrpc: '2.0',
143
+ id: pendingPermission.requestId,
144
+ result: {
145
+ outcome: {
146
+ outcome: 'selected',
147
+ optionId: matched.optionId,
148
+ },
149
+ },
150
+ });
151
+ if (!accepted) {
152
+ throw new Error(session.closeReason || 'ACP session is not writable');
153
+ }
154
+ session.pendingPermissionByRun.delete(session.currentRunId);
155
+ this.emitBridgeEvent({
156
+ type: 'typing_start',
157
+ sessionKey: session.bridgeSessionKey,
158
+ replyCtx: session.currentRunId,
159
+ });
160
+ return { runId: session.currentRunId };
161
+ }
162
+ async interruptRun(runId) {
163
+ return this.sessionCoordinator.interruptRun(runId);
164
+ }
165
+ async runPrompt(threadId, runId, bridgeSessionKey, config, content) {
166
+ const row = this.options.store.getThreadRow(threadId);
167
+ if (!row) {
168
+ throw new Error(`Thread not found: ${threadId}`);
169
+ }
170
+ this.emitBridgeEvent({
171
+ type: 'typing_start',
172
+ sessionKey: bridgeSessionKey,
173
+ replyCtx: runId,
174
+ });
175
+ let session = null;
176
+ try {
177
+ session = await this.sessionCoordinator.ensureSession(threadId, bridgeSessionKey, config);
178
+ session.currentRunId = runId;
179
+ session.currentTurn = {
180
+ runId,
181
+ replyCtx: runId,
182
+ previewHandle: (0, node_crypto_1.randomUUID)(),
183
+ assistantText: '',
184
+ typingStarted: true,
185
+ previewStarted: false,
186
+ pendingToolCallTitle: undefined,
187
+ permission: null,
188
+ };
189
+ const promptPromise = this.transport.request(session, 'session/prompt', {
190
+ sessionId: session.sessionId,
191
+ messageId: (0, node_crypto_1.randomUUID)(),
192
+ prompt: [
193
+ {
194
+ type: 'text',
195
+ text: content,
196
+ },
197
+ ],
198
+ }, ACP_PROMPT_TIMEOUT_MS);
199
+ session.promptPromise = promptPromise;
200
+ const result = await promptPromise;
201
+ const currentTurn = session.currentTurn;
202
+ if (!currentTurn || currentTurn.runId !== runId) {
203
+ return;
204
+ }
205
+ this.turnCoordinator.flushPendingToolCall(session);
206
+ if (currentTurn.assistantText) {
207
+ const processed = await this.responseProcessor.processAssistantResponse(threadId, currentTurn.assistantText);
208
+ if (processed.displayContent) {
209
+ this.options.store.appendMessage(threadId, 'assistant', processed.displayContent, 'final');
210
+ this.options.eventBus.emit({
211
+ type: 'thread.message.accepted',
212
+ payload: {
213
+ threadId,
214
+ workspaceId: row.workspace_id,
215
+ role: 'assistant',
216
+ content: processed.displayContent,
217
+ kind: 'final',
218
+ source: 'agent',
219
+ },
220
+ });
221
+ this.emitBridgeEvent({
222
+ type: 'reply',
223
+ sessionKey: bridgeSessionKey,
224
+ replyCtx: runId,
225
+ content: processed.displayContent,
226
+ });
227
+ }
228
+ for (const systemResponse of processed.systemResponses) {
229
+ this.options.store.appendMessage(threadId, 'system', systemResponse, 'system');
230
+ this.options.eventBus.emit({
231
+ type: 'thread.message.accepted',
232
+ payload: {
233
+ threadId,
234
+ workspaceId: row.workspace_id,
235
+ role: 'system',
236
+ content: systemResponse,
237
+ kind: 'system',
238
+ source: 'system',
239
+ },
240
+ });
241
+ }
242
+ }
243
+ else if (String(content || '').trim().startsWith('/')) {
244
+ const slashReply = this.responseProcessor.deriveSlashCommandReply(content, result);
245
+ if (slashReply) {
246
+ this.options.store.appendMessage(threadId, 'assistant', slashReply, 'final');
247
+ this.options.eventBus.emit({
248
+ type: 'thread.message.accepted',
249
+ payload: {
250
+ threadId,
251
+ workspaceId: row.workspace_id,
252
+ role: 'assistant',
253
+ content: slashReply,
254
+ kind: 'final',
255
+ source: 'agent',
256
+ },
257
+ });
258
+ this.emitBridgeEvent({
259
+ type: 'reply',
260
+ sessionKey: bridgeSessionKey,
261
+ replyCtx: runId,
262
+ content: slashReply,
263
+ });
264
+ }
265
+ }
266
+ else if (result?.stopReason === 'cancelled') {
267
+ this.emitBridgeEvent({
268
+ type: 'reply',
269
+ sessionKey: bridgeSessionKey,
270
+ replyCtx: runId,
271
+ content: 'Request cancelled.',
272
+ });
273
+ }
274
+ const nextStatus = result?.stopReason === 'cancelled' ? 'interrupted' : 'completed';
275
+ this.options.store.updateRun(runId, threadId, nextStatus);
276
+ this.options.eventBus.emit({
277
+ type: 'run.completed',
278
+ payload: {
279
+ runId,
280
+ threadId,
281
+ workspaceId: row.workspace_id,
282
+ stopReason: result?.stopReason,
283
+ },
284
+ });
285
+ this.emitBridgeEvent({
286
+ type: 'typing_stop',
287
+ sessionKey: bridgeSessionKey,
288
+ replyCtx: runId,
289
+ });
290
+ }
291
+ catch (error) {
292
+ const errorContent = `Agent error: ${error instanceof Error ? error.message : String(error)}`;
293
+ this.options.store.updateRun(runId, threadId, 'failed');
294
+ this.options.store.appendMessage(threadId, 'assistant', errorContent, 'final');
295
+ this.options.eventBus.emit({
296
+ type: 'thread.message.accepted',
297
+ payload: {
298
+ threadId,
299
+ workspaceId: row.workspace_id,
300
+ role: 'assistant',
301
+ content: errorContent,
302
+ kind: 'final',
303
+ source: 'agent',
304
+ },
305
+ });
306
+ this.options.eventBus.emit({
307
+ type: 'run.failed',
308
+ payload: {
309
+ runId,
310
+ threadId,
311
+ workspaceId: row.workspace_id,
312
+ error: error instanceof Error ? error.message : String(error),
313
+ },
314
+ });
315
+ this.emitBridgeEvent({
316
+ type: 'reply',
317
+ sessionKey: bridgeSessionKey,
318
+ replyCtx: runId,
319
+ content: errorContent,
320
+ });
321
+ this.emitBridgeEvent({
322
+ type: 'typing_stop',
323
+ sessionKey: bridgeSessionKey,
324
+ replyCtx: runId,
325
+ });
326
+ }
327
+ finally {
328
+ if (session?.currentRunId === runId) {
329
+ session.currentRunId = null;
330
+ }
331
+ if (session?.currentTurn?.runId === runId) {
332
+ session.currentTurn = null;
333
+ }
334
+ if (session) {
335
+ session.promptPromise = null;
336
+ }
337
+ }
338
+ }
339
+ handleAgentRequest(session, payload) {
340
+ this.turnCoordinator.handleAgentRequest(session, payload);
341
+ }
342
+ handleAgentNotification(session, payload) {
343
+ this.turnCoordinator.handleAgentNotification(session, payload);
344
+ }
345
+ handleTransportSessionClosed(session, error) {
346
+ this.sessionCoordinator.handleTransportSessionClosed(session, error);
347
+ }
348
+ emitBridgeEvent(event) {
349
+ if (event.replyCtx) {
350
+ const threadId = this.options.runThreadMap.get(event.replyCtx);
351
+ if (threadId) {
352
+ const thread = this.options.store.getThreadRow(threadId);
353
+ if (thread) {
354
+ this.options.eventBus.emit({
355
+ type: 'run.progress',
356
+ payload: {
357
+ runId: event.replyCtx,
358
+ threadId,
359
+ workspaceId: thread.workspace_id,
360
+ stream: event,
361
+ },
362
+ });
363
+ }
364
+ }
365
+ }
366
+ this.options.emitBridge(event);
367
+ }
368
+ }
369
+ exports.LocalCoreAcpBackend = LocalCoreAcpBackend;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalCoreAcpResponseProcessor = void 0;
4
+ const cron_command_detector_js_1 = require("../scheduler/cron-command-detector.js");
5
+ class LocalCoreAcpResponseProcessor {
6
+ options;
7
+ constructor(options) {
8
+ this.options = options;
9
+ }
10
+ deriveSlashCommandReply(content, result) {
11
+ const normalized = String(content || '').trim();
12
+ const [commandName = ''] = normalized.split(/\s+/, 1);
13
+ const direct = [
14
+ result.result,
15
+ result.message,
16
+ result.summary,
17
+ result.output,
18
+ ];
19
+ for (const candidate of direct) {
20
+ const text = this.normalizeSlashCommandResult(candidate);
21
+ if (text) {
22
+ return text;
23
+ }
24
+ }
25
+ if (commandName === '/mode') {
26
+ return '模式命令已执行,但当前 ACP 运行时没有返回可显示的模式菜单。请直接使用 `/mode <name>`。';
27
+ }
28
+ return `命令已执行:${commandName}`;
29
+ }
30
+ async processAssistantResponse(threadId, content) {
31
+ const commands = (0, cron_command_detector_js_1.detectCronCommands)(content);
32
+ if (commands.length === 0) {
33
+ return {
34
+ displayContent: content,
35
+ systemResponses: [],
36
+ };
37
+ }
38
+ const systemResponses = [];
39
+ for (const command of commands) {
40
+ const response = await this.handleCronCommand(threadId, command);
41
+ if (response) {
42
+ systemResponses.push(response);
43
+ }
44
+ }
45
+ return {
46
+ displayContent: (0, cron_command_detector_js_1.stripCronCommands)(content),
47
+ systemResponses,
48
+ };
49
+ }
50
+ normalizeSlashCommandResult(value) {
51
+ if (typeof value === 'string') {
52
+ return value.trim();
53
+ }
54
+ if (!value || typeof value !== 'object') {
55
+ return '';
56
+ }
57
+ const record = value;
58
+ for (const key of ['text', 'content', 'message', 'summary', 'result']) {
59
+ if (typeof record[key] === 'string' && String(record[key]).trim()) {
60
+ return String(record[key]).trim();
61
+ }
62
+ }
63
+ return '';
64
+ }
65
+ async handleCronCommand(threadId, command) {
66
+ try {
67
+ switch (command.kind) {
68
+ case 'create': {
69
+ const binding = this.options.getScheduledDeliveryBinding(threadId);
70
+ if (!binding) {
71
+ return '定时任务创建失败:当前对话没有绑定可调度的平台会话。请先在平台对话线程中使用,或先建立平台绑定。';
72
+ }
73
+ const job = await this.options.scheduler.createJob({
74
+ workspaceId: binding.workspaceId,
75
+ platform: binding.platform,
76
+ route: {
77
+ ...binding.route,
78
+ threadId,
79
+ },
80
+ name: command.name,
81
+ schedule: command.schedule,
82
+ scheduleDescription: command.scheduleDescription,
83
+ message: command.message,
84
+ });
85
+ return `已创建定时任务:${job.description || command.name},计划 ${command.scheduleDescription}(${command.schedule}),ID: ${job.id}`;
86
+ }
87
+ case 'list': {
88
+ const jobs = await this.options.scheduler.listJobsForThread(threadId);
89
+ if (jobs.length === 0) {
90
+ return '当前对话没有定时任务。';
91
+ }
92
+ return [
93
+ '当前对话定时任务:',
94
+ ...jobs.map((job) => `- ${job.description || job.id} | ${job.triggerType === 'cron' ? job.cronExpr : job.runAt} | ${job.enabled ? 'enabled' : 'disabled'} | ${job.id}`),
95
+ ].join('\n');
96
+ }
97
+ case 'delete': {
98
+ await this.options.scheduler.deleteJob(command.jobId);
99
+ return `已删除定时任务:${command.jobId}`;
100
+ }
101
+ default:
102
+ return '';
103
+ }
104
+ }
105
+ catch (error) {
106
+ return `定时任务操作失败:${error instanceof Error ? error.message : String(error)}`;
107
+ }
108
+ }
109
+ }
110
+ exports.LocalCoreAcpResponseProcessor = LocalCoreAcpResponseProcessor;
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalCoreAcpSessionCoordinator = void 0;
4
+ const node_path_1 = require("node:path");
5
+ class LocalCoreAcpSessionCoordinator {
6
+ options;
7
+ sessions = new Map();
8
+ constructor(options) {
9
+ this.options = options;
10
+ }
11
+ getSession(threadId) {
12
+ return this.sessions.get(threadId);
13
+ }
14
+ closeAll() {
15
+ for (const session of this.sessions.values()) {
16
+ this.options.transport.closeSession(session);
17
+ }
18
+ this.sessions.clear();
19
+ }
20
+ closeThreadSession(threadId) {
21
+ const session = this.sessions.get(threadId);
22
+ if (!session) {
23
+ return;
24
+ }
25
+ this.options.transport.closeSession(session);
26
+ this.sessions.delete(threadId);
27
+ }
28
+ async ensureSession(threadId, bridgeSessionKey, config) {
29
+ const existing = this.sessions.get(threadId);
30
+ if (existing && !existing.closed && existing.sessionId) {
31
+ return existing;
32
+ }
33
+ if (existing) {
34
+ this.closeThreadSession(threadId);
35
+ }
36
+ const baseEnv = {
37
+ ...process.env,
38
+ ...config.env,
39
+ };
40
+ const session = this.options.transport.spawnSession({
41
+ threadId,
42
+ bridgeSessionKey,
43
+ config,
44
+ runtimeEnv: this.buildAgentRuntimeEnv(threadId, String(baseEnv.PATH || '')),
45
+ });
46
+ this.sessions.set(threadId, session);
47
+ await this.options.transport.initializeSession(session);
48
+ const row = this.options.store.getThreadRow(threadId);
49
+ if (row?.acp_session_id && row.acp_supports_load && session.supportsLoad) {
50
+ try {
51
+ session.loadReplayMode = true;
52
+ await this.options.transport.request(session, 'session/load', {
53
+ sessionId: row.acp_session_id,
54
+ cwd: config.workDir,
55
+ mcpServers: [],
56
+ }, 30000);
57
+ session.sessionId = row.acp_session_id;
58
+ }
59
+ catch (error) {
60
+ this.options.log?.(`ACP loadSession failed for ${threadId}; creating a fresh session instead: ${error instanceof Error ? error.message : String(error)}`);
61
+ }
62
+ finally {
63
+ session.loadReplayMode = false;
64
+ }
65
+ }
66
+ if (!session.sessionId) {
67
+ try {
68
+ const created = await this.options.transport.request(session, 'session/new', {
69
+ cwd: config.workDir,
70
+ mcpServers: [],
71
+ }, 30000);
72
+ session.sessionId = String(created.sessionId || created.session_id || created.id || created.session?.sessionId || created.session?.session_id || created.session?.id || '').trim();
73
+ if (!session.sessionId) {
74
+ throw new Error('ACP session/new did not return a sessionId');
75
+ }
76
+ this.options.store.updateThreadSession(threadId, session.sessionId, session.supportsLoad);
77
+ }
78
+ catch (error) {
79
+ this.closeThreadSession(threadId);
80
+ throw error;
81
+ }
82
+ }
83
+ return session;
84
+ }
85
+ async interruptRun(runId) {
86
+ const threadId = this.options.runThreadMap.get(runId);
87
+ if (!threadId) {
88
+ return { interrupted: false };
89
+ }
90
+ const session = this.sessions.get(threadId);
91
+ if (!session) {
92
+ this.options.store.updateRun(runId, threadId, 'interrupted');
93
+ return { interrupted: false };
94
+ }
95
+ if (!session.sessionId) {
96
+ this.options.store.updateRun(runId, threadId, 'interrupted');
97
+ this.closeThreadSession(threadId);
98
+ return { interrupted: false };
99
+ }
100
+ const pendingPermission = session.pendingPermissionByRun.get(runId);
101
+ if (pendingPermission) {
102
+ this.options.transport.sendRaw(session, {
103
+ jsonrpc: '2.0',
104
+ id: pendingPermission.requestId,
105
+ result: {
106
+ outcome: {
107
+ outcome: 'cancelled',
108
+ },
109
+ },
110
+ });
111
+ session.pendingPermissionByRun.delete(runId);
112
+ }
113
+ const cancelled = this.options.transport.sendRaw(session, {
114
+ jsonrpc: '2.0',
115
+ method: 'session/cancel',
116
+ params: {
117
+ sessionId: session.sessionId,
118
+ },
119
+ });
120
+ if (!cancelled) {
121
+ this.options.store.updateRun(runId, threadId, 'interrupted');
122
+ return { interrupted: false };
123
+ }
124
+ this.options.store.updateRun(runId, threadId, 'interrupted');
125
+ return { interrupted: true };
126
+ }
127
+ handleTransportSessionClosed(session, error) {
128
+ if (session.closed) {
129
+ return;
130
+ }
131
+ const activeRunId = session.currentRunId;
132
+ this.options.transport.closeSessionWithError(session, error);
133
+ if (activeRunId) {
134
+ this.options.store.updateRun(activeRunId, session.threadId, 'failed');
135
+ this.options.emitBridge({
136
+ type: 'typing_stop',
137
+ sessionKey: session.bridgeSessionKey,
138
+ replyCtx: activeRunId,
139
+ });
140
+ session.pendingPermissionByRun.delete(activeRunId);
141
+ }
142
+ if (this.sessions.get(session.threadId) === session) {
143
+ this.sessions.delete(session.threadId);
144
+ }
145
+ }
146
+ buildAgentRuntimeEnv(threadId, existingPath) {
147
+ const row = this.options.store.getThreadRow(threadId);
148
+ if (!row) {
149
+ return {};
150
+ }
151
+ const binding = this.options.store.getPlatformThreadBindingByThreadId(threadId);
152
+ const env = {
153
+ LOCAL_AI_CORE_BASE: this.options.localCoreBase || 'http://127.0.0.1:9831/api/local/v1',
154
+ LOCAL_AI_WORKSPACE_ID: row.workspace_id,
155
+ LOCAL_AI_THREAD_ID: threadId,
156
+ };
157
+ if (binding) {
158
+ env.LOCAL_AI_PLATFORM = binding.platform;
159
+ env.LOCAL_AI_ROUTE_TYPE = 'lark_chat';
160
+ env.LOCAL_AI_CHAT_ID = binding.chat_id;
161
+ env.LOCAL_AI_PLATFORM_USER_ID = binding.platform_user_id;
162
+ }
163
+ if (this.options.cliBinDir) {
164
+ env.PATH = existingPath
165
+ ? `${this.options.cliBinDir}${node_path_1.delimiter}${existingPath}`
166
+ : this.options.cliBinDir;
167
+ }
168
+ return env;
169
+ }
170
+ }
171
+ exports.LocalCoreAcpSessionCoordinator = LocalCoreAcpSessionCoordinator;