@kafca/agentdock 0.1.20 → 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 (60) hide show
  1. package/README.md +7 -0
  2. package/dist/renderer/assets/{Badge-C1f8JpRb.js → Badge-BRmQIqbd.js} +1 -1
  3. package/dist/renderer/assets/{Chat-S50iFg3I.js → Chat-D94ODABB.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-VWL9Vnpz.js → ChatMarkdown-DqW7OUvV.js} +1 -1
  5. package/dist/renderer/assets/{Config-Yc9ExUI6.js → Config-9lgzpTCp.js} +1 -1
  6. package/dist/renderer/assets/{CronList-DTboWazi.js → CronList-BYZ-x_bp.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-CzXCvZVR.js → Dashboard-COISXbQy.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-9FbY2o6z.js → EmptyState-w5-I9zg7.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-D_BDc4Zt.js → KnowledgeDetail-DVxwrTLw.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-DsUA_BCR.js → KnowledgeHome-BKkubcwi.js} +1 -1
  11. package/dist/renderer/assets/{Logs-DDAmR67f.js → Logs-D7HxZ8Nb.js} +1 -1
  12. package/dist/renderer/assets/{Modal-Bt5VBC6S.js → Modal-Ceu_MP2e.js} +1 -1
  13. package/dist/renderer/assets/{Page-Da_Kc6Qm.js → Page-CpSczAz8.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-B4Ps80yv.js → ProjectDetail-FJ_bNXLF.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-BWTP9npz.js → ProjectList-DZKL4K9C.js} +1 -1
  16. package/dist/renderer/assets/{Select-DWs3hVUh.js → Select-BHQk6qCU.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-DOhUnOfH.js → SessionChat-DpgSXF3m.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-ChZYFXuo.js → SessionList-CU6drQ4N.js} +1 -1
  19. package/dist/renderer/assets/{ThreadChat-nhnikHnk.js → ThreadChat-D3s4GXGh.js} +1 -1
  20. package/dist/renderer/assets/{Workspace-pq_JYdTN.js → Workspace-B7looTVo.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-CR39hNvq.js → arrow-left-Dtal-eOi.js} +1 -1
  22. package/dist/renderer/assets/{book-open-Bot2WKl8.js → book-open-q814R_Ov.js} +1 -1
  23. package/dist/renderer/assets/{check-Drbl2nU3.js → check-DjfCdLvw.js} +1 -1
  24. package/dist/renderer/assets/{circle-BXFfAwnA.js → circle-vfhmyE8v.js} +1 -1
  25. package/dist/renderer/assets/{desktop-5k3W66fH.js → desktop-BPIxWbkM.js} +1 -1
  26. package/dist/renderer/assets/desktop-DZHC4PDB.js +3 -0
  27. package/dist/renderer/assets/{funnel-BNKYan_N.js → funnel-CUQibo1I.js} +1 -1
  28. package/dist/renderer/assets/{heart-wUOSwQE-.js → heart-DG9_b8P1.js} +1 -1
  29. package/dist/renderer/assets/{index-DT5la73R.js → index-Bn8ane6_.js} +2 -2
  30. package/dist/renderer/assets/{index-CmOphqrf.js → index-Ds0IN5rP.js} +1 -1
  31. package/dist/renderer/assets/{pencil-D7-8ggue.js → pencil-Dij0LlRY.js} +1 -1
  32. package/dist/renderer/assets/{play-BCYOAfet.js → play-CPFGjIyJ.js} +1 -1
  33. package/dist/renderer/assets/{plug-Dsik74Yl.js → plug-DH3RKQyn.js} +1 -1
  34. package/dist/renderer/assets/{plus-CKrU26_l.js → plus-afkK9_Y8.js} +1 -1
  35. package/dist/renderer/assets/projects-DTDS9SnT.js +1 -0
  36. package/dist/renderer/assets/{save-q7M2_eAl.js → save-B-cKgWcy.js} +1 -1
  37. package/dist/renderer/assets/{search-BptY4_MT.js → search-DR6PeYkt.js} +1 -1
  38. package/dist/renderer/assets/{server-DSSEF0D8.js → server-BM6KOB5a.js} +1 -1
  39. package/dist/renderer/assets/{session-utils-BFuqPZ5x.js → session-utils-BDuThM0a.js} +1 -1
  40. package/dist/renderer/assets/{sessions-CrWo3g3u.js → sessions-C1yKps4T.js} +1 -1
  41. package/dist/renderer/assets/{shield-check-CCU722RQ.js → shield-check-BFjMG-r2.js} +1 -1
  42. package/dist/renderer/assets/{trash-2-DaeeiV8x.js → trash-2-CW1heMCf.js} +1 -1
  43. package/dist/renderer/assets/{user-Bkz8J7Yw.js → user-r_2sHG7m.js} +1 -1
  44. package/dist/renderer/assets/{wifi-off-P8xdIOHU.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 +96 -0
  48. package/dist-electron/electron/plugin-kernel.test.js +43 -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 +122 -5
  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/shared/desktop.js +4 -2
  58. package/package.json +2 -1
  59. package/dist/renderer/assets/desktop-D4Z7M9RM.js +0 -3
  60. package/dist/renderer/assets/projects-CGDtNb0M.js +0 -1
@@ -453,7 +453,8 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
453
453
  return { paired: true, threadId };
454
454
  }
