@lobehub/lobehub 2.0.0-next.311 → 2.0.0-next.313

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 (151) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/apps/desktop/src/main/controllers/AuthCtr.ts +75 -7
  3. package/changelog/v1.json +21 -0
  4. package/docs/usage/providers/internlm.mdx +2 -2
  5. package/docs/usage/providers/internlm.zh-CN.mdx +3 -3
  6. package/e2e/README.md +1 -1
  7. package/e2e/src/features/community/detail-pages.feature +2 -2
  8. package/e2e/src/features/community/interactions.feature +5 -5
  9. package/e2e/src/features/community/smoke.feature +1 -1
  10. package/e2e/src/steps/community/detail-pages.steps.ts +6 -4
  11. package/e2e/src/steps/community/interactions.steps.ts +3 -3
  12. package/locales/en-US/error.json +10 -1
  13. package/locales/en-US/subscription.json +1 -1
  14. package/locales/zh-CN/desktop-onboarding.json +5 -0
  15. package/locales/zh-CN/error.json +10 -1
  16. package/locales/zh-CN/subscription.json +1 -1
  17. package/package.json +1 -1
  18. package/packages/agent-runtime/src/agents/GeneralChatAgent.ts +14 -2
  19. package/packages/agent-runtime/src/agents/__tests__/GeneralChatAgent.test.ts +275 -1
  20. package/packages/builtin-tool-agent-builder/src/systemRole.ts +9 -0
  21. package/packages/builtin-tool-cloud-sandbox/package.json +1 -0
  22. package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +105 -134
  23. package/packages/builtin-tool-cloud-sandbox/src/executor/index.ts +254 -0
  24. package/packages/builtin-tool-cloud-sandbox/src/index.ts +1 -0
  25. package/packages/builtin-tool-cloud-sandbox/src/types/api.ts +22 -0
  26. package/packages/builtin-tool-cloud-sandbox/src/types/index.ts +4 -0
  27. package/packages/builtin-tool-cloud-sandbox/src/types/params.ts +85 -0
  28. package/packages/builtin-tool-cloud-sandbox/src/types/service.ts +48 -0
  29. package/packages/builtin-tool-cloud-sandbox/src/{types.ts → types/state.ts} +0 -23
  30. package/packages/builtin-tool-memory/src/manifest.ts +5 -5
  31. package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +1 -1
  32. package/packages/editor-runtime/src/__tests__/EditorRuntime.test.ts +1 -1
  33. package/packages/electron-client-ipc/src/events/index.ts +5 -1
  34. package/packages/electron-client-ipc/src/events/remoteServer.ts +23 -0
  35. package/packages/memory-user-memory/src/schemas/index.ts +0 -1
  36. package/packages/model-bank/src/modelProviders/internlm.ts +1 -1
  37. package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +5 -15
  38. package/packages/model-runtime/src/providers/internlm/index.test.ts +15 -15
  39. package/packages/model-runtime/src/providers/internlm/index.ts +1 -1
  40. package/packages/types/src/tool/intervention.ts +4 -2
  41. package/packages/types/src/user/preference.ts +1 -0
  42. package/public/favicon-32x-32-error.ico +0 -0
  43. package/public/favicon-32x32-done-dev.ico +0 -0
  44. package/public/favicon-32x32-done.ico +0 -0
  45. package/public/favicon-32x32-error-dev.ico +0 -0
  46. package/public/favicon-32x32-progress-dev.ico +0 -0
  47. package/public/favicon-32x32-progress.ico +0 -0
  48. package/public/favicon-done-dev.ico +0 -0
  49. package/public/favicon-done.ico +0 -0
  50. package/public/favicon-error-dev.ico +0 -0
  51. package/public/favicon-error.ico +0 -0
  52. package/public/favicon-progress-dev.ico +0 -0
  53. package/public/favicon-progress.ico +0 -0
  54. package/src/app/[variants]/(desktop)/desktop-onboarding/features/LoginStep.tsx +84 -26
  55. package/src/app/[variants]/(main)/_layout/DesktopAutoOidcOnFirstOpen.tsx +4 -0
  56. package/src/app/[variants]/(main)/agent/profile/features/Header/AgentPublishButton/PublishResultModal.tsx +1 -1
  57. package/src/app/[variants]/(main)/community/(detail)/_layout/Header.tsx +15 -3
  58. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Overview/TagList.tsx +1 -1
  59. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Related/index.tsx +2 -2
  60. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/SystemRole/TagList.tsx +1 -1
  61. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/SystemRole/index.tsx +1 -1
  62. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Header.tsx +2 -2
  63. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/ActionButton/AddAgent.tsx +1 -1
  64. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/ActionButton/index.tsx +1 -1
  65. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/Related/index.tsx +2 -2
  66. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/StatusPage/index.tsx +2 -2
  67. package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentCard.tsx +2 -2
  68. package/src/app/[variants]/(main)/community/(detail)/user/features/UserFavoriteAgents.tsx +1 -1
  69. package/src/app/[variants]/(main)/community/(list)/(home)/index.tsx +2 -2
  70. package/src/app/[variants]/(main)/community/(list)/(home)/loading.tsx +1 -1
  71. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/Client.tsx +5 -1
  72. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/Category/index.tsx +1 -1
  73. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/List/Item.tsx +1 -1
  74. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/MarketSourceSwitch.tsx +1 -1
  75. package/src/app/[variants]/(main)/community/_layout/Sidebar/Header/Nav.tsx +2 -2
  76. package/src/app/[variants]/(main)/home/features/CommunityAgents/List.tsx +1 -1
  77. package/src/app/[variants]/(main)/home/features/CommunityAgents/index.tsx +1 -1
  78. package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -1
  79. package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +6 -6
  80. package/src/app/[variants]/router/desktopRouter.config.tsx +8 -8
  81. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  82. package/src/business/server/user.ts +4 -0
  83. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  84. package/src/features/Conversation/Messages/Task/Actions/index.tsx +0 -2
  85. package/src/features/Conversation/Messages/Task/index.tsx +1 -1
  86. package/src/features/Conversation/Messages/Tasks/shared/ProcessingState.tsx +0 -2
  87. package/src/features/Electron/navigation/routeMetadata.ts +1 -1
  88. package/src/features/NavPanel/components/NavPanelDraggable.tsx +0 -14
  89. package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +4 -3
  90. package/src/features/SharePopover/index.tsx +5 -3
  91. package/src/hooks/useAppOrigin.ts +16 -0
  92. package/src/layout/GlobalProvider/FaviconProvider.tsx +92 -0
  93. package/src/layout/GlobalProvider/index.tsx +15 -11
  94. package/src/layout/GlobalProvider/useUserStateRedirect.ts +37 -24
  95. package/src/libs/next/config/define-config.ts +1 -1
  96. package/src/libs/trusted-client/index.ts +2 -5
  97. package/src/locales/default/desktop-onboarding.ts +5 -0
  98. package/src/locales/default/error.ts +11 -0
  99. package/src/locales/default/subscription.ts +1 -1
  100. package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +2 -0
  101. package/src/server/routers/lambda/user.ts +24 -10
  102. package/src/server/services/agentRuntime/AgentRuntimeService.test.ts +3 -0
  103. package/src/server/services/agentRuntime/AgentRuntimeService.ts +8 -5
  104. package/src/server/services/agentRuntime/types.ts +7 -0
  105. package/src/server/services/aiAgent/__tests__/execGroupSubAgentTask.test.ts +3 -0
  106. package/src/server/services/aiAgent/index.ts +10 -4
  107. package/src/server/services/market/index.ts +7 -0
  108. package/src/server/services/sandbox/index.ts +120 -0
  109. package/src/server/services/toolExecution/builtin.ts +12 -18
  110. package/src/server/services/toolExecution/index.ts +1 -1
  111. package/src/server/services/toolExecution/serverRuntimes/cloudSandbox.ts +31 -0
  112. package/src/server/services/toolExecution/serverRuntimes/index.ts +55 -0
  113. package/src/server/services/toolExecution/serverRuntimes/types.ts +14 -0
  114. package/src/server/services/toolExecution/serverRuntimes/webBrowsing.ts +20 -0
  115. package/src/server/services/toolExecution/types.ts +2 -0
  116. package/src/server/sitemap.test.ts +5 -5
  117. package/src/server/sitemap.ts +3 -3
  118. package/src/services/{codeInterpreter.ts → cloudSandbox.ts} +3 -3
  119. package/src/services/electron/remoteServer.ts +8 -0
  120. package/src/store/chat/agents/GroupOrchestration/__tests__/batch-exec-async-tasks.test.ts +626 -0
  121. package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +294 -0
  122. package/src/store/chat/slices/plugin/action.test.ts +0 -48
  123. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +0 -131
  124. package/src/store/tool/slices/builtin/executors/index.ts +2 -0
  125. package/src/store/user/slices/settings/selectors/toolIntervention.test.ts +143 -0
  126. package/src/store/user/slices/settings/selectors/toolIntervention.ts +11 -2
  127. package/packages/memory-user-memory/src/schemas/jsonSchemas.ts +0 -37
  128. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/DetailProvider.tsx +0 -0
  129. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/Block.tsx +0 -0
  130. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/Knowledge.tsx +0 -0
  131. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/KnowledgeItem.tsx +0 -0
  132. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/PluginItem.tsx +0 -0
  133. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/Plugins.tsx +0 -0
  134. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/index.tsx +0 -0
  135. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Nav.tsx +0 -0
  136. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Overview/index.tsx +0 -0
  137. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Versions/index.tsx +0 -0
  138. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/index.tsx +0 -0
  139. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/Related/Item.tsx +0 -0
  140. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/Summary/index.tsx +0 -0
  141. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/TocList/index.tsx +0 -0
  142. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/index.tsx +0 -0
  143. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/index.tsx +0 -0
  144. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/loading.tsx +0 -0
  145. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/_layout/index.tsx +0 -0
  146. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/_layout/style.ts +0 -0
  147. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/Category/useCategory.tsx +0 -0
  148. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/List/TokenTag.tsx +0 -0
  149. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/List/index.tsx +0 -0
  150. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/index.tsx +0 -0
  151. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/loading.tsx +0 -0
