cade-cli 0.16.7__tar.gz → 0.16.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cade_cli-0.16.7 → cade_cli-0.16.9}/PKG-INFO +8 -10
- {cade_cli-0.16.7 → cade_cli-0.16.9}/README.md +7 -9
- {cade_cli-0.16.7 → cade_cli-0.16.9}/pyproject.toml +1 -1
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ai/prompts.py +1 -1
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/mem.py +46 -31
- cade_cli-0.16.9/src/cadecoder/core/memory.py +312 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/server.py +42 -1
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/services.py +14 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/parallel.py +14 -3
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/manager/composite.py +93 -2
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/manager/config.py +4 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/manager/mcp.py +18 -1
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/session.py +11 -2
- cade_cli-0.16.7/src/cadecoder/core/memory.py +0 -76
- {cade_cli-0.16.7 → cade_cli-0.16.9}/.gitignore +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/LICENSE +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/__main__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/_metadata.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/config.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/errors.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/grants.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/resources.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/sandbox.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/server.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/_read_cache.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/access.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/agents.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/context.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/filesystem.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/git.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/notifications.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/questions.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/results.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/schemas.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/search.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/shell.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/snippets.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/tasks.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/web_fetch.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/tools/web_search.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/utils.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/arcade_client.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/cache.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/extract.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/markdown.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/preapproved.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/prompts.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/scrape.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/search/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/search/arcade.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/search/base.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cade_mcp_local/web/search/http.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ai/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/app.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/auth.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/account.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/agent_group.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/agents.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/auth.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/chat.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/cron.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/daemon.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/grant.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/hooks.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/mcp.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/tasks.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/thread.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/commands/tools.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/mcp_hosts.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/cli/onboarding.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/agent.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/comms.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/config.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/constants.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/errors.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/frozen.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/git.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/inbox.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/logging.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/model_specs.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/names.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/paths.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/runtime.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/secrets.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/spawn.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/core/types.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/base.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/factory.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/imessage.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/registry.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/stub.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/adapters/telegram.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/allowlist.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/attach.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/chunker.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/client.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/commands.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/config.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/control.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/cron.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/elicitation.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/ensure.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/format.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/app.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/arcade_export.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/auth.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/constants.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/elicitation.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/exposure.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/limits.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/middleware.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/resources.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/subscriptions.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/gateway/turn_bridge.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/grants.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/inbox_cron.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/install.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/live.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/lock.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/mesh.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/message_pump.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/observability.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/owner.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/progress_sink.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/registry.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/router.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/runner.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/runner_main.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/secrets.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/secrets_broker.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/shim.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/sinks.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/supervisor.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/tokens.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/traffic.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/turns.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/daemon/wake.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/context_window.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/mcp_schema_index.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/orchestrator.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/tool_result_store.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/execution/tool_schema_cache.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/config.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/engine.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/executors/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/executors/callback.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/executors/command.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/executors/http.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/executors/prompt.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/matchers.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/registry.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/hooks/types.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/providers/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/providers/anthropic.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/providers/base.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/providers/http_client.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/providers/ollama.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/providers/openai.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/serve/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/storage/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/storage/threads.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tasks/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tasks/scheduler.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tasks/store.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tasks/types.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/templates/login_failed.html +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/templates/login_success.html +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/templates/serve/serve.toml.example +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/templates/styles.css +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/manager/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/manager/base.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/search/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/search/discovered.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/search/scoring.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/tools/search/service.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/completion.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/display.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/elicitation.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/input.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/ui/slash.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/voice/__init__.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/voice/audio.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/voice/cleanup.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/voice/session.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/src/cadecoder/voice/stt.py +0 -0
- {cade_cli-0.16.7 → cade_cli-0.16.9}/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.16.
|
|
3
|
+
Version: 0.16.9
|
|
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
|
|
@@ -129,7 +129,7 @@ The script detects your OS/arch, downloads the matching prebuilt binary
|
|
|
129
129
|
`~/.local/bin`. Pin a version or install location:
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
|
-
CADE_VERSION=0.16.
|
|
132
|
+
CADE_VERSION=0.16.9 CADE_INSTALL_DIR=/usr/local/bin \
|
|
133
133
|
sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
|
|
134
134
|
```
|
|
135
135
|
|
|
@@ -252,18 +252,16 @@ cade agent list
|
|
|
252
252
|
cade agent use reviewer
|
|
253
253
|
cade agent info / cade agent edit reviewer
|
|
254
254
|
|
|
255
|
+
cade agent mem add ~/Notes # one step: register + index
|
|
255
256
|
cade agent mem # status
|
|
256
|
-
cade agent mem add ~/notes
|
|
257
257
|
cade agent mem search "auth design"
|
|
258
|
-
cade agent mem warm # pre-load semantic search
|
|
259
258
|
```
|
|
260
259
|
|
|
261
|
-
Memory is per-agent
|
|
262
|
-
`~/.cade/data/agents/<id>/memory
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
warmup before a conversation.
|
|
260
|
+
Memory is per-agent under
|
|
261
|
+
`~/.cade/data/agents/<id>/memory/<agent>/.librarian/`. Cade keeps the index and
|
|
262
|
+
embedder ready (auto-heal empty indexes, warm after add/update, longer first
|
|
263
|
+
search timeouts). The model uses `Memory_*` for notes recall — not Local_*
|
|
264
|
+
filesystem tools. Details: [`docs/agents.md`](./docs/agents.md#memory).
|
|
267
265
|
|
|
268
266
|
---
|
|
269
267
|
|
|
@@ -38,7 +38,7 @@ The script detects your OS/arch, downloads the matching prebuilt binary
|
|
|
38
38
|
`~/.local/bin`. Pin a version or install location:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
CADE_VERSION=0.16.
|
|
41
|
+
CADE_VERSION=0.16.9 CADE_INSTALL_DIR=/usr/local/bin \
|
|
42
42
|
sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -161,18 +161,16 @@ cade agent list
|
|
|
161
161
|
cade agent use reviewer
|
|
162
162
|
cade agent info / cade agent edit reviewer
|
|
163
163
|
|
|
164
|
+
cade agent mem add ~/Notes # one step: register + index
|
|
164
165
|
cade agent mem # status
|
|
165
|
-
cade agent mem add ~/notes
|
|
166
166
|
cade agent mem search "auth design"
|
|
167
|
-
cade agent mem warm # pre-load semantic search
|
|
168
167
|
```
|
|
169
168
|
|
|
170
|
-
Memory is per-agent
|
|
171
|
-
`~/.cade/data/agents/<id>/memory
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
warmup before a conversation.
|
|
169
|
+
Memory is per-agent under
|
|
170
|
+
`~/.cade/data/agents/<id>/memory/<agent>/.librarian/`. Cade keeps the index and
|
|
171
|
+
embedder ready (auto-heal empty indexes, warm after add/update, longer first
|
|
172
|
+
search timeouts). The model uses `Memory_*` for notes recall — not Local_*
|
|
173
|
+
filesystem tools. Details: [`docs/agents.md`](./docs/agents.md#memory).
|
|
176
174
|
|
|
177
175
|
---
|
|
178
176
|
|
|
@@ -174,7 +174,7 @@ Tool names are versioned (e.g. Slack.SendMessage@2.3.0). Pass the full string to
|
|
|
174
174
|
|
|
175
175
|
Re-call SelectTools when the domain shifts or the first result missed. Tools registered earlier in the session persist; you don't need to rediscover them.
|
|
176
176
|
|
|
177
|
-
Routing: external info/actions on third-party services → discovery + Arcade_UseTool | external pages on the open web → Local_WebFetch (read a URL) and Local_WebSearch (find URLs); pair them — search to find, fetch to read | local code → Local_Search, Local_ReadFile, Local_ListFiles, other Local_* tools. Never grep local files for external-service data, and never guess URLs — search or ask first.
|
|
177
|
+
Routing: external info/actions on third-party services → discovery + Arcade_UseTool | external pages on the open web → Local_WebFetch (read a URL) and Local_WebSearch (find URLs); pair them — search to find, fetch to read | agent notes / persistent library → Memory_SearchLibrary (and other Memory_* tools); never use Local_Search / Local_ReadFile as a fallback for vault or notes recall | local code → Local_Search, Local_ReadFile, Local_ListFiles, other Local_* tools. Never grep local files for external-service data, and never guess URLs — search or ask first.
|
|
178
178
|
|
|
179
179
|
Anti-patterns: Search → Select chained reflexively; one SelectTools per step instead of per workflow; invoking discovered tools by name instead of through Arcade_UseTool; stripping @version; ignoring an auth URL and retrying.
|
|
180
180
|
"""
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
"""``cade mem`` — manage the current agent's memory, backed by agent-library.
|
|
1
|
+
"""``cade agent mem`` — manage the current agent's memory, backed by agent-library.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
help
|
|
5
|
-
the current agent's memory directory
|
|
6
|
-
agent's ``Memory_*`` MCP tools
|
|
3
|
+
Preferred path: ``cade agent mem …``. Flat ``cade mem …`` remains a hidden
|
|
4
|
+
alias. Drives the librarian Typer app in-process (help branded as ``cade mem``),
|
|
5
|
+
scoped to the current agent's memory directory — the same catalogue the
|
|
6
|
+
agent's ``Memory_*`` MCP tools use.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
import os
|
|
12
11
|
from pathlib import Path
|
|
13
12
|
from typing import Annotated, Any
|
|
14
13
|
|
|
15
14
|
import typer
|
|
16
15
|
from rich.console import Console
|
|
17
16
|
|
|
18
|
-
from cadecoder.core.memory import
|
|
17
|
+
from cadecoder.core.memory import (
|
|
18
|
+
apply_memory_env,
|
|
19
|
+
ensure_memory_dir,
|
|
20
|
+
ensure_memory_ready,
|
|
21
|
+
warm_memory_embedder,
|
|
22
|
+
)
|
|
19
23
|
from cadecoder.core.runtime import get_runtime
|
|
20
24
|
|
|
21
25
|
console = Console(stderr=True)
|
|
@@ -36,18 +40,7 @@ def _apply_memory_env() -> Path:
|
|
|
36
40
|
Returns:
|
|
37
41
|
The agent's memory directory (parent of ``.librarian``).
|
|
38
42
|
"""
|
|
39
|
-
|
|
40
|
-
persona_dir = ensure_memory_dir(runtime.persona_name)
|
|
41
|
-
librarian_dir = persona_dir / ".librarian"
|
|
42
|
-
os.environ["DATABASE_PATH"] = str(librarian_dir / "index.db")
|
|
43
|
-
os.environ["DOCUMENTS_PATH"] = str(librarian_dir / "documents")
|
|
44
|
-
os.environ["SOURCES_CONFIG_PATH"] = str(librarian_dir / "sources.json")
|
|
45
|
-
os.environ["HOME"] = str(persona_dir)
|
|
46
|
-
# Rebrand librarian's usage hints (e.g. "Add a source with: libr add ...")
|
|
47
|
-
# to "cade mem". Must be set before librarian.cli is imported, which only
|
|
48
|
-
# happens later via _load_librarian_app().
|
|
49
|
-
os.environ["LIBRARIAN_PROG_NAME"] = "cade mem"
|
|
50
|
-
return persona_dir
|
|
43
|
+
return apply_memory_env()
|
|
51
44
|
|
|
52
45
|
|
|
53
46
|
def _load_librarian_app() -> Any:
|
|
@@ -62,6 +55,13 @@ def _load_librarian_app() -> Any:
|
|
|
62
55
|
* ``list`` shows the agent's memory **documents** (what users expect from
|
|
63
56
|
"list"), while the source-directory listing moves to ``sources``.
|
|
64
57
|
"""
|
|
58
|
+
# ``mem update`` / ``mem add`` import ``librarian.server`` (tool registration
|
|
59
|
+
# via arcade-core). Frozen binaries must patch return-value detection before
|
|
60
|
+
# that import or ``add_tool`` raises ``ValueError: Source code not found``.
|
|
61
|
+
from cadecoder.core.frozen import patch_frozen_return_detection
|
|
62
|
+
|
|
63
|
+
patch_frozen_return_detection()
|
|
64
|
+
|
|
65
65
|
from librarian.cli import app
|
|
66
66
|
|
|
67
67
|
_remap_index_to_update(app)
|
|
@@ -178,7 +178,7 @@ def _mem_status() -> None:
|
|
|
178
178
|
"""Render the current agent's memory under "Agent Memory" branding.
|
|
179
179
|
|
|
180
180
|
Shared by ``cade mem`` (CLI) and the in-chat ``/mem`` command so the view
|
|
181
|
-
is always the same.
|
|
181
|
+
is always the same. Auto-heals an empty index when sources are registered.
|
|
182
182
|
"""
|
|
183
183
|
from datetime import datetime
|
|
184
184
|
|
|
@@ -186,14 +186,18 @@ def _mem_status() -> None:
|
|
|
186
186
|
from rich.table import Table
|
|
187
187
|
|
|
188
188
|
runtime = get_runtime()
|
|
189
|
-
|
|
189
|
+
ready = ensure_memory_ready(runtime.persona_name, heal=True, warm=False)
|
|
190
|
+
memory_dir = ready.persona_dir
|
|
190
191
|
db_path = memory_dir / ".librarian" / "index.db"
|
|
191
192
|
|
|
192
193
|
counts = _read_memory_stats(db_path)
|
|
193
|
-
|
|
194
|
+
document_count = int(counts.get("document_count", ready.document_count) or 0)
|
|
195
|
+
if db_path.exists() and document_count > 0:
|
|
194
196
|
last_updated = datetime.fromtimestamp(db_path.stat().st_mtime).strftime("%Y-%m-%d %H:%M:%S")
|
|
197
|
+
elif ready.sources and document_count == 0:
|
|
198
|
+
last_updated = "empty — sources registered but nothing indexed yet"
|
|
195
199
|
else:
|
|
196
|
-
last_updated = "never
|
|
200
|
+
last_updated = "never"
|
|
197
201
|
|
|
198
202
|
agent_id = runtime.agent_id or "-"
|
|
199
203
|
|
|
@@ -206,10 +210,18 @@ def _mem_status() -> None:
|
|
|
206
210
|
table = Table(show_header=False, box=None, pad_edge=False)
|
|
207
211
|
table.add_column(style="green")
|
|
208
212
|
table.add_column(style="white")
|
|
209
|
-
table.add_row("Count", str(
|
|
213
|
+
table.add_row("Count", str(document_count))
|
|
214
|
+
table.add_row("Sources", str(len(ready.sources)))
|
|
210
215
|
table.add_row("Last updated", last_updated)
|
|
211
216
|
table.add_row("Location", str(memory_dir))
|
|
212
217
|
out.print(table)
|
|
218
|
+
if ready.healed:
|
|
219
|
+
out.print("[dim]Index was empty and has been rebuilt from registered sources.[/dim]")
|
|
220
|
+
elif not ready.sources:
|
|
221
|
+
out.print(
|
|
222
|
+
"[dim]No notes folder yet — add one with "
|
|
223
|
+
"[bold]cade agent mem add ~/Notes[/bold].[/dim]"
|
|
224
|
+
)
|
|
213
225
|
|
|
214
226
|
|
|
215
227
|
def _mem_list(*, json_output: bool = False, limit: int = 50) -> None:
|
|
@@ -265,7 +277,7 @@ def _mem_list(*, json_output: bool = False, limit: int = 50) -> None:
|
|
|
265
277
|
if not documents:
|
|
266
278
|
out.print(
|
|
267
279
|
"[dim]No documents in this agent's memory yet — "
|
|
268
|
-
"add
|
|
280
|
+
"add a notes folder with 'cade agent mem add ~/Notes'.[/dim]"
|
|
269
281
|
)
|
|
270
282
|
return
|
|
271
283
|
|
|
@@ -351,29 +363,32 @@ def _warm_memory() -> int:
|
|
|
351
363
|
|
|
352
364
|
|
|
353
365
|
def mem_command(ctx: typer.Context) -> None:
|
|
354
|
-
"""Run a ``cade mem`` subcommand scoped to the current agent.
|
|
366
|
+
"""Run a ``cade agent mem`` subcommand scoped to the current agent.
|
|
355
367
|
|
|
356
368
|
Examples:
|
|
357
|
-
cade mem #
|
|
358
|
-
cade mem
|
|
359
|
-
cade mem
|
|
369
|
+
cade agent mem # status (auto-heals empty index)
|
|
370
|
+
cade agent mem add ~/Notes # register + index a notes folder
|
|
371
|
+
cade agent mem search "topic" # search the current agent's memory
|
|
360
372
|
"""
|
|
361
373
|
memory_home = _apply_memory_env()
|
|
362
374
|
forwarded = list(ctx.args)
|
|
363
|
-
# Bare `cade mem` defaults to the Agent Memory status view (which already
|
|
375
|
+
# Bare `cade agent mem` defaults to the Agent Memory status view (which already
|
|
364
376
|
# names the current agent) rather than a help+error panel.
|
|
365
377
|
if not forwarded:
|
|
366
378
|
forwarded = ["status"]
|
|
367
379
|
elif forwarded[0] in {"--help", "-h"}:
|
|
368
380
|
agent_name = get_runtime().persona_name
|
|
369
381
|
console.print(
|
|
370
|
-
f"[dim]cade mem: scoped to agent [bold]{agent_name}[/bold] "
|
|
382
|
+
f"[dim]cade agent mem: scoped to agent [bold]{agent_name}[/bold] "
|
|
371
383
|
f"(storage={memory_home})[/dim]\n"
|
|
372
384
|
)
|
|
373
385
|
elif forwarded[0] == "warm":
|
|
374
386
|
rc = _warm_memory()
|
|
375
387
|
raise typer.Exit(rc)
|
|
376
388
|
rc = _run_mem(forwarded)
|
|
389
|
+
# After sources change or a rebuild, warm so the next Memory_* search is not cold.
|
|
390
|
+
if rc == 0 and forwarded and forwarded[0] in {"update", "add"}:
|
|
391
|
+
warm_memory_embedder(quiet=True)
|
|
377
392
|
raise typer.Exit(rc)
|
|
378
393
|
|
|
379
394
|
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Per-agent memory directory management for the bundled agent-library server.
|
|
2
|
+
|
|
3
|
+
The current runtime (agent-backed) scopes memory under
|
|
4
|
+
``data/agents/<id>/memory/<persona>/`` where ``<persona>`` is the agent name.
|
|
5
|
+
All librarian storage lives in a ``.librarian/`` subfolder so that pointing
|
|
6
|
+
``HOME`` at the persona directory makes librarian's hardcoded
|
|
7
|
+
``Path.home() / ".librarian"`` resolve to the same files the ``DATABASE_PATH`` /
|
|
8
|
+
``DOCUMENTS_PATH`` / ``SOURCES_CONFIG_PATH`` env vars point at — the ``Memory_*``
|
|
9
|
+
MCP tools and ``cade mem`` then share one on-disk catalogue.
|
|
10
|
+
|
|
11
|
+
``ensure_memory_ready`` is the invisible contract: sources without an index are
|
|
12
|
+
healed automatically, and the embedder can be prewarmed so the first search
|
|
13
|
+
does not time out.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import shutil
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from cadecoder.core.logging import log
|
|
25
|
+
from cadecoder.core.paths import validate_name
|
|
26
|
+
from cadecoder.core.runtime import get_runtime
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class MemoryReadyState:
|
|
31
|
+
"""Snapshot of an agent's memory after readiness checks."""
|
|
32
|
+
|
|
33
|
+
persona_dir: Path
|
|
34
|
+
librarian_dir: Path
|
|
35
|
+
sources: list[dict] = field(default_factory=list)
|
|
36
|
+
document_count: int = 0
|
|
37
|
+
healed: bool = False
|
|
38
|
+
missing_sources: list[str] = field(default_factory=list)
|
|
39
|
+
warmed: bool = False
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def has_sources(self) -> bool:
|
|
43
|
+
"""True when at least one source is registered."""
|
|
44
|
+
return bool(self.sources)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def is_ready(self) -> bool:
|
|
48
|
+
"""True when sources exist and the index has documents."""
|
|
49
|
+
return self.has_sources and self.document_count > 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def ensure_memory_dir(persona_name: str) -> Path:
|
|
53
|
+
"""Create the memory directory for a persona/agent and return it.
|
|
54
|
+
|
|
55
|
+
Two one-time bootstraps keep a fresh persona from being starved of state:
|
|
56
|
+
|
|
57
|
+
1. **Flat-layout migration** of any top-level ``index.db`` / ``sources.json``
|
|
58
|
+
/ ``settings.json`` / ``documents/`` into ``.librarian/``.
|
|
59
|
+
2. **Global librarian bootstrap**: copy ``~/.librarian/sources.json`` and
|
|
60
|
+
``settings.json`` (if present) into a brand-new persona so documents
|
|
61
|
+
indexed outside Cade aren't orphaned. The index itself is never copied —
|
|
62
|
+
call :func:`ensure_memory_ready` to rebuild it.
|
|
63
|
+
|
|
64
|
+
Both skip when the target already exists, so repeated calls never churn data.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
persona_name: The persona/agent name scoping the memory.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
The persona memory directory (the parent of ``.librarian``).
|
|
71
|
+
"""
|
|
72
|
+
safe_name = validate_name(persona_name, kind="persona")
|
|
73
|
+
persona_dir = get_runtime().memory_dir(safe_name)
|
|
74
|
+
persona_dir.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
|
|
76
|
+
librarian_dir = persona_dir / ".librarian"
|
|
77
|
+
librarian_dir.mkdir(exist_ok=True)
|
|
78
|
+
(librarian_dir / "documents").mkdir(exist_ok=True)
|
|
79
|
+
|
|
80
|
+
# 1) Flat-layout migration.
|
|
81
|
+
for filename in ("index.db", "sources.json", "settings.json"):
|
|
82
|
+
old = persona_dir / filename
|
|
83
|
+
new = librarian_dir / filename
|
|
84
|
+
if old.is_file() and not new.exists():
|
|
85
|
+
old.rename(new)
|
|
86
|
+
|
|
87
|
+
old_documents = persona_dir / "documents"
|
|
88
|
+
new_documents = librarian_dir / "documents"
|
|
89
|
+
if old_documents.is_dir() and old_documents.resolve() != new_documents.resolve():
|
|
90
|
+
for child in list(old_documents.iterdir()):
|
|
91
|
+
target = new_documents / child.name
|
|
92
|
+
if not target.exists():
|
|
93
|
+
child.rename(target)
|
|
94
|
+
try:
|
|
95
|
+
old_documents.rmdir()
|
|
96
|
+
except OSError:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
# 2) Global librarian bootstrap (sources/settings only — never index/docs).
|
|
100
|
+
global_librarian = Path.home() / ".librarian"
|
|
101
|
+
if global_librarian.is_dir() and global_librarian.resolve() != librarian_dir.resolve():
|
|
102
|
+
for filename in ("sources.json", "settings.json"):
|
|
103
|
+
source_file = global_librarian / filename
|
|
104
|
+
target_file = librarian_dir / filename
|
|
105
|
+
if source_file.is_file() and not target_file.exists():
|
|
106
|
+
shutil.copy2(source_file, target_file)
|
|
107
|
+
|
|
108
|
+
return persona_dir
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def librarian_dir_for(persona_name: str | None = None) -> Path:
|
|
112
|
+
"""Return ``.librarian`` for the persona, creating the layout if needed."""
|
|
113
|
+
runtime = get_runtime()
|
|
114
|
+
name = persona_name or runtime.persona_name
|
|
115
|
+
return ensure_memory_dir(name) / ".librarian"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def load_memory_sources(persona_name: str | None = None) -> list[dict]:
|
|
119
|
+
"""Load registered memory sources for a persona.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
persona_name: Persona/agent name; defaults to the active runtime persona.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
List of source dicts from ``sources.json``, or ``[]`` when missing/invalid.
|
|
126
|
+
"""
|
|
127
|
+
sources_path = librarian_dir_for(persona_name) / "sources.json"
|
|
128
|
+
if not sources_path.is_file():
|
|
129
|
+
return []
|
|
130
|
+
try:
|
|
131
|
+
data = json.loads(sources_path.read_text(encoding="utf-8"))
|
|
132
|
+
except (OSError, json.JSONDecodeError):
|
|
133
|
+
return []
|
|
134
|
+
return data if isinstance(data, list) else []
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def memory_has_sources(persona_name: str | None = None) -> bool:
|
|
138
|
+
"""Return True when the persona has at least one registered source."""
|
|
139
|
+
return bool(load_memory_sources(persona_name))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def read_memory_document_count(persona_name: str | None = None) -> int:
|
|
143
|
+
"""Return indexed document count without mutating process env.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
persona_name: Persona/agent name; defaults to the active runtime persona.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Document count, or ``0`` when the index is missing or unreadable.
|
|
150
|
+
"""
|
|
151
|
+
db_path = librarian_dir_for(persona_name) / "index.db"
|
|
152
|
+
if not db_path.exists():
|
|
153
|
+
return 0
|
|
154
|
+
try:
|
|
155
|
+
from librarian.storage.database import Database
|
|
156
|
+
|
|
157
|
+
stats = Database(db_path=str(db_path)).get_stats()
|
|
158
|
+
return int(stats.get("document_count", 0) or 0)
|
|
159
|
+
except Exception: # noqa: BLE001
|
|
160
|
+
return 0
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def missing_memory_sources(persona_name: str | None = None) -> list[str]:
|
|
164
|
+
"""Return source paths that are registered but missing on disk."""
|
|
165
|
+
missing: list[str] = []
|
|
166
|
+
for source in load_memory_sources(persona_name):
|
|
167
|
+
path = source.get("path")
|
|
168
|
+
if not path:
|
|
169
|
+
continue
|
|
170
|
+
if not Path(str(path)).expanduser().exists():
|
|
171
|
+
name = str(source.get("name") or path)
|
|
172
|
+
missing.append(name)
|
|
173
|
+
return missing
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def apply_memory_env(persona_name: str | None = None) -> Path:
|
|
177
|
+
"""Point librarian storage env vars at the persona's ``.librarian`` dir.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
persona_name: Persona/agent name; defaults to the active runtime persona.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
The persona memory directory (parent of ``.librarian``).
|
|
184
|
+
"""
|
|
185
|
+
runtime = get_runtime()
|
|
186
|
+
name = persona_name or runtime.persona_name
|
|
187
|
+
persona_dir = ensure_memory_dir(name)
|
|
188
|
+
librarian_dir = persona_dir / ".librarian"
|
|
189
|
+
os.environ["DATABASE_PATH"] = str(librarian_dir / "index.db")
|
|
190
|
+
os.environ["DOCUMENTS_PATH"] = str(librarian_dir / "documents")
|
|
191
|
+
os.environ["SOURCES_CONFIG_PATH"] = str(librarian_dir / "sources.json")
|
|
192
|
+
os.environ["HOME"] = str(persona_dir)
|
|
193
|
+
os.environ["LIBRARIAN_PROG_NAME"] = "cade mem"
|
|
194
|
+
return persona_dir
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def rebuild_memory_index(persona_name: str | None = None) -> int:
|
|
198
|
+
"""Non-interactively rebuild the memory index from registered sources.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
persona_name: Persona/agent name; defaults to the active runtime persona.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Indexed document count after rebuild (best-effort; ``0`` on failure).
|
|
205
|
+
"""
|
|
206
|
+
apply_memory_env(persona_name)
|
|
207
|
+
try:
|
|
208
|
+
from librarian.cli import index_build
|
|
209
|
+
except ImportError:
|
|
210
|
+
log.debug("agent-library not installed; cannot rebuild memory index")
|
|
211
|
+
return 0
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
index_build(source=None, confirm=True, verbose=False)
|
|
215
|
+
except SystemExit as exc:
|
|
216
|
+
code = exc.code
|
|
217
|
+
if code not in (None, 0):
|
|
218
|
+
log.warning(f"memory index rebuild exited with code {code}")
|
|
219
|
+
return read_memory_document_count(persona_name)
|
|
220
|
+
except Exception as exc: # noqa: BLE001
|
|
221
|
+
log.warning(f"memory index rebuild failed: {exc}")
|
|
222
|
+
return read_memory_document_count(persona_name)
|
|
223
|
+
|
|
224
|
+
return read_memory_document_count(persona_name)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def warm_memory_embedder(*, quiet: bool = True) -> bool:
|
|
228
|
+
"""Load the text embedding model so the next search is not cold.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
quiet: When True, only log failures at debug; otherwise raise-free warn.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
True when the embedder loaded successfully.
|
|
235
|
+
"""
|
|
236
|
+
try:
|
|
237
|
+
from librarian.processing.embed import get_embedder
|
|
238
|
+
except ImportError:
|
|
239
|
+
if not quiet:
|
|
240
|
+
log.warning("agent-library not installed; cannot warm memory embedder")
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
embedder = get_embedder()
|
|
245
|
+
_ = embedder.dimension
|
|
246
|
+
log.debug(
|
|
247
|
+
f"memory embedder warm (model={getattr(embedder, 'model_name', '?')}, "
|
|
248
|
+
f"dimension={embedder.dimension})"
|
|
249
|
+
)
|
|
250
|
+
return True
|
|
251
|
+
except Exception as exc: # noqa: BLE001
|
|
252
|
+
if quiet:
|
|
253
|
+
log.debug(f"memory embedder warm failed: {exc}")
|
|
254
|
+
else:
|
|
255
|
+
log.warning(f"memory embedder warm failed: {exc}")
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def ensure_memory_ready(
|
|
260
|
+
persona_name: str | None = None,
|
|
261
|
+
*,
|
|
262
|
+
heal: bool = True,
|
|
263
|
+
warm: bool = False,
|
|
264
|
+
) -> MemoryReadyState:
|
|
265
|
+
"""Ensure persona memory is usable: layout, non-empty index, optional warm.
|
|
266
|
+
|
|
267
|
+
When sources are registered but ``document_count == 0``, rebuilds the index
|
|
268
|
+
non-interactively so bootstrap-from-global-sources never presents an empty
|
|
269
|
+
catalogue as ready. Missing source paths are recorded quietly (logged) and
|
|
270
|
+
skipped by the rebuild.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
persona_name: Persona/agent name; defaults to the active runtime persona.
|
|
274
|
+
heal: Rebuild when sources exist but the index is empty.
|
|
275
|
+
warm: Preload the embedding model after heal (or when already ready).
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
:class:`MemoryReadyState` describing the post-check catalogue.
|
|
279
|
+
"""
|
|
280
|
+
runtime = get_runtime()
|
|
281
|
+
name = persona_name or runtime.persona_name
|
|
282
|
+
persona_dir = ensure_memory_dir(name)
|
|
283
|
+
librarian_dir = persona_dir / ".librarian"
|
|
284
|
+
sources = load_memory_sources(name)
|
|
285
|
+
missing = missing_memory_sources(name)
|
|
286
|
+
if missing:
|
|
287
|
+
log.debug(f"memory sources missing on disk (skipped): {', '.join(missing)}")
|
|
288
|
+
|
|
289
|
+
document_count = read_memory_document_count(name)
|
|
290
|
+
healed = False
|
|
291
|
+
if heal and sources and document_count == 0:
|
|
292
|
+
log.info(
|
|
293
|
+
f"memory index empty with {len(sources)} source(s) for agent "
|
|
294
|
+
f"{name!r}; rebuilding automatically"
|
|
295
|
+
)
|
|
296
|
+
document_count = rebuild_memory_index(name)
|
|
297
|
+
healed = True
|
|
298
|
+
|
|
299
|
+
warmed = False
|
|
300
|
+
if warm and (document_count > 0 or healed):
|
|
301
|
+
apply_memory_env(name)
|
|
302
|
+
warmed = warm_memory_embedder(quiet=True)
|
|
303
|
+
|
|
304
|
+
return MemoryReadyState(
|
|
305
|
+
persona_dir=persona_dir,
|
|
306
|
+
librarian_dir=librarian_dir,
|
|
307
|
+
sources=sources,
|
|
308
|
+
document_count=document_count,
|
|
309
|
+
healed=healed,
|
|
310
|
+
missing_sources=missing,
|
|
311
|
+
warmed=warmed,
|
|
312
|
+
)
|
|
@@ -269,9 +269,32 @@ def _resolve_sandbox_root(cfg: Any) -> Any:
|
|
|
269
269
|
return base.resolve()
|
|
270
270
|
|
|
271
271
|
|
|
272
|
+
async def _prewarm_shared_memory(memory_url: str) -> None:
|
|
273
|
+
"""Best-effort warm of the shared memory HTTP service (embedder load)."""
|
|
274
|
+
try:
|
|
275
|
+
from cadecoder.tools.manager.composite import build_builtin_http_config
|
|
276
|
+
from cadecoder.tools.manager.mcp import MCPToolManager
|
|
277
|
+
|
|
278
|
+
config = build_builtin_http_config("__builtin_memory__", memory_url)
|
|
279
|
+
manager = MCPToolManager(config, None)
|
|
280
|
+
try:
|
|
281
|
+
await manager.initialize()
|
|
282
|
+
# Keyword search still loads the hybrid stack / embedder on many builds.
|
|
283
|
+
await manager.execute(
|
|
284
|
+
"Memory_SearchLibrary",
|
|
285
|
+
{"query": "cade memory warm", "mode": "keyword", "limit": 1},
|
|
286
|
+
)
|
|
287
|
+
log.debug("shared memory service prewarmed")
|
|
288
|
+
finally:
|
|
289
|
+
await manager.close()
|
|
290
|
+
except Exception as e: # noqa: BLE001
|
|
291
|
+
log.debug(f"shared memory prewarm skipped: {e}")
|
|
292
|
+
|
|
293
|
+
|
|
272
294
|
async def _start_builtin_services(persona: str) -> None:
|
|
273
295
|
"""Promote stdio local-tools to a shared loopback HTTP service when possible."""
|
|
274
296
|
try:
|
|
297
|
+
from cadecoder.core.memory import ensure_memory_ready, memory_has_sources
|
|
275
298
|
from cadecoder.daemon.services import BuiltinServices
|
|
276
299
|
from cadecoder.tools.manager.composite import (
|
|
277
300
|
BUILTIN_LOCAL_NAME,
|
|
@@ -282,19 +305,37 @@ async def _start_builtin_services(persona: str) -> None:
|
|
|
282
305
|
log.debug(f"builtin services unavailable: {e}")
|
|
283
306
|
return
|
|
284
307
|
|
|
308
|
+
# Heal empty index before any worker can search; warm in-process is a
|
|
309
|
+
# best-effort hint — the shared memory HTTP process warms on first use /
|
|
310
|
+
# via ensure_memory_service prewarm.
|
|
311
|
+
try:
|
|
312
|
+
await asyncio.to_thread(ensure_memory_ready, persona, heal=True, warm=False)
|
|
313
|
+
except Exception as e: # noqa: BLE001
|
|
314
|
+
log.debug(f"memory readiness at daemon boot skipped: {e}")
|
|
315
|
+
|
|
285
316
|
servers = {s.name: s for s in effective_mcp_servers(persona=persona)}
|
|
286
317
|
local = servers.get(BUILTIN_LOCAL_NAME)
|
|
287
318
|
start_local = local is not None and local.transport == MCPTransportType.STDIO
|
|
288
319
|
if not start_local:
|
|
289
320
|
return
|
|
290
321
|
|
|
291
|
-
|
|
322
|
+
start_memory = False
|
|
323
|
+
try:
|
|
324
|
+
start_memory = memory_has_sources(persona)
|
|
325
|
+
except Exception: # noqa: BLE001
|
|
326
|
+
start_memory = False
|
|
327
|
+
|
|
328
|
+
services = BuiltinServices(persona=persona, start_local=True, start_memory=start_memory)
|
|
292
329
|
if not await services.start():
|
|
293
330
|
return
|
|
294
331
|
if services.local_url:
|
|
295
332
|
os.environ["CADE_BUILTIN_LOCAL_URL"] = services.local_url
|
|
296
333
|
if services.memory_url:
|
|
297
334
|
os.environ["CADE_BUILTIN_MEMORY_URL"] = services.memory_url
|
|
335
|
+
asyncio.create_task(
|
|
336
|
+
_prewarm_shared_memory(services.memory_url),
|
|
337
|
+
name="cade-daemon-memory-prewarm",
|
|
338
|
+
)
|
|
298
339
|
|
|
299
340
|
|
|
300
341
|
async def _start_cron(supervisor: Supervisor):
|
|
@@ -161,6 +161,20 @@ class BuiltinServices:
|
|
|
161
161
|
return True
|
|
162
162
|
|
|
163
163
|
async def _start_memory(self) -> None:
|
|
164
|
+
# Heal empty index in the parent before the memory subprocess starts so
|
|
165
|
+
# the first client never sees sources-with-zero-docs as ready.
|
|
166
|
+
try:
|
|
167
|
+
from cadecoder.core.memory import ensure_memory_ready
|
|
168
|
+
|
|
169
|
+
await asyncio.to_thread(
|
|
170
|
+
ensure_memory_ready,
|
|
171
|
+
self.persona,
|
|
172
|
+
heal=True,
|
|
173
|
+
warm=False,
|
|
174
|
+
)
|
|
175
|
+
except Exception as e: # noqa: BLE001
|
|
176
|
+
log.debug(f"memory readiness before shared service skipped: {e}")
|
|
177
|
+
|
|
164
178
|
config = _build_memory_config(self.persona)
|
|
165
179
|
if config is None:
|
|
166
180
|
return # agent-library not installed; nothing to share
|