@kafca/agentdock 0.1.55 → 0.1.57

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 (185) hide show
  1. package/README.md +14 -0
  2. package/dist/renderer/assets/{Badge-Bg_nNvBL.js → Badge-CDTbmw1h.js} +1 -1
  3. package/dist/renderer/assets/Card-DayUo35w.js +1 -0
  4. package/dist/renderer/assets/Config-ByGMQuzR.js +21 -0
  5. package/dist/renderer/assets/CronList-XqHdVlgu.js +1 -0
  6. package/dist/renderer/assets/Dashboard-CZj-oANw.js +41 -0
  7. package/dist/renderer/assets/{EmptyState-C60g3RMj.js → EmptyState-EFMkOjZO.js} +1 -1
  8. package/dist/renderer/assets/HighlightedMarkdown-DyvF3DHK.js +12 -0
  9. package/dist/renderer/assets/Input-DxoWVc9d.js +1 -0
  10. package/dist/renderer/assets/{KnowledgeDetail-CC5C7JYe.js → KnowledgeDetail-CbUb2tG7.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-3rlvxgWf.js → KnowledgeHome-BWV1gz4f.js} +3 -3
  12. package/dist/renderer/assets/Logs-DqnanQTc.js +6 -0
  13. package/dist/renderer/assets/{Modal-BQUW9LSf.js → Modal-yY7TzR0x.js} +3 -3
  14. package/dist/renderer/assets/MonitorList-CIjGQkFs.js +1 -0
  15. package/dist/renderer/assets/Page-vwHNUdoj.js +1 -0
  16. package/dist/renderer/assets/{Select-CfRkos3z.js → Select-CmbK36pN.js} +2 -2
  17. package/dist/renderer/assets/ThreadChat-4OceMgM4.js +86 -0
  18. package/dist/renderer/assets/Workspace-DplPJOjJ.js +19 -0
  19. package/dist/renderer/assets/{arrow-left-D1DbXrLA.js → arrow-left-C8HfBt2K.js} +1 -1
  20. package/dist/renderer/assets/{book-open-BxC1z1C1.js → book-open-BCdZWncQ.js} +1 -1
  21. package/dist/renderer/assets/channels-hgO3cAIx.js +6 -0
  22. package/dist/renderer/assets/chevron-down-BPk77tqY.js +11 -0
  23. package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
  24. package/dist/renderer/assets/index-BlA4xPdd.js +41 -0
  25. package/dist/renderer/assets/index-wYpjqVqC.js +167 -0
  26. package/dist/renderer/assets/knowledge-7pl06KuH.js +1 -0
  27. package/dist/renderer/assets/{pencil-X44UaxwR.js → pencil-CB1SWZ9A.js} +1 -1
  28. package/dist/renderer/assets/{play-ujo-De38.js → play-BvsoXUrM.js} +1 -1
  29. package/dist/renderer/assets/{plus-BHgVdG_A.js → plus-CokBDnaL.js} +1 -1
  30. package/dist/renderer/assets/{save-Cz6giKrE.js → save-D-w4kATO.js} +1 -1
  31. package/dist/renderer/assets/{search-DKWvTqi5.js → search-Cnsn6i4Q.js} +1 -1
  32. package/dist/renderer/assets/{shield-check-V82O8AC1.js → shield-check-Cjja2Gh6.js} +1 -1
  33. package/dist/renderer/assets/threads-BCm6j4eI.js +1 -0
  34. package/dist/renderer/assets/{trash-2-_Fgwk8mc.js → trash-2-CIxz9qYY.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 +6 -9
  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/channel/lark/cards.js +33 -18
  80. package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -2
  81. package/dist-electron/services/local-ai-core/src/channel/lark/gateway-utils.js +91 -0
  82. package/dist-electron/services/local-ai-core/src/channel/lark/inbound-handler.js +180 -0
  83. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +52 -456
  84. package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +124 -0
  85. package/dist-electron/services/local-ai-core/src/channel/shared/inbound-attachment-store.js +93 -0
  86. package/dist-electron/services/local-ai-core/src/channel/shared/inbound-authorization.js +54 -0
  87. package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +2 -2
  88. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-media.js +21 -0
  89. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +167 -0
  90. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +98 -483
  91. package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +156 -0
  92. package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +100 -0
  93. package/dist-electron/services/local-ai-core/src/cli/lac.js +6 -6
  94. package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +2 -2
  95. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +28 -70
  96. package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +33 -5
  97. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +2 -2
  98. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +2 -2
  99. package/dist-electron/services/local-ai-core/src/router/workspace-bridge-event-stream.js +29 -0
  100. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +5 -5
  101. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +47 -60
  102. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -5
  103. package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +6 -6
  104. package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +3 -3
  105. package/dist-electron/services/local-ai-core/src/runtime/external-service.js +3 -1
  106. package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +11 -2
  107. package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +12 -6
  108. package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +10 -2
  109. package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +18 -6
  110. package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +7 -2
  111. package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +28 -3
  112. package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +10 -2
  113. package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +18 -5
  114. package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +14 -2
  115. package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +18 -15
  116. package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +13 -2
  117. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +35 -8
  118. package/dist-electron/services/local-ai-core/src/runtime/request-validation.js +79 -0
  119. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +9 -9
  120. package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +10 -1
  121. package/dist-electron/services/local-ai-core/src/runtime/server.js +2 -1
  122. package/dist-electron/services/local-ai-core/src/runtime/workspace-project-registry.js +76 -0
  123. package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
  124. package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +75 -0
  125. package/dist-electron/shared/desktop.js +1 -64
  126. package/dist-electron/src/components/chat/chat-controller-state.js +60 -0
  127. package/dist-electron/src/components/chat/chat-event-gate.js +146 -0
  128. package/dist-electron/src/components/chat/markdown-code-block.js +8 -0
  129. package/dist-electron/src/pages/Desktop/workspace-model.js +212 -0
  130. package/dist-electron/src/pages/Threads/thread-chat-model.js +50 -51
  131. package/dist-electron/tests/contracts/agent-message-policy.test.js +21 -0
  132. package/dist-electron/tests/contracts/architecture-docs.test.js +56 -33
  133. package/dist-electron/tests/contracts/chat-event-gate.test.js +114 -0
  134. package/dist-electron/tests/contracts/chat-markdown.test.js +16 -0
  135. package/dist-electron/tests/contracts/thread-task-state.test.js +32 -0
  136. package/dist-electron/tests/contracts/workspace-identity.test.js +23 -0
  137. package/dist-electron/tests/electron/core-client.test.js +284 -0
  138. package/dist-electron/tests/electron/plugin-kernel.test.js +54 -0
  139. package/dist-electron/tests/electron/renderer-packaging.test.js +9 -0
  140. package/dist-electron/tests/electron/workspace-task-store.test.js +31 -0
  141. package/dist-electron/tests/integration/channel-inbound-attachment-store.test.js +117 -0
  142. package/dist-electron/tests/integration/channel-inbound-authorization.test.js +80 -0
  143. package/dist-electron/tests/integration/lark-gateway-utils.test.js +13 -0
  144. package/dist-electron/tests/integration/lark-gateway.test.js +58 -4
  145. package/dist-electron/tests/integration/local-core-errors.test.js +41 -0
  146. package/dist-electron/tests/integration/weixin-gateway.test.js +36 -0
  147. package/dist-electron/tests/integration/weixin-runtime-state.test.js +32 -0
  148. package/dist-electron/tests/integration/weixin-text-utils.test.js +15 -0
  149. package/package.json +9 -4
  150. package/dist/renderer/assets/Chat-BJveQQKu.js +0 -2
  151. package/dist/renderer/assets/ChatMarkdown-Bqn2HJ6f.js +0 -45
  152. package/dist/renderer/assets/Config-CMtZDRAE.js +0 -21
  153. package/dist/renderer/assets/CronList-C-ILB_Ae.js +0 -1
  154. package/dist/renderer/assets/Dashboard-CsKBSQIe.js +0 -26
  155. package/dist/renderer/assets/Logs-CktemGN0.js +0 -1
  156. package/dist/renderer/assets/MonitorList-B0hxjuOZ.js +0 -1
  157. package/dist/renderer/assets/Page-DL_Nv2i5.js +0 -1
  158. package/dist/renderer/assets/ProjectDetail-Djr9-MyT.js +0 -11
  159. package/dist/renderer/assets/ProjectList-BKl4eW7q.js +0 -1
  160. package/dist/renderer/assets/SessionChat-Dxv8D5DW.js +0 -1
  161. package/dist/renderer/assets/SessionList-BJ2GjXvR.js +0 -1
  162. package/dist/renderer/assets/ThreadChat-CAsIl9qf.js +0 -28
  163. package/dist/renderer/assets/Workspace-CaUhiGBX.js +0 -19
  164. package/dist/renderer/assets/bot-Fw3IzhJA.js +0 -6
  165. package/dist/renderer/assets/check-5zCTgp3A.js +0 -6
  166. package/dist/renderer/assets/chevron-down-N3PxTNxH.js +0 -6
  167. package/dist/renderer/assets/circle-C_chUflo.js +0 -6
  168. package/dist/renderer/assets/desktop-DkQTZNhe.js +0 -1
  169. package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
  170. package/dist/renderer/assets/funnel-B8ZAv6I7.js +0 -6
  171. package/dist/renderer/assets/heart-M9A3E4Ed.js +0 -6
  172. package/dist/renderer/assets/index-Bwv855ID.js +0 -41
  173. package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
  174. package/dist/renderer/assets/index-DJ8w4e0l.js +0 -182
  175. package/dist/renderer/assets/projects-DB1FLZ3A.js +0 -1
  176. package/dist/renderer/assets/server-BsuHcYl4.js +0 -11
  177. package/dist/renderer/assets/session-utils-DnAWL3eo.js +0 -6
  178. package/dist/renderer/assets/sessions-DptxO_BE.js +0 -1
  179. package/dist/renderer/assets/user-DQKTaUcR.js +0 -6
  180. package/dist/renderer/assets/wifi-off-vjH-MD2w.js +0 -11
  181. package/dist-electron/src/api/client.js +0 -73
  182. package/dist-electron/src/api/sessions.js +0 -21
  183. package/dist-electron/src/lib/session-utils.js +0 -51
  184. package/dist-electron/src/pages/Projects/project-provider-config.js +0 -75
  185. package/dist-electron/tests/electron/project-provider-config.test.js +0 -71
@@ -9,6 +9,7 @@ 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
11
  const node_stream_1 = require("node:stream");
12
+ const node_url_1 = require("node:url");
12
13
  const local_core_lark_gateway_js_1 = require("../../services/local-ai-core/src/channel/lark/local-core-lark-gateway.js");
13
14
  const runtime_state_js_1 = require("../../services/local-ai-core/src/channel/lark/runtime-state.js");
14
15
  function extractLarkCreatedMessage(request) {
@@ -1174,6 +1175,7 @@ function findLarkPostMdText(content) {
1174
1175
  });
1175
1176
  (0, node_test_1.default)('lark image messages are downloaded and forwarded as generic channel image parts', async () => {
1176
1177
  const sentMessages = [];
1178
+ const tempDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'lark-image-receive-'));
1177
1179
  const pngBytes = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
1178
1180
  const client = {
1179
1181
  im: {
@@ -1230,6 +1232,7 @@ function findLarkPostMdText(content) {
1230
1232
  }],
1231
1233
  }),
