tsugite-cli 0.9.3__tar.gz → 0.9.4__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.
Files changed (282) hide show
  1. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/PKG-INFO +1 -1
  2. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/pyproject.toml +1 -1
  3. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/adapters/http.py +55 -26
  4. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/session_store.py +7 -1
  5. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/css/styles.css +2 -1
  6. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/index.html +66 -64
  7. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/conversations.js +31 -3
  8. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/workspace.js +32 -15
  9. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/uv.lock +1 -1
  10. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/.github/copilot-instructions.md +0 -0
  11. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/.github/workflows/ci.yml +0 -0
  12. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/.github/workflows/docker-publish.yml +0 -0
  13. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/.github/workflows/pypi-publish.yml +0 -0
  14. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/.gitignore +0 -0
  15. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/AGENTS.md +0 -0
  16. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/CLAUDE.md +0 -0
  17. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/LICENSE +0 -0
  18. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/README.md +0 -0
  19. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/scripts/regenerate_schema.py +0 -0
  20. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/README.md +0 -0
  21. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/__init__.py +0 -0
  22. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/conftest.py +0 -0
  23. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/__init__.py +0 -0
  24. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_agent.py +0 -0
  25. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_agent_ui_events.py +0 -0
  26. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_content_blocks.py +0 -0
  27. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_executor.py +0 -0
  28. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_memory.py +0 -0
  29. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_proxy.py +0 -0
  30. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_sandbox.py +0 -0
  31. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_subprocess_executor.py +0 -0
  32. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/core/test_tools.py +0 -0
  33. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/daemon/__init__.py +0 -0
  34. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/daemon/test_http_adapter.py +0 -0
  35. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/events/test_event_consolidation.py +0 -0
  36. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/smoke_test.sh +0 -0
  37. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agent_file_hot_loading.py +0 -0
  38. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agent_inheritance.py +0 -0
  39. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agent_parser.py +0 -0
  40. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agent_sessions.py +0 -0
  41. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agent_skills.py +0 -0
  42. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agent_utils.py +0 -0
  43. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_agents_tool.py +0 -0
  44. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_attachment_deduplication.py +0 -0
  45. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_attachments.py +0 -0
  46. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_auto_context_handler.py +0 -0
  47. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_auto_discovery.py +0 -0
  48. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_background_task_status.py +0 -0
  49. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_background_tasks.py +0 -0
  50. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_builtin_agent_paths.py +0 -0
  51. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_builtin_agents.py +0 -0
  52. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_cache_control.py +0 -0
  53. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_chat_cli.py +0 -0
  54. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_chat_error_handling.py +0 -0
  55. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_claude_code_attachments.py +0 -0
  56. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_claude_code_provider.py +0 -0
  57. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_cli.py +0 -0
  58. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_cli_arguments.py +0 -0
  59. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_cli_rendering.py +0 -0
  60. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_cli_subcommands.py +0 -0
  61. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_config.py +0 -0
  62. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_continuation.py +0 -0
  63. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_custom_shell_tools.py +0 -0
  64. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_custom_ui.py +0 -0
  65. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_compaction_scheduler.py +0 -0
  66. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_config.py +0 -0
  67. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_history_persistence.py +0 -0
  68. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_memory.py +0 -0
  69. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_push.py +0 -0
  70. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_scheduler.py +0 -0
  71. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_session_isolation.py +0 -0
  72. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_daemon_unified_sessions.py +0 -0
  73. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_discord_progress.py +0 -0
  74. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_error_display.py +0 -0
  75. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_file_references.py +0 -0
  76. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_file_tools.py +0 -0
  77. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_history.py +0 -0
  78. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_history_integration.py +0 -0
  79. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_history_models.py +0 -0
  80. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_history_performance.py +0 -0
  81. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_history_tools.py +0 -0
  82. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_hooks.py +0 -0
  83. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_http_tools.py +0 -0
  84. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_interaction_backends.py +0 -0
  85. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_interactive_context.py +0 -0
  86. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_interactive_tool.py +0 -0
  87. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_jsonl_ui.py +0 -0
  88. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_kvstore.py +0 -0
  89. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_list_agents_tool.py +0 -0
  90. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_mcp_client.py +0 -0
  91. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_mcp_server.py +0 -0
  92. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_models.py +0 -0
  93. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_multi_agent.py +0 -0
  94. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_multistep_agents.py +0 -0
  95. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_orchestrator_heartbeat.py +0 -0
  96. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_plugins.py +0 -0
  97. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_reasoning_models.py +0 -0
  98. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_renderer.py +0 -0
  99. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_rendering_scenarios.py +0 -0
  100. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_repl_commands.py +0 -0
  101. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_repl_completer.py +0 -0
  102. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_repl_handler.py +0 -0
  103. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_retry_system.py +0 -0
  104. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_run_if.py +0 -0
  105. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_schedule_model_override.py +0 -0
  106. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_scheduler_history_injection.py +0 -0
  107. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_schema.py +0 -0
  108. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_security_phase1.py +0 -0
  109. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_send_message.py +0 -0
  110. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_session_orchestrator_tools.py +0 -0
  111. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_skill_discovery.py +0 -0
  112. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_skill_tools.py +0 -0
  113. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_stdin.py +0 -0
  114. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_subagent_subprocess.py +0 -0
  115. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_tmux_tools.py +0 -0
  116. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_tool_directives.py +0 -0
  117. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_tool_registry.py +0 -0
  118. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_workspace_auto_continue.py +0 -0
  119. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_workspace_cwd.py +0 -0
  120. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tests/test_workspace_discovery.py +0 -0
  121. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/__init__.py +0 -0
  122. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_inheritance.py +0 -0
  123. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_preparation.py +0 -0
  124. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/__init__.py +0 -0
  125. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/helpers.py +0 -0
  126. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/history_integration.py +0 -0
  127. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/metrics.py +0 -0
  128. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/models.py +0 -0
  129. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/runner.py +0 -0
  130. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_runner/validation.py +0 -0
  131. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/agent_utils.py +0 -0
  132. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/__init__.py +0 -0
  133. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/auto_context.py +0 -0
  134. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/base.py +0 -0
  135. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/file.py +0 -0
  136. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/inline.py +0 -0
  137. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/storage.py +0 -0
  138. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/url.py +0 -0
  139. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/attachments/youtube.py +0 -0
  140. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_agents/.gitkeep +0 -0
  141. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_agents/code_searcher.md +0 -0
  142. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_agents/default.md +0 -0
  143. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_agents/file_searcher.md +0 -0
  144. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_agents/onboard.md +0 -0
  145. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/.gitkeep +0 -0
  146. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/codebase_exploration.md +0 -0
  147. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/python_math.md +0 -0
  148. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/response_patterns.md +0 -0
  149. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/scheduling.md +0 -0
  150. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/skill_authoring.md +0 -0
  151. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/tsugite_agent_basics.md +0 -0
  152. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/tsugite_jinja_reference.md +0 -0
  153. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/builtin_skills/tsugite_skill_basics.md +0 -0
  154. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cache.py +0 -0
  155. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/__init__.py +0 -0
  156. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/agents.py +0 -0
  157. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/attachments.py +0 -0
  158. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/cache.py +0 -0
  159. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/chat.py +0 -0
  160. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/config.py +0 -0
  161. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/daemon.py +0 -0
  162. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/helpers.py +0 -0
  163. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/history.py +0 -0
  164. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/init.py +0 -0
  165. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/mcp.py +0 -0
  166. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/plugins.py +0 -0
  167. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/render.py +0 -0
  168. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/run.py +0 -0
  169. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/serve.py +0 -0
  170. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/tools.py +0 -0
  171. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/validate.py +0 -0
  172. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/cli/workspace.py +0 -0
  173. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/config.py +0 -0
  174. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/console.py +0 -0
  175. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/constants.py +0 -0
  176. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/__init__.py +0 -0
  177. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/agent.py +0 -0
  178. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/claude_code.py +0 -0
  179. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/content_blocks.py +0 -0
  180. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/executor.py +0 -0
  181. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/memory.py +0 -0
  182. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/proxy.py +0 -0
  183. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/sandbox.py +0 -0
  184. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/subprocess_executor.py +0 -0
  185. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/core/tools.py +0 -0
  186. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/__init__.py +0 -0
  187. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/adapters/__init__.py +0 -0
  188. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/adapters/base.py +0 -0
  189. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/adapters/discord.py +0 -0
  190. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/adapters/scheduler_adapter.py +0 -0
  191. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/commands.py +0 -0
  192. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/compaction_scheduler.py +0 -0
  193. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/config.py +0 -0
  194. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/gateway.py +0 -0
  195. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/memory.py +0 -0
  196. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/push.py +0 -0
  197. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/scheduler.py +0 -0
  198. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/session_runner.py +0 -0
  199. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/css/responsive.css +0 -0
  200. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/css/theme.css +0 -0
  201. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/icons/icon-192.png +0 -0
  202. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/icons/icon-512-maskable.png +0 -0
  203. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/icons/icon-512.png +0 -0
  204. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/icons/screenshot-narrow.png +0 -0
  205. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/icons/screenshot-wide.png +0 -0
  206. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/api.js +0 -0
  207. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/app.js +0 -0
  208. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/utils.js +0 -0
  209. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/dashboard.js +0 -0
  210. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/file-editor.js +0 -0
  211. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/kvstore.js +0 -0
  212. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/schedules.js +0 -0
  213. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/js/views/webhooks.js +0 -0
  214. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/manifest.json +0 -0
  215. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/web/sw.js +0 -0
  216. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/daemon/webhook_store.py +0 -0
  217. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/events/__init__.py +0 -0
  218. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/events/base.py +0 -0
  219. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/events/bus.py +0 -0
  220. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/events/events.py +0 -0
  221. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/events/helpers.py +0 -0
  222. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/exceptions.py +0 -0
  223. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/history/__init__.py +0 -0
  224. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/history/models.py +0 -0
  225. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/history/reconstruction.py +0 -0
  226. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/history/storage.py +0 -0
  227. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/hooks.py +0 -0
  228. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/interaction.py +0 -0
  229. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/kvstore/__init__.py +0 -0
  230. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/kvstore/backend.py +0 -0
  231. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/kvstore/sqlite.py +0 -0
  232. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/mcp_client.py +0 -0
  233. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/mcp_config.py +0 -0
  234. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/mcp_server.py +0 -0
  235. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/md_agents.py +0 -0
  236. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/models.py +0 -0
  237. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/options.py +0 -0
  238. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/plugins.py +0 -0
  239. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/renderer.py +0 -0
  240. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/schemas/__init__.py +0 -0
  241. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/schemas/agent.schema.json +0 -0
  242. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/shell_tool_config.py +0 -0
  243. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/skill_discovery.py +0 -0
  244. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/AGENTS.md +0 -0
  245. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/IDENTITY.md +0 -0
  246. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/MEMORY.md +0 -0
  247. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/USER.md +0 -0
  248. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/personas/casual-technical.md +0 -0
  249. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/personas/marvin.md +0 -0
  250. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/templates/personas/minimal.md +0 -0
  251. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/__init__.py +0 -0
  252. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/agents.py +0 -0
  253. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/fs.py +0 -0
  254. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/history.py +0 -0
  255. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/http.py +0 -0
  256. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/interactive.py +0 -0
  257. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/kv.py +0 -0
  258. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/notify.py +0 -0
  259. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/schedule.py +0 -0
  260. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/sessions.py +0 -0
  261. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/shell.py +0 -0
  262. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/shell_tools.py +0 -0
  263. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/skills.py +0 -0
  264. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tools/tmux.py +0 -0
  265. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/tsugite.py +0 -0
  266. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/__init__.py +0 -0
  267. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/base.py +0 -0
  268. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/chat.py +0 -0
  269. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/helpers.py +0 -0
  270. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/jsonl.py +0 -0
  271. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/plain.py +0 -0
  272. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/repl_chat.py +0 -0
  273. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/repl_commands.py +0 -0
  274. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/repl_completer.py +0 -0
  275. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui/repl_handler.py +0 -0
  276. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/ui_context.py +0 -0
  277. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/utils.py +0 -0
  278. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/workspace/__init__.py +0 -0
  279. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/workspace/context.py +0 -0
  280. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/workspace/models.py +0 -0
  281. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/workspace/session.py +0 -0
  282. {tsugite_cli-0.9.3 → tsugite_cli-0.9.4}/tsugite/workspace/templates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tsugite-cli
