@kafca/agentdock 0.1.19 → 0.1.21

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 (62) hide show
  1. package/README.md +7 -0
  2. package/dist/renderer/assets/{Badge-DcFVSGwl.js → Badge-BRmQIqbd.js} +1 -1
  3. package/dist/renderer/assets/{Chat-CTy10fKR.js → Chat-D94ODABB.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-C-ci0dQL.js → ChatMarkdown-DqW7OUvV.js} +1 -1
  5. package/dist/renderer/assets/{Config-Bl5vuqBS.js → Config-9lgzpTCp.js} +1 -1
  6. package/dist/renderer/assets/{CronList-BQOEXhHD.js → CronList-BYZ-x_bp.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-CauX4_SR.js → Dashboard-COISXbQy.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-DVYGGFl9.js → EmptyState-w5-I9zg7.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-BlV8Twrx.js → KnowledgeDetail-DVxwrTLw.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-vd6AO-Fn.js → KnowledgeHome-BKkubcwi.js} +1 -1
  11. package/dist/renderer/assets/{Logs-BrtF26F7.js → Logs-D7HxZ8Nb.js} +1 -1
  12. package/dist/renderer/assets/{Modal-D6ucs8W2.js → Modal-Ceu_MP2e.js} +1 -1
  13. package/dist/renderer/assets/{Page-BdF9afpu.js → Page-CpSczAz8.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-RuoRHs1Q.js → ProjectDetail-FJ_bNXLF.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-r9MXhUu7.js → ProjectList-DZKL4K9C.js} +1 -1
  16. package/dist/renderer/assets/{Select-CEsAkZ5j.js → Select-BHQk6qCU.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-DwnZpTVu.js → SessionChat-DpgSXF3m.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-C89Z6STB.js → SessionList-CU6drQ4N.js} +1 -1
  19. package/dist/renderer/assets/{ThreadChat-JB9GY3La.js → ThreadChat-D3s4GXGh.js} +1 -1
  20. package/dist/renderer/assets/Workspace-B7looTVo.js +14 -0
  21. package/dist/renderer/assets/{arrow-left-DuiS0IaQ.js → arrow-left-Dtal-eOi.js} +1 -1
  22. package/dist/renderer/assets/{book-open-B6l3y70b.js → book-open-q814R_Ov.js} +1 -1
  23. package/dist/renderer/assets/{check-CfewkzaJ.js → check-DjfCdLvw.js} +1 -1
  24. package/dist/renderer/assets/{circle-qzty4gzl.js → circle-vfhmyE8v.js} +1 -1
  25. package/dist/renderer/assets/{desktop-DTAjgvNs.js → desktop-BPIxWbkM.js} +1 -1
  26. package/dist/renderer/assets/desktop-DZHC4PDB.js +3 -0
  27. package/dist/renderer/assets/{funnel-CwjI0g4c.js → funnel-CUQibo1I.js} +1 -1
  28. package/dist/renderer/assets/{heart-CVHUi-lA.js → heart-DG9_b8P1.js} +1 -1
  29. package/dist/renderer/assets/{index-tQ6A4Zff.js → index-Bn8ane6_.js} +2 -2
  30. package/dist/renderer/assets/{index-BbDTSo1y.js → index-Ds0IN5rP.js} +1 -1
  31. package/dist/renderer/assets/{pencil-BvQPZxH6.js → pencil-Dij0LlRY.js} +1 -1
  32. package/dist/renderer/assets/{play-hJkkCcS6.js → play-CPFGjIyJ.js} +1 -1
  33. package/dist/renderer/assets/{plug-BGtbkjYJ.js → plug-DH3RKQyn.js} +1 -1
  34. package/dist/renderer/assets/{plus-DrYsrZyx.js → plus-afkK9_Y8.js} +1 -1
  35. package/dist/renderer/assets/projects-DTDS9SnT.js +1 -0
  36. package/dist/renderer/assets/{save-C8W3Wq2Z.js → save-B-cKgWcy.js} +1 -1
  37. package/dist/renderer/assets/{search-BuSByDes.js → search-DR6PeYkt.js} +1 -1
  38. package/dist/renderer/assets/{server-B9wEMLdo.js → server-BM6KOB5a.js} +1 -1
  39. package/dist/renderer/assets/{session-utils-K8E4S3Qx.js → session-utils-BDuThM0a.js} +1 -1
  40. package/dist/renderer/assets/{sessions-B-QTvESY.js → sessions-C1yKps4T.js} +1 -1
  41. package/dist/renderer/assets/{shield-check-C-oxstVp.js → shield-check-BFjMG-r2.js} +1 -1
  42. package/dist/renderer/assets/{trash-2-BQpE15-6.js → trash-2-CW1heMCf.js} +1 -1
  43. package/dist/renderer/assets/{user-CM4R9QVx.js → user-r_2sHG7m.js} +1 -1
  44. package/dist/renderer/assets/{wifi-off-CoaA7hVn.js → wifi-off-VMUokMpE.js} +1 -1
  45. package/dist/renderer/index.html +1 -1
  46. package/dist-electron/electron/agent-runtime-detector.test.js +11 -0
  47. package/dist-electron/electron/local-core-refactor.test.js +522 -0
  48. package/dist-electron/electron/plugin-kernel.test.js +77 -9
  49. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +9 -9
  50. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +60 -0
  51. package/dist-electron/services/local-ai-core/src/gateway/local-core-lark-gateway.js +337 -54
  52. package/dist-electron/services/local-ai-core/src/gateway/local-core-weixin-gateway.js +50 -2
  53. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +2 -1
  54. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
  55. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +19 -8
  56. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +23 -12
  57. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +34 -0
  58. package/dist-electron/shared/desktop.js +4 -2
  59. package/package.json +2 -1
  60. package/dist/renderer/assets/Workspace-Cftl8zps.js +0 -14
  61. package/dist/renderer/assets/desktop-D4Z7M9RM.js +0 -3
  62. package/dist/renderer/assets/projects-DcNm9FDK.js +0 -1
