@kafca/agentdock 0.1.47 → 0.1.49

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 (74) hide show
  1. package/README.md +6 -0
  2. package/dist/renderer/assets/{Badge-BYREM3t3.js → Badge-DXjx3T4U.js} +1 -1
  3. package/dist/renderer/assets/{Chat-Dtb5X7sB.js → Chat-BdLcSBda.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-_slLloCh.js → ChatMarkdown-CSc8mfpv.js} +1 -1
  5. package/dist/renderer/assets/{Config-pHichs7H.js → Config-b5NK6ZGX.js} +2 -2
  6. package/dist/renderer/assets/{CronList-CJQMeCjz.js → CronList-B4vdtNHN.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-CY77Imde.js → Dashboard-BItoWTOa.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-DCqmct2y.js → EmptyState-DUDyUsLL.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-DFrxa-pv.js → KnowledgeDetail-WWH6KxiG.js} +2 -2
  10. package/dist/renderer/assets/{KnowledgeHome-DL-vAfrH.js → KnowledgeHome-Dn-NmRxu.js} +2 -2
  11. package/dist/renderer/assets/{Logs-jw9Uv590.js → Logs-4zMv4FvK.js} +1 -1
  12. package/dist/renderer/assets/{Modal-ChSm7tk6.js → Modal-BYTZpyD2.js} +3 -3
  13. package/dist/renderer/assets/{MonitorList-DrEwyS71.js → MonitorList-DzcwVuQO.js} +1 -1
  14. package/dist/renderer/assets/{Page-DhD-zgMU.js → Page-B0lex3w0.js} +1 -1
  15. package/dist/renderer/assets/{ProjectDetail-BVSjpfpL.js → ProjectDetail-zhmSLySm.js} +2 -2
  16. package/dist/renderer/assets/{ProjectList-CHGbTJjk.js → ProjectList-DZr7agnE.js} +1 -1
  17. package/dist/renderer/assets/{Select-rHs9CSam.js → Select-CybeCWXZ.js} +1 -1
  18. package/dist/renderer/assets/SessionChat-DfsXUkfX.js +1 -0
  19. package/dist/renderer/assets/{SessionList-Mr1uAnRW.js → SessionList-BHzY2T_K.js} +1 -1
  20. package/dist/renderer/assets/ThreadChat-BB_Y2SLO.js +28 -0
  21. package/dist/renderer/assets/{Workspace-MRRg8ndJ.js → Workspace-COip3Zy2.js} +1 -1
  22. package/dist/renderer/assets/{arrow-left-BU2NB2LA.js → arrow-left-SFlZE8SW.js} +1 -1
  23. package/dist/renderer/assets/{book-open-D-TGvWwa.js → book-open-CENFRKvo.js} +1 -1
  24. package/dist/renderer/assets/{bot-IYNh8tH5.js → bot-DeqZUVXX.js} +1 -1
  25. package/dist/renderer/assets/{check-CCZ0JO_S.js → check-BNBksd9W.js} +1 -1
  26. package/dist/renderer/assets/{chevron-down-00bRZi15.js → chevron-down-BOhwopXd.js} +1 -1
  27. package/dist/renderer/assets/{circle-CpQ8x4k_.js → circle-BTT_e1ZT.js} +1 -1
  28. package/dist/renderer/assets/{desktop-D0k2aGSj.js → desktop-D88Gquih.js} +1 -1
  29. package/dist/renderer/assets/{funnel-UbcrCBCp.js → funnel-Btin4gwi.js} +1 -1
  30. package/dist/renderer/assets/{heart-DvLsV7tL.js → heart-BCqqkImr.js} +1 -1
  31. package/dist/renderer/assets/index-CjCuR6DI.css +11 -0
  32. package/dist/renderer/assets/{index-B2L4G-b_.js → index-DvKeLUQ8.js} +2 -2
  33. package/dist/renderer/assets/{index-dy-g2Bh4.js → index-on-scaWG.js} +9 -9
  34. package/dist/renderer/assets/{pencil-qBYOgW5M.js → pencil-BHu_seP7.js} +1 -1
  35. package/dist/renderer/assets/{play-Dn6b4AGj.js → play-BoIfwVcE.js} +1 -1
  36. package/dist/renderer/assets/{plus-CaApUwEm.js → plus-_5sDk2H8.js} +1 -1
  37. package/dist/renderer/assets/projects-BR_Y3Dov.js +1 -0
  38. package/dist/renderer/assets/{save-CN6yvTfD.js → save-Bn9HUt7T.js} +1 -1
  39. package/dist/renderer/assets/{search-Dn2MxYiO.js → search-BOU1rpMw.js} +1 -1
  40. package/dist/renderer/assets/{server-DpcVLHeh.js → server-BKF47usT.js} +1 -1
  41. package/dist/renderer/assets/{session-utils-DnD2Q_y1.js → session-utils-B4vjXX1q.js} +1 -1
  42. package/dist/renderer/assets/{sessions-eK5D4FLt.js → sessions-CtFBPvOB.js} +1 -1
  43. package/dist/renderer/assets/{shield-check-DSSJkacw.js → shield-check-Q1O3wIT4.js} +1 -1
  44. package/dist/renderer/assets/{trash-2-DmYJlEvO.js → trash-2-CANqI4Db.js} +1 -1
  45. package/dist/renderer/assets/{user-Cvf_uiyQ.js → user-DTURDktb.js} +1 -1
  46. package/dist/renderer/assets/{wifi-off-CCTloj-V.js → wifi-off-YfCo8Dbk.js} +1 -1
  47. package/dist/renderer/index.html +2 -2
  48. package/dist-electron/packages/core-sdk/src/index.js +4 -0
  49. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +51 -6
  50. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +3 -0
  51. package/dist-electron/services/local-ai-core/src/acp/store/platform-store.js +13 -5
  52. package/dist-electron/services/local-ai-core/src/acp/store/schema.js +1 -0
  53. package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +3 -0
  54. package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +4 -1
  55. package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +14 -3
  56. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +10 -3
  57. package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +4 -0
  58. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +3 -0
  59. package/dist-electron/services/local-ai-core/src/scheduler/channel-execution-policy.js +3 -1
  60. package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +2 -1
  61. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +10 -1
  62. package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +2 -1
  63. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +10 -1
  64. package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +23 -2
  65. package/dist-electron/services/local-ai-core/src/thread/thread-slash-command-dispatcher.js +1 -0
  66. package/dist-electron/src/pages/Threads/thread-chat-model.js +2 -0
  67. package/dist-electron/tests/electron/workspace-task-store.test.js +22 -3
  68. package/dist-electron/tests/integration/local-core-refactor.test.js +253 -0
  69. package/dist-electron/tests/integration/local-core-routes.test.js +45 -0
  70. package/package.json +2 -2
  71. package/dist/renderer/assets/SessionChat-CDc2FNMA.js +0 -1
  72. package/dist/renderer/assets/ThreadChat-Detu5UfN.js +0 -23
  73. package/dist/renderer/assets/index-CO4fGqmv.css +0 -11
  74. package/dist/renderer/assets/projects-BwF5RlNV.js +0 -1
