tagword-codeagent 1.1.36__tar.gz → 1.2.0__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 (274) hide show
  1. {tagword_codeagent-1.1.36/tagword_codeagent.egg-info → tagword_codeagent-1.2.0}/PKG-INFO +2 -2
  2. tagword_codeagent-1.2.0/codeagent/__init__.py +25 -0
  3. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/cli/entrypoint.py +2 -1
  4. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/cli/main.py +85 -34
  5. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/attachments.py +12 -16
  6. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/audio_models.py +2 -2
  7. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/bootstrap.py +1 -2
  8. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/default_presets.py +6 -7
  9. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/folder_picker.py +5 -6
  10. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/image_understanding.py +7 -7
  11. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/paths.py +79 -15
  12. tagword_codeagent-1.2.0/codeagent/core/pricing.py +67 -0
  13. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/process_ports.py +14 -14
  14. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/speech_synth.py +18 -21
  15. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/team_config.py +9 -9
  16. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/team_manager.py +10 -10
  17. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/usage_billing.py +14 -9
  18. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/vision_models.py +7 -12
  19. tagword_codeagent-1.2.0/codeagent/runtime/agent_bootstrap.py +121 -0
  20. tagword_codeagent-1.2.0/codeagent/runtime/agent_handle.py +110 -0
  21. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/llm_worker.py +2 -3
  22. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/orchestrator.py +163 -161
  23. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/prompt_enrichment.py +10 -9
  24. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/__init__.py +5 -4
  25. tagword_codeagent-1.2.0/codeagent/server/a2a/__init__.py +28 -0
  26. tagword_codeagent-1.2.0/codeagent/server/a2a/agent_card.py +77 -0
  27. tagword_codeagent-1.2.0/codeagent/server/a2a/handlers.py +217 -0
  28. tagword_codeagent-1.2.0/codeagent/server/a2a/models.py +144 -0
  29. tagword_codeagent-1.2.0/codeagent/server/a2a/streaming.py +260 -0
  30. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/app_factory.py +37 -25
  31. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/attachment_api.py +8 -8
  32. tagword_codeagent-1.2.0/codeagent/server/auto_continue.py +300 -0
  33. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/chat_turn.py +63 -17
  34. tagword_codeagent-1.2.0/codeagent/server/evidence.py +335 -0
  35. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/hub.py +3 -5
  36. tagword_codeagent-1.2.0/codeagent/server/team_engine.py +771 -0
  37. tagword_codeagent-1.2.0/codeagent/server/team_worker.py +168 -0
  38. tagword_codeagent-1.2.0/codeagent/server/webui_api_app.py +375 -0
  39. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_agentmeta.py +152 -0
  40. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_agents.py +292 -0
  41. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_auth.py +104 -0
  42. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_cronallow.py +104 -0
  43. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_envllm.py +252 -0
  44. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_files.py +285 -0
  45. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_git.py +695 -0
  46. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_health.py +86 -0
  47. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_mcp.py +390 -0
  48. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_projects.py +481 -0
  49. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_sessions.py +197 -0
  50. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_skills.py +255 -0
  51. tagword_codeagent-1.2.0/codeagent/server/webui_handlers_teams.py +224 -0
  52. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/skills/select.py +0 -1
  53. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/__pycache__/auth_impl.cpython-313.pyc +0 -0
  54. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/auth_impl.py +8 -4
  55. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01c-session-thinking.js +9 -5
  56. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-ws-connect.js +1 -1
  57. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/06-chat.js +6 -1
  58. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11c-cron-card.js +5 -0
  59. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/12-plugins.js +12 -1
  60. tagword_codeagent-1.2.0/codeagent/web/static/17-agents-list.css +396 -0
  61. tagword_codeagent-1.2.0/codeagent/web/static/17-agents-list.js +221 -0
  62. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/18-agent-mgr.js +13 -3
  63. tagword_codeagent-1.2.0/codeagent/web/static/25-teams-panel.css +121 -0
  64. tagword_codeagent-1.2.0/codeagent/web/static/25-teams-panel.js +537 -0
  65. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/body.html +87 -34
  66. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/pyproject.toml +2 -2
  67. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0/tagword_codeagent.egg-info}/PKG-INFO +2 -2
  68. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/tagword_codeagent.egg-info/SOURCES.txt +87 -1
  69. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/tagword_codeagent.egg-info/requires.txt +1 -1
  70. tagword_codeagent-1.2.0/tests/test_a2a.py +213 -0
  71. tagword_codeagent-1.2.0/tests/test_a2a_streaming.py +154 -0
  72. tagword_codeagent-1.2.0/tests/test_agent_discovery_hollow.py +101 -0
  73. tagword_codeagent-1.2.0/tests/test_agent_file_safety.py +72 -0
  74. tagword_codeagent-1.2.0/tests/test_agent_registry_bridge.py +151 -0
  75. tagword_codeagent-1.2.0/tests/test_agent_root_consistency.py +117 -0
  76. tagword_codeagent-1.2.0/tests/test_archive_probe.py +97 -0
  77. tagword_codeagent-1.2.0/tests/test_attachment_safety.py +60 -0
  78. tagword_codeagent-1.2.0/tests/test_attachments.py +112 -0
  79. tagword_codeagent-1.2.0/tests/test_auth_pure_fns.py +95 -0
  80. tagword_codeagent-1.2.0/tests/test_auth_routes.py +87 -0
  81. tagword_codeagent-1.2.0/tests/test_auto_continue.py +422 -0
  82. tagword_codeagent-1.2.0/tests/test_cli_chat_project_id.py +83 -0
  83. tagword_codeagent-1.2.0/tests/test_compact_state.py +71 -0
  84. tagword_codeagent-1.2.0/tests/test_default_presets.py +110 -0
  85. tagword_codeagent-1.2.0/tests/test_env.py +111 -0
  86. tagword_codeagent-1.2.0/tests/test_env_chat.py +99 -0
  87. tagword_codeagent-1.2.0/tests/test_evidence.py +338 -0
  88. tagword_codeagent-1.2.0/tests/test_git_provider.py +91 -0
  89. tagword_codeagent-1.2.0/tests/test_git_ssh_helpers.py +257 -0
  90. tagword_codeagent-1.2.0/tests/test_history_json_safe.py +62 -0
  91. tagword_codeagent-1.2.0/tests/test_history_rows.py +91 -0
  92. tagword_codeagent-1.2.0/tests/test_hub.py +92 -0
  93. tagword_codeagent-1.2.0/tests/test_image_understanding.py +102 -0
  94. tagword_codeagent-1.2.0/tests/test_list_agents_tool.py +81 -0
  95. tagword_codeagent-1.2.0/tests/test_listen_port.py +74 -0
  96. tagword_codeagent-1.2.0/tests/test_metas.py +111 -0
  97. tagword_codeagent-1.2.0/tests/test_nudge_edge.py +73 -0
  98. tagword_codeagent-1.2.0/tests/test_orchestrator.py +93 -0
  99. tagword_codeagent-1.2.0/tests/test_paths_files.py +83 -0
  100. tagword_codeagent-1.2.0/tests/test_paths_safety.py +88 -0
  101. tagword_codeagent-1.2.0/tests/test_persist_summarize.py +92 -0
  102. tagword_codeagent-1.2.0/tests/test_persona_fallback.py +80 -0
  103. tagword_codeagent-1.2.0/tests/test_persona_hash.py +65 -0
  104. tagword_codeagent-1.2.0/tests/test_persona_init.py +64 -0
  105. tagword_codeagent-1.2.0/tests/test_preset_support.py +75 -0
  106. tagword_codeagent-1.2.0/tests/test_pricing.py +172 -0
  107. tagword_codeagent-1.2.0/tests/test_project_dirs.py +54 -0
  108. tagword_codeagent-1.2.0/tests/test_prompt_enrichment.py +42 -0
  109. tagword_codeagent-1.2.0/tests/test_reply_summary.py +85 -0
  110. tagword_codeagent-1.2.0/tests/test_self_healing.py +88 -0
  111. tagword_codeagent-1.2.0/tests/test_selfheal_heal.py +55 -0
  112. tagword_codeagent-1.2.0/tests/test_selfheal_health.py +102 -0
  113. tagword_codeagent-1.2.0/tests/test_server_init.py +149 -0
  114. tagword_codeagent-1.2.0/tests/test_server_pure.py +64 -0
  115. tagword_codeagent-1.2.0/tests/test_session_history.py +114 -0
  116. tagword_codeagent-1.2.0/tests/test_settings.py +100 -0
  117. tagword_codeagent-1.2.0/tests/test_skills_select.py +83 -0
  118. tagword_codeagent-1.2.0/tests/test_speech_synth_pure.py +115 -0
  119. tagword_codeagent-1.2.0/tests/test_task_split.py +83 -0
  120. tagword_codeagent-1.2.0/tests/test_team_concurrency.py +150 -0
  121. tagword_codeagent-1.2.0/tests/test_team_config.py +124 -0
  122. tagword_codeagent-1.2.0/tests/test_team_engine.py +398 -0
  123. tagword_codeagent-1.2.0/tests/test_token_counter.py +86 -0
  124. tagword_codeagent-1.2.0/tests/test_tts_voices.py +95 -0
  125. tagword_codeagent-1.2.0/tests/test_usage_billing.py +105 -0
  126. tagword_codeagent-1.2.0/tests/test_webui_pure_fns.py +133 -0
  127. tagword_codeagent-1.2.0/tests/test_webui_pure_fns2.py +79 -0
  128. tagword_codeagent-1.2.0/tests/test_write_env.py +44 -0
  129. tagword_codeagent-1.1.36/codeagent/__init__.py +0 -16
  130. tagword_codeagent-1.1.36/codeagent/core/pricing.py +0 -107
  131. tagword_codeagent-1.1.36/codeagent/server/team_engine.py +0 -243
  132. tagword_codeagent-1.1.36/codeagent/server/webui_api_app.py +0 -3350
  133. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/LICENSE +0 -0
  134. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/README.md +0 -0
  135. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/__main__.py +0 -0
  136. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/cli/__init__.py +0 -0
  137. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/__init__.py +0 -0
  138. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/env.py +0 -0
  139. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/models.py +0 -0
  140. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/seed_bridge.py +0 -0
  141. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/settings.py +0 -0
  142. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/token_counter.py +0 -0
  143. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/core/tts_voices.py +0 -0
  144. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/memory/diary.py +0 -0
  145. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/README.md +0 -0
  146. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/agent.md +0 -0
  147. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/identity.md +0 -0
  148. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/memory.md +0 -0
  149. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/skills.md +0 -0
  150. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/soul.md +0 -0
  151. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/tools.md +0 -0
  152. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/persona_defaults/user.md +0 -0
  153. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/__init__.py +0 -0
  154. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/agent_context.py +0 -0
  155. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/agent_runtime.py +0 -0
  156. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/compact_prompt.py +0 -0
  157. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/compact_state.py +0 -0
  158. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/session_manager.py +0 -0
  159. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/task_split.py +0 -0
  160. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/turn_loop.py +0 -0
  161. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/runtime/worker.py +0 -0
  162. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/icon.png +0 -0
  163. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/self_healing.py +0 -0
  164. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/server/tts_api.py +0 -0
  165. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/skills/__init__.py +0 -0
  166. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/tools/__init__.py +0 -0
  167. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/tools/agent_tools.py +0 -0
  168. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/__init__.py +0 -0
  169. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/__pycache__/__init__.cpython-311.pyc +0 -0
  170. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/__pycache__/__init__.cpython-313.pyc +0 -0
  171. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/__pycache__/app.cpython-313.pyc +0 -0
  172. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/__pycache__/auth_impl.cpython-311.pyc +0 -0
  173. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/00-marked.min.js +0 -0
  174. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/00-mermaid.min.js +0 -0
  175. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/00-purify.min.js +0 -0
  176. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/00-storage.js +0 -0
  177. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/00-utils.js +0 -0
  178. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01-theme.css +0 -0
  179. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01a-scroll.js +0 -0
  180. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01b-think.js +0 -0
  181. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01c-session-identity.js +0 -0
  182. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01c-session-tree-menus.js +0 -0
  183. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01c-session-tree.js +0 -0
  184. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01c-session-utilities.js +0 -0
  185. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01c-vision-model.js +0 -0
  186. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01d-image-gen-model.js +0 -0
  187. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01e-audio-model.js +0 -0
  188. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01f-compose-ui.js +0 -0
  189. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01g-tts-settings.js +0 -0
  190. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01h-music-model.js +0 -0
  191. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01i-model-stack.js +0 -0
  192. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01j-video-gen-model.js +0 -0
  193. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01k-model-stack-bootstrap.js +0 -0
  194. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01k-model-stack-state.js +0 -0
  195. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/01q-inflight.js +0 -0
  196. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/02-reset.css +0 -0
  197. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/02-session-ui.js +0 -0
  198. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03-layout-p1.css +0 -0
  199. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03-layout-p2.css +0 -0
  200. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03a-links-stream.js +0 -0
  201. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03b-stream-text.js +0 -0
  202. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03c-render-bubble.js +0 -0
  203. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03d-agent-bubble.js +0 -0
  204. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03e-tool-trace.js +0 -0
  205. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03f-timeline.js +0 -0
  206. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/03g-bubble-tts.js +0 -0
  207. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p1.css +0 -0
  208. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p2b.css +0 -0
  209. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p3.css +0 -0
  210. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p4.css +0 -0
  211. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p5.css +0 -0
  212. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p6.css +0 -0
  213. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p7.css +0 -0
  214. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p8.css +0 -0
  215. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-components-p9.css +0 -0
  216. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/04-ws-helpers.js +0 -0
  217. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05-chat-p1.css +0 -0
  218. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05-chat-p2.css +0 -0
  219. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05-chat-p3.css +0 -0
  220. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05a-session-history.js +0 -0
  221. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05b-session-list.js +0 -0
  222. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05c-dialog.js +0 -0
  223. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05d-project-create.js +0 -0
  224. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/05e-project-remote.js +0 -0
  225. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/06-utilities.css +0 -0
  226. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/06b-attachments.js +0 -0
  227. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/06c-camera.js +0 -0
  228. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/07-activity-bar.css +0 -0
  229. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09-git-p1.css +0 -0
  230. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09-git-p2.css +0 -0
  231. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09a-init-bind.js +0 -0
  232. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09b-panel-toggle.js +0 -0
  233. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09c-api-refresh.js +0 -0
  234. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09d-status-log.js +0 -0
  235. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09e-commit.js +0 -0
  236. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/09f-remote.js +0 -0
  237. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11a-cron-parse.js +0 -0
  238. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11b-cron-panel.js +0 -0
  239. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11d-cron-form.js +0 -0
  240. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11e-env-config.js +0 -0
  241. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11f-env-llm-a-state.js +0 -0
  242. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11f-env-llm-b-form.js +0 -0
  243. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11f-env-llm-c-board.js +0 -0
  244. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11g-env-paths-git.js +0 -0
  245. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11h-env-mcp-a-state.js +0 -0
  246. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11h-env-mcp-b-form.js +0 -0
  247. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/11h-env-mcp-c-board.js +0 -0
  248. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13a-skills.js +0 -0
  249. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13a-todo-utils.js +0 -0
  250. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13b-todo-fetch.js +0 -0
  251. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13b-tools.js +0 -0
  252. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13c-skills-panel.js +0 -0
  253. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13c-todo-crud.js +0 -0
  254. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/13d-file-browser.js +0 -0
  255. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/14-activity-bar.js +0 -0
  256. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/15-plans.css +0 -0
  257. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/15-plans.js +0 -0
  258. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/16-mobile.css +0 -0
  259. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/16-mobile.js +0 -0
  260. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/17-preset-ux.css +0 -0
  261. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/18-agent-mgr.css +0 -0
  262. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/24-health-dashboard.js +0 -0
  263. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/99-brand-polish.css +0 -0
  264. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/setup/setup_form.html +0 -0
  265. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/setup/setup_head.html +0 -0
  266. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/setup/setup_tail.html +0 -0
  267. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/static/setup/setup_test.html +0 -0
  268. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/web_login.html +0 -0
  269. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/codeagent/web/webui.html +0 -0
  270. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/setup.cfg +0 -0
  271. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/tagword_codeagent.egg-info/dependency_links.txt +0 -0
  272. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/tagword_codeagent.egg-info/entry_points.txt +0 -0
  273. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/tagword_codeagent.egg-info/top_level.txt +0 -0
  274. {tagword_codeagent-1.1.36 → tagword_codeagent-1.2.0}/tests/test_webui_auth_cookie.py +1 -1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tagword-codeagent
3
- Version: 1.1.36
3
+ Version: 1.2.0
4
4
  Summary: Autonomous AI coding agent driven by Markdown config — DeepSeek-ready, CLI + Web UI, 45+ tools, self-hosted
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/tagword/codeagent
@@ -26,7 +26,7 @@ Requires-Python: >=3.9
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: seed-kernel
29
- Requires-Dist: seed-model-providers
29
+ Requires-Dist: seed-model-providers>=1.0.3
30
30
  Requires-Dist: seed-toolbox[code]
31
31
  Requires-Dist: uvicorn[standard]>=0.30.0
32
32
  Requires-Dist: starlette>=0.37.0
@@ -0,0 +1,25 @@
1
+ """
2
+ CodeAgent — Personality layer
3
+
4
+ Architecture:
5
+ - seed/ : Core engine (LLM execution, tool system, session management)
6
+ - codeagent/ : Personality layer (CLI, server, web UI, skills, per-agent config)
7
+ """
8
+
9
+ # 版本号从包元数据动态读取,避免 pyproject.toml 与 __init__ 写死值不同步
10
+ # 注意:发行名(tagword-codeagent)与历史 editable 安装的 dist-info 名(codeagent)
11
+ # 可能不一致,两者都尝试,避免静默回落到 0.0.0。
12
+ def _resolve_version() -> str:
13
+ from importlib.metadata import PackageNotFoundError
14
+ from importlib.metadata import version as _pkg_version
15
+
16
+ for dist_name in ("tagword-codeagent", "codeagent"):
17
+ try:
18
+ return _pkg_version(dist_name)
19
+ except PackageNotFoundError:
20
+ continue
21
+ return "0.0.0"
22
+
23
+
24
+ __version__ = _resolve_version()
25
+
@@ -3,9 +3,10 @@ import argparse
3
3
  import sys
4
4
  from datetime import datetime
5
5
 
6
+ from seed_tools import setup_builtin_tools
7
+
6
8
  from seed.core.persistence import ensure_session_dir, list_sessions, save_session
7
9
  from seed.core.routing import find_commands, get_all_commands
8
- from seed_tools import setup_builtin_tools
9
10
 
10
11
  # Prefer ``python -m codeagent`` / ``codeagent.cli.main`` — kept for compatibility.
11
12
 
@@ -184,7 +184,11 @@ def main():
184
184
  formatter_class=argparse.RawDescriptionHelpFormatter
185
185
  )
186
186
 
187
- parser.add_argument('--version', action='version', version='CodeAgent v1.1.0')
187
+ from codeagent import __version__ as _codeagent_version
188
+
189
+ parser.add_argument(
190
+ '--version', action='version', version=f'CodeAgent v{_codeagent_version}'
191
+ )
188
192
  subparsers = parser.add_subparsers(dest='command', help='Available commands')
189
193
 
190
194
  # Run command
@@ -307,6 +311,15 @@ def main():
307
311
  metavar='PRESET_ID',
308
312
  help='Video generation preset for video_generate (or CODEAGENT_VIDEO_GEN_PRESET_ID)',
309
313
  )
314
+ chat_parser.add_argument(
315
+ '--project-id',
316
+ default=None,
317
+ help=(
318
+ 'Project id this chat belongs to. When set, the session is registered '
319
+ 'under that project in the Web UI (instead of 未分类), so dispatched '
320
+ 'tasks stay grouped with their project.'
321
+ ),
322
+ )
310
323
 
311
324
  serve_parser = subparsers.add_parser(
312
325
  'serve',
@@ -471,9 +484,8 @@ def _cli_vision_preset_id(args) -> str:
471
484
 
472
485
 
473
486
  def _cli_stage_path_attachment(path: str, *, agent_id: str, session_id: str):
474
- from pathlib import Path
475
-
476
487
  import mimetypes
488
+ from pathlib import Path
477
489
 
478
490
  from codeagent.core.attachments import save_attachment
479
491
 
@@ -541,10 +553,8 @@ def _cli_startup_attachments(args, *, agent_id: str, session_id: str, project_ro
541
553
 
542
554
  def _cli_handle_slash_attach(line: str, *, agent_id: str, session_id: str, project_root):
543
555
  """Return (handled, attachments, image_dir_tag, message_text)."""
544
- from pathlib import Path
545
556
 
546
557
  from codeagent.core import env as ca_env
547
- from codeagent.core.attachments import scan_image_directory
548
558
 
549
559
  low = line.strip().lower()
550
560
  if low in ('/clear-vision', '/clear_vision'):
@@ -575,14 +585,19 @@ def _cmd_chat_llm(args):
575
585
 
576
586
  from seed_tools import setup_builtin_tools
577
587
 
588
+ from codeagent.runtime.prompt_enrichment import (
589
+ build_skills_suffix,
590
+ get_cached_system_prompt,
591
+ record_chat_turn_diary,
592
+ )
578
593
  from seed.core.agent_context import (
579
594
  set_active_agent_id,
580
595
  set_active_audio_preset,
581
596
  set_active_image_gen_preset,
582
597
  set_active_llm_session,
583
- set_active_vision_preset,
584
598
  set_active_music_preset,
585
599
  set_active_video_gen_preset,
600
+ set_active_vision_preset,
586
601
  )
587
602
  from seed.core.agent_runtime import (
588
603
  build_api_projection_messages,
@@ -592,12 +607,6 @@ def _cmd_chat_llm(args):
592
607
  run_llm_tool_loop,
593
608
  strip_ephemeral_message_fields,
594
609
  )
595
- from codeagent.runtime.prompt_enrichment import (
596
- build_skills_suffix,
597
- fresh_system_prompt,
598
- get_cached_system_prompt,
599
- record_chat_turn_diary,
600
- )
601
610
  from seed.core.llm_exec import LLMError
602
611
  from seed.core.llm_presets import llm_executor_from_resolved, resolve_preset
603
612
  from seed.core.llm_sess import (
@@ -624,7 +633,14 @@ def _cmd_chat_llm(args):
624
633
  audio_preset_id = _cli_audio_preset_id(args)
625
634
  music_preset_id = _cli_music_preset_id(args)
626
635
  video_gen_preset_id = _cli_video_gen_preset_id(args)
627
- chat_sess = load_or_create_chat_session(name, agent_id)
636
+ # 项目归属:显式 --project-id 优先;其次沿用会话已存的 project_id,
637
+ # 让既有会话也能"重新归类"到新项目(不会清空历史)。
638
+ project_id = getattr(args, 'project_id', None)
639
+ chat_sess = load_or_create_chat_session(
640
+ name,
641
+ agent_id,
642
+ project_id=project_id,
643
+ )
628
644
  fresh_sys = get_cached_system_prompt(chat_sess, agent_id=agent_id)
629
645
  chat_sess.messages[:] = merge_fresh_system(chat_sess.messages, fresh_sys)
630
646
 
@@ -691,7 +707,6 @@ def _cmd_chat_llm(args):
691
707
  message_has_video_attachments,
692
708
  )
693
709
  from codeagent.core.audio_models import preset_supports_audio_id
694
- from codeagent.core.vision_models import preset_supports_vision_id
695
710
 
696
711
  user_msg = build_user_message(
697
712
  message_text,
@@ -700,21 +715,24 @@ def _cmd_chat_llm(args):
700
715
  )
701
716
  from codeagent.core.image_understanding import image_attachment_allowed, video_attachment_allowed
702
717
 
703
- if message_has_image_attachments(user_msg) or pending_dir_tag:
704
- if not image_attachment_allowed(vision_preset_id or ""):
705
- print(
706
- '[error] 发送图片需要 --vision-llm(supports_vision)'
707
- ' 或已配置 MiniMax MCP(understand_image)'
708
- )
709
- continue
710
- if message_has_video_attachments(user_msg):
711
- if not video_attachment_allowed(vision_preset_id or ""):
712
- print('[error] 发送视频需要有效的 --vision-llm 或 CODEAGENT_VISION_PRESET_ID')
713
- continue
714
- if message_has_audio_attachments(user_msg):
715
- if not audio_preset_id or not preset_supports_audio_id(audio_preset_id):
716
- print('[error] 发送音频需要有效的 --audio-llm 或 CODEAGENT_AUDIO_PRESET_ID')
717
- continue
718
+ if (message_has_image_attachments(user_msg) or pending_dir_tag) and not image_attachment_allowed(
719
+ vision_preset_id or ""
720
+ ):
721
+ print(
722
+ '[error] 发送图片需要 --vision-llm(supports_vision)'
723
+ ' 或已配置 MiniMax MCP(understand_image)'
724
+ )
725
+ continue
726
+ if message_has_video_attachments(user_msg) and not video_attachment_allowed(
727
+ vision_preset_id or ""
728
+ ):
729
+ print('[error] 发送视频需要有效的 --vision-llm 或 CODEAGENT_VISION_PRESET_ID')
730
+ continue
731
+ if message_has_audio_attachments(user_msg) and (
732
+ not audio_preset_id or not preset_supports_audio_id(audio_preset_id)
733
+ ):
734
+ print('[error] 发送音频需要有效的 --audio-llm 或 CODEAGENT_AUDIO_PRESET_ID')
735
+ continue
718
736
 
719
737
  pending_attachments = []
720
738
  pending_dir_tag = ''
@@ -728,17 +746,33 @@ def _cmd_chat_llm(args):
728
746
  chat_sess.messages,
729
747
  skills_suffix=_skills_suffix,
730
748
  )
731
- compact_result = maybe_compact_context_messages(api_msgs, llm)
749
+ _persisted_usage = (
750
+ chat_sess.metadata.get("context_usage")
751
+ if isinstance(chat_sess.metadata, dict)
752
+ else None
753
+ )
754
+ compact_result = maybe_compact_context_messages(
755
+ api_msgs,
756
+ llm,
757
+ persisted_context_usage=_persisted_usage,
758
+ )
732
759
  persist_compact_summary(chat_sess.messages, compact_result)
733
760
  strip_ephemeral_message_fields(api_msgs)
734
761
  if not isinstance(chat_sess.metadata, dict):
735
762
  chat_sess.metadata = {}
763
+ # CLI --project-id 优先;其次回退到会话已存的 project_id。
764
+ # finalize_episodic_for_llm 会保留既有项目归属(不会强行覆盖)。
765
+ effective_project_id = project_id
766
+ if effective_project_id is None and isinstance(chat_sess.metadata, dict):
767
+ existing = chat_sess.metadata.get('project_id')
768
+ if isinstance(existing, str) and existing:
769
+ effective_project_id = existing
736
770
  finalize_episodic_for_llm(
737
771
  api_msgs,
738
772
  chat_sess.metadata,
739
773
  agent_id=agent_id,
740
774
  session_id=name,
741
- project_id=None,
775
+ project_id=effective_project_id,
742
776
  compact_happened=compact_result is not None,
743
777
  )
744
778
  set_active_llm_session(f'{agent_id}::{name}')
@@ -759,7 +793,25 @@ def _cmd_chat_llm(args):
759
793
  max_tool_rounds=max_rounds,
760
794
  )
761
795
  )
762
- tail = merge_llm_tail_into_full(chat_sess.messages, api_msgs, n_before)
796
+ merge_llm_tail_into_full(chat_sess.messages, api_msgs, n_before)
797
+ # 落盘本轮峰值用量,供下一轮 maybe_compact_context_messages 的
798
+ # persisted_context_usage 来源使用(CLI 路径此前完全缺失此来源)。
799
+ with contextlib.suppress(Exception):
800
+ if isinstance(_loop_meta, dict):
801
+ _peak = int(_loop_meta.get("peak_prompt_tokens") or 0)
802
+ if _peak > 0:
803
+ if not isinstance(chat_sess.metadata, dict):
804
+ chat_sess.metadata = {}
805
+ _prev = chat_sess.metadata.get("context_usage")
806
+ _prev_peak = (
807
+ int(_prev.get("peak_prompt_tokens") or 0)
808
+ if isinstance(_prev, dict)
809
+ else 0
810
+ )
811
+ chat_sess.metadata["context_usage"] = {
812
+ "peak_prompt_tokens": max(_peak, _prev_peak),
813
+ "compact_count": int(_loop_meta.get("compact_count") or 0),
814
+ }
763
815
  print(reply)
764
816
  if tools_used:
765
817
  print(f" [tools] {', '.join(tools_used)}")
@@ -801,10 +853,9 @@ def _cmd_chat_llm(args):
801
853
  def cmd_webui_token(args):
802
854
  import secrets
803
855
 
804
- from codeagent.web.auth_impl import TOKEN_FILENAME
805
-
806
856
  # cmd_webui_token can be invoked directly; ensure env file is loaded.
807
857
  from codeagent.core.bootstrap import bootstrap_codeagent_runtime
858
+ from codeagent.web.auth_impl import TOKEN_FILENAME
808
859
 
809
860
  bootstrap_codeagent_runtime()
810
861
  from seed.core.config_plane import project_root as _project_root
@@ -6,10 +6,10 @@ import fnmatch
6
6
  import mimetypes
7
7
  import os
8
8
  import re
9
- from dataclasses import asdict, dataclass, field
9
+ from dataclasses import asdict, dataclass
10
10
  from datetime import datetime, timezone
11
11
  from pathlib import Path
12
- from typing import Any, Optional
12
+ from typing import Any
13
13
 
14
14
  from codeagent.core import env as ca_env
15
15
 
@@ -33,7 +33,7 @@ class AttachmentMeta:
33
33
  filename: str
34
34
  size_bytes: int
35
35
  path: str = ""
36
- extracted_text: Optional[str] = None
36
+ extracted_text: str | None = None
37
37
 
38
38
  def to_dict(self) -> dict[str, Any]:
39
39
  return {k: v for k, v in asdict(self).items() if v is not None}
@@ -101,9 +101,10 @@ def extract_document_text(raw: bytes, mime: str, filename: str = "") -> str:
101
101
  return raw.decode("utf-8", errors="replace").strip()
102
102
  if m == "application/pdf" or name.endswith(".pdf"):
103
103
  try:
104
- from pypdf import PdfReader
105
104
  import io
106
105
 
106
+ from pypdf import PdfReader
107
+
107
108
  reader = PdfReader(io.BytesIO(raw))
108
109
  parts = []
109
110
  for page in reader.pages:
@@ -159,14 +160,14 @@ def save_attachment(
159
160
  )
160
161
 
161
162
 
162
- def resolve_attachment_path(agent_id: str, session_id: str, attachment_id: str) -> Optional[Path]:
163
+ def resolve_attachment_path(agent_id: str, session_id: str, attachment_id: str) -> Path | None:
163
164
  from seed.core.media_store import resolve_session_media_path
164
165
 
165
166
  p = resolve_session_media_path(agent_id, session_id, attachment_id)
166
167
  return p
167
168
 
168
169
 
169
- def load_attachment_meta(agent_id: str, session_id: str, attachment_id: str) -> Optional[AttachmentMeta]:
170
+ def load_attachment_meta(agent_id: str, session_id: str, attachment_id: str) -> AttachmentMeta | None:
170
171
  p = resolve_attachment_path(agent_id, session_id, attachment_id)
171
172
  if not p or not p.is_file():
172
173
  return None
@@ -245,8 +246,8 @@ def scan_image_directory(
245
246
  workspace_root: Path,
246
247
  rel_path: str,
247
248
  *,
248
- pattern: Optional[str] = None,
249
- max_files: Optional[int] = None,
249
+ pattern: str | None = None,
250
+ max_files: int | None = None,
250
251
  ) -> tuple[list[Path], bool]:
251
252
  """Return (image paths, truncated). Paths resolved under workspace_root."""
252
253
  root = workspace_root.resolve()
@@ -277,7 +278,7 @@ def scan_image_directory(
277
278
  return found, truncated
278
279
 
279
280
 
280
- def _parse_globs(pattern: Optional[str]) -> tuple[str, ...]:
281
+ def _parse_globs(pattern: str | None) -> tuple[str, ...]:
281
282
  if pattern and pattern.strip():
282
283
  return tuple(g.strip() for g in pattern.split(",") if g.strip())
283
284
  raw = ca_env.pick_default(
@@ -289,10 +290,7 @@ def _parse_globs(pattern: Optional[str]) -> tuple[str, ...]:
289
290
 
290
291
  def _matches_globs(name: str, globs: tuple[str, ...]) -> bool:
291
292
  low = name.lower()
292
- for g in globs:
293
- if fnmatch.fnmatch(low, g.lower()):
294
- return True
295
- return False
293
+ return any(fnmatch.fnmatch(low, g.lower()) for g in globs)
296
294
 
297
295
 
298
296
  def message_has_image_attachments(msg: dict[str, Any]) -> bool:
@@ -301,9 +299,7 @@ def message_has_image_attachments(msg: dict[str, Any]) -> bool:
301
299
  for a in atts:
302
300
  if isinstance(a, dict) and a.get("kind") == "image":
303
301
  return True
304
- if IMAGE_DIR_TAG_RE.search(str(msg.get("content") or "")):
305
- return True
306
- return False
302
+ return bool(IMAGE_DIR_TAG_RE.search(str(msg.get("content") or "")))
307
303
 
308
304
 
309
305
  def message_has_video_attachments(msg: dict[str, Any]) -> bool:
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  from codeagent.core import env as ca_env
8
8
 
@@ -29,7 +29,7 @@ def preset_supports_audio_id(preset_id: str) -> bool:
29
29
  return False
30
30
 
31
31
 
32
- def resolve_audio_preset(preset_id: Optional[str] = None) -> dict[str, Any]:
32
+ def resolve_audio_preset(preset_id: str | None = None) -> dict[str, Any]:
33
33
  from seed.core.llm_presets import load_presets
34
34
 
35
35
  pid = (preset_id or "").strip()
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  import logging
6
6
  import os
7
7
  from pathlib import Path
8
- from typing import Optional
9
8
 
10
9
  from codeagent.core.env import apply_default_product_home
11
10
  from codeagent.core.seed_bridge import bridge_codeagent_env_to_seed
@@ -127,7 +126,7 @@ def _ensure_codeagent_bootstrap_md(home: Path) -> None:
127
126
  dest.write_text(_CODEAGENT_BOOTSTRAP_MD, encoding="utf-8")
128
127
 
129
128
 
130
- def bootstrap_codeagent_runtime(base: Optional[Path] = None) -> Path:
129
+ def bootstrap_codeagent_runtime(base: Path | None = None) -> Path:
131
130
  """
132
131
  1. Default data root ``~/.codeagent`` (or ``CODEAGENT_HOME``)
133
132
  2. Set ``SEED_PROJECT_ROOT`` so Seed / seed-tools use the same tree
@@ -16,7 +16,6 @@ from __future__ import annotations
16
16
  import json
17
17
  import logging
18
18
  from pathlib import Path
19
- from typing import Dict
20
19
 
21
20
  logger = logging.getLogger(__name__)
22
21
 
@@ -70,7 +69,7 @@ def _read_legacy_single_id() -> str:
70
69
  return ""
71
70
 
72
71
 
73
- def get_default_preset_ids() -> Dict[str, str]:
72
+ def get_default_preset_ids() -> dict[str, str]:
74
73
  """Return a mapping ``{slot: preset_id}`` for every slot that has a default.
75
74
 
76
75
  Reads the new JSON map first; falls back to ``codeagent.default_preset_ids.json``
@@ -83,7 +82,7 @@ def get_default_preset_ids() -> Dict[str, str]:
83
82
  except (json.JSONDecodeError, OSError):
84
83
  data = {}
85
84
  if isinstance(data, dict):
86
- out: Dict[str, str] = {}
85
+ out: dict[str, str] = {}
87
86
  for slot in SLOTS:
88
87
  v = data.get(slot)
89
88
  if isinstance(v, str) and v.strip():
@@ -97,7 +96,7 @@ def get_default_preset_ids() -> Dict[str, str]:
97
96
  except (json.JSONDecodeError, OSError):
98
97
  data = {}
99
98
  if isinstance(data, dict):
100
- out: Dict[str, str] = {}
99
+ out: dict[str, str] = {}
101
100
  for slot in SLOTS:
102
101
  v = data.get(slot)
103
102
  if isinstance(v, str) and v.strip():
@@ -124,9 +123,9 @@ def set_default_preset_id_for_slot(slot: str, preset_id: str) -> None:
124
123
  _write_default_preset_ids(cur)
125
124
 
126
125
 
127
- def set_default_preset_ids(mapping: Dict[str, str]) -> None:
126
+ def set_default_preset_ids(mapping: dict[str, str]) -> None:
128
127
  """Bulk-replace the entire default map (ignoring unknown slots)."""
129
- out: Dict[str, str] = {}
128
+ out: dict[str, str] = {}
130
129
  for slot in SLOTS:
131
130
  if slot in mapping:
132
131
  v = mapping[slot]
@@ -135,7 +134,7 @@ def set_default_preset_ids(mapping: Dict[str, str]) -> None:
135
134
  _write_default_preset_ids(out)
136
135
 
137
136
 
138
- def _write_default_preset_ids(mapping: Dict[str, str]) -> None:
137
+ def _write_default_preset_ids(mapping: dict[str, str]) -> None:
139
138
  p = _new_path()
140
139
  p.parent.mkdir(parents=True, exist_ok=True)
141
140
  try:
@@ -6,7 +6,6 @@ import os
6
6
  import subprocess
7
7
  import sys
8
8
  from pathlib import Path
9
- from typing import Optional, Tuple
10
9
 
11
10
 
12
11
  def has_graphical_session() -> bool:
@@ -20,7 +19,7 @@ def has_graphical_session() -> bool:
20
19
  )
21
20
 
22
21
 
23
- def pick_directory_sync() -> Tuple[str, bool, str]:
22
+ def pick_directory_sync() -> tuple[str, bool, str]:
24
23
  """
25
24
  Blocking folder picker.
26
25
 
@@ -36,7 +35,7 @@ def pick_directory_sync() -> Tuple[str, bool, str]:
36
35
  return _pick_linux()
37
36
 
38
37
 
39
- def _pick_darwin() -> Tuple[str, bool, str]:
38
+ def _pick_darwin() -> tuple[str, bool, str]:
40
39
  script = (
41
40
  'POSIX path of (choose folder with prompt '
42
41
  '"选择目录" default location (path to desktop folder))'
@@ -63,7 +62,7 @@ def _pick_darwin() -> Tuple[str, bool, str]:
63
62
  return "", False, ""
64
63
 
65
64
 
66
- def _pick_windows() -> Tuple[str, bool, str]:
65
+ def _pick_windows() -> tuple[str, bool, str]:
67
66
  ps = r"""
68
67
  Add-Type -AssemblyName System.Windows.Forms
69
68
  $top = New-Object System.Windows.Forms.Form
@@ -103,7 +102,7 @@ try {
103
102
  return "", True, "PowerShell not found for folder picker"
104
103
 
105
104
 
106
- def _pick_linux() -> Tuple[str, bool, str]:
105
+ def _pick_linux() -> tuple[str, bool, str]:
107
106
  if not has_graphical_session():
108
107
  return (
109
108
  "",
@@ -145,7 +144,7 @@ def _pick_linux() -> Tuple[str, bool, str]:
145
144
  return "", True, hint
146
145
 
147
146
 
148
- def _which(name: str) -> Optional[str]:
147
+ def _which(name: str) -> str | None:
149
148
  from shutil import which as _w
150
149
 
151
150
  return _w(name)
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from pathlib import Path
6
- from typing import Any, Optional
6
+ from typing import Any
7
7
 
8
8
  from seed.integrations.mcp_config import MINIMAX_MCP_SERVER_ID, get_server_config
9
9
 
@@ -12,7 +12,7 @@ MINIMAX_UNDERSTAND_IMAGE_TOOL = "understand_image"
12
12
  MCP_QUALIFIED_UNDERSTAND_IMAGE = f"mcp__{MINIMAX_MCP_SERVER_ID}__{MINIMAX_UNDERSTAND_IMAGE_TOOL}"
13
13
 
14
14
 
15
- def minimax_mcp_configured(base: Optional[Path] = None) -> bool:
15
+ def minimax_mcp_configured(base: Path | None = None) -> bool:
16
16
  """MiniMax MCP server enabled in config with a non-empty API key."""
17
17
  from seed.integrations.mcp_client import mcp_globally_enabled
18
18
 
@@ -26,10 +26,10 @@ def minimax_mcp_configured(base: Optional[Path] = None) -> bool:
26
26
 
27
27
 
28
28
  def minimax_mcp_understand_image_ready(
29
- base: Optional[Path] = None,
29
+ base: Path | None = None,
30
30
  *,
31
31
  probe: bool = False,
32
- servers_status: Optional[list[dict[str, Any]]] = None,
32
+ servers_status: list[dict[str, Any]] | None = None,
33
33
  ) -> bool:
34
34
  """True when MiniMax MCP can run ``understand_image`` (configured; optionally probed)."""
35
35
  if not minimax_mcp_configured(base):
@@ -51,7 +51,7 @@ def minimax_mcp_understand_image_ready(
51
51
  return False
52
52
 
53
53
 
54
- def image_attachment_allowed(vision_llm_id: str, base: Optional[Path] = None) -> bool:
54
+ def image_attachment_allowed(vision_llm_id: str, base: Path | None = None) -> bool:
55
55
  """User may send image attachments when a vision preset or MiniMax MCP is available."""
56
56
  vid = (vision_llm_id or "").strip()
57
57
  if vid == MCP_VISION_SENTINEL:
@@ -72,10 +72,10 @@ def video_attachment_allowed(vision_llm_id: str) -> bool:
72
72
 
73
73
 
74
74
  def image_understanding_status(
75
- base: Optional[Path] = None,
75
+ base: Path | None = None,
76
76
  *,
77
77
  probe: bool = False,
78
- servers_status: Optional[list[dict[str, Any]]] = None,
78
+ servers_status: list[dict[str, Any]] | None = None,
79
79
  ) -> dict[str, Any]:
80
80
  configured = minimax_mcp_configured(base)
81
81
  ready = minimax_mcp_understand_image_ready(