1232
1234
  getWorkspaceRouter: () => ({
1235
+ getWorkspaceRegistryEntry: async () => ({ path: tempDir }),
1233
1236
  getThreadSessionKey: (threadId) => `session:${threadId}`,
1234
1237
  sendThreadMessage: async (threadId, content) => {
1235
1238
  sentMessages.push({ threadId, content });
@@ -1262,10 +1265,14 @@ function findLarkPostMdText(content) {
1262
1265
  });
1263
1266
  strict_1.default.equal(sentMessages.length, 1);
1264
1267
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1265
- 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]');
1266
1269
  strict_1.default.deepEqual(sentMessages[0]?.content?.contentParts?.map((part) => part.type), ['text', 'image']);
1267
1270
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.mimeType, 'image/png');
1268
1271
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.data, pngBytes.toString('base64'));
1272
+ const imagePath = (0, node_path_1.join)(tempDir, '.agentdock', 'channel-uploads', 'lark', 'default', 'msg-image-1-img-key-1.png');
1273
+ strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.uri, (0, node_url_1.pathToFileURL)(imagePath).href);
1274
+ strict_1.default.deepEqual((0, node_fs_1.readFileSync)(imagePath), pngBytes);
1275
+ (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
1269
1276
  });
1270
1277
  (0, node_test_1.default)('lark file messages are downloaded and forwarded as generic channel file parts', async () => {
1271
1278
  const sentMessages = [];
@@ -1359,7 +1366,7 @@ function findLarkPostMdText(content) {
1359
1366
  });
1360
1367
  strict_1.default.equal(sentMessages.length, 1);
1361
1368
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1362
- 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]');
1363
1370
  strict_1.default.deepEqual(sentMessages[0]?.content?.contentParts?.map((part) => part.type), ['text', 'file']);