3
- Version: 0.9.3
3
+ Version: 0.9.4
4
4
  Summary: Micro-agent runner for task automation using markdown definitions
5
5
  Author: Justyn Shull
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tsugite-cli"
3
- version = "0.9.3"
3
+ version = "0.9.4"
4
4
  description = "Micro-agent runner for task automation using markdown definitions"
5
5
  authors = [{ name = "Justyn Shull" }]
6
6
  requires-python = ">=3.11"
@@ -343,6 +343,7 @@ class HTTPServer:
343
343
  Route("/api/health", self._health, methods=["GET"]),
344
344
  Route("/api/agents", self._list_agents, methods=["GET"]),
345
345
  Route("/api/agents/{agent}/sessions", self._list_sessions, methods=["GET"]),
346
+ Route("/api/agents/{agent}/sessions/new", self._new_interactive_session, methods=["POST"]),
346
347
  Route("/api/agents/{agent}/chat", self._chat, methods=["POST"]),
347
348
  Route("/api/agents/{agent}/chat/cancel", self._cancel_chat, methods=["POST"]),
348
349
  Route("/api/agents/{agent}/upload", self._upload, methods=["POST"]),
@@ -481,6 +482,7 @@ class HTTPServer:
481
482
  agent=adapter.agent_name, source=source, status=status, parent_id=parent_id, limit=limit