455
455
  this.options.store.clearPlatformThreadMessageId(input.workspaceId, input.chatId, input.platformUserId);
456
- await router.sendThreadMessage(threadId, (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(input.text));
456
+ const wrappedText = (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(input.text);
457
+ await router.sendThreadMessage(threadId, this.createThreadMessageInput(wrappedText, input.contentParts));
457
458
  return { paired: true, threadId };
458
459
  }
459
460
  close() {
@@ -610,18 +611,41 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
610
611
  catch {
611
612
  parsedContent = {};
612
613
  }
614
+ const messageType = String(message.message_type || '').trim().toLowerCase();
613
615
  const text = String(parsedContent.text || '').trim();
614
- if (!text) {
615
- this.options.log?.(`localcore-lark ignored non-text message for ${workspaceId}: type=${String(message.message_type || 'unknown')} contentKeys=${JSON.stringify(Object.keys(parsedContent))}`);
616
+ const contentParts = [];
617
+ if (text) {
618
+ contentParts.push({ type: 'text', text });
619
+ }
620
+ if (messageType === 'image') {
621
+ const imageKey = String(parsedContent.image_key || parsedContent.file_key || '').trim();
622
+ if (imageKey) {
623
+ try {
624
+ contentParts.push(await this.downloadMessageImage(workspaceId, String(message.message_id || ''), imageKey));
625
+ }
626
+ catch (error) {
627
+ const errorText = `[Image download failed: ${error instanceof Error ? error.message : String(error)}]`;
628
+ contentParts.push({ type: 'text', text: errorText });
629
+ this.options.log?.(`localcore-lark image download failed for ${workspaceId}: message=${String(message.message_id || '')} imageKey=${imageKey} error=${error instanceof Error ? error.message : String(error)}`);
630
+ }
631
+ }
632
+ }
633
+ if (contentParts.length === 0) {
634
+ this.options.log?.(`localcore-lark ignored unsupported message for ${workspaceId}: type=${String(message.message_type || 'unknown')} contentKeys=${JSON.stringify(Object.keys(parsedContent))}`);
616
635
  return;
617
636
  }
637
+ const displayText = text || (contentParts.some((part) => part.type === 'image') ? '[Image]' : contentParts
638
+ .filter((part) => part.type === 'text')
639
+ .map((part) => part.text)
640
+ .join('\n\n')
641
+ .trim());
618
642
  const platformUserId = String(sender.sender_id?.user_id || sender.sender_id?.open_id || '').trim();
619
643
  const chatId = String(message.chat_id || platformUserId).trim();
620
644
  if (!platformUserId || !chatId) {
621
645
  this.options.log?.(`localcore-lark ignored message without sender/chat for ${workspaceId}: senderKeys=${JSON.stringify(Object.keys(sender?.sender_id || {}))} chat=${String(message.chat_id || '')}`);
622
646
  return;
623
647
  }
624
- this.options.log?.(`localcore-lark inbound message for ${workspaceId}: chat=${chatId} user=${platformUserId} text=${JSON.stringify(text.slice(0, 120))}`);
648
+ this.options.log?.(`localcore-lark inbound message for ${workspaceId}: chat=${chatId} user=${platformUserId} type=${messageType || 'unknown'} text=${JSON.stringify(displayText.slice(0, 120))}`);
625
649
  await this.handleInboundMessage({
626
650
  workspaceId,
627
651
  platformUserId,
@@ -629,10 +653,103 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
629
653
  displayName: String(payload?.sender?.sender_id?.user_id ||
630
654
  payload?.sender?.sender_id?.open_id ||
631
655
  `Lark ${platformUserId.slice(-6)}`),
632
- text,
656
+ text: displayText,
633
657
  messageId: String(message.message_id || (0, node_crypto_1.randomUUID)()),
658
+ contentParts,
634
659
  });
635
660
  }
661
+ createWrappedContentParts(wrappedText, parts) {
662
+ const nonTextParts = Array.isArray(parts)
663
+ ? parts.filter((part) => part.type !== 'text')
664
+ : [];
665
+ return [
666
+ { type: 'text', text: wrappedText },
667
+ ...nonTextParts,
668
+ ];
669
+ }
670
+ createThreadMessageInput(wrappedText, parts) {
671
+ const hasNonTextPart = Array.isArray(parts) && parts.some((part) => part.type !== 'text');
672
+ if (!hasNonTextPart) {
673
+ return wrappedText;
674
+ }
675
+ return {
676
+ displayText: wrappedText,
677
+ contentParts: this.createWrappedContentParts(wrappedText, parts),
678
+ };
679
+ }
680
+ async downloadMessageImage(workspaceId, messageId, imageKey) {
681
+ const state = this.runtime.get(workspaceId);
682
+ if (!state?.client) {
683
+ throw new Error('Lark client is not connected');
684
+ }
685
+ if (!messageId) {
686
+ throw new Error('Lark image message is missing message_id');
687
+ }
688
+ const resource = await state.client.im.messageResource.get({
689
+ path: {
690
+ message_id: messageId,
691
+ file_key: imageKey,
692
+ },
693
+ params: {
694
+ type: 'image',
695
+ },
696
+ });
697
+ const buffer = await this.readLarkResourceBuffer(resource);
698
+ if (buffer.length === 0) {
699
+ throw new Error('Lark image resource is empty');
700
+ }
701
+ const headerMime = this.extractHeaderMimeType(resource?.headers);
702
+ return {
703
+ type: 'image',
704
+ data: buffer.toString('base64'),
705
+ mimeType: headerMime || this.sniffImageMimeType(buffer),
706
+ fileName: `${imageKey}.${this.sniffImageExtension(buffer)}`,
707
+ };
708
+ }
709
+ async readLarkResourceBuffer(resource) {
710
+ const readable = resource?.getReadableStream?.();
711
+ if (!readable || typeof readable.on !== 'function') {
712
+ throw new Error('Lark resource did not provide a readable stream');
713
+ }
714
+ const chunks = [];
715
+ await new Promise((resolve, reject) => {
716
+ readable.on('data', (chunk) => {
717
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
718
+ });
719
+ readable.on('error', reject);
720
+ readable.on('end', resolve);
721
+ });
722
+ return Buffer.concat(chunks);
723
+ }
724
+ extractHeaderMimeType(headers) {
725
+ const value = headers && typeof headers === 'object'
726
+ ? headers['content-type'] || headers['Content-Type']
727
+ : '';
728
+ return String(value || '').split(';')[0]?.trim() || '';
729
+ }
730
+ sniffImageMimeType(buffer) {
731
+ if (buffer[0] === 0xff && buffer[1] === 0xd8)
732
+ return 'image/jpeg';
733
+ if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47)
734
+ return 'image/png';
735
+ if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46)
736
+ return 'image/gif';
737
+ if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46)
738
+ return 'image/webp';
739
+ return 'application/octet-stream';
740
+ }
741
+ sniffImageExtension(buffer) {
742
+ const mimeType = this.sniffImageMimeType(buffer);
743
+ if (mimeType === 'image/jpeg')
744
+ return 'jpg';
745
+ if (mimeType === 'image/png')
746
+ return 'png';
747
+ if (mimeType === 'image/gif')
748
+ return 'gif';
749
+ if (mimeType === 'image/webp')
750
+ return 'webp';
751
+ return 'bin';
752
+ }
636
753
  attachWsDiagnostics(workspaceId, wsClient) {
637
754
  const on = typeof wsClient?.on === 'function' ? wsClient.on.bind(wsClient) : null;
638
755
  if (!on) {
@@ -585,7 +585,8 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
585
585
  return { paired: true, threadId };
586
586
  }
587
587
  this.options.store.updatePlatformThreadMessageId(input.workspaceId, input.chatId, input.platformUserId, input.contextToken || input.messageId, 'weixin');
588
- await router.sendThreadMessage(threadId, (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(input.text));
588
+ const wrappedText = (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(input.text);
589
+ await router.sendThreadMessage(threadId, this.createThreadMessageInput(wrappedText, input.contentParts));
589
590
  return { paired: true, threadId };
590
591
  }
591
592
  // ==================== Private: Bindings ====================
@@ -799,6 +800,7 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
799
800
  const msgId = msg.msg_id ?? String(Date.now());
800
801
  // Handle attachments
801
802
  let attachmentText = '';
803
+ const attachmentParts = [];
802
804
  if (mediaItems.length > 0) {
803
805
  const uploadsDir = node_path_1.default.join(binding.stateDir, 'weixin-uploads');
804
806
  for (const [idx, item] of mediaItems.entries()) {
@@ -807,6 +809,14 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
807
809
  if (att) {
808
810
  attachmentText += attachmentText ? '\n' : '';
809
811
  attachmentText += att.kind === 'image' ? `[Image: ${att.path}]` : `[File "${att.name}": ${att.path}]`;
812
+ if (att.kind === 'image' && att.data) {
813
+ attachmentParts.push({
814
+ type: 'image',
815
+ data: att.data,
816
+ mimeType: att.mimeType,
817
+ fileName: att.name,
818
+ });
819
+ }
810
820
  }
811
821
  }
812
822
  catch (dlErr) {
@@ -825,6 +835,10 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
825
835
  text: fullText,
826
836
  messageId: msgId,
827
837
  contextToken: msg.context_token,
838
+ contentParts: [
839
+ ...(text ? [{ type: 'text', text }] : []),
840
+ ...attachmentParts,
841
+ ],
828
842
  });
829
843
  }
830
844
  }