@@ -911,7 +911,12 @@ describe('GeneralChatAgent', () => {
911
911
 
912
912
  const context = createMockContext('task_result', {
913
913
  parentMessageId: 'task-parent-msg',
914
- result: { success: true, taskMessageId: 'task-1', threadId: 'thread-1', result: 'Task result' },
914
+ result: {
915
+ success: true,
916
+ taskMessageId: 'task-1',
917
+ threadId: 'thread-1',
918
+ result: 'Task result',
919
+ },
915
920
  });
916
921
 
917
922
  const result = await agent.runner(context, state);
@@ -1519,4 +1524,273 @@ describe('GeneralChatAgent', () => {
1519
1524
  ]);
1520
1525
  });
1521
1526
  });
1527
+
1528
+ describe('headless mode (for async tasks)', () => {
1529
+ it('should execute all tools directly in headless mode including those requiring approval', async () => {
1530
+ const agent = new GeneralChatAgent({
1531
+ agentConfig: { maxSteps: 100 },
1532
+ operationId: 'test-session',
1533
+ modelRuntimeConfig: mockModelRuntimeConfig,
1534
+ });
1535
+
1536
+ const toolCall: ChatToolPayload = {
1537
+ id: 'call-1',
1538
+ identifier: 'dangerous-tool',
1539
+ apiName: 'delete',
1540
+ arguments: '{}',
1541
+ type: 'default',
1542
+ };
1543
+
1544
+ const state = createMockState({
1545
+ toolManifestMap: {
1546
+ 'dangerous-tool': {
1547
+ identifier: 'dangerous-tool',
1548
+ humanIntervention: 'required', // Tool requires approval
1549
+ },
1550
+ },
1551
+ userInterventionConfig: {
1552
+ approvalMode: 'headless', // Headless mode for async tasks
1553
+ },
1554
+ });
1555
+
1556
+ const context = createMockContext('llm_result', {
1557
+ hasToolsCalling: true,
1558
+ toolsCalling: [toolCall],
1559
+ parentMessageId: 'msg-1',
1560
+ });
1561
+
1562
+ const result = await agent.runner(context, state);
1563
+
1564
+ // Should execute directly in headless mode
1565
+ expect(result).toEqual([
1566
+ {
1567
+ type: 'call_tool',
1568
+ payload: {
1569
+ parentMessageId: 'msg-1',
1570
+ toolCalling: toolCall,
1571
+ },
1572
+ },
1573
+ ]);
1574
+ });
1575
+
1576
+ it('should execute tools with "always" policy in headless mode', async () => {
1577
+ const agent = new GeneralChatAgent({
1578
+ agentConfig: { maxSteps: 100 },
1579
+ operationId: 'test-session',
1580
+ modelRuntimeConfig: mockModelRuntimeConfig,
1581
+ });
1582
+
1583
+ const alwaysTool: ChatToolPayload = {
1584
+ id: 'call-1',
1585
+ identifier: 'agent-builder',
1586
+ apiName: 'installPlugin',
1587
+ arguments: '{"identifier":"some-plugin","source":"market"}',
1588
+ type: 'builtin',
1589
+ };
1590
+
1591
+ const state = createMockState({
1592
+ toolManifestMap: {
1593
+ 'agent-builder': {
1594
+ identifier: 'agent-builder',
1595
+ api: [
1596
+ {
1597
+ name: 'installPlugin',
1598
+ humanIntervention: 'always', // Always requires intervention normally
1599
+ },
1600
+ ],
1601
+ },
1602
+ },
1603
+ userInterventionConfig: {
1604
+ approvalMode: 'headless', // Headless mode bypasses even 'always'
1605
+ },
1606
+ });
1607
+
1608
+ const context = createMockContext('llm_result', {
1609
+ hasToolsCalling: true,
1610
+ toolsCalling: [alwaysTool],
1611
+ parentMessageId: 'msg-1',
1612
+ });
1613
+
1614
+ const result = await agent.runner(context, state);
1615
+
1616
+ // Should execute directly in headless mode, even for 'always' policy
1617
+ expect(result).toEqual([
1618
+ {
1619
+ type: 'call_tool',
1620
+ payload: {
1621
+ parentMessageId: 'msg-1',
1622
+ toolCalling: alwaysTool,
1623
+ },
1624
+ },
1625
+ ]);
1626
+ });
1627
+
1628
+ it('should skip security blacklisted tools in headless mode', async () => {
1629
+ const agent = new GeneralChatAgent({
1630
+ agentConfig: { maxSteps: 100 },
1631
+ operationId: 'test-session',
1632
+ modelRuntimeConfig: mockModelRuntimeConfig,
1633
+ });
1634
+
1635
+ const blacklistedTool: ChatToolPayload = {
1636
+ id: 'call-1',
1637
+ identifier: 'bash',
1638
+ apiName: 'bash',
1639
+ arguments: '{"command":"rm -rf /"}', // Matches security blacklist
1640
+ type: 'builtin',
1641
+ };
1642
+
1643
+ const state = createMockState({
1644
+ toolManifestMap: {
1645
+ bash: {
1646
+ identifier: 'bash',
1647
+ humanIntervention: 'never',
1648
+ },
1649
+ },
1650
+ userInterventionConfig: {
1651
+ approvalMode: 'headless',
1652
+ },
1653
+ // Using default security blacklist which blocks "rm -rf /"
1654
+ });
1655
+
1656
+ const context = createMockContext('llm_result', {
1657
+ hasToolsCalling: true,
1658
+ toolsCalling: [blacklistedTool],
1659
+ parentMessageId: 'msg-1',
1660
+ });
1661
+
1662
+ const result = await agent.runner(context, state);
1663
+
1664
+ // Should return empty array (tool is skipped, not executed or pending)
1665
+ expect(result).toEqual([]);
1666
+ });
1667
+
1668
+ it('should handle mixed tools in headless mode - execute safe ones, skip blacklisted', async () => {
1669
+ const agent = new GeneralChatAgent({
1670
+ agentConfig: { maxSteps: 100 },
1671
+ operationId: 'test-session',
1672
+ modelRuntimeConfig: mockModelRuntimeConfig,
1673
+ });
1674
+
1675
+ const safeTool: ChatToolPayload = {
1676
+ id: 'call-1',
1677
+ identifier: 'web-search',
1678
+ apiName: 'search',
1679
+ arguments: '{"query":"hello"}',
1680
+ type: 'default',
1681
+ };
1682
+
1683
+ const blacklistedTool: ChatToolPayload = {
1684
+ id: 'call-2',
1685
+ identifier: 'bash',
1686
+ apiName: 'bash',
1687
+ arguments: '{"command":"rm -rf /"}', // Matches security blacklist
1688
+ type: 'builtin',
1689
+ };
1690
+
1691
+ const alwaysTool: ChatToolPayload = {
1692
+ id: 'call-3',
1693
+ identifier: 'agent-builder',
1694
+ apiName: 'installPlugin',
1695
+ arguments: '{}',
1696
+ type: 'builtin',
1697
+ };
1698
+
1699
+ const state = createMockState({
1700
+ toolManifestMap: {
1701
+ 'web-search': {
1702
+ identifier: 'web-search',
1703
+ humanIntervention: 'required',
1704
+ },
1705
+ 'bash': {
1706
+ identifier: 'bash',
1707
+ },
1708
+ 'agent-builder': {
1709
+ identifier: 'agent-builder',
1710
+ api: [
1711
+ {
1712
+ name: 'installPlugin',
1713
+ humanIntervention: 'always',
1714
+ },
1715
+ ],
1716
+ },
1717
+ },
1718
+ userInterventionConfig: {
1719
+ approvalMode: 'headless',
1720
+ },
1721
+ });
1722
+
1723
+ const context = createMockContext('llm_result', {
1724
+ hasToolsCalling: true,
1725
+ toolsCalling: [safeTool, blacklistedTool, alwaysTool],
1726
+ parentMessageId: 'msg-1',
1727
+ });
1728
+
1729
+ const result = await agent.runner(context, state);
1730
+
1731
+ // Should execute safeTool and alwaysTool, skip blacklistedTool
1732
+ expect(result).toEqual([
1733
+ {
1734
+ type: 'call_tools_batch',
1735
+ payload: {
1736
+ parentMessageId: 'msg-1',
1737
+ toolsCalling: [safeTool, alwaysTool],
1738
+ },
1739
+ },
1740
+ ]);
1741
+ });
1742
+
1743
+ it('should execute multiple tools as batch in headless mode', async () => {
1744
+ const agent = new GeneralChatAgent({
1745
+ agentConfig: { maxSteps: 100 },
1746
+ operationId: 'test-session',
1747
+ modelRuntimeConfig: mockModelRuntimeConfig,
1748
+ });
1749
+
1750
+ const tool1: ChatToolPayload = {
1751
+ id: 'call-1',
1752
+ identifier: 'search',
1753
+ apiName: 'search',
1754
+ arguments: '{}',
1755
+ type: 'default',
1756
+ };
1757
+
1758
+ const tool2: ChatToolPayload = {
1759
+ id: 'call-2',
1760
+ identifier: 'crawl',
1761
+ apiName: 'crawl',
1762
+ arguments: '{}',
1763
+ type: 'default',
1764
+ };
1765
+
1766
+ const state = createMockState({
1767
+ toolManifestMap: {
1768
+ search: { identifier: 'search', humanIntervention: 'required' },
1769
+ crawl: { identifier: 'crawl', humanIntervention: 'always' },
1770
+ },
1771
+ userInterventionConfig: {
1772
+ approvalMode: 'headless',
1773
+ },
1774
+ });
1775
+
1776
+ const context = createMockContext('llm_result', {
1777
+ hasToolsCalling: true,
1778
+ toolsCalling: [tool1, tool2],
1779
+ parentMessageId: 'msg-1',
1780
+ });
1781
+
1782
+ const result = await agent.runner(context, state);
1783
+
1784
+ // Should execute both tools as batch in headless mode
1785
+ expect(result).toEqual([
1786
+ {
1787
+ type: 'call_tools_batch',
1788
+ payload: {
1789
+ parentMessageId: 'msg-1',
1790
+ toolsCalling: [tool1, tool2],
1791
+ },
1792
+ },
1793
+ ]);
1794
+ });
1795
+ });
1522
1796
  });