@@ -31,7 +31,7 @@ function plugin(id, dependsOn = []) {
31
31
  strict_1.default.deepEqual(kernel.getCapabilitySnapshot(), {
32
32
  adapters: {
33
33
  channels: ['localcore-acp'],
34
- agents: ['codex', 'cursor', 'gemini', 'qoder', 'iflow', 'localcore-acp'],
34
+ agents: ['cursor', 'gemini', 'qoder', 'iflow', 'localcore-acp'],
35
35
  knowledge: false,
36
36
  knowledgeProviders: [],
37
37
  },
@@ -43,7 +43,6 @@ function plugin(id, dependsOn = []) {
43
43
  },
44
44
  snapshot: {
45
45
  agents: [
46
- { id: 'agent.codex', agentType: 'codex', displayName: 'codex' },
47
46
  { id: 'agent.cursor', agentType: 'cursor', displayName: 'cursor' },
48
47
  { id: 'agent.gemini', agentType: 'gemini', displayName: 'gemini' },
49
48
  { id: 'agent.qoder', agentType: 'qoder', displayName: 'qoder' },
@@ -115,10 +114,9 @@ function plugin(id, dependsOn = []) {
115
114
  const kernel = (0, bootstrap_js_1.bootstrapLocalCoreKernel)();
116
115
  await kernel.lifecycle.initAll();
117
116
  const diagnostics = await kernel.diagnostics.snapshot();
118
- strict_1.default.equal(diagnostics.pluginCount, 7);
119
- strict_1.default.equal(diagnostics.enabledPluginCount, 7);
117
+ strict_1.default.equal(diagnostics.pluginCount, 6);
118
+ strict_1.default.equal(diagnostics.enabledPluginCount, 6);
120
119
  strict_1.default.deepEqual(diagnostics.plugins.map((plugin) => plugin.pluginId).sort(), [
121
- 'builtin.agent-codex',
122
120
  'builtin.agent-cursor',
123
121
  'builtin.agent-gemini',
124
122
  'builtin.agent-iflow',
@@ -131,7 +129,6 @@ function plugin(id, dependsOn = []) {
131
129
  enabled: plugin.enabled,
132
130
  health: plugin.health,
133
131
  })), [
134
- 'builtin.agent-codex',
135
132
  'builtin.agent-cursor',
136
133
  'builtin.agent-gemini',
137
134
  'builtin.agent-qoder',
@@ -151,13 +148,13 @@ function plugin(id, dependsOn = []) {
151
148
  userDataPath,
152
149
  });
153
150
  strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.agents, [
154
- 'codex',
155
151
  'cursor',
156
152
  'gemini',
157
153
  'qoder',
158
154
  'iflow',
159
155
  'localcore-acp',
160
156
  'opencode',
157
+ 'codex',
161
158
  'claudecode',
162
159
  ]);
163
160
  strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
@@ -184,13 +181,13 @@ function plugin(id, dependsOn = []) {
184
181
  enableKnowledge: false,
185
182
  });
186
183
  strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.agents, [
187
- 'codex',
188
184
  'cursor',
189
185
  'gemini',
190
186
  'qoder',
191
187
  'iflow',
192
188
  'localcore-acp',
193
189
  'opencode',
190
+ 'codex',
194
191
  'claudecode',
195
192
  ]);
196
193
  strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
@@ -207,6 +204,43 @@ function plugin(id, dependsOn = []) {
207
204
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
208
205
  }
209
206
  });
207
+ (0, node_test_1.default)('codex agent runtime routes projects through the bundled ACP adapter', async () => {
208
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
209
+ try {
210
+ const runtime = (0, bootstrap_js_2.bootstrapLocalCoreRuntime)({
211
+ userDataPath,
212
+ enableKnowledge: false,
213
+ });
214
+ await runtime.state.saveRawConfigFile(`
215
+ [[projects]]
216
+ name = "codex-workspace"
217
+
218
+ [projects.agent]
219
+ type = "codex"
220
+ `);
221
+ const configState = await runtime.state.readConfigFile();
222
+ const project = configState.parsed?.projects?.find((entry) => entry.name === 'codex-workspace');
223
+ const codexRuntime = runtime.agentRuntimes.find((entry) => entry.agentType === 'codex');
224
+ const route = project ? codexRuntime?.createRoute(configState, project) : null;
225
+ strict_1.default.equal(route?.agentType, 'codex');
226
+ strict_1.default.equal(route?.config.command, process.execPath);
227
+ strict_1.default.match(route?.config.args[0] || '', /@zed-industries[/\\]codex-acp[/\\]bin[/\\]codex-acp\.js$/);
228
+ strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), [
229
+ {
230
+ id: 'codex-workspace',
231
+ name: 'codex-workspace',
232
+ agentType: 'codex',
233
+ platforms: [],
234
+ sessionsCount: 0,
235
+ heartbeatEnabled: false,
236
+ },
237
+ ]);
238
+ await runtime.stop();
239
+ }
240
+ finally {
241
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
242
+ }
243
+ });
210
244
  (0, node_test_1.default)('runtime bootstrap knowledge capabilities come from the selected provider plugin', () => {
211
245
  const enabledUserDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
212
246
  const disabledUserDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
@@ -408,6 +442,40 @@ function plugin(id, dependsOn = []) {
408
442
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
409
443
  }
410
444
  });