@@ -585,7 +585,7 @@ const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-uti
585
585
  process.env.HOME = '/home/agentdock-test';
586
586
  try {
587
587
  const existingPath = ['/usr/bin', '/bin'].join(node_path_1.delimiter);
588
- const path = (0, local_core_acp_session_coordinator_js_1.buildAgentPath)(existingPath, '/opt/agentdock/bin');
588
+ const path = (0, local_core_acp_session_coordinator_js_1.buildAgentPath)(existingPath, '/opt/agentdock/bin', { pathExists: () => false });
589
589
  const expectedEntries = process.platform === 'win32'
590
590
  ? ['/opt/agentdock/bin', '/usr/bin', '/bin']
591
591
  : ['/opt/agentdock/bin', '/home/agentdock-test/.local/bin', '/home/agentdock-test/bin', '/usr/bin', '/bin'];
@@ -600,6 +600,21 @@ const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-uti
600
600
  }
601
601
  }
602
602
  });
603
+ (0, node_test_1.default)('ACP runtime PATH adds Git Bash bin when Windows PATH only has Git cmd', () => {
604
+ const existingPath = [
605
+ 'C:\\Windows\\System32',
606
+ 'D:\\Program Files\\Git\\cmd',
607
+ ].join(';');
608
+ const path = (0, local_core_acp_session_coordinator_js_1.buildAgentPath)(existingPath, undefined, {
609
+ platform: 'win32',
610
+ pathExists: (candidate) => candidate === 'D:\\Program Files\\Git\\bin\\bash.exe',
611
+ });
612
+ strict_1.default.equal(path, [
613
+ 'D:\\Program Files\\Git\\bin',
614
+ 'C:\\Windows\\System32',
615
+ 'D:\\Program Files\\Git\\cmd',
616
+ ].join(';'));
617
+ });
603
618
  (0, node_test_1.default)('ACP runtime reads Windows Path env regardless of key casing', () => {
604
619
  strict_1.default.equal((0, env_utils_js_1.getPathEnv)({ Path: 'C:\\Windows\\System32;C:\\Program Files\\nodejs' }), 'C:\\Windows\\System32;C:\\Program Files\\nodejs');
605
620
  strict_1.default.equal((0, env_utils_js_1.getPathEnv)({ PATH: '/usr/bin:/bin' }), '/usr/bin:/bin');
@@ -635,8 +650,9 @@ const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-uti
635
650
  });