@@ -86,6 +86,7 @@ Always adapt to user's language. Use natural descriptions, not raw field names.
86
86
  5. **Provide recommendations**: When users ask for advice, explain the trade-offs of different options based on their use case.
87
87
  6. **Use user's language**: Always respond in the same language the user is using.
88
88
  7. **Keep it simple**: Focus on core settings. Don't overwhelm users with advanced options unless they ask.
89
+ 8. **Install plugins one by one**: When multiple plugins need to be installed, install them sequentially one at a time instead of batching. This ensures better error handling, allows users to understand each plugin's purpose, and makes it easier to troubleshoot if something goes wrong.
89
90
  </guidelines>
90
91
 
91
92
  <configuration_knowledge>
@@ -202,6 +203,14 @@ Action: Use updateConfig with { config: { params: { temperature: 0.7 } } }
202
203
 
203
204
  User: "我想调整对话配置" / "I want to configure chat settings"
204
205
  Action: Explain the available chatConfig options and help them configure as needed.
206
+
207
+ User: "帮我安装网页浏览和图片生成这两个插件" / "Install web browsing and image generation plugins for me"
208
+ Action: Install plugins one by one:
209
+ 1. First, use installPlugin to install "lobe-web-browsing", explain what it does
210
+ 2. Wait for confirmation of success
211
+ 3. Then, use installPlugin to install "lobe-image-generation", explain what it does
212
+ 4. Confirm both plugins are installed successfully
213
+ This sequential approach ensures each plugin is properly installed and allows the user to understand each tool's purpose.
205
214
  </examples>
