tagword-codeagent 1.2.6__tar.gz → 1.2.8__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 (278) hide show
  1. {tagword_codeagent-1.2.6/tagword_codeagent.egg-info → tagword_codeagent-1.2.8}/PKG-INFO +2 -2
  2. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/agent_card.py +17 -7
  3. tagword_codeagent-1.2.8/codeagent/server/a2a/auth.py +59 -0
  4. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/handlers.py +7 -0
  5. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/pyproject.toml +2 -2
  6. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8/tagword_codeagent.egg-info}/PKG-INFO +2 -2
  7. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tagword_codeagent.egg-info/SOURCES.txt +2 -0
  8. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tagword_codeagent.egg-info/requires.txt +1 -1
  9. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_a2a.py +15 -1
  10. tagword_codeagent-1.2.8/tests/test_a2a_auth.py +147 -0
  11. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/LICENSE +0 -0
  12. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/README.md +0 -0
  13. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/__init__.py +0 -0
  14. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/__main__.py +0 -0
  15. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/cli/__init__.py +0 -0
  16. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/cli/entrypoint.py +0 -0
  17. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/cli/main.py +0 -0
  18. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/__init__.py +0 -0
  19. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/attachments.py +0 -0
  20. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/audio_models.py +0 -0
  21. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/bootstrap.py +0 -0
  22. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/default_presets.py +0 -0
  23. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/env.py +0 -0
  24. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/folder_picker.py +0 -0
  25. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/image_understanding.py +0 -0
  26. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/models.py +0 -0
  27. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/paths.py +0 -0
  28. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/pricing.py +0 -0
  29. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/process_ports.py +0 -0
  30. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/seed_bridge.py +0 -0
  31. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/settings.py +0 -0
  32. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/speech_synth.py +0 -0
  33. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/team_config.py +0 -0
  34. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/team_manager.py +0 -0
  35. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/token_counter.py +0 -0
  36. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/tts_voices.py +0 -0
  37. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/usage_billing.py +0 -0
  38. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/core/vision_models.py +0 -0
  39. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/memory/diary.py +0 -0
  40. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/README.md +0 -0
  41. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/agent.md +0 -0
  42. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/identity.md +0 -0
  43. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/memory.md +0 -0
  44. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/skills.md +0 -0
  45. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/soul.md +0 -0
  46. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/tools.md +0 -0
  47. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/persona_defaults/user.md +0 -0
  48. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/__init__.py +0 -0
  49. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/agent_bootstrap.py +0 -0
  50. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/agent_context.py +0 -0
  51. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/agent_handle.py +0 -0
  52. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/agent_runtime.py +0 -0
  53. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/compact_prompt.py +0 -0
  54. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/compact_state.py +0 -0
  55. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/llm_worker.py +0 -0
  56. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/orchestrator.py +0 -0
  57. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/prompt_enrichment.py +0 -0
  58. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/session_manager.py +0 -0
  59. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/task_split.py +0 -0
  60. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/turn_loop.py +0 -0
  61. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/runtime/worker.py +0 -0
  62. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/__init__.py +0 -0
  63. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/__init__.py +0 -0
  64. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/client.py +0 -0
  65. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/client_card.py +0 -0
  66. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/context.py +0 -0
  67. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/models.py +0 -0
  68. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/a2a/streaming.py +0 -0
  69. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/app_factory.py +0 -0
  70. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/attachment_api.py +0 -0
  71. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/auto_continue.py +0 -0
  72. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/chat_turn.py +0 -0
  73. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/evidence.py +0 -0
  74. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/hub.py +0 -0
  75. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/icon.png +0 -0
  76. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/self_healing.py +0 -0
  77. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/team_engine.py +0 -0
  78. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/team_worker.py +0 -0
  79. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/tts_api.py +0 -0
  80. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_api_app.py +0 -0
  81. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_agentmeta.py +0 -0
  82. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_agents.py +0 -0
  83. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_auth.py +0 -0
  84. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_cronallow.py +0 -0
  85. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_envllm.py +0 -0
  86. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_files.py +0 -0
  87. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_git.py +0 -0
  88. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_health.py +0 -0
  89. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_mcp.py +0 -0
  90. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_projects.py +0 -0
  91. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_sessions.py +0 -0
  92. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_skills.py +0 -0
  93. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/server/webui_handlers_teams.py +0 -0
  94. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/skills/__init__.py +0 -0
  95. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/skills/select.py +0 -0
  96. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/tools/__init__.py +0 -0
  97. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/tools/agent_tools.py +0 -0
  98. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/__init__.py +0 -0
  99. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/__pycache__/__init__.cpython-311.pyc +0 -0
  100. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/__pycache__/__init__.cpython-313.pyc +0 -0
  101. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/__pycache__/app.cpython-313.pyc +0 -0
  102. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/__pycache__/auth_impl.cpython-311.pyc +0 -0
  103. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/__pycache__/auth_impl.cpython-313.pyc +0 -0
  104. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/auth_impl.py +0 -0
  105. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/00-marked.min.js +0 -0
  106. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/00-mermaid.min.js +0 -0
  107. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/00-purify.min.js +0 -0
  108. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/00-storage.js +0 -0
  109. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/00-utils.js +0 -0
  110. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01-theme.css +0 -0
  111. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01a-scroll.js +0 -0
  112. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01b-think.js +0 -0
  113. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01c-session-identity.js +0 -0
  114. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01c-session-thinking.js +0 -0
  115. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01c-session-tree-menus.js +0 -0
  116. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01c-session-tree.js +0 -0
  117. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01c-session-utilities.js +0 -0
  118. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01c-vision-model.js +0 -0
  119. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01d-image-gen-model.js +0 -0
  120. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01e-audio-model.js +0 -0
  121. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01f-compose-ui.js +0 -0
  122. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01g-tts-settings.js +0 -0
  123. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01h-music-model.js +0 -0
  124. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01i-model-stack.js +0 -0
  125. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01j-video-gen-model.js +0 -0
  126. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01k-model-stack-bootstrap.js +0 -0
  127. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01k-model-stack-state.js +0 -0
  128. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/01q-inflight.js +0 -0
  129. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/02-reset.css +0 -0
  130. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/02-session-ui.js +0 -0
  131. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03-layout-p1.css +0 -0
  132. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03-layout-p2.css +0 -0
  133. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03a-links-stream.js +0 -0
  134. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03b-stream-text.js +0 -0
  135. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03c-render-bubble.js +0 -0
  136. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03d-agent-bubble.js +0 -0
  137. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03e-tool-trace.js +0 -0
  138. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03f-timeline.js +0 -0
  139. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/03g-bubble-tts.js +0 -0
  140. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p1.css +0 -0
  141. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p2b.css +0 -0
  142. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p3.css +0 -0
  143. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p4.css +0 -0
  144. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p5.css +0 -0
  145. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p6.css +0 -0
  146. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p7.css +0 -0
  147. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p8.css +0 -0
  148. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-components-p9.css +0 -0
  149. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-ws-connect.js +0 -0
  150. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/04-ws-helpers.js +0 -0
  151. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05-chat-p1.css +0 -0
  152. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05-chat-p2.css +0 -0
  153. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05-chat-p3.css +0 -0
  154. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05a-session-history.js +0 -0
  155. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05b-session-list.js +0 -0
  156. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05c-dialog.js +0 -0
  157. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05d-project-create.js +0 -0
  158. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/05e-project-remote.js +0 -0
  159. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/06-chat.js +0 -0
  160. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/06-utilities.css +0 -0
  161. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/06b-attachments.js +0 -0
  162. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/06c-camera.js +0 -0
  163. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/07-activity-bar.css +0 -0
  164. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09-git-p1.css +0 -0
  165. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09-git-p2.css +0 -0
  166. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09a-init-bind.js +0 -0
  167. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09b-panel-toggle.js +0 -0
  168. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09c-api-refresh.js +0 -0
  169. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09d-status-log.js +0 -0
  170. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09e-commit.js +0 -0
  171. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/09f-remote.js +0 -0
  172. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11a-cron-parse.js +0 -0
  173. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11b-cron-panel.js +0 -0
  174. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11c-cron-card.js +0 -0
  175. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11d-cron-form.js +0 -0
  176. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11e-env-config.js +0 -0
  177. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11f-env-llm-a-state.js +0 -0
  178. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11f-env-llm-b-form.js +0 -0
  179. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11f-env-llm-c-board.js +0 -0
  180. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11g-env-paths-git.js +0 -0
  181. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11h-env-mcp-a-state.js +0 -0
  182. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11h-env-mcp-b-form.js +0 -0
  183. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/11h-env-mcp-c-board.js +0 -0
  184. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/12-plugins.js +0 -0
  185. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13a-skills.js +0 -0
  186. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13a-todo-utils.js +0 -0
  187. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13b-todo-fetch.js +0 -0
  188. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13b-tools.js +0 -0
  189. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13c-skills-panel.js +0 -0
  190. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13c-todo-crud.js +0 -0
  191. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/13d-file-browser.js +0 -0
  192. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/14-activity-bar.js +0 -0
  193. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/15-plans.css +0 -0
  194. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/15-plans.js +0 -0
  195. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/16-mobile.css +0 -0
  196. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/16-mobile.js +0 -0
  197. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/17-agents-list.css +0 -0
  198. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/17-agents-list.js +0 -0
  199. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/17-preset-ux.css +0 -0
  200. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/18-agent-mgr.css +0 -0
  201. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/18-agent-mgr.js +0 -0
  202. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/24-health-dashboard.js +0 -0
  203. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/25-teams-panel.css +0 -0
  204. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/25-teams-panel.js +0 -0
  205. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/99-brand-polish.css +0 -0
  206. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/body.html +0 -0
  207. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/setup/setup_form.html +0 -0
  208. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/setup/setup_head.html +0 -0
  209. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/setup/setup_tail.html +0 -0
  210. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/static/setup/setup_test.html +0 -0
  211. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/web_login.html +0 -0
  212. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/codeagent/web/webui.html +0 -0
  213. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/setup.cfg +0 -0
  214. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tagword_codeagent.egg-info/dependency_links.txt +0 -0
  215. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tagword_codeagent.egg-info/entry_points.txt +0 -0
  216. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tagword_codeagent.egg-info/top_level.txt +0 -0
  217. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_a2a_context.py +0 -0
  218. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_a2a_streaming.py +0 -0
  219. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_a2a_tool.py +0 -0
  220. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_agent_discovery_hollow.py +0 -0
  221. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_agent_file_safety.py +0 -0
  222. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_agent_registry_bridge.py +0 -0
  223. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_agent_root_consistency.py +0 -0
  224. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_archive_probe.py +0 -0
  225. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_attachment_safety.py +0 -0
  226. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_attachments.py +0 -0
  227. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_auth_pure_fns.py +0 -0
  228. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_auth_routes.py +0 -0
  229. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_auto_continue.py +0 -0
  230. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_auto_continue_wiring.py +0 -0
  231. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_cli_chat_project_id.py +0 -0
  232. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_compact_state.py +0 -0
  233. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_default_presets.py +0 -0
  234. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_env.py +0 -0
  235. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_env_chat.py +0 -0
  236. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_evidence.py +0 -0
  237. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_git_provider.py +0 -0
  238. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_git_ssh_helpers.py +0 -0
  239. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_history_json_safe.py +0 -0
  240. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_history_rows.py +0 -0
  241. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_hub.py +0 -0
  242. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_image_understanding.py +0 -0
  243. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_list_agents_tool.py +0 -0
  244. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_listen_port.py +0 -0
  245. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_metas.py +0 -0
  246. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_nudge_edge.py +0 -0
  247. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_orchestrator.py +0 -0
  248. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_paths_files.py +0 -0
  249. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_paths_safety.py +0 -0
  250. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_persist_summarize.py +0 -0
  251. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_persona_fallback.py +0 -0
  252. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_persona_hash.py +0 -0
  253. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_persona_init.py +0 -0
  254. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_preset_support.py +0 -0
  255. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_pricing.py +0 -0
  256. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_project_dirs.py +0 -0
  257. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_prompt_enrichment.py +0 -0
  258. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_reply_summary.py +0 -0
  259. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_self_healing.py +0 -0
  260. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_selfheal_heal.py +0 -0
  261. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_selfheal_health.py +0 -0
  262. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_server_init.py +0 -0
  263. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_server_pure.py +0 -0
  264. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_session_history.py +0 -0
  265. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_settings.py +0 -0
  266. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_skills_select.py +0 -0
  267. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_speech_synth_pure.py +0 -0
  268. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_task_split.py +0 -0
  269. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_team_concurrency.py +0 -0
  270. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_team_config.py +0 -0
  271. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_team_engine.py +0 -0
  272. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_token_counter.py +0 -0
  273. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_tts_voices.py +0 -0
  274. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_usage_billing.py +0 -0
  275. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_webui_auth_cookie.py +0 -0
  276. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_webui_pure_fns.py +0 -0
  277. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_webui_pure_fns2.py +0 -0
  278. {tagword_codeagent-1.2.6 → tagword_codeagent-1.2.8}/tests/test_write_env.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tagword-codeagent