482
483
  )
483
484
 
485
+ default_ids = adapter.session_store.default_interactive_ids(adapter.agent_name)
484
486
  sessions = []
485
487
  for s in all_sessions:
486
488
  user_id = s.user_id or ""
@@ -502,16 +504,43 @@ class HTTPServer:
502
504
  "created_at": s.created_at,
503
505
  "last_active": s.last_active,
504
506
  "parent_id": s.parent_id,
505
- "prompt": (s.prompt or "")[:200],
507
+ "prompt": s.prompt or "",
506
508
  "model": s.model,
507
509
  "error": s.error,
508
510
  "result": s.result,
509
511
  "title": s.title,
512
+ "is_default": default_ids.get(user_id) == s.id,
510
513
  }
511
514
  )
512
515
 
513
516
  return JSONResponse({"sessions": sessions})
514
517
 
518
+ async def _new_interactive_session(self, request: Request) -> JSONResponse:
519
+ adapter, err = self._get_adapter(request)
520
+ if err:
521
+ return err
522
+ try:
523
+ body = await request.json()
524
+ except Exception:
525
+ return JSONResponse({"error": "invalid JSON body"}, status_code=400)
526
+
527
+ user_id = adapter.resolve_http_user(body.get("user_id", "web-anonymous"))
528
+
529
+ from tsugite.daemon.session_store import Session, SessionSource
530
+ from tsugite.history.storage import generate_session_id
531
+
532
+ session_id = generate_session_id(adapter.agent_name)
533
+ session = Session(
534
+ id=session_id,
535
+ agent=adapter.agent_name,
536
+ source=SessionSource.INTERACTIVE.value,
537
+ user_id=user_id,
538
+ )
539
+ adapter.session_store.create_session(session)
540
+ if self.event_bus:
541
+ self.event_bus.emit("session_update", {"action": "created", "id": session_id})
542
+ return JSONResponse({"id": session_id}, status_code=201)
543
+
515
544
  async def _status(self, request: Request) -> JSONResponse:
516
545
  adapter, err = self._get_adapter(request)
517
546
  if err:
@@ -1208,7 +1237,7 @@ class HTTPServer:
1208
1237
  "agent": s.agent,
1209
1238
  "source": s.source,
1210
1239
  "state": s.status,
1211
- "prompt": (s.prompt or "")[:200],
1240
+ "prompt": s.prompt or "",
1212
1241
  "created_at": s.created_at,
1213
1242
  "updated_at": s.last_active,
1214
1243
  "error": s.error,