636
651
  (0, node_test_1.default)('ACP scheduled session can override permission mode without changing thread mode', async () => {
637
652
  const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-permission-mode-'));
653
+ let store = null;
638
654
  try {
639
- const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
655
+ store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
640
656
  const thread = store.createThread('workspace-a', 'Thread');
641
657
  const capturedSessionNewParams = [];
642
658
  let closeCount = 0;
@@ -702,13 +718,15 @@ const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-uti
702
718
  });
703
719
  }
704
720
  finally {
721
+ store?.close();
705
722
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
706
723
  }
707
724
  });
708
725
  (0, node_test_1.default)('ACP sandbox sessions use container workspace cwd', async () => {
709
726
  const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'sandbox-session-cwd-'));
727
+ let store = null;
710
728
  try {
711
- const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
729
+ store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
712
730
  const thread = store.createThread('workspace-a', 'Thread');
713
731
  let sessionNewParams;
714
732
  const coordinator = new local_core_acp_session_coordinator_js_1.LocalCoreAcpSessionCoordinator({
@@ -779,6 +797,7 @@ const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-uti
779
797
  strict_1.default.equal(sessionNewParams?.cwd, '/workspace');
780
798
  }
781
799
  finally {
800
+ store?.close();
782
801
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
783
802
  }
784
803
  });
@@ -313,11 +313,15 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
313
313
  try {
314
314
  const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
315
315
  const thread = store.createThread('workspace-a', 'Mode test', 'claudecode');
316
+ strict_1.default.equal(thread.agentMode, 'default');
316
317
  strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'default');
317
318
  const inheritedThread = store.createThread('workspace-a', 'Inherited mode test', 'claudecode', 'bypassPermissions');
319
+ strict_1.default.equal(inheritedThread.agentMode, 'bypassPermissions');
318
320
  strict_1.default.equal(store.getThreadRow(inheritedThread.id)?.agent_mode, 'bypassPermissions');
319
321
  store.updateThreadAgentMode(thread.id, 'bypassPermissions');
320
322
  strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'bypassPermissions');
323
+ strict_1.default.equal(store.getThread(thread.id, []).agentMode, 'bypassPermissions');
324
+ strict_1.default.equal(store.listThreadSummaries('workspace-a').find((item) => item.id === thread.id)?.agentMode, 'bypassPermissions');
321
325
  store.close();
322
326
  }
323
327
  finally {
@@ -854,6 +858,82 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
854
858
  policy.afterExecute?.(target, job);
855
859
  strict_1.default.equal(unregisteredBridge, true);
856
860
  });
861
+ (0, node_test_1.default)('lark side-thread execution policy applies preferred agent when creating a new scheduled thread', async () => {
862
+ const job = {
863
+ id: 'job-1',
864
+ workspaceId: '知识库',
865
+ platform: 'lark',
866
+ route: { type: 'channel.chat', channelId: 'chat-1', participantId: 'user-1', threadId: 'thread-origin' },
867
+ executionMode: 'side-thread',
868
+ triggerType: 'cron',
869
+ cronExpr: '*/2 * * * *',
870
+ promptTemplate: 'ping',
871
+ description: 'two-minute ping',
872
+ enabled: true,
873
+ concurrencyPolicy: 'skip_if_running',
874
+ createdAt: '2026-04-22T06:00:00.000Z',
875
+ updatedAt: '2026-04-22T06:00:00.000Z',
876
+ };
877
+ const createThreadCalls = [];
878
+ const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
879
+ store: {},
880
+ workspaceRouter: {
881
+ getThreadSessionKey: (threadId) => `session:${threadId}`,
882
+ listThreads: async () => [],
883
+ createThread: async (workspaceId, title, agentType) => {
884
+ createThreadCalls.push({ workspaceId, title: title || '', agentType });
885
+ return { id: 'thread-new' };
886
+ },
887
+ },
888
+ getChannelRuntime: () => ({
889
+ registerScheduledThreadBridge: () => () => { },
890
+ }),
891
+ }, async () => 'thread-origin', () => 'claudecode');
892
+ const target = await policy.resolveTarget(job);
893
+ strict_1.default.equal(target.threadId, 'thread-new');
894
+ strict_1.default.equal(createThreadCalls.length, 1);
895
+ strict_1.default.deepEqual(createThreadCalls[0], {
896
+ workspaceId: '知识库',
897
+ title: '[Scheduled:Lark] two-minute ping',
898
+ agentType: 'claudecode',
899
+ });
900
+ });
901
+ (0, node_test_1.default)('lark side-thread execution policy falls back to workspace default when no preferred agent is set', async () => {
902
+ const job = {
903
+ id: 'job-1',
904
+ workspaceId: '知识库',
905
+ platform: 'lark',
906
+ route: { type: 'channel.chat', channelId: 'chat-1', participantId: 'user-1', threadId: 'thread-origin' },
907
+ executionMode: 'side-thread',
908
+ triggerType: 'cron',
909
+ cronExpr: '*/2 * * * *',
910
+ promptTemplate: 'ping',
911
+ description: 'two-minute ping',
912
+ enabled: true,
913
+ concurrencyPolicy: 'skip_if_running',
914
+ createdAt: '2026-04-22T06:00:00.000Z',
915
+ updatedAt: '2026-04-22T06:00:00.000Z',
916
+ };
917
+ const createThreadCalls = [];
918
+ const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
919
+ store: {},
920
+ workspaceRouter: {
921
+ getThreadSessionKey: (threadId) => `session:${threadId}`,
922
+ listThreads: async () => [],
923
+ createThread: async (workspaceId, title, agentType) => {
924
+ createThreadCalls.push({ workspaceId, title: title || '', agentType });
925
+ return { id: 'thread-new' };
926
+ },
927
+ },
928
+ getChannelRuntime: () => ({
929
+ registerScheduledThreadBridge: () => () => { },
930
+ }),
931
+ }, async () => 'thread-origin', () => '');
932
+ const target = await policy.resolveTarget(job);
933
+ strict_1.default.equal(target.threadId, 'thread-new');
934
+ strict_1.default.equal(createThreadCalls.length, 1);
935
+ strict_1.default.equal(createThreadCalls[0].agentType, undefined);
936
+ });
857
937
  (0, node_test_1.default)('lark same-thread execution policy keeps the original thread target', async () => {
858
938
  const job = {
859
939
  id: 'job-1',
@@ -894,3 +974,176 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
894
974
  sessionKey: 'session:thread-origin',
895
975
  });
896
976
  });
977
+ (0, node_test_1.default)('platform thread binding persists preferred agent across upserts and updates', () => {
978
+ const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-binding-pref-'));
979
+ try {
980
+ const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
981
+ const now = new Date().toISOString();
982
+ store.upsertPlatformThreadBinding({
983
+ workspace_id: 'workspace-a',
984
+ platform: 'lark',
985
+ chat_id: 'chat-1',
986
+ platform_user_id: 'user-1',
987
+ thread_id: 'thread-1',
988
+ last_platform_message_id: null,
989
+ created_at: now,
990
+ updated_at: now,
991
+ });
992
+ store.updatePlatformThreadPreferredAgent('workspace-a', 'chat-1', 'user-1', 'claudecode', 'lark');
993
+ let binding = store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark');
994
+ strict_1.default.equal(binding?.preferred_agent_type, 'claudecode');
995
+ const later = new Date().toISOString();
996
+ store.upsertPlatformThreadBinding({
997
+ workspace_id: 'workspace-a',
998
+ platform: 'lark',
999
+ chat_id: 'chat-1',
1000
+ platform_user_id: 'user-1',
1001
+ thread_id: 'thread-2',
1002
+ last_platform_message_id: null,
1003
+ created_at: later,
1004
+ updated_at: later,
1005
+ });
1006
+ binding = store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark');
1007
+ strict_1.default.equal(binding?.thread_id, 'thread-2');
1008
+ strict_1.default.equal(binding?.preferred_agent_type, 'claudecode');
1009
+ store.updatePlatformThreadPreferredAgent('workspace-a', 'chat-1', 'user-1', null, 'lark');
1010
+ binding = store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark');
1011
+ strict_1.default.equal(binding?.preferred_agent_type, null);
1012
+ store.close();
1013
+ }
1014
+ finally {
1015
+ (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
1016
+ }
1017
+ });
1018
+ (0, node_test_1.default)('slash agent command in a channel writes preferred agent to the binding', async () => {
1019
+ const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-channel-agent-cmd-'));
1020
+ try {
1021
+ const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
1022
+ const thread = store.createThread('workspace-a', 'Channel thread', 'codex');
1023
+ const now = new Date().toISOString();
1024
+ store.upsertPlatformThreadBinding({
1025
+ workspace_id: 'workspace-a',
1026
+ platform: 'lark',
1027
+ chat_id: 'chat-1',
1028
+ platform_user_id: 'user-1',
1029
+ thread_id: thread.id,
1030
+ last_platform_message_id: null,
1031
+ created_at: now,
1032
+ updated_at: now,
1033
+ });
1034
+ const row = store.getThreadRow(thread.id);
1035
+ const dispatcher = new thread_slash_command_dispatcher_js_1.ThreadSlashCommandDispatcher({
1036
+ session: {
1037
+ listThreads: () => [],
1038
+ getThread: (id) => ({ id, title: row.title, messages: [] }),
1039
+ createThread: (workspaceId, title) => ({ id: 'new-thread', title, messages: [] }),
1040
+ renameThread: (id, title) => ({ id, title, messages: [] }),
1041
+ deleteThread: () => ({ deleted: true }),
1042
+ },
1043
+ thread: {
1044
+ getThreadRow: (id) => store.getThreadRow(id),
1045
+ updateThreadAgentMode: (id, mode) => store.updateThreadAgentMode(id, mode),
1046
+ updateThreadAgentType: (id, agentType) => store.updateThreadAgentType(id, agentType),
1047
+ getLatestRunForThread: () => undefined,
1048
+ createAuditEvent: () => { },
1049
+ getAgentTypes: () => ['codex', 'pi', 'claudecode'],
1050
+ setChannelPreferredAgent: (input) => store.updatePlatformThreadPreferredAgent(input.workspaceId, input.chatId, input.platformUserId, input.agentType, input.platform),
1051
+ },
1052
+ });
1053
+ const useResult = await dispatcher.execute({
1054
+ threadId: thread.id,
1055
+ workspaceId: 'workspace-a',
1056
+ content: '/agent use claudecode',
1057
+ defaultAgentType: 'codex',
1058
+ channel: { chatId: 'chat-1', platformUserId: 'user-1', platform: 'lark' },
1059
+ });
1060
+ strict_1.default.equal(useResult.handled, true);
1061
+ strict_1.default.equal(store.getThreadRow(thread.id)?.agent_type, 'claudecode');
1062
+ strict_1.default.equal(store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark')?.preferred_agent_type, 'claudecode');
1063
+ const resetResult = await dispatcher.execute({
1064
+ threadId: thread.id,
1065
+ workspaceId: 'workspace-a',
1066
+ content: '/agent reset',
1067
+ defaultAgentType: 'codex',
1068
+ channel: { chatId: 'chat-1', platformUserId: 'user-1', platform: 'lark' },
1069
+ });
1070
+ strict_1.default.equal(resetResult.handled, true);
1071
+ strict_1.default.equal(store.getThreadRow(thread.id)?.agent_type, 'codex');
1072
+ strict_1.default.equal(store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark')?.preferred_agent_type, null);
1073
+ store.close();
1074
+ }
1075
+ finally {
1076
+ (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
1077
+ }
1078
+ });
1079
+ (0, node_test_1.default)('channel session runtime applies preferred agent when creating a new thread', async () => {
1080
+ const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-channel-new-inherit-'));
1081
+ try {
1082
+ const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
1083
+ const original = store.createThread('workspace-a', 'Original channel thread', 'codex');
1084
+ const now = new Date().toISOString();
1085
+ store.upsertPlatformThreadBinding({
1086
+ workspace_id: 'workspace-a',
1087
+ platform: 'lark',
1088
+ chat_id: 'chat-1',
1089
+ platform_user_id: 'user-1',
1090
+ thread_id: original.id,
1091
+ last_platform_message_id: null,
1092
+ created_at: now,
1093
+ updated_at: now,
1094
+ });
1095
+ store.updatePlatformThreadPreferredAgent('workspace-a', 'chat-1', 'user-1', 'claudecode', 'lark');
1096
+ store.updateThreadAgentType(original.id, 'claudecode');
1097
+ const createdThreadIds = [];
1098
+ const routes = new Map();
1099
+ const dispatcher = new thread_slash_command_dispatcher_js_1.ThreadSlashCommandDispatcher({
1100
+ session: {
1101
+ listThreads: () => [],
1102
+ getThread: (id) => ({ id, title: 'X', messages: [] }),
1103
+ createThread: (_workspaceId, title) => {
1104
+ const created = store.createThread('workspace-a', title, 'codex');
1105
+ createdThreadIds.push({ id: created.id, initialAgentType: 'codex' });
1106
+ return created;
1107
+ },
1108
+ renameThread: (id, title) => ({ id, title, messages: [] }),
1109
+ deleteThread: () => ({ deleted: true }),
1110
+ },
1111
+ thread: {
1112
+ getThreadRow: (id) => store.getThreadRow(id),
1113
+ updateThreadAgentMode: (id, mode) => store.updateThreadAgentMode(id, mode),
1114
+ updateThreadAgentType: (id, agentType) => store.updateThreadAgentType(id, agentType),
1115
+ getLatestRunForThread: () => undefined,
1116
+ createAuditEvent: () => { },
1117
+ getAgentTypes: () => ['codex', 'claudecode'],
1118
+ },
1119
+ });
1120
+ const runtime = new session_command_runtime_js_1.ChannelSessionCommandRuntime({
1121
+ dispatcher,
1122
+ store,
1123
+ getThreadSessionKey: (id) => `session:${id}`,
1124
+ setThreadRoute: (sessionKey, route) => { routes.set(sessionKey, route); },
1125
+ createRoute: (input, id) => ({ workspaceId: input.workspaceId, chatId: input.chatId, threadId: id }),
1126
+ sendResult: async () => { },
1127
+ });
1128
+ const result = await runtime.execute({
1129
+ workspaceId: 'workspace-a',
1130
+ currentThreadId: original.id,
1131
+ text: '/new Follow up',
1132
+ defaultTitle: 'Follow up',
1133
+ defaultAgentType: 'codex',
1134
+ chatId: 'chat-1',
1135
+ platformUserId: 'user-1',
1136
+ platformKey: 'lark',
1137
+ instanceId: 'default',
1138
+ });
1139
+ strict_1.default.equal(result.handled, true);
1140
+ strict_1.default.equal(result.threadId, createdThreadIds[0]?.id);
1141
+ strict_1.default.equal(createdThreadIds[0]?.initialAgentType, 'codex', 'createThread starts with default agent before inheritance');
1142
+ strict_1.default.equal(store.getThreadRow(result.threadId)?.agent_type, 'claudecode', 'new thread should inherit preferred agent after creation');
1143
+ strict_1.default.equal(store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark')?.preferred_agent_type, 'claudecode');
1144
+ store.close();
1145
+ }
1146
+ finally {
1147
+ (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
1148
+ }
1149
+ });
@@ -5,7 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const node_test_1 = __importDefault(require("node:test"));
7
7
  const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const node_stream_1 = require("node:stream");
8
9
  const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/server-routes.js");
10
+ const thread_handler_js_1 = require("../../services/local-ai-core/src/runtime/handlers/thread-handler.js");
9
11
  (0, node_test_1.default)('local core route parser separates runtime refresh and runtime detail routes', () => {
10
12
  strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/logs'), { name: 'logs.list' });
11
13
  strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/runtimes'), { name: 'runtimes.list' });
@@ -45,6 +47,10 @@ const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/ser
45
47
  name: 'thread.update-knowledge-bases',
46
48
  threadId: 'thread-1',
47
49
  });
50
+ strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/threads/thread-1/mode'), {
51
+ name: 'thread.update-mode',
52
+ threadId: 'thread-1',
53
+ });
48
54
  strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/threads/thread-1/messages'), {
49
55
  name: 'thread.messages.send',
50
56
  threadId: 'thread-1',
@@ -55,6 +61,45 @@ const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/ser
55
61
  });
56
62
  strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/threads/thread-1/messages'), null);
57
63
  });
64
+ (0, node_test_1.default)('thread mode route handler persists normalized mode through workspace router', async () => {
65
+ const calls = [];
66
+ const map = new Map();
67
+ (0, thread_handler_js_1.registerThreadHandlers)(map, {
68
+ setThreadMode: async (threadId, mode) => {
69
+ calls.push({ threadId, mode });
70
+ return {
71
+ id: threadId,
72
+ workspaceId: 'workspace-a',
73
+ title: 'Thread',
74
+ live: false,
75
+ updatedAt: '2026-06-14T00:00:00.000Z',
76
+ createdAt: '2026-06-14T00:00:00.000Z',
77
+ historyCount: 0,
78
+ excerpt: '',
79
+ agentMode: 'bypassPermissions',
80
+ messages: [],
81
+ selectedKnowledgeBaseIds: [],
82
+ };
83
+ },
84
+ });
85
+ const req = node_stream_1.Readable.from([Buffer.from(JSON.stringify({ mode: 'bypassPermissions' }))]);
86
+ const headers = {};
87
+ let responseBody = '';
88
+ const res = {
89
+ statusCode: 0,
90
+ setHeader: (name, value) => {
91
+ headers[name] = value;
92
+ },
93
+ end: (body) => {
94
+ responseBody = body;
95
+ },
96
+ };
97
+ await map.get('thread.update-mode')({ name: 'thread.update-mode', threadId: 'thread-1' }, req, res, new URL('http://127.0.0.1/api/local/v1/threads/thread-1/mode'));
98
+ strict_1.default.deepEqual(calls, [{ threadId: 'thread-1', mode: 'bypassPermissions' }]);
99
+ strict_1.default.equal(res.statusCode, 200);
100
+ strict_1.default.equal(headers['Content-Type'], 'application/json; charset=utf-8');
101
+ strict_1.default.equal(JSON.parse(responseBody).data.agentMode, 'bypassPermissions');
102
+ });
58
103
  (0, node_test_1.default)('local core route parser only accepts run interrupt action with POST', () => {
59
104
  strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/runs/run-1/interrupt'), {
60
105
  name: 'run.interrupt',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kafca/agentdock",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "license": "PolyForm-Noncommercial-1.0.0",
5
5
  "type": "module",
6
6
  "main": "dist-electron/electron/main.js",
@@ -74,7 +74,7 @@
74
74
  "@types/react-dom": "^19.1.2",
75
75
  "@vitejs/plugin-react": "^4.4.1",
76
76
  "autoprefixer": "^10.4.21",
77
- "electron": "^35.0.2",
77
+ "electron": "^35.7.5",
78
78
  "electron-builder": "^26.8.1",
79
79
  "postcss": "^8.5.3",
80
80
  "tailwindcss": "^3.4.17",
@@ -1 +0,0 @@
1
- import{u as C,D as z,r,j as e,L as S,B as g,g as b,T as B}from"./index-dy-g2Bh4.js";import{B as j}from"./Badge-BYREM3t3.js";import{g as E,a as L}from"./sessions-eK5D4FLt.js";import{C as A,A as D}from"./ChatMarkdown-_slLloCh.js";import{A as I}from"./arrow-left-BU2NB2LA.js";import{C as M}from"./circle-CpQ8x4k_.js";import{W as y,R}from"./wifi-off-CCTloj-V.js";import{B as T}from"./bot-IYNh8tH5.js";import{U}from"./user-Cvf_uiyQ.js";import"./check-CCZ0JO_S.js";function J(){var p;const{t:a}=C(),{project:i,id:l}=z(),[s,v]=r.useState(null),[d,m]=r.useState(""),[c,x]=r.useState(!1),[w,f]=r.useState(!0),u=r.useRef(null),o=r.useCallback(async()=>{if(!(!i||!l))try{f(!0);const t=await E(i,l,200);v(t)}finally{f(!1)}},[i,l]);r.useEffect(()=>{o()},[o]),r.useEffect(()=>{var t;(t=u.current)==null||t.scrollIntoView({behavior:"smooth"})},[s==null?void 0:s.history]);const h=async()=>{if(!d.trim()||!i||!s)return;const t=d.trim();m(""),x(!0);try{await L(i,{session_key:s.session_key,message:t}),setTimeout(o,1500)}finally{x(!1)}},N=t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),h())};return w&&!s?e.jsx("div",{className:"flex items-center justify-center h-64 text-muted-foreground animate-pulse",children:"Loading..."}):e.jsxs("div",{className:"flex flex-col h-[calc(100vh-8rem)] animate-fade-in",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-slate-200/80 pb-4 dark:border-white/[0.08]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(S,{to:"/sessions",className:"p-2 rounded-md hover:bg-accent/10 transition-colors",children:e.jsx(I,{size:18,className:"text-muted-foreground"})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-[1.4rem] font-semibold text-foreground",children:(s==null?void 0:s.name)||l}),s!=null&&s.live?e.jsxs("span",{className:"flex items-center gap-1 rounded-full bg-primary/10 px-2 py-0.5 text-[10px] text-primary",children:[e.jsx(M,{size:5,className:"fill-current"})," live"]}):e.jsxs("span",{className:"flex items-center gap-1 text-[10px] text-muted-foreground bg-muted px-1.5 py-0.5 rounded-full",children:[e.jsx(y,{size:9})," ",a("sessions.offline")]})]}),e.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[e.jsx(j,{children:i}),(s==null?void 0:s.platform)&&e.jsx(j,{variant:"info",children:s.platform}),e.jsx("span",{className:"text-xs text-muted-foreground",children:s==null?void 0:s.session_key})]})]})]}),e.jsxs(g,{size:"sm",variant:"ghost",onClick:o,children:[e.jsx(R,{size:14})," ",a("common.refresh")]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto py-6",children:[(!(s!=null&&s.history)||s.history.length===0)&&e.jsx("p",{className:"text-center text-sm text-muted-foreground py-12",children:a("sessions.noMessages")}),e.jsx("div",{className:"mx-auto max-w-4xl space-y-5",children:(p=s==null?void 0:s.history)==null?void 0:p.map((t,k)=>{const n=t.role==="user";return e.jsxs("div",{className:b("flex gap-3",n?"justify-end":"justify-start"),children:[!n&&e.jsx("div",{className:"mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary/10 dark:bg-white/[0.06]",children:e.jsx(T,{size:16,className:"text-primary"})}),e.jsx("div",{className:b("rounded-[20px] px-5 py-3.5 text-sm",n?"chat-user-glass max-w-[70%] rounded-br-sm text-slate-950 dark:text-slate-50":"max-w-[78%] rounded-bl-sm border border-slate-200 bg-[#fbfbfd] text-card-foreground dark:border-white/[0.08] dark:bg-white/[0.04]"),children:e.jsx(A,{content:t.content,isUser:n})}),n&&e.jsx("div",{className:"mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted dark:bg-white/[0.08]",children:e.jsx(U,{size:16,className:"text-muted-foreground"})})]},k)})}),e.jsx("div",{ref:u})]}),e.jsx("div",{className:"border-t pt-4",children:s!=null&&s.live?e.jsx("div",{className:"mx-auto max-w-4xl",children:e.jsxs("div",{className:"relative",children:[e.jsx(B,{value:d,onChange:t=>m(t.target.value),onKeyDown:N,placeholder:a("sessions.messageInput"),rows:2,className:"min-h-[88px] rounded-[22px] border-gray-200 bg-[#fbfbfd] px-4 pb-14 pt-3 text-[15px] leading-6 dark:border-white/[0.08] dark:bg-[rgba(255,255,255,0.04)] sm:min-h-[96px] sm:px-5 sm:pt-4",disabled:c}),e.jsx(g,{onClick:h,disabled:c||!d.trim(),size:"icon","aria-label":a("sessions.messageInput"),className:"absolute bottom-3 right-3 h-10 w-10 rounded-full bg-primary px-0 text-white shadow-none hover:bg-[#0071e3] disabled:bg-slate-300 disabled:text-white disabled:opacity-100 dark:bg-primary dark:text-white dark:hover:bg-[#2997ff] dark:disabled:bg-white/20 dark:disabled:text-white/55 sm:h-11 sm:w-11",children:c?e.jsxs("svg",{className:"animate-spin h-4 w-4",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4",fill:"none"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]}):e.jsx(D,{size:20,strokeWidth:2.2})})]})}):e.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground bg-muted rounded-lg",children:[e.jsx(y,{size:14}),e.jsx("span",{children:a("sessions.notLiveHint")})]})})]})}export{J as default};