3
- Version: 1.2.6
3
+ Version: 1.2.8
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
@@ -27,7 +27,7 @@ Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: seed-kernel
29
29
  Requires-Dist: seed-model-providers>=1.0.3
30
- Requires-Dist: seed-toolbox[code]>=1.0.8
30
+ Requires-Dist: seed-toolbox[code]>=1.0.9
31
31
  Requires-Dist: uvicorn[standard]>=0.30.0
32
32
  Requires-Dist: starlette>=0.37.0
33
33
  Provides-Extra: dev
@@ -10,6 +10,8 @@ from __future__ import annotations
10
10
  import os
11
11
  from typing import Any
12
12
 
13
+ from .auth import auth_enabled
14
+
13
15
  PROTOCOL_VERSION = "0.2.5"
14
16
 
15
17
 
@@ -74,12 +76,20 @@ def build_agent_card() -> dict[str, Any]:
74
76
  "outputModes": ["text"],
75
77
  }
76
78
  ],
77
- "securitySchemes": {
78
- "bearer": {
79
- "type": "http",
80
- "scheme": "bearer",
81
- "description": "Bearer token(复用 WebUI token,见 config/webui.token)",
79
+ # 认证声明与真实行为一致:未配置 token 时不声明 security,
80
+ # 否则客户端会带 token 而服务端根本不校验(声明与实现脱节)。
81
+ **(
82
+ {
83
+ "securitySchemes": {
84
+ "bearer": {
85
+ "type": "http",
86
+ "scheme": "bearer",
87
+ "description": "Bearer token(复用 WebUI token,见 config/webui.token)",
88
+ }
89
+ },
90
+ "security": [{"bearer": []}],
82
91
  }
83
- },
84
- "security": [{"bearer": []}],
92
+ if auth_enabled()
93
+ else {}
94
+ ),
85
95
  }
@@ -0,0 +1,59 @@
1
+ """A2A Bearer 认证 — 复用 WebUI token。
2
+
3
+ 设计要点:
4
+ - **单一 token 源**:复用 `codeagent.web.auth_impl.get_webui_token()`,
5
+ 即 `CODEAGENT_WEBUI_TOKEN` 环境变量或 `config/webui.token` 文件。
6
+ 不引入第二套密钥,避免运维分裂。
7
+ - **未配置 token 时放行**:与 WebUI 行为一致(未设 token = 本机开发模式,
8
+ WebUI 本身也不鉴权)。此时 Agent Card 不声明 security,客户端不会带 token。
9
+ - **常量时间比较**:`hmac.compare_digest` 防时序侧信道。
10
+ - **认证在 JSON-RPC 入口统一做**:所有方法(含 `message/stream` SSE)
11
+ 都经过 `a2a_jsonrpc`,故一处拦截即全覆盖。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import hmac
17
+ import logging
18
+
19
+ from starlette.requests import Request
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # JSON-RPC 错误码:-32001 = 未授权(A2A/实现自定义区间 -32000..-32099)
24
+ ERR_UNAUTHORIZED = -32001
25
+
26
+
27
+ def get_a2a_token() -> str:
28
+ """返回期望的 Bearer token;空字符串表示未启用认证。"""
29
+ try:
30
+ from codeagent.web.auth_impl import get_webui_token
31
+
32
+ return get_webui_token()
33
+ except Exception: # noqa: BLE001 - 认证不可用时不阻断服务
34
+ logger.warning("a2a: 无法读取 webui token,A2A 认证降级为关闭", exc_info=True)
35
+ return ""
36
+
37
+
38
+ def auth_enabled() -> bool:
39
+ """是否启用 A2A 认证(配置了 token 才启用)。"""
40
+ return bool(get_a2a_token())
41
+
42
+
43
+ def _extract_bearer(request: Request) -> str:
44
+ raw = request.headers.get("authorization") or ""
45
+ parts = raw.split(None, 1)
46
+ if len(parts) == 2 and parts[0].lower() == "bearer":
47
+ return parts[1].strip()
48
+ return ""
49
+
50
+
51
+ def check_request(request: Request) -> bool:
52
+ """校验请求是否通过认证。未配置 token 时恒为 True。"""
53
+ expected = get_a2a_token()
54
+ if not expected:
55
+ return True
56
+ got = _extract_bearer(request)
57
+ if not got:
58
+ return False
59
+ return hmac.compare_digest(got.encode("utf-8"), expected.encode("utf-8"))
@@ -18,6 +18,7 @@ from starlette.requests import Request
18
18
  from starlette.responses import JSONResponse
19
19
 
20
20
  from .agent_card import build_agent_card
21
+ from .auth import ERR_UNAUTHORIZED, check_request
21
22
  from .context import ContextError, context_id_for_task, record_task
22
23
  from .context import resolve as resolve_context
23
24
  from .models import (
@@ -52,7 +53,13 @@ async def a2a_jsonrpc(request: Request):
52
53
  """POST /a2a — A2A JSON-RPC 2.0 入口。
53
54
 
54
55
  ``message/stream`` 返回 SSE 流(StreamingResponse),其余返回 JSON。
56
+
57
+ 认证在此统一校验:所有方法(含 SSE)都经过本入口。
55
58
  """
59
+ # ── Bearer 认证(未配置 token 时放行,见 auth.py)──
60
+ if not check_request(request):
61
+ return _rpc_error(None, ERR_UNAUTHORIZED, "Unauthorized: invalid or missing Bearer token")
62
+
56
63
  try:
57
64
  body = await request.json()
58
65
  except Exception:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tagword-codeagent"
7
- version = "1.2.6"
7
+ version = "1.2.8"
8
8
  description = "Autonomous AI coding agent driven by Markdown config — DeepSeek-ready, CLI + Web UI, 45+ tools, self-hosted"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -29,7 +29,7 @@ classifiers = [
29
29
  dependencies = [
30
30
  "seed-kernel",
31
31
  "seed-model-providers>=1.0.3",
32
- "seed-toolbox[code]>=1.0.8",
32
+ "seed-toolbox[code]>=1.0.9",
33
33
  "uvicorn[standard]>=0.30.0",
34
34
  "starlette>=0.37.0"
35
35
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tagword-codeagent
3
- Version: 1.2.6
3
+ Version: 1.2.8
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
@@ -27,7 +27,7 @@ Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: seed-kernel
29
29
  Requires-Dist: seed-model-providers>=1.0.3
30
- Requires-Dist: seed-toolbox[code]>=1.0.8
30
+ Requires-Dist: seed-toolbox[code]>=1.0.9
31
31
  Requires-Dist: uvicorn[standard]>=0.30.0
32
32
  Requires-Dist: starlette>=0.37.0
33
33
  Provides-Extra: dev
@@ -78,6 +78,7 @@ codeagent/server/webui_handlers_skills.py
78
78
  codeagent/server/webui_handlers_teams.py
79
79
  codeagent/server/a2a/__init__.py
80
80
  codeagent/server/a2a/agent_card.py
81
+ codeagent/server/a2a/auth.py
81
82
  codeagent/server/a2a/client.py
82
83
  codeagent/server/a2a/client_card.py
83
84
  codeagent/server/a2a/context.py
@@ -210,6 +211,7 @@ tagword_codeagent.egg-info/entry_points.txt
210
211
  tagword_codeagent.egg-info/requires.txt
211
212
  tagword_codeagent.egg-info/top_level.txt
212
213
  tests/test_a2a.py
214
+ tests/test_a2a_auth.py
213
215
  tests/test_a2a_context.py
214
216
  tests/test_a2a_streaming.py
215
217
  tests/test_a2a_tool.py
@@ -1,6 +1,6 @@
1
1
  seed-kernel
2
2
  seed-model-providers>=1.0.3
3
- seed-toolbox[code]>=1.0.8
3
+ seed-toolbox[code]>=1.0.9
4
4
  uvicorn[standard]>=0.30.0
5
5
  starlette>=0.37.0
6
6
 
@@ -40,7 +40,11 @@ def test_agent_card_structure():
40
40
  skill = card["skills"][0]
41
41
  for key in ("id", "name", "description", "inputModes", "outputModes"):
42
42
  assert key in skill
43
- assert "securitySchemes" in card
43
+ # security 声明与 auth_enabled() 一致(未配置 token 时不声明),
44
+ # 两种状态各自的行为由 test_a2a_auth.py 覆盖。
45
+ from codeagent.server.a2a.auth import auth_enabled
46
+
47
+ assert ("securitySchemes" in card) is auth_enabled()
44
48
 
45
49
 
46
50
  def test_agent_card_endpoint(client):
@@ -79,6 +83,16 @@ def test_empty_text(client):
79
83
 
80
84
 
81
85
  # ---------- 项目校验默认放行 ----------
86
+ @pytest.fixture(autouse=True)
87
+ def _no_a2a_auth(monkeypatch):
88
+ """禁用 A2A 认证,隔离本机 config/webui.token 的影响。
89
+
90
+ 认证行为由 tests/test_a2a_auth.py 专门覆盖;本文件测的是协议逻辑,
91
+ 若读取真实 token 文件,所有用例都会因缺 Bearer 头而 401。
92
+ """
93
+ monkeypatch.setattr(ai, "get_webui_token", lambda *a, **k: "")
94
+
95
+
82
96
  @pytest.fixture(autouse=True)
83
97
  def _allow_any_project(monkeypatch):
84
98
  """默认认为任意 projectId 都存在,避免每个测试都要 mock。
@@ -0,0 +1,147 @@
1
+ """A2A Bearer 认证测试。
2
+
3
+ 覆盖 4 种情形 + 声明一致性:
4
+ 1. 未配置 token → 放行(本机开发模式),且 Agent Card 不声明 security
5
+ 2. 配置 token + 无 Authorization → 401 语义(JSON-RPC -32001)
6
+ 3. 配置 token + 错误 token → 拒绝
7
+ 4. 配置 token + 正确 token → 通过
8
+ 5. Agent Card 的 security 声明与 auth_enabled() 一致(声明不脱节)
9
+
10
+ 认证在 JSON-RPC 入口统一做,故 message/send 与 message/stream 均覆盖。
11
+ """
12
+
13
+ import pytest
14
+ from starlette.applications import Starlette
15
+ from starlette.routing import Route
16
+ from starlette.testclient import TestClient
17
+
18
+ from codeagent.server.a2a import a2a_agent_card, a2a_jsonrpc
19
+ from codeagent.server.a2a.agent_card import build_agent_card
20
+ from codeagent.server.a2a.auth import ERR_UNAUTHORIZED, auth_enabled, check_request
21
+ from codeagent.web import auth_impl as ai
22
+
23
+ TOKEN = "test-a2a-secret-token"
24
+
25
+
26
+ @pytest.fixture()
27
+ def client():
28
+ app = Starlette(routes=[
29
+ Route("/.well-known/agent-card.json", a2a_agent_card, methods=["GET"]),
30
+ Route("/a2a", a2a_jsonrpc, methods=["POST"]),
31
+ ])
32
+ return TestClient(app)
33
+
34
+
35
+ @pytest.fixture()
36
+ def no_token(monkeypatch):
37
+ monkeypatch.setattr(ai, "get_webui_token", lambda *a, **k: "")
38
+ return None
39
+
40
+
41
+ @pytest.fixture()
42
+ def with_token(monkeypatch):
43
+ monkeypatch.setattr(ai, "get_webui_token", lambda *a, **k: TOKEN)
44
+ return TOKEN
45
+
46
+
47
+ def _rpc_body():
48
+ return {
49
+ "jsonrpc": "2.0",
50
+ "id": 1,
51
+ "method": "message/send",
52
+ "params": {
53
+ "message": {
54
+ "role": "user",
55
+ "parts": [{"kind": "text", "text": "hi"}],
56
+ "messageId": "m-1",
57
+ "contextId": "ctx-auth",
58
+ }
59
+ },
60
+ }
61
+
62
+
63
+ # ---------- 1. 未配置 token:放行 ----------
64
+ def test_no_token_configured_allows_request(client, no_token):
65
+ r = client.post("/a2a", json=_rpc_body())
66
+ assert r.status_code == 200
67
+ assert r.json().get("error", {}).get("code") != ERR_UNAUTHORIZED
68
+
69
+
70
+ def test_auth_disabled_when_no_token(no_token):
71
+ assert auth_enabled() is False
72
+
73
+
74
+ # ---------- 2. 配置 token + 无 header:拒绝 ----------
75
+ def test_missing_header_rejected(client, with_token, monkeypatch):
76
+ # 避免真的跑 chat turn:认证失败应在进入业务逻辑前返回
77
+ r = client.post("/a2a", json=_rpc_body())
78
+ assert r.status_code == 200 # JSON-RPC 错误走 200 + error 字段
79
+ err = r.json().get("error") or {}
80
+ assert err.get("code") == ERR_UNAUTHORIZED
81
+
82
+
83
+ # ---------- 3. 配置 token + 错误 token:拒绝 ----------
84
+ def test_wrong_token_rejected(client, with_token):
85
+ r = client.post("/a2a", json=_rpc_body(), headers={"Authorization": "Bearer wrong"})
86
+ err = r.json().get("error") or {}
87
+ assert err.get("code") == ERR_UNAUTHORIZED
88
+
89
+
90
+ def test_non_bearer_scheme_rejected(client, with_token):
91
+ r = client.post("/a2a", json=_rpc_body(), headers={"Authorization": f"Basic {TOKEN}"})
92
+ err = r.json().get("error") or {}
93
+ assert err.get("code") == ERR_UNAUTHORIZED
94
+
95
+
96
+ # ---------- 4. 配置 token + 正确 token:通过 ----------
97
+ def test_correct_token_accepted(client, with_token, monkeypatch):
98
+ """认证通过后应进入业务逻辑(此处用假 run_chat_turn 隔离)。"""
99
+ import codeagent.server.a2a.handlers as h
100
+
101
+ async def fake_turn(**kwargs):
102
+ return {"text": "pong", "tools_used": [], "token_usage": {}}
103
+
104
+ monkeypatch.setattr(h, "run_chat_turn", fake_turn, raising=False)
105
+ r = client.post("/a2a", json=_rpc_body(), headers={"Authorization": f"Bearer {TOKEN}"})
106
+ assert r.status_code == 200
107
+ assert "error" not in r.json()
108
+
109
+
110
+ def test_token_case_insensitive_scheme(client, with_token, monkeypatch):
111
+ """`bearer` 小写也应被接受(HTTP 认证方案大小写不敏感)。"""
112
+ import codeagent.server.a2a.handlers as h
113
+
114
+ async def fake_turn(**kwargs):
115
+ return {"text": "pong", "tools_used": [], "token_usage": {}}
116
+
117
+ monkeypatch.setattr(h, "run_chat_turn", fake_turn, raising=False)
118
+ r = client.post("/a2a", json=_rpc_body(), headers={"Authorization": f"bearer {TOKEN}"})
119
+ assert "error" not in r.json()
120
+
121
+
122
+ # ---------- 5. Agent Card 声明与实现一致 ----------
123
+ def test_card_declares_security_when_token_configured(with_token):
124
+ card = build_agent_card()
125
+ assert "security" in card
126
+ assert "bearer" in card.get("securitySchemes", {})
127
+
128
+
129
+ def test_card_omits_security_when_no_token(no_token):
130
+ card = build_agent_card()
131
+ assert "security" not in card
132
+ assert "securitySchemes" not in card
133
+
134
+
135
+ # ---------- check_request 单元级 ----------
136
+ def test_check_request_uses_constant_time_compare(with_token):
137
+ class _Req:
138
+ headers = {"authorization": f"Bearer {TOKEN}"}
139
+
140
+ assert check_request(_Req()) is True
141
+
142
+
143
+ def test_check_request_empty_header(with_token):
144
+ class _Req:
145
+ headers = {}
146
+
147
+ assert check_request(_Req()) is False