@kafca/agentdock 0.1.20 → 0.1.22

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 (73) hide show
  1. package/README.md +13 -0
  2. package/dist/renderer/assets/{Badge-C1f8JpRb.js → Badge-B_rgzxYO.js} +1 -1
  3. package/dist/renderer/assets/{Chat-S50iFg3I.js → Chat-CYtp6m7T.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-VWL9Vnpz.js → ChatMarkdown-DOBMwa9d.js} +1 -1
  5. package/dist/renderer/assets/{Config-Yc9ExUI6.js → Config-DppMSPHF.js} +2 -2
  6. package/dist/renderer/assets/CronList-BDtB1SuA.js +1 -0
  7. package/dist/renderer/assets/{Dashboard-CzXCvZVR.js → Dashboard-iFlRnd7Y.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-9FbY2o6z.js → EmptyState-bKDt_T88.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-D_BDc4Zt.js → KnowledgeDetail-CKPhKPIg.js} +2 -2
  10. package/dist/renderer/assets/{KnowledgeHome-DsUA_BCR.js → KnowledgeHome-CFeJre6i.js} +3 -3
  11. package/dist/renderer/assets/Logs-CyxSVOFK.js +1 -0
  12. package/dist/renderer/assets/{Modal-Bt5VBC6S.js → Modal-CxPnk1Ey.js} +1 -1
  13. package/dist/renderer/assets/{Page-Da_Kc6Qm.js → Page-kU9vubzj.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-B4Ps80yv.js → ProjectDetail-D9FY9NWZ.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-BWTP9npz.js → ProjectList-DIwr2TXg.js} +1 -1
  16. package/dist/renderer/assets/{Select-DWs3hVUh.js → Select-BaWoz5Dn.js} +2 -7
  17. package/dist/renderer/assets/SessionChat-Cm9CW8Zx.js +1 -0
  18. package/dist/renderer/assets/SessionList-UPnWlXXO.js +1 -0
  19. package/dist/renderer/assets/{ThreadChat-nhnikHnk.js → ThreadChat-YvVsACW3.js} +7 -7
  20. package/dist/renderer/assets/{Workspace-pq_JYdTN.js → Workspace-ST7V8u6T.js} +2 -2
  21. package/dist/renderer/assets/{arrow-left-CR39hNvq.js → arrow-left-BmZAVGV7.js} +1 -1
  22. package/dist/renderer/assets/{book-open-Bot2WKl8.js → book-open-DZ3GXFgI.js} +1 -1
  23. package/dist/renderer/assets/{check-Drbl2nU3.js → check-CeVSvDCv.js} +1 -1
  24. package/dist/renderer/assets/chevron-down-yryuMoQ-.js +6 -0
  25. package/dist/renderer/assets/{circle-BXFfAwnA.js → circle-eASsFISH.js} +1 -1
  26. package/dist/renderer/assets/desktop-Che1cbpE.js +4 -0
  27. package/dist/renderer/assets/desktop-DkrP_s-b.js +1 -0
  28. package/dist/renderer/assets/{funnel-BNKYan_N.js → funnel-pwAe2Ixp.js} +1 -1
  29. package/dist/renderer/assets/{heart-wUOSwQE-.js → heart-B2Sp-bCm.js} +1 -1
  30. package/dist/renderer/assets/{index-DT5la73R.js → index-BC3MWbS1.js} +3 -3
  31. package/dist/renderer/assets/{index-CmOphqrf.js → index-Cse2Br64.js} +1 -1
  32. package/dist/renderer/assets/{index-3CzgyQYB.css → index-DFiJR0vv.css} +1 -1
  33. package/dist/renderer/assets/{pencil-D7-8ggue.js → pencil-BfEunUF3.js} +1 -1
  34. package/dist/renderer/assets/{play-BCYOAfet.js → play-Dcs9Lj_Z.js} +1 -1
  35. package/dist/renderer/assets/{plug-Dsik74Yl.js → plug-B_YschQe.js} +1 -1
  36. package/dist/renderer/assets/{plus-CKrU26_l.js → plus-Pqo-cXHQ.js} +1 -1
  37. package/dist/renderer/assets/projects-BCj0k_Gy.js +1 -0
  38. package/dist/renderer/assets/{save-q7M2_eAl.js → save-BKM5yi1A.js} +1 -1
  39. package/dist/renderer/assets/{search-BptY4_MT.js → search-BU42h10M.js} +1 -1
  40. package/dist/renderer/assets/{server-DSSEF0D8.js → server-DA5t3Rtr.js} +1 -1
  41. package/dist/renderer/assets/{session-utils-BFuqPZ5x.js → session-utils-Cr9VhNSq.js} +1 -1
  42. package/dist/renderer/assets/{sessions-CrWo3g3u.js → sessions-bvV8ZMfX.js} +1 -1
  43. package/dist/renderer/assets/{shield-check-CCU722RQ.js → shield-check-BeOJKur_.js} +1 -1
  44. package/dist/renderer/assets/{trash-2-DaeeiV8x.js → trash-2-CQ7HKO3q.js} +1 -1
  45. package/dist/renderer/assets/{user-Bkz8J7Yw.js → user-CtFjY9SV.js} +1 -1
  46. package/dist/renderer/assets/{wifi-off-P8xdIOHU.js → wifi-off-Ci5m0ysf.js} +1 -1
  47. package/dist/renderer/index.html +2 -2
  48. package/dist-electron/electron/agent-runtime-detector.test.js +11 -0
  49. package/dist-electron/electron/local-core-refactor.test.js +403 -3
  50. package/dist-electron/electron/plugin-kernel.test.js +43 -9
  51. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +9 -9
  52. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +60 -0
  53. package/dist-electron/services/local-ai-core/src/{gateway → channel/lark}/local-core-lark-gateway.js +371 -13
  54. package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +33 -0
  55. package/dist-electron/services/local-ai-core/src/{gateway → channel/weixin}/local-core-weixin-gateway.js +238 -3
  56. package/dist-electron/services/local-ai-core/src/cli/lac.js +41 -1
  57. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +2 -1
  58. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
  59. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-lark-plugin.js +1 -1
  60. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-weixin-plugin.js +1 -1
  61. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +19 -8
  62. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +23 -12
  63. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +41 -0
  64. package/dist-electron/services/local-ai-core/src/runtime/server.js +10 -0
  65. package/dist-electron/shared/desktop.js +17 -2
  66. package/package.json +2 -1
  67. package/dist/renderer/assets/CronList-DTboWazi.js +0 -1
  68. package/dist/renderer/assets/Logs-DDAmR67f.js +0 -1
  69. package/dist/renderer/assets/SessionChat-DOhUnOfH.js +0 -1
  70. package/dist/renderer/assets/SessionList-ChZYFXuo.js +0 -1
  71. package/dist/renderer/assets/desktop-5k3W66fH.js +0 -1
  72. package/dist/renderer/assets/desktop-D4Z7M9RM.js +0 -3
  73. package/dist/renderer/assets/projects-CGDtNb0M.js +0 -1
