meshagent-cli 0.28.2__tar.gz → 0.28.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.
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/PKG-INFO +13 -13
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/chatbot.py +141 -4
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/helper.py +16 -0
- meshagent_cli-0.28.4/meshagent/cli/helper_test.py +33 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/mailbot.py +83 -2
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/port.py +42 -4
- meshagent_cli-0.28.4/meshagent/cli/port_test.py +38 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/task_runner.py +99 -1
- meshagent_cli-0.28.4/meshagent/cli/version.py +1 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/worker.py +82 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent_cli.egg-info/PKG-INFO +13 -13
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent_cli.egg-info/SOURCES.txt +2 -0
- meshagent_cli-0.28.4/meshagent_cli.egg-info/requires.txt +29 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/pyproject.toml +12 -12
- meshagent_cli-0.28.2/meshagent/cli/version.py +0 -1
- meshagent_cli-0.28.2/meshagent_cli.egg-info/requires.txt +0 -29
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/README.md +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/__init__.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/agent.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/api_keys.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/async_typer.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/auth.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/auth_async.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/call.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/cli.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/cli_mcp.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/cli_secrets.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/codex.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/common_options.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/containers.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/database.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/developer.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/helpers.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/host.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/mailboxes.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/meeting_transcriber.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/memory.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/messaging.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/multi.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/oauth2.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/participant_token.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/projects.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/queue.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/room.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/room_services.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/rooms.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/routes.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/services.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/sessions.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/storage.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/sync.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/test.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/voicebot.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/webhook.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent/cli/webserver.py +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent_cli.egg-info/dependency_links.txt +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent_cli.egg-info/entry_points.txt +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/meshagent_cli.egg-info/top_level.txt +0 -0
- {meshagent_cli-0.28.2 → meshagent_cli-0.28.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshagent-cli
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.4
|
|
4
4
|
Summary: CLI for Meshagent
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.meshagent.com
|
|
@@ -19,21 +19,21 @@ Requires-Dist: rich~=14.3.0
|
|
|
19
19
|
Requires-Dist: textual<2.0,>=0.50
|
|
20
20
|
Requires-Dist: prompt-toolkit~=3.0.52
|
|
21
21
|
Provides-Extra: all
|
|
22
|
-
Requires-Dist: meshagent-agents[all]~=0.28.
|
|
23
|
-
Requires-Dist: meshagent-api[all]~=0.28.
|
|
24
|
-
Requires-Dist: meshagent-computers~=0.28.
|
|
25
|
-
Requires-Dist: meshagent-openai~=0.28.
|
|
26
|
-
Requires-Dist: meshagent-anthropic~=0.28.
|
|
27
|
-
Requires-Dist: meshagent-codex~=0.28.
|
|
28
|
-
Requires-Dist: meshagent-mcp~=0.28.
|
|
29
|
-
Requires-Dist: meshagent-tools~=0.28.
|
|
22
|
+
Requires-Dist: meshagent-agents[all]~=0.28.4; extra == "all"
|
|
23
|
+
Requires-Dist: meshagent-api[all]~=0.28.4; extra == "all"
|
|
24
|
+
Requires-Dist: meshagent-computers~=0.28.4; extra == "all"
|
|
25
|
+
Requires-Dist: meshagent-openai~=0.28.4; extra == "all"
|
|
26
|
+
Requires-Dist: meshagent-anthropic~=0.28.4; extra == "all"
|
|
27
|
+
Requires-Dist: meshagent-codex~=0.28.4; extra == "all"
|
|
28
|
+
Requires-Dist: meshagent-mcp~=0.28.4; extra == "all"
|
|
29
|
+
Requires-Dist: meshagent-tools~=0.28.4; extra == "all"
|
|
30
30
|
Requires-Dist: supabase-auth~=2.28.0; extra == "all"
|
|
31
31
|
Requires-Dist: prompt-toolkit~=3.0.52; extra == "all"
|
|
32
32
|
Provides-Extra: mcp-service
|
|
33
|
-
Requires-Dist: meshagent-agents[all]~=0.28.
|
|
34
|
-
Requires-Dist: meshagent-api~=0.28.
|
|
35
|
-
Requires-Dist: meshagent-mcp~=0.28.
|
|
36
|
-
Requires-Dist: meshagent-tools~=0.28.
|
|
33
|
+
Requires-Dist: meshagent-agents[all]~=0.28.4; extra == "mcp-service"
|
|
34
|
+
Requires-Dist: meshagent-api~=0.28.4; extra == "mcp-service"
|
|
35
|
+
Requires-Dist: meshagent-mcp~=0.28.4; extra == "mcp-service"
|
|
36
|
+
Requires-Dist: meshagent-tools~=0.28.4; extra == "mcp-service"
|
|
37
37
|
Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
|
|
38
38
|
|
|
39
39
|
# [Meshagent](https://www.meshagent.com)
|
|
@@ -7,6 +7,7 @@ from meshagent.tools import (
|
|
|
7
7
|
WebFetchTool,
|
|
8
8
|
WebFetchToolkitBuilder,
|
|
9
9
|
ContainerShellTool,
|
|
10
|
+
MemoriesToolkit,
|
|
10
11
|
)
|
|
11
12
|
from meshagent.tools.storage import (
|
|
12
13
|
StorageToolMount,
|
|
@@ -40,6 +41,7 @@ from meshagent.cli.helper import (
|
|
|
40
41
|
cleanup_args,
|
|
41
42
|
get_client,
|
|
42
43
|
parse_shell_tool_mounts,
|
|
44
|
+
parse_memory_selector,
|
|
43
45
|
parse_storage_tool_mounts,
|
|
44
46
|
resolve_key,
|
|
45
47
|
resolve_project_id,
|
|
@@ -250,6 +252,8 @@ def build_chatbot(
|
|
|
250
252
|
require_read_only_storage: Optional[str] = None,
|
|
251
253
|
require_time: bool = True,
|
|
252
254
|
require_uuid: bool = False,
|
|
255
|
+
use_memory: Optional[str] = None,
|
|
256
|
+
memory_model: Optional[str] = None,
|
|
253
257
|
rules_file: Optional[list[str]] = None,
|
|
254
258
|
room_rules_path: Optional[list[str]] = None,
|
|
255
259
|
require_discovery: Optional[str] = None,
|
|
@@ -316,6 +320,10 @@ def build_chatbot(
|
|
|
316
320
|
)
|
|
317
321
|
raise typer.Exit(1)
|
|
318
322
|
|
|
323
|
+
memory_selection: Optional[tuple[str, Optional[list[str]]]] = None
|
|
324
|
+
if use_memory is not None:
|
|
325
|
+
memory_selection = parse_memory_selector(use_memory)
|
|
326
|
+
|
|
319
327
|
BaseClass = ChatBot
|
|
320
328
|
decision_model = None
|
|
321
329
|
if llm_participant:
|
|
@@ -539,6 +547,16 @@ def build_chatbot(
|
|
|
539
547
|
if require_uuid:
|
|
540
548
|
providers.extend((UUIDToolkit()).tools)
|
|
541
549
|
|
|
550
|
+
if memory_selection is not None:
|
|
551
|
+
memory_name, memory_namespace = memory_selection
|
|
552
|
+
providers.extend(
|
|
553
|
+
MemoriesToolkit(
|
|
554
|
+
memory_name=memory_name,
|
|
555
|
+
namespace=memory_namespace,
|
|
556
|
+
llm_model=memory_model,
|
|
557
|
+
).tools
|
|
558
|
+
)
|
|
559
|
+
|
|
542
560
|
if len(require_table_write) > 0:
|
|
543
561
|
providers.extend(
|
|
544
562
|
(
|
|
@@ -845,6 +863,20 @@ async def join(
|
|
|
845
863
|
help="Enable UUID generation tools",
|
|
846
864
|
),
|
|
847
865
|
] = False,
|
|
866
|
+
use_memory: Annotated[
|
|
867
|
+
Optional[str],
|
|
868
|
+
typer.Option(
|
|
869
|
+
"--use-memory",
|
|
870
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
871
|
+
),
|
|
872
|
+
] = None,
|
|
873
|
+
memory_model: Annotated[
|
|
874
|
+
Optional[str],
|
|
875
|
+
typer.Option(
|
|
876
|
+
"--memory-model",
|
|
877
|
+
help="Model name for memory LLM ingestion",
|
|
878
|
+
),
|
|
879
|
+
] = None,
|
|
848
880
|
require_document_authoring: Annotated[
|
|
849
881
|
Optional[bool],
|
|
850
882
|
typer.Option(..., help="Enable MeshDocument authoring"),
|
|
@@ -916,7 +948,6 @@ async def join(
|
|
|
916
948
|
token = ParticipantToken(
|
|
917
949
|
name=agent_name,
|
|
918
950
|
)
|
|
919
|
-
|
|
920
951
|
token.add_api_grant(ApiScope.agent_default(tunnels=require_computer_use))
|
|
921
952
|
|
|
922
953
|
token.add_role_grant(role=role)
|
|
@@ -969,6 +1000,8 @@ async def join(
|
|
|
969
1000
|
require_read_only_storage=require_read_only_storage,
|
|
970
1001
|
require_time=require_time,
|
|
971
1002
|
require_uuid=require_uuid,
|
|
1003
|
+
use_memory=use_memory,
|
|
1004
|
+
memory_model=memory_model,
|
|
972
1005
|
room_rules_path=room_rules,
|
|
973
1006
|
require_document_authoring=require_document_authoring,
|
|
974
1007
|
require_discovery=require_discovery,
|
|
@@ -1190,6 +1223,20 @@ async def service(
|
|
|
1190
1223
|
help="Enable UUID generation tools",
|
|
1191
1224
|
),
|
|
1192
1225
|
] = False,
|
|
1226
|
+
use_memory: Annotated[
|
|
1227
|
+
Optional[str],
|
|
1228
|
+
typer.Option(
|
|
1229
|
+
"--use-memory",
|
|
1230
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
1231
|
+
),
|
|
1232
|
+
] = None,
|
|
1233
|
+
memory_model: Annotated[
|
|
1234
|
+
Optional[str],
|
|
1235
|
+
typer.Option(
|
|
1236
|
+
"--memory-model",
|
|
1237
|
+
help="Model name for memory LLM ingestion",
|
|
1238
|
+
),
|
|
1239
|
+
] = None,
|
|
1193
1240
|
working_dir: WorkingDirOption = None,
|
|
1194
1241
|
working_directory: WorkingDirectoryAliasOption = None,
|
|
1195
1242
|
require_document_authoring: Annotated[
|
|
@@ -1302,6 +1349,8 @@ async def service(
|
|
|
1302
1349
|
require_read_only_storage=require_read_only_storage,
|
|
1303
1350
|
require_time=require_time,
|
|
1304
1351
|
require_uuid=require_uuid,
|
|
1352
|
+
use_memory=use_memory,
|
|
1353
|
+
memory_model=memory_model,
|
|
1305
1354
|
room_rules_path=room_rules,
|
|
1306
1355
|
working_dir=working_dir,
|
|
1307
1356
|
require_document_authoring=require_document_authoring,
|
|
@@ -1501,6 +1550,20 @@ async def spec(
|
|
|
1501
1550
|
help="Enable UUID generation tools",
|
|
1502
1551
|
),
|
|
1503
1552
|
] = False,
|
|
1553
|
+
use_memory: Annotated[
|
|
1554
|
+
Optional[str],
|
|
1555
|
+
typer.Option(
|
|
1556
|
+
"--use-memory",
|
|
1557
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
1558
|
+
),
|
|
1559
|
+
] = None,
|
|
1560
|
+
memory_model: Annotated[
|
|
1561
|
+
Optional[str],
|
|
1562
|
+
typer.Option(
|
|
1563
|
+
"--memory-model",
|
|
1564
|
+
help="Model name for memory LLM ingestion",
|
|
1565
|
+
),
|
|
1566
|
+
] = None,
|
|
1504
1567
|
working_dir: WorkingDirOption = None,
|
|
1505
1568
|
working_directory: WorkingDirectoryAliasOption = None,
|
|
1506
1569
|
require_document_authoring: Annotated[
|
|
@@ -1612,6 +1675,8 @@ async def spec(
|
|
|
1612
1675
|
require_read_only_storage=require_read_only_storage,
|
|
1613
1676
|
require_time=require_time,
|
|
1614
1677
|
require_uuid=require_uuid,
|
|
1678
|
+
use_memory=use_memory,
|
|
1679
|
+
memory_model=memory_model,
|
|
1615
1680
|
room_rules_path=room_rules,
|
|
1616
1681
|
working_dir=working_dir,
|
|
1617
1682
|
require_document_authoring=require_document_authoring,
|
|
@@ -1822,6 +1887,20 @@ async def deploy(
|
|
|
1822
1887
|
help="Enable UUID generation tools",
|
|
1823
1888
|
),
|
|
1824
1889
|
] = False,
|
|
1890
|
+
use_memory: Annotated[
|
|
1891
|
+
Optional[str],
|
|
1892
|
+
typer.Option(
|
|
1893
|
+
"--use-memory",
|
|
1894
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
1895
|
+
),
|
|
1896
|
+
] = None,
|
|
1897
|
+
memory_model: Annotated[
|
|
1898
|
+
Optional[str],
|
|
1899
|
+
typer.Option(
|
|
1900
|
+
"--memory-model",
|
|
1901
|
+
help="Model name for memory LLM ingestion",
|
|
1902
|
+
),
|
|
1903
|
+
] = None,
|
|
1825
1904
|
working_dir: WorkingDirOption = None,
|
|
1826
1905
|
working_directory: WorkingDirectoryAliasOption = None,
|
|
1827
1906
|
require_document_authoring: Annotated[
|
|
@@ -1940,6 +2019,8 @@ async def deploy(
|
|
|
1940
2019
|
require_read_only_storage=require_read_only_storage,
|
|
1941
2020
|
require_time=require_time,
|
|
1942
2021
|
require_uuid=require_uuid,
|
|
2022
|
+
use_memory=use_memory,
|
|
2023
|
+
memory_model=memory_model,
|
|
1943
2024
|
room_rules_path=room_rules,
|
|
1944
2025
|
working_dir=working_dir,
|
|
1945
2026
|
require_document_authoring=require_document_authoring,
|
|
@@ -2835,7 +2916,11 @@ async def chat_with(
|
|
|
2835
2916
|
return
|
|
2836
2917
|
|
|
2837
2918
|
items = message_nodes[0].get_children()
|
|
2838
|
-
|
|
2919
|
+
has_active_event_nodes = self._thread_has_active_event_nodes(items)
|
|
2920
|
+
thread_status_text = self._active_thread_status_text()
|
|
2921
|
+
self._has_active_events = (
|
|
2922
|
+
has_active_event_nodes or thread_status_text is not None
|
|
2923
|
+
)
|
|
2839
2924
|
selected_before = self._selected_pending_approval()
|
|
2840
2925
|
selected_id = selected_before[0] if selected_before is not None else None
|
|
2841
2926
|
self._pending_approval_items = self._collect_pending_approvals(items)
|
|
@@ -2863,6 +2948,11 @@ async def chat_with(
|
|
|
2863
2948
|
):
|
|
2864
2949
|
rendered_items.append(renderable)
|
|
2865
2950
|
|
|
2951
|
+
if thread_status_text is not None and not has_active_event_nodes:
|
|
2952
|
+
rendered_items.append(
|
|
2953
|
+
self._render_thread_status_item(thread_status_text)
|
|
2954
|
+
)
|
|
2955
|
+
|
|
2866
2956
|
if len(rendered_items) == 0:
|
|
2867
2957
|
self._messages_view.update("")
|
|
2868
2958
|
else:
|
|
@@ -2871,7 +2961,7 @@ async def chat_with(
|
|
|
2871
2961
|
if self._messages_scroll is not None:
|
|
2872
2962
|
self._messages_scroll.scroll_end(animate=False)
|
|
2873
2963
|
|
|
2874
|
-
def
|
|
2964
|
+
def _thread_has_active_event_nodes(self, items) -> bool:
|
|
2875
2965
|
for item in items:
|
|
2876
2966
|
if getattr(item, "tag_name", None) != "event":
|
|
2877
2967
|
continue
|
|
@@ -2880,6 +2970,37 @@ async def chat_with(
|
|
|
2880
2970
|
return True
|
|
2881
2971
|
return False
|
|
2882
2972
|
|
|
2973
|
+
def _active_thread_status_text(self) -> str | None:
|
|
2974
|
+
participant = self._chat_client._participant
|
|
2975
|
+
if participant is None:
|
|
2976
|
+
return None
|
|
2977
|
+
|
|
2978
|
+
status_attr = f"thread.status.text.{self._chat_client.thread_path}"
|
|
2979
|
+
status = participant.get_attribute(status_attr)
|
|
2980
|
+
if not isinstance(status, str):
|
|
2981
|
+
return None
|
|
2982
|
+
|
|
2983
|
+
normalized = status.strip()
|
|
2984
|
+
if normalized == "":
|
|
2985
|
+
return None
|
|
2986
|
+
|
|
2987
|
+
return normalized
|
|
2988
|
+
|
|
2989
|
+
def _render_thread_status_item(self, status_text: str) -> RenderableType:
|
|
2990
|
+
table = Table.grid(expand=True, padding=(0, 0))
|
|
2991
|
+
table.add_column(width=2, no_wrap=True)
|
|
2992
|
+
table.add_column(ratio=1)
|
|
2993
|
+
table.add_column(width=2, no_wrap=True)
|
|
2994
|
+
|
|
2995
|
+
table.add_row(Text(" "), Text(" "), Text(" "))
|
|
2996
|
+
table.add_row(
|
|
2997
|
+
Text(" "),
|
|
2998
|
+
Text(f"{self._event_spinner()} {status_text}", style="bold magenta"),
|
|
2999
|
+
Text(" "),
|
|
3000
|
+
)
|
|
3001
|
+
table.add_row(Text(" "), Text(" "), Text(" "))
|
|
3002
|
+
return table
|
|
3003
|
+
|
|
2883
3004
|
def _collect_pending_approvals(self, items) -> list[tuple[str, str, str]]:
|
|
2884
3005
|
approvals: list[tuple[str, str, str]] = []
|
|
2885
3006
|
seen: set[str] = set()
|
|
@@ -3615,6 +3736,20 @@ async def run(
|
|
|
3615
3736
|
help="Enable UUID generation tools",
|
|
3616
3737
|
),
|
|
3617
3738
|
] = False,
|
|
3739
|
+
use_memory: Annotated[
|
|
3740
|
+
Optional[str],
|
|
3741
|
+
typer.Option(
|
|
3742
|
+
"--use-memory",
|
|
3743
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
3744
|
+
),
|
|
3745
|
+
] = None,
|
|
3746
|
+
memory_model: Annotated[
|
|
3747
|
+
Optional[str],
|
|
3748
|
+
typer.Option(
|
|
3749
|
+
"--memory-model",
|
|
3750
|
+
help="Model name for memory LLM ingestion",
|
|
3751
|
+
),
|
|
3752
|
+
] = None,
|
|
3618
3753
|
require_document_authoring: Annotated[
|
|
3619
3754
|
Optional[bool],
|
|
3620
3755
|
typer.Option(..., help="Enable MeshDocument authoring"),
|
|
@@ -3687,7 +3822,7 @@ async def run(
|
|
|
3687
3822
|
working_dir=working_dir,
|
|
3688
3823
|
working_directory=working_directory,
|
|
3689
3824
|
)
|
|
3690
|
-
if not verbose:
|
|
3825
|
+
if not verbose and not log_llm_requests:
|
|
3691
3826
|
root = logging.getLogger()
|
|
3692
3827
|
root.setLevel(logging.ERROR)
|
|
3693
3828
|
|
|
@@ -3767,6 +3902,8 @@ async def run(
|
|
|
3767
3902
|
require_read_only_storage=require_read_only_storage,
|
|
3768
3903
|
require_time=require_time,
|
|
3769
3904
|
require_uuid=require_uuid,
|
|
3905
|
+
use_memory=use_memory,
|
|
3906
|
+
memory_model=memory_model,
|
|
3770
3907
|
room_rules_path=room_rules,
|
|
3771
3908
|
require_document_authoring=require_document_authoring,
|
|
3772
3909
|
require_discovery=require_discovery,
|
|
@@ -213,6 +213,22 @@ async def resolve_key(project_id: str | None, key: str | None):
|
|
|
213
213
|
return key
|
|
214
214
|
|
|
215
215
|
|
|
216
|
+
def parse_memory_selector(value: str) -> tuple[str, Optional[list[str]]]:
|
|
217
|
+
cleaned = value.strip()
|
|
218
|
+
if cleaned == "":
|
|
219
|
+
raise typer.BadParameter("--use-memory cannot be empty")
|
|
220
|
+
|
|
221
|
+
segments = [segment.strip() for segment in cleaned.split("/")]
|
|
222
|
+
if any(segment == "" for segment in segments):
|
|
223
|
+
raise typer.BadParameter(
|
|
224
|
+
"--use-memory must be '<name>' or '<namespace>/<name>' with no empty segments"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
memory_name = segments[-1]
|
|
228
|
+
namespace = segments[:-1]
|
|
229
|
+
return memory_name, namespace or None
|
|
230
|
+
|
|
231
|
+
|
|
216
232
|
def _split_mount_value(
|
|
217
233
|
value: str, option_name: str, default_read_only: bool
|
|
218
234
|
) -> tuple[str, str, bool]:
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import typer
|
|
3
|
+
|
|
4
|
+
from meshagent.cli.helper import parse_memory_selector
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_parse_memory_selector_name_only() -> None:
|
|
8
|
+
memory_name, namespace = parse_memory_selector("graph")
|
|
9
|
+
|
|
10
|
+
assert memory_name == "graph"
|
|
11
|
+
assert namespace is None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_parse_memory_selector_with_namespace() -> None:
|
|
15
|
+
memory_name, namespace = parse_memory_selector("team/shared/graph")
|
|
16
|
+
|
|
17
|
+
assert memory_name == "graph"
|
|
18
|
+
assert namespace == ["team", "shared"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.parametrize(
|
|
22
|
+
"value",
|
|
23
|
+
[
|
|
24
|
+
"",
|
|
25
|
+
" ",
|
|
26
|
+
"/graph",
|
|
27
|
+
"team/",
|
|
28
|
+
"team//graph",
|
|
29
|
+
],
|
|
30
|
+
)
|
|
31
|
+
def test_parse_memory_selector_rejects_empty_segments(value: str) -> None:
|
|
32
|
+
with pytest.raises(typer.BadParameter):
|
|
33
|
+
parse_memory_selector(value)
|
|
@@ -9,13 +9,14 @@ from meshagent.cli.common_options import (
|
|
|
9
9
|
ProjectIdOption,
|
|
10
10
|
RoomOption,
|
|
11
11
|
)
|
|
12
|
-
from meshagent.tools import Toolkit, WebFetchTool, ContainerShellTool
|
|
12
|
+
from meshagent.tools import Toolkit, WebFetchTool, ContainerShellTool, MemoriesToolkit
|
|
13
13
|
from meshagent.api import RoomClient, WebSocketClientProtocol, ApiScope
|
|
14
14
|
from meshagent.api.helpers import websocket_room_url
|
|
15
15
|
from meshagent.cli.helper import (
|
|
16
16
|
cleanup_args,
|
|
17
17
|
get_client,
|
|
18
18
|
parse_shell_tool_mounts,
|
|
19
|
+
parse_memory_selector,
|
|
19
20
|
parse_storage_tool_mounts,
|
|
20
21
|
resolve_key,
|
|
21
22
|
resolve_project_id,
|
|
@@ -212,6 +213,8 @@ def build_mailbot(
|
|
|
212
213
|
shell_tool_mounts: Optional[ContainerMountSpec] = None,
|
|
213
214
|
require_time: bool = True,
|
|
214
215
|
require_uuid: bool = False,
|
|
216
|
+
use_memory: Optional[str] = None,
|
|
217
|
+
memory_model: Optional[str] = None,
|
|
215
218
|
require_table_read: bool,
|
|
216
219
|
require_table_write: bool,
|
|
217
220
|
require_computer_use: bool,
|
|
@@ -257,6 +260,10 @@ def build_mailbot(
|
|
|
257
260
|
supports_openai_tools = llm_participant is None and not is_claude_model
|
|
258
261
|
base_shell_env = _copy_shell_env_vars(copy_env=shell_copy_env)
|
|
259
262
|
base_shell_env.update(_set_shell_env_vars(set_env=shell_set_env))
|
|
263
|
+
memory_selection: Optional[tuple[str, Optional[list[str]]]] = None
|
|
264
|
+
if use_memory is not None:
|
|
265
|
+
memory_selection = parse_memory_selector(use_memory)
|
|
266
|
+
|
|
260
267
|
if not supports_openai_tools:
|
|
261
268
|
if image_generation:
|
|
262
269
|
print("image generation tool is only supported by openai models")
|
|
@@ -506,6 +513,16 @@ def build_mailbot(
|
|
|
506
513
|
if require_uuid:
|
|
507
514
|
thread_toolkit.tools.extend(UUIDToolkit().tools)
|
|
508
515
|
|
|
516
|
+
if memory_selection is not None:
|
|
517
|
+
memory_name, memory_namespace = memory_selection
|
|
518
|
+
thread_toolkit.tools.extend(
|
|
519
|
+
MemoriesToolkit(
|
|
520
|
+
memory_name=memory_name,
|
|
521
|
+
namespace=memory_namespace,
|
|
522
|
+
llm_model=memory_model,
|
|
523
|
+
).tools
|
|
524
|
+
)
|
|
525
|
+
|
|
509
526
|
if require_computer_use:
|
|
510
527
|
from meshagent.computers.agent import ComputerToolkit
|
|
511
528
|
|
|
@@ -670,6 +687,20 @@ async def join(
|
|
|
670
687
|
help="Enable UUID generation tools",
|
|
671
688
|
),
|
|
672
689
|
] = False,
|
|
690
|
+
use_memory: Annotated[
|
|
691
|
+
Optional[str],
|
|
692
|
+
typer.Option(
|
|
693
|
+
"--use-memory",
|
|
694
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
695
|
+
),
|
|
696
|
+
] = None,
|
|
697
|
+
memory_model: Annotated[
|
|
698
|
+
Optional[str],
|
|
699
|
+
typer.Option(
|
|
700
|
+
"--memory-model",
|
|
701
|
+
help="Model name for memory LLM ingestion",
|
|
702
|
+
),
|
|
703
|
+
] = None,
|
|
673
704
|
database_namespace: Annotated[
|
|
674
705
|
Optional[str],
|
|
675
706
|
typer.Option(..., help="Use a specific database namespace"),
|
|
@@ -792,6 +823,8 @@ async def join(
|
|
|
792
823
|
shell_tool_mounts=shell_tool_mounts,
|
|
793
824
|
require_time=require_time,
|
|
794
825
|
require_uuid=require_uuid,
|
|
826
|
+
use_memory=use_memory,
|
|
827
|
+
memory_model=memory_model,
|
|
795
828
|
require_table_read=require_table_read,
|
|
796
829
|
require_table_write=require_table_write,
|
|
797
830
|
require_computer_use=require_computer_use,
|
|
@@ -978,6 +1011,20 @@ async def service(
|
|
|
978
1011
|
help="Enable UUID generation tools",
|
|
979
1012
|
),
|
|
980
1013
|
] = False,
|
|
1014
|
+
use_memory: Annotated[
|
|
1015
|
+
Optional[str],
|
|
1016
|
+
typer.Option(
|
|
1017
|
+
"--use-memory",
|
|
1018
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
1019
|
+
),
|
|
1020
|
+
] = None,
|
|
1021
|
+
memory_model: Annotated[
|
|
1022
|
+
Optional[str],
|
|
1023
|
+
typer.Option(
|
|
1024
|
+
"--memory-model",
|
|
1025
|
+
help="Model name for memory LLM ingestion",
|
|
1026
|
+
),
|
|
1027
|
+
] = None,
|
|
981
1028
|
database_namespace: Annotated[
|
|
982
1029
|
Optional[str],
|
|
983
1030
|
typer.Option(..., help="Use a specific database namespace"),
|
|
@@ -1081,6 +1128,8 @@ async def service(
|
|
|
1081
1128
|
shell_tool_mounts=shell_tool_mounts,
|
|
1082
1129
|
require_time=require_time,
|
|
1083
1130
|
require_uuid=require_uuid,
|
|
1131
|
+
use_memory=use_memory,
|
|
1132
|
+
memory_model=memory_model,
|
|
1084
1133
|
require_table_read=require_table_read,
|
|
1085
1134
|
require_table_write=require_table_write,
|
|
1086
1135
|
require_computer_use=require_computer_use,
|
|
@@ -1254,6 +1303,20 @@ async def spec(
|
|
|
1254
1303
|
help="Enable UUID generation tools",
|
|
1255
1304
|
),
|
|
1256
1305
|
] = False,
|
|
1306
|
+
use_memory: Annotated[
|
|
1307
|
+
Optional[str],
|
|
1308
|
+
typer.Option(
|
|
1309
|
+
"--use-memory",
|
|
1310
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
1311
|
+
),
|
|
1312
|
+
] = None,
|
|
1313
|
+
memory_model: Annotated[
|
|
1314
|
+
Optional[str],
|
|
1315
|
+
typer.Option(
|
|
1316
|
+
"--memory-model",
|
|
1317
|
+
help="Model name for memory LLM ingestion",
|
|
1318
|
+
),
|
|
1319
|
+
] = None,
|
|
1257
1320
|
database_namespace: Annotated[
|
|
1258
1321
|
Optional[str],
|
|
1259
1322
|
typer.Option(..., help="Use a specific database namespace"),
|
|
@@ -1357,6 +1420,8 @@ async def spec(
|
|
|
1357
1420
|
shell_tool_mounts=shell_tool_mounts,
|
|
1358
1421
|
require_time=require_time,
|
|
1359
1422
|
require_uuid=require_uuid,
|
|
1423
|
+
use_memory=use_memory,
|
|
1424
|
+
memory_model=memory_model,
|
|
1360
1425
|
require_table_read=require_table_read,
|
|
1361
1426
|
require_table_write=require_table_write,
|
|
1362
1427
|
require_computer_use=require_computer_use,
|
|
@@ -1541,6 +1606,20 @@ async def deploy(
|
|
|
1541
1606
|
help="Enable UUID generation tools",
|
|
1542
1607
|
),
|
|
1543
1608
|
] = False,
|
|
1609
|
+
use_memory: Annotated[
|
|
1610
|
+
Optional[str],
|
|
1611
|
+
typer.Option(
|
|
1612
|
+
"--use-memory",
|
|
1613
|
+
help="Use memories toolkit for <name> or <namespace>/<name>",
|
|
1614
|
+
),
|
|
1615
|
+
] = None,
|
|
1616
|
+
memory_model: Annotated[
|
|
1617
|
+
Optional[str],
|
|
1618
|
+
typer.Option(
|
|
1619
|
+
"--memory-model",
|
|
1620
|
+
help="Model name for memory LLM ingestion",
|
|
1621
|
+
),
|
|
1622
|
+
] = None,
|
|
1544
1623
|
database_namespace: Annotated[
|
|
1545
1624
|
Optional[str],
|
|
1546
1625
|
typer.Option(..., help="Use a specific database namespace"),
|
|
@@ -1651,6 +1730,8 @@ async def deploy(
|
|
|
1651
1730
|
shell_tool_mounts=shell_tool_mounts,
|
|
1652
1731
|
require_time=require_time,
|
|
1653
1732
|
require_uuid=require_uuid,
|
|
1733
|
+
use_memory=use_memory,
|
|
1734
|
+
memory_model=memory_model,
|
|
1654
1735
|
require_table_read=require_table_read,
|
|
1655
1736
|
require_table_write=require_table_write,
|
|
1656
1737
|
require_computer_use=require_computer_use,
|
|
@@ -1677,7 +1758,7 @@ async def deploy(
|
|
|
1677
1758
|
spec.metadata.description = service_description
|
|
1678
1759
|
spec.container.image = "meshagent/cli:default"
|
|
1679
1760
|
spec.container.command = shlex.join(
|
|
1680
|
-
["meshagent", "mailbot", *cleanup_args(sys.argv[:
|
|
1761
|
+
["meshagent", "mailbot", "service", *cleanup_args(sys.argv[2:])]
|
|
1681
1762
|
)
|
|
1682
1763
|
|
|
1683
1764
|
client = await get_client()
|
|
@@ -8,6 +8,7 @@ from meshagent.api.helpers import websocket_room_url
|
|
|
8
8
|
from typing import Annotated, Optional
|
|
9
9
|
|
|
10
10
|
import typer
|
|
11
|
+
from rich import print
|
|
11
12
|
|
|
12
13
|
from meshagent.cli import async_typer
|
|
13
14
|
from meshagent.cli.common_options import ProjectIdOption
|
|
@@ -18,6 +19,41 @@ from meshagent.api.port_forward import port_forward
|
|
|
18
19
|
app = async_typer.AsyncTyper(help="Port forwarding into room containers")
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
def _parse_port_mapping(value: str) -> tuple[int, int]:
|
|
23
|
+
raw = value.strip()
|
|
24
|
+
if raw == "":
|
|
25
|
+
raise typer.BadParameter("--port cannot be empty")
|
|
26
|
+
|
|
27
|
+
parts = [part.strip() for part in raw.split(":")]
|
|
28
|
+
if len(parts) != 2:
|
|
29
|
+
raise typer.BadParameter("Expected --port in LOCAL:REMOTE format")
|
|
30
|
+
|
|
31
|
+
local_raw, remote_raw = parts
|
|
32
|
+
if local_raw == "" or remote_raw == "":
|
|
33
|
+
raise typer.BadParameter("Expected --port in LOCAL:REMOTE format")
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
local_port = int(local_raw)
|
|
37
|
+
except ValueError as exc:
|
|
38
|
+
raise typer.BadParameter(
|
|
39
|
+
f"LOCAL port must be an integer, got: {local_raw}"
|
|
40
|
+
) from exc
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
remote_port = int(remote_raw)
|
|
44
|
+
except ValueError as exc:
|
|
45
|
+
raise typer.BadParameter(
|
|
46
|
+
f"REMOTE port must be an integer, got: {remote_raw}"
|
|
47
|
+
) from exc
|
|
48
|
+
|
|
49
|
+
if not (0 <= local_port <= 65535):
|
|
50
|
+
raise typer.BadParameter("LOCAL port must be between 0 and 65535")
|
|
51
|
+
if not (1 <= remote_port <= 65535):
|
|
52
|
+
raise typer.BadParameter("REMOTE port must be between 1 and 65535")
|
|
53
|
+
|
|
54
|
+
return local_port, remote_port
|
|
55
|
+
|
|
56
|
+
|
|
21
57
|
@app.async_command("forward", help="Forward a container port to localhost")
|
|
22
58
|
async def forward(
|
|
23
59
|
*,
|
|
@@ -59,12 +95,11 @@ async def forward(
|
|
|
59
95
|
|
|
60
96
|
client = await get_client()
|
|
61
97
|
try:
|
|
98
|
+
local_port, remote_port = _parse_port_mapping(port)
|
|
62
99
|
project_id = await resolve_project_id(project_id)
|
|
63
100
|
|
|
64
101
|
connection = await client.connect_room(project_id=project_id, room=room)
|
|
65
102
|
|
|
66
|
-
ports = port.split(":")
|
|
67
|
-
|
|
68
103
|
if name is not None:
|
|
69
104
|
async with RoomClient(
|
|
70
105
|
protocol=WebSocketClientProtocol(
|
|
@@ -86,8 +121,8 @@ async def forward(
|
|
|
86
121
|
raise typer.Exit(1)
|
|
87
122
|
|
|
88
123
|
handler = await port_forward(
|
|
89
|
-
listen_port=
|
|
90
|
-
port=
|
|
124
|
+
listen_port=local_port,
|
|
125
|
+
port=remote_port,
|
|
91
126
|
container_id=container_id,
|
|
92
127
|
token=connection.jwt,
|
|
93
128
|
)
|
|
@@ -95,6 +130,9 @@ async def forward(
|
|
|
95
130
|
await asyncio.sleep(10000)
|
|
96
131
|
|
|
97
132
|
await handler.close()
|
|
133
|
+
except typer.BadParameter as exc:
|
|
134
|
+
print(f"[red]{exc}[/red]")
|
|
135
|
+
raise typer.Exit(1) from exc
|
|
98
136
|
|
|
99
137
|
finally:
|
|
100
138
|
await client.close()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import typer
|
|
3
|
+
|
|
4
|
+
from meshagent.cli.port import _parse_port_mapping
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
("value", "expected"),
|
|
9
|
+
[
|
|
10
|
+
("3000:3000", (3000, 3000)),
|
|
11
|
+
("0:3000", (0, 3000)),
|
|
12
|
+
(" 8080 : 80 ", (8080, 80)),
|
|
13
|
+
],
|
|
14
|
+
)
|
|
15
|
+
def test_parse_port_mapping_accepts_valid_values(
|
|
16
|
+
value: str, expected: tuple[int, int]
|
|
17
|
+
) -> None:
|
|
18
|
+
assert _parse_port_mapping(value) == expected
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.parametrize(
|
|
22
|
+
"value",
|
|
23
|
+
[
|
|
24
|
+
"",
|
|
25
|
+
"3000",
|
|
26
|
+
":3000",
|
|
27
|
+
"3000:",
|
|
28
|
+
"abc:3000",
|
|
29
|
+
"3000:abc",
|
|
30
|
+
"-1:3000",
|
|
31
|
+
"70000:3000",
|
|
32
|
+
"3000:0",
|
|
33
|
+
"3000:70000",
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
def test_parse_port_mapping_rejects_invalid_values(value: str) -> None:
|
|
37
|
+
with pytest.raises(typer.BadParameter):
|
|
38
|
+
_parse_port_mapping(value)
|