@@ -991,7 +1005,41 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
991
1005
  const filePath = node_path_1.default.join(uploadsDir, fileName);
992
1006
  node_fs_1.default.mkdirSync(uploadsDir, { recursive: true });
993
1007
  node_fs_1.default.writeFileSync(filePath, resultBuf);
994
- return { path: filePath, kind, name: declaredName };
1008
+ return {
1009
+ path: filePath,
1010
+ kind,
1011
+ name: declaredName,
1012
+ data: kind === 'image' ? resultBuf.toString('base64') : undefined,
1013
+ mimeType: kind === 'image' ? this.mimeTypeForImageExt(ext) : undefined,
1014
+ };
1015
+ }
1016
+ createWrappedContentParts(wrappedText, parts) {
1017
+ const nonTextParts = Array.isArray(parts)
1018
+ ? parts.filter((part) => part.type !== 'text')
1019
+ : [];
1020
+ return [
1021
+ { type: 'text', text: wrappedText },
1022
+ ...nonTextParts,
1023
+ ];
1024
+ }
1025
+ createThreadMessageInput(wrappedText, parts) {
1026
+ const hasNonTextPart = Array.isArray(parts) && parts.some((part) => part.type !== 'text');
1027
+ if (!hasNonTextPart) {
1028
+ return wrappedText;
1029
+ }
1030
+ return {
1031
+ displayText: wrappedText,
1032
+ contentParts: this.createWrappedContentParts(wrappedText, parts),
1033
+ };
1034
+ }
1035
+ mimeTypeForImageExt(ext) {
1036
+ if (ext === '.jpg')
1037
+ return 'image/jpeg';
1038
+ if (ext === '.png')
1039
+ return 'image/png';
1040
+ if (ext === '.gif')
1041
+ return 'image/gif';
1042
+ return 'application/octet-stream';
995
1043
  }