@@ -1 +1 @@
1
- import{y as o}from"./index-DT5la73R.js";const i=s=>o.get(`/projects/${s}/sessions`),c=(s,e,t)=>o.get(`/projects/${s}/sessions/${e}`,{history_limit:String(t)}),r=(s,e,t)=>o.patch(`/projects/${s}/sessions/${e}`,t),a=(s,e)=>o.delete(`/projects/${s}/sessions/${e}`),p=(s,e)=>o.post(`/projects/${s}/sessions/switch`,e),$=(s,e)=>o.post(`/projects/${s}/send`,e);export{$ as a,a as d,c as g,i as l,r,p as s};
1
+ import{z as o}from"./index-BC3MWbS1.js";const i=s=>o.get(`/projects/${s}/sessions`),c=(s,e,t)=>o.get(`/projects/${s}/sessions/${e}`,{history_limit:String(t)}),r=(s,e,t)=>o.patch(`/projects/${s}/sessions/${e}`,t),a=(s,e)=>o.delete(`/projects/${s}/sessions/${e}`),p=(s,e)=>o.post(`/projects/${s}/sessions/switch`,e),$=(s,e)=>o.post(`/projects/${s}/send`,e);export{$ as a,a as d,c as g,i as l,r,p as s};
@@ -1,4 +1,4 @@
1
- import{c}from"./index-DT5la73R.js";/**
1
+ import{c}from"./index-BC3MWbS1.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-DT5la73R.js";/**
1
+ import{c as e}from"./index-BC3MWbS1.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-DT5la73R.js";/**
1
+ import{c as e}from"./index-BC3MWbS1.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-DT5la73R.js";/**
1
+ import{c as a}from"./index-BC3MWbS1.js";/**
2
2
  * @license lucide-react v0.487.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="./favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>AgentDock</title>
8
- <script type="module" crossorigin src="./assets/index-DT5la73R.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-3CzgyQYB.css">
8
+ <script type="module" crossorigin src="./assets/index-BC3MWbS1.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-DFiJR0vv.css">
10
10
  </head>
11
11
  <body class="bg-[#f5f5f7] dark:bg-[#151516]">
12
12
  <div id="root"></div>
@@ -14,9 +14,11 @@ const agent_runtime_detector_js_1 = require("../services/local-ai-core/src/runti
14
14
  try {
15
15
  const opencode = (0, node_path_1.join)(dir, 'opencode');
16
16
  (0, node_fs_1.writeFileSync)(opencode, '#!/bin/sh\nexit 0\n', 'utf8');
17
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'package.json'), '{}\n', 'utf8');
17
18
  (0, node_fs_1.chmodSync)(opencode, 0o755);
18
19
  const runtimes = (0, agent_runtime_detector_js_1.detectInstalledAgentRuntimes)({
19
20
  env: { PATH: dir },
21
+ requireFrom: (0, node_path_1.join)(dir, 'package.json'),
20
22
  });
21
23
  const byType = new Map(runtimes.map((runtime) => [runtime.agentType, runtime]));
22
24
  strict_1.default.equal(byType.get('opencode')?.installed, true);
@@ -34,6 +36,15 @@ const agent_runtime_detector_js_1 = require("../services/local-ai-core/src/runti
34
36
  (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
35
37
  }
36
38
  });
39
+ (0, node_test_1.default)('agent runtime detector reports bundled codex ACP when available', () => {
40
+ const codex = (0, agent_runtime_detector_js_1.detectInstalledAgentRuntimes)({
41
+ env: { PATH: '' },
42
+ }).find((runtime) => runtime.agentType === 'codex');
43
+ strict_1.default.equal(codex?.installed, true);
44
+ strict_1.default.equal(codex?.status, 'installed');
45
+ strict_1.default.equal(codex?.source, 'bundled');
46
+ strict_1.default.match(codex?.command || '', /@zed-industries[/\\]codex-acp[/\\]bin[/\\]codex-acp\.js$/);
47
+ });
37
48
  (0, node_test_1.default)('agent runtime detector honors configured project commands', () => {
38
49
  const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agent-runtime-config-'));
39
50
  try {
@@ -8,13 +8,14 @@ const strict_1 = __importDefault(require("node:assert/strict"));
8
8
  const node_fs_1 = require("node:fs");
9
9
  const node_os_1 = require("node:os");
10
10
  const node_path_1 = require("node:path");
11
+ const node_stream_1 = require("node:stream");
11
12
  const local_core_acp_response_processor_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-response-processor.js");
12
13
  const scheduled_conversation_executor_js_1 = require("../services/local-ai-core/src/scheduler/scheduled-conversation-executor.js");
13
14
  const scheduler_run_lifecycle_js_1 = require("../services/local-ai-core/src/scheduler/scheduler-run-lifecycle.js");
14
15
  const lark_execution_policies_js_1 = require("../services/local-ai-core/src/scheduler/lark-execution-policies.js");
15
16
  const local_schedule_adapter_js_1 = require("../services/local-ai-core/src/scheduler/local-schedule-adapter.js");
16
- const local_core_weixin_gateway_js_1 = require("../services/local-ai-core/src/gateway/local-core-weixin-gateway.js");
17
- const local_core_lark_gateway_js_1 = require("../services/local-ai-core/src/gateway/local-core-lark-gateway.js");
17
+ const local_core_weixin_gateway_js_1 = require("../services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js");
18
+ const local_core_lark_gateway_js_1 = require("../services/local-ai-core/src/channel/lark/local-core-lark-gateway.js");
18
19
  const local_core_acp_turn_coordinator_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js");
19
20
  const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-store.js");
20
21
  (0, node_test_1.default)('ACP tool call update is emitted with its pending tool name', () => {
@@ -668,6 +669,7 @@ const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/loc
668
669
  });
669
670
  (0, node_test_1.default)('lark permission requests render as clickable card buttons', async () => {
670
671
  const createdCards = [];
672
+ const patchedCards = [];
671
673
  const threadActions = [];
672
674
  const client = {
673
675
  im: {
@@ -676,7 +678,12 @@ const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/loc
676
678
  createdCards.push(JSON.parse(String(request.data.content || '{}')));
677
679
  return { data: { message_id: 'permission-msg-1' } };
678
680
  },
679
- patch: async () => { },
681
+ patch: async (request) => {
682
+ patchedCards.push({
683
+ messageId: request.path?.message_id,
684
+ card: JSON.parse(String(request.data.content || '{}')),
685
+ });
686
+ },
680
687
  },
681
688
  },
682
689
  };
@@ -756,13 +763,134 @@ const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/loc
756
763
  action: 'permission_response',
757
764
  response: 'allow all',
758
765
  thread_id: 'thread-1',
766
+ session_key: 'session:thread-1',
759
767
  },
760
768
  },
769
+ context: {
770
+ open_message_id: 'permission-msg-1',
771
+ },
761
772
  },
762
773
  });
763
774
  strict_1.default.deepEqual(threadActions, [
764
775
  { threadId: 'thread-1', action: 'allow all' },
765
776
  ]);
777
+ strict_1.default.equal(patchedCards.length, 1);
778
+ strict_1.default.equal(patchedCards[0]?.messageId, 'permission-msg-1');
779
+ strict_1.default.match(patchedCards[0]?.card?.elements?.[0]?.content || '', /工具确认已处理/);
780
+ strict_1.default.equal(patchedCards[0]?.card?.elements?.some((element) => element.tag === 'action'), false);
781
+ });
782
+ (0, node_test_1.default)('lark channel folds tool result output in progress cards', async () => {
783
+ const createdCards = [];
784
+ const patchedCards = [];
785
+ const client = {
786
+ im: {
787
+ message: {
788
+ create: async (request) => {
789
+ createdCards.push(JSON.parse(String(request.data.content || '{}')));
790
+ return { data: { message_id: 'tool-msg-1' } };
791
+ },
792
+ patch: async (request) => {
793
+ patchedCards.push(JSON.parse(String(request.data.content || '{}')));
794
+ },
795
+ },
796
+ },
797
+ };
798
+ const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
799
+ store: {
800
+ getPlatformThreadBinding: () => ({
801
+ workspace_id: 'default',
802
+ platform: 'lark',
803
+ chat_id: 'chat-1',
804
+ platform_user_id: 'user-1',
805
+ thread_id: 'thread-1',
806
+ last_platform_message_id: null,
807
+ }),
808
+ },
809
+ readConfig: async () => null,
810
+ getWorkspaceRouter: () => ({}),
811
+ eventBus: { emit: () => { }, on: () => () => { } },
812
+ });
813
+ const internals = gateway;
814
+ internals.runtime.set('default', {
815
+ workspaceId: 'default',
816
+ enabled: true,
817
+ status: 'running',
818
+ connected: true,
819
+ appId: 'app-1',
820
+ client,
821
+ });
822
+ internals.threadRouting.set('session:thread-1', {
823
+ workspaceId: 'default',
824
+ platformUserId: 'user-1',
825
+ chatId: 'chat-1',
826
+ threadId: 'thread-1',
827
+ });
828
+ await gateway.onBridgeEvent({
829
+ type: 'reply',
830
+ sessionKey: 'session:thread-1',
831
+ replyCtx: 'run-1',
832
+ messageId: 'tool-1',
833
+ content: '🔧 Terminal: ls - completed - secret terminal output',
834
+ });
835
+ const text = createdCards[0]?.elements?.[0]?.content || patchedCards[0]?.elements?.[0]?.content || '';
836
+ strict_1.default.match(text, /Terminal: ls - completed/);
837
+ strict_1.default.doesNotMatch(text, /secret terminal output/);
838
+ });
839
+ (0, node_test_1.default)('lark card action message id can be extracted from full callback payload', async () => {
840
+ const patchedCards = [];
841
+ const threadActions = [];
842
+ const client = {
843
+ im: {
844
+ message: {
845
+ patch: async (request) => {
846
+ patchedCards.push({
847
+ messageId: request.path?.message_id,
848
+ card: JSON.parse(String(request.data.content || '{}')),
849
+ });
850
+ },
851
+ },
852
+ },
853
+ };
854
+ const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
855
+ store: {},
856
+ readConfig: async () => null,
857
+ getWorkspaceRouter: () => ({
858
+ sendThreadAction: async (threadId, action) => {
859
+ threadActions.push({ threadId, action });
860
+ return { runId: 'run-1' };
861
+ },
862
+ }),
863
+ eventBus: { emit: () => { }, on: () => () => { } },
864
+ });
865
+ const internals = gateway;
866
+ internals.runtime.set('default', {
867
+ workspaceId: 'default',
868
+ enabled: true,
869
+ status: 'running',
870
+ connected: true,
871
+ appId: 'app-1',
872
+ client,
873
+ });
874
+ await internals.handleCardActionEvent('default', {
875
+ schema: '2.0',
876
+ event: {
877
+ action: {
878
+ value: {
879
+ action: 'permission_response',
880
+ response: 'allow',
881
+ thread_id: 'thread-1',
882
+ },
883
+ },
884
+ },
885
+ event_context: {
886
+ open_message_id: 'permission-msg-nested',
887
+ },
888
+ });
889
+ strict_1.default.deepEqual(threadActions, [
890
+ { threadId: 'thread-1', action: 'allow' },
891
+ ]);
892
+ strict_1.default.equal(patchedCards[0]?.messageId, 'permission-msg-nested');
893
+ strict_1.default.equal(patchedCards[0]?.card?.elements?.some((element) => element.tag === 'action'), false);
766
894
  });
767
895
  (0, node_test_1.default)('lark permission requests fall back to text commands when card actions are disabled', async () => {
768
896
  const createdCards = [];
@@ -821,6 +949,278 @@ const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/loc
821
949
  strict_1.default.match(createdCards[0]?.elements?.[0]?.content || '', /请直接回复/);
822
950
  strict_1.default.equal(createdCards[0]?.elements?.some((element) => element.tag === 'action'), false);
823
951
  });
952
+ (0, node_test_1.default)('lark image messages are downloaded and forwarded as generic channel image parts', async () => {
953
+ const sentMessages = [];
954
+ const pngBytes = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
955
+ const client = {
956
+ im: {
957
+ messageResource: {
958
+ get: async (request) => {
959
+ strict_1.default.equal(request.path.message_id, 'msg-image-1');
960
+ strict_1.default.equal(request.path.file_key, 'img-key-1');
961
+ strict_1.default.equal(request.params.type, 'image');
962
+ return {
963
+ headers: { 'content-type': 'image/png' },
964
+ getReadableStream: () => node_stream_1.Readable.from([pngBytes]),
965
+ };
966
+ },
967
+ },
968
+ messageReaction: {
969
+ create: async () => ({ data: { reaction_id: 'reaction-1' } }),
970
+ },
971
+ },
972
+ };
973
+ const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
974
+ store: {
975
+ expirePendingPairings: () => { },
976
+ getAuthorizedUser: () => ({
977
+ id: 'auth-1',
978
+ workspace_id: 'default',
979
+ platform: 'lark',
980
+ platform_user_id: 'user-1',
981
+ chat_id: 'chat-1',
982
+ display_name: 'User',
983
+ thread_id: 'thread-1',
984
+ }),
985
+ getPlatformThreadBinding: () => ({
986
+ workspace_id: 'default',
987
+ platform: 'lark',
988
+ chat_id: 'chat-1',
989
+ platform_user_id: 'user-1',
990
+ thread_id: 'thread-1',
991
+ last_platform_message_id: null,
992
+ }),
993
+ getLatestRunForThread: () => null,
994
+ clearPlatformThreadMessageId: () => { },
995
+ },
996
+ readConfig: async () => ({
997
+ projects: [{
998
+ name: 'default',
999
+ root: '/tmp/project',
1000
+ platforms: [{
1001
+ type: 'lark',
1002
+ options: {
1003
+ app_id: 'app-1',
1004
+ app_secret: 'secret-1',
1005
+ },
1006
+ }],
1007
+ }],
1008
+ }),
1009
+ getWorkspaceRouter: () => ({
1010
+ getThreadSessionKey: (threadId) => `session:${threadId}`,
1011
+ sendThreadMessage: async (threadId, content) => {
1012
+ sentMessages.push({ threadId, content });
1013
+ return { runId: 'run-1' };
1014
+ },
1015
+ }),
1016
+ eventBus: { emit: () => { }, on: () => () => { } },
1017
+ });
1018
+ const internals = gateway;
1019
+ internals.runtime.set('default', {
1020
+ workspaceId: 'default',
1021
+ enabled: true,
1022
+ status: 'running',
1023
+ connected: true,
1024
+ appId: 'app-1',
1025
+ client,
1026
+ });
1027
+ await internals.handleMessageEvent('default', {
1028
+ event: {
1029
+ sender: {
1030
+ sender_id: { user_id: 'user-1' },
1031
+ },
1032
+ message: {
1033
+ message_id: 'msg-image-1',
1034
+ message_type: 'image',
1035
+ chat_id: 'chat-1',
1036
+ content: JSON.stringify({ image_key: 'img-key-1' }),
1037
+ },
1038
+ },
1039
+ });
1040
+ strict_1.default.equal(sentMessages.length, 1);
1041
+ strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1042
+ strict_1.default.match(sentMessages[0]?.content?.displayText, /\[User Message\]\n\[Image\]\n\[\/User Message\]/);
1043
+ strict_1.default.deepEqual(sentMessages[0]?.content?.contentParts?.map((part) => part.type), ['text', 'image']);
1044
+ strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.mimeType, 'image/png');
1045
+ strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.data, pngBytes.toString('base64'));
1046
+ });
1047
+ (0, node_test_1.default)('lark channel can upload and send a local file', async () => {
1048
+ const tempDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'lark-file-send-'));
1049
+ try {
1050
+ const filePath = (0, node_path_1.join)(tempDir, 'report.pdf');
1051
+ (0, node_fs_1.writeFileSync)(filePath, 'pdf content');
1052
+ const uploads = [];
1053
+ const messages = [];
1054
+ const client = {
1055
+ im: {
1056
+ file: {
1057
+ create: async (request) => {
1058
+ uploads.push(request);
1059
+ await new Promise((resolve, reject) => {
1060
+ request.data.file.on('data', () => { });
1061
+ request.data.file.on('error', reject);
1062
+ request.data.file.on('end', resolve);
1063
+ });
1064
+ return { file_key: 'file-key-1' };
1065
+ },
1066
+ },
1067
+ message: {
1068
+ create: async (request) => {
1069
+ messages.push(request);
1070
+ return { data: { message_id: 'msg-file-1' } };
1071
+ },
1072
+ },
1073
+ },
1074
+ };
1075
+ const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
1076
+ store: {},
1077
+ readConfig: async () => null,
1078
+ getWorkspaceRouter: () => ({}),
1079
+ eventBus: { emit: () => { }, on: () => () => { } },
1080
+ });
1081
+ const internals = gateway;
1082
+ internals.runtime.set('default', {
1083
+ workspaceId: 'default',
1084
+ enabled: true,
1085
+ status: 'running',
1086
+ connected: true,
1087
+ appId: 'app-1',
1088
+ client,
1089
+ });
1090
+ const result = await gateway.sendFile('default', {
1091
+ path: filePath,
1092
+ channelId: 'oc_chat_1',
1093
+ });
1094
+ strict_1.default.equal(uploads.length, 1);
1095
+ strict_1.default.equal(uploads[0]?.data?.file_type, 'pdf');
1096
+ strict_1.default.equal(uploads[0]?.data?.file_name, 'report.pdf');
1097
+ strict_1.default.equal(messages.length, 1);
1098
+ strict_1.default.equal(messages[0]?.params?.receive_id_type, 'chat_id');
1099
+ strict_1.default.equal(messages[0]?.data?.receive_id, 'oc_chat_1');
1100
+ strict_1.default.equal(messages[0]?.data?.msg_type, 'file');
1101
+ strict_1.default.deepEqual(JSON.parse(messages[0]?.data?.content), { file_key: 'file-key-1' });
1102
+ strict_1.default.deepEqual(result, {
1103
+ platform: 'lark',
1104
+ workspaceId: 'default',
1105
+ channelId: 'oc_chat_1',
1106
+ messageId: 'msg-file-1',
1107
+ fileKey: 'file-key-1',
1108
+ fileName: 'report.pdf',
1109
+ fileSize: Buffer.byteLength('pdf content'),
1110
+ });
1111
+ }
1112
+ finally {
1113
+ (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
1114
+ }
1115
+ });
1116
+ (0, node_test_1.default)('weixin channel can encrypt, upload, and send a local file', async () => {
1117
+ const tempDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'weixin-file-send-'));
1118
+ const originalFetch = globalThis.fetch;
1119
+ try {
1120
+ const filePath = (0, node_path_1.join)(tempDir, 'report.txt');
1121
+ (0, node_fs_1.writeFileSync)(filePath, 'hello weixin');
1122
+ const uploadUrlRequests = [];
1123
+ const cdnUploads = [];
1124
+ const sentMessages = [];
1125
+ globalThis.fetch = (async (url, init) => {
1126
+ const target = String(url);
1127
+ if (target.endsWith('/ilink/bot/getuploadurl')) {
1128
+ uploadUrlRequests.push(JSON.parse(String(init?.body || '{}')));
1129
+ return {
1130
+ ok: true,
1131
+ json: async () => ({ ret: 0, upload_param: 'upload-param-1' }),
1132
+ };
1133
+ }
1134
+ if (target.includes('/upload?')) {
1135
+ const body = init?.body instanceof Uint8Array ? init.body : new Uint8Array();
1136
+ cdnUploads.push({ url: target, size: body.byteLength });
1137
+ return {
1138
+ ok: true,
1139
+ headers: {
1140
+ get: (name) => name.toLowerCase() === 'x-encrypted-param' ? 'download-param-1' : null,
1141
+ },
1142
+ };
1143
+ }
1144
+ if (target.endsWith('/ilink/bot/sendmessage')) {
1145
+ sentMessages.push(JSON.parse(String(init?.body || '{}')));
1146
+ return {
1147
+ ok: true,
1148
+ json: async () => ({ ret: 0 }),
1149
+ };
1150
+ }
1151
+ throw new Error(`Unexpected fetch ${target}`);
1152
+ });
1153
+ const gateway = new local_core_weixin_gateway_js_1.LocalCoreWeixinGateway({
1154
+ store: {
1155
+ getPlatformThreadBinding: () => ({
1156
+ workspace_id: 'default',
1157
+ platform: 'weixin',
1158
+ chat_id: 'user-1',
1159
+ platform_user_id: 'user-1',
1160
+ thread_id: 'thread-1',
1161
+ last_platform_message_id: 'ctx-1',
1162
+ }),
1163
+ listAuthorizedUsers: () => [],
1164
+ },
1165
+ readConfig: async () => ({
1166
+ projects: [{
1167
+ name: 'default',
1168
+ root: '/tmp/project',
1169
+ platforms: [{
1170
+ type: 'weixin',
1171
+ options: {
1172
+ token: 'token-1',
1173
+ account_id: 'account-1',
1174
+ base_url: 'https://weixin.example',
1175
+ cdn_base_url: 'https://cdn.example/c2c',
1176
+ },
1177
+ }],
1178
+ }],
1179
+ }),
1180
+ getWorkspaceRouter: () => ({}),
1181
+ eventBus: { emit: () => { }, on: () => () => { } },
1182
+ });
1183
+ const internals = gateway;
1184
+ internals.runtime.set('default', {
1185
+ workspaceId: 'default',
1186
+ enabled: true,
1187
+ status: 'running',
1188
+ connected: true,
1189
+ accountId: 'account-1',
1190
+ });
1191
+ const result = await gateway.sendFile('default', {
1192
+ path: filePath,
1193
+ channelId: 'user-1',
1194
+ participantId: 'user-1',
1195
+ });
1196
+ strict_1.default.equal(uploadUrlRequests.length, 1);
1197
+ strict_1.default.equal(uploadUrlRequests[0]?.media_type, 3);
1198
+ strict_1.default.equal(uploadUrlRequests[0]?.to_user_id, 'user-1');
1199
+ strict_1.default.equal(uploadUrlRequests[0]?.rawsize, Buffer.byteLength('hello weixin'));
1200
+ strict_1.default.equal(uploadUrlRequests[0]?.filesize, 16);
1201
+ strict_1.default.equal(cdnUploads.length, 1);
1202
+ strict_1.default.match(cdnUploads[0]?.url || '', /encrypted_query_param=upload-param-1/);
1203
+ strict_1.default.equal(cdnUploads[0]?.size, 16);
1204
+ strict_1.default.equal(sentMessages.length, 1);
1205
+ const message = sentMessages[0]?.msg;
1206
+ strict_1.default.equal(message?.to_user_id, 'user-1');
1207
+ strict_1.default.equal(message?.context_token, 'ctx-1');
1208
+ strict_1.default.equal(message?.item_list?.[0]?.type, 4);
1209
+ strict_1.default.equal(message?.item_list?.[0]?.file_item?.file_name, 'report.txt');
1210
+ strict_1.default.equal(message?.item_list?.[0]?.file_item?.len, String(Buffer.byteLength('hello weixin')));
1211
+ strict_1.default.equal(message?.item_list?.[0]?.file_item?.media?.encrypt_query_param, 'download-param-1');
1212
+ strict_1.default.equal(result.platform, 'weixin');
1213
+ strict_1.default.equal(result.channelId, 'user-1');
1214
+ strict_1.default.equal(result.fileName, 'report.txt');
1215
+ strict_1.default.equal(result.fileSize, Buffer.byteLength('hello weixin'));
1216
+ strict_1.default.match(result.messageId, /^openclaw-weixin-/);
1217
+ strict_1.default.ok(result.fileKey);
1218
+ }
1219
+ finally {
1220
+ globalThis.fetch = originalFetch;
1221
+ (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
1222
+ }
1223
+ });
824
1224
  (0, node_test_1.default)('lark message callbacks acknowledge before long thread runs finish', async () => {
825
1225
  let registeredHandlers = {};
826
1226
  let sentMessages = 0;
@@ -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-'));
@@ -538,7 +572,7 @@ name = "claude-workspace"
538
572
  [projects.agent]
539
573
  type = "claudecode"
540
574
  `);
541
- 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']);
542
576
  strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'claudecode'), false);
543
577
  strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), []);
544
578
  await strict_1.default.rejects(() => runtime.workspaceRouter.listThreads('claude-workspace'), /Workspace "claude-workspace" is not configured as a Local AI Core ACP workspace/);