@@ -1598,7 +1627,7 @@ class HTTPServer:
1598
1627
 
1599
1628
  workspace_dir = adapter.agent_config.workspace_dir
1600
1629
  if not workspace_dir.is_dir():
1601
- return JSONResponse({"files": []})
1630
+ return JSONResponse({"entries": [], "subdir": "", "workspace_dir": str(workspace_dir)})
1602
1631
 
1603
1632
  subdir = request.query_params.get("subdir", "")
1604
1633
  if subdir:
@@ -1613,37 +1642,37 @@ class HTTPServer:
1613
1642
  from tsugite.tools.fs import _build_gitignore_matcher
1614
1643
 
1615
1644
  gitignore_spec = _build_gitignore_matcher(workspace_dir)
1616
- resolved_ws = workspace_dir.resolve()
1617
- files = []
1645
+ entries = []
1618
1646
  try:
1619
- for item in target.rglob("*"):
1620
- if not item.is_file() or item.is_symlink():
1621
- continue
1647
+ import stat as stat_mod
1648
+
1649
+ for item in sorted(target.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower())):
1622
1650
  try:
1623
- if not item.resolve().is_relative_to(resolved_ws):
1624
- continue
1625
- except (ValueError, OSError):
1651
+ st = item.lstat()
1652
+ except OSError:
1653
+ continue
1654
+ if stat_mod.S_ISLNK(st.st_mode):
1626
1655
  continue
1656
+ is_dir = stat_mod.S_ISDIR(st.st_mode)
1627
1657
  rel = str(item.relative_to(workspace_dir))
1628
- if gitignore_spec and gitignore_spec.match_file(rel):
1658
+ if gitignore_spec and gitignore_spec.match_file(rel + ("/" if is_dir else "")):
1629
1659
  continue
1630
- stat = item.stat()
1631
- files.append(
1632
- {
1633
- "path": rel,
1634
- "name": item.name,
1635
- "size": stat.st_size,
1636
- "is_text": _is_text_mime(item),
1637
- "modified": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(),
1638
- }
1639
- )
1640
- if len(files) >= MAX_WORKSPACE_LIST_FILES:
1641
- break
1660
+ if is_dir:
1661
+ entries.append({"path": rel, "name": item.name, "is_dir": True})
1662
+ elif stat_mod.S_ISREG(st.st_mode) and _is_text_mime(item):
1663
+ entries.append(
1664
+ {
1665
+ "path": rel,
1666
+ "name": item.name,
1667
+ "is_dir": False,
1668
+ "size": st.st_size,
1669
+ "modified": datetime.fromtimestamp(st.st_mtime, tz=timezone.utc).isoformat(),
1670
+ }
1671
+ )
1642
1672
  except OSError as e:
1643
1673
  return JSONResponse({"error": f"listing failed: {e}"}, status_code=500)
1644
1674
 
1645
- files.sort(key=lambda f: f["path"])
1646
- return JSONResponse({"files": files, "workspace_dir": str(workspace_dir)})
1675
+ return JSONResponse({"entries": entries, "subdir": subdir, "workspace_dir": str(workspace_dir)})
1647
1676
 
1648
1677
  async def _read_workspace_file(self, request: Request) -> JSONResponse:
1649
1678
  adapter, err = self._get_adapter(request)
@@ -165,7 +165,7 @@ class SessionStore:
165
165
  session_id = self._interactive_index[key]
166
166
  if session_id in self._sessions:
167
167
  session = self._sessions[session_id]
168
- if session.status in (SessionStatus.CANCELLED.value, SessionStatus.COMPLETED.value):
168
+ if session.status in (SessionStatus.CANCELLED.value, SessionStatus.COMPLETED.value, SessionStatus.FAILED.value):
169
169
  session.status = SessionStatus.ACTIVE.value
170
170
  self._mark_dirty()
171
171
  return session
@@ -177,6 +177,7 @@ class SessionStore:
177
177
  agent=agent,
178
178
  source=SessionSource.INTERACTIVE.value,
179
179
  user_id=user_id,
180
+ title="Main Session",
180
181
  )
181
182
 
182
183
  # Estimate tokens from existing history
@@ -189,6 +190,11 @@ class SessionStore:
189
190
  self._save()
190
191
  return session
191
192
 
193
+ def default_interactive_ids(self, agent: str) -> dict:
194
+ """Return {user_id: session_id} for all default interactive sessions for this agent."""
195
+ with self._lock:
196
+ return {uid: sid for (uid, ag), sid in self._interactive_index.items() if ag == agent}
197
+
192
198
  def needs_compaction(self, session_id: str) -> bool:
193
199
  with self._lock:
194
200
  session = self._sessions.get(session_id)
@@ -96,8 +96,9 @@ main { flex: 1; overflow: hidden; display: flex; flex-direction: column; }
96
96
  .command-suggestion:hover, .command-suggestion.selected { background: var(--bg); }
97
97
  .command-suggestion .cmd-name { color: var(--accent); font-weight: 600; font-size: 14px; white-space: nowrap; }