445
+ (0, node_test_1.default)('controller logs are persisted to local-core.log', () => {
446
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-controller-logs-'));
447
+ try {
448
+ const runtime = (0, bootstrap_js_2.bootstrapLocalCoreRuntime)({
449
+ userDataPath,
450
+ enableKnowledge: false,
451
+ });
452
+ const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
453
+ const emittedLogs = [];
454
+ controller.on('logs', (line) => emittedLogs.push(line));
455
+ controller.handleLog('localcore-lark inbound message for project-1');
456
+ const logPath = (0, node_path_1.join)(userDataPath, 'runtime', 'local-core.log');
457
+ const raw = (0, node_fs_1.readFileSync)(logPath, 'utf-8');
458
+ strict_1.default.deepEqual(emittedLogs, ['localcore-lark inbound message for project-1']);
459
+ strict_1.default.match(raw, /^\d{4}-\d{2}-\d{2}T.* localcore-lark inbound message for project-1/m);
460
+ }
461
+ finally {
462
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
463
+ }
464
+ });
465
+ (0, node_test_1.default)('controller buffers bootstrap logs until runtime state is assigned', () => {
466
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-bootstrap-logs-'));
467
+ try {
468
+ const controller = new local_core_controller_js_1.LocalCoreController(userDataPath);
469
+ const logPath = (0, node_path_1.join)(userDataPath, 'runtime', 'local-core.log');
470
+ const raw = (0, node_fs_1.readFileSync)(logPath, 'utf-8');
471
+ strict_1.default.match(raw, /\[plugin:builtin\.agent-codex\] registered/);
472
+ strict_1.default.equal((raw.match(/\[plugin:builtin\.agent-codex\] registered/g) || []).length, 1);
473
+ void controller.close();
474
+ }
475
+ finally {
476
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
477
+ }
478
+ });
411
479
  (0, node_test_1.default)('channel plugin lifecycle start and stop are driven by the kernel lifecycle', async () => {
412
480
  const calls = [];
413
481
  const registry = new plugin_registry_js_1.LocalCorePluginRegistry();
@@ -504,7 +572,7 @@ name = "claude-workspace"
504
572
  [projects.agent]
505
573
  type = "claudecode"
506
574
  `);
507
- strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode']);
575
+ strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode', 'codex']);
508
576
  strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'claudecode'), false);
509
577
  strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), []);
510
578
  await strict_1.default.rejects(() => runtime.workspaceRouter.listThreads('claude-workspace'), /Workspace "claude-workspace" is not configured as a Local AI Core ACP workspace/);
@@ -7,6 +7,7 @@ const local_core_acp_transport_js_1 = require("./local-core-acp-transport.js");
7
7
  const local_core_acp_turn_coordinator_js_1 = require("./local-core-acp-turn-coordinator.js");
8
8
  const local_core_acp_session_coordinator_js_1 = require("./local-core-acp-session-coordinator.js");
9
9
  const local_core_acp_response_processor_js_1 = require("./local-core-acp-response-processor.js");
10
+ const local_core_acp_content_js_1 = require("./local-core-acp-content.js");
10
11
  const command_risk_js_1 = require("../security/command-risk.js");
11
12
  const ACP_PROMPT_TIMEOUT_MS = 15 * 60 * 1000;
12
13
  class LocalCoreAcpBackend {
@@ -126,7 +127,7 @@ class LocalCoreAcpBackend {
126
127
  this.options.store.deleteThread(threadId);
127
128
  return { deleted: true };
128
129
  }
129
- async sendThreadMessage(threadId, content, config) {
130
+ async sendThreadMessage(threadId, input, config) {
130
131
  if (!config) {
131
132
  throw new Error('localcore-acp message send requires a workspace config.');
132
133
  }
@@ -134,6 +135,8 @@ class LocalCoreAcpBackend {
134
135
  if (!row) {
135
136
  throw new Error(`Thread not found: ${threadId}`);
136
137
  }
138
+ const message = (0, local_core_acp_content_js_1.normalizeThreadMessageInput)(input);
139
+ const content = message.displayText;
137
140
  this.options.store.appendMessage(threadId, 'user', content, 'final');
138
141
  this.options.eventBus.emit({
139
142
  type: 'thread.message.accepted',
@@ -169,7 +172,7 @@ class LocalCoreAcpBackend {
169
172
  sessionKey: row.bridge_session_key,
170
173
  },
171
174
  });
172
- void this.runPrompt(threadId, runId, row.bridge_session_key, config, content).catch((error) => {
175
+ void this.runPrompt(threadId, runId, row.bridge_session_key, config, message).catch((error) => {
173
176
  this.options.log?.(`localcore-acp prompt failed for ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
174
177
  });