996
1044
  // ==================== Private: Turn State ====================
997
1045
  createTurnState(sessionKey) {
@@ -35,7 +35,7 @@ function bootstrapLocalCoreKernel(options) {
35
35
  const lifecycle = new lifecycle_manager_js_1.LocalCoreLifecycleManager(plugins, context);
36
36
  const diagnostics = new diagnostics_js_1.LocalCoreDiagnostics(plugins, lifecycle);
37
37
  const builtIns = [
38
- ...['codex', 'cursor', 'gemini', 'qoder', 'iflow'].map((agentType) => (0, agent_localcore_acp_plugin_js_1.createBuiltinStaticAgentCapabilityPlugin)(agentType)),
38
+ ...['cursor', 'gemini', 'qoder', 'iflow'].map((agentType) => (0, agent_localcore_acp_plugin_js_1.createBuiltinStaticAgentCapabilityPlugin)(agentType)),
39
39
  (0, agent_localcore_acp_plugin_js_1.createBuiltinLocalCoreAcpAgentPlugin)(),
40
40
  (0, scheduler_cron_plugin_js_1.createBuiltinCronSchedulerPlugin)(),
41
41
  ];
@@ -164,6 +164,7 @@ function bootstrapLocalCoreRuntime(options) {
164
164
  const agentPlugins = [
165
165
  localCoreAgentPlugin,
166
166
  (0, agent_localcore_acp_plugin_js_1.createBuiltinOpencodeAgentPlugin)(),
167
+ (0, agent_localcore_acp_plugin_js_1.createBuiltinCodexAgentPlugin)(),
167
168
  (0, agent_localcore_acp_plugin_js_1.createBuiltinClaudeCodeAgentPlugin)(),
168
169
  ];
169
170
  let workspaceRouter;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createBuiltinLocalCoreAcpAgentPlugin = createBuiltinLocalCoreAcpAgentPlugin;
4
4
  exports.createBuiltinOpencodeAgentPlugin = createBuiltinOpencodeAgentPlugin;
5
+ exports.createBuiltinCodexAgentPlugin = createBuiltinCodexAgentPlugin;
5
6
  exports.createBuiltinClaudeCodeAgentPlugin = createBuiltinClaudeCodeAgentPlugin;
6
7
  exports.createBuiltinStaticAgentCapabilityPlugin = createBuiltinStaticAgentCapabilityPlugin;
7
8
  const desktop_js_1 = require("../../../../../shared/desktop.js");
@@ -85,6 +86,14 @@ function createBuiltinOpencodeAgentPlugin() {
85
86
  displayName: 'OpenCode',
86
87
  });
87
88
  }
89
+ function createBuiltinCodexAgentPlugin() {
90
+ return createBuiltinAgentPlugin({
91
+ pluginId: 'builtin.agent-codex',
92
+ agentType: 'codex',
93
+ match: (normalizedAgentType) => normalizedAgentType === 'codex',
94
+ displayName: 'Codex',
95
+ });
96
+ }
88
97
  function createBuiltinClaudeCodeAgentPlugin() {
89
98
  return createBuiltinAgentPlugin({
90
99
  pluginId: 'builtin.agent-claudecode',
@@ -150,22 +150,28 @@ function shouldUseOpenAiCompatibleProvider(providerId) {
150
150
  ]);
151
151
  return !builtInNonCompatibleProviders.has(providerId);
152
152
  }
153
- function resolveBundledClaudeCodeCommand() {
153
+ function resolveBundledAcpCommand(packageName, binName) {
154
154
  const require = (0, node_module_1.createRequire)(__filename);
155
- const packageJsonPath = require.resolve(`${desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE}/package.json`);
155
+ const packageJsonPath = require.resolve(`${packageName}/package.json`);
156
156
  const packageJson = require(packageJsonPath);
157
157
  const binField = packageJson.bin;
158
158
  const relativeBinPath = typeof binField === 'string'
159
159
  ? binField
160
- : binField?.['claude-agent-acp'];
160
+ : binField?.[binName];
161
161
  if (!relativeBinPath) {
162
- throw new Error(`Bundled package "${desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE}" does not declare the claude-agent-acp bin.`);
162
+ throw new Error(`Bundled package "${packageName}" does not declare the ${binName} bin.`);
163
163
  }
164
164
  return {
165
165
  command: process.execPath,
166
166
  args: [(0, node_path_1.resolve)((0, node_path_1.dirname)(packageJsonPath), relativeBinPath)],
167
167
  };
168
168
  }
169
+ function resolveBundledCodexCommand() {
170
+ return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, 'codex-acp');
171
+ }
172
+ function resolveBundledClaudeCodeCommand() {
173
+ return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, 'claude-agent-acp');
174
+ }
169
175
  function toLocalCoreProjectConfig(configState, project) {
170
176
  const rawWorkDir = String(project.agent?.options?.work_dir || '.').trim() || '.';
171
177
  const configDir = (0, node_path_1.dirname)(configState.path);
@@ -198,21 +204,26 @@ function toLocalCoreProjectConfig(configState, project) {
198
204
  ANTHROPIC_MODEL: model,
199
205
  }
200
206
  : {};
207
+ const bundledCodex = agentType === 'codex'
208
+ ? resolveBundledCodexCommand()
209
+ : null;
201
210
  const bundledClaudeCode = agentType === 'claudecode'
202
211
  ? resolveBundledClaudeCodeCommand()
203
212
  : null;
204
213
  const inferredCommand = agentType === 'opencode'
205
214
  ? 'opencode'
206
- : bundledClaudeCode?.command || '';
215
+ : bundledCodex?.command || bundledClaudeCode?.command || '';
207
216
  const command = String(project.agent?.options?.command || inferredCommand).trim();
208
217
  if (!command) {
209
218
  throw new Error(`Workspace "${project.name}" requires [projects.agent.options].command for Local AI Core ACP execution.`);
210
219
  }
211
220
  const defaultArgs = agentType === 'opencode'
212
221
  ? ['acp']
213
- : agentType === 'claudecode'
214
- ? [...(bundledClaudeCode?.args || [])]
215
- : [];
222
+ : agentType === 'codex'
223
+ ? [...(bundledCodex?.args || [])]
224
+ : agentType === 'claudecode'
225
+ ? [...(bundledClaudeCode?.args || [])]
226
+ : [];
216
227
  return {
217
228
  workspaceId: project.name,
218
229
  agentType,
@@ -18,7 +18,7 @@ const DISPLAY_NAMES = {
18
18
  };
19
19
  const COMMAND_CANDIDATES = {
20
20
  opencode: ['opencode'],
21
- codex: ['codex'],
21
+ codex: ['codex-acp', 'codex'],
22
22
  claudecode: ['claude-agent-acp'],
23
23
  cursor: ['cursor-agent', 'cursor'],
24
24
  gemini: ['gemini'],
@@ -61,8 +61,14 @@ function detectInstalledAgentRuntimes(options = {}) {
61
61
  ? installedRuntime(agentType, resolved, 'config', detectedAt, env, versionTimeoutMs)
62
62
  : missingRuntime(agentType, detectedAt, `Configured command not found: ${configured}`);
63
63
  }
64
- if (agentType === 'claudecode') {
65
- const bundled = resolveBundledClaudeAgentAcp(options.requireFrom);
64
+ if (agentType === 'codex' || agentType === 'claudecode') {
65
+ const bundled = agentType === 'codex'
66
+ ? resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, ['bin/codex-acp.js'])
67
+ : resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, [
68
+ 'dist/cli.js',
69
+ 'bin/claude-agent-acp.js',
70
+ 'cli.js',
71
+ ]);
66
72
  if (bundled) {
67
73
  return installedRuntime(agentType, bundled, 'bundled', detectedAt, env, versionTimeoutMs);
68
74
  }
@@ -215,18 +221,23 @@ function parseVersionOutput(output) {
215
221
  const match = text.match(/\d+(?:\.\d+)+(?:[-+._a-zA-Z0-9]*)?/);
216
222
  return match?.[0] || text || undefined;
217
223
  }
218
- function resolveBundledClaudeAgentAcp(requireFrom) {
224
+ function resolveBundledAcpPackage(requireFrom, packageName, candidates) {
219
225
  try {
220
226
  const require = (0, node_module_1.createRequire)(requireFrom || (0, node_path_1.join)(process.cwd(), 'package.json'));
221
- const packageJsonPath = require.resolve(`${desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE}/package.json`);
227
+ const packageJsonPath = require.resolve(`${packageName}/package.json`);
222
228
  const packageDir = (0, node_path_1.dirname)(packageJsonPath);
223
- for (const candidate of [
224
- (0, node_path_1.join)(packageDir, 'dist', 'cli.js'),
225
- (0, node_path_1.join)(packageDir, 'bin', 'claude-agent-acp.js'),
226
- (0, node_path_1.join)(packageDir, 'cli.js'),
227
- ]) {
228
- if ((0, node_fs_1.existsSync)(candidate)) {
229
- return candidate;
229
+ const packageJson = require(packageJsonPath);
230
+ const binField = packageJson.bin;
231
+ if (typeof binField === 'string') {
232
+ candidates = [binField, ...candidates];
233
+ }
234
+ else if (binField && typeof binField === 'object') {
235
+ candidates = [...Object.values(binField), ...candidates];
236
+ }
237
+ for (const candidate of candidates) {
238
+ const candidatePath = (0, node_path_1.join)(packageDir, candidate);
239
+ if ((0, node_fs_1.existsSync)(candidatePath)) {
240
+ return candidatePath;
230
241
  }
231
242
  }
232
243
  return packageJsonPath;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SCHEDULER_PROTOCOL_INSTRUCTION = exports.LOCALCORE_ACP_AGENT_TYPE = exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = exports.DESKTOP_PROVIDER_THINKING_OPTIONS = exports.DESKTOP_PROVIDER_PRESET_OPTIONS = exports.DESKTOP_PLATFORM_TYPE_OPTIONS = exports.DESKTOP_AGENT_TYPE_OPTIONS = exports.DESKTOP_LARK_SDK_PACKAGE = exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
3
+ exports.SCHEDULER_PROTOCOL_INSTRUCTION = exports.LOCALCORE_ACP_AGENT_TYPE = exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = exports.DESKTOP_PROVIDER_THINKING_OPTIONS = exports.DESKTOP_PROVIDER_PRESET_OPTIONS = exports.DESKTOP_PLATFORM_TYPE_OPTIONS = exports.DESKTOP_AGENT_TYPE_OPTIONS = exports.DESKTOP_LARK_SDK_PACKAGE = exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = exports.DESKTOP_CODEX_ACP_PACKAGE = exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
4
4
  exports.deriveDesktopRuntimePhase = deriveDesktopRuntimePhase;
5
5
  exports.deriveDesktopRuntimeRoles = deriveDesktopRuntimeRoles;
6
6
  exports.normalizeDesktopPlatformType = normalizeDesktopPlatformType;
@@ -15,6 +15,7 @@ exports.isAcpAgentType = isAcpAgentType;
15
15
  exports.DEFAULT_DESKTOP_AGENT_TYPE = 'opencode';
16
16
  exports.DEFAULT_DESKTOP_OPENCODE_MODEL = 'opencode/minimax-m2.5-free';
17
17
  exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = '';
18
+ exports.DESKTOP_CODEX_ACP_PACKAGE = '@zed-industries/codex-acp';
18
19
  exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = '@agentclientprotocol/claude-agent-acp';
19
20
  exports.DESKTOP_LARK_SDK_PACKAGE = '@larksuiteoapi/node-sdk';
20
21
  exports.DESKTOP_AGENT_TYPE_OPTIONS = [
@@ -52,7 +53,7 @@ exports.DESKTOP_PROVIDER_PRESET_OPTIONS = [
52
53
  'ollama',
53
54
  ];
54
55
  exports.DESKTOP_PROVIDER_THINKING_OPTIONS = ['', 'enabled', 'disabled'];
55
- exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = ['opencode', 'claudecode', 'acp', 'localcore-acp'];
56
+ exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = ['opencode', 'codex', 'claudecode', 'acp', 'localcore-acp'];
56
57
  exports.LOCALCORE_ACP_AGENT_TYPE = 'localcore-acp';
57
58
  exports.SCHEDULER_PROTOCOL_INSTRUCTION = [
58
59
  '[Scheduler Tools]',
@@ -190,6 +191,7 @@ function isAcpAgentType(agentType) {
190
191
  const normalized = String(agentType || '').trim().toLowerCase();
191
192
  return normalized === 'acp'
192
193
  || normalized === 'opencode'
194
+ || normalized === 'codex'
193
195
  || normalized === 'claudecode'
194
196
  || normalized === exports.LOCALCORE_ACP_AGENT_TYPE;
195
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kafca/agentdock",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "type": "module",
5
5
  "main": "dist-electron/electron/main.js",
6
6
  "bin": {
@@ -49,6 +49,7 @@
49
49
  "@radix-ui/react-select": "^2.2.6",
50
50
  "@radix-ui/react-slot": "^1.2.4",
51
51
  "@tailwindcss/typography": "^0.5.19",
52
+ "@zed-industries/codex-acp": "^0.12.0",
52
53
  "clsx": "^2.1.1",
53
54
  "highlight.js": "^11.11.1",
54
55
  "i18next": "^25.1.2",
@@ -1,3 +0,0 @@
1
- const u="opencode",s="opencode/minimax-m2.5-free";const h=["opencode","codex","claudecode","cursor","gemini","qoder","iflow","localcore-acp"],_=["telegram","feishu","lark","discord","slack","dingtalk","wecom","weixin","qq","qqbot","line"],l=["opencode","claudecode","acp","localcore-acp"],c="localcore-acp",d=["[Scheduler Tools]","If the user asks to create, view, edit, delete, or manually run a scheduled task for this conversation, use the Bash tool to run the local scheduler CLI.","Use these commands:",'lac scheduler add --cron "<5-field cron>" --message "<exact message to send>" --desc "<short label>" [--execution-mode same-thread|side-thread]',"lac scheduler list","lac scheduler list --thread","lac scheduler info <job-id>",'lac scheduler edit <job-id> [--cron "<5-field cron>"] [--message "<exact message>"] [--desc "<short label>"] [--enabled true|false] [--execution-mode same-thread|side-thread]',"lac scheduler del <job-id>","lac scheduler run <job-id>","Environment variables LOCAL_AI_WORKSPACE_ID, LOCAL_AI_THREAD_ID, LOCAL_AI_PLATFORM, LOCAL_AI_CHAT_ID, and LOCAL_AI_PLATFORM_USER_ID are already set when available.","Prefer relying on those variables instead of inventing your own route or creating session-only cron jobs.","By default, `lac scheduler list` shows all scheduled tasks in the current workspace. Use `lac scheduler list --thread` to show only the current conversation thread.","Use `--execution-mode same-thread` to reuse the current thread, or `--execution-mode side-thread` to run in a dedicated scheduled thread.","Only use the scheduler CLI when the user explicitly asks for scheduled automation.","[/Scheduler Tools]"].join(`
2
- `),i={allow:"allow",deny:"deny","allow all":"allow all",allowall:"allow all","perm:allow":"allow","perm:deny":"deny","perm:allow_all":"allow all"};function E(e,t=[]){return[d,...t.flatMap(o=>o?["",o]:[]),"","[User Message]",e,"[/User Message]"].join(`
3
- `)}function m(e){switch(String(e||"").trim().toLowerCase()){case"opencode":return s;case"claudecode":return"";default:return""}}function O(e,t){const o=String(e||"").trim().toLowerCase(),r=String(t||"").trim();return o?o==="opencode"?r||s:o==="claudecode"&&r.startsWith("opencode/")?"":r:r}function n(e){return e&&i[String(e).trim().toLowerCase()]||null}function f(e){return!!n(e==null?void 0:e.data)}function T(e){if(!e||typeof e!="object")return null;const t=e,o=typeof t.text=="string"?t.text:typeof t.Text=="string"?t.Text:"",r=typeof t.data=="string"?t.data:typeof t.Data=="string"?t.Data:"";if(!o||!r)return null;const a=n(r);return a?{text:a,data:a}:{text:o,data:r}}function A(e){return e?l.includes(String(e).trim().toLowerCase()):!1}function D(e){const t=String(e||"").trim().toLowerCase();return t==="acp"||t==="opencode"||t==="claudecode"||t===c}export{h as D,n as a,D as b,O as c,_ as d,u as e,m as g,f as i,T as n,A as s,E as w};
@@ -1 +0,0 @@
1
- import{y as t}from"./index-DT5la73R.js";const e=()=>t.get("/projects"),r=o=>t.get(`/projects/${o}`);export{r as g,e as l};