cade-cli 0.19.2__tar.gz → 0.20.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.
- {cade_cli-0.19.2 → cade_cli-0.20.0}/PKG-INFO +8 -4
- {cade_cli-0.19.2 → cade_cli-0.20.0}/README.md +3 -3
- {cade_cli-0.19.2 → cade_cli-0.20.0}/pyproject.toml +6 -1
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/app.py +5 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/runner_main.py +3 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/server.py +4 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/orchestrator.py +110 -61
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/parallel.py +70 -53
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/.claude-plugin/plugin.json +1 -1
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/.cursor-plugin/plugin.json +1 -1
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/providers/anthropic.py +19 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/providers/ollama.py +15 -4
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/providers/openai.py +48 -5
- cade_cli-0.20.0/src/cadecoder/telemetry/__init__.py +45 -0
- cade_cli-0.20.0/src/cadecoder/telemetry/otel.py +349 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/.gitignore +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/LICENSE +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/__main__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/_metadata.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/config.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/errors.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/grants.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/resources.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/sandbox.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/server.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/_read_cache.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/access.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/agents.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/filesystem.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/git.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/notifications.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/questions.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/results.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/schemas.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/search.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/shell.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/tasks.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/web_fetch.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/tools/web_search.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/utils.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/arcade_client.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/cache.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/extract.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/markdown.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/preapproved.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/prompts.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/scrape.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/search/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/search/arcade.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/search/base.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cade_mcp_local/web/search/http.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ai/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ai/prompts.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/auth.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/account.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/agent_group.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/agents.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/auth.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/chat.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/cron.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/daemon.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/grant.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/hooks.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/mcp.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/mem.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/tasks.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/thread.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/commands/tools.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/mcp_hosts.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/onboarding.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/cli/ui.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/agent.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/binary.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/comms.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/config.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/constants.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/errors.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/frozen.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/git.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/inbox.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/logging.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/memory.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/model_specs.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/names.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/paths.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/runtime.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/secrets.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/spawn.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/text.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/core/types.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/base.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/factory.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/imessage.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/registry.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/stub.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/adapters/telegram.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/allowlist.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/attach.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/chunker.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/client.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/commands.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/config.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/control.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/cron.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/elicitation.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/ensure.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/format.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/app.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/auth.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/constants.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/elicitation.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/exposure.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/host_agent.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/limits.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/middleware.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/resources.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/subscriptions.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/gateway/turn_bridge.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/grants.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/inbox.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/inbox_cron.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/install.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/lock.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/mesh.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/message_pump.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/observability.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/owner.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/progress_sink.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/registry.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/router.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/runner.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/secrets.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/secrets_broker.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/services.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/sinks.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/supervisor.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/tokens.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/traffic.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/turns.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/daemon/wake.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/context_window.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/mcp_schema_index.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/tool_result_store.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/execution/tool_schema_cache.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/config.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/engine.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/executors/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/executors/callback.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/executors/command.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/executors/http.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/executors/prompt.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/matchers.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/registry.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/hooks/types.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/.claude-plugin/marketplace.json +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/.cursor-plugin/marketplace.json +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/agents/operator.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/commands/ask.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/commands/memory.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/commands/tasks.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/mcp.json +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/rules/gateway.mdc +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/skills/ask/SKILL.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/skills/memory/SKILL.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/skills/tasks/SKILL.md +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/doctor.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/install.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/paths.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/state.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/providers/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/providers/base.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/providers/http_client.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/storage/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/storage/threads.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tasks/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tasks/scheduler.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tasks/store.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tasks/types.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/templates/login_failed.html +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/templates/login_success.html +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/templates/serve/serve.toml.example +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/templates/styles.css +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/manager/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/manager/base.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/manager/composite.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/manager/config.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/manager/mcp.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/search/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/search/discovered.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/search/scoring.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/tools/search/service.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/completion.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/display.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/elicitation.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/input.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/session.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/ui/slash.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/voice/__init__.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/voice/audio.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/voice/cleanup.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/voice/session.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/voice/stt.py +0 -0
- {cade_cli-0.19.2 → cade_cli-0.20.0}/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.
|
|
3
|
+
Version: 0.20.0
|
|
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
|
|
@@ -76,6 +76,10 @@ Requires-Dist: ruff<1.0.0,>=0.5.0; extra == 'dev'
|
|
|
76
76
|
Provides-Extra: docs
|
|
77
77
|
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
|
+
Provides-Extra: otel
|
|
80
|
+
Requires-Dist: opentelemetry-api<2.0.0,>=1.27.0; extra == 'otel'
|
|
81
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0; extra == 'otel'
|
|
82
|
+
Requires-Dist: opentelemetry-sdk<2.0.0,>=1.27.0; extra == 'otel'
|
|
79
83
|
Provides-Extra: training
|
|
80
84
|
Requires-Dist: accelerate>=0.27.0; extra == 'training'
|
|
81
85
|
Requires-Dist: safetensors>=0.4.2; extra == 'training'
|
|
@@ -137,9 +141,9 @@ CADE_VERSION=0.16.9 CADE_INSTALL_DIR=/usr/local/bin \
|
|
|
137
141
|
sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
|
|
138
142
|
```
|
|
139
143
|
|
|
140
|
-
On Linux the installer
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
On Linux the installer ships the **cpu** build today (gpu release assets are
|
|
145
|
+
paused). Force a variant later with `CADE_VARIANT=cpu` or `CADE_VARIANT=gpu`
|
|
146
|
+
when gpu builds return.
|
|
143
147
|
|
|
144
148
|
### Alternatives
|
|
145
149
|
|
|
@@ -46,9 +46,9 @@ CADE_VERSION=0.16.9 CADE_INSTALL_DIR=/usr/local/bin \
|
|
|
46
46
|
sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
On Linux the installer
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
On Linux the installer ships the **cpu** build today (gpu release assets are
|
|
50
|
+
paused). Force a variant later with `CADE_VARIANT=cpu` or `CADE_VARIANT=gpu`
|
|
51
|
+
when gpu builds return.
|
|
52
52
|
|
|
53
53
|
### Alternatives
|
|
54
54
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cade-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.20.0"
|
|
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"
|
|
@@ -85,6 +85,11 @@ voice = [
|
|
|
85
85
|
# PyInstaller (see .github/workflows/release.yml), and cade.spec bundles it
|
|
86
86
|
# so misaki never downloads it at runtime in the frozen app.
|
|
87
87
|
]
|
|
88
|
+
otel = [
|
|
89
|
+
"opentelemetry-api>=1.27.0,<2.0.0",
|
|
90
|
+
"opentelemetry-sdk>=1.27.0,<2.0.0",
|
|
91
|
+
"opentelemetry-exporter-otlp-proto-http>=1.27.0,<2.0.0",
|
|
92
|
+
]
|
|
88
93
|
training = [
|
|
89
94
|
"torch>=2.1.0",
|
|
90
95
|
"transformers>=4.38.0",
|
|
@@ -365,6 +365,11 @@ def main_callback(
|
|
|
365
365
|
ctx.ensure_object(dict)
|
|
366
366
|
ctx.obj["verbose"] = verbose
|
|
367
367
|
|
|
368
|
+
# Optional OpenTelemetry (CADE_OTEL=1 + cade-cli[otel]). No-op otherwise.
|
|
369
|
+
from cadecoder.telemetry import init_telemetry
|
|
370
|
+
|
|
371
|
+
init_telemetry()
|
|
372
|
+
|
|
368
373
|
# Agent management and inspection commands manage their own (possibly empty)
|
|
369
374
|
# agent state and must not auto-create a `default` agent — that would clobber
|
|
370
375
|
# the empty-state UX of `cade list` / `cade status` on a fresh install.
|
|
@@ -27,6 +27,9 @@ async def _serve(agent_name: str, fd: int) -> None:
|
|
|
27
27
|
from cadecoder.core import agent as agent_mod
|
|
28
28
|
from cadecoder.daemon.control import ControlServer
|
|
29
29
|
from cadecoder.daemon.runner import InProcessRunner
|
|
30
|
+
from cadecoder.telemetry import init_telemetry
|
|
31
|
+
|
|
32
|
+
init_telemetry()
|
|
30
33
|
|
|
31
34
|
agent = agent_mod.resolve(agent_name)
|
|
32
35
|
if agent is None:
|
|
@@ -115,6 +115,10 @@ async def run_daemon(
|
|
|
115
115
|
# Same SSL_CERT_FILE footgun as the CLI — scrub before Telegram/MCP/Anthropic.
|
|
116
116
|
prepare_ssl_env()
|
|
117
117
|
|
|
118
|
+
from cadecoder.telemetry import init_telemetry
|
|
119
|
+
|
|
120
|
+
init_telemetry()
|
|
121
|
+
|
|
118
122
|
host, port = _parse_bind(bind)
|
|
119
123
|
_guard_auth_bind(host, require_auth)
|
|
120
124
|
prewarm = prewarm or []
|
|
@@ -246,9 +246,23 @@ class Orchestrator:
|
|
|
246
246
|
)
|
|
247
247
|
|
|
248
248
|
async def stream(self, context: ExecutionContext) -> AsyncIterator[ExecutionEvent]:
|
|
249
|
-
"""Stream execution events."""
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
"""Stream execution events, wrapping the turn in an OTEL span when enabled."""
|
|
250
|
+
from cadecoder.telemetry import set_span_output, trace_turn
|
|
251
|
+
|
|
252
|
+
with trace_turn(
|
|
253
|
+
thread_id=str(context.metadata.get("thread_id") or ""),
|
|
254
|
+
conversation_id=str(context.metadata.get("conversation_id") or ""),
|
|
255
|
+
agent=str(context.metadata.get("agent") or self._persona or ""),
|
|
256
|
+
model=self.default_model,
|
|
257
|
+
prompt=context.task,
|
|
258
|
+
) as turn_span:
|
|
259
|
+
reply_parts: list[str] = []
|
|
260
|
+
async for event in self._stream_inner(context):
|
|
261
|
+
if event.type == ExecutionEventType.CONTENT and event.content:
|
|
262
|
+
reply_parts.append(event.content)
|
|
263
|
+
yield event
|
|
264
|
+
if reply_parts:
|
|
265
|
+
set_span_output(turn_span, "".join(reply_parts))
|
|
252
266
|
|
|
253
267
|
async def _stream_inner(self, context: ExecutionContext) -> AsyncIterator[ExecutionEvent]:
|
|
254
268
|
"""Core streaming implementation."""
|
|
@@ -368,69 +382,104 @@ class Orchestrator:
|
|
|
368
382
|
invalid_tool_pairs = False
|
|
369
383
|
stream_error = False
|
|
370
384
|
|
|
385
|
+
from cadecoder.telemetry import (
|
|
386
|
+
record_message_parts,
|
|
387
|
+
record_usage,
|
|
388
|
+
set_span_output,
|
|
389
|
+
trace_llm_call,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
provider_name = ""
|
|
371
393
|
try:
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
394
|
+
provider_name = str(getattr(self.provider, "provider_type", "") or "")
|
|
395
|
+
except Exception: # noqa: BLE001
|
|
396
|
+
provider_name = ""
|
|
397
|
+
|
|
398
|
+
with trace_llm_call(
|
|
399
|
+
model=self.default_model,
|
|
400
|
+
iteration=iteration,
|
|
401
|
+
provider=provider_name,
|
|
402
|
+
estimated_prompt_tokens=context_status.get("token_count"),
|
|
403
|
+
) as llm_span:
|
|
404
|
+
record_message_parts(
|
|
405
|
+
llm_span,
|
|
406
|
+
messages,
|
|
407
|
+
chars_per_token=self.context_manager.chars_per_token,
|
|
408
|
+
)
|
|
409
|
+
try:
|
|
410
|
+
async for stream_event in self.provider.stream(request):
|
|
411
|
+
usage = stream_event.metadata.get("usage")
|
|
412
|
+
if usage:
|
|
413
|
+
record_usage(llm_span, usage)
|
|
414
|
+
|
|
415
|
+
# Handle error events from the provider
|
|
416
|
+
if stream_event.type == "error" and stream_event.content:
|
|
417
|
+
error_text = str(stream_event.content)
|
|
418
|
+
if "prompt is too long" in error_text or (
|
|
419
|
+
"too long" in error_text and "token" in error_text
|
|
420
|
+
):
|
|
421
|
+
log.warning(
|
|
422
|
+
f"Prompt too long for API, forcing compaction: "
|
|
423
|
+
f"{error_text[:200]}"
|
|
424
|
+
)
|
|
425
|
+
prompt_too_long = True
|
|
426
|
+
break
|
|
427
|
+
elif "tool_use" in error_text and "tool_result" in error_text:
|
|
428
|
+
# Orphaned tool_use/tool_result from bad compaction
|
|
429
|
+
log.warning(
|
|
430
|
+
f"Invalid tool_use/tool_result pairing, "
|
|
431
|
+
f"attempting repair: {error_text[:200]}"
|
|
432
|
+
)
|
|
433
|
+
invalid_tool_pairs = True
|
|
434
|
+
break
|
|
435
|
+
else:
|
|
436
|
+
# Surface non-recoverable stream errors to the user
|
|
437
|
+
log.error(f"Provider stream error: {error_text[:300]}")
|
|
438
|
+
yield ExecutionEvent(
|
|
439
|
+
type=ExecutionEventType.CONTENT,
|
|
440
|
+
content=f"\n\nError from provider: {error_text}\n",
|
|
441
|
+
)
|
|
442
|
+
stream_error = True
|
|
443
|
+
break
|
|
444
|
+
|
|
445
|
+
if stream_event.content:
|
|
446
|
+
accumulated_content += stream_event.content
|
|
395
447
|
yield ExecutionEvent(
|
|
396
448
|
type=ExecutionEventType.CONTENT,
|
|
397
|
-
content=
|
|
449
|
+
content=stream_event.content,
|
|
398
450
|
)
|
|
399
|
-
stream_error = True
|
|
400
|
-
break
|
|
401
|
-
|
|
402
|
-
if stream_event.content:
|
|
403
|
-
accumulated_content += stream_event.content
|
|
404
|
-
yield ExecutionEvent(
|
|
405
|
-
type=ExecutionEventType.CONTENT,
|
|
406
|
-
content=stream_event.content,
|
|
407
|
-
)
|
|
408
451
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
452
|
+
# Handle tool call events from provider
|
|
453
|
+
if stream_event.type == ExecutionEventType.TOOL_CALL.value:
|
|
454
|
+
tool_call = stream_event.metadata.get("tool_call")
|
|
455
|
+
if tool_call:
|
|
456
|
+
tool_calls.append(tool_call)
|
|
457
|
+
# Surface read-only classification so the UI can
|
|
458
|
+
# render mutating calls more prominently than
|
|
459
|
+
# idempotent reads. Best-effort — old/incompatible
|
|
460
|
+
# tool managers just won't set the flag.
|
|
461
|
+
tc_name = tool_call.get("function", {}).get("name", "")
|
|
462
|
+
is_read_only = self._classify_read_only(tc_name)
|
|
463
|
+
event_meta: dict[str, Any] = {"tool_call": tool_call}
|
|
464
|
+
if is_read_only is not None:
|
|
465
|
+
event_meta["is_read_only"] = is_read_only
|
|
466
|
+
yield ExecutionEvent(
|
|
467
|
+
type=ExecutionEventType.TOOL_CALL,
|
|
468
|
+
metadata=event_meta,
|
|
469
|
+
)
|
|
470
|
+
except Exception as e:
|
|
471
|
+
log.error(f"Provider stream failed: {e}")
|
|
472
|
+
yield ExecutionEvent(
|
|
473
|
+
type=ExecutionEventType.CONTENT,
|
|
474
|
+
content=f"\n\nProvider error: {e}\n",
|
|
475
|
+
)
|
|
476
|
+
stream_error = True
|
|
477
|
+
finally:
|
|
478
|
+
if accumulated_content:
|
|
479
|
+
set_span_output(llm_span, accumulated_content)
|
|
480
|
+
if tool_calls:
|
|
481
|
+
if llm_span is not None:
|
|
482
|
+
llm_span.set_attribute("cade.tool_call_count", len(tool_calls))
|
|
434
483
|
|
|
435
484
|
# Stop the execution loop on non-recoverable stream errors
|
|
436
485
|
if stream_error:
|
|
@@ -194,78 +194,95 @@ class AsyncToolExecutor:
|
|
|
194
194
|
timeout: float = 120.0,
|
|
195
195
|
) -> ToolExecutionResult:
|
|
196
196
|
"""Execute a single tool with timeout and cancellation support."""
|
|
197
|
+
from cadecoder.telemetry import set_tool_result, trace_tool_call
|
|
198
|
+
|
|
197
199
|
function = tool_call.get("function", {})
|
|
198
200
|
name = function.get("name", "unknown")
|
|
199
201
|
tool_call_id = tool_call.get("id", "unknown")
|
|
202
|
+
args_str = function.get("arguments", "{}") or "{}"
|
|
200
203
|
|
|
201
204
|
try:
|
|
202
|
-
args_str = function.get("arguments", "{}") or "{}"
|
|
203
205
|
args = json.loads(args_str)
|
|
204
|
-
|
|
206
|
+
except json.JSONDecodeError:
|
|
207
|
+
args = args_str # span preview only; execute path will error below
|
|
205
208
|
|
|
209
|
+
with trace_tool_call(name=name, tool_call_id=tool_call_id, arguments=args) as tool_span:
|
|
206
210
|
try:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
f"Memory tool timed out after {timeout:.0f}s (often a cold "
|
|
216
|
-
"embedder load). Retry Memory_SearchLibrary once; do not fall "
|
|
217
|
-
"back to Local_* filesystem tools for library recall."
|
|
211
|
+
if not isinstance(args, dict):
|
|
212
|
+
raise ValueError(f"Tool arguments must be a JSON object, got: {args_str[:120]}")
|
|
213
|
+
log.debug(f"Executing tool: {name} with args: {args}")
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
result_content = await asyncio.wait_for(
|
|
217
|
+
self.tool_manager.execute(name, args),
|
|
218
|
+
timeout=timeout,
|
|
218
219
|
)
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
except TimeoutError:
|
|
221
|
+
log.warning(f"Tool {name} timed out after {timeout}s")
|
|
222
|
+
if isinstance(name, str) and name.startswith("Memory_"):
|
|
223
|
+
content = (
|
|
224
|
+
f"Memory tool timed out after {timeout:.0f}s (often a cold "
|
|
225
|
+
"embedder load). Retry Memory_SearchLibrary once; do not fall "
|
|
226
|
+
"back to Local_* filesystem tools for library recall."
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
content = f"Tool execution timed out after {timeout} seconds"
|
|
230
|
+
set_tool_result(tool_span, status="error", content=content, error="timeout")
|
|
231
|
+
return ToolExecutionResult(
|
|
232
|
+
tool_call_id=tool_call_id,
|
|
233
|
+
name=name,
|
|
234
|
+
content=content,
|
|
235
|
+
status="error",
|
|
236
|
+
error="timeout",
|
|
237
|
+
authorization_url=None,
|
|
238
|
+
)
|
|
239
|
+
except asyncio.CancelledError:
|
|
240
|
+
log.info(f"Tool {name} was cancelled")
|
|
241
|
+
set_tool_result(
|
|
242
|
+
tool_span,
|
|
243
|
+
status="cancelled",
|
|
244
|
+
content="Tool execution was cancelled",
|
|
245
|
+
error="cancelled",
|
|
246
|
+
)
|
|
247
|
+
return ToolExecutionResult(
|
|
248
|
+
tool_call_id=tool_call_id,
|
|
249
|
+
name=name,
|
|
250
|
+
content="Tool execution was cancelled",
|
|
251
|
+
status="cancelled",
|
|
252
|
+
error="cancelled",
|
|
253
|
+
authorization_url=None,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
actual_content = extract_tool_output_content(result_content)
|
|
257
|
+
set_tool_result(tool_span, status="success", content=actual_content)
|
|
221
258
|
return ToolExecutionResult(
|
|
222
259
|
tool_call_id=tool_call_id,
|
|
223
260
|
name=name,
|
|
224
|
-
content=
|
|
225
|
-
status="
|
|
226
|
-
error=
|
|
261
|
+
content=actual_content,
|
|
262
|
+
status="success",
|
|
263
|
+
error=None,
|
|
227
264
|
authorization_url=None,
|
|
228
265
|
)
|
|
229
|
-
|
|
230
|
-
|
|
266
|
+
|
|
267
|
+
except Exception as e:
|
|
268
|
+
auth_url = None
|
|
269
|
+
if isinstance(e, ToolAuthorizationRequired):
|
|
270
|
+
auth_url = e.authorization_url
|
|
271
|
+
log.error(f"Authorization required for tool {name}")
|
|
272
|
+
else:
|
|
273
|
+
log.error(f"Tool execution failed for {name}: {e}")
|
|
274
|
+
log.exception(e)
|
|
275
|
+
|
|
276
|
+
set_tool_result(tool_span, status="error", content=str(e), error=str(e))
|
|
231
277
|
return ToolExecutionResult(
|
|
232
278
|
tool_call_id=tool_call_id,
|
|
233
279
|
name=name,
|
|
234
|
-
content=
|
|
235
|
-
status="
|
|
236
|
-
error=
|
|
237
|
-
authorization_url=
|
|
280
|
+
content=str(e),
|
|
281
|
+
status="error",
|
|
282
|
+
error=str(e),
|
|
283
|
+
authorization_url=auth_url,
|
|
238
284
|
)
|
|
239
285
|
|
|
240
|
-
actual_content = extract_tool_output_content(result_content)
|
|
241
|
-
|
|
242
|
-
return ToolExecutionResult(
|
|
243
|
-
tool_call_id=tool_call_id,
|
|
244
|
-
name=name,
|
|
245
|
-
content=actual_content,
|
|
246
|
-
status="success",
|
|
247
|
-
error=None,
|
|
248
|
-
authorization_url=None,
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
except Exception as e:
|
|
252
|
-
auth_url = None
|
|
253
|
-
if isinstance(e, ToolAuthorizationRequired):
|
|
254
|
-
auth_url = e.authorization_url
|
|
255
|
-
log.error(f"Authorization required for tool {name}")
|
|
256
|
-
else:
|
|
257
|
-
log.error(f"Tool execution failed for {name}: {e}")
|
|
258
|
-
log.exception(e)
|
|
259
|
-
|
|
260
|
-
return ToolExecutionResult(
|
|
261
|
-
tool_call_id=tool_call_id,
|
|
262
|
-
name=name,
|
|
263
|
-
content=str(e),
|
|
264
|
-
status="error",
|
|
265
|
-
error=str(e),
|
|
266
|
-
authorization_url=auth_url,
|
|
267
|
-
)
|
|
268
|
-
|
|
269
286
|
def _analyze_tool_dependencies(self, tool_calls: ToolCallList) -> ToolGroups:
|
|
270
287
|
"""Analyze tool dependencies to determine execution groups.
|
|
271
288
|
|
{cade_cli-0.19.2 → cade_cli-0.20.0}/src/cadecoder/host_plugin/assets/.cursor-plugin/plugin.json
RENAMED
|
@@ -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.
|
|
5
|
+
"version": "0.20.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Arcade.dev",
|
|
8
8
|
"email": "support@arcade.dev"
|
|
@@ -329,6 +329,25 @@ class AnthropicProvider(Provider):
|
|
|
329
329
|
},
|
|
330
330
|
)
|
|
331
331
|
|
|
332
|
+
# Final message carries authoritative input/output token counts.
|
|
333
|
+
try:
|
|
334
|
+
final = await stream.get_final_message()
|
|
335
|
+
if final is not None and getattr(final, "usage", None) is not None:
|
|
336
|
+
usage = {
|
|
337
|
+
"prompt_tokens": int(final.usage.input_tokens or 0),
|
|
338
|
+
"completion_tokens": int(final.usage.output_tokens or 0),
|
|
339
|
+
"total_tokens": int(
|
|
340
|
+
(final.usage.input_tokens or 0) + (final.usage.output_tokens or 0)
|
|
341
|
+
),
|
|
342
|
+
}
|
|
343
|
+
yield StreamEvent(
|
|
344
|
+
type=ExecutionEventType.COMPLETE.value,
|
|
345
|
+
content=None,
|
|
346
|
+
metadata={"usage": usage, "model": request.model},
|
|
347
|
+
)
|
|
348
|
+
except Exception as exc: # noqa: BLE001
|
|
349
|
+
log.debug(f"Anthropic stream usage unavailable: {exc}")
|
|
350
|
+
|
|
332
351
|
@staticmethod
|
|
333
352
|
def _default_max_tokens(request: ProviderRequest, streaming: bool) -> int:
|
|
334
353
|
"""Return the default output-token cap for a request without one.
|
|
@@ -354,13 +354,24 @@ class OllamaProvider(Provider):
|
|
|
354
354
|
)
|
|
355
355
|
|
|
356
356
|
if chunk.get("done"):
|
|
357
|
+
complete_meta: dict[str, Any] = {
|
|
358
|
+
"finish_reason": "tool_calls" if saw_tool_calls else "stop",
|
|
359
|
+
"model": request.model,
|
|
360
|
+
}
|
|
361
|
+
prompt_tokens = chunk.get("prompt_eval_count")
|
|
362
|
+
completion_tokens = chunk.get("eval_count")
|
|
363
|
+
if prompt_tokens is not None or completion_tokens is not None:
|
|
364
|
+
p = int(prompt_tokens or 0)
|
|
365
|
+
c = int(completion_tokens or 0)
|
|
366
|
+
complete_meta["usage"] = {
|
|
367
|
+
"prompt_tokens": p,
|
|
368
|
+
"completion_tokens": c,
|
|
369
|
+
"total_tokens": p + c,
|
|
370
|
+
}
|
|
357
371
|
yield StreamEvent(
|
|
358
372
|
type=ExecutionEventType.COMPLETE.value,
|
|
359
373
|
content=None,
|
|
360
|
-
metadata=
|
|
361
|
-
"finish_reason": "tool_calls" if saw_tool_calls else "stop",
|
|
362
|
-
"model": request.model,
|
|
363
|
-
},
|
|
374
|
+
metadata=complete_meta,
|
|
364
375
|
)
|
|
365
376
|
break
|
|
366
377
|
|
|
@@ -217,13 +217,44 @@ class OpenAIProvider(Provider):
|
|
|
217
217
|
"""Yield one attempt's worth of stream events. Raises on error."""
|
|
218
218
|
openai_request = self._build_openai_request(request)
|
|
219
219
|
openai_request["stream"] = True
|
|
220
|
+
# Ask OpenAI (and compatible servers that honor it) for a final usage
|
|
221
|
+
# chunk so OTEL spans can record prompt/completion tokens. Some local
|
|
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
|
+
)
|
|
229
|
+
if want_usage:
|
|
230
|
+
openai_request["stream_options"] = {"include_usage": True}
|
|
220
231
|
|
|
221
|
-
|
|
232
|
+
try:
|
|
233
|
+
stream = await self.client.chat.completions.create(**openai_request)
|
|
234
|
+
except Exception as exc:
|
|
235
|
+
if want_usage and "stream_options" in openai_request:
|
|
236
|
+
log.debug(
|
|
237
|
+
f"stream_options.include_usage rejected by endpoint; "
|
|
238
|
+
f"retrying without usage: {exc}"
|
|
239
|
+
)
|
|
240
|
+
openai_request.pop("stream_options", None)
|
|
241
|
+
stream = await self.client.chat.completions.create(**openai_request)
|
|
242
|
+
else:
|
|
243
|
+
raise
|
|
222
244
|
current_tool_calls: dict[int, dict[str, Any]] = {}
|
|
245
|
+
usage: dict[str, int] | None = None
|
|
246
|
+
usage_emitted = False
|
|
223
247
|
|
|
224
248
|
async for chunk in stream:
|
|
225
249
|
chunk = cast(ChatCompletionChunk, chunk)
|
|
226
250
|
|
|
251
|
+
if chunk.usage is not None:
|
|
252
|
+
usage = {
|
|
253
|
+
"prompt_tokens": chunk.usage.prompt_tokens or 0,
|
|
254
|
+
"completion_tokens": chunk.usage.completion_tokens or 0,
|
|
255
|
+
"total_tokens": chunk.usage.total_tokens or 0,
|
|
256
|
+
}
|
|
257
|
+
|
|
227
258
|
# Handle content delta
|
|
228
259
|
if chunk.choices and chunk.choices[0].delta.content:
|
|
229
260
|
yield StreamEvent(
|
|
@@ -259,15 +290,27 @@ class OpenAIProvider(Provider):
|
|
|
259
290
|
metadata={"tool_call": tool_call},
|
|
260
291
|
)
|
|
261
292
|
|
|
293
|
+
complete_meta: dict[str, Any] = {
|
|
294
|
+
"finish_reason": chunk.choices[0].finish_reason,
|
|
295
|
+
"model": chunk.model,
|
|
296
|
+
}
|
|
297
|
+
if usage:
|
|
298
|
+
complete_meta["usage"] = usage
|
|
299
|
+
usage_emitted = True
|
|
262
300
|
yield StreamEvent(
|
|
263
301
|
type=ExecutionEventType.COMPLETE.value,
|
|
264
302
|
content=None,
|
|
265
|
-
metadata=
|
|
266
|
-
"finish_reason": chunk.choices[0].finish_reason,
|
|
267
|
-
"model": chunk.model,
|
|
268
|
-
},
|
|
303
|
+
metadata=complete_meta,
|
|
269
304
|
)
|
|
270
305
|
|
|
306
|
+
# Usage often arrives on a trailing empty-choices chunk after finish.
|
|
307
|
+
if usage and not usage_emitted:
|
|
308
|
+
yield StreamEvent(
|
|
309
|
+
type=ExecutionEventType.COMPLETE.value,
|
|
310
|
+
content=None,
|
|
311
|
+
metadata={"usage": usage},
|
|
312
|
+
)
|
|
313
|
+
|
|
271
314
|
def _build_openai_request(self, request: ProviderRequest) -> dict[str, Any]:
|
|
272
315
|
"""Build OpenAI API request from unified request.
|
|
273
316
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Optional OpenTelemetry tracing for Cade agent turns.
|
|
2
|
+
|
|
3
|
+
Enable with ``CADE_OTEL=1`` (requires the ``otel`` extra). Spans cover each
|
|
4
|
+
agent turn, every LLM call inside it, and every tool invocation — with
|
|
5
|
+
latency, tool names/args/status, and token usage when the provider reports it.
|
|
6
|
+
|
|
7
|
+
Export defaults to OTLP HTTP at ``http://127.0.0.1:6006/v1/traces`` (Arize
|
|
8
|
+
Phoenix). Override with the standard ``OTEL_EXPORTER_OTLP_*`` env vars.
|
|
9
|
+
|
|
10
|
+
Example::
|
|
11
|
+
|
|
12
|
+
CADE_OTEL=1 cade chat
|
|
13
|
+
# another terminal:
|
|
14
|
+
make otel-ui
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from cadecoder.telemetry.otel import (
|
|
20
|
+
init_telemetry,
|
|
21
|
+
is_enabled,
|
|
22
|
+
record_message_parts,
|
|
23
|
+
record_usage,
|
|
24
|
+
set_span_output,
|
|
25
|
+
set_tool_result,
|
|
26
|
+
shutdown_telemetry,
|
|
27
|
+
start_span,
|
|
28
|
+
trace_llm_call,
|
|
29
|
+
trace_tool_call,
|
|
30
|
+
trace_turn,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"init_telemetry",
|
|
35
|
+
"is_enabled",
|
|
36
|
+
"record_message_parts",
|
|
37
|
+
"record_usage",
|
|
38
|
+
"set_span_output",
|
|
39
|
+
"set_tool_result",
|
|
40
|
+
"shutdown_telemetry",
|
|
41
|
+
"start_span",
|
|
42
|
+
"trace_llm_call",
|
|
43
|
+
"trace_tool_call",
|
|
44
|
+
"trace_turn",
|
|
45
|
+
]
|