cade-cli 0.20.0__tar.gz → 0.20.2__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 (222) hide show
  1. {cade_cli-0.20.0 → cade_cli-0.20.2}/PKG-INFO +2 -1
  2. {cade_cli-0.20.0 → cade_cli-0.20.2}/pyproject.toml +2 -1
  3. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.claude-plugin/plugin.json +1 -1
  4. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.cursor-plugin/plugin.json +1 -1
  5. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/http_client.py +18 -8
  6. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/openai.py +12 -6
  7. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/telemetry/otel.py +188 -25
  8. {cade_cli-0.20.0 → cade_cli-0.20.2}/.gitignore +0 -0
  9. {cade_cli-0.20.0 → cade_cli-0.20.2}/LICENSE +0 -0
  10. {cade_cli-0.20.0 → cade_cli-0.20.2}/README.md +0 -0
  11. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/__init__.py +0 -0
  12. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/__main__.py +0 -0
  13. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/_metadata.py +0 -0
  14. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/config.py +0 -0
  15. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/errors.py +0 -0
  16. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/grants.py +0 -0
  17. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/resources.py +0 -0
  18. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/sandbox.py +0 -0
  19. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/server.py +0 -0
  20. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/__init__.py +0 -0
  21. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/_read_cache.py +0 -0
  22. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/access.py +0 -0
  23. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/agents.py +0 -0
  24. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/filesystem.py +0 -0
  25. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/git.py +0 -0
  26. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/notifications.py +0 -0
  27. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/questions.py +0 -0
  28. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/results.py +0 -0
  29. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/schemas.py +0 -0
  30. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/search.py +0 -0
  31. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/shell.py +0 -0
  32. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/tasks.py +0 -0
  33. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/web_fetch.py +0 -0
  34. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/web_search.py +0 -0
  35. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/utils.py +0 -0
  36. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/__init__.py +0 -0
  37. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/arcade_client.py +0 -0
  38. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/cache.py +0 -0
  39. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/extract.py +0 -0
  40. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/markdown.py +0 -0
  41. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/preapproved.py +0 -0
  42. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/prompts.py +0 -0
  43. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/scrape.py +0 -0
  44. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/__init__.py +0 -0
  45. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/arcade.py +0 -0
  46. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/base.py +0 -0
  47. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/http.py +0 -0
  48. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/__init__.py +0 -0
  49. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ai/__init__.py +0 -0
  50. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ai/prompts.py +0 -0
  51. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/__init__.py +0 -0
  52. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/app.py +0 -0
  53. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/auth.py +0 -0
  54. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/__init__.py +0 -0
  55. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/account.py +0 -0
  56. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/agent_group.py +0 -0
  57. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/agents.py +0 -0
  58. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/auth.py +0 -0
  59. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/chat.py +0 -0
  60. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/cron.py +0 -0
  61. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/daemon.py +0 -0
  62. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/grant.py +0 -0
  63. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/hooks.py +0 -0
  64. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/mcp.py +0 -0
  65. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/mem.py +0 -0
  66. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/tasks.py +0 -0
  67. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/thread.py +0 -0
  68. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/tools.py +0 -0
  69. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/mcp_hosts.py +0 -0
  70. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/onboarding.py +0 -0
  71. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/ui.py +0 -0
  72. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/__init__.py +0 -0
  73. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/agent.py +0 -0
  74. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/binary.py +0 -0
  75. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/comms.py +0 -0
  76. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/config.py +0 -0
  77. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/constants.py +0 -0
  78. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/errors.py +0 -0
  79. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/frozen.py +0 -0
  80. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/git.py +0 -0
  81. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/inbox.py +0 -0
  82. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/logging.py +0 -0
  83. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/memory.py +0 -0
  84. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/model_specs.py +0 -0
  85. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/names.py +0 -0
  86. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/paths.py +0 -0
  87. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/runtime.py +0 -0
  88. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/secrets.py +0 -0
  89. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/spawn.py +0 -0
  90. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/text.py +0 -0
  91. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/types.py +0 -0
  92. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/__init__.py +0 -0
  93. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/__init__.py +0 -0
  94. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/base.py +0 -0
  95. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/factory.py +0 -0
  96. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/imessage.py +0 -0
  97. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/registry.py +0 -0
  98. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/stub.py +0 -0
  99. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/telegram.py +0 -0
  100. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/allowlist.py +0 -0
  101. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/attach.py +0 -0
  102. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/chunker.py +0 -0
  103. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/client.py +0 -0
  104. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/commands.py +0 -0
  105. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/config.py +0 -0
  106. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/control.py +0 -0
  107. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/cron.py +0 -0
  108. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/elicitation.py +0 -0
  109. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/ensure.py +0 -0
  110. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/format.py +0 -0
  111. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/__init__.py +0 -0
  112. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/app.py +0 -0
  113. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/auth.py +0 -0
  114. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/constants.py +0 -0
  115. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/elicitation.py +0 -0
  116. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/exposure.py +0 -0
  117. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/host_agent.py +0 -0
  118. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/limits.py +0 -0
  119. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/middleware.py +0 -0
  120. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/resources.py +0 -0
  121. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/subscriptions.py +0 -0
  122. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/turn_bridge.py +0 -0
  123. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/grants.py +0 -0
  124. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/inbox.py +0 -0
  125. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/inbox_cron.py +0 -0
  126. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/install.py +0 -0
  127. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/lock.py +0 -0
  128. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/mesh.py +0 -0
  129. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/message_pump.py +0 -0
  130. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/observability.py +0 -0
  131. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/owner.py +0 -0
  132. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/progress_sink.py +0 -0
  133. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/registry.py +0 -0
  134. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/router.py +0 -0
  135. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/runner.py +0 -0
  136. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/runner_main.py +0 -0
  137. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/secrets.py +0 -0
  138. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/secrets_broker.py +0 -0
  139. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/server.py +0 -0
  140. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/services.py +0 -0
  141. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/sinks.py +0 -0
  142. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/supervisor.py +0 -0
  143. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/tokens.py +0 -0
  144. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/traffic.py +0 -0
  145. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/turns.py +0 -0
  146. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/wake.py +0 -0
  147. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/__init__.py +0 -0
  148. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/context_window.py +0 -0
  149. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/mcp_schema_index.py +0 -0
  150. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/orchestrator.py +0 -0
  151. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/parallel.py +0 -0
  152. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/tool_result_store.py +0 -0
  153. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/tool_schema_cache.py +0 -0
  154. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/__init__.py +0 -0
  155. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/config.py +0 -0
  156. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/engine.py +0 -0
  157. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/__init__.py +0 -0
  158. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/callback.py +0 -0
  159. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/command.py +0 -0
  160. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/http.py +0 -0
  161. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/prompt.py +0 -0
  162. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
  163. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/matchers.py +0 -0
  164. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/registry.py +0 -0
  165. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/types.py +0 -0
  166. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/__init__.py +0 -0
  167. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.claude-plugin/marketplace.json +0 -0
  168. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.cursor-plugin/marketplace.json +0 -0
  169. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/agents/operator.md +0 -0
  170. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/commands/ask.md +0 -0
  171. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/commands/memory.md +0 -0
  172. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/commands/tasks.md +0 -0
  173. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/mcp.json +0 -0
  174. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/rules/gateway.mdc +0 -0
  175. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/skills/ask/SKILL.md +0 -0
  176. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/skills/memory/SKILL.md +0 -0
  177. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/skills/tasks/SKILL.md +0 -0
  178. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/doctor.py +0 -0
  179. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/install.py +0 -0
  180. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/paths.py +0 -0
  181. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/state.py +0 -0
  182. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/__init__.py +0 -0
  183. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/anthropic.py +0 -0
  184. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/base.py +0 -0
  185. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/ollama.py +0 -0
  186. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/storage/__init__.py +0 -0
  187. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/storage/threads.py +0 -0
  188. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/__init__.py +0 -0
  189. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/scheduler.py +0 -0
  190. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/store.py +0 -0
  191. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/types.py +0 -0
  192. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/telemetry/__init__.py +0 -0
  193. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/login_failed.html +0 -0
  194. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/login_success.html +0 -0
  195. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
  196. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/serve/serve.toml.example +0 -0
  197. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
  198. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/styles.css +0 -0
  199. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/__init__.py +0 -0
  200. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/__init__.py +0 -0
  201. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
  202. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/base.py +0 -0
  203. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/composite.py +0 -0
  204. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/config.py +0 -0
  205. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/mcp.py +0 -0
  206. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/__init__.py +0 -0
  207. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/discovered.py +0 -0
  208. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/scoring.py +0 -0
  209. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/service.py +0 -0
  210. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/__init__.py +0 -0
  211. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/completion.py +0 -0
  212. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/display.py +0 -0
  213. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/elicitation.py +0 -0
  214. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/input.py +0 -0
  215. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/session.py +0 -0
  216. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/slash.py +0 -0
  217. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/__init__.py +0 -0
  218. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/audio.py +0 -0
  219. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/cleanup.py +0 -0
  220. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/session.py +0 -0
  221. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/stt.py +0 -0
  222. {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/tts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cade-cli
3
- Version: 0.20.0
3
+ Version: 0.20.2
4
4
  Summary: Cade - The CLI Agent from Arcade.dev (install: curl -fsSL https://arcadeagent.dev/install | sh)
5
5
  Project-URL: Homepage, https://arcade.dev
6
6
  Project-URL: Documentation, https://arcadeagent.dev/docs
@@ -78,6 +78,7 @@ Requires-Dist: mkdocs-material<10.0.0,>=9.5.0; extra == 'docs'
78
78
  Requires-Dist: mkdocs<2.0.0,>=1.6.0; extra == 'docs'
79
79
  Provides-Extra: otel
80
80
  Requires-Dist: opentelemetry-api<2.0.0,>=1.27.0; extra == 'otel'
81
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc<2.0.0,>=1.27.0; extra == 'otel'
81
82
  Requires-Dist: opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0; extra == 'otel'
82
83
  Requires-Dist: opentelemetry-sdk<2.0.0,>=1.27.0; extra == 'otel'
83
84
  Provides-Extra: training
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cade-cli"
3
- version = "0.20.0"
3
+ version = "0.20.2"
4
4
  description = "Cade - The CLI Agent from Arcade.dev (install: curl -fsSL https://arcadeagent.dev/install | sh)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -89,6 +89,7 @@ otel = [
89
89
  "opentelemetry-api>=1.27.0,<2.0.0",
90
90
  "opentelemetry-sdk>=1.27.0,<2.0.0",
91
91
  "opentelemetry-exporter-otlp-proto-http>=1.27.0,<2.0.0",
92
+ "opentelemetry-exporter-otlp-proto-grpc>=1.27.0,<2.0.0",
92
93
  ]
93
94
  training = [
94
95
  "torch>=2.1.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cade",
3
3
  "description": "Your persistent on-device agent: notes memory, durable tasks, and connected tools via Ask.",
4
- "version": "0.20.0",
4
+ "version": "0.20.2",
5
5
  "author": {
6
6
  "name": "Arcade.dev",
7
7
  "url": "https://docs.cade.dev"
@@ -2,7 +2,7 @@
2
2
  "name": "cade",
3
3
  "displayName": "Cade",
4
4
  "description": "Your persistent on-device agent: notes memory, durable tasks, and connected tools via Ask.",
5
- "version": "0.20.0",
5
+ "version": "0.20.2",
6
6
  "author": {
7
7
  "name": "Arcade.dev",
8
8
  "email": "support@arcade.dev"
@@ -1,9 +1,11 @@
1
1
  """Shared httpx client helpers for LLM providers.
2
2
 
3
3
  Frozen/PyInstaller builds have hit ``zlib.error: Error -3 while decompressing
4
- data: incorrect header check`` on Anthropic SSE streams when httpx negotiates
5
- brotli (``br``) or otherwise mishandles ``Content-Encoding``. Pinning
6
- ``Accept-Encoding`` to gzip/deflate avoids that path while keeping compression.
4
+ data: incorrect header check`` on Anthropic/OpenAI-compatible SSE streams when
5
+ httpx negotiates compression and the body is not actually compressed — common
6
+ with local vLLM/Ollama and some proxies. Default ``Accept-Encoding: identity``
7
+ so the client never attempts to decompress streams. Override with
8
+ ``CADE_HTTP_ACCEPT_ENCODING`` (e.g. ``gzip, deflate``) if you want compression.
7
9
 
8
10
  Also: shell profiles often export ``SSL_CERT_FILE`` / ``REQUESTS_CA_BUNDLE`` to a
9
11
  path that exists on one machine and not another. httpx then raises
@@ -20,9 +22,10 @@ from pathlib import Path
20
22
 
21
23
  import httpx
22
24
 
23
- # Do not advertise ``br`` / ``zstd`` — those decoders are fragile in frozen
24
- # binaries and have produced "incorrect header check" failures on streams.
25
- _SAFE_ACCEPT_ENCODING = "gzip, deflate"
25
+ # Never advertise ``br`` / ``zstd`` / ``gzip`` by default — those decoders are
26
+ # fragile in frozen binaries and with OpenAI-compatible SSE servers that omit
27
+ # or mis-set Content-Encoding ("incorrect header check").
28
+ _DEFAULT_ACCEPT_ENCODING = "identity"
26
29
 
27
30
  _SSL_ENV_KEYS = ("SSL_CERT_FILE", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")
28
31
 
@@ -30,6 +33,12 @@ _SSL_ENV_KEYS = ("SSL_CERT_FILE", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")
30
33
  CLEARED_SSL_ENV_KEYS: list[str] = []
31
34
 
32
35
 
36
+ def accept_encoding_header() -> str:
37
+ """Return the Accept-Encoding value for provider HTTP clients."""
38
+ raw = os.environ.get("CADE_HTTP_ACCEPT_ENCODING", "").strip()
39
+ return raw or _DEFAULT_ACCEPT_ENCODING
40
+
41
+
33
42
  def scrub_broken_ssl_env() -> list[str]:
34
43
  """Unset SSL CA env vars that point at missing files.
35
44
 
@@ -94,7 +103,7 @@ def make_async_http_client(*, timeout: float = 600.0) -> httpx.AsyncClient:
94
103
  """Return an AsyncClient safe for Anthropic/OpenAI streaming in frozen apps."""
95
104
  prepare_ssl_env()
96
105
  return httpx.AsyncClient(
97
- headers={"Accept-Encoding": _SAFE_ACCEPT_ENCODING},
106
+ headers={"Accept-Encoding": accept_encoding_header()},
98
107
  timeout=httpx.Timeout(timeout, connect=10.0),
99
108
  follow_redirects=True,
100
109
  verify=ssl_verify(),
@@ -105,7 +114,7 @@ def make_sync_http_client(*, timeout: float = 60.0) -> httpx.Client:
105
114
  """Return a sync Client with the same encoding constraints."""
106
115
  prepare_ssl_env()
107
116
  return httpx.Client(
108
- headers={"Accept-Encoding": _SAFE_ACCEPT_ENCODING},
117
+ headers={"Accept-Encoding": accept_encoding_header()},
109
118
  timeout=httpx.Timeout(timeout, connect=10.0),
110
119
  follow_redirects=True,
111
120
  verify=ssl_verify(),
@@ -126,6 +135,7 @@ def is_decompress_error(exc: BaseException) -> bool:
126
135
 
127
136
  __all__ = [
128
137
  "CLEARED_SSL_ENV_KEYS",
138
+ "accept_encoding_header",
129
139
  "is_decompress_error",
130
140
  "make_async_http_client",
131
141
  "make_sync_http_client",
@@ -220,12 +220,18 @@ class OpenAIProvider(Provider):
220
220
  # Ask OpenAI (and compatible servers that honor it) for a final usage
221
221
  # chunk so OTEL spans can record prompt/completion tokens. Some local
222
222
  # OpenAI-compatible servers reject stream_options — fall back cleanly.
223
- want_usage = os.environ.get("CADE_OTEL", "").strip().lower() in (
224
- "1",
225
- "true",
226
- "yes",
227
- "on",
228
- )
223
+ want_usage = False
224
+ if os.environ.get("CADE_OTEL", "").strip().lower() in ("1", "true", "yes", "on"):
225
+ # Official OpenAI honors stream_options; many local OpenAI-compatible
226
+ # servers (vLLM, etc.) mishandle it. Opt in for custom base_url with
227
+ # CADE_OTEL_STREAM_USAGE=1.
228
+ force_usage = os.environ.get("CADE_OTEL_STREAM_USAGE", "").strip().lower() in (
229
+ "1",
230
+ "true",
231
+ "yes",
232
+ "on",
233
+ )
234
+ want_usage = force_usage or not self.base_url
229
235
  if want_usage:
230
236
  openai_request["stream_options"] = {"include_usage": True}
231
237
 
@@ -2,21 +2,31 @@
2
2
 
3
3
  Soft-depends on ``opentelemetry-*``: when the extra is missing or tracing is
4
4
  disabled, every public helper is a no-op so callers never need a try/except.
5
+
6
+ Export defaults to OTLP HTTP at Phoenix (``http://127.0.0.1:6006/v1/traces``).
7
+ When the optional gRPC exporter is installed and Phoenix's gRPC port is
8
+ reachable, prefer gRPC (``127.0.0.1:4317``) — Phoenix's HTTP OTLP path is
9
+ known to intermittently reset connections under load, which used to dump
10
+ full SDK tracebacks into the chat TTY.
5
11
  """
6
12
 
7
13
  from __future__ import annotations
8
14
 
9
15
  import json
16
+ import logging
10
17
  import os
11
18
  import time
12
- from collections.abc import Iterator
19
+ from collections.abc import Iterator, Sequence
13
20
  from contextlib import contextmanager
14
21
  from typing import Any
22
+ from urllib.parse import urlparse
15
23
 
16
24
  from cadecoder.core.logging import log
17
25
 
18
26
  _TRACER_NAME = "cade"
19
- _DEFAULT_ENDPOINT = "http://127.0.0.1:6006/v1/traces"
27
+ _DEFAULT_HTTP_ENDPOINT = "http://127.0.0.1:6006/v1/traces"
28
+ _DEFAULT_GRPC_ENDPOINT = "127.0.0.1:4317"
29
+ _MAX_MESSAGE_PART_ATTRS = 40
20
30
  _initialized = False
21
31
  _enabled = False
22
32
 
@@ -30,6 +40,143 @@ def is_enabled() -> bool:
30
40
  return _enabled
31
41
 
32
42
 
43
+ def _silence_otel_loggers() -> None:
44
+ """Keep OTEL SDK export failures out of the chat TTY.
45
+
46
+ Cade configures file-only logging and clears root handlers. Without a
47
+ handler, Python's ``lastResort`` prints WARNING+ (including the OTEL
48
+ exporter's ``logging.exception`` tracebacks) straight to stderr — which
49
+ lands in the middle of ``cade chat``.
50
+ """
51
+ for name in (
52
+ "opentelemetry",
53
+ "opentelemetry.sdk",
54
+ "opentelemetry.sdk.trace",
55
+ "opentelemetry.sdk.trace.export",
56
+ "opentelemetry.exporter",
57
+ "opentelemetry.exporter.otlp",
58
+ "opentelemetry.exporter.otlp.proto.http",
59
+ "opentelemetry.exporter.otlp.proto.http.trace_exporter",
60
+ "opentelemetry.exporter.otlp.proto.grpc",
61
+ "opentelemetry.exporter.otlp.proto.grpc.trace_exporter",
62
+ ):
63
+ logger = logging.getLogger(name)
64
+ logger.handlers.clear()
65
+ logger.addHandler(logging.NullHandler())
66
+ logger.propagate = False
67
+ logger.setLevel(logging.CRITICAL)
68
+
69
+
70
+ class _QuietSpanExporter:
71
+ """Wrap an OTEL span exporter so failures stay out of the chat UI."""
72
+
73
+ def __init__(self, inner: Any, *, label: str) -> None:
74
+ self._inner = inner
75
+ self._label = label
76
+ self._failures = 0
77
+
78
+ def export(self, spans: Sequence[Any]) -> Any:
79
+ from opentelemetry.sdk.trace.export import SpanExportResult
80
+
81
+ try:
82
+ result = self._inner.export(spans)
83
+ except Exception as exc: # noqa: BLE001
84
+ self._note_failure(exc)
85
+ return SpanExportResult.FAILURE
86
+ if result == SpanExportResult.FAILURE:
87
+ self._note_failure(RuntimeError(f"{self._label} returned FAILURE"))
88
+ return result
89
+
90
+ def shutdown(self) -> None:
91
+ shutdown = getattr(self._inner, "shutdown", None)
92
+ if callable(shutdown):
93
+ try:
94
+ shutdown()
95
+ except Exception as exc: # noqa: BLE001
96
+ log.debug(f"otel exporter shutdown failed: {exc}")
97
+
98
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
99
+ flush = getattr(self._inner, "force_flush", None)
100
+ if not callable(flush):
101
+ return True
102
+ try:
103
+ return bool(flush(timeout_millis))
104
+ except Exception as exc: # noqa: BLE001
105
+ self._note_failure(exc)
106
+ return False
107
+
108
+ def _note_failure(self, exc: BaseException) -> None:
109
+ self._failures += 1
110
+ # Rate-limit: first 3, then every 25th — enough signal without spam.
111
+ if self._failures <= 3 or self._failures % 25 == 0:
112
+ log.warning(
113
+ f"otel export via {self._label} failed ({self._failures}x): {exc} "
114
+ f"— is Phoenix up? (`make otel-ui`, endpoint "
115
+ f"{os.environ.get('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') or _DEFAULT_HTTP_ENDPOINT})"
116
+ )
117
+
118
+
119
+ def _resolve_http_endpoint() -> str:
120
+ endpoint = (
121
+ os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
122
+ or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
123
+ or _DEFAULT_HTTP_ENDPOINT
124
+ ).rstrip("/")
125
+ if not endpoint.endswith("/v1/traces"):
126
+ endpoint = f"{endpoint}/v1/traces"
127
+ return endpoint
128
+
129
+
130
+ def _resolve_grpc_endpoint(http_endpoint: str) -> str:
131
+ explicit = (
132
+ os.environ.get("OTEL_EXPORTER_OTLP_TRACES_GRPC_ENDPOINT")
133
+ or os.environ.get("CADE_OTEL_GRPC_ENDPOINT")
134
+ or ""
135
+ ).strip()
136
+ if explicit:
137
+ return explicit.removeprefix("http://").removeprefix("https://")
138
+ # When pointing at local Phoenix HTTP, use its companion gRPC port.
139
+ parsed = urlparse(http_endpoint)
140
+ host = parsed.hostname or "127.0.0.1"
141
+ if host in ("127.0.0.1", "localhost", "::1") and (parsed.port in (6006, None)):
142
+ return f"{host}:4317"
143
+ return _DEFAULT_GRPC_ENDPOINT
144
+
145
+
146
+ def _build_exporter() -> tuple[Any, str]:
147
+ """Return ``(exporter, label)`` preferring gRPC for local Phoenix."""
148
+ from opentelemetry.exporter.otlp.proto.http import Compression
149
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
150
+
151
+ http_endpoint = _resolve_http_endpoint()
152
+ prefer_grpc = not _truthy("CADE_OTEL_HTTP_ONLY")
153
+
154
+ if prefer_grpc:
155
+ try:
156
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
157
+ OTLPSpanExporter as GrpcOTLPSpanExporter,
158
+ )
159
+
160
+ grpc_endpoint = _resolve_grpc_endpoint(http_endpoint)
161
+ exporter = GrpcOTLPSpanExporter(endpoint=grpc_endpoint, insecure=True)
162
+ return exporter, f"otlp/grpc:{grpc_endpoint}"
163
+ except ImportError:
164
+ pass
165
+ except Exception as exc: # noqa: BLE001
166
+ log.debug(f"otel gRPC exporter unavailable, falling back to HTTP: {exc}")
167
+
168
+ try:
169
+ exporter = OTLPSpanExporter(
170
+ endpoint=http_endpoint,
171
+ compression=Compression.Gzip,
172
+ timeout=5,
173
+ )
174
+ except TypeError:
175
+ # Older exporter builds may not accept compression=.
176
+ exporter = OTLPSpanExporter(endpoint=http_endpoint, timeout=5)
177
+ return exporter, f"otlp/http:{http_endpoint}"
178
+
179
+
33
180
  def init_telemetry(*, force: bool = False) -> bool:
34
181
  """Initialize the tracer provider when ``CADE_OTEL=1``.
35
182
 
@@ -49,7 +196,6 @@ def init_telemetry(*, force: bool = False) -> bool:
49
196
 
50
197
  try:
51
198
  from opentelemetry import trace
52
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
53
199
  from opentelemetry.sdk.resources import Resource
54
200
  from opentelemetry.sdk.trace import TracerProvider
55
201
  from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
@@ -61,15 +207,17 @@ def init_telemetry(*, force: bool = False) -> bool:
61
207
  _enabled = False
62
208
  return False
63
209
 
210
+ _silence_otel_loggers()
211
+
64
212
  service_name = os.environ.get("OTEL_SERVICE_NAME", "cade").strip() or "cade"
65
- endpoint = (
66
- os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
67
- or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
68
- or _DEFAULT_ENDPOINT
69
- ).rstrip("/")
70
- if not endpoint.endswith("/v1/traces"):
71
- endpoint = f"{endpoint}/v1/traces"
213
+ try:
214
+ raw_exporter, label = _build_exporter()
215
+ except Exception as exc: # noqa: BLE001
216
+ log.warning(f"CADE_OTEL=1 but exporter setup failed: {exc}")
217
+ _enabled = False
218
+ return False
72
219
 
220
+ exporter = _QuietSpanExporter(raw_exporter, label=label)
73
221
  resource = Resource.create(
74
222
  {
75
223
  "service.name": service_name,
@@ -78,19 +226,26 @@ def init_telemetry(*, force: bool = False) -> bool:
78
226
  }
79
227
  )
80
228
  provider = TracerProvider(resource=resource)
81
- exporter = OTLPSpanExporter(endpoint=endpoint)
82
- # Simple processor in tests / when CADE_OTEL_SYNC=1 so spans flush immediately.
83
229
  if _truthy("CADE_OTEL_SYNC"):
84
230
  provider.add_span_processor(SimpleSpanProcessor(exporter))
85
231
  else:
86
- provider.add_span_processor(BatchSpanProcessor(exporter))
232
+ # Small batches + short delay: Phoenix's HTTP OTLP path is flaky with
233
+ # large concurrent exports (connection resets mid-batch).
234
+ provider.add_span_processor(
235
+ BatchSpanProcessor(
236
+ exporter,
237
+ max_queue_size=2048,
238
+ schedule_delay_millis=500,
239
+ max_export_batch_size=16,
240
+ export_timeout_millis=5000,
241
+ )
242
+ )
87
243
  trace.set_tracer_provider(provider)
88
244
  _enabled = True
89
- # Flush on process exit so short-lived CLI turns still export.
90
245
  import atexit
91
246
 
92
247
  atexit.register(shutdown_telemetry)
93
- log.info(f"OpenTelemetry tracing enabled → {endpoint} (service={service_name})")
248
+ log.info(f"OpenTelemetry tracing enabled → {label} (service={service_name})")
94
249
  return True
95
250
 
96
251
 
@@ -220,8 +375,8 @@ def trace_tool_call(
220
375
  "openinference.span.kind": "TOOL",
221
376
  "tool.name": name,
222
377
  "tool.call_id": tool_call_id or None,
223
- "tool.parameters": _truncate(arguments) if arguments is not None else None,
224
- "input.value": _truncate(arguments) if arguments is not None else None,
378
+ "tool.parameters": _truncate(arguments, 512) if arguments is not None else None,
379
+ "input.value": _truncate(arguments, 512) if arguments is not None else None,
225
380
  },
226
381
  ) as span:
227
382
  started = time.perf_counter()
@@ -268,12 +423,15 @@ def record_message_parts(
268
423
  """Record per-message role + estimated tokens for the prompt sent to the LLM.
269
424
 
270
425
  Uses a cheap length heuristic so every thread message is visible in the
271
- trace even when the provider does not report official usage yet.
426
+ trace even when the provider does not report official usage yet. Caps
427
+ per-message attributes so long threads (email triage, etc.) do not blow
428
+ up OTLP payloads.
272
429
  """
273
430
  if span is None or not messages:
274
431
  return
275
432
  by_role: dict[str, int] = {}
276
433
  parts: list[dict[str, Any]] = []
434
+ attributed = 0
277
435
  for index, message in enumerate(messages):
278
436
  role = str(message.get("role") or "unknown")
279
437
  content = message.get("content")
@@ -293,15 +451,18 @@ def record_message_parts(
293
451
  "index": index,
294
452
  "role": role,
295
453
  "estimated_tokens": estimated,
296
- "content": _truncate(text, 240),
454
+ "content": _truncate(text, 160),
297
455
  }
298
456
  )
299
- span.set_attribute(f"cade.message.{index}.role", role)
300
- span.set_attribute(f"cade.message.{index}.estimated_tokens", estimated)
301
- span.set_attribute("cade.messages.count", len(parts))
457
+ if attributed < _MAX_MESSAGE_PART_ATTRS:
458
+ span.set_attribute(f"cade.message.{index}.role", role)
459
+ span.set_attribute(f"cade.message.{index}.estimated_tokens", estimated)
460
+ attributed += 1
461
+ span.set_attribute("cade.messages.count", len(messages))
302
462
  for role, tokens in by_role.items():
303
463
  span.set_attribute(f"cade.tokens.by_role.{role}", tokens)
304
- span.set_attribute("llm.input_messages", _truncate(parts, 8192))
464
+ # Bounded JSON summary — enough for Phoenix, small enough for OTLP HTTP.
465
+ span.set_attribute("llm.input_messages", _truncate(parts[-_MAX_MESSAGE_PART_ATTRS:], 4096))
305
466
 
306
467
 
307
468
  def _record_exception(span: Any, exc: BaseException) -> None:
@@ -316,7 +477,7 @@ def _record_exception(span: Any, exc: BaseException) -> None:
316
477
  pass
317
478
 
318
479
 
319
- def set_span_output(span: Any, value: Any, *, limit: int = 2048) -> None:
480
+ def set_span_output(span: Any, value: Any, *, limit: int = 1024) -> None:
320
481
  """Record a truncated output payload on the span."""
321
482
  if span is None or value is None:
322
483
  return
@@ -337,7 +498,9 @@ def set_tool_result(
337
498
  span.set_attribute("cade.tool.status", status)
338
499
  if content is not None:
339
500
  span.set_attribute("cade.tool.result_bytes", len(content.encode("utf-8", errors="replace")))
340
- set_span_output(span, content)
501
+ # Keep tool payloads small — Gmail/list results are huge and Phoenix's
502
+ # HTTP OTLP path resets connections on oversized batches.
503
+ set_span_output(span, content, limit=512)
341
504
  if error:
342
505
  span.set_attribute("cade.tool.error", _truncate(error, 512))
343
506
  try:
File without changes
File without changes
File without changes