tsugite-cli 0.14.2__tar.gz → 0.14.3__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.
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/PKG-INFO +1 -1
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/pyproject.toml +1 -1
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/daemon.py +16 -11
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/run.py +50 -46
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/adapters/base.py +42 -15
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/adapters/discord.py +1 -1
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/adapters/http.py +22 -10
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/compaction_scheduler.py +39 -12
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/config.py +38 -2
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/scheduler.py +2 -2
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/session_runner.py +1 -1
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/session_store.py +76 -22
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversation/sessions.js +30 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversations.js +57 -24
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/.gitignore +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/AGENTS.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/CONTRIBUTING.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/LICENSE +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/README.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_inheritance.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_preparation.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/exec_directives.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/exec_runner.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/helpers.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/history_integration.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/metrics.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/models.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/runner.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_runner/validation.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/agent_utils.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/auto_context.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/base.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/file.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/inline.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/storage.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/url.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/attachments/youtube.py +0 -0
- {tsugite_cli-0.14.2/tsugite/builtin_skills → tsugite_cli-0.14.3/tsugite/builtin_agents}/.gitkeep +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_agents/code_searcher.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_agents/default.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_agents/file_searcher.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_agents/onboard.md +0 -0
- {tsugite_cli-0.14.2/tsugite/builtin_agents → tsugite_cli-0.14.3/tsugite/builtin_skills}/.gitkeep +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/codebase-exploration/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/python-math/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/response-patterns/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/scheduling/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/skill-authoring/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/tsugite-agent-basics/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/tsugite-jinja-reference/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/builtin_skills/tsugite-skill-basics/SKILL.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cache.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/agents.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/attachments.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/cache.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/chat.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/config.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/helpers.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/history.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/init.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/models.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/plugins.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/render.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/secrets.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/skills.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/tools.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/usage.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/validate.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/cli/workspace.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/config.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/console.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/constants.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/agent.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/claude_code.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/content_blocks.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/executor.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/memory.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/proxy.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/sandbox.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/state.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/subprocess_executor.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/core/tools.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/adapters/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/adapters/scheduler_adapter.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/auth.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/commands.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/gateway.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/memory.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/push.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/css/console.css +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/css/responsive.css +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/css/styles.css +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/css/theme.css +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/icons/icon-192.png +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/icons/icon-512-maskable.png +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/icons/icon-512.png +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/icons/screenshot-narrow.png +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/icons/screenshot-wide.png +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/index.html +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/api.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/app.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/utils.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/vendor/marked.LICENSE.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/vendor/marked.esm.min.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversation/attachments.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversation/event_types.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversation/history.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversation/input.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/conversation/streaming.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/file-editor.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/schedules.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/usage.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/webhooks.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/js/views/workspace.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/manifest.json +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/web/sw.js +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/daemon/webhook_store.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/events/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/events/base.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/events/bus.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/events/events.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/events/helpers.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/exceptions.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/history/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/history/models.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/history/reconstruction.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/history/storage.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/hooks.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/interaction.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/md_agents.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/models.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/options.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/plugins.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/anthropic.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/base.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/claude_code.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/model_cache.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/model_registry.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/ollama.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/openai_compat.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/providers/openrouter.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/renderer.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/schemas/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/schemas/agent.schema.json +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/backend.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/env.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/exec.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/file.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/masking.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/registry.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/secrets/sqlite.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/shell_tool_config.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/skill_discovery.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/AGENTS.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/IDENTITY.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/MEMORY.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/USER.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/personas/casual-technical.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/personas/marvin.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/templates/personas/minimal.md +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/agents.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/fs.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/history.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/http.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/interactive.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/notify.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/schedule.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/scratchpad.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/secrets.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/sessions.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/shell.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/shell_tools.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/skills.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tools/time.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/tsugite.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/base.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/chat.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/helpers.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/jsonl.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/live.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/plain.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/repl_chat.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/repl_commands.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/repl_completer.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui/repl_handler.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/ui_context.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/usage/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/usage/store.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/user_agent.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/utils.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/workspace/__init__.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/workspace/context.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/workspace/models.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/workspace/session.py +0 -0
- {tsugite_cli-0.14.2 → tsugite_cli-0.14.3}/tsugite/workspace/templates.py +0 -0
|
@@ -44,13 +44,14 @@ def _prompt_bot_name(style) -> str:
|
|
|
44
44
|
).ask()
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def
|
|
48
|
-
"""Prompt for
|
|
47
|
+
def _prompt_token_file_path(style, bot_name: str) -> str:
|
|
48
|
+
"""Prompt for the file path that holds the Discord token."""
|
|
49
49
|
import questionary
|
|
50
50
|
|
|
51
|
+
default_path = f"~/.config/tsugite/discord-{bot_name}.token"
|
|
51
52
|
return questionary.text(
|
|
52
|
-
"
|
|
53
|
-
default=
|
|
53
|
+
"Path to file containing Discord token:",
|
|
54
|
+
default=default_path,
|
|
54
55
|
style=style,
|
|
55
56
|
).ask()
|
|
56
57
|
|
|
@@ -200,7 +201,8 @@ def _config_to_dict(config) -> dict:
|
|
|
200
201
|
"discord_bots": [
|
|
201
202
|
{
|
|
202
203
|
"name": bot.name,
|
|
203
|
-
"
|
|
204
|
+
**({"token_secret": bot.token_secret} if bot.token_secret else {}),
|
|
205
|
+
**({"token_file": str(bot.token_file)} if bot.token_file else {}),
|
|
204
206
|
"agent": bot.agent,
|
|
205
207
|
"command_prefix": bot.command_prefix,
|
|
206
208
|
"dm_policy": bot.dm_policy,
|
|
@@ -220,14 +222,17 @@ def _show_generated_config(config_data: dict):
|
|
|
220
222
|
console.print(Panel(config_yaml, border_style="cyan"))
|
|
221
223
|
|
|
222
224
|
|
|
223
|
-
def _show_next_steps(
|
|
225
|
+
def _show_next_steps(token_file: str, bot_name: str):
|
|
224
226
|
"""Display next steps for the user."""
|
|
225
227
|
console.print("\n" + "=" * 60)
|
|
226
228
|
console.print("[bold green]Setup Complete![/bold green]")
|
|
227
229
|
console.print("=" * 60)
|
|
228
230
|
|
|
229
231
|
console.print("\n[bold]Next Steps:[/bold]")
|
|
230
|
-
console.print(f
|
|
232
|
+
console.print(f" 1. Save your Discord token to: [cyan]{token_file}[/cyan]")
|
|
233
|
+
console.print(
|
|
234
|
+
f' [dim]mkdir -p $(dirname {token_file}) && echo "your-token" > {token_file} && chmod 600 {token_file}[/dim]'
|
|
235
|
+
)
|
|
231
236
|
console.print(" 2. Start the daemon: [cyan]tsugite daemon[/cyan]")
|
|
232
237
|
console.print(" 3. DM your bot with: [cyan]!hello[/cyan]")
|
|
233
238
|
|
|
@@ -295,8 +300,8 @@ def init_daemon(
|
|
|
295
300
|
if not bot_name:
|
|
296
301
|
raise typer.Exit(1)
|
|
297
302
|
|
|
298
|
-
|
|
299
|
-
if not
|
|
303
|
+
token_file = _prompt_token_file_path(style, bot_name)
|
|
304
|
+
if not token_file:
|
|
300
305
|
raise typer.Exit(1)
|
|
301
306
|
|
|
302
307
|
command_prefix = _prompt_command_prefix(style)
|
|
@@ -335,7 +340,7 @@ def init_daemon(
|
|
|
335
340
|
|
|
336
341
|
bot_config = {
|
|
337
342
|
"name": bot_name,
|
|
338
|
-
"
|
|
343
|
+
"token_file": token_file,
|
|
339
344
|
"agent": agent_config_name,
|
|
340
345
|
"command_prefix": command_prefix,
|
|
341
346
|
"dm_policy": dm_policy,
|
|
@@ -357,7 +362,7 @@ def init_daemon(
|
|
|
357
362
|
|
|
358
363
|
console.print(f"[green]✓[/green] Configuration saved to: {config_path}")
|
|
359
364
|
|
|
360
|
-
_show_next_steps(
|
|
365
|
+
_show_next_steps(token_file, bot_name)
|
|
361
366
|
|
|
362
367
|
|
|
363
368
|
def _daemon_request(method: str, host: str, port: int, path: str, token: Optional[str] = None, **kwargs):
|
|
@@ -87,19 +87,21 @@ def _check_and_run_onboarding(
|
|
|
87
87
|
return workspace
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def _resolve_ui_mode(ui_mode: Optional[str], ui_opts: UIOptions,
|
|
90
|
+
def _resolve_ui_mode(ui_mode: Optional[str], ui_opts: UIOptions, stderr_console: Console) -> UIOptions:
|
|
91
91
|
"""Resolve UI mode flag and return updated UIOptions."""
|
|
92
92
|
if not ui_mode:
|
|
93
93
|
return ui_opts
|
|
94
94
|
|
|
95
95
|
if ui_opts.plain or ui_opts.headless:
|
|
96
|
-
|
|
96
|
+
stderr_console.print("[red]Error: --ui cannot be used with --plain or --headless[/red]")
|
|
97
97
|
raise typer.Exit(1)
|
|
98
98
|
|
|
99
99
|
ui_modes = {"plain", "headless", "live"}
|
|
100
100
|
ui_lower = ui_mode.lower()
|
|
101
101
|
if ui_lower not in ui_modes:
|
|
102
|
-
|
|
102
|
+
stderr_console.print(
|
|
103
|
+
f"[red]Error: Invalid UI mode '{ui_mode}'. Choose from: {', '.join(sorted(ui_modes))}[/red]"
|
|
104
|
+
)
|
|
103
105
|
raise typer.Exit(1)
|
|
104
106
|
|
|
105
107
|
if ui_lower == "plain":
|
|
@@ -142,7 +144,7 @@ def _build_executor_kwargs(
|
|
|
142
144
|
|
|
143
145
|
|
|
144
146
|
def _resolve_conversation_continuation(
|
|
145
|
-
continue_conversation: bool, conversation_id: Optional[str],
|
|
147
|
+
continue_conversation: bool, conversation_id: Optional[str], stderr_console: Console
|
|
146
148
|
) -> Optional[str]:
|
|
147
149
|
"""Resolve which conversation to continue."""
|
|
148
150
|
if not continue_conversation:
|
|
@@ -151,15 +153,15 @@ def _resolve_conversation_continuation(
|
|
|
151
153
|
from tsugite.agent_runner.history_integration import get_latest_conversation
|
|
152
154
|
|
|
153
155
|
if conversation_id:
|
|
154
|
-
|
|
156
|
+
stderr_console.print(f"[cyan]Continuing conversation: {conversation_id}[/cyan]")
|
|
155
157
|
return conversation_id
|
|
156
158
|
|
|
157
159
|
continue_conversation_id = get_latest_conversation()
|
|
158
160
|
if not continue_conversation_id:
|
|
159
|
-
|
|
161
|
+
stderr_console.print("[red]No conversations found to resume[/red]")
|
|
160
162
|
raise typer.Exit(1)
|
|
161
163
|
|
|
162
|
-
|
|
164
|
+
stderr_console.print(f"[cyan]Continuing latest conversation: {continue_conversation_id}[/cyan]")
|
|
163
165
|
return continue_conversation_id
|
|
164
166
|
|
|
165
167
|
|
|
@@ -318,21 +320,24 @@ def run(
|
|
|
318
320
|
tsu run --daemon odyn "follow up message" # Join daemon session for agent 'odyn'
|
|
319
321
|
"""
|
|
320
322
|
from tsugite.agent_runner import get_agent_info, run_agent
|
|
323
|
+
from tsugite.console import get_stderr_console
|
|
321
324
|
from tsugite.md_agents import validate_agent_execution
|
|
322
325
|
from tsugite.secrets import init_cli as init_secrets
|
|
323
326
|
from tsugite.utils import should_use_plain_output
|
|
324
327
|
|
|
325
328
|
from . import console
|
|
326
329
|
|
|
330
|
+
stderr_console = get_stderr_console(no_color=no_color)
|
|
331
|
+
|
|
327
332
|
init_secrets(no_secrets)
|
|
328
333
|
|
|
329
334
|
# Validate flag combinations
|
|
330
335
|
if new_session and continue_conversation:
|
|
331
|
-
|
|
336
|
+
stderr_console.print("[red]Error: Cannot use --new-session with --continue[/red]")
|
|
332
337
|
raise typer.Exit(1)
|
|
333
338
|
|
|
334
339
|
if workspace and no_workspace:
|
|
335
|
-
|
|
340
|
+
stderr_console.print("[red]Error: Cannot use --workspace with --no-workspace[/red]")
|
|
336
341
|
raise typer.Exit(1)
|
|
337
342
|
|
|
338
343
|
ui_opts = UIOptions(
|
|
@@ -349,7 +354,7 @@ def run(
|
|
|
349
354
|
workspace_to_use, resolved_workspace = _resolve_effective_workspace(workspace, no_workspace)
|
|
350
355
|
|
|
351
356
|
if new_session and not workspace_to_use:
|
|
352
|
-
|
|
357
|
+
stderr_console.print("[yellow]Warning: --new-session has no effect without a workspace[/yellow]")
|
|
353
358
|
exec_opts = ExecutionOptions.from_cli(
|
|
354
359
|
model=model,
|
|
355
360
|
debug=debug,
|
|
@@ -367,7 +372,7 @@ def run(
|
|
|
367
372
|
history_dir=history_dir,
|
|
368
373
|
)
|
|
369
374
|
if workspace_to_use and not resolved_workspace:
|
|
370
|
-
|
|
375
|
+
stderr_console.print(f"[yellow]Warning: Workspace '{workspace_to_use}' not found[/yellow]")
|
|
371
376
|
|
|
372
377
|
# Auto-continue workspace session unless explicitly overridden
|
|
373
378
|
workspace_session_continued = False
|
|
@@ -407,14 +412,16 @@ def run(
|
|
|
407
412
|
console.no_color = True
|
|
408
413
|
|
|
409
414
|
# Resolve UI mode to update ui_opts
|
|
410
|
-
ui_opts = _resolve_ui_mode(ui, ui_opts,
|
|
415
|
+
ui_opts = _resolve_ui_mode(ui, ui_opts, stderr_console)
|
|
411
416
|
|
|
412
417
|
# Handle subagent mode and daemon mode (both need os)
|
|
413
418
|
import os
|
|
414
419
|
|
|
415
420
|
if subagent_mode:
|
|
416
421
|
if ui_opts.plain or ui_opts.headless or ui_opts.live:
|
|
417
|
-
|
|
422
|
+
stderr_console.print(
|
|
423
|
+
"[red]Error: --subagent-mode cannot be combined with --plain, --headless, or --ui live[/red]"
|
|
424
|
+
)
|
|
418
425
|
raise typer.Exit(1)
|
|
419
426
|
|
|
420
427
|
ui_opts.non_interactive = True
|
|
@@ -430,14 +437,14 @@ def run(
|
|
|
430
437
|
|
|
431
438
|
daemon_config = load_daemon_config()
|
|
432
439
|
if daemon_agent not in daemon_config.agents:
|
|
433
|
-
|
|
440
|
+
stderr_console.print(f"[red]Agent '{daemon_agent}' not found in daemon config[/red]")
|
|
434
441
|
raise typer.Exit(1)
|
|
435
442
|
|
|
436
443
|
agent_config = daemon_config.agents[daemon_agent]
|
|
437
444
|
|
|
438
445
|
except ValueError as e:
|
|
439
|
-
|
|
440
|
-
|
|
446
|
+
stderr_console.print(f"[red]Daemon config not found: {e}[/red]")
|
|
447
|
+
stderr_console.print("[dim]Run 'tsugite daemon' to start daemon first[/dim]")
|
|
441
448
|
raise typer.Exit(1)
|
|
442
449
|
|
|
443
450
|
# Find latest session for this agent
|
|
@@ -455,11 +462,11 @@ def run(
|
|
|
455
462
|
continue
|
|
456
463
|
|
|
457
464
|
if latest_conv_id:
|
|
458
|
-
|
|
465
|
+
stderr_console.print(f"[cyan]Joining daemon session: {latest_conv_id}[/cyan]")
|
|
459
466
|
history_opts.continue_id = latest_conv_id
|
|
460
467
|
else:
|
|
461
|
-
|
|
462
|
-
|
|
468
|
+
stderr_console.print(f"[yellow]No active daemon session found for '{daemon_agent}'[/yellow]")
|
|
469
|
+
stderr_console.print("[dim]Creating new daemon-managed session...[/dim]")
|
|
463
470
|
|
|
464
471
|
# Override agent with daemon agent
|
|
465
472
|
args = [f"+{daemon_agent}"] + args
|
|
@@ -489,9 +496,9 @@ def run(
|
|
|
489
496
|
|
|
490
497
|
# Handle conversation continuation - check before parsing args
|
|
491
498
|
if continue_conversation and not history_opts.continue_id:
|
|
492
|
-
history_opts.continue_id = _resolve_conversation_continuation(True, None,
|
|
499
|
+
history_opts.continue_id = _resolve_conversation_continuation(True, None, stderr_console)
|
|
493
500
|
elif history_opts.continue_id:
|
|
494
|
-
|
|
501
|
+
stderr_console.print(f"[cyan]Continuing conversation: {history_opts.continue_id}[/cyan]")
|
|
495
502
|
|
|
496
503
|
# Parse CLI arguments into agents and prompt (allow empty agents when continuing)
|
|
497
504
|
try:
|
|
@@ -501,10 +508,10 @@ def run(
|
|
|
501
508
|
args, allow_empty_agents=continue_conversation, check_stdin=not continue_conversation
|
|
502
509
|
)
|
|
503
510
|
except ValueError as e:
|
|
504
|
-
|
|
511
|
+
stderr_console.print(f"[red]Error: {e}[/red]")
|
|
505
512
|
raise typer.Exit(1)
|
|
506
513
|
|
|
507
|
-
with workspace_directory_context(resolved_workspace, root,
|
|
514
|
+
with workspace_directory_context(resolved_workspace, root, stderr_console) as path_context:
|
|
508
515
|
try:
|
|
509
516
|
base_dir = Path.cwd()
|
|
510
517
|
|
|
@@ -518,15 +525,17 @@ def run(
|
|
|
518
525
|
if not agent_name:
|
|
519
526
|
raise ValueError("agent name missing from session_start")
|
|
520
527
|
except Exception:
|
|
521
|
-
|
|
528
|
+
stderr_console.print(
|
|
529
|
+
f"[red]Could not load metadata for conversation: {history_opts.continue_id}[/red]"
|
|
530
|
+
)
|
|
522
531
|
raise typer.Exit(1)
|
|
523
532
|
|
|
524
|
-
|
|
533
|
+
stderr_console.print(f"[cyan]Auto-detected agent from conversation: {agent_name}[/cyan]")
|
|
525
534
|
agent_refs = [f"+{agent_name}"]
|
|
526
535
|
|
|
527
536
|
# Handle multi-agent mode: first agent is primary, rest are allowed to spawn
|
|
528
537
|
if not agent_refs:
|
|
529
|
-
|
|
538
|
+
stderr_console.print("[red]Error: No agent specified[/red]")
|
|
530
539
|
raise typer.Exit(1)
|
|
531
540
|
|
|
532
541
|
from tsugite.agent_runner.helpers import set_allowed_agents
|
|
@@ -537,27 +546,23 @@ def run(
|
|
|
537
546
|
if len(agent_refs) > 1:
|
|
538
547
|
allowed_agent_names = []
|
|
539
548
|
for allowed_ref in agent_refs[1:]:
|
|
540
|
-
_, allowed_file, _ = load_and_validate_agent(allowed_ref,
|
|
549
|
+
_, allowed_file, _ = load_and_validate_agent(allowed_ref, stderr_console)
|
|
541
550
|
allowed_agent = parse_agent_file(allowed_file)
|
|
542
551
|
allowed_agent_names.append(allowed_agent.config.name)
|
|
543
552
|
|
|
544
553
|
set_allowed_agents(allowed_agent_names)
|
|
545
|
-
|
|
554
|
+
stderr_console.print(f"[cyan]Allowed agents to spawn: {', '.join(allowed_agent_names)}[/cyan]")
|
|
546
555
|
else:
|
|
547
556
|
set_allowed_agents(None)
|
|
548
557
|
|
|
549
|
-
_, agent_file, _ = load_and_validate_agent(primary_agent_ref,
|
|
558
|
+
_, agent_file, _ = load_and_validate_agent(primary_agent_ref, stderr_console)
|
|
550
559
|
|
|
551
560
|
except ValueError as e:
|
|
552
|
-
|
|
561
|
+
stderr_console.print(f"[red]Error: {e}[/red]")
|
|
553
562
|
raise typer.Exit(1)
|
|
554
563
|
|
|
555
564
|
use_plain_output = ui_opts.plain or should_use_plain_output()
|
|
556
565
|
|
|
557
|
-
from tsugite.console import get_stderr_console
|
|
558
|
-
|
|
559
|
-
stderr_console = get_stderr_console(no_color=ui_opts.no_color)
|
|
560
|
-
|
|
561
566
|
# Print deferred workspace status messages
|
|
562
567
|
if not ui_opts.headless and not ui_opts.final_only:
|
|
563
568
|
if workspace_to_use and not workspace:
|
|
@@ -591,7 +596,7 @@ def run(
|
|
|
591
596
|
cli_attachments=attach_opts.sources,
|
|
592
597
|
base_dir=base_dir,
|
|
593
598
|
refresh_cache=attach_opts.refresh_cache,
|
|
594
|
-
console=
|
|
599
|
+
console=stderr_console,
|
|
595
600
|
stdin_attachment=stdin_attachment,
|
|
596
601
|
)
|
|
597
602
|
|
|
@@ -624,7 +629,7 @@ def run(
|
|
|
624
629
|
|
|
625
630
|
is_valid, error_msg = validate_agent_execution(agent_file)
|
|
626
631
|
if not is_valid:
|
|
627
|
-
|
|
632
|
+
stderr_console.print(f"[red]Agent validation failed: {error_msg}[/red]")
|
|
628
633
|
raise typer.Exit(1)
|
|
629
634
|
|
|
630
635
|
from tsugite.agent_runner import preview_multistep_agent, run_multistep_agent
|
|
@@ -638,11 +643,11 @@ def run(
|
|
|
638
643
|
preview_multistep_agent(
|
|
639
644
|
agent_path=agent_file,
|
|
640
645
|
prompt=prompt,
|
|
641
|
-
console=
|
|
646
|
+
console=stderr_console,
|
|
642
647
|
)
|
|
643
648
|
else:
|
|
644
|
-
|
|
645
|
-
|
|
649
|
+
stderr_console.print("[yellow]Dry-run mode is for multi-step agents only.[/yellow]")
|
|
650
|
+
stderr_console.print("[dim]This is a single-step agent. Use --debug to see the rendered prompt.[/dim]")
|
|
646
651
|
return
|
|
647
652
|
|
|
648
653
|
executor = run_multistep_agent if is_multistep else run_agent
|
|
@@ -708,7 +713,7 @@ def run(
|
|
|
708
713
|
executor_kwargs,
|
|
709
714
|
ui_opts,
|
|
710
715
|
use_plain_output,
|
|
711
|
-
|
|
716
|
+
stderr_console,
|
|
712
717
|
)
|
|
713
718
|
|
|
714
719
|
(
|
|
@@ -734,20 +739,19 @@ def run(
|
|
|
734
739
|
|
|
735
740
|
except ValueError as e:
|
|
736
741
|
_save_history(status="error", error_message=str(e))
|
|
737
|
-
|
|
742
|
+
stderr_console.print(f"[red]Configuration error: {e}[/red]")
|
|
738
743
|
raise typer.Exit(1)
|
|
739
744
|
except RuntimeError as e:
|
|
740
745
|
_save_history(status="error", error_message=str(e))
|
|
741
|
-
|
|
746
|
+
stderr_console.print(f"[red]Execution error: {e}[/red]")
|
|
742
747
|
raise typer.Exit(1)
|
|
743
748
|
except KeyboardInterrupt:
|
|
744
749
|
_save_history(status="interrupted")
|
|
745
|
-
|
|
750
|
+
stderr_console.print("\n[yellow]Agent execution interrupted by user[/yellow]")
|
|
746
751
|
raise typer.Exit(130)
|
|
747
752
|
except Exception as e:
|
|
748
753
|
_save_history(status="error", error_message=str(e))
|
|
749
|
-
|
|
750
|
-
err_console.print(f"[red]Unexpected error: {e}[/red]")
|
|
754
|
+
stderr_console.print(f"[red]Unexpected error: {e}[/red]")
|
|
751
755
|
if not ui_opts.log_json:
|
|
752
|
-
|
|
756
|
+
stderr_console.print("\n[dim]Use --log-json for machine-readable output[/dim]")
|
|
753
757
|
raise typer.Exit(1)
|
|
@@ -431,6 +431,7 @@ class BaseAdapter(ABC):
|
|
|
431
431
|
scheduled_xml = render_iso_element("scheduled_for", meta.get("scheduled_for"), tz, tz_label, now)
|
|
432
432
|
actual_xml = render_iso_element("actual_fire_time", meta.get("actual_fire_time"), tz, tz_label, now)
|
|
433
433
|
scheduler_timing_xml = scheduled_xml + actual_xml
|
|
434
|
+
context_limit_for_render = self.session_store.get_context_limit(self.agent_name)
|
|
434
435
|
try:
|
|
435
436
|
conv_id_override = (channel_context.metadata or {}).get("conv_id_override")
|
|
436
437
|
if conv_id_override:
|
|
@@ -440,6 +441,7 @@ class BaseAdapter(ABC):
|
|
|
440
441
|
if session is None:
|
|
441
442
|
raise ValueError("no default session yet")
|
|
442
443
|
tokens_used = session.cumulative_tokens
|
|
444
|
+
context_limit_for_render = self.session_store.get_session_context_limit(session.id)
|
|
443
445
|
session_started_xml = render_iso_element("session_started", session.created_at, tz, tz_label, now)
|
|
444
446
|
last_active_xml = render_iso_element("last_active", session.last_active, tz, tz_label, now)
|
|
445
447
|
if session.metadata:
|
|
@@ -465,7 +467,7 @@ class BaseAdapter(ABC):
|
|
|
465
467
|
<source>{channel_context.source}</source>
|
|
466
468
|
<user_id>{channel_context.user_id}</user_id>
|
|
467
469
|
<context_tokens_used>{tokens_used}</context_tokens_used>
|
|
468
|
-
<context_limit>{
|
|
470
|
+
<context_limit>{context_limit_for_render}</context_limit>{session_topic_xml}{session_meta_xml}{scratchpad_xml}
|
|
469
471
|
</message_context>
|
|
470
472
|
|
|
471
473
|
{message}"""
|
|
@@ -684,8 +686,10 @@ class BaseAdapter(ABC):
|
|
|
684
686
|
|
|
685
687
|
ps = getattr(result, "provider_state", None) or {}
|
|
686
688
|
if ps.get("context_window"):
|
|
687
|
-
|
|
688
|
-
|
|
689
|
+
# Per-session storage: this turn's reported window applies to THIS
|
|
690
|
+
# session only. An agent-wide scalar would let any other turn (or a
|
|
691
|
+
# secondary model call) clobber the displayed limit.
|
|
692
|
+
self.session_store.update_session_context_limit(conv_id, ps["context_window"])
|
|
689
693
|
|
|
690
694
|
last_input = getattr(result, "last_input_tokens", None)
|
|
691
695
|
context_tokens = last_input if isinstance(last_input, int) and last_input > 0 else (result.token_count or 0)
|
|
@@ -866,7 +870,27 @@ class BaseAdapter(ABC):
|
|
|
866
870
|
use the returned session for downstream id-keyed work; rediscovering
|
|
867
871
|
via `find_default_session` is unreliable for non-default or
|
|
868
872
|
non-interactive sessions.
|
|
873
|
+
|
|
874
|
+
Defensive snapshot/restore of the agent-wide context-limit fallback. The
|
|
875
|
+
primary per-session limit lives on `Session.context_limit` and isn't
|
|
876
|
+
touched here; this guard catches any future code path that mutates the
|
|
877
|
+
agent-wide default during the compaction flow.
|
|
869
878
|
"""
|
|
879
|
+
saved_session_store_limit = self.session_store.get_context_limit(self.agent_name)
|
|
880
|
+
saved_agent_config_limit = self.agent_config.context_limit
|
|
881
|
+
try:
|
|
882
|
+
return await self._compact_session_inner(session_id, instructions, reason, progress_callback)
|
|
883
|
+
finally:
|
|
884
|
+
self.session_store.update_context_limit(self.agent_name, saved_session_store_limit)
|
|
885
|
+
self.agent_config.context_limit = saved_agent_config_limit
|
|
886
|
+
|
|
887
|
+
async def _compact_session_inner(
|
|
888
|
+
self,
|
|
889
|
+
session_id: str,
|
|
890
|
+
instructions: str | None,
|
|
891
|
+
reason: str | None,
|
|
892
|
+
progress_callback: Optional[Callable[[Dict[str, Any]], None]],
|
|
893
|
+
) -> Optional[Session]:
|
|
870
894
|
if instructions is None:
|
|
871
895
|
instructions = self._DEFAULT_COMPACT_INSTRUCTIONS
|
|
872
896
|
from tsugite.daemon.memory import (
|
|
@@ -894,7 +918,12 @@ class BaseAdapter(ABC):
|
|
|
894
918
|
None,
|
|
895
919
|
)
|
|
896
920
|
|
|
897
|
-
|
|
921
|
+
# Fallback to the session's tracked window (set from the main model's
|
|
922
|
+
# last reported context_window) rather than the agent-wide scalar so
|
|
923
|
+
# sessions with different model overrides compute their own correct
|
|
924
|
+
# retention budget.
|
|
925
|
+
session_limit_fallback = self.session_store.get_session_context_limit(session_id)
|
|
926
|
+
context_limit = get_context_limit(model, fallback=session_limit_fallback)
|
|
898
927
|
retention_budget = int(context_limit * RETENTION_BUDGET_RATIO)
|
|
899
928
|
|
|
900
929
|
old_events, recent_events = split_events_for_compaction(all_events, model, retention_budget)
|
|
@@ -1002,25 +1031,16 @@ class BaseAdapter(ABC):
|
|
|
1002
1031
|
|
|
1003
1032
|
old_messages = sanitize_for_summary(old_messages, model=model, attachment_basenames=attachment_basenames)
|
|
1004
1033
|
|
|
1005
|
-
# Snapshot the agent's tracked context limit before summarization so
|
|
1006
|
-
# that any mutation during the call (e.g. provider state leakage from
|
|
1007
|
-
# a smaller compact model) doesn't corrupt the displayed value or
|
|
1008
|
-
# the next compaction-threshold computation.
|
|
1009
|
-
saved_session_store_limit = self.session_store.get_context_limit(self.agent_name)
|
|
1010
|
-
saved_agent_config_limit = self.agent_config.context_limit
|
|
1011
1034
|
try:
|
|
1012
1035
|
summary = await summarize_session(
|
|
1013
1036
|
old_messages,
|
|
1014
1037
|
model=model,
|
|
1015
|
-
max_context_tokens=
|
|
1038
|
+
max_context_tokens=session_limit_fallback,
|
|
1016
1039
|
progress_callback=progress_callback,
|
|
1017
1040
|
)
|
|
1018
1041
|
except Exception:
|
|
1019
1042
|
logger.exception("[%s] Compaction summarization failed", self.agent_name)
|
|
1020
1043
|
raise
|
|
1021
|
-
finally:
|
|
1022
|
-
self.session_store.update_context_limit(self.agent_name, saved_session_store_limit)
|
|
1023
|
-
self.agent_config.context_limit = saved_agent_config_limit
|
|
1024
1044
|
|
|
1025
1045
|
new_session = self.session_store.compact_session(session_id)
|
|
1026
1046
|
new_session_path = get_history_dir() / f"{new_session.id}.jsonl"
|
|
@@ -1044,7 +1064,14 @@ class BaseAdapter(ABC):
|
|
|
1044
1064
|
source_session_id=old_conv_id,
|
|
1045
1065
|
)
|
|
1046
1066
|
for event in recent_events:
|
|
1047
|
-
|
|
1067
|
+
data = event.data
|
|
1068
|
+
if event.type == "model_response" and "state_delta" in data:
|
|
1069
|
+
# state_delta holds provider-specific runtime IDs (e.g. claude_code
|
|
1070
|
+
# session_id, compaction flags) tied to the pre-compaction session.
|
|
1071
|
+
# Carrying them forward causes the next turn to resume the old
|
|
1072
|
+
# Claude Code session and bypass compaction entirely.
|
|
1073
|
+
data = {k: v for k, v in data.items() if k != "state_delta"}
|
|
1074
|
+
new_storage.record(event.type, **data)
|
|
1048
1075
|
|
|
1049
1076
|
post_compact_execs = await fire_compact_hooks(
|
|
1050
1077
|
self.agent_config.workspace_dir,
|
|
@@ -892,7 +892,7 @@ class DiscordAdapter(BaseAdapter):
|
|
|
892
892
|
backoff = 5
|
|
893
893
|
for attempt in range(max_retries):
|
|
894
894
|
try:
|
|
895
|
-
await self.bot.start(self.bot_config.
|
|
895
|
+
await self.bot.start(self.bot_config.resolve_token())
|
|
896
896
|
break
|
|
897
897
|
except (discord.errors.DiscordServerError, OSError) as e:
|
|
898
898
|
if attempt < max_retries - 1:
|
|
@@ -7,7 +7,7 @@ import mimetypes
|
|
|
7
7
|
import re
|
|
8
8
|
import shutil
|
|
9
9
|
import threading
|
|
10
|
-
from dataclasses import
|
|
10
|
+
from dataclasses import dataclass
|
|
11
11
|
from dataclasses import fields as dataclass_fields
|
|
12
12
|
from datetime import datetime, timezone
|
|
13
13
|
from pathlib import Path
|
|
@@ -28,7 +28,7 @@ from tsugite.attachments.base import AttachmentContentType
|
|
|
28
28
|
from tsugite.attachments.file import FileHandler
|
|
29
29
|
from tsugite.daemon.adapters.base import _PERSIST_EVENT_TYPES, BaseAdapter, ChannelContext
|
|
30
30
|
from tsugite.daemon.config import AgentConfig, HTTPConfig
|
|
31
|
-
from tsugite.daemon.scheduler import ScheduleEntry
|
|
31
|
+
from tsugite.daemon.scheduler import ScheduleEntry, entry_to_dict
|
|
32
32
|
from tsugite.daemon.webhook_store import WebhookStore
|
|
33
33
|
from tsugite.events.base import BaseEvent
|
|
34
34
|
from tsugite.history.storage import get_history_dir
|
|
@@ -836,8 +836,16 @@ class HTTPServer:
|
|
|
836
836
|
"model": model,
|
|
837
837
|
"resolved_model": resolved_model if resolved_model != model else None,
|
|
838
838
|
"tokens": tokens,
|
|
839
|
-
"context_limit":
|
|
840
|
-
|
|
839
|
+
"context_limit": (
|
|
840
|
+
adapter.session_store.get_session_context_limit(session.id)
|
|
841
|
+
if session
|
|
842
|
+
else adapter.session_store.get_context_limit(adapter.agent_name)
|
|
843
|
+
),
|
|
844
|
+
"threshold": (
|
|
845
|
+
adapter.session_store.get_session_compaction_threshold(session.id)
|
|
846
|
+
if session
|
|
847
|
+
else adapter.session_store.get_compaction_threshold(adapter.agent_name)
|
|
848
|
+
),
|
|
841
849
|
"message_count": message_count,
|
|
842
850
|
"compacting": adapter.session_store.is_compacting(
|
|
843
851
|
user_id, adapter.agent_name, session_id=session.id if session else None
|
|
@@ -860,6 +868,10 @@ class HTTPServer:
|
|
|
860
868
|
|
|
861
869
|
if "model" in body:
|
|
862
870
|
new_model = body["model"].strip() if body["model"] else None
|
|
871
|
+
# Pin existing sessions to the current model before mutating the
|
|
872
|
+
# agent default so they don't silently switch on their next turn.
|
|
873
|
+
# The default change should only affect sessions created after it.
|
|
874
|
+
adapter.session_store.freeze_session_models_to_current(adapter.agent_name, agent_config.model)
|
|
863
875
|
agent_config.model = new_model
|
|
864
876
|
|
|
865
877
|
from tsugite.daemon.memory import DEFAULT_CONTEXT_LIMIT, get_context_limit
|
|
@@ -1469,8 +1481,8 @@ class HTTPServer:
|
|
|
1469
1481
|
{
|
|
1470
1482
|
"session_id": target_session_id,
|
|
1471
1483
|
"tokens": refreshed.cumulative_tokens,
|
|
1472
|
-
"context_limit": adapter.session_store.
|
|
1473
|
-
"threshold": adapter.session_store.
|
|
1484
|
+
"context_limit": adapter.session_store.get_session_context_limit(target_session_id),
|
|
1485
|
+
"threshold": adapter.session_store.get_session_compaction_threshold(target_session_id),
|
|
1474
1486
|
"message_count": refreshed.message_count,
|
|
1475
1487
|
"model": adapter.resolve_model(),
|
|
1476
1488
|
"attachments": [a.name for a in adapter._get_all_attachments()],
|
|
@@ -1541,7 +1553,7 @@ class HTTPServer:
|
|
|
1541
1553
|
async def _list_schedules(self, request: Request) -> JSONResponse:
|
|
1542
1554
|
if err := self._require_auth_and_scheduler(request):
|
|
1543
1555
|
return err
|
|
1544
|
-
return JSONResponse({"schedules": [
|
|
1556
|
+
return JSONResponse({"schedules": [entry_to_dict(e) for e in self.scheduler.list()]})
|
|
1545
1557
|
|
|
1546
1558
|
async def _create_schedule(self, request: Request) -> JSONResponse:
|
|
1547
1559
|
if err := self._require_auth_and_scheduler(request):
|
|
@@ -1565,7 +1577,7 @@ class HTTPServer:
|
|
|
1565
1577
|
return JSONResponse({"error": str(e)}, status_code=400)
|
|
1566
1578
|
|
|
1567
1579
|
self.event_bus.emit("schedule_update", {"action": "created", "id": entry.id})
|
|
1568
|
-
return JSONResponse(
|
|
1580
|
+
return JSONResponse(entry_to_dict(entry), status_code=201)
|
|
1569
1581
|
|
|
1570
1582
|
async def _get_schedule(self, request: Request) -> JSONResponse:
|
|
1571
1583
|
if err := self._require_auth_and_scheduler(request):
|
|
@@ -1574,7 +1586,7 @@ class HTTPServer:
|
|
|
1574
1586
|
entry = self.scheduler.get(request.path_params["schedule_id"])
|
|
1575
1587
|
except ValueError as e:
|
|
1576
1588
|
return JSONResponse({"error": str(e)}, status_code=404)
|
|
1577
|
-
return JSONResponse(
|
|
1589
|
+
return JSONResponse(entry_to_dict(entry))
|
|
1578
1590
|
|
|
1579
1591
|
async def _update_schedule(self, request: Request) -> JSONResponse:
|
|
1580
1592
|
if err := self._require_auth_and_scheduler(request):
|
|
@@ -1609,7 +1621,7 @@ class HTTPServer:
|
|
|
1609
1621
|
except ValueError as e:
|
|
1610
1622
|
return JSONResponse({"error": str(e)}, status_code=400)
|
|
1611
1623
|
self.event_bus.emit("schedule_update", {"action": "updated", "id": schedule_id})
|
|
1612
|
-
return JSONResponse(
|
|
1624
|
+
return JSONResponse(entry_to_dict(entry))
|
|
1613
1625
|
|
|
1614
1626
|
async def _delete_schedule(self, request: Request) -> JSONResponse:
|
|
1615
1627
|
if err := self._require_auth_and_scheduler(request):
|