175
178
  return { runId };
@@ -219,7 +222,7 @@ class LocalCoreAcpBackend {
219
222
  async interruptRun(runId) {
220
223
  return this.sessionCoordinator.interruptRun(runId);
221
224
  }
222
- async runPrompt(threadId, runId, bridgeSessionKey, config, content) {
225
+ async runPrompt(threadId, runId, bridgeSessionKey, config, input) {
223
226
  const row = this.options.store.getThreadRow(threadId);
224
227
  if (!row) {
225
228
  throw new Error(`Thread not found: ${threadId}`);
@@ -229,6 +232,8 @@ class LocalCoreAcpBackend {
229
232
  sessionKey: bridgeSessionKey,
230
233
  replyCtx: runId,
231
234
  });
235
+ const message = (0, local_core_acp_content_js_1.normalizeThreadMessageInput)(input);
236
+ const content = message.displayText;
232
237
  let session = null;
233
238
  try {
234
239
  session = await this.sessionCoordinator.ensureSession(threadId, bridgeSessionKey, config);
@@ -256,12 +261,7 @@ class LocalCoreAcpBackend {
256
261
  const promptPromise = this.transport.request(session, 'session/prompt', {
257
262
  sessionId: session.sessionId,
258
263
  messageId: (0, node_crypto_1.randomUUID)(),
259
- prompt: [
260
- {
261
- type: 'text',
262
- text: content,
263
- },
264
- ],
264
+ prompt: message.contentParts,
265
265
  }, ACP_PROMPT_TIMEOUT_MS);
266
266
  session.promptPromise = promptPromise;
267
267
  const result = await promptPromise;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeThreadMessageInput = normalizeThreadMessageInput;
4
+ exports.createTextMessageInput = createTextMessageInput;
5
+ function normalizeThreadMessageInput(input) {
6
+ if (typeof input === 'string') {
7
+ return {
8
+ displayText: input,
9
+ contentParts: [{ type: 'text', text: input }],
10
+ };
11
+ }
12
+ const displayText = String(input.displayText || '').trim();
13
+ const contentParts = Array.isArray(input.contentParts)
14
+ ? input.contentParts.filter(isSupportedContentPart)
15
+ : [];
16
+ if (contentParts.length > 0) {
17
+ return {
18
+ displayText: displayText || summarizeContentParts(contentParts),
19
+ contentParts,
20
+ };
21
+ }
22
+ const fallbackText = displayText || '';
23
+ return {
24
+ displayText: fallbackText,
25
+ contentParts: [{ type: 'text', text: fallbackText }],
26
+ };
27
+ }
28
+ function createTextMessageInput(text) {
29
+ return normalizeThreadMessageInput(text);
30
+ }
31
+ function isSupportedContentPart(part) {
32
+ if (!part || typeof part !== 'object') {
33
+ return false;
34
+ }
35
+ const type = String(part.type || '');
36
+ if (type === 'text') {
37
+ return Boolean(String(part.text || ''));
38
+ }
39
+ if (type === 'image') {
40
+ const image = part;
41
+ return Boolean(String(image.data || image.uri || ''));
42
+ }
43
+ return false;
44
+ }
45
+ function summarizeContentParts(parts) {
46
+ const text = parts
47
+ .filter((part) => part.type === 'text')
48
+ .map((part) => part.text.trim())
49
+ .filter(Boolean)
50
+ .join('\n\n')
51
+ .trim();
52
+ if (text) {
53
+ return text;
54
+ }
55
+ const imageCount = parts.filter((part) => part.type === 'image').length;
56
+ if (imageCount > 0) {
57
+ return imageCount === 1 ? '[Image]' : `[${imageCount} images]`;
58
+ }
59
+ return '';
60
+ }