1364
1371
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.mimeType, 'application/pdf');
1365
1372
  strict_1.default.equal(sentMessages[0]?.content?.contentParts?.[1]?.data, undefined);
@@ -1368,6 +1375,53 @@ function findLarkPostMdText(content) {
1368
1375
  strict_1.default.deepEqual((0, node_fs_1.readFileSync)(sentMessages[0]?.content?.contentParts?.[1]?.path), fileBytes);
1369
1376
  (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
1370
1377
  });
1378
+ (0, node_test_1.default)('lark file messages are not downloaded before the sender is authorized', async () => {
1379
+ let resourceDownloads = 0;
1380
+ let inboundMessage;
1381
+ const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
1382
+ store: {
1383
+ getAuthorizedUser: () => undefined,
1384
+ },
1385
+ readConfig: async () => null,
1386
+ getWorkspaceRouter: () => ({}),
1387
+ eventBus: { emit: () => { }, on: () => () => { } },
1388
+ });
1389
+ const internals = gateway;
1390
+ internals.runtime.set('default', {
1391
+ workspaceId: 'default',
1392
+ enabled: true,
1393
+ status: 'running',
1394
+ connected: true,
1395
+ autoApprove: false,
1396
+ client: {
1397
+ im: {
1398
+ messageResource: {
1399
+ get: async () => {
1400
+ resourceDownloads += 1;
1401
+ throw new Error('should not download');
1402
+ },
1403
+ },
1404
+ },
1405
+ },
1406
+ });
1407
+ internals.handleInboundMessage = async (message) => {
1408
+ inboundMessage = message;
1409
+ };
1410
+ await internals.handleMessageEvent('default', {
1411
+ event: {
1412
+ sender: { sender_id: { user_id: 'user-1' } },
1413
+ message: {
1414
+ message_id: 'msg-file-unauthorized',
1415
+ message_type: 'file',
1416
+ chat_id: 'chat-1',
1417
+ content: JSON.stringify({ file_key: 'file-key-1', file_name: 'private.pdf' }),
1418
+ },
1419
+ },
1420
+ });
1421
+ strict_1.default.equal(resourceDownloads, 0);
1422
+ strict_1.default.equal(inboundMessage.text, '[File: private.pdf]');
1423
+ strict_1.default.deepEqual(inboundMessage.contentParts, [{ type: 'text', text: '[File: private.pdf]' }]);
1424
+ });
1371
1425
  (0, node_test_1.default)('lark group text messages strip the bot mention before dispatching', async () => {
1372
1426
  const sentMessages = [];
1373
1427
  const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
@@ -1440,7 +1494,7 @@ function findLarkPostMdText(content) {
1440
1494
  });
1441
1495
  strict_1.default.equal(sentMessages.length, 1);
1442
1496
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1443
- 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');
1444
1498
  });