206
215
 
207
216
  <response_format>
@@ -5,6 +5,7 @@
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
7
7
  "./client": "./src/client/index.ts",
8
+ "./executor": "./src/executor/index.ts",
8
9
  "./executionRuntime": "./src/ExecutionRuntime/index.ts"
9
10
  },
10
11
  "main": "./src/index.ts",
@@ -1,125 +1,62 @@
1
+ import {
2
+ formatEditResult,
3
+ formatFileContent,
4
+ formatFileList,
5
+ formatFileSearchResults,
6
+ formatGlobResults,
7
+ formatMoveResults,
8
+ formatRenameResult,
9
+ formatWriteResult,
10
+ } from '@lobechat/prompts';
1
11
  import { type BuiltinServerRuntimeOutput } from '@lobechat/types';
2
12
 
3
- import { codeInterpreterService } from '@/services/codeInterpreter';
4
-
5
13
  import {
14
+ type EditLocalFileParams,
6
15
  type EditLocalFileState,
16
+ type ExecuteCodeParams,
7
17
  type ExecuteCodeState,
18
+ type ExportFileParams,
8
19
  type ExportFileState,
20
+ type GetCommandOutputParams,
9
21
  type GetCommandOutputState,
10
22
  type GlobFilesState,
23
+ type GlobLocalFilesParams,
24
+ type GrepContentParams,
11
25
  type GrepContentState,
26
+ type ISandboxService,
27
+ type KillCommandParams,
12
28
  type KillCommandState,
29
+ type ListLocalFilesParams,
13
30
  type ListLocalFilesState,
31
+ type MoveLocalFilesParams,
14
32
  type MoveLocalFilesState,
33
+ type ReadLocalFileParams,
15
34
  type ReadLocalFileState,
35
+ type RenameLocalFileParams,
16
36
  type RenameLocalFileState,
37
+ type RunCommandParams,
17
38
  type RunCommandState,
39
+ type SearchLocalFilesParams,
18
40
  type SearchLocalFilesState,
41
+ type WriteLocalFileParams,
19
42
  type WriteLocalFileState,
20
43
  } from '../types';
