tsugite-cli 0.22.0__tar.gz → 0.22.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.22.0 → tsugite_cli-0.22.1}/PKG-INFO +1 -1
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/pyproject.toml +1 -1
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_preparation.py +3 -2
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/history_integration.py +7 -1
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/base.py +2 -4
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/default.md +2 -2
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/run.py +17 -2
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/proxy.py +8 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/md_agents.py +2 -2
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/agents.py +26 -12
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/jobs.py +5 -3
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/.gitignore +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/AGENTS.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/CONTRIBUTING.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/LICENSE +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/README.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_inheritance.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/exec_directives.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/exec_runner.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/helpers.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/metrics.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/models.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/runner.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/steps.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_runner/validation.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/agent_utils.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/approval.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/agent_config.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/auto_context.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/delegation.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/file.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/inline.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/storage.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/attachments/url.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/.gitkeep +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/code_searcher.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/file_searcher.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/job_verifier.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/job_worker.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_agents/onboard.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_context.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/.gitkeep +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/codebase-exploration/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/external-agent-bridge/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/python-math/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/response-patterns/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/scheduling/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/skill-authoring/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/tsugite-agent-basics/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/tsugite-jinja-reference/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/tsugite-skill-basics/SKILL.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cache.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cancellation.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/agents.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/attachments.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/cache.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/chat.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/config.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/daemon.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/exec.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/helpers.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/history.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/init.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/models.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/plugins.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/render.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/secrets.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/skills.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/tools.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/usage.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/validate.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/cli/workspace.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/config.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/console.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/constants.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/context.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/context_block.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/agent.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/content_blocks.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/executor.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/executor_registry.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/memory.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/record_store.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/sandbox.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/state.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/subprocess_executor.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/core/tools.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/events/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/events/base.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/events/bus.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/events/events.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/events/helpers.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/exceptions.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/base.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/models.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/reconstruction.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/registry.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/sqlite_backend.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/sqlite_conn.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/sqlite_schema.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/history/ui_events.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/hooks.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/interaction.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/models.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/options.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/permissions.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/plugins.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/prompt_xml.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/anthropic.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/base.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/model_cache.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/model_registry.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/ollama.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/openai_compat.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/providers/openrouter.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/py.typed +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/renderer.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/schemas/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/schemas/agent.schema.json +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/backend.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/env.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/exec.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/file.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/masking.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/redaction.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/registry.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/secrets/sqlite.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/shell_tool_config.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/skill_discovery.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/AGENTS.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/IDENTITY.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/MEMORY.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/USER.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/personas/casual-technical.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/personas/marvin.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/templates/personas/minimal.md +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/artifacts.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/daemon_control.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/fs.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/history.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/http.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/interactive.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/notify.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/schedule.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/secrets.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/sessions.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/shell.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/shell_tools.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/skills.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tools/time.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/tsugite.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/base.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/chat.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/dispatch.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/helpers.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/jsonl.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/plain.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/repl_chat.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/repl_commands.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/repl_completer.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui/repl_handler.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui_context.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/ui_surfaces.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/usage/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/usage/quota.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/usage/store.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/user_agent.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/utils.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/workspace/__init__.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/workspace/models.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/workspace/session.py +0 -0
- {tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/workspace/templates.py +0 -0
|
@@ -119,8 +119,9 @@ class AgentPreparer:
|
|
|
119
119
|
placeholders: Dict[str, str] = {}
|
|
120
120
|
modified = content
|
|
121
121
|
for d in directives:
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
for var in (d.assign_var, getattr(d, "stdout_assign", None)):
|
|
123
|
+
if var:
|
|
124
|
+
placeholders[var] = f"[{kind} directive: {d.name}(...) - not executed in render mode]"
|
|
124
125
|
if rewrite_to is not None:
|
|
125
126
|
modified = modified.replace(d.raw_match, rewrite_to(d))
|
|
126
127
|
return modified, placeholders
|
|
@@ -231,11 +231,14 @@ def save_run_to_history(
|
|
|
231
231
|
# agent ran to completion against this storage.
|
|
232
232
|
existing = list(storage.iter_events())
|
|
233
233
|
last_session_start = -1
|
|
234
|
+
last_user_input = -1
|
|
234
235
|
last_model_response = -1
|
|
235
236
|
last_session_end = -1
|
|
236
237
|
for i, e in enumerate(existing):
|
|
237
238
|
if e.type == "session_start":
|
|
238
239
|
last_session_start = i
|
|
240
|
+
elif e.type == "user_input":
|
|
241
|
+
last_user_input = i
|
|
239
242
|
elif e.type == "model_response":
|
|
240
243
|
last_model_response = i
|
|
241
244
|
elif e.type == "session_end":
|
|
@@ -244,7 +247,10 @@ def save_run_to_history(
|
|
|
244
247
|
agent_already_recorded = last_model_response > last_session_start
|
|
245
248
|
|
|
246
249
|
if agent_already_recorded:
|
|
247
|
-
|
|
250
|
+
# A continued conversation pins session_start at index 0, so the current
|
|
251
|
+
# turn starts at its own user_input.
|
|
252
|
+
turn_start = max(last_session_start, last_user_input)
|
|
253
|
+
if last_session_end < turn_start:
|
|
248
254
|
record_session_end(storage, status=status, error_message=error_message)
|
|
249
255
|
return storage.session_id
|
|
250
256
|
|
|
@@ -68,8 +68,7 @@ class Attachment:
|
|
|
68
68
|
|
|
69
69
|
``key`` is the stable id used for dedupe and the UI chip; ``label`` is the
|
|
70
70
|
human name the model and UI show; ``value`` is the text. Fills the text
|
|
71
|
-
content-type so context producers don't repeat it.
|
|
72
|
-
old ``ContextItem`` carried, now just an Attachment.
|
|
71
|
+
content-type so context producers don't repeat it.
|
|
73
72
|
"""
|
|
74
73
|
return cls(
|
|
75
74
|
name=label,
|
|
@@ -88,8 +87,7 @@ class Attachment:
|
|
|
88
87
|
|
|
89
88
|
@property
|
|
90
89
|
def value(self) -> str:
|
|
91
|
-
"""The text content (an alias of ``content`` when it is text, else "").
|
|
92
|
-
Lets context code read ``.value`` the way the old ``ContextItem`` did."""
|
|
90
|
+
"""The text content (an alias of ``content`` when it is text, else "")."""
|
|
93
91
|
return self.content if isinstance(self.content, str) else ""
|
|
94
92
|
|
|
95
93
|
def to_metadata(self) -> dict:
|
|
@@ -157,12 +157,12 @@ You are managing a shared channel. When a user asks for something that would ben
|
|
|
157
157
|
- **Inline** - single edit, one-shot read, small calculation. Don't spawn anything.
|
|
158
158
|
- **`spawn_job`** - background work: long-running tasks, parallel work, or delegating to a different agent/model. Fire-and-forget by default; add `acceptance_criteria=[...]` when "done" has a checkable shape (tests pass, copy satisfies criteria, refactor preserves behavior) and the verification loop becomes the value.
|
|
159
159
|
|
|
160
|
-
**Acceptance criteria**: pass each criterion as a plain string. The verifier reads
|
|
160
|
+
**Acceptance criteria**: pass each criterion as a plain string. A criterion starting with `cmd:<command>` (passes on exit 0), `exit_code:<command>:<n>` (passes on exit code `n`, default 0) or `file_exists:<path>` is checked in the Job's working directory with no verifier model. Write one whenever the check is mechanical. The verifier reads the rest verbatim and returns per-criterion pass/fail in `result.ac_results`:
|
|
161
161
|
```python-exec
|
|
162
162
|
spawn_job(
|
|
163
163
|
prompt="Refactor the agent loop to handle nested tool calls",
|
|
164
164
|
acceptance_criteria=[
|
|
165
|
-
"
|
|
165
|
+
"cmd:uv run pytest -q",
|
|
166
166
|
"Adds at least 2 new tests for nested calls",
|
|
167
167
|
"No new dependencies",
|
|
168
168
|
],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""CLI run command - execute agents."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from types import SimpleNamespace
|
|
4
5
|
from typing import Any, Dict, List, Optional, Tuple
|
|
5
6
|
|
|
6
7
|
import typer
|
|
@@ -137,11 +138,18 @@ def _execute_agent_with_ui(
|
|
|
137
138
|
"""Execute agent with appropriate UI mode.
|
|
138
139
|
|
|
139
140
|
Dispatch order:
|
|
140
|
-
1. --
|
|
141
|
-
2.
|
|
141
|
+
1. --log-json -> JSONL event stream on stdout
|
|
142
|
+
2. --headless / --final-only -> custom_agent_ui with silent progress
|
|
143
|
+
3. fall-through default -> plain logger (covers no flags, --plain, piped/NO_COLOR)
|
|
142
144
|
"""
|
|
143
145
|
from tsugite.ui import create_plain_logger, custom_agent_ui
|
|
144
146
|
|
|
147
|
+
if ui_opts.log_json:
|
|
148
|
+
from tsugite.ui.jsonl import JSONLUIHandler
|
|
149
|
+
|
|
150
|
+
executor_kwargs["custom_logger"] = SimpleNamespace(ui_handler=JSONLUIHandler())
|
|
151
|
+
return executor(**executor_kwargs)
|
|
152
|
+
|
|
145
153
|
if ui_opts.headless or ui_opts.final_only:
|
|
146
154
|
stderr_console = get_error_console(True, console)
|
|
147
155
|
show_progress_items = ui_opts.verbose and not ui_opts.final_only
|
|
@@ -188,6 +196,9 @@ def _display_result(result_str: str, ui_opts: UIOptions, stderr_console: Console
|
|
|
188
196
|
|
|
189
197
|
from tsugite.console import get_stdout_console
|
|
190
198
|
|
|
199
|
+
if ui_opts.log_json:
|
|
200
|
+
return
|
|
201
|
+
|
|
191
202
|
if not (ui_opts.headless or ui_opts.final_only):
|
|
192
203
|
stderr_console.print()
|
|
193
204
|
stderr_console.rule("[bold green]Agent Execution Complete[/bold green]")
|
|
@@ -358,6 +369,10 @@ def run(
|
|
|
358
369
|
|
|
359
370
|
if history_opts.storage_dir:
|
|
360
371
|
history_opts.storage_dir.mkdir(parents=True, exist_ok=True)
|
|
372
|
+
from tsugite.history import set_history_backend
|
|
373
|
+
from tsugite.history.sqlite_backend import SqliteHistoryBackend
|
|
374
|
+
|
|
375
|
+
set_history_backend(SqliteHistoryBackend(history_opts.storage_dir / "history.db"))
|
|
361
376
|
|
|
362
377
|
if ui_opts.no_color:
|
|
363
378
|
console.no_color = True
|
|
@@ -8,6 +8,7 @@ import asyncio
|
|
|
8
8
|
import fnmatch
|
|
9
9
|
import ipaddress
|
|
10
10
|
import logging
|
|
11
|
+
import socket
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Optional
|
|
13
14
|
|
|
@@ -46,6 +47,13 @@ def _is_ip_address(host: str) -> bool:
|
|
|
46
47
|
ipaddress.ip_address(host)
|
|
47
48
|
return True
|
|
48
49
|
except ValueError:
|
|
50
|
+
pass
|
|
51
|
+
# inet_aton accepts the integer, octal and short IPv4 forms that
|
|
52
|
+
# ip_address rejects and getaddrinfo resolves, and rejects hostnames.
|
|
53
|
+
try:
|
|
54
|
+
socket.inet_aton(host)
|
|
55
|
+
return True
|
|
56
|
+
except OSError:
|
|
49
57
|
return False
|
|
50
58
|
|
|
51
59
|
|
|
@@ -305,7 +305,7 @@ def extract_tool_directives(content: str) -> List[ToolDirective]:
|
|
|
305
305
|
{'url': 'http://example.com'}
|
|
306
306
|
"""
|
|
307
307
|
directives = []
|
|
308
|
-
pattern = r"<!--\s*tsu:tool\s+(
|
|
308
|
+
pattern = r"<!--\s*tsu:tool\s+(.+?)\s*-->"
|
|
309
309
|
|
|
310
310
|
for match in re.finditer(pattern, content):
|
|
311
311
|
raw_args = match.group(1).strip()
|
|
@@ -478,7 +478,7 @@ def extract_step_directives(content: str, include_preamble: bool = True) -> tupl
|
|
|
478
478
|
True
|
|
479
479
|
"""
|
|
480
480
|
steps = []
|
|
481
|
-
pattern = r"<!--\s*tsu:step\s+(
|
|
481
|
+
pattern = r"<!--\s*tsu:step\s+(.+?)\s*-->"
|
|
482
482
|
matches = list(re.finditer(pattern, content))
|
|
483
483
|
|
|
484
484
|
# Extract preamble (content before first step)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Agent orchestration tools for spawning and managing sub-agents."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
import signal
|
|
3
5
|
from pathlib import Path
|
|
4
6
|
from typing import Any, Dict, List, Optional
|
|
5
7
|
|
|
@@ -31,6 +33,13 @@ def _build_subagent_cmd(agent_file: Path, model_override: Optional[str], sandbox
|
|
|
31
33
|
return cmd
|
|
32
34
|
|
|
33
35
|
|
|
36
|
+
def _kill_process_tree(proc) -> None:
|
|
37
|
+
try:
|
|
38
|
+
os.killpg(proc.pid, signal.SIGKILL)
|
|
39
|
+
except ProcessLookupError:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
34
43
|
def resolve_agent_path(agent_path: str) -> Optional[Path]:
|
|
35
44
|
"""Resolve an agent reference (path or name) to a file. Returns None if missing.
|
|
36
45
|
|
|
@@ -187,7 +196,6 @@ def spawn_agent(
|
|
|
187
196
|
emit_info_event(f"🚀 Spawning subagent: [cyan]{agent_name}[/cyan]...")
|
|
188
197
|
|
|
189
198
|
try:
|
|
190
|
-
# Spawn subprocess with line buffering
|
|
191
199
|
proc = subprocess.Popen(
|
|
192
200
|
cmd,
|
|
193
201
|
stdin=subprocess.PIPE,
|
|
@@ -196,18 +204,16 @@ def spawn_agent(
|
|
|
196
204
|
text=True,
|
|
197
205
|
bufsize=1, # Line buffered for real-time output
|
|
198
206
|
cwd=str(_effective_cwd()),
|
|
207
|
+
start_new_session=True,
|
|
199
208
|
)
|
|
200
209
|
|
|
201
|
-
# Write context to stdin then close it
|
|
202
210
|
proc.stdin.write(context_json)
|
|
203
211
|
proc.stdin.close()
|
|
204
212
|
|
|
205
|
-
# Queue for passing lines from reader thread to main thread
|
|
206
213
|
line_queue = queue.Queue()
|
|
207
214
|
reader_exception = None
|
|
208
215
|
|
|
209
216
|
def read_stdout():
|
|
210
|
-
"""Read stdout lines in separate thread and put in queue."""
|
|
211
217
|
nonlocal reader_exception
|
|
212
218
|
try:
|
|
213
219
|
for line in proc.stdout:
|
|
@@ -217,11 +223,19 @@ def spawn_agent(
|
|
|
217
223
|
reader_exception = e
|
|
218
224
|
line_queue.put(None)
|
|
219
225
|
|
|
220
|
-
# Start reader thread
|
|
221
226
|
reader_thread = threading.Thread(target=read_stdout, daemon=True)
|
|
222
227
|
reader_thread.start()
|
|
223
228
|
|
|
224
|
-
#
|
|
229
|
+
# A child that fills the stderr pipe blocks on write(2).
|
|
230
|
+
stderr_text = ""
|
|
231
|
+
|
|
232
|
+
def read_stderr():
|
|
233
|
+
nonlocal stderr_text
|
|
234
|
+
stderr_text = proc.stderr.read()
|
|
235
|
+
|
|
236
|
+
stderr_thread = threading.Thread(target=read_stderr, daemon=True)
|
|
237
|
+
stderr_thread.start()
|
|
238
|
+
|
|
225
239
|
final_result = None
|
|
226
240
|
errors = []
|
|
227
241
|
# The child signals EOF by closing stdout, so one that hangs without
|
|
@@ -231,7 +245,7 @@ def spawn_agent(
|
|
|
231
245
|
|
|
232
246
|
while True:
|
|
233
247
|
if time.monotonic() >= deadline:
|
|
234
|
-
proc
|
|
248
|
+
_kill_process_tree(proc)
|
|
235
249
|
raise RuntimeError(f"Subagent timed out after {timeout}s")
|
|
236
250
|
# Try to get line from queue with timeout for periodic updates
|
|
237
251
|
try:
|
|
@@ -280,17 +294,17 @@ def spawn_agent(
|
|
|
280
294
|
|
|
281
295
|
# Wait for completion
|
|
282
296
|
try:
|
|
283
|
-
return_code = proc.wait(timeout=
|
|
297
|
+
return_code = proc.wait(timeout=max(0.0, deadline - time.monotonic()))
|
|
284
298
|
except subprocess.TimeoutExpired:
|
|
285
|
-
proc
|
|
299
|
+
_kill_process_tree(proc)
|
|
286
300
|
raise RuntimeError(f"Subagent timed out after {timeout}s")
|
|
287
301
|
|
|
288
302
|
# Check for failures
|
|
289
303
|
if return_code != 0:
|
|
290
|
-
|
|
304
|
+
stderr_thread.join(timeout=1.0)
|
|
291
305
|
error_msg = f"Subagent failed with exit code {return_code}"
|
|
292
|
-
if
|
|
293
|
-
error_msg += f": {
|
|
306
|
+
if stderr_text:
|
|
307
|
+
error_msg += f": {stderr_text}"
|
|
294
308
|
if errors:
|
|
295
309
|
error_msg += f"\nErrors: {[e['error'] for e in errors]}"
|
|
296
310
|
raise RuntimeError(error_msg)
|
|
@@ -57,9 +57,11 @@ def spawn_job(
|
|
|
57
57
|
|
|
58
58
|
Args:
|
|
59
59
|
prompt: Task instruction for the spawned Job.
|
|
60
|
-
acceptance_criteria: List of criterion strings
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
acceptance_criteria: List of criterion strings. A criterion starting
|
|
61
|
+
with cmd:<command>, exit_code:<command>:<n> or file_exists:<path>
|
|
62
|
+
is checked by exit status in the Job's working directory and the
|
|
63
|
+
verifier grades the rest. Empty list short-circuits verification
|
|
64
|
+
(Job goes straight to done).
|
|
63
65
|
repo: Repo path (workspace-relative or absolute). The worker runs in a
|
|
64
66
|
fresh git worktree provisioned under `<repo>/.tsugite-jobs/<job_id>`,
|
|
65
67
|
isolated from the parent checkout; it's pruned on done/cancelled and
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/codebase-exploration/SKILL.md
RENAMED
|
File without changes
|
{tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/external-agent-bridge/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/tsugite-agent-basics/SKILL.md
RENAMED
|
File without changes
|
{tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/tsugite-jinja-reference/SKILL.md
RENAMED
|
File without changes
|
{tsugite_cli-0.22.0 → tsugite_cli-0.22.1}/tsugite/builtin_skills/tsugite-skill-basics/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|