illusion-code 0.2.7__tar.gz → 0.2.9__tar.gz

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 (541) hide show
  1. {illusion_code-0.2.7 → illusion_code-0.2.9}/.github/workflows/publish.yml +1 -1
  2. {illusion_code-0.2.7 → illusion_code-0.2.9}/PKG-INFO +6 -1
  3. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/architecture.md +10 -0
  4. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/channels.md +64 -8
  5. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/commands.md +1 -1
  6. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/architecture.md +10 -0
  7. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/channels.md +63 -8
  8. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/commands.md +1 -1
  9. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/package-lock.json +2 -2
  10. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/package.json +1 -1
  11. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/registry.ts +18 -0
  12. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/version.ts +1 -1
  13. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/index.html +12 -0
  14. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/package-lock.json +2 -2
  15. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/package.json +1 -1
  16. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/ChatArea.tsx +32 -3
  17. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/MessageBubble.tsx +6 -2
  18. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/ModalCard.tsx +7 -4
  19. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/RightPanel.tsx +29 -11
  20. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/TodoPanel.tsx +26 -9
  21. illusion_code-0.2.9/frontend/web/src/hooks/useTheme.ts +89 -0
  22. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/i18n.ts +10 -0
  23. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/index.css +147 -70
  24. illusion_code-0.2.9/frontend/web/src/utils/toolDisplayName.ts +140 -0
  25. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/version.ts +1 -1
  26. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/tailwind.config.js +16 -16
  27. {illusion_code-0.2.7 → illusion_code-0.2.9}/pyproject.toml +4 -1
  28. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/__init__.py +1 -1
  29. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/__init__.py +342 -56
  30. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/base.py +112 -5
  31. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/base_commands.py +28 -14
  32. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/config.py +4 -1
  33. illusion_code-0.2.9/src/illusion/channels/delivery.py +646 -0
  34. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/feishu/adapter.py +306 -3
  35. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/feishu/messaging.py +346 -0
  36. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/feishu/session_map.py +56 -1
  37. illusion_code-0.2.9/src/illusion/channels/feishu/streaming.py +398 -0
  38. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/qq/adapter.py +317 -10
  39. illusion_code-0.2.9/src/illusion/channels/qq/api.py +844 -0
  40. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/qq/session_map.py +52 -1
  41. illusion_code-0.2.9/src/illusion/channels/qq/streaming.py +357 -0
  42. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/serve.py +11 -3
  43. illusion_code-0.2.9/src/illusion/channels/tools/cross_channel.py +231 -0
  44. illusion_code-0.2.9/src/illusion/channels/tools/feishu_doc.py +358 -0
  45. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/tools/feishu_drive.py +198 -5
  46. illusion_code-0.2.9/src/illusion/channels/tools/media.py +204 -0
  47. illusion_code-0.2.9/src/illusion/channels/weixin/adapter.py +1039 -0
  48. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/weixin/ilink_api.py +351 -2
  49. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/weixin/session_map.py +37 -1
  50. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/cli.py +58 -18
  51. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/config/i18n.py +2 -3
  52. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/config/paths.py +13 -7
  53. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/engine/query.py +4 -3
  54. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/plugins/loader.py +12 -4
  55. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/prompts/__init__.py +2 -0
  56. illusion_code-0.2.9/src/illusion/prompts/channel_hints.py +213 -0
  57. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/prompts/context.py +5 -0
  58. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/cron.py +3 -0
  59. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/cron_scheduler.py +34 -0
  60. illusion_code-0.2.9/src/illusion/tasks/todo_sync.py +172 -0
  61. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/cron_tool.py +45 -0
  62. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/task_get_tool.py +4 -2
  63. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/task_output_tool.py +4 -2
  64. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/task_stop_tool.py +4 -2
  65. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/app.py +6 -0
  66. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/backend_host.py +25 -3
  67. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/runtime.py +7 -1
  68. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/web/ws_host.py +15 -3
  69. illusion_code-0.2.9/tests/channels/weixin/test_adapter.py +827 -0
  70. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_qq_channel.py +81 -0
  71. illusion_code-0.2.9/tests/unit/__init__.py +0 -0
  72. illusion_code-0.2.9/tests/unit/test_channel_hints.py +74 -0
  73. illusion_code-0.2.9/tests/unit/test_cross_channel.py +186 -0
  74. illusion_code-0.2.9/tests/unit/test_delivery.py +207 -0
  75. illusion_code-0.2.9/tests/unit/test_feishu_streaming.py +666 -0
  76. illusion_code-0.2.9/tests/unit/test_media_tools.py +32 -0
  77. illusion_code-0.2.9/tests/unit/test_qq_streaming.py +430 -0
  78. illusion_code-0.2.9/tests/unit/test_session_list_active.py +72 -0
  79. illusion_code-0.2.7/src/illusion/channels/qq/api.py +0 -469
  80. illusion_code-0.2.7/src/illusion/channels/tools/feishu_doc.py +0 -148
  81. illusion_code-0.2.7/src/illusion/channels/weixin/adapter.py +0 -490
  82. illusion_code-0.2.7/tests/channels/weixin/test_adapter.py +0 -93
  83. {illusion_code-0.2.7 → illusion_code-0.2.9}/.gitignore +0 -0
  84. {illusion_code-0.2.7 → illusion_code-0.2.9}/.python-version +0 -0
  85. {illusion_code-0.2.7 → illusion_code-0.2.9}/LICENSE +0 -0
  86. {illusion_code-0.2.7 → illusion_code-0.2.9}/README.md +0 -0
  87. {illusion_code-0.2.7 → illusion_code-0.2.9}/README.zh-CN.md +0 -0
  88. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/extensions.md +0 -0
  89. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/getting-started.md +0 -0
  90. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/introduction.md +0 -0
  91. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/project-files.md +0 -0
  92. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/en/settings.md +0 -0
  93. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/images/IllusionCode.png +0 -0
  94. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/images/image1.png +0 -0
  95. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/images/image2.png +0 -0
  96. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/extensions.md +0 -0
  97. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/getting-started.md +0 -0
  98. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/introduction.md +0 -0
  99. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/project-files.md +0 -0
  100. {illusion_code-0.2.7 → illusion_code-0.2.9}/docs/zh-CN/settings.md +0 -0
  101. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/build.mjs +0 -0
  102. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/App.tsx +0 -0
  103. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/CommandPicker.tsx +0 -0
  104. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/Composer.tsx +0 -0
  105. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/ComposerController.tsx +0 -0
  106. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/ConversationView.tsx +0 -0
  107. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/Footer.tsx +0 -0
  108. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/MarkdownContent.tsx +0 -0
  109. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/MarkdownTable.tsx +0 -0
  110. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/ModalHost.tsx +0 -0
  111. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/MultilineTextInput.tsx +0 -0
  112. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/PromptInput.tsx +0 -0
  113. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/QuestionNavigationBar.tsx +0 -0
  114. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/QuestionPreviewBox.tsx +0 -0
  115. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/SelectModal.tsx +0 -0
  116. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/SidePanel.tsx +0 -0
  117. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/Spinner.tsx +0 -0
  118. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/StatusBar.tsx +0 -0
  119. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/SwarmPanel.tsx +0 -0
  120. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/TodoPanel.tsx +0 -0
  121. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/ToolCallDisplay.tsx +0 -0
  122. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/TranscriptPane.tsx +0 -0
  123. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/components/WelcomeBanner.tsx +0 -0
  124. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/hooks/useAnimationFrame.ts +0 -0
  125. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/hooks/useBackendSession.ts +0 -0
  126. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/hooks/useBlink.ts +0 -0
  127. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/hooks/useQuestionState.ts +0 -0
  128. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/hooks/useTerminalSize.ts +0 -0
  129. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/i18n.ts +0 -0
  130. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/index.tsx +0 -0
  131. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/theme/ThemeContext.tsx +0 -0
  132. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/theme/builtinThemes.ts +0 -0
  133. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/ToolInterface.ts +0 -0
  134. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/AgentTool.ts +0 -0
  135. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/BashTool.ts +0 -0
  136. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/CronTool.ts +0 -0
  137. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/EditTool.ts +0 -0
  138. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/GenericTool.ts +0 -0
  139. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/LspTool.ts +0 -0
  140. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/McpTool.ts +0 -0
  141. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/NotebookTool.ts +0 -0
  142. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/PlanTool.ts +0 -0
  143. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/ReadTool.ts +0 -0
  144. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/SearchTool.ts +0 -0
  145. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/SkillTool.ts +0 -0
  146. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/TaskTool.ts +0 -0
  147. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/WebTool.ts +0 -0
  148. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/WorktreeTool.ts +0 -0
  149. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/tools/implementations/WriteTool.ts +0 -0
  150. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/types.ts +0 -0
  151. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/utils/markdown.ts +0 -0
  152. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/src/utils/thinking.ts +0 -0
  153. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/terminal/tsconfig.json +0 -0
  154. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/postcss.config.js +0 -0
  155. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/App.tsx +0 -0
  156. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/ErrorBoundary.tsx +0 -0
  157. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/PromptInput.tsx +0 -0
  158. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/Sidebar.tsx +0 -0
  159. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/Toolbar.tsx +0 -0
  160. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/components/WelcomeScreen.tsx +0 -0
  161. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/hooks/useWebSocketSession.ts +0 -0
  162. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/main.tsx +0 -0
  163. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/remarkSuperscript.ts +0 -0
  164. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/types/protocol.ts +0 -0
  165. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/src/vite-env.d.ts +0 -0
  166. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/tsconfig.json +0 -0
  167. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/tsconfig.node.json +0 -0
  168. {illusion_code-0.2.7 → illusion_code-0.2.9}/frontend/web/vite.config.ts +0 -0
  169. {illusion_code-0.2.7 → illusion_code-0.2.9}/scripts/build_frontend.py +0 -0
  170. {illusion_code-0.2.7 → illusion_code-0.2.9}/scripts/hatch_build.py +0 -0
  171. {illusion_code-0.2.7 → illusion_code-0.2.9}/scripts/publish.py +0 -0
  172. {illusion_code-0.2.7 → illusion_code-0.2.9}/scripts/sync_version.py +0 -0
  173. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/__main__.py +0 -0
  174. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/__init__.py +0 -0
  175. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/client.py +0 -0
  176. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/codex_client.py +0 -0
  177. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/compat.py +0 -0
  178. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/effort.py +0 -0
  179. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/errors.py +0 -0
  180. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/openai_client.py +0 -0
  181. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/provider.py +0 -0
  182. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/registry.py +0 -0
  183. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/api/usage.py +0 -0
  184. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/__init__.py +0 -0
  185. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/codex_oauth.py +0 -0
  186. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/copilot.py +0 -0
  187. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/external.py +0 -0
  188. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/flows.py +0 -0
  189. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/manager.py +0 -0
  190. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/auth/storage.py +0 -0
  191. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/bridge/__init__.py +0 -0
  192. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/bridge/manager.py +0 -0
  193. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/bridge/session_runner.py +0 -0
  194. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/bridge/types.py +0 -0
  195. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/bridge/work_secret.py +0 -0
  196. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/feishu/__init__.py +0 -0
  197. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/feishu/commands.py +0 -0
  198. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/feishu/ws_client.py +0 -0
  199. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/pid.py +0 -0
  200. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/qq/__init__.py +0 -0
  201. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/qq/commands.py +0 -0
  202. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/qq/ws_client.py +0 -0
  203. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/tools/__init__.py +0 -0
  204. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/weixin/__init__.py +0 -0
  205. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/channels/weixin/commands.py +0 -0
  206. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/__init__.py +0 -0
  207. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/auth.py +0 -0
  208. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/bridge.py +0 -0
  209. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/context.py +0 -0
  210. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/git.py +0 -0
  211. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/helpers.py +0 -0
  212. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/__init__.py +0 -0
  213. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/analysis/__init__.py +0 -0
  214. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/analysis/architecture.py +0 -0
  215. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/analysis/conventions.py +0 -0
  216. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/analysis/dependencies.py +0 -0
  217. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/analysis/key_modules.py +0 -0
  218. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/extraction/__init__.py +0 -0
  219. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/extraction/lsp_symbols.py +0 -0
  220. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/extraction/readme.py +0 -0
  221. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/extraction/scanner.py +0 -0
  222. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/generation/__init__.py +0 -0
  223. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/generation/claudemd.py +0 -0
  224. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/generation/illusionmd.py +0 -0
  225. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/generation/memory_template.py +0 -0
  226. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/generation/rules.py +0 -0
  227. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/generation/sections.py +0 -0
  228. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/orchestrator.py +0 -0
  229. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/init/types.py +0 -0
  230. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/mcp.py +0 -0
  231. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/memory.py +0 -0
  232. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/misc.py +0 -0
  233. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/model.py +0 -0
  234. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/plugin.py +0 -0
  235. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/registry.py +0 -0
  236. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/rules.py +0 -0
  237. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/sandbox.py +0 -0
  238. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/session.py +0 -0
  239. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/settings.py +0 -0
  240. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/commands/types.py +0 -0
  241. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/config/__init__.py +0 -0
  242. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/config/plan_file.py +0 -0
  243. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/config/settings.py +0 -0
  244. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/coordinator/__init__.py +0 -0
  245. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/coordinator/agent_definitions.py +0 -0
  246. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/coordinator/coordinator_mode.py +0 -0
  247. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/engine/__init__.py +0 -0
  248. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/engine/cost_tracker.py +0 -0
  249. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/engine/messages.py +0 -0
  250. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/engine/query_engine.py +0 -0
  251. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/engine/stream_events.py +0 -0
  252. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/__init__.py +0 -0
  253. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/events.py +0 -0
  254. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/executor.py +0 -0
  255. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/hot_reload.py +0 -0
  256. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/loader.py +0 -0
  257. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/register_hooks.py +0 -0
  258. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/schemas.py +0 -0
  259. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/session_hooks.py +0 -0
  260. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/types.py +0 -0
  261. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/hooks/utils.py +0 -0
  262. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/mcp/__init__.py +0 -0
  263. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/mcp/client.py +0 -0
  264. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/mcp/config.py +0 -0
  265. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/mcp/types.py +0 -0
  266. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/__init__.py +0 -0
  267. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/manager.py +0 -0
  268. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/memdir.py +0 -0
  269. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/paths.py +0 -0
  270. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/scan.py +0 -0
  271. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/search.py +0 -0
  272. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/memory/types.py +0 -0
  273. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/output_styles/__init__.py +0 -0
  274. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/output_styles/loader.py +0 -0
  275. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/permissions/__init__.py +0 -0
  276. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/permissions/checker.py +0 -0
  277. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/permissions/loader.py +0 -0
  278. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/permissions/modes.py +0 -0
  279. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/permissions/schemas.py +0 -0
  280. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/platforms.py +0 -0
  281. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/plugins/__init__.py +0 -0
  282. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/plugins/installer.py +0 -0
  283. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/plugins/options.py +0 -0
  284. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/plugins/schemas.py +0 -0
  285. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/plugins/types.py +0 -0
  286. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/prompts/claudemd.py +0 -0
  287. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/prompts/environment.py +0 -0
  288. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/prompts/system_prompt.py +0 -0
  289. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/__init__.py +0 -0
  290. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/adapter.py +0 -0
  291. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/platforms/__init__.py +0 -0
  292. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/platforms/base.py +0 -0
  293. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/platforms/linux.py +0 -0
  294. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/platforms/macos.py +0 -0
  295. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/platforms/windows.py +0 -0
  296. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/proxy/__init__.py +0 -0
  297. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/proxy/env_vars.py +0 -0
  298. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/runtime.py +0 -0
  299. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/symlink_protection.py +0 -0
  300. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/utils.py +0 -0
  301. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/sandbox/violation_store.py +0 -0
  302. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/__init__.py +0 -0
  303. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/__init__.py +0 -0
  304. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/auto_compact.py +0 -0
  305. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/compact_core.py +0 -0
  306. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/compact_prompt.py +0 -0
  307. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/constants.py +0 -0
  308. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/message_ops.py +0 -0
  309. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/microcompact.py +0 -0
  310. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/models.py +0 -0
  311. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/compact/token_utils.py +0 -0
  312. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/file_history.py +0 -0
  313. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/lsp/__init__.py +0 -0
  314. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/lsp/client.py +0 -0
  315. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/lsp/config.py +0 -0
  316. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/lsp/manager.py +0 -0
  317. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/lsp/types.py +0 -0
  318. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/session_storage.py +0 -0
  319. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/services/token_estimation.py +0 -0
  320. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/__init__.py +0 -0
  321. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/__init__.py +0 -0
  322. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/batch.md +0 -0
  323. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/debug.md +0 -0
  324. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/loop.md +0 -0
  325. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/remember.md +0 -0
  326. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/simplify.md +0 -0
  327. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/skillify.md +0 -0
  328. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/stuck.md +0 -0
  329. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/update-config.md +0 -0
  330. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/bundled/content/verify.md +0 -0
  331. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/loader.py +0 -0
  332. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/registry.py +0 -0
  333. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/skills/types.py +0 -0
  334. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/state/__init__.py +0 -0
  335. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/state/app_state.py +0 -0
  336. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/state/store.py +0 -0
  337. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/__init__.py +0 -0
  338. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/agent_executor.py +0 -0
  339. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/in_process.py +0 -0
  340. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/subprocess_backend.py +0 -0
  341. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/team_helpers.py +0 -0
  342. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/types.py +0 -0
  343. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/swarm/worktree.py +0 -0
  344. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tasks/__init__.py +0 -0
  345. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tasks/local_agent_task.py +0 -0
  346. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tasks/local_shell_task.py +0 -0
  347. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tasks/manager.py +0 -0
  348. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tasks/stop_task.py +0 -0
  349. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tasks/types.py +0 -0
  350. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/__init__.py +0 -0
  351. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/agent_tool.py +0 -0
  352. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/ask_user_question_tool.py +0 -0
  353. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/base.py +0 -0
  354. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/bash_tool.py +0 -0
  355. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/config_tool.py +0 -0
  356. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/enter_plan_mode_tool.py +0 -0
  357. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/enter_worktree_tool.py +0 -0
  358. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/exit_plan_mode_tool.py +0 -0
  359. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/exit_worktree_tool.py +0 -0
  360. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/file_edit_tool.py +0 -0
  361. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/file_read_tool.py +0 -0
  362. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/file_write_tool.py +0 -0
  363. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/glob_tool.py +0 -0
  364. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/grep_tool.py +0 -0
  365. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/list_mcp_resources_tool.py +0 -0
  366. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/lsp_formatters.py +0 -0
  367. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/lsp_schemas.py +0 -0
  368. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/lsp_tool.py +0 -0
  369. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/mcp_auth_tool.py +0 -0
  370. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/mcp_tool.py +0 -0
  371. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/notebook_edit_tool.py +0 -0
  372. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/powershell_tool.py +0 -0
  373. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/read_mcp_resource_tool.py +0 -0
  374. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/repl_tool.py +0 -0
  375. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/send_message_tool.py +0 -0
  376. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/shell_common.py +0 -0
  377. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/skill_tool.py +0 -0
  378. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/sleep_tool.py +0 -0
  379. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/structured_output_tool.py +0 -0
  380. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/task_create_tool.py +0 -0
  381. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/task_list_tool.py +0 -0
  382. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/task_update_tool.py +0 -0
  383. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/team_create_tool.py +0 -0
  384. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/team_delete_tool.py +0 -0
  385. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/todo_write_tool.py +0 -0
  386. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/tool_search_tool.py +0 -0
  387. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/web_fetch_tool.py +0 -0
  388. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/tools/web_search_tool.py +0 -0
  389. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/__init__.py +0 -0
  390. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/input.py +0 -0
  391. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/output.py +0 -0
  392. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/permission_dialog.py +0 -0
  393. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/permission_store.py +0 -0
  394. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/protocol.py +0 -0
  395. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/react_launcher.py +0 -0
  396. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/textual_app.py +0 -0
  397. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/web/__init__.py +0 -0
  398. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/web/server.py +0 -0
  399. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/ui/web/ws_web_api.py +0 -0
  400. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/utils/__init__.py +0 -0
  401. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/utils/atomic_write.py +0 -0
  402. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/utils/file_state_cache.py +0 -0
  403. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/utils/ripgrep.py +0 -0
  404. {illusion_code-0.2.7 → illusion_code-0.2.9}/src/illusion/utils/shell.py +0 -0
  405. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/__init__.py +0 -0
  406. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/api/test_anthropic_client.py +0 -0
  407. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/api/test_client.py +0 -0
  408. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/api/test_codex_client.py +0 -0
  409. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/api/test_effort.py +0 -0
  410. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/api/test_fallback/346/217/220/347/244/272.py" +0 -0
  411. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/api/test_openai_client.py +0 -0
  412. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/__init__.py +0 -0
  413. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/feishu/__init__.py +0 -0
  414. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/feishu/test_adapter.py +0 -0
  415. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/feishu/test_commands.py +0 -0
  416. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/feishu/test_session_map.py +0 -0
  417. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/test_base.py +0 -0
  418. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/test_base_commands.py +0 -0
  419. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/test_config.py +0 -0
  420. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/test_pid.py +0 -0
  421. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/test_tool_registration.py +0 -0
  422. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/tools/__init__.py +0 -0
  423. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/tools/test_feishu_doc.py +0 -0
  424. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/tools/test_feishu_drive.py +0 -0
  425. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/weixin/__init__.py +0 -0
  426. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/weixin/test_config.py +0 -0
  427. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/weixin/test_ilink_api.py +0 -0
  428. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/channels/weixin/test_session_map.py +0 -0
  429. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/commands/init/__init__.py +0 -0
  430. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/commands/init/test_claudemd.py +0 -0
  431. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/commands/init/test_orchestrator.py +0 -0
  432. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/commands/init/test_scanner.py +0 -0
  433. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/commands/init/test_sections.py +0 -0
  434. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/commands/test_effort_command.py +0 -0
  435. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/config/test_i18n.py +0 -0
  436. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/config/test_settings.py +0 -0
  437. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/conftest.py +0 -0
  438. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/engine/test_query_engine.py +0 -0
  439. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/fixtures/fake_mcp_server.py +0 -0
  440. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/integration/test_effort_integration.py +0 -0
  441. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/services/lsp/test_lsp_client.py +0 -0
  442. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/services/lsp/test_lsp_config.py +0 -0
  443. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/services/lsp/test_lsp_manager.py +0 -0
  444. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/services/lsp/test_lsp_types.py +0 -0
  445. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_api/__init__.py +0 -0
  446. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_api/test_client.py +0 -0
  447. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_api/test_codex_client.py +0 -0
  448. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_api/test_compat.py +0 -0
  449. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_api/test_openai_client.py +0 -0
  450. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_auth/test_external.py +0 -0
  451. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_bridge/test_core.py +0 -0
  452. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_bridge/test_session_flow.py +0 -0
  453. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_commands/__init__.py +0 -0
  454. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_commands/test_cli.py +0 -0
  455. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_commands/test_command_flows.py +0 -0
  456. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_commands/test_registry.py +0 -0
  457. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_config/__init__.py +0 -0
  458. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_config/test_paths.py +0 -0
  459. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_config/test_settings.py +0 -0
  460. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_coordinator/test_agent_definitions.py +0 -0
  461. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_coordinator/test_coordinator_mode.py +0 -0
  462. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_engine/__init__.py +0 -0
  463. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_engine/test_media_block.py +0 -0
  464. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_engine/test_query_engine.py +0 -0
  465. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_engine/test_tool_result_media.py +0 -0
  466. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_hooks/__init__.py +0 -0
  467. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_hooks/test_executor.py +0 -0
  468. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_hooks/test_loader_permissions.py +0 -0
  469. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_hooks_skills_plugins_real.py +0 -0
  470. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_mcp/__init__.py +0 -0
  471. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_mcp/test_config_permissions.py +0 -0
  472. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_mcp/test_integration.py +0 -0
  473. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_mcp/test_stdio_flow.py +0 -0
  474. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_memory/__init__.py +0 -0
  475. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_memory/test_manager_permissions.py +0 -0
  476. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_memory/test_memdir.py +0 -0
  477. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_merged_prs_on_autoagent.py +0 -0
  478. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_permissions/__init__.py +0 -0
  479. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_permissions/test_checker.py +0 -0
  480. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_permissions/test_loader.py +0 -0
  481. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_permissions/test_project_permissions.py +0 -0
  482. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_platforms.py +0 -0
  483. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_plugins/__init__.py +0 -0
  484. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_plugins/test_lifecycle_flow.py +0 -0
  485. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_plugins/test_loader.py +0 -0
  486. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_plugins/test_loader_permissions.py +0 -0
  487. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_prompts/__init__.py +0 -0
  488. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_prompts/test_claudemd.py +0 -0
  489. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_prompts/test_claudemd_permissions.py +0 -0
  490. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_prompts/test_environment.py +0 -0
  491. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_prompts/test_system_prompt.py +0 -0
  492. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_real_large_tasks.py +0 -0
  493. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_adapter.py +0 -0
  494. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_config_schema.py +0 -0
  495. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_platform_linux.py +0 -0
  496. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_platform_macos.py +0 -0
  497. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_platform_windows.py +0 -0
  498. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_proxy_env_vars.py +0 -0
  499. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_runtime.py +0 -0
  500. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_symlink_protection.py +0 -0
  501. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_utils.py +0 -0
  502. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_sandbox/test_violation_store.py +0 -0
  503. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_scripts/test_sync_version.py +0 -0
  504. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_services/test_compact.py +0 -0
  505. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_services/test_cron.py +0 -0
  506. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_services/test_cron_scheduler.py +0 -0
  507. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_services/test_session_storage.py +0 -0
  508. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_skills/test_loader.py +0 -0
  509. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_skills/test_loader_permissions.py +0 -0
  510. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_swarm/__init__.py +0 -0
  511. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_swarm/test_agent_executor.py +0 -0
  512. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_swarm/test_imports.py +0 -0
  513. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_swarm/test_in_process.py +0 -0
  514. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_swarm/test_types.py +0 -0
  515. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_swarm/test_worktree.py +0 -0
  516. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tasks/test_manager.py +0 -0
  517. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/__init__.py +0 -0
  518. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_agent_tool.py +0 -0
  519. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_core_tools.py +0 -0
  520. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_file_read_media.py +0 -0
  521. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_glob_tool.py +0 -0
  522. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_grep_tool.py +0 -0
  523. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_integration.py +0 -0
  524. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_integration_flows.py +0 -0
  525. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_mcp_auth_tool.py +0 -0
  526. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_ripgrep.py +0 -0
  527. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_shell_common.py +0 -0
  528. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_task_tools.py +0 -0
  529. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_team_tools.py +0 -0
  530. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_tools/test_web_fetch_tool.py +0 -0
  531. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_ui/__init__.py +0 -0
  532. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_ui/test_modes.py +0 -0
  533. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_ui/test_permission_store.py +0 -0
  534. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_ui/test_react_backend.py +0 -0
  535. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_ui/test_react_launcher.py +0 -0
  536. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_ui/test_textual_app.py +0 -0
  537. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_untested_features.py +0 -0
  538. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/test_utils/test_shell.py +0 -0
  539. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/ui/test_backend_host.py +0 -0
  540. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/ui/test_protocol_web.py +0 -0
  541. {illusion_code-0.2.7 → illusion_code-0.2.9}/tests/ui/test_web_api.py +0 -0
@@ -29,7 +29,7 @@ jobs:
29
29
  run: |
30
30
  python -m pip install --upgrade pip
31
31
  pip install build twine pytest ruff
32
- pip install -e ".[dev]"
32
+ pip install -e ".[dev,all]"
33
33
 
34
34
  - name: Lint
35
35
  run: ruff check src/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: illusion-code
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Open-source Python port of Claude Code - an AI-powered CLI coding assistant
5
5
  Project-URL: Homepage, https://github.com/YunTaiHua/illusion-code
6
6
  Project-URL: Repository, https://github.com/YunTaiHua/illusion-code
@@ -37,6 +37,11 @@ Requires-Dist: typer>=0.12.0
37
37
  Requires-Dist: uvicorn[standard]>=0.34.0
38
38
  Requires-Dist: watchfiles>=0.20.0
39
39
  Requires-Dist: websockets>=12.0
40
+ Provides-Extra: all
41
+ Requires-Dist: aiohttp>=3.9.0; extra == 'all'
42
+ Requires-Dist: cryptography>=42.0.0; extra == 'all'
43
+ Requires-Dist: lark-oapi>=1.4.0; extra == 'all'
44
+ Requires-Dist: qrcode>=7.4.0; extra == 'all'
40
45
  Provides-Extra: dev
41
46
  Requires-Dist: mypy>=1.10.0; extra == 'dev'
42
47
  Requires-Dist: pexpect>=4.9.0; extra == 'dev'
@@ -60,6 +60,16 @@ Provides 34 core tools, covering:
60
60
  - **Interaction**: `ask_user_question`
61
61
  - **Scheduled Tasks**: `cron` (unified tool with status/list/add/update/remove/run actions)
62
62
 
63
+ ### Scheduled Tasks & Delivery Pipeline
64
+
65
+ The cron subsystem is composed of three cooperating modules:
66
+
67
+ - `services/cron.py` — CronJob data model and persistence (`cron.json`)
68
+ - `services/cron_scheduler.py` — scheduler process; runs the prompt in a subprocess and delivers the result to a channel based on the `deliver_to` field
69
+ - `channels/delivery.py` — delivery module; `parse_deliver_to` parses the target, `deliver_to_channel` dispatches to Feishu/WeChat/QQ `_deliver_*` functions
70
+
71
+ Delivery targets accept `channel:chat_id` (fully qualified) or a bare channel name (combined with the `chat_id` field). Failed jobs include stderr in the delivered text so users can see the error. See [Channels doc](channels.md#cron-job-result-delivery) for details.
72
+
63
73
  ### Permission System
64
74
 
65
75
  Three permission modes:
@@ -261,38 +261,94 @@ Send a message to your bot in QQ — private chat replies directly, group chat r
261
261
  - **No message editing** — replies are sent as complete text
262
262
  - **4000 char limit** — longer replies auto-split into multiple messages with 1.5s delay
263
263
  - **File sending** — supports 3-step chunked upload
264
+ - **Markdown dynamic hint** — when `markdown_support` is `true` in `channels.json`, the platform prompt tells the LLM that markdown is available (msg_type=2); when `false` (default), the prompt forces plain text (msg_type=0) and `send_text` auto-degrades to plain text on failure. Keep the default `false` for normal developer accounts without template permissions
265
+ - **Attachment download security** — when downloading QQ inbound attachments, the bot token is only attached for `.qq.com` / `.qq.com.cn` hosts, preventing access_token leakage via malicious URLs
264
266
 
265
267
  ## Channel Architecture
266
268
 
267
269
  ```
268
270
  src/illusion/channels/
269
- ├── __init__.py # ChannelRunner (message→agent glue) + maybe_spawn_channel_daemon
270
- ├── base.py # Channel ABC + InboundMessage + typing methods
271
+ ├── __init__.py # ChannelRunner (message→agent glue) + maybe_spawn_channel_daemon + kill_channel_daemon
272
+ ├── base.py # Channel ABC + InboundMessage + Attachment + typing methods
271
273
  ├── base_commands.py # BaseCommandHandler (shared slash commands)
272
274
  ├── config.py # ChannelsConfig / FeishuChannelConfig / WeixinChannelConfig
275
+ ├── delivery.py # cron job result delivery to channels (parse_deliver_to + deliver_to_channel)
273
276
  ├── serve.py # 'illusion channel serve' entry point (multi-channel)
274
277
  ├── pid.py # PID file management (avoid duplicate daemons)
275
278
  ├── feishu/
276
279
  │ ├── adapter.py # FeishuChannel: WS connection, event dispatch, admission control
277
280
  │ ├── ws_client.py # lark-oapi WS client wrapper
278
- │ ├── messaging.py # Card send/patch, message rendering
281
+ │ ├── messaging.py # Card send/patch, message rendering, resolve_receive_id
279
282
  │ ├── stream_editor.py # Streaming card editor (throttled patch updates)
280
283
  │ ├── session_map.py # Feishu session store (chat_id → session)
281
284
  │ └── commands.py # FeishuCommandHandler (extends BaseCommandHandler)
282
285
  ├── weixin/
283
286
  │ ├── __init__.py # WEIXIN_DEPENDENCIES / ensure_weixin_dependencies
284
- │ ├── adapter.py # WeixinChannel: long-poll, admission, context_token, typing
285
- │ ├── ilink_api.py # iLink Bot API client (QR login / send / poll / typing)
287
+ │ ├── adapter.py # WeixinChannel: long-poll, admission, context_token, AES key cache, typing
288
+ │ ├── ilink_api.py # iLink Bot API client (QR login / send / poll / typing / CDN allowlist)
286
289
  │ ├── session_map.py # WeixinSessionStore (user_id → session)
287
290
  │ └── commands.py # WeixinCommandHandler (extends BaseCommandHandler)
288
291
  ├── qq/
289
292
  │ ├── __init__.py # QQ_DEPENDENCIES / ensure_qq_dependencies
290
- │ ├── adapter.py # QQChannel: WS connection, admission, message normalization
293
+ │ ├── adapter.py # QQChannel: WS connection, admission, message normalization, attachment host validation
291
294
  │ ├── ws_client.py # QQ Bot WS gateway client (heartbeat/reconnect/events)
292
- │ ├── api.py # QQ Bot REST API client (token/send/upload)
295
+ │ ├── api.py # QQ Bot REST API client (token/send/upload/_parse_qq_response)
293
296
  │ ├── session_map.py # QQSessionStore (chat_id → session)
294
297
  │ └── commands.py # QQCommandHandler (extends BaseCommandHandler)
295
298
  └── tools/
296
299
  ├── feishu_doc.py # feishu_doc_read / feishu_doc_create
297
- └── feishu_drive.py # feishu_drive_list / upload / download
300
+ ├── feishu_drive.py # feishu_drive_list / upload / download
301
+ └── media.py # SendMediaTool / ReceiveMediaTool (activated by channel config)
298
302
  ```
303
+
304
+ ## Cron Job Result Delivery
305
+
306
+ Cron jobs can deliver execution results to Feishu/WeChat/QQ channel sessions. Configure via the `deliver_to` field in `cron_tool.py`:
307
+
308
+ - **Empty** — local execution only (terminal output)
309
+ - **`channel:chat_id`** — fully qualified format, e.g. `feishu:oc_xxx` (group), `feishu:ou_xxx` (private), `weixin:wxid_xxx`, `qq:openid`
310
+ - **`channel` (name only)** — requires the `chat_id` field; otherwise treated as "unspecified"
311
+
312
+ Session filename prefix rules (for extracting the real ID from `~/.illusion/channels/<channel>/sessions/`):
313
+
314
+ | Channel | Filename format | Real ID |
315
+ |---------|-----------------|---------|
316
+ | Feishu private | `u_ou_xxx.json` | `ou_xxx` (strip `u_` prefix) |
317
+ | Feishu group | `g_oc_xxx_ou_xxx.json` | `oc_xxx` (use the `oc_` part) |
318
+ | WeChat | `u_<wxid>.json` | `<wxid>` (strip `u_` prefix) |
319
+ | QQ | `<openid>.json` | `<openid>` (filename is the ID) |
320
+
321
+ Delivery failures do not affect task status; only a warning is logged. Failed jobs (non-success status) include stderr in the delivered text so users can see the error.
322
+
323
+ ## Concurrent Message Handling
324
+
325
+ When a user sends multiple messages in quick succession, `ChannelRunner` serializes agent turns per `chat_id` using an `asyncio.Lock` to prevent session history corruption:
326
+
327
+ - M1 acquires the lock and starts the agent; M2/M3 enter the queue and wait
328
+ - After M1 completes (or exits pending_replies wait), M2 acquires the lock
329
+ - Permission/ask reply messages are **not locked** — they immediately `set_result` the waiting future, avoiding 300s timeouts
330
+ - Different `chat_id`s run fully in parallel without blocking each other
331
+
332
+ This ensures conversation history is written in order for the same session, preventing the race condition where "M1/A1 and M3/A3 are lost, only M2/A2 remains."
333
+
334
+ ## Cross-Channel File Transfer
335
+
336
+ When multiple channels are enabled, the LLM can perceive all enabled channels and transfer files across channels.
337
+
338
+ ### How It Works
339
+
340
+ 1. **Channel-aware prompts**: System prompt includes current channel identity + overview of other enabled channels (with active sessions)
341
+ 2. **Cross-channel tool**: `send_to_channel` for cross-channel file transfer, `send_media` for within-current-channel
342
+ 3. **Active session list**: Up to 5 recent sessions per channel (sorted by last active time) to help LLM decide delivery target
343
+
344
+ ### Use Cases
345
+
346
+ - **PC terminal → channel**: User says "send this file to Zhang San on Feishu"
347
+ - **Channel → channel**: User on QQ says "send this file to WeChat"
348
+ - **Within channel (default)**: User on QQ says "send this file" → uses `send_media` to current QQ session
349
+
350
+ ### Limitations
351
+
352
+ - For cross-channel **text** messages, use cron tasks (`send_to_channel` only supports files)
353
+ - QQ and WeChat file upload APIs are complex; current version uses text prompts as fallback. Feishu is fully supported
354
+ - Active session list comes from session storage directory; users never interacted with won't appear
@@ -24,7 +24,7 @@ illusion plugin list # List plugins
24
24
  illusion plugin install <source> # Install plugin
25
25
  illusion plugin uninstall <name> # Uninstall plugin
26
26
 
27
- # Channel management (Feishu/Lark messaging)
27
+ # Channel management (Feishu/WeChat/QQ messaging)
28
28
  illusion channel login # Interactive channel setup (select channel → configure credentials)
29
29
  illusion channel serve # Run channel daemon in foreground (listen for messages)
30
30
  illusion channel status # View channel status (enabled/connected/PID)
@@ -62,6 +62,16 @@ illusion-code/
62
62
  - **交互**: `ask_user_question`
63
63
  - **定时任务**: `cron`(统一工具,支持 status/list/add/update/remove/run 操作)
64
64
 
65
+ ### 定时任务与投递链路
66
+
67
+ 定时任务子系统由三个模块协作:
68
+
69
+ - `services/cron.py` — 任务数据模型(CronJob)与持久化(`cron.json`)
70
+ - `services/cron_scheduler.py` — 调度器进程,子进程执行提示词,按 `deliver_to` 字段投递结果到渠道
71
+ - `channels/delivery.py` — 渠道投递模块,`parse_deliver_to` 解析目标,`deliver_to_channel` 派发到飞书/微信/QQ 的 `_deliver_*` 函数
72
+
73
+ 投递目标支持 `channel:chat_id` 完全限定格式或仅渠道名(配合 `chat_id` 字段)。任务失败时附 stderr 让用户可见错误。详见 [渠道文档](channels.md#cron-任务结果投递)。
74
+
65
75
  ### 权限系统
66
76
 
67
77
  三种权限模式:
@@ -223,6 +223,7 @@ illusion channel serve # 前台模式(查看日志)
223
223
  - **不支持消息编辑**——回复作为完整文本发送(打字状态指示处理中)
224
224
  - **2000 字符限制**——超长回复自动分多条发送,间隔 1.5s
225
225
  - **会话过期**——如果看到 `errcode=-14`,重新运行 `illusion channel login` 扫码
226
+ - **附件 AES 加密**——入站图片/视频/文件/语音经 CDN 传输时使用 AES-128-ECB + PKCS#7 加密,密钥按 `{msg_id}:{att_id}` 缓存,跨消息不会串键;依赖 `cryptography` 包(首次登录时自动安装)
226
227
 
227
228
  ### 快速开始(QQ)
228
229
 
@@ -268,33 +269,87 @@ illusion channel serve # 前台模式(查看日志)
268
269
 
269
270
  ```
270
271
  src/illusion/channels/
271
- ├── __init__.py # ChannelRunner(消息→agent 粘合层)+ maybe_spawn_channel_daemon
272
- ├── base.py # Channel 抽象基类 + InboundMessage + 打字状态方法
272
+ ├── __init__.py # ChannelRunner(消息→agent 粘合层)+ maybe_spawn_channel_daemon + kill_channel_daemon
273
+ ├── base.py # Channel 抽象基类 + InboundMessage + Attachment + 打字状态方法
273
274
  ├── base_commands.py # BaseCommandHandler(通用斜杠命令基类)
274
275
  ├── config.py # ChannelsConfig / FeishuChannelConfig / WeixinChannelConfig
276
+ ├── delivery.py # cron 任务结果投递到渠道(parse_deliver_to + deliver_to_channel)
275
277
  ├── serve.py # 'illusion channel serve' 入口(多渠道调度)
276
278
  ├── pid.py # PID 文件管理(避免重复启动守护进程)
277
279
  ├── feishu/
278
280
  │ ├── adapter.py # FeishuChannel:WS 连接、事件分发、准入控制
279
281
  │ ├── ws_client.py # lark-oapi WS 客户端包装
280
- │ ├── messaging.py # 卡片发送/更新、消息渲染
282
+ │ ├── messaging.py # 卡片发送/更新、消息渲染、resolve_receive_id
281
283
  │ ├── stream_editor.py # 流式卡片编辑器(节流 patch 更新)
282
284
  │ ├── session_map.py # 飞书会话存储(chat_id → 会话)
283
285
  │ └── commands.py # FeishuCommandHandler(继承 BaseCommandHandler)
284
286
  ├── weixin/
285
287
  │ ├── __init__.py # WEIXIN_DEPENDENCIES / ensure_weixin_dependencies
286
- │ ├── adapter.py # WeixinChannel:长轮询、准入、context_token、打字状态
287
- │ ├── ilink_api.py # iLink Bot API 客户端(扫码/收发/打字/分片)
288
+ │ ├── adapter.py # WeixinChannel:长轮询、准入、context_token、AES 密钥缓存、打字状态
289
+ │ ├── ilink_api.py # iLink Bot API 客户端(扫码/收发/打字/分片/CDN allowlist)
288
290
  │ ├── session_map.py # WeixinSessionStore(user_id → 会话)
289
291
  │ └── commands.py # WeixinCommandHandler(继承 BaseCommandHandler)
290
292
  ├── qq/
291
293
  │ ├── __init__.py # QQ_DEPENDENCIES / ensure_qq_dependencies
292
- │ ├── adapter.py # QQChannel:WS 连接、准入、消息标准化
294
+ │ ├── adapter.py # QQChannel:WS 连接、准入、消息标准化、附件 host 校验
293
295
  │ ├── ws_client.py # QQ Bot WS 网关客户端(心跳/重连/事件分发)
294
- │ ├── api.py # QQ Bot REST API 客户端(token/收发/上传)
296
+ │ ├── api.py # QQ Bot REST API 客户端(token/收发/上传/_parse_qq_response)
295
297
  │ ├── session_map.py # QQSessionStore(chat_id → 会话)
296
298
  │ └── commands.py # QQCommandHandler(继承 BaseCommandHandler)
297
299
  └── tools/
298
300
  ├── feishu_doc.py # feishu_doc_read / feishu_doc_create
299
- └── feishu_drive.py # feishu_drive_list / upload / download
301
+ ├── feishu_drive.py # feishu_drive_list / upload / download
302
+ └── media.py # SendMediaTool / ReceiveMediaTool(按渠道配置激活)
300
303
  ```
304
+
305
+ ### Cron 任务结果投递
306
+
307
+ cron 定时任务支持把执行结果投递到飞书/微信/QQ 渠道会话。配置在 `cron_tool.py` 的 `deliver_to` 字段中指定:
308
+
309
+ - **空值**——任务仅本地执行(终端输出)
310
+ - **`channel:chat_id`**——完全限定格式,例如 `feishu:oc_xxx`(群聊)、`feishu:ou_xxx`(私聊)、`weixin:wxid_xxx`、`qq:openid`
311
+ - **`channel`(仅渠道名)**——需配合 `chat_id` 字段,否则按"未指定"处理
312
+
313
+ 会话文件名前缀规则(用于从 `~/.illusion/channels/<channel>/sessions/` 提取真实 ID):
314
+
315
+ | 渠道 | 文件名格式 | 真实 ID |
316
+ |------|-----------|---------|
317
+ | 飞书私聊 | `u_ou_xxx.json` | `ou_xxx`(去掉 `u_` 前缀) |
318
+ | 飞书群聊 | `g_oc_xxx_ou_xxx.json` | `oc_xxx`(取 `oc_` 部分) |
319
+ | 微信 | `u_<wxid>.json` | `<wxid>`(去掉 `u_` 前缀) |
320
+ | QQ | `<openid>.json` | `<openid>`(文件名即 ID) |
321
+
322
+ 投递失败不影响任务状态,仅记录 warning 日志。任务失败时(非成功状态)会附上 stderr 让用户可见错误。
323
+
324
+ ### 消息并发处理
325
+
326
+ 当用户快速连发多条消息时,`ChannelRunner` 按 `chat_id` 加 `asyncio.Lock` 串行化 agent turn,避免会话历史覆盖:
327
+
328
+ - M1 拿到锁后开始跑 agent,M2/M3 进入队列等待
329
+ - M1 完成(或退出 pending_replies 等待)后,M2 拿到锁开始处理
330
+ - 权限/询问回复消息**不加锁**,立即 set_result 给等待中的 future,避免 300s 超时
331
+ - 不同 `chat_id` 之间完全并行,互不阻塞
332
+
333
+ 这保证同一会话的对话历史按顺序写入,不会出现"M1/A1 和 M3/A3 丢失、只保留 M2/A2"的 race condition。
334
+
335
+ ## 跨渠道文件传输
336
+
337
+ 当多个渠道同时启用时,LLM 能感知所有已启用渠道并跨渠道传输文件。
338
+
339
+ ### 工作机制
340
+
341
+ 1. **渠道感知提示词**:系统提示词中包含当前渠道身份 + 其他已启用渠道概览(含活跃会话列表)
342
+ 2. **跨渠道工具**:`send_to_channel` 工具用于跨渠道发文件,`send_media` 用于当前渠道内发文件
343
+ 3. **活跃会话列表**:每个渠道最近 5 个会话(按最后活跃时间排序),帮助 LLM 决定投递目标
344
+
345
+ ### 使用场景
346
+
347
+ - **PC 终端 → 渠道**:用户在 PC 终端说"把这个文件发到飞书的张三"
348
+ - **渠道 → 渠道**:用户在 QQ 上说"把这个文件发到微信"
349
+ - **渠道内(默认)**:用户在 QQ 上说"发这个文件"→ 用 `send_media` 发到当前 QQ 会话
350
+
351
+ ### 限制
352
+
353
+ - 跨渠道**文本**消息投递请使用 cron 任务(`send_to_channel` 仅支持文件)
354
+ - QQ 和微信的文件上传 API 较复杂,当前版本暂用文本提示替代,飞书已完整支持
355
+ - 活跃会话列表来自会话存储目录,从未交互过的用户不会出现在列表中
@@ -26,7 +26,7 @@ illusion plugin list # 列出插件
26
26
  illusion plugin install <source> # 安装插件
27
27
  illusion plugin uninstall <name> # 卸载插件
28
28
 
29
- # 渠道管理(飞书等消息渠道)
29
+ # 渠道管理(飞书/微信/QQ 消息渠道)
30
30
  illusion channel login # 交互式配置渠道(选择渠道 → 配置凭据)
31
31
  illusion channel serve # 前台运行渠道守护进程(监听消息)
32
32
  illusion channel status # 查看渠道状态(启用/连接/PID)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@illusion/terminal",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@illusion/terminal",
9
- "version": "0.2.7",
9
+ "version": "0.2.9",
10
10
  "dependencies": {
11
11
  "chalk": "^5.6.2",
12
12
  "ink": "^5.1.0",
@@ -24,5 +24,5 @@
24
24
  "tsx": "^4.19.2",
25
25
  "typescript": "^5.7.3"
26
26
  },
27
- "version": "0.2.7"
27
+ "version": "0.2.9"
28
28
  }
@@ -120,6 +120,24 @@ function ensureInitialized(): void {
120
120
  register(createGenericTool('mcp_auth', 'McpAuth'));
121
121
  register(createGenericTool('structured_output', 'StructuredOutput'));
122
122
 
123
+ // 渠道媒体工具(当前渠道内发/收文件)
124
+ register(createGenericTool('send_media', 'SendMedia'));
125
+ register(createGenericTool('receive_media', 'ReceiveMedia'));
126
+ // 跨渠道文件传输
127
+ register(createGenericTool('list_channel_sessions', 'ListChannelSessions'));
128
+ register(createGenericTool('send_to_channel', 'SendToChannel'));
129
+ // 飞书文档工具
130
+ register(createGenericTool('feishu_doc_read', 'FeishuDocRead'));
131
+ register(createGenericTool('feishu_doc_create', 'FeishuDocCreate'));
132
+ register(createGenericTool('feishu_doc_write', 'FeishuDocWrite'));
133
+ register(createGenericTool('feishu_doc_delete', 'FeishuDocDelete'));
134
+ // 飞书云盘工具
135
+ register(createGenericTool('feishu_drive_list', 'FeishuDriveList'));
136
+ register(createGenericTool('feishu_drive_upload', 'FeishuDriveUpload'));
137
+ register(createGenericTool('feishu_drive_download', 'FeishuDriveDownload'));
138
+ register(createGenericTool('feishu_drive_mkdir', 'FeishuDriveMkdir'));
139
+ register(createGenericTool('feishu_drive_delete', 'FeishuDriveDelete'));
140
+
123
141
  // 专用工具(需要特殊处理)
124
142
  register({
125
143
  name: 'todo_write',
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  /** 当前版本号 */
9
- export const VERSION = '0.2.7';
9
+ export const VERSION = '0.2.9';
@@ -7,6 +7,18 @@
7
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap" rel="stylesheet" />
9
9
  <title>Illusion Code</title>
10
+ <script>
11
+ // 预加载主题:在 React 渲染前应用 dark 类,避免深色模式 FOUC 闪烁
12
+ (function() {
13
+ try {
14
+ var stored = localStorage.getItem('illusion-theme');
15
+ var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
16
+ if (stored === 'dark' || (stored !== 'light' && prefersDark)) {
17
+ document.documentElement.classList.add('dark');
18
+ }
19
+ } catch (e) { /* 忽略 */ }
20
+ })();
21
+ </script>
10
22
  </head>
11
23
  <body>
12
24
  <div id="root"></div>
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@illusion/web",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@illusion/web",
9
- "version": "0.2.7",
9
+ "version": "0.2.9",
10
10
  "dependencies": {
11
11
  "highlight.js": "^11.11.0",
12
12
  "react": "^18.3.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@illusion/web",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -29,15 +29,32 @@ const BOTTOM_THRESHOLD_PX = 80;
29
29
  * - thinkingItems:工具调用 + 中间 assistant 消息(归入"思考过程"可折叠区)
30
30
  * - finalAssistant:最后一条含文本的 assistant 消息(始终可见,其 reasoning 归入思考区)
31
31
  *
32
+ * 流式阶段(streaming=true)所有 assistant 消息都视作思考过程的一部分:
33
+ * 最终回复由 StreamingBuffer 实时展示,避免中间 LLM 消息被误判为最终回复,
34
+ * 导致后续工具调用显示在消息上方、以及复制/回退按钮闪烁等问题。
35
+ *
32
36
  * @param items - 单轮转录项列表
37
+ * @param streaming - 是否处于流式输出阶段
33
38
  * @returns 拆分结果
34
39
  */
35
- function splitTurnItems(items: TranscriptItem[]) {
40
+ function splitTurnItems(items: TranscriptItem[], streaming: boolean = false) {
36
41
  const userItems: TranscriptItem[] = [];
37
42
  const thinkingItems: TranscriptItem[] = [];
38
43
  let finalAssistant: TranscriptItem | null = null;
39
44
 
40
- // 找到最后一条有非空 text 的 assistant 消息作为"最终回复"
45
+ // 流式阶段:所有 assistant 消息归入思考过程,不区分"最终回复"
46
+ if (streaming) {
47
+ for (const item of items) {
48
+ if (item.role === 'user') {
49
+ userItems.push(item);
50
+ } else {
51
+ thinkingItems.push(item);
52
+ }
53
+ }
54
+ return { userItems, thinkingItems, finalAssistant };
55
+ }
56
+
57
+ // 完成态:找最后一条有非空 text 的 assistant 消息作为"最终回复"
41
58
  let lastAssistantIdx = -1;
42
59
  for (let i = items.length - 1; i >= 0; i--) {
43
60
  if (items[i]!.role === 'assistant' && items[i]!.text.trim()) {
@@ -278,6 +295,18 @@ export default function ChatArea({
278
295
  el.scrollTop = el.scrollHeight;
279
296
  }, [staticItems, assistantBuffer, streamingReasoning, pendingToolCalls, modal]);
280
297
 
298
+ // 用户发送新消息时强制回到底部(忽略用户是否手动上滑过)
299
+ const userMsgCount = useMemo(() => staticItems.filter((i) => i.role === 'user').length, [staticItems]);
300
+ const prevUserMsgCountRef = useRef(0);
301
+ useEffect(() => {
302
+ if (userMsgCount > prevUserMsgCountRef.current) {
303
+ userScrolledUpRef.current = false;
304
+ const el = scrollRef.current;
305
+ if (el) el.scrollTop = el.scrollHeight;
306
+ }
307
+ prevUserMsgCountRef.current = userMsgCount;
308
+ }, [userMsgCount]);
309
+
281
310
  // 新会话或恢复后重置展开状态
282
311
  useEffect(() => {
283
312
  setExpanded(false);
@@ -329,8 +358,8 @@ export default function ChatArea({
329
358
  {visibleTurns.map((turn, visIdx) => {
330
359
  const turnIdx = turnOffset + visIdx;
331
360
  const isLastTurn = turnIdx === turns.length - 1;
332
- const { userItems, thinkingItems, finalAssistant } = splitTurnItems(turn);
333
361
  const autoExpand = busy && isLastTurn;
362
+ const { userItems, thinkingItems, finalAssistant } = splitTurnItems(turn, autoExpand);
334
363
  const turnsToRewind = turns.length - turnIdx;
335
364
  return (
336
365
  <div key={turnIdx} className={visIdx > 0 ? 'mt-12' : ''}>
@@ -19,6 +19,7 @@ import rehypeHighlight from 'rehype-highlight';
19
19
  import rehypeRaw from 'rehype-raw';
20
20
  import 'highlight.js/styles/github.css';
21
21
  import { t, type UiLanguage } from '../i18n';
22
+ import { toolDisplayName } from '../utils/toolDisplayName';
22
23
  import type { TranscriptItem, PendingToolCall } from '../types/protocol';
23
24
 
24
25
  /** 从 rehype-highlight 注入的 className 中提取语言名 */
@@ -266,7 +267,9 @@ export default function MessageBubble({ item, toolInputMap, lang = 'zh-CN', onRe
266
267
  */
267
268
  function ToolResultBubble({ name, text, isError, toolInput }: { name: string; text: string; isError?: boolean; toolInput?: Record<string, unknown> }) {
268
269
  const [open, setOpen] = useState(false);
270
+ // summarizeInput 用原名做大小写不敏感匹配,显示名用映射后的友好名
269
271
  const summary = summarizeInput(name, toolInput, name);
272
+ const displayName = toolDisplayName(name);
270
273
 
271
274
  return (
272
275
  <div className="py-1.5">
@@ -276,7 +279,7 @@ function ToolResultBubble({ name, text, isError, toolInput }: { name: string; te
276
279
  >
277
280
  <span className={`inline-block w-2 h-2 rounded-full shrink-0 mt-1.5 ${isError ? 'bg-danger' : 'bg-primary'}`} />
278
281
  <span>
279
- <span className={`font-medium font-mono ${isError ? 'text-danger' : 'text-content-primary'}`}>{name}</span>
282
+ <span className={`font-medium font-mono ${isError ? 'text-danger' : 'text-content-primary'}`}>{displayName}</span>
280
283
  {!open && summary && <span className={`text-xs ${isError ? 'text-danger' : 'text-content-disabled'}`}>({summary})</span>}
281
284
  {isError && <span className="text-xs text-danger font-medium"> ERROR</span>}
282
285
  </span>
@@ -367,11 +370,12 @@ export function ReasoningContent({ text }: { text: string }) {
367
370
  */
368
371
  export function PendingToolBubble({ call }: { call: PendingToolCall }) {
369
372
  const summary = summarizeInput(call.tool_name, call.tool_input, call.tool_name);
373
+ const displayName = toolDisplayName(call.tool_name);
370
374
  return (
371
375
  <div className="py-1.5 flex items-start gap-2">
372
376
  <span className="inline-block w-2 h-2 rounded-full bg-primary animate-pulse-scale shrink-0 mt-1.5" />
373
377
  <span className="text-sm">
374
- <span className="font-medium font-mono text-content-primary">{call.tool_name}</span>
378
+ <span className="font-medium font-mono text-content-primary">{displayName}</span>
375
379
  {summary && <span className="text-xs text-content-disabled">({summary})</span>}
376
380
  </span>
377
381
  </div>
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
12
12
  import { t, type UiLanguage } from '../i18n';
13
+ import { toolDisplayName } from '../utils/toolDisplayName';
13
14
 
14
15
  /**
15
16
  * 问题选项接口
@@ -61,6 +62,8 @@ interface PermissionCardProps {
61
62
  */
62
63
  export function PermissionCard({ modal, lang, onRespond }: PermissionCardProps) {
63
64
  const toolName = String(modal.tool_name ?? 'tool');
65
+ // 显示用友好名;onRespond 回调仍传原始 toolName 给后端识别
66
+ const displayToolName = toolDisplayName(toolName);
64
67
  const reason = modal.reason ? String(modal.reason) : null;
65
68
  const requestId = String(modal.request_id ?? '');
66
69
 
@@ -75,7 +78,7 @@ export function PermissionCard({ modal, lang, onRespond }: PermissionCardProps)
75
78
  <div className="px-4 py-3">
76
79
  <div className="text-sm text-content-primary mb-1">
77
80
  {lang === 'zh-CN' ? '允许使用工具 ' : 'Allow '}
78
- <span className="font-mono font-medium text-primary">{toolName}</span>
81
+ <span className="font-mono font-medium text-primary">{displayToolName}</span>
79
82
  <span>?</span>
80
83
  </div>
81
84
  {reason && (
@@ -359,7 +362,7 @@ export function QuestionCard({ modal, lang, onRespond }: QuestionCardProps) {
359
362
  <div className="px-4 py-3">
360
363
  {typeof modal.tool_name === 'string' && modal.tool_name && (
361
364
  <div className="text-xs text-content-secondary mb-3">
362
- Tool: <span className="font-mono text-primary">{modal.tool_name}</span>
365
+ Tool: <span className="font-mono text-primary">{toolDisplayName(modal.tool_name)}</span>
363
366
  </div>
364
367
  )}
365
368
 
@@ -402,7 +405,7 @@ export function QuestionCard({ modal, lang, onRespond }: QuestionCardProps) {
402
405
  </button>
403
406
  );
404
407
  })}
405
- {/* "其他"选项:内联输入框,带序号与普通选项格式一致,沙箱等 noCustomInput 场景不显示 */}
408
+ {/* "其他"选项:内联输入框,与普通选项格式一致(无序号),沙箱等 noCustomInput 场景不显示 */}
406
409
  {!noCustomInput && (
407
410
  <div
408
411
  className={`w-full text-left px-3 py-2.5 rounded-lg text-sm transition-colors cursor-pointer flex items-start gap-2.5 ${
@@ -431,7 +434,7 @@ export function QuestionCard({ modal, lang, onRespond }: QuestionCardProps) {
431
434
  <span className={`text-sm font-medium shrink-0 ${
432
435
  (isMultiSelect && selectedIndices.has(otherIdx)) || (!isMultiSelect && isOtherFocused) ? 'text-primary' : ''
433
436
  }`}>
434
- {otherIdx + 1}. {lang === 'zh-CN' ? '其他' : 'Other'}
437
+ {lang === 'zh-CN' ? '其他' : 'Other'}
435
438
  </span>{' '}
436
439
  {isOtherFocused ? (
437
440
  <input
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { useState } from 'react';
16
16
  import { t, type UiLanguage } from '../i18n';
17
+ import { useTheme } from '../hooks/useTheme';
17
18
  import TodoPanel from './TodoPanel';
18
19
  import type { McpServerSnapshot, PluginSnapshot, RuleSnapshot, SkillSnapshot, TodoItemSnapshot } from '../types/protocol';
19
20
 
@@ -59,6 +60,8 @@ export default function RightPanel({
59
60
  lang, status, connected, busy, collapsed, onToggle, todoItems,
60
61
  skills, plugins, rules, mcpServers, width = 260,
61
62
  }: RightPanelProps) {
63
+ // 主题(深色/浅色)— 在折叠判断前调用以保证 hook 始终执行
64
+ const { theme, toggleTheme } = useTheme();
62
65
  // 上下文使用量
63
66
  const contextWindow = Number(status?.context_window ?? 0);
64
67
  const contextTokens = Number(status?.context_tokens ?? 0);
@@ -88,22 +91,37 @@ export default function RightPanel({
88
91
 
89
92
  return (
90
93
  <aside className="glass-panel border-l border-white/30 flex flex-col h-full shrink-0 overflow-y-auto select-none" style={{ width: `${width}px` }}>
91
- {/* 折叠按钮 */}
92
- <div className="px-5 pt-3 pb-1 flex justify-end">
94
+ {/* 标题行:主题切换按钮 + 居中标题 + 折叠按钮(3 列 grid 严格居中) */}
95
+ <div className="grid grid-cols-3 items-center px-5 pt-3 pb-2">
96
+ <button onClick={toggleTheme} title={t(lang, 'toggle_theme')}
97
+ aria-label={t(lang, 'toggle_theme')}
98
+ className="justify-self-start w-7 h-7 flex items-center justify-center rounded-lg text-content-secondary glass-option-hover hover:text-content-primary transition-colors cursor-pointer">
99
+ {theme === 'dark' ? (
100
+ /* 太阳图标(深色模式下点击切换到浅色) */
101
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
102
+ <circle cx="8" cy="8" r="3" />
103
+ <path d="M8 1.5v1.5M8 13v1.5M1.5 8h1.5M13 8h1.5M3.4 3.4l1.1 1.1M11.5 11.5l1.1 1.1M3.4 12.6l1.1-1.1M11.5 4.5l1.1-1.1" />
104
+ </svg>
105
+ ) : (
106
+ /* 月亮图标(浅色模式下点击切换到深色) */
107
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
108
+ <path d="M13 8.5a5 5 0 0 1-5.5-5.5 5 5 0 1 0 5.5 5.5z" />
109
+ </svg>
110
+ )}
111
+ </button>
112
+ <span className="justify-self-center font-display font-bold text-content-primary text-sm tracking-wider">{t(lang, 'management_title')}</span>
93
113
  <button onClick={onToggle} title={t(lang, 'collapse_panel')}
94
- className="w-7 h-7 flex items-center justify-center rounded-lg text-content-secondary glass-option-hover hover:text-content-primary transition-colors cursor-pointer">
114
+ className="justify-self-end w-7 h-7 flex items-center justify-center rounded-lg text-content-secondary glass-option-hover hover:text-content-primary transition-colors cursor-pointer">
95
115
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
96
116
  <path d="M6 3l5 5-5 5" />
97
117
  </svg>
98
118
  </button>
99
119
  </div>
100
120
 
101
- {/* Todo 列表 */}
102
- {todoItems.length > 0 && (
103
- <div className="px-3 pb-3">
104
- <TodoPanel items={todoItems} />
105
- </div>
106
- )}
121
+ {/* Todo 列表(始终显示,空列表显示占位) */}
122
+ <div className="px-3 pb-3">
123
+ <TodoPanel items={todoItems} lang={lang} />
124
+ </div>
107
125
 
108
126
  {/* Skills */}
109
127
  {skills.length > 0 && (
@@ -208,7 +226,7 @@ function CollapsibleSection({
208
226
  <path d="M6 3l5 5-5 5" />
209
227
  </svg>
210
228
  <span className="text-xs font-semibold text-content-primary tracking-wide">{title}</span>
211
- <span className="text-[10px] text-content-secondary bg-white/50 px-1.5 py-0.5 rounded-full tabular-nums">{count}</span>
229
+ <span className="text-[10px] text-content-secondary bg-[var(--badge-bg)] px-1.5 py-0.5 rounded-full tabular-nums">{count}</span>
212
230
  {subtitle && <span className="text-xs text-content-disabled ml-auto">{subtitle}</span>}
213
231
  </button>
214
232
  <div className="grid transition-[grid-template-rows] duration-200 ease-out" style={{ gridTemplateRows: collapsed ? '0fr' : '1fr' }}>
@@ -237,7 +255,7 @@ function ItemRow({ name, description, tag }: { name: string; description: string
237
255
  >
238
256
  <span className="text-content-primary font-medium truncate flex-1 text-left">{name}</span>
239
257
  {tag && (
240
- <span className="text-[10px] text-primary/80 bg-white/50 px-1.5 py-0.5 rounded-full font-medium shrink-0">{tag}</span>
258
+ <span className="text-[10px] text-primary/80 bg-[var(--badge-bg)] px-1.5 py-0.5 rounded-full font-medium shrink-0">{tag}</span>
241
259
  )}
242
260
  </button>
243
261
  {expanded && hasDesc && (