meshagent-cli 0.41.4__tar.gz → 0.41.6__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 (142) hide show
  1. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/PKG-INFO +16 -16
  2. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/ask.py +173 -18
  3. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/ask_test.py +230 -7
  4. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/async_typer.py +26 -2
  5. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/create.py +119 -18
  6. meshagent_cli-0.41.6/meshagent/cli/create_project_templates/python/contact-form/server.py +368 -0
  7. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/create_test.py +170 -1
  8. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/doctor.py +20 -0
  9. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/image.py +942 -163
  10. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/image_test.py +286 -10
  11. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/local_settings.py +0 -9
  12. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/local_settings_test.py +2 -2
  13. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/process.py +3 -1
  14. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/root_commands.py +9 -2
  15. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/root_commands_test.py +69 -0
  16. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/services.py +66 -11
  17. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/services_test.py +143 -0
  18. meshagent_cli-0.41.6/meshagent/cli/tui/deploy_room.py +1296 -0
  19. meshagent_cli-0.41.6/meshagent/cli/tui/deploy_room_test.py +61 -0
  20. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/setup.py +41 -7
  21. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/setup_test.py +76 -0
  22. meshagent_cli-0.41.6/meshagent/cli/version.py +1 -0
  23. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent_cli.egg-info/PKG-INFO +16 -16
  24. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent_cli.egg-info/SOURCES.txt +1 -0
  25. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent_cli.egg-info/requires.txt +15 -15
  26. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/pyproject.toml +15 -15
  27. meshagent_cli-0.41.4/meshagent/cli/tui/deploy_room.py +0 -517
  28. meshagent_cli-0.41.4/meshagent/cli/tui/deploy_room_test.py +0 -19
  29. meshagent_cli-0.41.4/meshagent/cli/version.py +0 -1
  30. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/LICENSE +0 -0
  31. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/README.md +0 -0
  32. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/__init__.py +0 -0
  33. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/agent.py +0 -0
  34. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/agent_cli_options_test.py +0 -0
  35. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/agent_package_cli.py +0 -0
  36. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/agent_package_cli_test.py +0 -0
  37. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/agents.py +0 -0
  38. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/agents_test.py +0 -0
  39. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/api_keys.py +0 -0
  40. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/api_keys_test.py +0 -0
  41. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/async_typer_test.py +0 -0
  42. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/auth.py +0 -0
  43. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/auth_async.py +0 -0
  44. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/auth_async_test.py +0 -0
  45. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/auth_test.py +0 -0
  46. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/call.py +0 -0
  47. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/chatbot.py +0 -0
  48. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/cli.py +0 -0
  49. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/cli_mcp.py +0 -0
  50. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/cli_secrets.py +0 -0
  51. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/cli_test.py +0 -0
  52. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/common_options.py +0 -0
  53. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/config.py +0 -0
  54. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/config_test.py +0 -0
  55. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/containers.py +0 -0
  56. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/containers_test.py +0 -0
  57. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/create_project_templates/__init__.py +0 -0
  58. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/create_project_templates/python/backend-agent/server.py +0 -0
  59. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/create_project_templates/python/webserver/server.py +0 -0
  60. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/dataset.py +0 -0
  61. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/dataset_test.py +0 -0
  62. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/developer.py +0 -0
  63. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/developer_test.py +0 -0
  64. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/doctor_dockerfiles/__init__.py +0 -0
  65. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/doctor_templates/__init__.py +0 -0
  66. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/doctor_test.py +0 -0
  67. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/feeds.py +0 -0
  68. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/helper.py +0 -0
  69. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/helper_test.py +0 -0
  70. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/helpers.py +0 -0
  71. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/host.py +0 -0
  72. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/launch.py +0 -0
  73. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/launch_test.py +0 -0
  74. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/llm.py +0 -0
  75. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/llm_test.py +0 -0
  76. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/mailbot.py +0 -0
  77. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/mailbot_test.py +0 -0
  78. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/mailboxes.py +0 -0
  79. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/meeting_transcriber.py +0 -0
  80. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/memory.py +0 -0
  81. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/memory_test.py +0 -0
  82. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/meshagent_images.py +0 -0
  83. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/meshagent_images_test.py +0 -0
  84. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/messaging.py +0 -0
  85. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/multi.py +0 -0
  86. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/oauth2.py +0 -0
  87. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/oauth2_test.py +0 -0
  88. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/oci_archive.py +0 -0
  89. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/oci_archive_test.py +0 -0
  90. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/participant_token.py +0 -0
  91. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/port.py +0 -0
  92. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/port_test.py +0 -0
  93. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/preamble_rules.py +0 -0
  94. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/preamble_rules_test.py +0 -0
  95. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/process_live_test.py +0 -0
  96. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/process_test.py +0 -0
  97. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/projects.py +0 -0
  98. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/projects_test.py +0 -0
  99. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/queue.py +0 -0
  100. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/queue_test.py +0 -0
  101. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/registry.py +0 -0
  102. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/registry_test.py +0 -0
  103. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/room.py +0 -0
  104. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/room_connect.py +0 -0
  105. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/room_connect_test.py +0 -0
  106. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/room_services.py +0 -0
  107. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/rooms.py +0 -0
  108. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/rooms_test.py +0 -0
  109. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/routes.py +0 -0
  110. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/scheduled_tasks.py +0 -0
  111. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/scheduled_tasks_test.py +0 -0
  112. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/sessions.py +0 -0
  113. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/sessions_test.py +0 -0
  114. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/storage.py +0 -0
  115. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/storage_test.py +0 -0
  116. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/subscriptions.py +0 -0
  117. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/sync.py +0 -0
  118. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/sync_test.py +0 -0
  119. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/task_runner.py +0 -0
  120. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/task_runner_test.py +0 -0
  121. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/test.py +0 -0
  122. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tool_call_summary.py +0 -0
  123. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tool_call_summary_test.py +0 -0
  124. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tool_integrations.py +0 -0
  125. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tool_integrations_test.py +0 -0
  126. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/__init__.py +0 -0
  127. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/auth_switch.py +0 -0
  128. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/auth_switch_test.py +0 -0
  129. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/create.py +0 -0
  130. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/project_activate.py +0 -0
  131. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/project_activate_test.py +0 -0
  132. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/tui/setup_splash_frames.py +0 -0
  133. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/voicebot.py +0 -0
  134. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/webhook.py +0 -0
  135. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/webserver.py +0 -0
  136. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/webserver_test.py +0 -0
  137. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/worker.py +0 -0
  138. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent/cli/worker_test.py +0 -0
  139. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent_cli.egg-info/dependency_links.txt +0 -0
  140. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent_cli.egg-info/entry_points.txt +0 -0
  141. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/meshagent_cli.egg-info/top_level.txt +0 -0
  142. {meshagent_cli-0.41.4 → meshagent_cli-0.41.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-cli
3
- Version: 0.41.4
3
+ Version: 0.41.6
4
4
  Summary: CLI for Meshagent
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Documentation, https://docs.meshagent.com
@@ -22,7 +22,7 @@ Requires-Dist: openpyxl~=3.1
22
22
  Requires-Dist: xlsxwriter~=3.2
23
23
  Requires-Dist: pathspec<2,>=1.0.3
24
24
  Requires-Dist: zstandard~=0.25.0
25
- Requires-Dist: meshagent-llm-proxy==0.41.4
25
+ Requires-Dist: meshagent-llm-proxy==0.41.6
26
26
  Requires-Dist: rich~=14.3.0
27
27
  Requires-Dist: sounddevice~=0.5
28
28
  Requires-Dist: textual<9.0,>=8.2.3
@@ -30,23 +30,23 @@ Requires-Dist: prompt-toolkit~=3.0.52
30
30
  Requires-Dist: ascii-magic~=2.3
31
31
  Requires-Dist: pillow~=11.3.0
32
32
  Provides-Extra: all
33
- Requires-Dist: meshagent-agents[all]==0.41.4; extra == "all"
34
- Requires-Dist: meshagent-api[all]==0.41.4; extra == "all"
35
- Requires-Dist: meshagent-commoncrawl==0.41.4; extra == "all"
36
- Requires-Dist: meshagent-scrapy==0.41.4; extra == "all"
37
- Requires-Dist: meshagent-computers==0.41.4; extra == "all"
38
- Requires-Dist: meshagent-openai==0.41.4; extra == "all"
39
- Requires-Dist: meshagent-anthropic==0.41.4; extra == "all"
40
- Requires-Dist: meshagent-otel==0.41.4; extra == "all"
41
- Requires-Dist: meshagent-mcp==0.41.4; extra == "all"
42
- Requires-Dist: meshagent-tools==0.41.4; extra == "all"
33
+ Requires-Dist: meshagent-agents[all]==0.41.6; extra == "all"
34
+ Requires-Dist: meshagent-api[all]==0.41.6; extra == "all"
35
+ Requires-Dist: meshagent-commoncrawl==0.41.6; extra == "all"
36
+ Requires-Dist: meshagent-scrapy==0.41.6; extra == "all"
37
+ Requires-Dist: meshagent-computers==0.41.6; extra == "all"
38
+ Requires-Dist: meshagent-openai==0.41.6; extra == "all"
39
+ Requires-Dist: meshagent-anthropic==0.41.6; extra == "all"
40
+ Requires-Dist: meshagent-otel==0.41.6; extra == "all"
41
+ Requires-Dist: meshagent-mcp==0.41.6; extra == "all"
42
+ Requires-Dist: meshagent-tools==0.41.6; extra == "all"
43
43
  Requires-Dist: supabase-auth~=2.28.0; extra == "all"
44
44
  Requires-Dist: prompt-toolkit~=3.0.52; extra == "all"
45
45
  Provides-Extra: mcp-service
46
- Requires-Dist: meshagent-agents[all]==0.41.4; extra == "mcp-service"
47
- Requires-Dist: meshagent-api==0.41.4; extra == "mcp-service"
48
- Requires-Dist: meshagent-mcp==0.41.4; extra == "mcp-service"
49
- Requires-Dist: meshagent-tools==0.41.4; extra == "mcp-service"
46
+ Requires-Dist: meshagent-agents[all]==0.41.6; extra == "mcp-service"
47
+ Requires-Dist: meshagent-api==0.41.6; extra == "mcp-service"
48
+ Requires-Dist: meshagent-mcp==0.41.6; extra == "mcp-service"
49
+ Requires-Dist: meshagent-tools==0.41.6; extra == "mcp-service"
50
50
  Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
51
51
  Dynamic: license-file
52
52
 
@@ -14,6 +14,7 @@ import time
14
14
  import uuid
15
15
  from collections.abc import Awaitable, Callable, Sequence
16
16
  from dataclasses import dataclass
17
+ from importlib import resources
17
18
  from typing import Annotated, Any, Literal, Protocol, runtime_checkable
18
19
 
19
20
  import click
@@ -131,6 +132,7 @@ from meshagent.agents.process import (
131
132
  from meshagent.api import Participant, RoomException
132
133
  from meshagent.cli import async_typer, auth_async
133
134
  from meshagent.cli.common_options import ProjectIdOption
135
+ from meshagent.cli.create import CREATE_TEMPLATE_PACKAGE
134
136
  from meshagent.cli.helper import resolve_project_id
135
137
  from meshagent.cli.preamble_rules import DEFAULT_PREAMBLE_RULE
136
138
  from meshagent.cli.tool_call_summary import format_tool_call_summary
@@ -419,6 +421,9 @@ class _AskExternalThreadState(Protocol):
419
421
  @property
420
422
  def thread_status_text(self) -> str | None: ...
421
423
 
424
+ @property
425
+ def thread_status(self) -> AgentThreadStatus | None: ...
426
+
422
427
  @property
423
428
  def queued_message_labels(self) -> tuple[str, ...]: ...
424
429
 
@@ -471,6 +476,9 @@ class _AgentMessageChannelClient(Protocol):
471
476
  @property
472
477
  def thread_status_text(self) -> str | None: ...
473
478
 
479
+ @property
480
+ def thread_status(self) -> AgentThreadStatus | None: ...
481
+
474
482
  @property
475
483
  def queued_message_labels(self) -> tuple[str, ...]: ...
476
484
 
@@ -509,6 +517,44 @@ def _thread_status_text(status: object) -> str | None:
509
517
  return normalized
510
518
 
511
519
 
520
+ def _format_grouped_status_digits(value: int) -> str:
521
+ text = str(value)
522
+ parts: list[str] = []
523
+ for index, char in enumerate(text):
524
+ if index > 0 and (len(text) - index) % 3 == 0:
525
+ parts.append(",")
526
+ parts.append(char)
527
+ return "".join(parts)
528
+
529
+
530
+ def _format_agent_thread_status_text(message: AgentThreadStatus) -> str | None:
531
+ text = _thread_status_text(message.status)
532
+ if text is None:
533
+ return None
534
+ if message.lines_added is not None or message.lines_removed is not None:
535
+ parts = [text]
536
+ if message.lines_added is not None:
537
+ parts.append(f"+{_format_grouped_status_digits(message.lines_added)}")
538
+ if message.lines_removed is not None:
539
+ parts.append(f"-{_format_grouped_status_digits(message.lines_removed)}")
540
+ return " ".join(parts)
541
+ if message.total_bytes is not None and message.total_bytes > 100:
542
+ return f"{text} {_format_grouped_status_digits(message.total_bytes)} bytes"
543
+ return text
544
+
545
+
546
+ def _ask_thread_status_feed_text(status: object) -> str | None:
547
+ if isinstance(status, AgentThreadStatus):
548
+ text = _format_agent_thread_status_text(status)
549
+ if text is None:
550
+ return None
551
+ return f"• {text}"
552
+ text = _thread_status_text(status)
553
+ if text is None:
554
+ return None
555
+ return f"• {text}"
556
+
557
+
512
558
  def _agent_message_content_text(
513
559
  content: list[AgentTextContent | AgentFileContent],
514
560
  ) -> str:
@@ -645,7 +691,7 @@ def _image_dataset_client_from_agent_client(
645
691
  chat_client = client.client
646
692
  if not isinstance(chat_client, MessagingChatClient):
647
693
  return None
648
- return ImageDatasetClient(chat_client.room)
694
+ return ImageDatasetClient(chat_client.room.datasets)
649
695
 
650
696
 
651
697
  def _role_for_sender(
@@ -995,6 +1041,10 @@ class _AgentMessageSession:
995
1041
  return self._thread_status_text
996
1042
  return self._client.thread_status_text
997
1043
 
1044
+ @property
1045
+ def thread_status(self) -> AgentThreadStatus | None:
1046
+ return self._client.thread_status
1047
+
998
1048
  @property
999
1049
  def queued_message_labels(self) -> tuple[str, ...]:
1000
1050
  return self._client.queued_message_labels
@@ -1378,10 +1428,14 @@ class _AskSession:
1378
1428
  resolved_current_working_directory = os.path.abspath(
1379
1429
  current_working_directory or os.getcwd()
1380
1430
  )
1431
+ self._resource_stack = contextlib.ExitStack()
1432
+ create_samples_path = _enter_create_samples_path(self._resource_stack)
1433
+ self._create_samples_path = create_samples_path
1381
1434
  self._current_working_directory = resolved_current_working_directory
1382
1435
  self._toolkits = _build_ask_toolkits(
1383
1436
  model=model,
1384
1437
  current_working_directory=resolved_current_working_directory,
1438
+ create_samples_path=create_samples_path,
1385
1439
  )
1386
1440
  self._status_adapter = _StatusAwareLLMAdapter(delegate=llm_adapter)
1387
1441
  self._process = LLMAgentProcess(
@@ -1391,6 +1445,7 @@ class _AskSession:
1391
1445
  toolkits=self._toolkits,
1392
1446
  turn_instructions_provider=_build_ask_turn_instructions_provider(
1393
1447
  current_working_directory=resolved_current_working_directory,
1448
+ create_samples_path=create_samples_path,
1394
1449
  interactive=interactive,
1395
1450
  preamble_rule=preamble_rule,
1396
1451
  ),
@@ -1411,10 +1466,18 @@ class _AskSession:
1411
1466
  def current_working_directory(self) -> str:
1412
1467
  return self._current_working_directory
1413
1468
 
1469
+ @property
1470
+ def create_samples_path(self) -> str:
1471
+ return self._create_samples_path
1472
+
1414
1473
  @property
1415
1474
  def thread_status_text(self) -> str | None:
1416
1475
  return self._session.thread_status_text
1417
1476
 
1477
+ @property
1478
+ def thread_status(self) -> AgentThreadStatus | None:
1479
+ return self._session.thread_status
1480
+
1418
1481
  @property
1419
1482
  def queued_message_labels(self) -> tuple[str, ...]:
1420
1483
  return self._session.queued_message_labels
@@ -1437,10 +1500,13 @@ class _AskSession:
1437
1500
  await self._supervisor.start()
1438
1501
 
1439
1502
  async def stop(self) -> None:
1440
- self._status_adapter.set_status_callback(None)
1441
- await self._session.close(close_client=False)
1442
- await self._channel_client.close()
1443
- await self._supervisor.stop()
1503
+ try:
1504
+ self._status_adapter.set_status_callback(None)
1505
+ await self._session.close(close_client=False)
1506
+ await self._channel_client.close()
1507
+ await self._supervisor.stop()
1508
+ finally:
1509
+ self._resource_stack.close()
1444
1510
 
1445
1511
  def _handle_adapter_status(self, status: str) -> None:
1446
1512
  self._session.add_agent_message(
@@ -1566,10 +1632,19 @@ async def _resolve_ask_access_token() -> str | None:
1566
1632
  return normalized_access_token
1567
1633
 
1568
1634
 
1635
+ def _enter_create_samples_path(resource_stack: contextlib.ExitStack) -> str:
1636
+ create_samples_resource = resources.files(CREATE_TEMPLATE_PACKAGE)
1637
+ create_samples_path = resource_stack.enter_context(
1638
+ resources.as_file(create_samples_resource)
1639
+ )
1640
+ return os.path.abspath(os.fspath(create_samples_path))
1641
+
1642
+
1569
1643
  def _build_ask_toolkits(
1570
1644
  *,
1571
1645
  model: str,
1572
1646
  current_working_directory: str,
1647
+ create_samples_path: str | None = None,
1573
1648
  ) -> list[Toolkit]:
1574
1649
  from meshagent.anthropic.web_fetch import WebFetchTool as AnthropicWebFetchTool
1575
1650
  from meshagent.anthropic.web_search import (
@@ -1578,13 +1653,23 @@ def _build_ask_toolkits(
1578
1653
  from meshagent.openai.tools.responses_adapter import ApplyPatchTool, WebSearchTool
1579
1654
  from meshagent.tools.web_toolkit import WebFetchTool
1580
1655
 
1581
- storage_toolkit = StorageToolkit(
1582
- mounts=[
1656
+ storage_mounts = [
1657
+ StorageToolLocalMount(
1658
+ path=current_working_directory,
1659
+ local_path=current_working_directory,
1660
+ )
1661
+ ]
1662
+ if create_samples_path is not None:
1663
+ storage_mounts.append(
1583
1664
  StorageToolLocalMount(
1584
- path=current_working_directory,
1585
- local_path=current_working_directory,
1665
+ path=create_samples_path,
1666
+ local_path=create_samples_path,
1667
+ read_only=True,
1586
1668
  )
1587
- ]
1669
+ )
1670
+
1671
+ storage_toolkit = StorageToolkit(
1672
+ mounts=storage_mounts,
1588
1673
  )
1589
1674
  toolkits: list[Toolkit] = [storage_toolkit]
1590
1675
 
@@ -1604,6 +1689,7 @@ def _build_ask_toolkits(
1604
1689
  def _build_ask_instructions(
1605
1690
  *,
1606
1691
  current_working_directory: str,
1692
+ create_samples_path: str | None = None,
1607
1693
  interactive: bool = True,
1608
1694
  preamble_rule: bool = True,
1609
1695
  ) -> str:
@@ -1622,7 +1708,16 @@ def _build_ask_instructions(
1622
1708
  ),
1623
1709
  (
1624
1710
  "You can also use the meshagent cli to perform tasks. You can get "
1625
- "help for the sdk and its subcommands with the --help flag."
1711
+ "help for the sdk and its subcommands with the --help flag. "
1712
+ "meshagent create has an interactive mode, and users generally "
1713
+ "want that mode when creating sample projects unless they ask for "
1714
+ "a fully scripted flow."
1715
+ ),
1716
+ (
1717
+ "When discussing deployment, assume the user wants to deploy with "
1718
+ "meshagent and its built-in deployment flow. Do not recommend "
1719
+ "third-party deployment services unless the user asks for them or "
1720
+ "the project clearly requires one."
1626
1721
  ),
1627
1722
  (
1628
1723
  "The current working directory is "
@@ -1631,6 +1726,13 @@ def _build_ask_instructions(
1631
1726
  "so read and write files there using that exact path."
1632
1727
  ),
1633
1728
  ]
1729
+ if create_samples_path is not None:
1730
+ sections.append(
1731
+ "The embedded meshagent create sample projects are mounted read-only "
1732
+ f"at {create_samples_path}. Grep and read those examples when "
1733
+ "answering questions about generated project structure, generated "
1734
+ "files, or MeshAgent sample code."
1735
+ )
1634
1736
  if not interactive:
1635
1737
  sections.append(
1636
1738
  "You are not being run interactively. Return useful and actionable "
@@ -1644,11 +1746,13 @@ def _build_ask_instructions(
1644
1746
  def _build_ask_turn_instructions_provider(
1645
1747
  *,
1646
1748
  current_working_directory: str,
1749
+ create_samples_path: str | None = None,
1647
1750
  interactive: bool,
1648
1751
  preamble_rule: bool = True,
1649
1752
  ) -> TurnInstructionsProvider:
1650
1753
  instructions = _build_ask_instructions(
1651
1754
  current_working_directory=current_working_directory,
1755
+ create_samples_path=create_samples_path,
1652
1756
  interactive=interactive,
1653
1757
  preamble_rule=preamble_rule,
1654
1758
  )
@@ -1682,15 +1786,20 @@ def _is_cancelled_turn_error(error: Exception) -> bool:
1682
1786
  return isinstance(error, RoomException) and error.code == "cancelled"
1683
1787
 
1684
1788
 
1789
+ _ASK_SUPPRESSED_LOGGER_NAMES = ("agent-process", "openai_agent")
1790
+
1791
+
1685
1792
  @contextlib.contextmanager
1686
1793
  def _suppress_ask_process_logs() -> Any:
1687
- logger = logging.getLogger("agent-process")
1688
- previous_disabled = logger.disabled
1689
- logger.disabled = True
1794
+ loggers = [logging.getLogger(name) for name in _ASK_SUPPRESSED_LOGGER_NAMES]
1795
+ previous_disabled = {logger: logger.disabled for logger in loggers}
1796
+ for logger in loggers:
1797
+ logger.disabled = True
1690
1798
  try:
1691
1799
  yield
1692
1800
  finally:
1693
- logger.disabled = previous_disabled
1801
+ for logger, disabled in previous_disabled.items():
1802
+ logger.disabled = disabled
1694
1803
 
1695
1804
 
1696
1805
  def _suppress_textual_debug_features() -> None:
@@ -2048,6 +2157,7 @@ async def _run_ask_tui(
2048
2157
  self._pending_session_image_message_ids: set[str] = set()
2049
2158
  self._external_thread_active = False
2050
2159
  self._active_assistant_entry: _AskFeedEntry | None = None
2160
+ self._thread_status_entry_id = "__meshagent_ask_thread_status__"
2051
2161
  self._status_text: str | None = None
2052
2162
  self._usage_state: _AskUsageState | None = None
2053
2163
  self._reasoning_parts: dict[str, list[str]] = {}
@@ -2772,7 +2882,7 @@ async def _run_ask_tui(
2772
2882
  return
2773
2883
  if isinstance(message, AgentThreadStatus):
2774
2884
  self._sync_session_messages()
2775
- self._set_status_text(_thread_status_text(message.status))
2885
+ self._set_thread_status_text(message)
2776
2886
  return
2777
2887
  if isinstance(message, AgentTextContentStarted):
2778
2888
  await self._prepare_active_assistant_text_item(item_id=message.item_id)
@@ -3147,6 +3257,38 @@ async def _run_ask_tui(
3147
3257
  self._render_status_line()
3148
3258
  self._render_turn_queue()
3149
3259
 
3260
+ def _set_thread_status_text(self, status: object) -> None:
3261
+ if isinstance(status, AgentThreadStatus):
3262
+ self._status_text = _format_agent_thread_status_text(status)
3263
+ else:
3264
+ self._status_text = _thread_status_text(status)
3265
+ changed = self._set_thread_status_feed_entry(status)
3266
+ self._render_status_line()
3267
+ self._render_turn_queue()
3268
+ if changed:
3269
+ self._render_feed()
3270
+ self._scroll_to_end()
3271
+
3272
+ def _set_thread_status_feed_entry(self, status: object) -> bool:
3273
+ text = _ask_thread_status_feed_text(status)
3274
+ if text is None:
3275
+ for index, entry in enumerate(self._entries):
3276
+ if entry.message_id != self._thread_status_entry_id:
3277
+ continue
3278
+ del self._entries[index]
3279
+ if index < self._rendered_entry_count:
3280
+ self._reset_rendered_feed()
3281
+ return True
3282
+ return False
3283
+ return self._append_or_replace_feed_entry(
3284
+ _AskFeedEntry(
3285
+ role="event",
3286
+ text=text,
3287
+ pending=True,
3288
+ message_id=self._thread_status_entry_id,
3289
+ )
3290
+ )
3291
+
3150
3292
  def _sync_external_thread_state(self) -> None:
3151
3293
  session = self._session
3152
3294
  if not isinstance(session, _AskExternalThreadState):
@@ -3167,7 +3309,12 @@ async def _run_ask_tui(
3167
3309
  self._thread_generation = generation
3168
3310
  self._reset_current_thread_feed()
3169
3311
 
3170
- status = session.thread_status_text
3312
+ thread_status = session.thread_status
3313
+ status = (
3314
+ _format_agent_thread_status_text(thread_status)
3315
+ if thread_status is not None
3316
+ else session.thread_status_text
3317
+ )
3171
3318
  labels = list(session.queued_message_labels)
3172
3319
  active = status is not None and status.strip() != ""
3173
3320
  status_changed = active != self._external_thread_active
@@ -3179,10 +3326,18 @@ async def _run_ask_tui(
3179
3326
  if status is not None and status.strip() != "":
3180
3327
  status_changed = status_changed or status != self._status_text
3181
3328
  self._status_text = status
3329
+ if self._set_thread_status_feed_entry(thread_status or status):
3330
+ self._render_feed()
3331
+ self._scroll_to_end()
3182
3332
  else:
3183
3333
  next_status = status if active else None
3184
3334
  status_changed = status_changed or next_status != self._status_text
3185
3335
  self._status_text = next_status
3336
+ if self._set_thread_status_feed_entry(
3337
+ thread_status if active else next_status
3338
+ ):
3339
+ self._render_feed()
3340
+ self._scroll_to_end()
3186
3341
 
3187
3342
  if status_changed:
3188
3343
  self._render_status_line()
@@ -3718,7 +3873,7 @@ async def _run_ask_tui(
3718
3873
  interactive=True,
3719
3874
  preamble_rule=preamble_rule,
3720
3875
  ) as created_session:
3721
- await _run_app(created_session.thread_session)
3876
+ await _run_app(created_session)
3722
3877
 
3723
3878
 
3724
3879
  @app.async_command("ask", help="Send a one-shot LLM prompt.")