tsugite-cli 0.23.2__tar.gz → 0.24.1__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.23.2 → tsugite_cli-0.24.1}/PKG-INFO +1 -1
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/pyproject.toml +1 -1
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_preparation.py +2 -2
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/history_integration.py +28 -20
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/runner.py +25 -5
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/steps.py +2 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/agent_config.py +14 -14
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/default.md +11 -3
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/scheduling/SKILL.md +2 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/daemon.py +64 -83
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/history.py +2 -2
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/agent.py +23 -6
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/executor.py +28 -2
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/subprocess_executor.py +22 -7
- tsugite_cli-0.24.1/tsugite/daemon_client.py +201 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/events/helpers.py +14 -3
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/models.py +1 -1
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/sqlite_backend.py +17 -7
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/md_agents.py +1 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/permissions.py +31 -3
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/anthropic.py +5 -3
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/openai_compat.py +4 -1
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/schemas/agent.schema.json +10 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/skill_discovery.py +6 -0
- tsugite_cli-0.24.1/tsugite/tools/daemon_control.py +107 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/http.py +27 -2
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/jobs.py +21 -2
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/schedule.py +13 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/skills.py +19 -13
- tsugite_cli-0.23.2/tsugite/tools/daemon_control.py +0 -60
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/.gitignore +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/AGENTS.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/CONTRIBUTING.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/LICENSE +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/README.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_inheritance.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/exec_directives.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/exec_runner.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/helpers.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/metrics.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/models.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_runner/validation.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/agent_utils.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/approval.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/auto_context.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/base.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/delegation.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/file.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/inline.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/storage.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/attachments/url.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/.gitkeep +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/code_searcher.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/file_searcher.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/job_verifier.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/job_worker.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_agents/onboard.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_context.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/.gitkeep +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/codebase-exploration/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/external-agent-bridge/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/python-math/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/response-patterns/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/skill-authoring/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/tsugite-agent-basics/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/tsugite-jinja-reference/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/builtin_skills/tsugite-skill-basics/SKILL.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cache.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cancellation.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/agents.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/attachments.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/cache.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/chat.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/config.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/exec.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/helpers.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/init.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/models.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/plugins.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/render.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/run.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/secrets.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/skills.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/tools.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/usage.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/validate.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/cli/workspace.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/config.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/console.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/constants.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/context.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/context_block.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/content_blocks.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/executor_registry.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/memory.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/proxy.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/record_store.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/sandbox.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/state.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/core/tools.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/events/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/events/base.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/events/bus.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/events/events.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/exceptions.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/base.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/reconstruction.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/registry.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/sqlite_conn.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/sqlite_schema.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/history/ui_events.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/hooks.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/interaction.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/models.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/options.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/plugins.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/prompt_xml.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/base.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/model_cache.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/model_registry.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/ollama.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/providers/openrouter.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/py.typed +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/renderer.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/schemas/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/backend.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/env.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/exec.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/file.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/masking.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/redaction.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/registry.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/secrets/sqlite.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/shell_tool_config.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/AGENTS.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/IDENTITY.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/MEMORY.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/USER.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/personas/casual-technical.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/personas/marvin.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/templates/personas/minimal.md +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/agents.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/artifacts.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/fs.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/history.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/interactive.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/notify.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/secrets.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/sessions.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/shell.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/shell_tools.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tools/time.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/tsugite.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/base.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/chat.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/dispatch.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/helpers.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/jsonl.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/plain.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/repl_chat.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/repl_commands.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/repl_completer.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui/repl_handler.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui_context.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/ui_surfaces.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/usage/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/usage/quota.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/usage/store.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/user_agent.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/utils.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/workspace/__init__.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/workspace/models.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/workspace/session.py +0 -0
- {tsugite_cli-0.23.2 → tsugite_cli-0.24.1}/tsugite/workspace/templates.py +0 -0
|
@@ -242,7 +242,7 @@ class AgentPreparer:
|
|
|
242
242
|
auto_load_skills = [s for s in (agent_config.auto_load_skills or []) if s not in suppressed_skills]
|
|
243
243
|
|
|
244
244
|
for skill_name in auto_load_skills:
|
|
245
|
-
result = skill_manager.load_skill(skill_name)
|
|
245
|
+
result = skill_manager.load_skill(skill_name, explicit=True)
|
|
246
246
|
if result.startswith("Failed") or result.startswith("Skill '"):
|
|
247
247
|
if event_bus:
|
|
248
248
|
event_bus.emit(SkillLoadFailedEvent(skill_name=skill_name, error_message=result))
|
|
@@ -268,7 +268,7 @@ class AgentPreparer:
|
|
|
268
268
|
if name in auto_load_skills:
|
|
269
269
|
# Already loaded above; no need to double-load but it's still sticky.
|
|
270
270
|
continue
|
|
271
|
-
result = skill_manager.load_skill(name)
|
|
271
|
+
result = skill_manager.load_skill(name, explicit=True)
|
|
272
272
|
if result.startswith("Failed") or result.startswith("Skill '"):
|
|
273
273
|
if event_bus:
|
|
274
274
|
event_bus.emit(SkillLoadFailedEvent(skill_name=name, error_message=result))
|
|
@@ -73,7 +73,11 @@ def open_or_create_session(
|
|
|
73
73
|
backend = get_history_backend()
|
|
74
74
|
if continue_conversation_id:
|
|
75
75
|
if backend.exists(continue_conversation_id):
|
|
76
|
-
|
|
76
|
+
session = backend.load(continue_conversation_id)
|
|
77
|
+
# Daemon-spawned sessions are created without a model.
|
|
78
|
+
if session.summary().model is None:
|
|
79
|
+
session.record("session_start", agent=agent_name, model=model, workspace=workspace)
|
|
80
|
+
return session
|
|
77
81
|
return backend.create(
|
|
78
82
|
agent_name=agent_name,
|
|
79
83
|
model=model,
|
|
@@ -92,8 +96,8 @@ def _current_turn_already_has_user_input(storage: Session) -> bool:
|
|
|
92
96
|
session_end after it; the post-hoc save_run_to_history on the error path
|
|
93
97
|
would otherwise record a second, identical one (a duplicate user bubble).
|
|
94
98
|
A genuinely repeated message opens a new turn *after* the prior session_end,
|
|
95
|
-
so this stays False for it. A final_result also ends a turn
|
|
96
|
-
|
|
99
|
+
so this stays False for it. A final_result also ends a turn copied without
|
|
100
|
+
its session_end (branches, older compacted sessions).
|
|
97
101
|
"""
|
|
98
102
|
events = list(storage.iter_events(types=("user_input", "session_end", "final_result")))
|
|
99
103
|
return bool(events) and events[-1].type == "user_input"
|
|
@@ -129,8 +133,9 @@ def record_user_input(
|
|
|
129
133
|
data["attachments"] = [
|
|
130
134
|
{"name": a.name, "type": a.content_type.value, "source_url": a.source_url} for a in uploads
|
|
131
135
|
]
|
|
132
|
-
if
|
|
133
|
-
|
|
136
|
+
channel = {k: v for k, v in (channel_metadata or {}).items() if k != "tsugite_token"}
|
|
137
|
+
if channel:
|
|
138
|
+
data["channel"] = channel
|
|
134
139
|
# Structured client context, recorded alongside the folded <client_context> XML
|
|
135
140
|
# in `text` so the UI reads it back without re-parsing the prompt string.
|
|
136
141
|
if client_context_items:
|
|
@@ -244,8 +249,8 @@ def save_run_to_history(
|
|
|
244
249
|
agent_already_recorded = last_model_response > last_session_start
|
|
245
250
|
|
|
246
251
|
if agent_already_recorded:
|
|
247
|
-
# A continued conversation
|
|
248
|
-
# turn starts at its own user_input.
|
|
252
|
+
# A continued conversation's session_start can sit at index 0 or come from
|
|
253
|
+
# a backfill. The current turn starts at its own user_input.
|
|
249
254
|
turn_start = max(last_session_start, last_user_input)
|
|
250
255
|
if last_session_end < turn_start:
|
|
251
256
|
record_session_end(storage, status=status, error_message=error_message)
|
|
@@ -264,19 +269,20 @@ def save_run_to_history(
|
|
|
264
269
|
tools_called=list(getattr(step, "tools_called", []) or []),
|
|
265
270
|
)
|
|
266
271
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
272
|
+
if status == "success":
|
|
273
|
+
state_delta = provider_state if provider_state else None
|
|
274
|
+
# Split as the agent loop records it: bare id in `model`, prefix in `provider`.
|
|
275
|
+
model_provider, _, model_id = model.partition(":")
|
|
276
|
+
storage.record(
|
|
277
|
+
"model_response",
|
|
278
|
+
provider=model_provider if model_id else None,
|
|
279
|
+
model=model_id or model,
|
|
280
|
+
raw_content=result or "",
|
|
281
|
+
usage={"total_tokens": token_count} if token_count else None,
|
|
282
|
+
cost=cost,
|
|
283
|
+
duration_ms=duration_ms,
|
|
284
|
+
state_delta=state_delta,
|
|
285
|
+
)
|
|
280
286
|
|
|
281
287
|
record_session_end(storage, status=status, error_message=error_message)
|
|
282
288
|
return storage.session_id
|
|
@@ -288,6 +294,7 @@ class ResumableSessionState:
|
|
|
288
294
|
|
|
289
295
|
session_id: str
|
|
290
296
|
compacted: bool = False
|
|
297
|
+
cumulative_cost: float = 0.0
|
|
291
298
|
|
|
292
299
|
|
|
293
300
|
def get_resumable_session_state(conversation_id: str) -> Optional[ResumableSessionState]:
|
|
@@ -324,6 +331,7 @@ def get_resumable_session_state(conversation_id: str) -> Optional[ResumableSessi
|
|
|
324
331
|
return ResumableSessionState(
|
|
325
332
|
session_id=session_id,
|
|
326
333
|
compacted=bool(state.get("compacted", False)),
|
|
334
|
+
cumulative_cost=state.get("cumulative_cost") or 0.0,
|
|
327
335
|
)
|
|
328
336
|
return None
|
|
329
337
|
except Exception:
|
|
@@ -10,7 +10,7 @@ from pathlib import Path # noqa: E402
|
|
|
10
10
|
from types import SimpleNamespace # noqa: E402
|
|
11
11
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional # noqa: E402
|
|
12
12
|
|
|
13
|
-
from tsugite.core.agent import TsugiteAgent # noqa: E402
|
|
13
|
+
from tsugite.core.agent import NO_ACTION_MESSAGE, AgentResult, TsugiteAgent # noqa: E402
|
|
14
14
|
from tsugite.core.executor_registry import get_executor_class # noqa: E402
|
|
15
15
|
from tsugite.core.proxy import _parse_pattern # noqa: E402
|
|
16
16
|
from tsugite.core.state import session_state_path # noqa: E402
|
|
@@ -395,11 +395,13 @@ async def _execute_agent_with_prompt(
|
|
|
395
395
|
path_context: Optional[Any] = None,
|
|
396
396
|
resume_session: Optional[str] = None,
|
|
397
397
|
resume_after_compaction: bool = False,
|
|
398
|
+
resume_cost_baseline: float = 0.0,
|
|
398
399
|
hook_vars: Optional[Dict[str, str]] = None,
|
|
399
400
|
continue_conversation_id: Optional[str] = None,
|
|
400
401
|
user_input_for_history: Optional[str] = None,
|
|
401
402
|
channel_metadata: Optional[Dict[str, Any]] = None,
|
|
402
403
|
usage_sink: Optional[Dict[str, Any]] = None,
|
|
404
|
+
requires_action: bool = False,
|
|
403
405
|
) -> str | AgentExecutionResult:
|
|
404
406
|
"""Execute agent with a prepared agent.
|
|
405
407
|
|
|
@@ -408,6 +410,9 @@ async def _execute_agent_with_prompt(
|
|
|
408
410
|
`usage_sink`, when given, receives this call's token and cost counters. It is
|
|
409
411
|
how a multi-step run collects accounting from steps that return plain
|
|
410
412
|
strings.
|
|
413
|
+
|
|
414
|
+
`requires_action` makes a run that ends without executing any code raise, so a
|
|
415
|
+
step whose result feeds later steps cannot pass on a prose answer.
|
|
411
416
|
"""
|
|
412
417
|
if exec_options is None:
|
|
413
418
|
exec_options = ExecutionOptions()
|
|
@@ -610,9 +615,11 @@ async def _execute_agent_with_prompt(
|
|
|
610
615
|
previous_messages=previous_messages,
|
|
611
616
|
resume_session=resume_session,
|
|
612
617
|
resume_after_compaction=resume_after_compaction,
|
|
618
|
+
resume_cost_baseline=resume_cost_baseline,
|
|
613
619
|
hook_vars=hook_vars,
|
|
614
620
|
storage=session_storage,
|
|
615
621
|
pre_llm_call=pre_llm_call_cb,
|
|
622
|
+
expects_action=requires_action or (channel_metadata or {}).get("source") == "scheduler",
|
|
616
623
|
)
|
|
617
624
|
# Set event_bus in context so tools can access it during execution
|
|
618
625
|
_setup_event_context(event_bus)
|
|
@@ -663,10 +670,18 @@ async def _execute_agent_with_prompt(
|
|
|
663
670
|
except Exception as e:
|
|
664
671
|
logger.debug("Failed to record usage: %s", e)
|
|
665
672
|
|
|
673
|
+
if requires_action and agent.ended_no_action:
|
|
674
|
+
steps = result.steps if isinstance(result, AgentResult) and result.steps else []
|
|
675
|
+
raise AgentExecutionError(
|
|
676
|
+
NO_ACTION_MESSAGE,
|
|
677
|
+
execution_steps=steps,
|
|
678
|
+
token_usage=agent.total_tokens,
|
|
679
|
+
cost=agent.reported_cost,
|
|
680
|
+
step_count=len(steps),
|
|
681
|
+
)
|
|
682
|
+
|
|
666
683
|
# Return appropriate format
|
|
667
684
|
if exec_options.return_token_usage:
|
|
668
|
-
from tsugite.core.agent import AgentResult
|
|
669
|
-
|
|
670
685
|
if isinstance(result, AgentResult):
|
|
671
686
|
step_count = len(result.steps) if result.steps else 0
|
|
672
687
|
steps_list = result.steps if result.steps else []
|
|
@@ -709,8 +724,6 @@ async def _execute_agent_with_prompt(
|
|
|
709
724
|
session_id=session_storage.session_id if session_storage else None,
|
|
710
725
|
)
|
|
711
726
|
else:
|
|
712
|
-
from tsugite.core.agent import AgentResult
|
|
713
|
-
|
|
714
727
|
if isinstance(result, AgentResult):
|
|
715
728
|
return str(result.output)
|
|
716
729
|
else:
|
|
@@ -867,7 +880,9 @@ class _RunSetup:
|
|
|
867
880
|
previous_messages: List[Dict] = field(default_factory=list)
|
|
868
881
|
resume_session: Optional[str] = None
|
|
869
882
|
resume_after_compaction: bool = False
|
|
883
|
+
resume_cost_baseline: float = 0.0
|
|
870
884
|
conversation_id: Optional[str] = None
|
|
885
|
+
requires_action: bool = False
|
|
871
886
|
|
|
872
887
|
|
|
873
888
|
def _prepare_step(agent: Any, prompt: str, context: Dict[str, Any], setup: "_RunSetup") -> "PreparedAgent":
|
|
@@ -951,6 +966,7 @@ async def _run_unit(
|
|
|
951
966
|
model_kwargs=model_kwargs,
|
|
952
967
|
injectable_vars=injectable_vars,
|
|
953
968
|
usage_sink=usage_sink,
|
|
969
|
+
requires_action=setup.requires_action,
|
|
954
970
|
)
|
|
955
971
|
|
|
956
972
|
try:
|
|
@@ -958,6 +974,7 @@ async def _run_unit(
|
|
|
958
974
|
previous_messages=setup.previous_messages,
|
|
959
975
|
resume_session=setup.resume_session,
|
|
960
976
|
resume_after_compaction=setup.resume_after_compaction,
|
|
977
|
+
resume_cost_baseline=setup.resume_cost_baseline,
|
|
961
978
|
**execute_kwargs,
|
|
962
979
|
)
|
|
963
980
|
except (RuntimeError, AgentExecutionError) as e:
|
|
@@ -1151,6 +1168,7 @@ async def run_agent_async(
|
|
|
1151
1168
|
previous_messages = []
|
|
1152
1169
|
resume_session = None
|
|
1153
1170
|
resume_after_compaction = False
|
|
1171
|
+
resume_cost_baseline = 0.0
|
|
1154
1172
|
if continue_conversation_id:
|
|
1155
1173
|
from tsugite.agent_runner.history_integration import (
|
|
1156
1174
|
get_resumable_session_state,
|
|
@@ -1162,6 +1180,7 @@ async def run_agent_async(
|
|
|
1162
1180
|
if session_info:
|
|
1163
1181
|
resume_session = session_info.session_id
|
|
1164
1182
|
resume_after_compaction = session_info.compacted
|
|
1183
|
+
resume_cost_baseline = session_info.cumulative_cost
|
|
1165
1184
|
logger.info(
|
|
1166
1185
|
"Resuming provider session %s (compacted=%s)",
|
|
1167
1186
|
resume_session,
|
|
@@ -1210,6 +1229,7 @@ async def run_agent_async(
|
|
|
1210
1229
|
previous_messages=previous_messages,
|
|
1211
1230
|
resume_session=resume_session,
|
|
1212
1231
|
resume_after_compaction=resume_after_compaction,
|
|
1232
|
+
resume_cost_baseline=resume_cost_baseline,
|
|
1213
1233
|
conversation_id=continue_conversation_id,
|
|
1214
1234
|
)
|
|
1215
1235
|
|
|
@@ -251,6 +251,8 @@ async def _execute_step_with_retries(
|
|
|
251
251
|
user_input_for_history=f"[Step {i}/{total_steps}: {step.name}]",
|
|
252
252
|
resume_session=None,
|
|
253
253
|
resume_after_compaction=False,
|
|
254
|
+
resume_cost_baseline=0.0,
|
|
255
|
+
requires_action=bool(step.assign_var),
|
|
254
256
|
)
|
|
255
257
|
errors = []
|
|
256
258
|
step_start_time = time.time()
|
|
@@ -17,6 +17,12 @@ from tsugite.utils import has_glob_chars
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def _warn_skipped(subject: str, detail: str) -> None:
|
|
21
|
+
from tsugite.events.helpers import emit_warning_event
|
|
22
|
+
|
|
23
|
+
emit_warning_event(f"Agent attachment skipped: {subject} ({detail})")
|
|
24
|
+
|
|
25
|
+
|
|
20
26
|
def _render_path(template: str, template_vars: Dict[str, Any]) -> Optional[str]:
|
|
21
27
|
"""Render a Jinja path template; return None on render failure."""
|
|
22
28
|
from tsugite.renderer import AgentRenderer
|
|
@@ -24,7 +30,7 @@ def _render_path(template: str, template_vars: Dict[str, Any]) -> Optional[str]:
|
|
|
24
30
|
try:
|
|
25
31
|
return AgentRenderer().render_string(template, template_vars)
|
|
26
32
|
except Exception as e:
|
|
27
|
-
|
|
33
|
+
_warn_skipped(template, f"path template failed to render: {e}")
|
|
28
34
|
return None
|
|
29
35
|
|
|
30
36
|
|
|
@@ -124,12 +130,10 @@ def _resolve_string_item(
|
|
|
124
130
|
return
|
|
125
131
|
resolved = _resolve_path(rendered, workspace_path)
|
|
126
132
|
if not resolved.exists():
|
|
127
|
-
|
|
133
|
+
_warn_skipped(template, f"not found: {resolved}")
|
|
128
134
|
return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
attachments.append(att)
|
|
132
|
-
logger.debug("Loaded agent attachment: %s", resolved)
|
|
135
|
+
attachments.append(file_handler.fetch(str(resolved)))
|
|
136
|
+
logger.debug("Loaded agent attachment: %s", resolved)
|
|
133
137
|
|
|
134
138
|
|
|
135
139
|
def _resolve_spec(
|
|
@@ -161,13 +165,12 @@ def _resolve_spec(
|
|
|
161
165
|
if not spec.attach and spec.assign:
|
|
162
166
|
try:
|
|
163
167
|
content = path.read_text(encoding="utf-8")
|
|
164
|
-
except (OSError, UnicodeDecodeError):
|
|
168
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
169
|
+
_warn_skipped(str(path), f"unreadable: {e}")
|
|
165
170
|
continue
|
|
166
171
|
bindings[spec.assign].append({"path": str(path), "content": content})
|
|
167
172
|
continue
|
|
168
173
|
att = file_handler.fetch(str(path))
|
|
169
|
-
if not att:
|
|
170
|
-
continue
|
|
171
174
|
if spec.attach:
|
|
172
175
|
attachments.append(att)
|
|
173
176
|
if spec.assign and att.content_type == AttachmentContentType.TEXT:
|
|
@@ -178,14 +181,11 @@ def _resolve_spec(
|
|
|
178
181
|
if not resolved.exists():
|
|
179
182
|
if spec.assign:
|
|
180
183
|
bindings[spec.assign] = None
|
|
181
|
-
|
|
184
|
+
elif not spec.optional:
|
|
185
|
+
_warn_skipped(spec.path, f"not found: {resolved}")
|
|
182
186
|
return
|
|
183
187
|
|
|
184
188
|
att = file_handler.fetch(str(resolved))
|
|
185
|
-
if not att:
|
|
186
|
-
if spec.assign:
|
|
187
|
-
bindings[spec.assign] = None
|
|
188
|
-
return
|
|
189
189
|
if spec.attach:
|
|
190
190
|
attachments.append(att)
|
|
191
191
|
if spec.assign:
|
|
@@ -6,9 +6,17 @@ extends: none
|
|
|
6
6
|
# volatile memory last, so a change to today's memory / now.md doesn't invalidate
|
|
7
7
|
# the identity files' cache. A flat list would be one block; these are three.
|
|
8
8
|
attachments:
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- - {path: PERSONA.md, optional: true}
|
|
10
|
+
- {path: SOUL.md, optional: true}
|
|
11
|
+
- {path: USER.md, optional: true}
|
|
12
|
+
- {path: IDENTITY.md, optional: true}
|
|
13
|
+
- {path: AGENTS.md, optional: true}
|
|
14
|
+
- - {path: MEMORY.md, optional: true}
|
|
15
|
+
- {path: "memory/{{ yesterday() }}.md", optional: true}
|
|
16
|
+
- {path: "memory/{{ date_format(datetime.now().replace(day=1) - timedelta(days=1), '%Y-%m') }}.md", optional: true}
|
|
17
|
+
- - {path: "memory/{{ today() }}.md", optional: true}
|
|
18
|
+
- {path: notes/context/now.md, optional: true}
|
|
19
|
+
- {path: NOW.md, optional: true}
|
|
12
20
|
max_turns: 10
|
|
13
21
|
tools:
|
|
14
22
|
- spawn_agent
|
|
@@ -178,6 +178,8 @@ print(schedules)
|
|
|
178
178
|
|
|
179
179
|
Each schedule includes: `id`, `execution_type`, `schedule_type`, `enabled`, `next_run`, `last_run`, `last_status`, `last_error`.
|
|
180
180
|
|
|
181
|
+
`last_status` is `success`, `error`, `skipped`, or `no_action`.
|
|
182
|
+
|
|
181
183
|
## Managing Schedules
|
|
182
184
|
|
|
183
185
|
```python-exec
|