21
44
 
22
45
  /**
23
46
  * Cloud Sandbox Execution Runtime
24
47
  *
25
- * This runtime executes tools via the LobeHub Market SDK's runBuildInTool API,
26
- * which connects to AWS Bedrock AgentCore sandbox.
48
+ * This runtime executes tools via the injected ISandboxService.
49
+ * The service handles context (topicId, userId) internally - Runtime doesn't need to know about it.
27
50
  *
28
- * Session Management:
29
- * - Sessions are automatically created per userId + topicId combination
30
- * - Sessions are recreated automatically if expired
31
- * - The sessionExpiredAndRecreated flag indicates if recreation occurred
51
+ * Dependency Injection:
52
+ * - Client: Inject codeInterpreterService (uses tRPC client)
53
+ * - Server: Inject ServerSandboxService (uses MarketSDK directly)
32
54
  */
33
-
34
- interface ExecutionContext {
35
- topicId: string;
36
- userId: string;
37
- }
38
-
39
- // Types for tool parameters matching market-sdk
40
- interface ListLocalFilesParams {
41
- directoryPath: string;
42
- }
43
-
44
- interface ReadLocalFileParams {
45
- endLine?: number;
46
- path: string;
47
- startLine?: number;
48
- }
49
-
50
- interface WriteLocalFileParams {
51
- content: string;
52
- createDirectories?: boolean;
53
- path: string;
54
- }
55
-
56
- interface EditLocalFileParams {
57
- all?: boolean;
58
- path: string;
59
- replace: string;
60
- search: string;
61
- }
62
-
63
- interface SearchLocalFilesParams {
64
- directory: string;
65
- fileType?: string;
66
- keyword?: string;
67
- modifiedAfter?: string;
68
- modifiedBefore?: string;
69
- }
70
-
71
- interface MoveLocalFilesParams {
72
- operations: Array<{
73
- destination: string;
74
- source: string;
75
- }>;
76
- }
77
-
78
- interface RenameLocalFileParams {
79
- newName: string;
80
- oldPath: string;
81
- }
82
-
83
- interface RunCommandParams {
84
- background?: boolean;
85
- command: string;
86
- timeout?: number;
87
- }
88
-
89
- interface GetCommandOutputParams {
90
- commandId: string;
91
- }
92
-
93
- interface KillCommandParams {
94
- commandId: string;
95
- }
96
-
97
- interface GrepContentParams {
98
- directory: string;
99
- filePattern?: string;
100
- pattern: string;
101
- recursive?: boolean;
102
- }
103
-
104
- interface GlobLocalFilesParams {
105
- directory?: string;
106
- pattern: string;
107
- }
108
-
109
- interface ExportFileParams {
110
- path: string;
111
- }
112
-
113
- interface ExecuteCodeParams {
114
- code: string;
115
- language?: 'javascript' | 'python' | 'typescript';
116
- }
117
-
118
55
  export class CloudSandboxExecutionRuntime {
119
- private context: ExecutionContext;
56
+ private sandboxService: ISandboxService;
120
57
 
121
- constructor(context: ExecutionContext) {
122
- this.context = context;
58
+ constructor(sandboxService: ISandboxService) {
59
+ this.sandboxService = sandboxService;
123
60
  }
124
61
 
125
62
  // ==================== File Operations ====================
@@ -128,12 +65,19 @@ export class CloudSandboxExecutionRuntime {
128
65
  try {
129
66
  const result = await this.callTool('listLocalFiles', args);
130
67
 
131
- const state: ListLocalFilesState = {
132
- files: result.result?.files || [],
133
- };
68
+ const files = result.result?.files || [];
69
+ const state: ListLocalFilesState = { files };
70
+
71
+ const content = formatFileList(
72
+ files.map((f: { isDirectory: boolean; name: string }) => ({
73
+ isDirectory: f.isDirectory,
74
+ name: f.name,
75
+ })),
76
+ args.directoryPath,
77
+ );
134
78
 
135
79
  return {
136
- content: JSON.stringify(result.result),
80
+ content,
137
81
  state,
138
82
  success: true,
139
83
  };
@@ -154,8 +98,19 @@ export class CloudSandboxExecutionRuntime {
154
98
  totalLines: result.result?.totalLines,
155
99
  };
156
100
 
101
+ const lineRange: [number, number] | undefined =
102
+ args.startLine !== undefined && args.endLine !== undefined
103
+ ? [args.startLine, args.endLine]
104
+ : undefined;
105
+
106
+ const content = formatFileContent({
107
+ content: result.result?.content || '',
108
+ lineRange,
109
+ path: args.path,
110
+ });
111
+
157
112
  return {
158
- content: JSON.stringify(result.result),
113
+ content,
159
114
  state,
160
115
  success: true,
161
116
  };
@@ -174,11 +129,13 @@ export class CloudSandboxExecutionRuntime {
174
129
  success: result.success,
175
130
  };
176
131
 
132
+ const content = formatWriteResult({
133
+ path: args.path,
134
+ success: true,
135
+ });
136
+
177
137
  return {
178
- content: JSON.stringify({
179
- message: `Successfully wrote to ${args.path}`,
180
- success: true,
181
- }),
138
+ content,
182
139
  state,
183
140
  success: true,
184
141
  };
@@ -199,13 +156,15 @@ export class CloudSandboxExecutionRuntime {
199
156
  replacements: result.result?.replacements || 0,
200
157
  };
201
158
 
202
- const statsText =
203
- state.linesAdded || state.linesDeleted
204
- ? ` (+${state.linesAdded || 0} -${state.linesDeleted || 0})`
205
- : '';
159
+ const content = formatEditResult({
160
+ filePath: args.path,
161
+ linesAdded: state.linesAdded,
162
+ linesDeleted: state.linesDeleted,
163
+ replacements: state.replacements,
164
+ });
206
165
 
207
166
  return {
208
- content: `Successfully replaced ${state.replacements} occurrence(s) in ${args.path}${statsText}`,
167
+ content,
209
168
  state,
210
169
  success: true,
211
170
  };
@@ -218,13 +177,18 @@ export class CloudSandboxExecutionRuntime {
218
177
  try {
219
178
  const result = await this.callTool('searchLocalFiles', args);
220
179
 
180
+ const results = result.result?.results || [];
221
181
  const state: SearchLocalFilesState = {
222
- results: result.result?.results || [],
182
+ results,
223
183
  totalCount: result.result?.totalCount || 0,
224
184
  };
225
185
 
186
+ const content = formatFileSearchResults(
187
+ results.map((r: { path: string }) => ({ path: r.path })),
188
+ );
189
+
226
190
  return {
227
- content: JSON.stringify(result.result),
191
+ content,
228
192
  state,
229
193
  success: true,
230
194
  };
@@ -237,17 +201,17 @@ export class CloudSandboxExecutionRuntime {
237
201
  try {
238
202
  const result = await this.callTool('moveLocalFiles', args);
239
203
 
204
+ const results = result.result?.results || [];
240
205
  const state: MoveLocalFilesState = {
241
- results: result.result?.results || [],
206
+ results,
242
207
  successCount: result.result?.successCount || 0,
243
208
  totalCount: args.operations.length,
244
209
  };
245
210
 
211
+ const content = formatMoveResults(results);
212
+
246
213
  return {
247
- content: JSON.stringify({
248
- message: `Moved ${state.successCount}/${state.totalCount} items`,
249
- results: state.results,
250
- }),
214
+ content,
251
215
  state,
252
216
  success: true,
253
217
  };
@@ -267,11 +231,15 @@ export class CloudSandboxExecutionRuntime {
267
231
  success: result.success,
268
232
  };
269
233
 
234
+ const content = formatRenameResult({
235
+ error: result.result?.error,
236
+ newName: args.newName,
237
+ oldPath: args.oldPath,
238
+ success: result.success,
239
+ });
240
+
270
241
  return {
271
- content: JSON.stringify({
272
- message: `Successfully renamed ${args.oldPath} to ${args.newName}`,
273
- success: true,
274
- }),
242
+ content,
275
243
  state,
276
244
  success: result.success,
277
245
  };
@@ -405,14 +373,22 @@ export class CloudSandboxExecutionRuntime {
405
373
  try {
406
374
  const result = await this.callTool('globLocalFiles', args);
407
375
 
376
+ const files = result.result?.files || [];
377
+ const totalCount = result.result?.totalCount || 0;
378
+
408
379
  const state: GlobFilesState = {
409
- files: result.result?.files || [],
380
+ files,
410
381
  pattern: args.pattern,
411
- totalCount: result.result?.totalCount || 0,
382
+ totalCount,
412
383
  };
413
384
 
385
+ const content = formatGlobResults({
386
+ files,
387
+ totalFiles: totalCount,
388
+ });
389
+
414
390
  return {
415
- content: JSON.stringify(result.result),
391
+ content,
416
392
  state,
417
393
  success: true,
418
394
  };
@@ -425,7 +401,7 @@ export class CloudSandboxExecutionRuntime {
425
401
 
426
402
  /**
427
403
  * Export a file from the sandbox to cloud storage
428
- * Uses a single tRPC call that handles:
404
+ * Uses a single call that handles:
429
405
  * 1. Generate pre-signed upload URL
430
406
  * 2. Call sandbox to upload file
431
407
  * 3. Create persistent file record
@@ -437,11 +413,7 @@ export class CloudSandboxExecutionRuntime {
437
413
  const filename = args.path.split('/').pop() || 'exported_file';
438
414
 
439
415
  // Single call that handles everything: upload URL generation, sandbox upload, and file record creation
440
- const result = await codeInterpreterService.exportAndUploadFile(
441
- args.path,
442
- filename,
443
- this.context.topicId,
444
- );
416
+ const result = await this.sandboxService.exportAndUploadFile(args.path, filename);
445
417
 
446
418
  const state: ExportFileState = {
447
419
  downloadUrl: result.success && result.url ? result.url : '',
@@ -478,17 +450,16 @@ export class CloudSandboxExecutionRuntime {
478
450
  // ==================== Helper Methods ====================
479
451
 
480
452
  /**
481
- * Call a tool via the market SDK through tRPC
482
- * Routes through: ExecutionRuntime -> codeInterpreterService -> tRPC -> codeInterpreterRouter -> MarketSDK
453
+ * Call a tool via the injected sandbox service
483
454
  */
484
455
  private async callTool(
485
456
  toolName: string,
486
457
  params: Record<string, any>,
487
458
  ): Promise<{ result: any; sessionExpiredAndRecreated?: boolean; success: boolean }> {
488
- const result = await codeInterpreterService.callTool(toolName, params, this.context);
459
+ const result = await this.sandboxService.callTool(toolName, params);
489
460
 
490
461
  if (!result.success) {
491
- throw new Error((result as any).error?.message || `Cloud Sandbox tool ${toolName} failed`);
462
+ throw new Error(result.error?.message || `Cloud Sandbox tool ${toolName} failed`);
492
463
  }
493
464
 
494
465
  return result;