1445
1499
  (0, node_test_1.default)('lark group text messages ignore non-mentioned bot messages by default', async () => {
1446
1500
  const sentMessages = [];
@@ -1566,7 +1620,7 @@ function findLarkPostMdText(content) {
1566
1620
  });
1567
1621
  strict_1.default.equal(sentMessages.length, 1);
1568
1622
  strict_1.default.equal(sentMessages[0]?.threadId, 'thread-1');
1569
- 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');
1570
1624
  });
1571
1625
  (0, node_test_1.default)('lark inbound messages create a chat binding when an authorized user has an old direct thread', async () => {
1572
1626
  const bindings = [];
@@ -0,0 +1,41 @@
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 node_test_1 = __importDefault(require("node:test"));
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const local_core_errors_js_1 = require("../../services/local-ai-core/src/kernel/local-core-errors.js");
9
+ (0, node_test_1.default)('LocalCoreError coerces non-string message into a string', () => {
10
+ const err = new local_core_errors_js_1.LocalCoreError('internal_error', { weird: 'shape' });
11
+ strict_1.default.equal(typeof err.info.message, 'string');
12
+ strict_1.default.equal(err.message, err.info.message);
13
+ strict_1.default.ok(err.info.message.includes('"weird"'));
14
+ const line = (0, local_core_errors_js_1.formatLogError)(err.info);
15
+ strict_1.default.ok(!line.includes('[object Object]'), `log line leaked [object Object]: ${line}`);
16
+ });
17
+ (0, node_test_1.default)('toLocalCoreErrorInfo stringifies Error whose message is an object', () => {
18
+ const raw = new Error('');
19
+ raw.message = { nested: 'value' };
20
+ const info = (0, local_core_errors_js_1.toLocalCoreErrorInfo)(raw);
21
+ strict_1.default.equal(typeof info.message, 'string');
22
+ const line = (0, local_core_errors_js_1.formatLogError)(info);
23
+ strict_1.default.ok(!line.includes('[object Object]'), `log line leaked [object Object]: ${line}`);
24
+ });
25
+ (0, node_test_1.default)('toLocalCoreErrorInfo preserves runtime_protocol_timeout classification for timeout string', () => {
26
+ const info = (0, local_core_errors_js_1.toLocalCoreErrorInfo)(new Error('Timed out waiting for ACP session/prompt after 900000ms'));
27
+ strict_1.default.equal(info.code, 'runtime_protocol_timeout');
28
+ strict_1.default.equal(typeof info.message, 'string');
29
+ });
30
+ (0, node_test_1.default)('formatLogError renders cause suffix when present', () => {
31
+ const err = new local_core_errors_js_1.LocalCoreError('runtime_exited', 'child crashed', { cause: 'SIGTERM' });
32
+ const line = (0, local_core_errors_js_1.formatLogError)(err.info);
33
+ strict_1.default.equal(line, 'runtime_exited: child crashed cause=SIGTERM');
34
+ });
35
+ (0, node_test_1.default)('toLocalCoreErrorInfo carries details through LocalCoreError instances', () => {
36
+ const base = new local_core_errors_js_1.LocalCoreError('runtime_protocol_timeout', 'Timed out waiting for ACP session/prompt after 100ms');
37
+ const info = (0, local_core_errors_js_1.toLocalCoreErrorInfo)(base, 'internal_error', { threadId: 'thread-x', runtimeId: 'hermes' });
38
+ strict_1.default.equal(info.code, 'runtime_protocol_timeout');
39
+ strict_1.default.equal(info.details?.threadId, 'thread-x');
40
+ strict_1.default.equal(info.details?.runtimeId, 'hermes');
41
+ });
@@ -5,10 +5,44 @@ 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_crypto_1 = __importDefault(require("node:crypto"));
8
9
  const node_fs_1 = require("node:fs");
9
10
  const node_os_1 = require("node:os");
10
11
  const node_path_1 = require("node:path");
12
+ const node_url_1 = require("node:url");
11
13
  const local_core_weixin_gateway_js_1 = require("../../services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js");
14
+ (0, node_test_1.default)('weixin inbound attachment source streams AES-decrypted files into the shared store', async () => {
15
+ const tempDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'weixin-file-receive-'));
16
+ const originalFetch = globalThis.fetch;
17
+ try {
18
+ const plaintext = Buffer.from('%PDF streamed file');
19
+ const aesKey = node_crypto_1.default.randomBytes(16);
20
+ const cipher = node_crypto_1.default.createCipheriv('aes-128-ecb', aesKey, null);
21
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
22
+ globalThis.fetch = (async () => new Response(encrypted));
23
+ const gateway = new local_core_weixin_gateway_js_1.LocalCoreWeixinGateway({
24
+ store: {},
25
+ readConfig: async () => null,
26
+ getWorkspaceRouter: () => ({}),
27
+ eventBus: { emit: () => { }, on: () => () => { } },
28
+ });
29
+ const downloaded = await gateway.downloadMediaItem({
30
+ type: 4,
31
+ file_item: {
32
+ file_name: 'report.pdf',
33
+ aeskey: aesKey.toString('hex'),
34
+ media: { encrypt_query_param: 'download-token' },
35
+ },
36
+ }, 'msg-1', 0, tempDir, { cdnBaseUrl: 'https://weixin.example' });
37
+ strict_1.default.equal(downloaded.kind, 'file');
38
+ strict_1.default.deepEqual((0, node_fs_1.readFileSync)(downloaded.path), plaintext);
39
+ strict_1.default.equal(downloaded.data, undefined);
40
+ }
41
+ finally {
42
+ globalThis.fetch = originalFetch;
43
+ (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
44
+ }
45
+ });
12
46
  (0, node_test_1.default)('weixin channel can encrypt, upload, and send a local file', async () => {
13
47
  const tempDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'weixin-file-send-'));
14
48
  const originalFetch = globalThis.fetch;
@@ -388,6 +422,7 @@ const local_core_weixin_gateway_js_1 = require("../../services/local-ai-core/src
388
422
  (0, node_test_1.default)('weixin downloaded image attachment keeps image data content part', () => {
389
423
  const part = (0, local_core_weixin_gateway_js_1.createWeixinAttachmentContentPart)({
390
424
  path: '/tmp/image.png',
425
+ uri: (0, node_url_1.pathToFileURL)('/tmp/image.png').href,
391
426
  kind: 'image',
392
427
  name: 'image.png',
393
428
  data: 'aW1n',
@@ -396,6 +431,7 @@ const local_core_weixin_gateway_js_1 = require("../../services/local-ai-core/src
396
431
  strict_1.default.deepEqual(part, {
397
432
  type: 'image',
398
433
  data: 'aW1n',
434
+ uri: (0, node_url_1.pathToFileURL)('/tmp/image.png').href,
399
435
  mimeType: 'image/png',
400
436
  fileName: 'image.png',
401
437
  });
@@ -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.55",
3
+ "version": "0.1.57",
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-DJ8w4e0l.js";import{M as be}from"./Modal-BQUW9LSf.js";import{E as $}from"./EmptyState-C60g3RMj.js";import{C as Le,A as Be}from"./ChatMarkdown-Bqn2HJ6f.js";import{l as Ke}from"./projects-DB1FLZ3A.js";import{l as Ue,g as $e,s as qe,a as je,r as Fe,d as Oe}from"./sessions-DptxO_BE.js";import{s as We,a as we,b as w,L as _,t as He}from"./session-utils-DnAWL3eo.js";import{S as Ve}from"./search-DKWvTqi5.js";import{P as Ge}from"./pencil-X44UaxwR.js";import{T as Je}from"./trash-2-_Fgwk8mc.js";import{B as Qe}from"./bot-Fw3IzhJA.js";import{U as Xe}from"./user-DQKTaUcR.js";import"./index-Bwv855ID.js";import"./check-5zCTgp3A.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};