98
98
  .command-suggestion .cmd-desc { color: var(--text-muted); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
99
- #message-input { flex: 1; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; font-size: 14px; font-family: inherit; resize: none; min-height: 42px; max-height: 150px; }
99
+ #message-input { flex: 1; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; font-size: 14px; font-family: inherit; resize: none; min-height: 42px; max-height: 150px; transition: max-height 0.15s ease; }
100
100
  #message-input:focus { outline: none; border-color: var(--accent); }
101
+ #message-input.input-expanded { max-height: 60vh; height: 60vh; resize: vertical; }
101
102
  #send-btn, #stop-btn { border-radius: 8px; padding: 10px 20px; font-size: 14px; font-weight: 600; cursor: pointer; align-self: flex-end; }
102
103
  #send-btn { background: var(--accent); color: var(--btn-text); border: none; }
103
104
  #send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
@@ -110,30 +110,35 @@
110
110
  <template x-if="loading">
111
111
  <div class="empty-state"><span class="spinner"></span></div>
112
112
  </template>
113
- <template x-if="!loading && allSessions.length === 0">
114
- <div class="empty-state">No sessions</div>
115
- </template>
116
113
  <input type="text" x-model="sessionFilter" placeholder="Filter sessions..." class="session-filter"
117
114
  x-show="!loading && allSessions.length > 0">
118
115
  <div class="turn-list">
119
- <!-- Interactive sessions -->
120
- <template x-if="groupedSessions.interactive.length > 0">
116
+ <!-- Interactive sessions (always show header with + button) -->
117
+ <template x-if="!loading">
121
118
  <div>
122
- <div class="session-group-header">Interactive</div>
119
+ <div class="session-group-header" style="display:flex;align-items:center;justify-content:space-between">
120
+ <span>Interactive</span>
121
+ <button class="btn-sm" @click="newSession()" title="New conversation" style="padding:2px 8px;font-size:12px">+</button>
122
+ </div>
123
123
  <template x-for="s in groupedSessions.interactive" :key="s.conversation_id || s.id">
124
124
  <div class="turn-item" :class="{ active: selectedSessionId === (s.conversation_id || s.id) }"
125
125
  @click="selectSession(s)">
126
126
  <div class="item-row">
127
127
  <span class="status-dot" :style="{ background: statusDotColor(s.state) }"></span>
128
- <template x-if="editingSessionId === s.id">
128
+ <template x-if="s.is_default">
129
+ <span style="font-size:9px;color:var(--accent);flex-shrink:0" title="Default session">&#9679;</span>
130
+ </template>
131
+ <template x-if="editingSessionId === s.id && !s.is_default">
129
132
  <input class="session-title-input" type="text" x-model="editingTitle"
130
133
  @blur="saveTitle(s)" @keydown.enter="saveTitle(s)" @keydown.escape="editingSessionId = null"
131
134
  @click.stop>
132
135
  </template>
133
- <template x-if="editingSessionId !== s.id">
136
+ <template x-if="editingSessionId !== s.id || s.is_default">
134
137
  <span class="session-label-row">
135
138
  <span x-text="sessionLabel(s)" class="session-label"></span>
136
- <span class="session-edit-btn" @click="startEditTitle(s, $event)" title="Rename">&#9998;</span>
139
+ <template x-if="!s.is_default">
140
+ <span class="session-edit-btn" @click="startEditTitle(s, $event)" title="Rename">&#9998;</span>
141
+ </template>
137
142
  </span>
138
143
  </template>
139
144
  </div>
@@ -221,7 +226,10 @@
221
226
  </button>
222
227
  <button class="mobile-back-btn" @click="backToSessions()">&#8592; Sessions</button>
223
228
  <template x-if="!selectedSessionId">
224
- <div class="empty-state">Select a session</div>
229
+ <div class="empty-state">
230
+ <div>Select a session or</div>
231
+ <button class="btn-sm" @click="newSession()" style="margin-top:8px">Start new conversation</button>
232
+ </div>
225
233
  </template>
226
234
 
227
235
  <!-- Active interactive session: full chat UI -->
@@ -390,7 +398,7 @@
390
398
  <span><span class="spinner"></span> Compacting...</span>
391
399
  </template>
392
400
  <button class="btn-sm" x-show="!compacting" @click="compactSession()" :disabled="sending">Compact</button>
393
- <button class="btn-sm" x-show="!compacting && !sending" @click="completeSession(selectedSessionMeta)">Mark Complete</button>
401
+ <button class="btn-sm" x-show="!compacting && !sending && !selectedSessionMeta?.is_default" @click="completeSession(selectedSessionMeta)">Mark Complete</button>
394
402
  </div>
395
403
 
396
404
  <div class="drag-overlay" x-show="isDragging" x-cloak>Drop files to attach</div>
@@ -422,10 +430,13 @@
422
430
  </div>
423
431
  <button class="attach-btn" @click="openFilePicker()" title="Attach files">📎</button>
424
432
  <input type="file" multiple x-ref="fileInput" @change="onFileInputChange($event)" style="display:none">
433
+ <button class="attach-btn" @click="expandedInput = !expandedInput" :title="expandedInput ? 'Collapse editor' : 'Expand editor'" x-text="expandedInput ? '⤓' : '⤢'"></button>
425
434
  <textarea id="message-input" x-model="messageText" rows="1"
435
+ :class="{ 'input-expanded': expandedInput }"
426
436
  placeholder="Send a message..." autofocus
427
- @keydown="onInputKeydown" @input="autoResize($event); onInputChange()"></textarea>
428
- <span class="input-hint">Shift+Enter for newline</span>
437
+ @keydown="onInputKeydown($event)" @input="if (!expandedInput) autoResize($event); onInputChange()"
438
+ @keydown.escape="expandedInput = false"></textarea>
439
+ <span class="input-hint" x-text="expandedInput ? 'Escape to collapse' : 'Shift+Enter for newline'"></span>
429
440
  <button id="send-btn" x-show="!sending" @click="sendMessage()">Send</button>
430
441
  <button id="stop-btn" x-show="sending" @click="cancelChat()">Stop</button>
431
442
  </div>
@@ -435,34 +446,17 @@
435
446
  <!-- Non-interactive session: read-only history -->
436
447
  <template x-if="selectedSessionId && !isActiveSession">
437
448
  <div style="overflow-y:auto;flex:1">
438
- <!-- Session metadata card -->
449
+ <!-- Session metadata header -->
439
450
  <template x-if="selectedSessionMeta">
440
- <div class="card" style="margin-bottom:12px">
441
- <h3 style="display:flex;align-items:center;gap:8px">
442
- <span x-text="selectedSessionMeta.id || selectedSessionMeta.conversation_id"></span>
451
+ <div style="margin-bottom:12px">
452
+ <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap">
453
+ <span class="badge" :class="stateBadge(selectedSessionMeta.state)" x-text="selectedSessionMeta.state"></span>
443
454
  <span class="badge badge-muted" x-text="selectedSessionMeta.source || 'background'" style="font-weight:normal"></span>
444
- </h3>
445
- <div class="card-meta">
446
- <div>Status: <span class="badge" :class="stateBadge(selectedSessionMeta.state)" x-text="selectedSessionMeta.state"></span></div>
447
- <template x-if="selectedSessionMeta.prompt">
448
- <div>Prompt: <span x-text="selectedSessionMeta.prompt" style="font-size:12px"></span></div>
449
- </template>
450
455
  <template x-if="selectedSessionMeta.model">
451
- <div>Model: <span x-text="selectedSessionMeta.model"></span></div>
452
- </template>
453
- <template x-if="selectedSessionMeta.error">
454
- <div style="color:var(--error)">Error: <span x-text="selectedSessionMeta.error"></span></div>
455
- </template>
456
- <template x-if="selectedSessionMeta.result">
457
- <div>Result: <span x-text="selectedSessionMeta.result" style="font-size:12px"></span></div>
456
+ <span style="font-size:11px;color:var(--muted)" x-text="selectedSessionMeta.model"></span>
458
457
  </template>
459
- <div style="font-size:12px;color:var(--muted);margin-top:4px">
460
- Created: <span x-text="formatDate(selectedSessionMeta.created_at)"></span>
461
- <template x-if="selectedSessionMeta.last_active">
462
- · Last active: <span x-text="formatDate(selectedSessionMeta.last_active)"></span>
463
- </template>
464
- </div>
465
- <div style="display:flex;gap:8px;margin-top:8px">
458
+ <span style="font-size:11px;color:var(--muted)" x-text="formatDate(selectedSessionMeta.created_at)"></span>
459
+ <div style="display:flex;gap:6px;margin-left:auto">
466
460
  <template x-if="canCancel(selectedSessionMeta)">
467
461
  <button class="btn-sm danger" @click="cancelSession(selectedSessionMeta)">Cancel</button>
468
462
  </template>
@@ -474,6 +468,16 @@
474
468
  </template>
475
469
  </div>
476
470
  </div>
471
+ <div style="font-size:11px;color:var(--muted);margin-bottom:8px;word-break:break-all" x-text="selectedSessionMeta.id || selectedSessionMeta.conversation_id"></div>
472
+ <template x-if="selectedSessionMeta.prompt">
473
+ <div class="msg user" x-html="renderHtml(selectedSessionMeta.prompt)"></div>
474
+ </template>
475
+ <template x-if="selectedSessionMeta.error">
476
+ <div class="msg error" x-text="selectedSessionMeta.error" style="margin-top:8px"></div>
477
+ </template>
478
+ <template x-if="selectedSessionMeta.result && turns.length === 0">
479
+ <div class="msg agent" x-html="renderHtml(selectedSessionMeta.result)" style="margin-top:8px"></div>
480
+ </template>
477
481
  </div>
478
482
  </template>
479
483
 
@@ -600,26 +604,34 @@
600
604
  <div class="section-header">
601
605
  <h2>Workspace</h2>
602
606
  </div>
603
- <template x-if="workspaceDir">
604
- <div style="font-size:11px;color:var(--muted);padding:0 12px 8px;word-break:break-all" x-text="workspaceDir"></div>
605
- </template>
607
+ <div style="font-size:12px;padding:4px 12px 8px;display:flex;flex-wrap:wrap;align-items:center;gap:2px">
608
+ <span style="cursor:pointer;color:var(--accent)" @click="goToRoot()">workspace</span>
609
+ <template x-for="crumb in breadcrumbs" :key="crumb.path">
610
+ <span>
611
+ <span style="color:var(--muted);margin:0 2px">/</span>
612
+ <span style="cursor:pointer;color:var(--accent)" @click="goToBreadcrumb(crumb)" x-text="crumb.name"></span>
613
+ </span>
614
+ </template>
615
+ </div>
606
616
  <template x-if="loading">
607
617
  <div class="empty-state"><span class="spinner"></span></div>
608
618
  </template>
609
- <template x-if="!loading && files.length === 0">
610
- <div class="empty-state">No files in workspace</div>
619
+ <template x-if="!loading && entries.length === 0">
620
+ <div class="empty-state">Empty directory</div>
611
621
  </template>
612
622
  <div class="turn-list">
613
- <template x-for="file in files" :key="file.path">
614
- <div class="turn-item" :class="{ active: selectedFile?.path === file.path }"
615
- @click="selectFile(file)">
623
+ <template x-for="entry in entries" :key="entry.path">
624
+ <div class="turn-item" :class="{ active: !entry.is_dir && selectedFile?.path === entry.path }"
625
+ @click="entry.is_dir ? openDir(entry) : selectFile(entry)">
616
626
  <div class="item-row">
617
- <span x-text="file.path" style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></span>
618
- <template x-if="!file.is_text">
619
- <span class="badge" style="font-size:10px;flex-shrink:0">binary</span>
620
- </template>
627
+ <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
628
+ <span x-text="entry.is_dir ? '\u{1F4C1} ' : ''" style="font-size:13px"></span>
629
+ <span x-text="entry.name"></span>
630
+ </span>
621
631
  </div>
622
- <div class="turn-meta" x-text="formatSize(file.size) + ' · ' + formatDate(file.modified)"></div>
632
+ <template x-if="!entry.is_dir">
633
+ <div class="turn-meta" x-text="formatSize(entry.size) + ' \u00b7 ' + formatDate(entry.modified)"></div>
634
+ </template>
623
635
  </div>
624
636
  </template>
625
637
  </div>
@@ -645,26 +657,16 @@
645
657
  </h2>
646
658
  <div style="display:flex;gap:6px">
647
659
  <button class="btn-sm" @click="attachToChat(selectedFile)" title="Attach to conversation">Attach</button>
648
- <template x-if="selectedFile.is_text">
649
- <button class="btn-sm" @click="save()" :disabled="saving || !isDirty"
650
- x-text="saving ? 'Saving...' : 'Save'"></button>
651
- </template>
660
+ <button class="btn-sm" @click="save()" :disabled="saving || !isDirty"
661
+ x-text="saving ? 'Saving...' : 'Save'"></button>
652
662
  </div>
653
663
  </div>
654
664
  <div style="font-size:11px;color:var(--muted);margin-bottom:8px" x-text="selectedFile.path"></div>
655
665
  <template x-if="error">
656
666
  <div class="msg error" x-text="error" style="margin-bottom:8px"></div>
657
667
  </template>
658
- <template x-if="!selectedFile.is_text">
659
- <div class="empty-state">
660
- <div>Binary file</div>
661
- <div style="font-size:12px;color:var(--muted);margin-top:4px" x-text="formatSize(selectedFile.size)"></div>
662
- </div>
663
- </template>
664
- <template x-if="selectedFile.is_text">
665
- <textarea x-model="content"
666
- style="flex:1;min-height:300px;font-family:monospace;font-size:13px;resize:vertical;background:var(--surface0);color:var(--text);border:1px solid var(--surface2);border-radius:6px;padding:12px;tab-size:2"></textarea>
667
- </template>
668
+ <textarea x-model="content"
669
+ style="flex:1;min-height:300px;font-family:monospace;font-size:13px;resize:vertical;background:var(--surface0);color:var(--text);border:1px solid var(--surface2);border-radius:6px;padding:12px;tab-size:2"></textarea>
668
670
  </div>
669
671
  </template>
670
672
  </div>
@@ -21,6 +21,7 @@ export default () => ({
21
21
  loadedSkills: [],
22
22
  pendingFiles: [],
23
23
  isDragging: false,
24
+ expandedInput: false,
24
25
  allSessions: [],
25
26
  selectedSessionId: null,
26
27
  isActiveSession: true,
@@ -53,6 +54,10 @@ export default () => ({
53
54
  this.$watch('$store.app.view', (view) => {
54
55
  if (view === 'conversations' && this.$store.app.selectedAgent) this.reload();
55
56
  });
57
+ // Eagerly load if agent is already selected (from localStorage)
58
+ if (this.$store.app.view === 'conversations' && this.$store.app.selectedAgent) {
59
+ this.reload();
60
+ }
56
61
  this.$watch('$store.app.lastEvent', (ev) => {
57
62
  if (!ev) return;
58
63
  if (ev.type === 'history_update' && ev.agent === this.$store.app.selectedAgent) {
@@ -424,15 +429,28 @@ export default () => ({
424
429
  },
425
430
 
426
431
  canCancel(s) {
427
- return s?.state === 'running';
432
+ return s?.state === 'running' && !s?.is_default;
428
433
  },
429
434
 
430
435
  canRestart(s) {
431
- return s?.state === 'failed' || s?.state === 'cancelled';
436
+ return (s?.state === 'failed' || s?.state === 'cancelled') && !s?.is_default;
432
437
  },
433
438
 
434
439
  canComplete(s) {
435
- return s?.state === 'active' || s?.state === 'running';
440
+ return (s?.state === 'active' || s?.state === 'running') && !s?.is_default;
441
+ },
442
+
443
+ async newSession() {
444
+ const agent = this.$store.app.selectedAgent;
445
+ if (!agent) return;
446
+ try {
447
+ const data = await post(`/api/agents/${agent}/sessions/new`, { user_id: this.userId });
448
+ await this.loadSessions();
449
+ const session = this.allSessions.find(s => s.id === data.id);
450
+ if (session) this.selectSession(session);
451
+ } catch (e) {
452
+ this.messages.push({ type: 'error', text: `Failed to create session: ${e.message}` });
453
+ }
436
454
  },
437
455
 
438
456
  async completeSession(session) {
@@ -613,6 +631,7 @@ export default () => ({
613
631
  if (parsed && !this.pendingFiles.length) {
614
632
  this.sending = true;
615
633
  this.messageText = '';
634
+ this._resetInputHeight();
616
635
  this.showCommandSuggestions = false;
617
636
  this.messages.push({ type: 'user', text: msg });
618
637
  this.scrollMessages();
@@ -630,6 +649,7 @@ export default () => ({
630
649
 
631
650
  this.sending = true;
632
651
  this.messageText = '';
652
+ this._resetInputHeight();
633
653
 
634
654
  let uploadedFiles = [];
635
655
  const fileNames = this.pendingFiles.map(f => f.name);
@@ -956,4 +976,12 @@ export default () => ({
956
976
  e.target.style.height = 'auto';
957
977
  e.target.style.height = Math.min(e.target.scrollHeight, maxH) + 'px';
958
978
  },
979
+
980
+ _resetInputHeight() {
981
+ this.expandedInput = false;
982
+ this.$nextTick(() => {
983
+ const ta = document.getElementById('message-input');
984
+ if (ta) ta.style.height = 'auto';
985
+ });
986
+ },
959
987
  });
@@ -3,7 +3,8 @@ import { formatDate, formatFileSize } from '../utils.js';
3
3
 
4
4
  export default () => ({
5
5
  sidebarOpen: false,
6
- files: [],
6
+ entries: [],
7
+ currentDir: '',
7
8
  loading: true,
8
9
  error: null,
9
10
  selectedFile: null,
@@ -16,18 +17,19 @@ export default () => ({
16
17
 
17
18
  init() {
18
19
  this.$watch('$store.app.selectedAgent', () => {
19
- if (this.$store.app.view === 'workspace') this.load();
20
+ if (this.$store.app.view === 'workspace') { this.currentDir = ''; this.load(); }
20
21
  });
21
22
  this.$watch('$store.app.view', (view) => {
22
23
  if (view === 'workspace' && this.$store.app.selectedAgent) this.load();
23
24
  });
24
25
  this._keyHandler = (e) => {
25
- if ((e.ctrlKey || e.metaKey) && e.key === 's' && this.$store.app.view === 'workspace' && this.selectedFile && this.selectedFile.is_text) {
26
+ if ((e.ctrlKey || e.metaKey) && e.key === 's' && this.$store.app.view === 'workspace' && this.selectedFile) {
26
27
  e.preventDefault();
27
28
  this.save();
28
29
  }
29
30
  };
30
31
  document.addEventListener('keydown', this._keyHandler);
32
+ if (this.$store.app.view === 'workspace' && this.$store.app.selectedAgent) this.load();
31
33
  },
32
34
 
33
35
  destroy() {
@@ -39,12 +41,11 @@ export default () => ({
39
41
  if (!agent) return;
40
42
  this.loading = true;
41
43
  this.error = null;
42
- this.selectedFile = null;
43
- this.content = '';
44
- this.originalContent = '';
45
44
  try {
46
- const data = await get(`/api/agents/${agent}/workspace`);
47
- this.files = data.files || [];
45
+ let url = `/api/agents/${agent}/workspace`;
46
+ if (this.currentDir) url += `?subdir=${encodeURIComponent(this.currentDir)}`;
47
+ const data = await get(url);
48
+ this.entries = data.entries || [];
48
49
  this.workspaceDir = data.workspace_dir || '';
49
50
  } catch (e) {
50
51
  this.error = e.message;
@@ -53,6 +54,28 @@ export default () => ({
53
54
  }
54
55
  },
55
56
 
57
+ get breadcrumbs() {
58
+ if (!this.currentDir) return [];
59
+ const parts = this.currentDir.split('/').filter(Boolean);
60
+ return parts.map((name, i) => ({
61
+ name,
62
+ path: parts.slice(0, i + 1).join('/'),
63
+ }));
64
+ },
65
+
66
+ _navigateTo(dir) {
67
+ if (this.isDirty && !confirm('You have unsaved changes. Discard?')) return;
68
+ this.selectedFile = null;
69
+ this.content = '';
70
+ this.originalContent = '';
71
+ this.currentDir = dir;
72
+ this.load();
73
+ },
74
+
75
+ openDir(entry) { this._navigateTo(entry.path); },
76
+ goToRoot() { this._navigateTo(''); },
77
+ goToBreadcrumb(crumb) { this._navigateTo(crumb.path); },
78
+
56
79
  get isDirty() {
57
80
  return this.content !== this.originalContent;
58
81
  },
@@ -67,15 +90,9 @@ export default () => ({
67
90
  this.originalContent = '';
68
91
  this.error = null;
69
92
 
70
- if (!file.is_text) return;
71
-
72
93
  const agent = this.$store.app.selectedAgent;
73
94
  try {
74
95
  const data = await get(`/api/agents/${agent}/workspace/content?path=${encodeURIComponent(file.path)}`);
75
- if (!data.is_text) {
76
- this.selectedFile = { ...file, is_text: false };
77
- return;
78
- }
79
96
  this.content = data.content;
80
97
  this.originalContent = data.content;
81
98
  } catch (e) {
@@ -84,7 +101,7 @@ export default () => ({
84
101
  },
85
102
 
86
103
  async save() {
87
- if (!this.selectedFile || !this.selectedFile.is_text || this.saving) return;
104
+ if (!this.selectedFile || this.saving) return;
88
105
  this.saving = true;
89
106
  this.error = null;
90
107
  try {
@@ -2633,7 +2633,7 @@ wheels = [
2633
2633
 
2634
2634
  [[package]]
2635
2635
  name = "tsugite-cli"
2636
- version = "0.9.3"
2636
+ version = "0.9.4"
2637
2637
  source = { editable = "." }
2638
2638
  dependencies = [
2639
2639
  { name = "ddgs" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes