@kafca/agentdock 0.1.56 → 0.1.58

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 (200) hide show
  1. package/README.md +7 -0
  2. package/dist/renderer/assets/{Badge-DjcoXx2J.js → Badge-BfY1VaHS.js} +1 -1
  3. package/dist/renderer/assets/Card-DWA_Tww_.js +1 -0
  4. package/dist/renderer/assets/Config-nHj3g1U5.js +21 -0
  5. package/dist/renderer/assets/CronList-BwkA_I7v.js +1 -0
  6. package/dist/renderer/assets/Dashboard-D0_A7k02.js +41 -0
  7. package/dist/renderer/assets/{EmptyState-DIZLiccW.js → EmptyState-zatDjLj_.js} +1 -1
  8. package/dist/renderer/assets/HighlightedMarkdown-CsM5n70d.js +12 -0
  9. package/dist/renderer/assets/Input-R1XdFK1K.js +1 -0
  10. package/dist/renderer/assets/{KnowledgeDetail-CJ9IYOsa.js → KnowledgeDetail-Co5sxeGd.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-BLGPwnBO.js → KnowledgeHome-DwziPw5m.js} +3 -3
  12. package/dist/renderer/assets/Logs-NwZUtR_G.js +6 -0
  13. package/dist/renderer/assets/{Modal-CQiUpfl4.js → Modal-CrcIwIFL.js} +3 -3
  14. package/dist/renderer/assets/MonitorList-CS1uLS5A.js +1 -0
  15. package/dist/renderer/assets/Page-Dp0obeSw.js +1 -0
  16. package/dist/renderer/assets/{Select-D6z65IT3.js → Select-6mVQaf84.js} +2 -2
  17. package/dist/renderer/assets/ThreadChat-BFzbn3L6.js +86 -0
  18. package/dist/renderer/assets/Workspace-DCmo8BMm.js +19 -0
  19. package/dist/renderer/assets/{arrow-left-CV5cL3f4.js → arrow-left-9rjg_Hp4.js} +1 -1
  20. package/dist/renderer/assets/{book-open-DThvB7pj.js → book-open-Bu8GCHnG.js} +1 -1
  21. package/dist/renderer/assets/channels-BxVbG-6j.js +6 -0
  22. package/dist/renderer/assets/chevron-down-BE8uQmWM.js +11 -0
  23. package/dist/renderer/assets/index-B7TeF6Es.js +41 -0
  24. package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
  25. package/dist/renderer/assets/index-BkI9POxj.js +167 -0
  26. package/dist/renderer/assets/knowledge-Kl19SqGS.js +1 -0
  27. package/dist/renderer/assets/{pencil-Dv5WxhdC.js → pencil-CPB7SK1m.js} +1 -1
  28. package/dist/renderer/assets/{play-DnCnft83.js → play-DGAO4OCu.js} +1 -1
  29. package/dist/renderer/assets/{plus-DsxIagzS.js → plus-Bt26z-4q.js} +1 -1
  30. package/dist/renderer/assets/{save-CST01lIc.js → save-Une7GMxT.js} +1 -1
  31. package/dist/renderer/assets/{search-B_bqy6Wd.js → search-DE0agJjJ.js} +1 -1
  32. package/dist/renderer/assets/{shield-check-D6lG5Aoc.js → shield-check-De7xaXZh.js} +1 -1
  33. package/dist/renderer/assets/threads-BnsNL5ei.js +1 -0
  34. package/dist/renderer/assets/{trash-2-Cefngb6Q.js → trash-2-6iGF59Ac.js} +1 -1
  35. package/dist/renderer/index.html +2 -2
  36. package/dist-electron/packages/contracts/src/automation.js +2 -0
  37. package/dist-electron/packages/contracts/src/channels.js +2 -0
  38. package/dist-electron/packages/contracts/src/knowledge.js +2 -0
  39. package/dist-electron/packages/contracts/src/local-core.js +15 -0
  40. package/dist-electron/packages/contracts/src/runtime.js +2 -0
  41. package/dist-electron/packages/contracts/src/scheduler.js +2 -0
  42. package/dist-electron/packages/contracts/src/threads.js +2 -0
  43. package/dist-electron/packages/core-sdk/src/automation.js +32 -0
  44. package/dist-electron/packages/core-sdk/src/channels.js +121 -0
  45. package/dist-electron/packages/core-sdk/src/client.js +455 -0
  46. package/dist-electron/packages/core-sdk/src/index.js +43 -607
  47. package/dist-electron/packages/core-sdk/src/knowledge.js +85 -0
  48. package/dist-electron/packages/core-sdk/src/request.js +22 -0
  49. package/dist-electron/packages/core-sdk/src/runtime.js +214 -0
  50. package/dist-electron/packages/core-sdk/src/scheduler.js +32 -0
  51. package/dist-electron/packages/core-sdk/src/threads.js +67 -0
  52. package/dist-electron/packages/plugin-sdk/src/agents.js +2 -0
  53. package/dist-electron/packages/plugin-sdk/src/automation.js +2 -0
  54. package/dist-electron/packages/plugin-sdk/src/channels.js +2 -0
  55. package/dist-electron/packages/plugin-sdk/src/index.js +21 -0
  56. package/dist-electron/packages/plugin-sdk/src/knowledge.js +2 -0
  57. package/dist-electron/packages/plugin-sdk/src/runtime.js +2 -0
  58. package/dist-electron/packages/plugin-sdk/src/scheduler.js +2 -0
  59. package/dist-electron/packages/plugin-sdk/src/threads.js +2 -0
  60. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +5 -8
  61. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +2 -2
  62. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +3 -3
  63. package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +5 -5
  64. package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +12 -12
  65. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +2 -2
  66. package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +11 -11
  67. package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +3 -3
  68. package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +7 -7
  69. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +2 -2
  70. package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +3 -3
  71. package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +3 -3
  72. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +2 -2
  73. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +4 -4
  74. package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +2 -2
  75. package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +2 -2
  76. package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +3 -3
  77. package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +1 -1
  78. package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +4 -4
  79. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +6 -47
  80. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +2 -5
  81. package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +33 -18
  82. package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -2
  83. package/dist-electron/services/local-ai-core/src/channel/lark/gateway-utils.js +91 -0
  84. package/dist-electron/services/local-ai-core/src/channel/lark/inbound-handler.js +180 -0
  85. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +58 -539
  86. package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +112 -0
  87. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +5 -20
  88. package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +37 -0
  89. package/dist-electron/services/local-ai-core/src/channel/shared/bridge-event-helpers.js +23 -0
  90. package/dist-electron/services/local-ai-core/src/channel/shared/channel-keys.js +6 -0
  91. package/dist-electron/services/local-ai-core/src/channel/shared/inbound-authorization.js +54 -0
  92. package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +2 -2
  93. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-media.js +21 -0
  94. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +167 -0
  95. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +55 -512
  96. package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +149 -0
  97. package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +92 -0
  98. package/dist-electron/services/local-ai-core/src/cli/lac.js +9 -11
  99. package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +2 -2
  100. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +28 -70
  101. package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +8 -0
  102. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +2 -2
  103. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +2 -2
  104. package/dist-electron/services/local-ai-core/src/router/workspace-bridge-event-stream.js +29 -0
  105. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +5 -5
  106. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +47 -60
  107. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -5
  108. package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +6 -6
  109. package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +3 -3
  110. package/dist-electron/services/local-ai-core/src/runtime/external-service.js +3 -1
  111. package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +11 -2
  112. package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +12 -6
  113. package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +10 -2
  114. package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +18 -6
  115. package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +7 -2
  116. package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +28 -3
  117. package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +10 -2
  118. package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +18 -5
  119. package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +14 -2
  120. package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +18 -15
  121. package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +13 -2
  122. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +35 -8
  123. package/dist-electron/services/local-ai-core/src/runtime/request-validation.js +79 -0
  124. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +9 -9
  125. package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +10 -1
  126. package/dist-electron/services/local-ai-core/src/runtime/server.js +2 -1
  127. package/dist-electron/services/local-ai-core/src/runtime/workspace-project-registry.js +76 -0
  128. package/dist-electron/services/local-ai-core/src/sandbox/opensandbox-client.js +3 -2
  129. package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
  130. package/dist-electron/services/local-ai-core/src/scheduler/base-channel-schedule-adapter.js +77 -0
  131. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +13 -79
  132. package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +2 -10
  133. package/dist-electron/services/local-ai-core/src/scheduler/run-polling.js +19 -0
  134. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +3 -37
  135. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +20 -0
  136. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +15 -28
  137. package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +12 -0
  138. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +13 -79
  139. package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +75 -0
  140. package/dist-electron/shared/desktop.js +1 -64
  141. package/dist-electron/src/components/chat/chat-controller-state.js +60 -0
  142. package/dist-electron/src/components/chat/chat-event-gate.js +146 -0
  143. package/dist-electron/src/components/chat/markdown-code-block.js +8 -0
  144. package/dist-electron/src/pages/Desktop/workspace-model.js +212 -0
  145. package/dist-electron/src/pages/Threads/thread-chat-model.js +50 -51
  146. package/dist-electron/tests/contracts/agent-message-policy.test.js +21 -0
  147. package/dist-electron/tests/contracts/architecture-docs.test.js +56 -33
  148. package/dist-electron/tests/contracts/chat-event-gate.test.js +114 -0
  149. package/dist-electron/tests/contracts/chat-markdown.test.js +16 -0
  150. package/dist-electron/tests/contracts/thread-task-state.test.js +32 -0
  151. package/dist-electron/tests/contracts/workspace-identity.test.js +23 -0
  152. package/dist-electron/tests/electron/core-client.test.js +284 -0
  153. package/dist-electron/tests/electron/plugin-kernel.test.js +54 -0
  154. package/dist-electron/tests/electron/renderer-packaging.test.js +9 -0
  155. package/dist-electron/tests/electron/workspace-task-store.test.js +31 -0
  156. package/dist-electron/tests/integration/channel-inbound-authorization.test.js +80 -0
  157. package/dist-electron/tests/integration/lark-gateway-utils.test.js +13 -0
  158. package/dist-electron/tests/integration/lark-gateway.test.js +4 -4
  159. package/dist-electron/tests/integration/local-core-refactor.test.js +15 -8
  160. package/dist-electron/tests/integration/weixin-runtime-state.test.js +32 -0
  161. package/dist-electron/tests/integration/weixin-text-utils.test.js +15 -0
  162. package/package.json +9 -4
  163. package/dist/renderer/assets/Chat-C5Bkm9-M.js +0 -2
  164. package/dist/renderer/assets/ChatMarkdown-uc36EzyD.js +0 -45
  165. package/dist/renderer/assets/Config-DHtQuuLy.js +0 -21
  166. package/dist/renderer/assets/CronList-BYUnhzSC.js +0 -1
  167. package/dist/renderer/assets/Dashboard-BPnGMDg3.js +0 -26
  168. package/dist/renderer/assets/Logs-Dyd2Uwlt.js +0 -1
  169. package/dist/renderer/assets/MonitorList-Byg3E3Gt.js +0 -1
  170. package/dist/renderer/assets/Page-7DxXvTMm.js +0 -1
  171. package/dist/renderer/assets/ProjectDetail-Dfr5s0Ja.js +0 -11
  172. package/dist/renderer/assets/ProjectList-CDDQjAQ_.js +0 -1
  173. package/dist/renderer/assets/SessionChat-s7CAaCPL.js +0 -1
  174. package/dist/renderer/assets/SessionList-D-OycKKS.js +0 -1
  175. package/dist/renderer/assets/ThreadChat-CLdFynB5.js +0 -28
  176. package/dist/renderer/assets/Workspace-xfT5z4j_.js +0 -19
  177. package/dist/renderer/assets/bot-CWQ5-ovl.js +0 -6
  178. package/dist/renderer/assets/check-DKM7zfWG.js +0 -6
  179. package/dist/renderer/assets/chevron-down-Dfw2KU87.js +0 -6
  180. package/dist/renderer/assets/circle-CoP4RA4s.js +0 -6
  181. package/dist/renderer/assets/desktop-CsDYrOOU.js +0 -1
  182. package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
  183. package/dist/renderer/assets/funnel-DpVLlG3g.js +0 -6
  184. package/dist/renderer/assets/heart-BMD36ejP.js +0 -6
  185. package/dist/renderer/assets/index-COxSYBjE.js +0 -182
  186. package/dist/renderer/assets/index-CTGTI-cm.js +0 -41
  187. package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
  188. package/dist/renderer/assets/projects-DgjHcNgt.js +0 -1
  189. package/dist/renderer/assets/server-ecH4GNwa.js +0 -11
  190. package/dist/renderer/assets/session-utils-Dd_WKMYW.js +0 -6
  191. package/dist/renderer/assets/sessions-BQ33Mv4k.js +0 -1
  192. package/dist/renderer/assets/user-D_8RSH-T.js +0 -6
  193. package/dist/renderer/assets/wifi-off-DB9D-pOu.js +0 -11
  194. package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +0 -13
  195. package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +0 -13
  196. package/dist-electron/src/api/client.js +0 -73
  197. package/dist-electron/src/api/sessions.js +0 -21
  198. package/dist-electron/src/lib/session-utils.js +0 -51
  199. package/dist-electron/src/pages/Projects/project-provider-config.js +0 -75
  200. package/dist-electron/tests/electron/project-provider-config.test.js +0 -71
@@ -1021,6 +1021,60 @@ model = "deepseek-v4-flash"
1021
1021
  strict_1.default.match(response.body.error, /Unknown route/);
1022
1022
  strict_1.default.equal(response.body.errorInfo?.severity, 'error');
1023
1023
  });
1024
+ (0, node_test_1.default)('server rejects malformed endpoint input with a 400 response before domain dispatch', async () => {
1025
+ let dispatched = false;
1026
+ const controller = new node_events_1.EventEmitter();
1027
+ const workspaceRouter = {
1028
+ createThread: async () => {
1029
+ dispatched = true;
1030
+ return {};
1031
+ },
1032
+ };
1033
+ const server = new server_js_1.LocalAiCoreServer({ controller, workspaceRouter }, { port: 0 });
1034
+ const response = await invokeServer(server, 'POST', '/api/local/v1/threads', { workspaceId: 42 });
1035
+ strict_1.default.equal(response.statusCode, 400);
1036
+ strict_1.default.equal(response.body.ok, false);
1037
+ strict_1.default.match(response.body.error, /workspaceId/);
1038
+ strict_1.default.equal(dispatched, false);
1039
+ const emptyResponse = await invokeServer(server, 'POST', '/api/local/v1/threads', { workspaceId: ' ' });
1040
+ strict_1.default.equal(emptyResponse.statusCode, 400);
1041
+ strict_1.default.match(emptyResponse.body.error, /workspaceId/);
1042
+ strict_1.default.equal(dispatched, false);
1043
+ });
1044
+ (0, node_test_1.default)('server validates the actual desktop settings contract before dispatch', async () => {
1045
+ let dispatched = false;
1046
+ const controller = new node_events_1.EventEmitter();
1047
+ controller.saveSettings = async () => {
1048
+ dispatched = true;
1049
+ return {};
1050
+ };
1051
+ const server = new server_js_1.LocalAiCoreServer({ controller }, { port: 0 });
1052
+ const invalidKnowledge = await invokeServer(server, 'POST', '/api/local/v1/runtime/settings', { knowledge: 42 });
1053
+ strict_1.default.equal(invalidKnowledge.statusCode, 400);
1054
+ strict_1.default.match(invalidKnowledge.body.error, /knowledge/);
1055
+ const invalidPlugins = await invokeServer(server, 'POST', '/api/local/v1/runtime/settings', { plugins: [] });
1056
+ strict_1.default.equal(invalidPlugins.statusCode, 400);
1057
+ strict_1.default.match(invalidPlugins.body.error, /plugins/);
1058
+ const invalidPluginSettings = await invokeServer(server, 'POST', '/api/local/v1/runtime/settings', {
1059
+ plugins: { 'channel.lark': { enabled: 'yes' } },
1060
+ });
1061
+ strict_1.default.equal(invalidPluginSettings.statusCode, 400);
1062
+ strict_1.default.match(invalidPluginSettings.body.error, /plugins\.channel\.lark\.enabled/);
1063
+ const invalidAuthMode = await invokeServer(server, 'POST', '/api/local/v1/runtime/settings', {
1064
+ knowledge: { authMode: 'cookie' },
1065
+ });
1066
+ strict_1.default.equal(invalidAuthMode.statusCode, 400);
1067
+ strict_1.default.match(invalidAuthMode.body.error, /knowledge\.authMode/);
1068
+ strict_1.default.equal(dispatched, false);
1069
+ const valid = await invokeServer(server, 'POST', '/api/local/v1/runtime/settings', {
1070
+ autoStartService: true,
1071
+ defaultProject: 'workspace-stable',
1072
+ knowledge: { authMode: 'bearer', token: 'secret' },
1073
+ plugins: { 'channel.lark': { enabled: true, config: { appId: 'app-id' } } },
1074
+ });
1075
+ strict_1.default.equal(valid.statusCode, 200);
1076
+ strict_1.default.equal(dispatched, true);
1077
+ });
1024
1078
  (0, node_test_1.default)('server diagnostics routes dispatch through generic bindings', async () => {
1025
1079
  const calls = [];
1026
1080
  const controller = Object.assign(new node_events_1.EventEmitter(), {
@@ -31,6 +31,15 @@ const rootDir = node_process_1.default.cwd();
31
31
  strict_1.default.ok(buildIndex >= 0, 'pnpm e2e:smoke must start from a production build');
32
32
  strict_1.default.ok(smokeIndex > buildIndex, 'pnpm e2e:smoke must run smoke checks after the production build');
33
33
  });
34
+ (0, node_test_1.default)('development startup rewrites workspace aliases before launching Electron', () => {
35
+ const devScript = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, 'scripts', 'dev.mjs'), 'utf8');
36
+ const aliasCommandIndex = devScript.indexOf("['exec', 'tsc-alias', '-p', 'tsconfig.electron.json']");
37
+ const readyAssignmentIndex = devScript.indexOf('electronReady = true', aliasCommandIndex);
38
+ const launchIndex = devScript.indexOf('maybeLaunchElectron()', readyAssignmentIndex);
39
+ strict_1.default.ok(aliasCommandIndex >= 0, 'pnpm dev must rewrite emitted @cc/* aliases');
40
+ strict_1.default.ok(readyAssignmentIndex > aliasCommandIndex, 'Electron must become ready only after alias rewriting is configured');
41
+ strict_1.default.ok(launchIndex > readyAssignmentIndex, 'Electron must launch only after alias rewriting completes');
42
+ });
34
43
  (0, node_test_1.default)('production renderer build has a loadable entry document and assets', () => {
35
44
  const rendererDir = (0, node_path_1.join)(rootDir, 'dist', 'renderer');
36
45
  const indexPath = (0, node_path_1.join)(rendererDir, 'index.html');
@@ -42,6 +42,37 @@ const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-uti
42
42
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
43
43
  }
44
44
  });
45
+ (0, node_test_1.default)('runtime project migration makes workspace registry authoritative and preserves identity across rename', async () => {
46
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-project-migration-'));
47
+ try {
48
+ const runtime = (0, bootstrap_js_1.bootstrapLocalCoreRuntime)({ userDataPath, enableKnowledge: false, log: () => { } });
49
+ const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
50
+ await controller.saveRuntimeConfig({
51
+ projects: [{
52
+ name: 'Workspace A',
53
+ agent: { type: 'pi', options: { work_dir: '/tmp/workspace-a' } },
54
+ platforms: [],
55
+ }],
56
+ });
57
+ const first = await controller.readRuntimeConfig();
58
+ const workspaceId = first.config.projects?.[0]?.workspace_id;
59
+ strict_1.default.ok(workspaceId);
60
+ strict_1.default.equal(controller.store.getWorkspaceRegistryEntry(workspaceId)?.displayName, 'Workspace A');
61
+ strict_1.default.equal(controller.store.readRuntimeConfig().config.projects, undefined);
62
+ await controller.saveRuntimeConfig({
63
+ ...first.config,
64
+ projects: [{ ...first.config.projects[0], name: 'Workspace B' }],
65
+ });
66
+ const renamed = await controller.readRuntimeConfig();
67
+ strict_1.default.equal(renamed.config.projects?.[0]?.workspace_id, workspaceId);
68
+ strict_1.default.equal(controller.store.getWorkspaceRegistryEntry(workspaceId)?.displayName, 'Workspace B');
69
+ strict_1.default.equal((await controller.workspaceRouter.listWorkspaces())[0]?.id, workspaceId);
70
+ await controller.close();
71
+ }
72
+ finally {
73
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
74
+ }
75
+ });
45
76
  (0, node_test_1.default)('model providers persist independently from workspace config', () => {
46
77
  const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'model-provider-store-'));
47
78
  try {
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const inbound_authorization_js_1 = require("../../services/local-ai-core/src/channel/shared/inbound-authorization.js");
9
+ function createStore() {
10
+ const authorizedUsers = [];
11
+ const pairings = [];
12
+ return {
13
+ authorizedUsers,
14
+ pairings,
15
+ store: {
16
+ expirePendingPairings() { },
17
+ getAuthorizedUser(workspaceId, platformUserId, platform) {
18
+ return authorizedUsers.find((row) => row.workspace_id === workspaceId &&
19
+ row.platform_user_id === platformUserId &&
20
+ row.platform === platform);
21
+ },
22
+ createAuthorizedUser(input) {
23
+ authorizedUsers.push(input);
24
+ },
25
+ listPendingPairings() {
26
+ return pairings;
27
+ },
28
+ createPairingRequest(input) {
29
+ pairings.push(input);
30
+ },
31
+ },
32
+ };
33
+ }
34
+ const identity = {
35
+ workspaceId: 'workspace-1',
36
+ platformKey: 'lark:default',
37
+ platformUserId: 'user-1',
38
+ chatId: 'chat-1',
39
+ displayName: 'User One',
40
+ };
41
+ (0, node_test_1.default)('shared inbound authorization auto-approves and returns the stored user', () => {
42
+ const fixture = createStore();
43
+ let changes = 0;
44
+ const result = (0, inbound_authorization_js_1.resolveInboundChannelAuthorization)({
45
+ store: fixture.store,
46
+ identity,
47
+ autoApprove: true,
48
+ authorizedUserIdPrefix: 'lark-user',
49
+ generatePairingCode: () => '123456',
50
+ onStateChanged: () => { changes += 1; },
51
+ });
52
+ strict_1.default.equal(result.status, 'authorized');
53
+ strict_1.default.equal(fixture.authorizedUsers.length, 1);
54
+ strict_1.default.equal(changes, 1);
55
+ });
56
+ (0, node_test_1.default)('shared inbound authorization reuses an existing pending pairing', () => {
57
+ const fixture = createStore();
58
+ fixture.pairings.push({
59
+ code: '654321',
60
+ workspace_id: 'workspace-1',
61
+ platform: 'lark:default',
62
+ platform_user_id: 'user-1',
63
+ chat_id: 'chat-1',
64
+ status: 'pending',
65
+ });
66
+ let generated = 0;
67
+ const result = (0, inbound_authorization_js_1.resolveInboundChannelAuthorization)({
68
+ store: fixture.store,
69
+ identity,
70
+ autoApprove: false,
71
+ authorizedUserIdPrefix: 'lark-user',
72
+ generatePairingCode: () => {
73
+ generated += 1;
74
+ return '123456';
75
+ },
76
+ });
77
+ strict_1.default.deepEqual(result, { status: 'pending', pairingCode: '654321' });
78
+ strict_1.default.equal(generated, 0);
79
+ strict_1.default.equal(fixture.pairings.length, 1);
80
+ });
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const gateway_utils_js_1 = require("../../services/local-ai-core/src/channel/lark/gateway-utils.js");
9
+ (0, node_test_1.default)('lark gateway attachment utilities preserve content summaries and mime detection', () => {
10
+ strict_1.default.equal((0, gateway_utils_js_1.summarizeLarkInboundContentParts)([{ type: 'file', fileName: 'report.pdf' }]), '[File: report.pdf]');
11
+ strict_1.default.equal((0, gateway_utils_js_1.extractLarkHeaderMimeType)({ 'Content-Type': 'image/png; charset=binary' }), 'image/png');
12
+ strict_1.default.equal((0, gateway_utils_js_1.sniffLarkImageExtension)(Buffer.from([0x89, 0x50, 0x4e, 0x47])), 'png');
13
+ });
@@ -1265,7 +1265,7 @@ function findLarkPostMdText(content) {
1265
1265
  });
1266
1266
  strict_1.default.equal(sentMessages.length, 1);
1267
1267
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1268
- strict_1.default.match(sentMessages[0]?.content?.displayText, /\[User Message\]\n\[Image\]\n\[\/User Message\]/);
1268
+ strict_1.default.equal(sentMessages[0]?.content?.displayText, '[Image]');
1269
1269
  strict_1.default.deepEqual(sentMessages[0]?.content?.contentParts?.map((part) => part.type), ['text', 'image']);
1270
1270
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.mimeType, 'image/png');
1271
1271
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.data, pngBytes.toString('base64'));
@@ -1366,7 +1366,7 @@ function findLarkPostMdText(content) {
1366
1366
  });
1367
1367
  strict_1.default.equal(sentMessages.length, 1);
1368
1368
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1369
- strict_1.default.match(sentMessages[0]?.content?.displayText, /\[User Message\]\n\[File: report\.pdf\]\n\[\/User Message\]/);
1369
+ strict_1.default.equal(sentMessages[0]?.content?.displayText, '[File: report.pdf]');
1370
1370
  strict_1.default.deepEqual(sentMessages[0]?.content?.contentParts?.map((part) => part.type), ['text', 'file']);
1371
1371
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.mimeType, 'application/pdf');
1372
1372
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.data, undefined);
@@ -1494,7 +1494,7 @@ function findLarkPostMdText(content) {
1494
1494
  });
1495
1495
  strict_1.default.equal(sentMessages.length, 1);
1496
1496
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1497
- strict_1.default.match(sentMessages[0]?.content, /\[User Message\]\nask @张三 to review\n\[\/User Message\]/);
1497
+ strict_1.default.equal(sentMessages[0]?.content, 'ask @张三 to review');
1498
1498
  });
1499
1499
  (0, node_test_1.default)('lark group text messages ignore non-mentioned bot messages by default', async () => {
1500
1500
  const sentMessages = [];
@@ -1620,7 +1620,7 @@ function findLarkPostMdText(content) {
1620
1620
  });
1621
1621
  strict_1.default.equal(sentMessages.length, 1);
1622
1622
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1623
- strict_1.default.match(sentMessages[0]?.content, /\[User Message\]\n1\. hi\n2\. hello\n\[\/User Message\]/);
1623
+ strict_1.default.equal(sentMessages[0]?.content, '1. hi\n2. hello');
1624
1624
  });
1625
1625
  (0, node_test_1.default)('lark inbound messages create a chat binding when an authorized user has an old direct thread', async () => {
1626
1626
  const bindings = [];
@@ -12,7 +12,14 @@ const local_core_acp_response_processor_js_1 = require("../../services/local-ai-
12
12
  const local_core_slash_commands_js_1 = require("../../services/local-ai-core/src/acp/local-core-slash-commands.js");
13
13
  const scheduled_conversation_executor_js_1 = require("../../services/local-ai-core/src/scheduler/scheduled-conversation-executor.js");
14
14
  const scheduler_run_lifecycle_js_1 = require("../../services/local-ai-core/src/scheduler/scheduler-run-lifecycle.js");
15
- const lark_execution_policies_js_1 = require("../../services/local-ai-core/src/scheduler/lark-execution-policies.js");
15
+ const channel_execution_policy_js_1 = require("../../services/local-ai-core/src/scheduler/channel-execution-policy.js");
16
+ const createLarkExecutionPolicy = (job, options, resolveSameThread, preferredAgentType) => (0, channel_execution_policy_js_1.createChannelExecutionPolicy)(job, options, {
17
+ platformBase: 'lark',
18
+ resolveSameThread,
19
+ sideThreadTitle: (nextJob) => `[Scheduled:Lark] ${nextJob.description || nextJob.id}`,
20
+ legacySideThreadTitles: (nextJob) => [`[Scheduled] ${nextJob.description || nextJob.id}`],
21
+ preferredAgentType,
22
+ });
16
23
  const local_schedule_adapter_js_1 = require("../../services/local-ai-core/src/scheduler/local-schedule-adapter.js");
17
24
  const cards_js_1 = require("../../services/local-ai-core/src/channel/lark/cards.js");
18
25
  const session_command_runtime_js_1 = require("../../services/local-ai-core/src/channel/shared/session-command-runtime.js");
@@ -820,7 +827,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
820
827
  let registeredBridge;
821
828
  const bridgeEvents = [];
822
829
  let unregisteredBridge = false;
823
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
830
+ const policy = createLarkExecutionPolicy(job, {
824
831
  store: {},
825
832
  workspaceRouter: {
826
833
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -876,7 +883,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
876
883
  updatedAt: '2026-04-22T06:00:00.000Z',
877
884
  };
878
885
  const createThreadCalls = [];
879
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
886
+ const policy = createLarkExecutionPolicy(job, {
880
887
  store: {},
881
888
  workspaceRouter: {
882
889
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -916,7 +923,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
916
923
  updatedAt: '2026-04-22T06:00:00.000Z',
917
924
  };
918
925
  const createThreadCalls = [];
919
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
926
+ const policy = createLarkExecutionPolicy(job, {
920
927
  store: {},
921
928
  workspaceRouter: {
922
929
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -958,7 +965,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
958
965
  updatedAt: '2026-04-22T06:00:00.000Z',
959
966
  };
960
967
  let created = false;
961
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
968
+ const policy = createLarkExecutionPolicy(job, {
962
969
  store: {},
963
970
  workspaceRouter: {
964
971
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -995,7 +1002,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
995
1002
  updatedAt: '2026-04-22T06:00:00.000Z',
996
1003
  };
997
1004
  const createThreadCalls = [];
998
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
1005
+ const policy = createLarkExecutionPolicy(job, {
999
1006
  store: {},
1000
1007
  workspaceRouter: {
1001
1008
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -1036,7 +1043,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
1036
1043
  updatedAt: '2026-04-22T06:00:00.000Z',
1037
1044
  };
1038
1045
  const createThreadCalls = [];
1039
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
1046
+ const policy = createLarkExecutionPolicy(job, {
1040
1047
  store: {},
1041
1048
  workspaceRouter: {
1042
1049
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -1079,7 +1086,7 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
1079
1086
  updatedAt: '2026-04-22T06:00:00.000Z',
1080
1087
  };
1081
1088
  let registeredBridge;
1082
- const policy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
1089
+ const policy = createLarkExecutionPolicy(job, {
1083
1090
  store: {},
1084
1091
  workspaceRouter: {
1085
1092
  getThreadSessionKey: (threadId) => `session:${threadId}`,
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const runtime_state_js_1 = require("../../services/local-ai-core/src/channel/weixin/runtime-state.js");
9
+ (0, node_test_1.default)('weixin turn state keeps thought, tool progress, and final reply ordered', () => {
10
+ const turn = (0, runtime_state_js_1.createWeixinTurnState)('workspace:thread');
11
+ (0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, { type: 'typing_start', sessionKey: turn.sessionKey });
12
+ (0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, { type: 'update_message', sessionKey: turn.sessionKey, bridgeKind: 'thought', content: 'Inspecting' });
13
+ (0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, { type: 'reply', sessionKey: turn.sessionKey, bridgeKind: 'tool', content: 'Search - running' });
14
+ const finalEvent = { type: 'reply', sessionKey: turn.sessionKey, bridgeKind: 'assistant', content: 'Done' };
15
+ (0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, finalEvent);
16
+ const rendered = (0, runtime_state_js_1.renderWeixinTurnText)(turn);
17
+ strict_1.default.match(rendered, /Inspecting/);
18
+ strict_1.default.match(rendered, /Done/);
19
+ strict_1.default.equal((0, runtime_state_js_1.isTerminalWeixinBridgeMessage)(finalEvent, rendered), true);
20
+ });
21
+ (0, node_test_1.default)('weixin permission events remain terminal and render fallback actions', () => {
22
+ const turn = (0, runtime_state_js_1.createWeixinTurnState)('workspace:thread');
23
+ const event = {
24
+ type: 'buttons',
25
+ sessionKey: turn.sessionKey,
26
+ buttonRows: [[{ text: 'Allow', data: 'allow' }]],
27
+ };
28
+ (0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, event);
29
+ const rendered = (0, runtime_state_js_1.renderWeixinTurnText)(turn);
30
+ strict_1.default.match(rendered, /allow all/);
31
+ strict_1.default.equal((0, runtime_state_js_1.isTerminalWeixinBridgeMessage)(event, rendered), true);
32
+ });
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const text_utils_js_1 = require("../../services/local-ai-core/src/channel/weixin/text-utils.js");
9
+ (0, node_test_1.default)('weixin text utilities preserve utf8 boundaries and decode simple html', () => {
10
+ strict_1.default.equal((0, text_utils_js_1.stripWeixinHtml)('<p>A&amp;B</p>'), 'A&B');
11
+ const chunks = (0, text_utils_js_1.splitTextByUtf8Bytes)('你好世界', 6);
12
+ strict_1.default.deepEqual(chunks, ['你好', '世界']);
13
+ strict_1.default.ok(chunks.every((chunk) => (0, text_utils_js_1.utf8ByteLength)(chunk) <= 6));
14
+ strict_1.default.ok((0, text_utils_js_1.utf8ByteLength)((0, text_utils_js_1.truncateTextByUtf8Bytes)('很长的内容'.repeat(20), 80)) <= 80);
15
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kafca/agentdock",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "license": "PolyForm-Noncommercial-1.0.0",
5
5
  "type": "module",
6
6
  "main": "dist-electron/electron/main.js",
@@ -26,13 +26,14 @@
26
26
  "dev": "node scripts/dev.mjs",
27
27
  "dev:web": "pnpm exec vite --host 127.0.0.1 --port 5173",
28
28
  "dev:core": "pnpm build:electron && node scripts/launch-core.mjs",
29
- "build": "vite build && node scripts/write-electron-package.mjs && tsc -p tsconfig.electron.json",
29
+ "build": "vite build && node scripts/write-electron-package.mjs && tsc -p tsconfig.electron.json && tsc-alias -p tsconfig.electron.json",
30
30
  "build:npm": "VITE_LOCAL_AI_CORE_BASE=/api/local/v1 pnpm build",
31
31
  "build:web": "vite build",
32
32
  "build:renderer": "vite build",
33
- "build:electron": "node scripts/write-electron-package.mjs && tsc -p tsconfig.electron.json",
33
+ "build:electron": "node scripts/write-electron-package.mjs && tsc -p tsconfig.electron.json && tsc-alias -p tsconfig.electron.json",
34
34
  "build:core": "pnpm build:electron",
35
- "test": "pnpm build:renderer && pnpm build:electron && node --test dist-electron/tests/electron/*.test.js dist-electron/tests/contracts/*.test.js dist-electron/tests/integration/*.test.js dist-electron/packages/knowledge-api/test/*.test.js dist-electron/src/pages/Threads/thread-chat-permission.test.js",
35
+ "typecheck": "tsc -p tsconfig.json --noEmit",
36
+ "test": "pnpm typecheck && pnpm build:renderer && pnpm build:electron && node --test dist-electron/tests/electron/*.test.js dist-electron/tests/contracts/*.test.js dist-electron/tests/integration/*.test.js dist-electron/packages/knowledge-api/test/*.test.js dist-electron/src/pages/Threads/thread-chat-permission.test.js",
36
37
  "e2e:smoke": "pnpm build && node scripts/e2e-smoke.mjs",
37
38
  "e2e:compose": "node scripts/e2e-compose.mjs",
38
39
  "dist:mac": "pnpm build && electron-builder --mac dmg zip --arm64",
@@ -45,6 +46,9 @@
45
46
  },
46
47
  "dependencies": {
47
48
  "@agentclientprotocol/claude-agent-acp": "^0.30.0",
49
+ "@cc/core-sdk": "workspace:*",
50
+ "@cc/plugin-sdk": "workspace:*",
51
+ "@cc/superai-contracts": "workspace:*",
48
52
  "@iarna/toml": "^2.2.5",
49
53
  "@larksuiteoapi/node-sdk": "^1.58.0",
50
54
  "@mariozechner/pi-coding-agent": "^0.72.1",
@@ -78,6 +82,7 @@
78
82
  "electron-builder": "^26.8.1",
79
83
  "postcss": "^8.5.3",
80
84
  "tailwindcss": "^3.4.17",
85
+ "tsc-alias": "^1.8.17",
81
86
  "typescript": "~5.8.3",
82
87
  "vite": "^6.3.2"
83
88
  },
@@ -1,2 +0,0 @@
1
- import{u as ze,y as Me,r as n,j as s,C as pe,I as ye,M as U,B as C,g as ee,R as De,T as Ae}from"./index-COxSYBjE.js";import{M as be}from"./Modal-CQiUpfl4.js";import{E as $}from"./EmptyState-DIZLiccW.js";import{C as Le,A as Be}from"./ChatMarkdown-uc36EzyD.js";import{l as Ke}from"./projects-DgjHcNgt.js";import{l as Ue,g as $e,s as qe,a as je,r as Fe,d as Oe}from"./sessions-BQ33Mv4k.js";import{s as We,a as we,b as w,L as _,t as He}from"./session-utils-Dd_WKMYW.js";import{S as Ve}from"./search-B_bqy6Wd.js";import{P as Ge}from"./pencil-Dv5WxhdC.js";import{T as Je}from"./trash-2-Cefngb6Q.js";import{B as Qe}from"./bot-CWQ5-ovl.js";import{U as Xe}from"./user-D_8RSH-T.js";import"./index-CTGTI-cm.js";import"./check-DKM7zfWG.js";const Ye=1500,Ze=9e4;function es(a){return String(a||"").startsWith("web:")}function ss(a){return(a||[]).map((d,y)=>({id:`${d.timestamp||y}-${d.role}-${y}`,role:d.role==="user"?"user":"assistant",content:d.content,timestamp:d.timestamp,kind:d.kind}))}function ts(a){return a.map(d=>`${d.role}:${d.kind||"final"}:${d.timestamp||""}:${d.content}`).join(`
2
- `)}function as(a){return a.filter(d=>d.role!=="user").length}function rs(a,d){var y;return(y=a.last_message)!=null&&y.content?a.last_message.content.replace(/\n/g," "):d("sessions.noMessages")}function ve(a){return(a instanceof Error?a.message:String(a||"")).trim().toLowerCase().includes("no active session found")}function ns(a,d){return(a instanceof Error?a.message:String(a||"")).trim().toLowerCase()?ve(a)?d("sessions.sessionUnavailable"):a instanceof Error?a.message:d("sessions.sendFailed"):d("sessions.sendFailed")}function js(){const{t:a}=ze(),[d,y]=Me(),[se,ke]=n.useState([]),[te,ae]=n.useState({}),[i,G]=n.useState(d.get("project")||""),[b,R]=n.useState([]),[r,h]=n.useState(null),[I,v]=n.useState([]),[J,re]=n.useState(""),[T,Q]=n.useState(""),[Ne,ne]=n.useState(!0),[Se,ie]=n.useState(!1),[Ce,le]=n.useState(!1),[X,oe]=n.useState(!1),[E,P]=n.useState("idle"),[de,k]=n.useState(""),[z,q]=n.useState(null),[F,Y]=n.useState(""),[M,O]=n.useState(null),[ce,W]=n.useState(null),me=n.useRef(null),x=n.useRef({project:"",sessionId:""}),H=n.useRef(null),V=n.useRef(null),j=d.get("project")||"",D=d.get("session")||"",ue=n.useMemo(()=>new Set(Object.values(te)),[te]),N=n.useMemo(()=>{const e=J.trim().toLowerCase();return e?b.filter(t=>We(t,e)):b},[J,b]),A=!!(r&&(r.live||ue.has(r.id))),Ee=!!(i&&A&&T.trim()&&!X&&E!=="sending"&&E!=="activating"&&E!=="polling"),f=n.useCallback((e,t)=>{const l=new URLSearchParams;e&&l.set("project",e),t&&l.set("session",t),y(l,{replace:!0})},[y]),c=n.useCallback((e="idle")=>{H.current&&(window.clearTimeout(H.current),H.current=null),V.current=null,P(e)},[]),xe=n.useCallback(e=>{R(t=>{const l=t.some(o=>o.id===e.id)?t.map(o=>o.id===e.id?{...o,...e}:o):[...t,e];return we(l)})},[]),fe=n.useCallback(async()=>{ne(!0);try{const{projects:e}=await Ke();ke(e||[])}finally{ne(!1)}},[]),u=n.useCallback(async e=>{if(!e)return R([]),ae({}),[];ie(!0);try{const{sessions:t,active_keys:l}=await Ue(e),o=we((t||[]).filter(m=>!es(m.session_key)));return ae(l||{}),x.current.project===e&&R(o),o}finally{ie(!1)}},[]),S=n.useCallback(async(e,t,l)=>{if(!e||!t)return h(null),v([]),null;l!=null&&l.silent||le(!0);try{const o=await $e(e,t,200);return x.current.project!==e||x.current.sessionId!==t||(xe(o),h({id:o.id,project:e,sessionKey:o.session_key,name:o.name||w(o),live:o.live,isDraft:!1,detail:o}),v(ss(o.history)),k("")),o}catch(o){return x.current.project===e&&x.current.sessionId===t&&k(o instanceof Error?o.message:a("sessions.loadFailed")),null}finally{l!=null&&l.silent||le(!1)}},[xe,a]),Z=n.useCallback(e=>{V.current=e,H.current=window.setTimeout(async()=>{const t=V.current;if(!t)return;if(Date.now()-t.startedAt>=Ze){c("timed_out"),k(a("sessions.pollTimeout")),await u(t.project);return}const l=await S(t.project,t.sessionId,{silent:!0});if(!l||!V.current){c(),await u(t.project);return}if(x.current.project!==t.project||x.current.sessionId!==t.sessionId){c();return}const o=ts(l.history),m=as(l.history)>t.assistantCountBefore,g=m&&o===t.lastSignature?t.stableCount+1:0,K={...t,lastSignature:o,stableCount:g};if(m&&g>=2){c(),await u(t.project);return}P("polling"),Z(K)},Ye)},[S,u,c,a]),he=n.useCallback((e,t,l)=>{c(),P("polling"),Z({project:e,sessionId:t,assistantCountBefore:l,stableCount:0,lastSignature:"",startedAt:Date.now()})},[Z,c]),Pe=n.useCallback(async e=>{if(c(),G(e),re(""),k(""),Q(""),!e){x.current={project:"",sessionId:""},h(null),v([]),R([]),f("");return}x.current={project:e,sessionId:""},h(null),v([]),f(e),await u(e)},[u,c,f]),L=n.useCallback(async e=>{if(e.isDraft||e.live||!e.id)return e;P("activating");try{await qe(e.project,{session_key:e.sessionKey,session_id:e.id}),await u(e.project);const t=await S(e.project,e.id,{silent:!0}),l={...e,live:(t==null?void 0:t.live)??!0,isDraft:!1,detail:t??e.detail,name:(t==null?void 0:t.name)||e.name};return h(l),l}finally{P("idle")}},[S,u]),B=n.useCallback(async(e,t)=>{c(),k(""),x.current={project:e,sessionId:t.id},G(e),f(e,t.id),h({id:t.id,project:e,sessionKey:t.session_key,name:w(t),live:t.live,isDraft:!1,detail:null});const l=await S(e,t.id);l&&!l.live&&await L({id:l.id,project:e,sessionKey:l.session_key,name:l.name||w(l),live:l.live,isDraft:!1,detail:l})},[L,S,c,f]),_e=n.useCallback(async()=>{c(),i&&(await u(i),r!=null&&r.id&&(x.current={project:i,sessionId:r.id},await S(i,r.id)))},[r==null?void 0:r.id,S,u,i,c]),ge=n.useCallback(async()=>{if(!T.trim()||!i)return;if(!A){k(a("sessions.sessionUnavailable"));return}const e=T.trim(),t=`${crypto.randomUUID()}-user`,l={id:t,role:"user",content:e,timestamp:new Date().toISOString()};Q(""),oe(!0),P("sending"),k("");try{const o=r||N[0];if(!o)throw new Error(a("sessions.noAvailableChatTarget"));let m=r&&r.id===o.id?r:{id:o.id,project:i,sessionKey:"session_key"in o?o.session_key:o.sessionKey,name:"session_key"in o?w(o):o.name,live:o.live,isDraft:!1,detail:null};(!r||r.id!==m.id)&&(x.current={project:i,sessionId:m.id},f(i,m.id),h(m)),m=await L(m),v(p=>[...p,l]);try{await je(i,{session_key:m.sessionKey,message:e})}catch(p){if(!ve(p)||!m.id)throw p;m=await L({...m,live:!1}),await je(i,{session_key:m.sessionKey,message:e})}const g=m.id;if(!g)throw new Error(a("sessions.createFailed"));const K=I.filter(p=>p.role!=="user").length;x.current={project:i,sessionId:g},h(p=>p&&{...p,id:g,isDraft:!1}),f(i,g),he(i,g,K),await u(i)}catch(o){c(),v(g=>g.filter(K=>K.id!==t));const m=ns(o,a);k(m)}finally{oe(!1)}},[r,A,L,T,N,I,u,i,he,c,a,f]),Re=n.useCallback(async()=>{if(!(!z||!i||!F.trim())){W("rename");try{await Fe(i,z.id,{name:F.trim()});const t=(await u(i)).find(l=>l.id===z.id);t&&(r==null?void 0:r.id)===t.id&&h(l=>l&&{...l,name:w(t)}),q(null),Y("")}finally{W(null)}}},[r==null?void 0:r.id,u,F,z,i]),Ie=n.useCallback(async()=>{if(!(!M||!i)){W("delete");try{await Oe(i,M.id);const e=await u(i);(r==null?void 0:r.id)===M.id&&(c(),x.current={project:i,sessionId:""},h(null),v([]),f(i)),e.length===0&&v([]),O(null)}finally{W(null)}}},[r==null?void 0:r.id,M,u,i,c,f]);n.useEffect(()=>{fe()},[fe]),n.useEffect(()=>{j&&(i&&i===j||G(j))},[j,i]),n.useEffect(()=>{if(!i){R([]);return}x.current.project=i,u(i)},[u,i]),n.useEffect(()=>{!i||D||r!=null&&r.id||N.length===0||B(i,N[0])},[r==null?void 0:r.id,N,B,D,i]),n.useEffect(()=>{if(!j||!D||i!==j)return;const e=b.find(t=>t.id===D);if(!e){b.length>0&&f(j,b[0].id);return}(r==null?void 0:r.id)!==e.id&&B(j,e)},[r==null?void 0:r.id,B,j,D,i,b,f]),n.useEffect(()=>{var e;(e=me.current)==null||e.scrollIntoView({behavior:"smooth"})},[I]),n.useEffect(()=>()=>c(),[c]);const Te=(r==null?void 0:r.name)||a("sessions.activeChat");return s.jsxs("div",{className:"grid min-h-[calc(100vh-8rem)] grid-cols-1 gap-4 xl:grid-cols-[22rem_minmax(0,1fr)] animate-fade-in",children:[s.jsxs(pe,{className:"flex flex-col gap-4 border-gray-200/80 bg-[#fbfbfd] p-4 shadow-none xl:h-[calc(100vh-8rem)] xl:overflow-hidden dark:border-white/[0.08] dark:bg-[#111214]",children:[s.jsx("div",{className:"flex items-center gap-3",children:s.jsxs("div",{className:"flex-1",children:[s.jsx("p",{className:"text-xs font-medium uppercase tracking-[0.2em] text-gray-400",children:a("sessions.selectProject")}),s.jsxs("select",{value:i,onChange:e=>void Pe(e.target.value),className:"mt-2 w-full rounded-[18px] border border-gray-300/90 bg-white px-3 py-2 text-sm text-gray-900 transition-all focus:border-primary/40 focus:outline-none focus:ring-2 focus:ring-primary/10 dark:border-white/[0.1] dark:bg-[rgba(0,0,0,0.45)] dark:text-white",children:[s.jsx("option",{value:"",children:a("sessions.allProjects")}),se.map(e=>s.jsx("option",{value:e.name,children:e.name},e.name))]})]})}),s.jsxs("div",{className:"relative",children:[s.jsx(ye,{value:J,onChange:e=>re(e.target.value),placeholder:a("common.search"),className:"pl-9","aria-label":a("common.search")}),s.jsx(Ve,{size:16,className:"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"})]}),s.jsxs("div",{className:"flex min-h-0 flex-1 flex-col overflow-hidden",children:[s.jsxs("div",{className:"mb-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400",children:[s.jsx("span",{children:a("sessions.historyTitle")}),s.jsx("span",{children:N.length})]}),s.jsx("div",{className:"min-h-0 flex-1 overflow-y-auto space-y-2 pr-1",children:Ne?s.jsx("div",{className:"flex h-32 items-center justify-center text-gray-400",children:s.jsx(_,{className:"animate-spin",size:18})}):i?Se&&b.length===0?s.jsx("div",{className:"flex h-32 items-center justify-center text-gray-400",children:s.jsx(_,{className:"animate-spin",size:18})}):b.length===0?s.jsx($,{message:a("sessions.noSessions"),icon:U}):N.length===0?s.jsx($,{message:a("sessions.noSessions"),icon:U}):N.map(e=>{const t=e.id===(r==null?void 0:r.id);return s.jsxs("div",{className:ee("rounded-[18px] border p-3 transition-all",t?"border-primary/25 bg-primary/5":"border-gray-200/80 bg-white hover:border-gray-300 dark:border-white/[0.08] dark:bg-white/[0.03]"),children:[s.jsxs("button",{type:"button",onClick:()=>void B(i,e),className:"w-full text-left","data-testid":"web-chat-session-open",children:[s.jsxs("div",{className:"flex items-start justify-between gap-2",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:"truncate text-sm font-medium text-gray-900 dark:text-white",children:w(e)}),e.live||ue.has(e.id)?s.jsx("span",{className:"rounded-full bg-primary/10 px-2 py-0.5 text-[10px] text-primary",children:"live"}):s.jsx("span",{className:"rounded-full bg-gray-100 px-2 py-0.5 text-[10px] text-gray-400 dark:bg-white/[0.06] dark:text-gray-500",children:"history"})]}),s.jsx("p",{className:"mt-1 truncate text-xs text-gray-500 dark:text-gray-400",children:He(e.updated_at||e.created_at,a("sessions.justNow"))})]}),s.jsx("span",{className:"shrink-0 text-[10px] text-gray-400",children:e.history_count})]}),s.jsx("p",{className:"mt-2 line-clamp-2 text-xs text-gray-500 dark:text-gray-400",children:rs(e,a)})]}),s.jsxs("div",{className:"mt-3 flex items-center justify-end gap-1",children:[s.jsx(C,{size:"sm",variant:"ghost",onClick:()=>{q({id:e.id,name:w(e)}),Y(w(e))},"data-testid":"web-chat-session-rename",children:s.jsx(Ge,{size:14})}),s.jsx(C,{size:"sm",variant:"ghost",onClick:()=>O({id:e.id,name:w(e)}),"data-testid":"web-chat-session-delete",children:s.jsx(Je,{size:14})})]})]},e.id)}):s.jsx($,{message:a("sessions.projectRequired"),icon:U})})]})]}),s.jsxs(pe,{className:"flex min-h-[32rem] flex-col border-gray-200/80 bg-white p-0 shadow-none xl:h-[calc(100vh-8rem)] dark:border-white/[0.08] dark:bg-[#0b0d10]",children:[s.jsxs("div",{className:"flex items-center justify-between gap-3 border-b border-gray-200/80 px-5 py-4 dark:border-white/[0.08]",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsx("div",{className:"flex items-center gap-2",children:s.jsx("h2",{className:"truncate text-lg font-semibold text-gray-900 dark:text-white",children:Te})}),s.jsx("p",{className:"mt-1 truncate text-sm text-gray-500 dark:text-gray-400",children:r?r.project:a("sessions.sendHint")})]}),s.jsxs(C,{size:"sm",variant:"secondary",onClick:()=>void _e(),"data-testid":"web-chat-refresh",children:[s.jsx(De,{size:14})," ",a("common.refresh")]})]}),s.jsx("div",{className:"flex-1 overflow-y-auto px-5 py-5",children:i?Ce?s.jsx("div",{className:"flex h-full items-center justify-center text-gray-400",children:s.jsx(_,{className:"animate-spin",size:22})}):I.length===0?s.jsx("div",{className:"flex h-full items-center justify-center",children:s.jsx($,{message:a(r?"sessions.noMessages":"sessions.sendHint"),icon:U})}):s.jsxs("div",{className:"mx-auto max-w-4xl space-y-5",children:[I.map(e=>{const t=e.role==="user";return s.jsxs("div",{className:ee("flex gap-3",t?"justify-end":"justify-start"),children:[!t&&s.jsx("div",{className:"mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#f5f5f7] dark:bg-white/[0.06]",children:s.jsx(Qe,{size:16,className:"text-primary"})}),s.jsx("div",{className:ee("max-w-[78%] rounded-[20px] px-5 py-3.5 text-sm",t?"chat-user-glass rounded-br-sm text-slate-950 dark:text-slate-50":"rounded-bl-sm border border-gray-200 bg-[#fbfbfd] text-gray-900 dark:border-white/[0.08] dark:bg-[rgba(255,255,255,0.04)] dark:text-gray-100"),children:s.jsx(Le,{content:e.content,isUser:t})}),t&&s.jsx("div",{className:"mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#f0f0f0] dark:bg-white/[0.08]",children:s.jsx(Xe,{size:16,className:"text-gray-500 dark:text-gray-300"})})]},e.id)}),s.jsx("div",{ref:me})]}):s.jsx("div",{className:"flex h-full items-center justify-center",children:s.jsx($,{message:a("sessions.emptySelection"),icon:U})})}),s.jsxs("div",{className:"border-t border-gray-200/80 px-5 py-4 dark:border-white/[0.08]",children:[de&&s.jsx("div",{className:"mb-3 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600 dark:border-red-900/40 dark:bg-red-950/20 dark:text-red-300",children:de}),E==="polling"&&s.jsxs("div",{className:"mb-3 flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400",children:[s.jsx(_,{size:14,className:"animate-spin"}),s.jsx("span",{children:a("sessions.waitingForReply")})]}),E==="activating"&&s.jsxs("div",{className:"mb-3 flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400",children:[s.jsx(_,{size:14,className:"animate-spin"}),s.jsx("span",{children:a("sessions.preparingChat")})]}),s.jsx("div",{className:"mx-auto max-w-4xl",children:s.jsxs("div",{className:"relative",children:[s.jsx(Ae,{value:T,onChange:e=>Q(e.target.value),onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),ge())},rows:3,placeholder:a(i?"sessions.messageInput":"sessions.projectRequired"),disabled:!i||!A||X||E==="polling"||E==="activating",className:"min-h-[104px] rounded-[22px] border-gray-200 bg-[#fbfbfd] px-4 pb-16 pt-3 text-[15px] leading-6 dark:border-white/[0.08] dark:bg-[rgba(255,255,255,0.04)] sm:min-h-[112px] sm:px-5 sm:pt-4","data-testid":"web-chat-input"}),s.jsx(C,{onClick:()=>void ge(),disabled:!Ee,size:"icon","aria-label":a("sessions.send"),className:"absolute bottom-3 right-3 h-11 w-11 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-12 sm:w-12","data-testid":"web-chat-send",children:X?s.jsx(_,{size:18,className:"animate-spin"}):s.jsx(Be,{size:22,strokeWidth:2.2})})]})}),!A&&r&&s.jsx("div",{className:"mt-3 rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-500 dark:border-white/[0.08] dark:bg-white/[0.03] dark:text-gray-400",children:a("sessions.notLiveHint")})]})]}),s.jsx(be,{open:!!z,onClose:()=>q(null),title:a("sessions.renameTitle"),children:s.jsxs("div",{className:"space-y-4",children:[s.jsx(ye,{value:F,onChange:e=>Y(e.target.value),placeholder:a("sessions.renamePlaceholder"),"data-testid":"web-chat-rename-input"}),s.jsxs("div",{className:"flex justify-end gap-2",children:[s.jsx(C,{variant:"secondary",onClick:()=>q(null),children:a("common.cancel")}),s.jsx(C,{onClick:()=>void Re(),loading:ce==="rename","data-testid":"web-chat-rename-save",children:a("common.save")})]})]})}),s.jsx(be,{open:!!M,onClose:()=>O(null),title:a("sessions.deleteConfirmTitle"),children:s.jsxs("div",{className:"space-y-4",children:[s.jsx("p",{className:"text-sm text-gray-500 dark:text-gray-400",children:a("sessions.deleteConfirmBody")}),s.jsxs("div",{className:"flex justify-end gap-2",children:[s.jsx(C,{variant:"secondary",onClick:()=>O(null),children:a("common.cancel")}),s.jsx(C,{variant:"danger",onClick:()=>void Ie(),loading:ce==="delete","data-testid":"web-chat-delete-confirm",children:a("common.delete")})]})]})})]})}export{js as default};