meshagent-cli 0.22.2__py3-none-any.whl

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.

Potentially problematic release.


This version of meshagent-cli might be problematic. Click here for more details.

Files changed (45) hide show
  1. meshagent/cli/__init__.py +3 -0
  2. meshagent/cli/agent.py +273 -0
  3. meshagent/cli/api_keys.py +102 -0
  4. meshagent/cli/async_typer.py +79 -0
  5. meshagent/cli/auth.py +30 -0
  6. meshagent/cli/auth_async.py +295 -0
  7. meshagent/cli/call.py +215 -0
  8. meshagent/cli/chatbot.py +1983 -0
  9. meshagent/cli/cli.py +187 -0
  10. meshagent/cli/cli_mcp.py +408 -0
  11. meshagent/cli/cli_secrets.py +414 -0
  12. meshagent/cli/common_options.py +47 -0
  13. meshagent/cli/containers.py +725 -0
  14. meshagent/cli/database.py +997 -0
  15. meshagent/cli/developer.py +70 -0
  16. meshagent/cli/exec.py +397 -0
  17. meshagent/cli/helper.py +236 -0
  18. meshagent/cli/helpers.py +185 -0
  19. meshagent/cli/host.py +41 -0
  20. meshagent/cli/mailbot.py +1295 -0
  21. meshagent/cli/mailboxes.py +223 -0
  22. meshagent/cli/meeting_transcriber.py +138 -0
  23. meshagent/cli/messaging.py +157 -0
  24. meshagent/cli/multi.py +357 -0
  25. meshagent/cli/oauth2.py +341 -0
  26. meshagent/cli/participant_token.py +63 -0
  27. meshagent/cli/port.py +70 -0
  28. meshagent/cli/projects.py +105 -0
  29. meshagent/cli/queue.py +91 -0
  30. meshagent/cli/room.py +26 -0
  31. meshagent/cli/rooms.py +214 -0
  32. meshagent/cli/services.py +722 -0
  33. meshagent/cli/sessions.py +26 -0
  34. meshagent/cli/storage.py +813 -0
  35. meshagent/cli/sync.py +434 -0
  36. meshagent/cli/task_runner.py +1317 -0
  37. meshagent/cli/version.py +1 -0
  38. meshagent/cli/voicebot.py +624 -0
  39. meshagent/cli/webhook.py +100 -0
  40. meshagent/cli/worker.py +1403 -0
  41. meshagent_cli-0.22.2.dist-info/METADATA +49 -0
  42. meshagent_cli-0.22.2.dist-info/RECORD +45 -0
  43. meshagent_cli-0.22.2.dist-info/WHEEL +5 -0
  44. meshagent_cli-0.22.2.dist-info/entry_points.txt +2 -0
  45. meshagent_cli-0.22.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1983 @@
1
+ import typer
2
+ from rich import print
3
+ from typing import Annotated, Optional, List
4
+ from meshagent.tools import Toolkit, ToolkitConfig
5
+ from meshagent.tools.storage import StorageToolkitBuilder, StorageToolkitConfig
6
+ from meshagent.tools.datetime import DatetimeToolkit
7
+ from meshagent.tools.uuid import UUIDToolkit
8
+ from meshagent.tools.document_tools import (
9
+ DocumentAuthoringToolkit,
10
+ DocumentTypeAuthoringToolkit,
11
+ )
12
+ from meshagent.agents.config import RulesConfig
13
+ from meshagent.agents.widget_schema import widget_schema
14
+
15
+ from meshagent.cli.common_options import (
16
+ ProjectIdOption,
17
+ RoomOption,
18
+ )
19
+ from meshagent.api import (
20
+ RoomClient,
21
+ WebSocketClientProtocol,
22
+ ParticipantToken,
23
+ ApiScope,
24
+ RoomException,
25
+ RemoteParticipant,
26
+ )
27
+ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
28
+ from meshagent.cli import async_typer
29
+ from meshagent.cli.helper import (
30
+ get_client,
31
+ resolve_project_id,
32
+ resolve_room,
33
+ resolve_key,
34
+ cleanup_args,
35
+ )
36
+
37
+ from meshagent.openai import OpenAIResponsesAdapter
38
+ from meshagent.anthropic import AnthropicOpenAIResponsesStreamAdapter
39
+
40
+ from pathlib import Path
41
+
42
+ from meshagent.openai.tools.responses_adapter import (
43
+ WebSearchToolkitBuilder,
44
+ MCPToolkitBuilder,
45
+ WebSearchTool,
46
+ LocalShellConfig,
47
+ ShellConfig,
48
+ WebSearchConfig,
49
+ ApplyPatchConfig,
50
+ ApplyPatchTool,
51
+ ApplyPatchToolkitBuilder,
52
+ ShellToolkitBuilder,
53
+ ShellTool,
54
+ LocalShellToolkitBuilder,
55
+ LocalShellTool,
56
+ ImageGenerationConfig,
57
+ ImageGenerationToolkitBuilder,
58
+ ImageGenerationTool,
59
+ )
60
+
61
+ from meshagent.tools.database import DatabaseToolkitBuilder, DatabaseToolkitConfig
62
+ from meshagent.agents.adapter import MessageStreamLLMAdapter
63
+
64
+ from meshagent.api import RequiredToolkit, RequiredSchema
65
+ import logging
66
+ import os.path
67
+
68
+ from meshagent.api.specs.service import AgentSpec, ANNOTATION_AGENT_TYPE
69
+
70
+ from meshagent.cli.host import get_service, run_services, get_deferred, service_specs
71
+
72
+ import yaml
73
+
74
+ import shlex
75
+ import sys
76
+
77
+ import asyncio
78
+
79
+
80
+ from meshagent.api.client import ConflictError
81
+
82
+ logger = logging.getLogger("chatbot")
83
+
84
+ app = async_typer.AsyncTyper(help="Join a chatbot to a room")
85
+
86
+
87
+ def build_chatbot(
88
+ *,
89
+ model: str,
90
+ rule: List[str],
91
+ toolkit: List[str],
92
+ schema: List[str],
93
+ image_generation: Optional[str] = None,
94
+ local_shell: Optional[str] = None,
95
+ shell: Optional[str] = None,
96
+ apply_patch: Optional[str] = None,
97
+ computer_use: Optional[str] = None,
98
+ web_search: Optional[str] = None,
99
+ mcp: Optional[str] = None,
100
+ storage: Optional[str] = None,
101
+ require_image_generation: Optional[str] = None,
102
+ require_local_shell: Optional[str] = None,
103
+ require_shell: Optional[bool] = None,
104
+ require_apply_patch: Optional[str] = None,
105
+ require_computer_use: Optional[str] = None,
106
+ require_web_search: Optional[str] = None,
107
+ require_mcp: Optional[str] = None,
108
+ require_storage: Optional[str] = None,
109
+ require_table_read: list[str] = None,
110
+ require_table_write: list[str] = None,
111
+ require_read_only_storage: Optional[str] = None,
112
+ require_time: bool = True,
113
+ require_uuid: bool = False,
114
+ rules_file: Optional[str] = None,
115
+ room_rules_path: Optional[list[str]] = None,
116
+ require_discovery: Optional[str] = None,
117
+ require_document_authoring: Optional[str] = None,
118
+ working_directory: Optional[str] = None,
119
+ llm_participant: Optional[str] = None,
120
+ database_namespace: Optional[list[str]] = None,
121
+ always_reply: Optional[bool] = None,
122
+ skill_dirs: Optional[list[str]] = None,
123
+ shell_image: Optional[str] = None,
124
+ log_llm_requests: Optional[bool] = None,
125
+ delegate_shell_token: Optional[bool] = None,
126
+ ):
127
+ from meshagent.agents.chat import ChatBot
128
+
129
+ from meshagent.tools.storage import StorageToolkit
130
+
131
+ requirements = []
132
+
133
+ toolkits = []
134
+
135
+ for t in toolkit:
136
+ requirements.append(RequiredToolkit(name=t))
137
+
138
+ for t in schema:
139
+ requirements.append(RequiredSchema(name=t))
140
+
141
+ client_rules = {}
142
+
143
+ if rules_file is not None:
144
+ try:
145
+ with open(Path(os.path.expanduser(rules_file)).resolve(), "r") as f:
146
+ rules_config = RulesConfig.parse(f.read())
147
+ rule.extend(rules_config.rules)
148
+ client_rules = rules_config.client_rules
149
+
150
+ except FileNotFoundError:
151
+ print(f"[yellow]rules file not found at {rules_file}[/yellow]")
152
+
153
+ BaseClass = ChatBot
154
+ decision_model = None
155
+ if llm_participant:
156
+ llm_adapter = MessageStreamLLMAdapter(
157
+ participant_name=llm_participant,
158
+ )
159
+ else:
160
+ if computer_use or require_computer_use:
161
+ llm_adapter = OpenAIResponsesAdapter(
162
+ model=model,
163
+ response_options={
164
+ "reasoning": {"summary": "concise"},
165
+ "truncation": "auto",
166
+ },
167
+ log_requests=log_llm_requests,
168
+ )
169
+ else:
170
+ if model.startswith("claude-"):
171
+ llm_adapter = AnthropicOpenAIResponsesStreamAdapter(
172
+ model=model,
173
+ log_requests=log_llm_requests,
174
+ )
175
+ decision_model = model
176
+ else:
177
+ llm_adapter = OpenAIResponsesAdapter(
178
+ model=model,
179
+ log_requests=log_llm_requests,
180
+ )
181
+
182
+ class CustomChatbot(BaseClass):
183
+ def __init__(self):
184
+ super().__init__(
185
+ llm_adapter=llm_adapter,
186
+ requires=requirements,
187
+ toolkits=toolkits,
188
+ rules=rule if len(rule) > 0 else None,
189
+ client_rules=client_rules,
190
+ always_reply=always_reply,
191
+ skill_dirs=skill_dirs,
192
+ decision_model=decision_model,
193
+ )
194
+
195
+ async def start(self, *, room: RoomClient):
196
+ await super().start(room=room)
197
+
198
+ if room_rules_path is not None:
199
+ for p in room_rules_path:
200
+ await self._load_room_rules(path=p)
201
+
202
+ async def init_chat_context(self):
203
+ from meshagent.cli.helper import init_context_from_spec
204
+
205
+ context = await super().init_chat_context()
206
+ await init_context_from_spec(context)
207
+
208
+ return context
209
+
210
+ async def _load_room_rules(
211
+ self,
212
+ *,
213
+ path: str,
214
+ participant: Optional[RemoteParticipant] = None,
215
+ ):
216
+ rules = []
217
+ try:
218
+ room_rules = await self.room.storage.download(path=path)
219
+
220
+ rules_txt = room_rules.data.decode()
221
+
222
+ rules_config = RulesConfig.parse(rules_txt)
223
+
224
+ if rules_config.rules is not None:
225
+ rules.extend(rules_config.rules)
226
+
227
+ if participant is not None:
228
+ client = participant.get_attribute("client")
229
+
230
+ if rules_config.client_rules is not None and client is not None:
231
+ cr = rules_config.client_rules.get(client)
232
+ if cr is not None:
233
+ rules.extend(cr)
234
+
235
+ except RoomException:
236
+ try:
237
+ logger.info("attempting to initialize rules file")
238
+ handle = await self.room.storage.open(path=path, overwrite=False)
239
+ await self.room.storage.write(
240
+ handle=handle,
241
+ data="# Add rules to this file to customize your agent's behavior, lines starting with # will be ignored.\n\n".encode(),
242
+ )
243
+ await self.room.storage.close(handle=handle)
244
+
245
+ except RoomException:
246
+ pass
247
+ logger.info(
248
+ f"unable to load rules from {path}, continuing with default rules"
249
+ )
250
+ pass
251
+
252
+ return rules
253
+
254
+ async def get_rules(self, *, thread_context, participant):
255
+ rules = await super().get_rules(
256
+ thread_context=thread_context, participant=participant
257
+ )
258
+
259
+ if room_rules_path is not None:
260
+ for p in room_rules_path:
261
+ rules.extend(
262
+ await self._load_room_rules(path=p, participant=participant)
263
+ )
264
+
265
+ logging.info(f"using rules {rules}")
266
+
267
+ return rules
268
+
269
+ async def get_thread_toolkits(self, *, thread_context, participant):
270
+ providers = []
271
+
272
+ if require_image_generation:
273
+ providers.append(
274
+ ImageGenerationTool(
275
+ config=ImageGenerationConfig(
276
+ name="image_generation",
277
+ partial_images=3,
278
+ ),
279
+ )
280
+ )
281
+
282
+ if require_local_shell:
283
+ providers.append(
284
+ LocalShellTool(
285
+ working_directory=working_directory,
286
+ config=LocalShellConfig(name="local_shell"),
287
+ )
288
+ )
289
+
290
+ env = {}
291
+
292
+ if delegate_shell_token:
293
+ env["MESHAGENT_TOKEN"] = self.room.protocol.token
294
+
295
+ if require_shell:
296
+ providers.append(
297
+ ShellTool(
298
+ working_directory=working_directory,
299
+ config=ShellConfig(name="shell"),
300
+ image=shell_image or "python:3.13",
301
+ env=env,
302
+ )
303
+ )
304
+
305
+ if require_apply_patch:
306
+ providers.append(
307
+ ApplyPatchTool(
308
+ config=ApplyPatchConfig(name="apply_patch"),
309
+ )
310
+ )
311
+
312
+ if require_mcp:
313
+ raise Exception(
314
+ "mcp tool cannot be required by cli currently, use 'optional' instead"
315
+ )
316
+
317
+ if require_web_search:
318
+ providers.append(
319
+ WebSearchTool(config=WebSearchConfig(name="web_search"))
320
+ )
321
+
322
+ if require_storage:
323
+ providers.extend(StorageToolkit().tools)
324
+
325
+ if len(require_table_read) > 0:
326
+ providers.extend(
327
+ (
328
+ await DatabaseToolkitBuilder().make(
329
+ room=self.room,
330
+ model=model,
331
+ config=DatabaseToolkitConfig(
332
+ tables=require_table_read,
333
+ read_only=True,
334
+ namespace=database_namespace,
335
+ ),
336
+ )
337
+ ).tools
338
+ )
339
+
340
+ if require_time:
341
+ providers.extend((DatetimeToolkit()).tools)
342
+
343
+ if require_uuid:
344
+ providers.extend((UUIDToolkit()).tools)
345
+
346
+ if len(require_table_write) > 0:
347
+ providers.extend(
348
+ (
349
+ await DatabaseToolkitBuilder().make(
350
+ room=self.room,
351
+ model=model,
352
+ config=DatabaseToolkitConfig(
353
+ tables=require_table_write,
354
+ read_only=False,
355
+ namespace=database_namespace,
356
+ ),
357
+ )
358
+ ).tools
359
+ )
360
+
361
+ if require_read_only_storage:
362
+ providers.extend(StorageToolkit(read_only=True).tools)
363
+
364
+ if require_document_authoring:
365
+ providers.extend(DocumentAuthoringToolkit().tools)
366
+ providers.extend(
367
+ DocumentTypeAuthoringToolkit(
368
+ schema=widget_schema, document_type="widget"
369
+ ).tools
370
+ )
371
+
372
+ if require_discovery:
373
+ from meshagent.tools.discovery import DiscoveryToolkit
374
+
375
+ providers.extend(DiscoveryToolkit().tools)
376
+
377
+ tk = await super().get_thread_toolkits(
378
+ thread_context=thread_context, participant=participant
379
+ )
380
+
381
+ if require_computer_use:
382
+ from meshagent.computers.agent import ComputerToolkit
383
+
384
+ def render_screen(image_bytes: bytes):
385
+ for participant in thread_context.participants:
386
+ self.room.messaging.send_message_nowait(
387
+ to=participant,
388
+ type="computer_screen",
389
+ message={},
390
+ attachment=image_bytes,
391
+ )
392
+
393
+ computer_toolkit = ComputerToolkit(
394
+ room=self.room, render_screen=render_screen
395
+ )
396
+
397
+ tk.append(computer_toolkit)
398
+
399
+ return [
400
+ *(
401
+ [Toolkit(name="tools", tools=providers)]
402
+ if len(providers) > 0
403
+ else []
404
+ ),
405
+ *tk,
406
+ ]
407
+
408
+ def get_toolkit_builders(self):
409
+ providers = []
410
+
411
+ if image_generation:
412
+ providers.append(ImageGenerationToolkitBuilder())
413
+
414
+ if apply_patch:
415
+ providers.append(ApplyPatchToolkitBuilder())
416
+
417
+ if local_shell:
418
+ providers.append(
419
+ LocalShellToolkitBuilder(
420
+ working_directory=working_directory,
421
+ )
422
+ )
423
+
424
+ if shell:
425
+ providers.append(
426
+ ShellToolkitBuilder(
427
+ working_directory=working_directory,
428
+ image=shell_image,
429
+ )
430
+ )
431
+
432
+ if mcp:
433
+ providers.append(MCPToolkitBuilder())
434
+
435
+ if web_search:
436
+ providers.append(WebSearchToolkitBuilder())
437
+
438
+ if storage:
439
+ providers.append(StorageToolkitBuilder())
440
+
441
+ return providers
442
+
443
+ return CustomChatbot
444
+
445
+
446
+ @app.async_command("join")
447
+ async def join(
448
+ *,
449
+ project_id: ProjectIdOption,
450
+ room: RoomOption,
451
+ role: str = "agent",
452
+ agent_name: Annotated[
453
+ Optional[str], typer.Option(..., help="Name of the agent to call")
454
+ ] = None,
455
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
456
+ room_rules: Annotated[
457
+ List[str],
458
+ typer.Option(
459
+ "--room-rules",
460
+ "-rr",
461
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
462
+ ),
463
+ ] = [],
464
+ rules_file: Optional[str] = None,
465
+ require_toolkit: Annotated[
466
+ List[str],
467
+ typer.Option(
468
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
469
+ ),
470
+ ] = [],
471
+ require_schema: Annotated[
472
+ List[str],
473
+ typer.Option(
474
+ "--require-schema", "-rs", help="the name or url of a required schema"
475
+ ),
476
+ ] = [],
477
+ toolkit: Annotated[
478
+ List[str],
479
+ typer.Option(
480
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
481
+ ),
482
+ ] = [],
483
+ schema: Annotated[
484
+ List[str],
485
+ typer.Option(
486
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
487
+ ),
488
+ ] = [],
489
+ model: Annotated[
490
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
491
+ ] = "gpt-5.2",
492
+ image_generation: Annotated[
493
+ Optional[str], typer.Option(..., help="Name of an image gen model")
494
+ ] = None,
495
+ computer_use: Annotated[
496
+ Optional[bool],
497
+ typer.Option(
498
+ ..., help="Enable computer use (requires computer-use-preview model)"
499
+ ),
500
+ ] = False,
501
+ local_shell: Annotated[
502
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
503
+ ] = False,
504
+ shell: Annotated[
505
+ Optional[bool], typer.Option(..., help="Enable function shell tool calling")
506
+ ] = False,
507
+ apply_patch: Annotated[
508
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
509
+ ] = False,
510
+ web_search: Annotated[
511
+ Optional[bool], typer.Option(..., help="Enable web search tool calling")
512
+ ] = False,
513
+ mcp: Annotated[
514
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
515
+ ] = False,
516
+ storage: Annotated[
517
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
518
+ ] = False,
519
+ require_image_generation: Annotated[
520
+ Optional[str], typer.Option(..., help="Name of an image gen model")
521
+ ] = None,
522
+ require_computer_use: Annotated[
523
+ Optional[bool],
524
+ typer.Option(
525
+ ...,
526
+ help="Enable computer use (requires computer-use-preview model)",
527
+ hidden=True,
528
+ ),
529
+ ] = False,
530
+ require_local_shell: Annotated[
531
+ Optional[bool],
532
+ typer.Option(..., help="Enable local shell tool calling"),
533
+ ] = False,
534
+ require_shell: Annotated[
535
+ Optional[bool],
536
+ typer.Option(..., help="Enable function shell tool calling"),
537
+ ] = False,
538
+ require_apply_patch: Annotated[
539
+ Optional[bool],
540
+ typer.Option(..., help="Enable apply patch tool calling"),
541
+ ] = False,
542
+ require_web_search: Annotated[
543
+ Optional[bool],
544
+ typer.Option(..., help="Enable web search tool calling"),
545
+ ] = False,
546
+ require_mcp: Annotated[
547
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
548
+ ] = False,
549
+ require_storage: Annotated[
550
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
551
+ ] = False,
552
+ database_namespace: Annotated[
553
+ Optional[str],
554
+ typer.Option(..., help="Use a specific database namespace"),
555
+ ] = None,
556
+ require_table_read: Annotated[
557
+ list[str],
558
+ typer.Option(..., help="Enable table read tools for a specific table"),
559
+ ] = [],
560
+ require_table_write: Annotated[
561
+ list[str],
562
+ typer.Option(..., help="Enable table write tools for a specific table"),
563
+ ] = [],
564
+ require_read_only_storage: Annotated[
565
+ Optional[bool],
566
+ typer.Option(..., help="Enable read only storage toolkit"),
567
+ ] = False,
568
+ require_time: Annotated[
569
+ bool,
570
+ typer.Option(
571
+ ...,
572
+ help="Enable time/datetime tools",
573
+ ),
574
+ ] = True,
575
+ require_uuid: Annotated[
576
+ bool,
577
+ typer.Option(
578
+ ...,
579
+ help="Enable UUID generation tools",
580
+ ),
581
+ ] = False,
582
+ require_document_authoring: Annotated[
583
+ Optional[bool],
584
+ typer.Option(..., help="Enable MeshDocument authoring"),
585
+ ] = False,
586
+ require_discovery: Annotated[
587
+ Optional[bool],
588
+ typer.Option(..., help="Enable discovery of agents and tools"),
589
+ ] = False,
590
+ working_directory: Annotated[
591
+ Optional[str],
592
+ typer.Option(..., help="The default working directory for shell commands"),
593
+ ] = None,
594
+ key: Annotated[
595
+ str,
596
+ typer.Option("--key", help="an api key to sign the token with"),
597
+ ] = None,
598
+ llm_participant: Annotated[
599
+ Optional[str],
600
+ typer.Option(..., help="Delegate LLM interactions to a remote participant"),
601
+ ] = None,
602
+ always_reply: Annotated[
603
+ Optional[bool],
604
+ typer.Option(..., help="Always reply"),
605
+ ] = None,
606
+ skill_dir: Annotated[
607
+ list[str],
608
+ typer.Option(..., help="an agent skills directory"),
609
+ ] = [],
610
+ shell_image: Annotated[
611
+ Optional[str],
612
+ typer.Option(..., help="an image tag to use to run shell commands in"),
613
+ ] = None,
614
+ delegate_shell_token: Annotated[
615
+ Optional[bool],
616
+ typer.Option(..., help="log all requests to the llm"),
617
+ ] = False,
618
+ log_llm_requests: Annotated[
619
+ Optional[bool],
620
+ typer.Option(..., help="log all requests to the llm"),
621
+ ] = False,
622
+ ):
623
+ if database_namespace is not None:
624
+ database_namespace = database_namespace.split("::")
625
+
626
+ key = await resolve_key(project_id=project_id, key=key)
627
+ account_client = await get_client()
628
+ try:
629
+ project_id = await resolve_project_id(project_id=project_id)
630
+ room = resolve_room(room)
631
+
632
+ jwt = os.getenv("MESHAGENT_TOKEN")
633
+ if jwt is None:
634
+ if agent_name is None:
635
+ print(
636
+ "[bold red]--agent-name must be specified when the MESHAGENT_TOKEN environment variable is not set[/bold red]"
637
+ )
638
+ raise typer.Exit(1)
639
+
640
+ token = ParticipantToken(
641
+ name=agent_name,
642
+ )
643
+
644
+ token.add_api_grant(ApiScope.agent_default(tunnels=require_computer_use))
645
+
646
+ token.add_role_grant(role=role)
647
+ token.add_room_grant(room)
648
+
649
+ jwt = token.to_jwt(api_key=key)
650
+
651
+ print("[bold green]Connecting to room...[/bold green]", flush=True)
652
+
653
+ CustomChatbot = build_chatbot(
654
+ computer_use=computer_use,
655
+ require_computer_use=require_computer_use,
656
+ model=model,
657
+ rule=rule,
658
+ toolkit=require_toolkit + toolkit,
659
+ schema=require_schema + schema,
660
+ rules_file=rules_file,
661
+ local_shell=local_shell,
662
+ shell=shell,
663
+ apply_patch=apply_patch,
664
+ image_generation=image_generation,
665
+ web_search=web_search,
666
+ mcp=mcp,
667
+ storage=storage,
668
+ require_apply_patch=require_apply_patch,
669
+ require_web_search=require_web_search,
670
+ require_local_shell=require_local_shell,
671
+ require_shell=require_shell,
672
+ require_image_generation=require_image_generation,
673
+ require_mcp=require_mcp,
674
+ require_storage=require_storage,
675
+ require_table_read=require_table_read,
676
+ require_table_write=require_table_write,
677
+ require_read_only_storage=require_read_only_storage,
678
+ require_time=require_time,
679
+ require_uuid=require_uuid,
680
+ room_rules_path=room_rules,
681
+ require_document_authoring=require_document_authoring,
682
+ require_discovery=require_discovery,
683
+ working_directory=working_directory,
684
+ llm_participant=llm_participant,
685
+ always_reply=always_reply,
686
+ database_namespace=database_namespace,
687
+ skill_dirs=skill_dir,
688
+ shell_image=shell_image,
689
+ delegate_shell_token=delegate_shell_token,
690
+ log_llm_requests=log_llm_requests,
691
+ )
692
+
693
+ bot = CustomChatbot()
694
+
695
+ if get_deferred():
696
+ from meshagent.cli.host import agents
697
+
698
+ agents.append((bot, jwt))
699
+ else:
700
+ async with RoomClient(
701
+ protocol=WebSocketClientProtocol(
702
+ url=websocket_room_url(
703
+ room_name=room, base_url=meshagent_base_url()
704
+ ),
705
+ token=jwt,
706
+ )
707
+ ) as client:
708
+ await bot.start(room=client)
709
+ try:
710
+ print(
711
+ f"[bold green]Open the studio to interact with your agent: {meshagent_base_url().replace('api.', 'studio.')}/projects/{project_id}/rooms/{client.room_name}[/bold green]",
712
+ flush=True,
713
+ )
714
+ await client.protocol.wait_for_close()
715
+
716
+ except KeyboardInterrupt:
717
+ await bot.stop()
718
+ finally:
719
+ await account_client.close()
720
+
721
+
722
+ @app.async_command("service")
723
+ async def service(
724
+ *,
725
+ agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
726
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
727
+ rules_file: Optional[str] = None,
728
+ room_rules: Annotated[
729
+ List[str],
730
+ typer.Option(
731
+ "--room-rules",
732
+ "-rr",
733
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
734
+ ),
735
+ ] = [],
736
+ require_toolkit: Annotated[
737
+ List[str],
738
+ typer.Option(
739
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
740
+ ),
741
+ ] = [],
742
+ require_schema: Annotated[
743
+ List[str],
744
+ typer.Option(
745
+ "--require-schema", "-rs", help="the name or url of a required schema"
746
+ ),
747
+ ] = [],
748
+ toolkit: Annotated[
749
+ List[str],
750
+ typer.Option(
751
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
752
+ ),
753
+ ] = [],
754
+ schema: Annotated[
755
+ List[str],
756
+ typer.Option(
757
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
758
+ ),
759
+ ] = [],
760
+ model: Annotated[
761
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
762
+ ] = "gpt-5.2",
763
+ image_generation: Annotated[
764
+ Optional[str], typer.Option(..., help="Name of an image gen model")
765
+ ] = None,
766
+ local_shell: Annotated[
767
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
768
+ ] = False,
769
+ shell: Annotated[
770
+ Optional[bool], typer.Option(..., help="Enable function shell tool calling")
771
+ ] = False,
772
+ apply_patch: Annotated[
773
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
774
+ ] = False,
775
+ computer_use: Annotated[
776
+ Optional[bool],
777
+ typer.Option(
778
+ ..., help="Enable computer use (requires computer-use-preview model)"
779
+ ),
780
+ ] = False,
781
+ web_search: Annotated[
782
+ Optional[bool], typer.Option(..., help="Enable web search tool calling")
783
+ ] = False,
784
+ mcp: Annotated[
785
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
786
+ ] = False,
787
+ storage: Annotated[
788
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
789
+ ] = False,
790
+ require_image_generation: Annotated[
791
+ Optional[str], typer.Option(..., help="Name of an image gen model")
792
+ ] = None,
793
+ require_computer_use: Annotated[
794
+ Optional[bool],
795
+ typer.Option(
796
+ ...,
797
+ help="Enable computer use (requires computer-use-preview model)",
798
+ hidden=True,
799
+ ),
800
+ ] = False,
801
+ require_local_shell: Annotated[
802
+ Optional[bool],
803
+ typer.Option(..., help="Enable local shell tool calling"),
804
+ ] = False,
805
+ require_shell: Annotated[
806
+ Optional[bool],
807
+ typer.Option(..., help="Enable function shell tool calling"),
808
+ ] = False,
809
+ require_apply_patch: Annotated[
810
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
811
+ ] = False,
812
+ require_web_search: Annotated[
813
+ Optional[bool],
814
+ typer.Option(..., help="Enable web search tool calling"),
815
+ ] = False,
816
+ require_mcp: Annotated[
817
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
818
+ ] = False,
819
+ require_storage: Annotated[
820
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
821
+ ] = False,
822
+ database_namespace: Annotated[
823
+ Optional[str],
824
+ typer.Option(..., help="Use a specific database namespace"),
825
+ ] = None,
826
+ require_table_read: Annotated[
827
+ list[str],
828
+ typer.Option(..., help="Enable table read tools for a specific table"),
829
+ ] = [],
830
+ require_table_write: Annotated[
831
+ list[str],
832
+ typer.Option(..., help="Enable table write tools for a specific table"),
833
+ ] = [],
834
+ require_read_only_storage: Annotated[
835
+ Optional[bool],
836
+ typer.Option(..., help="Enable read only storage toolkit"),
837
+ ] = False,
838
+ require_time: Annotated[
839
+ bool,
840
+ typer.Option(
841
+ ...,
842
+ help="Enable time/datetime tools",
843
+ ),
844
+ ] = True,
845
+ require_uuid: Annotated[
846
+ bool,
847
+ typer.Option(
848
+ ...,
849
+ help="Enable UUID generation tools",
850
+ ),
851
+ ] = False,
852
+ working_directory: Annotated[
853
+ Optional[str],
854
+ typer.Option(..., help="The default working directory for shell commands"),
855
+ ] = None,
856
+ require_document_authoring: Annotated[
857
+ Optional[bool],
858
+ typer.Option(..., help="Enable document authoring"),
859
+ ] = False,
860
+ require_discovery: Annotated[
861
+ Optional[bool],
862
+ typer.Option(..., help="Enable discovery of agents and tools"),
863
+ ] = False,
864
+ llm_participant: Annotated[
865
+ Optional[str],
866
+ typer.Option(..., help="Delegate LLM interactions to a remote participant"),
867
+ ] = None,
868
+ host: Annotated[
869
+ Optional[str], typer.Option(help="Host to bind the service on")
870
+ ] = None,
871
+ port: Annotated[
872
+ Optional[int], typer.Option(help="Port to bind the service on")
873
+ ] = None,
874
+ path: Annotated[
875
+ Optional[str], typer.Option(help="HTTP path to mount the service at")
876
+ ] = None,
877
+ always_reply: Annotated[
878
+ Optional[bool],
879
+ typer.Option(..., help="Always reply"),
880
+ ] = None,
881
+ skill_dir: Annotated[
882
+ list[str],
883
+ typer.Option(..., help="an agent skills directory"),
884
+ ] = [],
885
+ shell_image: Annotated[
886
+ Optional[str],
887
+ typer.Option(..., help="an image tag to use to run shell commands in"),
888
+ ] = None,
889
+ delegate_shell_token: Annotated[
890
+ Optional[bool],
891
+ typer.Option(..., help="log all requests to the llm"),
892
+ ] = False,
893
+ log_llm_requests: Annotated[
894
+ Optional[bool],
895
+ typer.Option(..., help="log all requests to the llm"),
896
+ ] = False,
897
+ ):
898
+ if database_namespace is not None:
899
+ database_namespace = database_namespace.split("::")
900
+
901
+ service = get_service(host=host, port=port)
902
+
903
+ if path is None:
904
+ path = "/agent"
905
+ i = 0
906
+ while service.has_path(path):
907
+ i += 1
908
+ path = f"/agent{i}"
909
+
910
+ service.agents.append(
911
+ AgentSpec(name=agent_name, annotations={ANNOTATION_AGENT_TYPE: "ChatBot"})
912
+ )
913
+
914
+ service.add_path(
915
+ identity=agent_name,
916
+ path=path,
917
+ cls=build_chatbot(
918
+ computer_use=computer_use,
919
+ require_computer_use=require_computer_use,
920
+ model=model,
921
+ local_shell=local_shell,
922
+ shell=shell,
923
+ apply_patch=apply_patch,
924
+ rule=rule,
925
+ toolkit=require_toolkit + toolkit,
926
+ schema=require_schema + schema,
927
+ rules_file=rules_file,
928
+ web_search=web_search,
929
+ image_generation=image_generation,
930
+ mcp=mcp,
931
+ storage=storage,
932
+ database_namespace=database_namespace,
933
+ require_web_search=require_web_search,
934
+ require_shell=require_shell,
935
+ require_apply_patch=require_apply_patch,
936
+ require_local_shell=require_local_shell,
937
+ require_image_generation=require_image_generation,
938
+ require_mcp=require_mcp,
939
+ require_storage=require_storage,
940
+ require_table_write=require_table_write,
941
+ require_table_read=require_table_read,
942
+ require_read_only_storage=require_read_only_storage,
943
+ require_time=require_time,
944
+ require_uuid=require_uuid,
945
+ room_rules_path=room_rules,
946
+ working_directory=working_directory,
947
+ require_document_authoring=require_document_authoring,
948
+ require_discovery=require_discovery,
949
+ llm_participant=llm_participant,
950
+ always_reply=always_reply,
951
+ skill_dirs=skill_dir,
952
+ shell_image=shell_image,
953
+ delegate_shell_token=delegate_shell_token,
954
+ log_llm_requests=log_llm_requests,
955
+ ),
956
+ )
957
+
958
+ if not get_deferred():
959
+ await run_services()
960
+
961
+
962
+ @app.async_command("spec")
963
+ async def spec(
964
+ *,
965
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
966
+ service_description: Annotated[
967
+ Optional[str], typer.Option("--service-description", help="service description")
968
+ ] = None,
969
+ service_title: Annotated[
970
+ Optional[str],
971
+ typer.Option("--service-title", help="a display name for the service"),
972
+ ] = None,
973
+ agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
974
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
975
+ rules_file: Optional[str] = None,
976
+ room_rules: Annotated[
977
+ List[str],
978
+ typer.Option(
979
+ "--room-rules",
980
+ "-rr",
981
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
982
+ ),
983
+ ] = [],
984
+ require_toolkit: Annotated[
985
+ List[str],
986
+ typer.Option(
987
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
988
+ ),
989
+ ] = [],
990
+ require_schema: Annotated[
991
+ List[str],
992
+ typer.Option(
993
+ "--require-schema", "-rs", help="the name or url of a required schema"
994
+ ),
995
+ ] = [],
996
+ toolkit: Annotated[
997
+ List[str],
998
+ typer.Option(
999
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
1000
+ ),
1001
+ ] = [],
1002
+ schema: Annotated[
1003
+ List[str],
1004
+ typer.Option(
1005
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
1006
+ ),
1007
+ ] = [],
1008
+ model: Annotated[
1009
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
1010
+ ] = "gpt-5.2",
1011
+ image_generation: Annotated[
1012
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1013
+ ] = None,
1014
+ local_shell: Annotated[
1015
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
1016
+ ] = False,
1017
+ shell: Annotated[
1018
+ Optional[bool], typer.Option(..., help="Enable function shell tool calling")
1019
+ ] = False,
1020
+ apply_patch: Annotated[
1021
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
1022
+ ] = False,
1023
+ computer_use: Annotated[
1024
+ Optional[bool],
1025
+ typer.Option(
1026
+ ..., help="Enable computer use (requires computer-use-preview model)"
1027
+ ),
1028
+ ] = False,
1029
+ web_search: Annotated[
1030
+ Optional[bool], typer.Option(..., help="Enable web search tool calling")
1031
+ ] = False,
1032
+ mcp: Annotated[
1033
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1034
+ ] = False,
1035
+ storage: Annotated[
1036
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1037
+ ] = False,
1038
+ require_image_generation: Annotated[
1039
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1040
+ ] = None,
1041
+ require_computer_use: Annotated[
1042
+ Optional[bool],
1043
+ typer.Option(
1044
+ ...,
1045
+ help="Enable computer use (requires computer-use-preview model)",
1046
+ hidden=True,
1047
+ ),
1048
+ ] = False,
1049
+ require_local_shell: Annotated[
1050
+ Optional[bool],
1051
+ typer.Option(..., help="Enable local shell tool calling"),
1052
+ ] = False,
1053
+ require_shell: Annotated[
1054
+ Optional[bool],
1055
+ typer.Option(..., help="Enable function shell tool calling"),
1056
+ ] = False,
1057
+ require_apply_patch: Annotated[
1058
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
1059
+ ] = False,
1060
+ require_web_search: Annotated[
1061
+ Optional[bool],
1062
+ typer.Option(..., help="Enable web search tool calling"),
1063
+ ] = False,
1064
+ require_mcp: Annotated[
1065
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1066
+ ] = False,
1067
+ require_storage: Annotated[
1068
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1069
+ ] = False,
1070
+ database_namespace: Annotated[
1071
+ Optional[str],
1072
+ typer.Option(..., help="Use a specific database namespace"),
1073
+ ] = None,
1074
+ require_table_read: Annotated[
1075
+ list[str],
1076
+ typer.Option(..., help="Enable table read tools for a specific table"),
1077
+ ] = [],
1078
+ require_table_write: Annotated[
1079
+ list[str],
1080
+ typer.Option(..., help="Enable table write tools for a specific table"),
1081
+ ] = [],
1082
+ require_read_only_storage: Annotated[
1083
+ Optional[bool],
1084
+ typer.Option(..., help="Enable read only storage toolkit"),
1085
+ ] = False,
1086
+ require_time: Annotated[
1087
+ bool,
1088
+ typer.Option(
1089
+ ...,
1090
+ help="Enable time/datetime tools",
1091
+ ),
1092
+ ] = True,
1093
+ require_uuid: Annotated[
1094
+ bool,
1095
+ typer.Option(
1096
+ ...,
1097
+ help="Enable UUID generation tools",
1098
+ ),
1099
+ ] = False,
1100
+ working_directory: Annotated[
1101
+ Optional[str],
1102
+ typer.Option(..., help="The default working directory for shell commands"),
1103
+ ] = None,
1104
+ require_document_authoring: Annotated[
1105
+ Optional[bool],
1106
+ typer.Option(..., help="Enable document authoring"),
1107
+ ] = False,
1108
+ require_discovery: Annotated[
1109
+ Optional[bool],
1110
+ typer.Option(..., help="Enable discovery of agents and tools"),
1111
+ ] = False,
1112
+ llm_participant: Annotated[
1113
+ Optional[str],
1114
+ typer.Option(..., help="Delegate LLM interactions to a remote participant"),
1115
+ ] = None,
1116
+ host: Annotated[
1117
+ Optional[str], typer.Option(help="Host to bind the service on")
1118
+ ] = None,
1119
+ port: Annotated[
1120
+ Optional[int], typer.Option(help="Port to bind the service on")
1121
+ ] = None,
1122
+ path: Annotated[
1123
+ Optional[str], typer.Option(help="HTTP path to mount the service at")
1124
+ ] = None,
1125
+ always_reply: Annotated[
1126
+ Optional[bool],
1127
+ typer.Option(..., help="Always reply"),
1128
+ ] = None,
1129
+ skill_dir: Annotated[
1130
+ list[str],
1131
+ typer.Option(..., help="an agent skills directory"),
1132
+ ] = [],
1133
+ shell_image: Annotated[
1134
+ Optional[str],
1135
+ typer.Option(..., help="an image tag to use to run shell commands in"),
1136
+ ] = None,
1137
+ delegate_shell_token: Annotated[
1138
+ Optional[bool],
1139
+ typer.Option(..., help="log all requests to the llm"),
1140
+ ] = False,
1141
+ log_llm_requests: Annotated[
1142
+ Optional[bool],
1143
+ typer.Option(..., help="log all requests to the llm"),
1144
+ ] = False,
1145
+ ):
1146
+ if database_namespace is not None:
1147
+ database_namespace = database_namespace.split("::")
1148
+
1149
+ service = get_service(host=host, port=port)
1150
+
1151
+ if path is None:
1152
+ path = "/agent"
1153
+ i = 0
1154
+ while service.has_path(path):
1155
+ i += 1
1156
+ path = f"/agent{i}"
1157
+
1158
+ service.agents.append(
1159
+ AgentSpec(name=agent_name, annotations={ANNOTATION_AGENT_TYPE: "ChatBot"})
1160
+ )
1161
+
1162
+ service.add_path(
1163
+ identity=agent_name,
1164
+ path=path,
1165
+ cls=build_chatbot(
1166
+ computer_use=computer_use,
1167
+ require_computer_use=require_computer_use,
1168
+ model=model,
1169
+ local_shell=local_shell,
1170
+ shell=shell,
1171
+ apply_patch=apply_patch,
1172
+ rule=rule,
1173
+ toolkit=require_toolkit + toolkit,
1174
+ schema=require_schema + schema,
1175
+ rules_file=rules_file,
1176
+ web_search=web_search,
1177
+ image_generation=image_generation,
1178
+ mcp=mcp,
1179
+ storage=storage,
1180
+ database_namespace=database_namespace,
1181
+ require_web_search=require_web_search,
1182
+ require_shell=require_shell,
1183
+ require_apply_patch=require_apply_patch,
1184
+ require_local_shell=require_local_shell,
1185
+ require_image_generation=require_image_generation,
1186
+ require_mcp=require_mcp,
1187
+ require_storage=require_storage,
1188
+ require_table_write=require_table_write,
1189
+ require_table_read=require_table_read,
1190
+ require_read_only_storage=require_read_only_storage,
1191
+ require_time=require_time,
1192
+ require_uuid=require_uuid,
1193
+ room_rules_path=room_rules,
1194
+ working_directory=working_directory,
1195
+ require_document_authoring=require_document_authoring,
1196
+ require_discovery=require_discovery,
1197
+ llm_participant=llm_participant,
1198
+ always_reply=always_reply,
1199
+ skill_dirs=skill_dir,
1200
+ shell_image=shell_image,
1201
+ delegate_shell_token=delegate_shell_token,
1202
+ log_llm_requests=log_llm_requests,
1203
+ ),
1204
+ )
1205
+
1206
+ spec = service_specs()[0]
1207
+ spec.metadata.annotations = {
1208
+ "meshagent.service.id": service_name,
1209
+ }
1210
+
1211
+ spec.metadata.name = service_name
1212
+ spec.metadata.description = service_description
1213
+ spec.container.image = (
1214
+ "us-central1-docker.pkg.dev/meshagent-public/images/cli:{SERVER_VERSION}-esgz"
1215
+ )
1216
+ spec.container.command = shlex.join(
1217
+ ["meshagent", "chatbot", "service", *cleanup_args(sys.argv[2:])]
1218
+ )
1219
+
1220
+ print(yaml.dump(spec.model_dump(mode="json", exclude_none=True), sort_keys=False))
1221
+
1222
+
1223
+ @app.async_command("deploy")
1224
+ async def deploy(
1225
+ *,
1226
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
1227
+ service_description: Annotated[
1228
+ Optional[str], typer.Option("--service-description", help="service description")
1229
+ ] = None,
1230
+ service_title: Annotated[
1231
+ Optional[str],
1232
+ typer.Option("--service-title", help="a display name for the service"),
1233
+ ] = None,
1234
+ agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
1235
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
1236
+ rules_file: Optional[str] = None,
1237
+ room_rules: Annotated[
1238
+ List[str],
1239
+ typer.Option(
1240
+ "--room-rules",
1241
+ "-rr",
1242
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
1243
+ ),
1244
+ ] = [],
1245
+ require_toolkit: Annotated[
1246
+ List[str],
1247
+ typer.Option(
1248
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
1249
+ ),
1250
+ ] = [],
1251
+ require_schema: Annotated[
1252
+ List[str],
1253
+ typer.Option(
1254
+ "--require-schema", "-rs", help="the name or url of a required schema"
1255
+ ),
1256
+ ] = [],
1257
+ toolkit: Annotated[
1258
+ List[str],
1259
+ typer.Option(
1260
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
1261
+ ),
1262
+ ] = [],
1263
+ schema: Annotated[
1264
+ List[str],
1265
+ typer.Option(
1266
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
1267
+ ),
1268
+ ] = [],
1269
+ model: Annotated[
1270
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
1271
+ ] = "gpt-5.2",
1272
+ image_generation: Annotated[
1273
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1274
+ ] = None,
1275
+ local_shell: Annotated[
1276
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
1277
+ ] = False,
1278
+ shell: Annotated[
1279
+ Optional[bool], typer.Option(..., help="Enable function shell tool calling")
1280
+ ] = False,
1281
+ apply_patch: Annotated[
1282
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
1283
+ ] = False,
1284
+ computer_use: Annotated[
1285
+ Optional[bool],
1286
+ typer.Option(
1287
+ ..., help="Enable computer use (requires computer-use-preview model)"
1288
+ ),
1289
+ ] = False,
1290
+ web_search: Annotated[
1291
+ Optional[bool], typer.Option(..., help="Enable web search tool calling")
1292
+ ] = False,
1293
+ mcp: Annotated[
1294
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1295
+ ] = False,
1296
+ storage: Annotated[
1297
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1298
+ ] = False,
1299
+ require_image_generation: Annotated[
1300
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1301
+ ] = None,
1302
+ require_computer_use: Annotated[
1303
+ Optional[bool],
1304
+ typer.Option(
1305
+ ...,
1306
+ help="Enable computer use (requires computer-use-preview model)",
1307
+ hidden=True,
1308
+ ),
1309
+ ] = False,
1310
+ require_local_shell: Annotated[
1311
+ Optional[bool],
1312
+ typer.Option(..., help="Enable local shell tool calling"),
1313
+ ] = False,
1314
+ require_shell: Annotated[
1315
+ Optional[bool],
1316
+ typer.Option(..., help="Enable function shell tool calling"),
1317
+ ] = False,
1318
+ require_apply_patch: Annotated[
1319
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
1320
+ ] = False,
1321
+ require_web_search: Annotated[
1322
+ Optional[bool],
1323
+ typer.Option(..., help="Enable web search tool calling"),
1324
+ ] = False,
1325
+ require_mcp: Annotated[
1326
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1327
+ ] = False,
1328
+ require_storage: Annotated[
1329
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1330
+ ] = False,
1331
+ database_namespace: Annotated[
1332
+ Optional[str],
1333
+ typer.Option(..., help="Use a specific database namespace"),
1334
+ ] = None,
1335
+ require_table_read: Annotated[
1336
+ list[str],
1337
+ typer.Option(..., help="Enable table read tools for a specific table"),
1338
+ ] = [],
1339
+ require_table_write: Annotated[
1340
+ list[str],
1341
+ typer.Option(..., help="Enable table write tools for a specific table"),
1342
+ ] = [],
1343
+ require_read_only_storage: Annotated[
1344
+ Optional[bool],
1345
+ typer.Option(..., help="Enable read only storage toolkit"),
1346
+ ] = False,
1347
+ require_time: Annotated[
1348
+ bool,
1349
+ typer.Option(
1350
+ ...,
1351
+ help="Enable time/datetime tools",
1352
+ ),
1353
+ ] = True,
1354
+ require_uuid: Annotated[
1355
+ bool,
1356
+ typer.Option(
1357
+ ...,
1358
+ help="Enable UUID generation tools",
1359
+ ),
1360
+ ] = False,
1361
+ working_directory: Annotated[
1362
+ Optional[str],
1363
+ typer.Option(..., help="The default working directory for shell commands"),
1364
+ ] = None,
1365
+ require_document_authoring: Annotated[
1366
+ Optional[bool],
1367
+ typer.Option(..., help="Enable document authoring"),
1368
+ ] = False,
1369
+ require_discovery: Annotated[
1370
+ Optional[bool],
1371
+ typer.Option(..., help="Enable discovery of agents and tools"),
1372
+ ] = False,
1373
+ llm_participant: Annotated[
1374
+ Optional[str],
1375
+ typer.Option(..., help="Delegate LLM interactions to a remote participant"),
1376
+ ] = None,
1377
+ host: Annotated[
1378
+ Optional[str], typer.Option(help="Host to bind the service on")
1379
+ ] = None,
1380
+ port: Annotated[
1381
+ Optional[int], typer.Option(help="Port to bind the service on")
1382
+ ] = None,
1383
+ path: Annotated[
1384
+ Optional[str], typer.Option(help="HTTP path to mount the service at")
1385
+ ] = None,
1386
+ always_reply: Annotated[
1387
+ Optional[bool],
1388
+ typer.Option(..., help="Always reply"),
1389
+ ] = None,
1390
+ skill_dir: Annotated[
1391
+ list[str],
1392
+ typer.Option(..., help="an agent skills directory"),
1393
+ ] = [],
1394
+ shell_image: Annotated[
1395
+ Optional[str],
1396
+ typer.Option(..., help="an image tag to use to run shell commands in"),
1397
+ ] = None,
1398
+ delegate_shell_token: Annotated[
1399
+ Optional[bool],
1400
+ typer.Option(..., help="log all requests to the llm"),
1401
+ ] = False,
1402
+ log_llm_requests: Annotated[
1403
+ Optional[bool],
1404
+ typer.Option(..., help="log all requests to the llm"),
1405
+ ] = False,
1406
+ project_id: ProjectIdOption,
1407
+ room: Annotated[
1408
+ Optional[str],
1409
+ typer.Option("--room", help="The name of a room to create the service for"),
1410
+ ] = None,
1411
+ ):
1412
+ project_id = await resolve_project_id(project_id=project_id)
1413
+
1414
+ if database_namespace is not None:
1415
+ database_namespace = database_namespace.split("::")
1416
+
1417
+ service = get_service(host=host, port=port)
1418
+
1419
+ if path is None:
1420
+ path = "/agent"
1421
+ i = 0
1422
+ while service.has_path(path):
1423
+ i += 1
1424
+ path = f"/agent{i}"
1425
+
1426
+ service.agents.append(
1427
+ AgentSpec(name=agent_name, annotations={ANNOTATION_AGENT_TYPE: "ChatBot"})
1428
+ )
1429
+
1430
+ service.add_path(
1431
+ identity=agent_name,
1432
+ path=path,
1433
+ cls=build_chatbot(
1434
+ computer_use=computer_use,
1435
+ require_computer_use=require_computer_use,
1436
+ model=model,
1437
+ local_shell=local_shell,
1438
+ shell=shell,
1439
+ apply_patch=apply_patch,
1440
+ rule=rule,
1441
+ toolkit=require_toolkit + toolkit,
1442
+ schema=require_schema + schema,
1443
+ rules_file=rules_file,
1444
+ web_search=web_search,
1445
+ image_generation=image_generation,
1446
+ mcp=mcp,
1447
+ storage=storage,
1448
+ database_namespace=database_namespace,
1449
+ require_web_search=require_web_search,
1450
+ require_shell=require_shell,
1451
+ require_apply_patch=require_apply_patch,
1452
+ require_local_shell=require_local_shell,
1453
+ require_image_generation=require_image_generation,
1454
+ require_mcp=require_mcp,
1455
+ require_storage=require_storage,
1456
+ require_table_write=require_table_write,
1457
+ require_table_read=require_table_read,
1458
+ require_read_only_storage=require_read_only_storage,
1459
+ require_time=require_time,
1460
+ require_uuid=require_uuid,
1461
+ room_rules_path=room_rules,
1462
+ working_directory=working_directory,
1463
+ require_document_authoring=require_document_authoring,
1464
+ require_discovery=require_discovery,
1465
+ llm_participant=llm_participant,
1466
+ always_reply=always_reply,
1467
+ skill_dirs=skill_dir,
1468
+ shell_image=shell_image,
1469
+ delegate_shell_token=delegate_shell_token,
1470
+ log_llm_requests=log_llm_requests,
1471
+ ),
1472
+ )
1473
+
1474
+ spec = service_specs()[0]
1475
+
1476
+ for port in spec.ports:
1477
+ port
1478
+
1479
+ spec.metadata.annotations = {
1480
+ "meshagent.service.id": service_name,
1481
+ }
1482
+
1483
+ spec.metadata.name = service_name
1484
+ spec.metadata.description = service_description
1485
+ spec.container.image = (
1486
+ "us-central1-docker.pkg.dev/meshagent-public/images/cli:{SERVER_VERSION}-esgz"
1487
+ )
1488
+ spec.container.command = shlex.join(
1489
+ ["meshagent", "chatbot", "service", *cleanup_args(sys.argv[2:])]
1490
+ )
1491
+
1492
+ project_id = await resolve_project_id(project_id)
1493
+
1494
+ client = await get_client()
1495
+ try:
1496
+ id = None
1497
+ try:
1498
+ if id is None:
1499
+ if room is None:
1500
+ services = await client.list_services(project_id=project_id)
1501
+ else:
1502
+ services = await client.list_room_services(
1503
+ project_id=project_id, room_name=room
1504
+ )
1505
+
1506
+ for s in services:
1507
+ if s.metadata.name == spec.metadata.name:
1508
+ id = s.id
1509
+
1510
+ if id is None:
1511
+ if room is None:
1512
+ id = await client.create_service(
1513
+ project_id=project_id, service=spec
1514
+ )
1515
+ else:
1516
+ id = await client.create_room_service(
1517
+ project_id=project_id, service=spec, room_name=room
1518
+ )
1519
+
1520
+ else:
1521
+ spec.id = id
1522
+ if room is None:
1523
+ await client.update_service(
1524
+ project_id=project_id, service_id=id, service=spec
1525
+ )
1526
+ else:
1527
+ await client.update_room_service(
1528
+ project_id=project_id,
1529
+ service_id=id,
1530
+ service=spec,
1531
+ room_name=room,
1532
+ )
1533
+
1534
+ except ConflictError:
1535
+ print(f"[red]Service name already in use: {spec.metadata.name}[/red]")
1536
+ raise typer.Exit(code=1)
1537
+ else:
1538
+ print(f"[green]Deployed service:[/] {id}")
1539
+
1540
+ finally:
1541
+ await client.close()
1542
+
1543
+
1544
+ async def chat_with(
1545
+ *,
1546
+ participant_name: str,
1547
+ project_id: str,
1548
+ room: str,
1549
+ thread_path: str,
1550
+ message: Optional[str] = None,
1551
+ use_web_search: bool = False,
1552
+ use_image_gen: bool = False,
1553
+ use_storage: bool = False,
1554
+ ):
1555
+ from prompt_toolkit.shortcuts import PromptSession
1556
+ from prompt_toolkit.key_binding import KeyBindings
1557
+ from meshagent.agents.chat import ChatBotClient
1558
+
1559
+ kb = KeyBindings()
1560
+
1561
+ session = PromptSession("> ", key_bindings=kb)
1562
+
1563
+ account_client = await get_client()
1564
+ try:
1565
+ connection = await account_client.connect_room(project_id=project_id, room=room)
1566
+ async with RoomClient(
1567
+ protocol=WebSocketClientProtocol(
1568
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
1569
+ token=connection.jwt,
1570
+ ),
1571
+ ) as user_client:
1572
+ await user_client.messaging.enable()
1573
+
1574
+ if thread_path is None:
1575
+ thread_path = f".threads/{participant_name}/{user_client.local_participant.get_attribute('name')}.thread"
1576
+
1577
+ async with ChatBotClient(
1578
+ room=user_client,
1579
+ participant_name=participant_name,
1580
+ thread_path=thread_path,
1581
+ ) as chat_client:
1582
+
1583
+ @kb.add("c-l")
1584
+ def _(event):
1585
+ event.app.renderer.clear()
1586
+ asyncio.ensure_future(chat_client.clear())
1587
+
1588
+ while True:
1589
+ user_input = message or await session.prompt_async()
1590
+
1591
+ if user_input == "/clear":
1592
+ await chat_client.clear()
1593
+
1594
+ else:
1595
+ tools: list[ToolkitConfig] = []
1596
+
1597
+ if use_web_search:
1598
+ tools.append(WebSearchConfig())
1599
+
1600
+ elif use_image_gen:
1601
+ tools.append(ImageGenerationConfig())
1602
+
1603
+ elif use_storage:
1604
+ tools.append(StorageToolkitConfig())
1605
+
1606
+ await chat_client.send(text=user_input, tools=tools)
1607
+
1608
+ response = await chat_client.receive()
1609
+
1610
+ print(response)
1611
+
1612
+ if message:
1613
+ break
1614
+
1615
+ except asyncio.CancelledError:
1616
+ pass
1617
+
1618
+ finally:
1619
+ await account_client.close()
1620
+
1621
+
1622
+ @app.async_command("run")
1623
+ async def run(
1624
+ *,
1625
+ project_id: ProjectIdOption,
1626
+ room: RoomOption,
1627
+ role: str = "agent",
1628
+ agent_name: Annotated[
1629
+ Optional[str], typer.Option(..., help="Name of the agent to call")
1630
+ ] = None,
1631
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
1632
+ room_rules: Annotated[
1633
+ List[str],
1634
+ typer.Option(
1635
+ "--room-rules",
1636
+ "-rr",
1637
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
1638
+ ),
1639
+ ] = [],
1640
+ rules_file: Optional[str] = None,
1641
+ require_toolkit: Annotated[
1642
+ List[str],
1643
+ typer.Option(
1644
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
1645
+ ),
1646
+ ] = [],
1647
+ require_schema: Annotated[
1648
+ List[str],
1649
+ typer.Option(
1650
+ "--require-schema", "-rs", help="the name or url of a required schema"
1651
+ ),
1652
+ ] = [],
1653
+ toolkit: Annotated[
1654
+ List[str],
1655
+ typer.Option(
1656
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
1657
+ ),
1658
+ ] = [],
1659
+ schema: Annotated[
1660
+ List[str],
1661
+ typer.Option(
1662
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
1663
+ ),
1664
+ ] = [],
1665
+ model: Annotated[
1666
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
1667
+ ] = "gpt-5.2",
1668
+ image_generation: Annotated[
1669
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1670
+ ] = None,
1671
+ computer_use: Annotated[
1672
+ Optional[bool],
1673
+ typer.Option(
1674
+ ..., help="Enable computer use (requires computer-use-preview model)"
1675
+ ),
1676
+ ] = False,
1677
+ local_shell: Annotated[
1678
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
1679
+ ] = False,
1680
+ shell: Annotated[
1681
+ Optional[bool], typer.Option(..., help="Enable function shell tool calling")
1682
+ ] = False,
1683
+ apply_patch: Annotated[
1684
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
1685
+ ] = False,
1686
+ web_search: Annotated[
1687
+ Optional[bool], typer.Option(..., help="Enable web search tool calling")
1688
+ ] = False,
1689
+ mcp: Annotated[
1690
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1691
+ ] = False,
1692
+ storage: Annotated[
1693
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1694
+ ] = False,
1695
+ require_image_generation: Annotated[
1696
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1697
+ ] = None,
1698
+ require_computer_use: Annotated[
1699
+ Optional[bool],
1700
+ typer.Option(
1701
+ ...,
1702
+ help="Enable computer use (requires computer-use-preview model)",
1703
+ hidden=True,
1704
+ ),
1705
+ ] = False,
1706
+ require_local_shell: Annotated[
1707
+ Optional[bool],
1708
+ typer.Option(..., help="Enable local shell tool calling"),
1709
+ ] = False,
1710
+ require_shell: Annotated[
1711
+ Optional[bool],
1712
+ typer.Option(..., help="Enable function shell tool calling"),
1713
+ ] = False,
1714
+ require_apply_patch: Annotated[
1715
+ Optional[bool],
1716
+ typer.Option(..., help="Enable apply patch tool calling"),
1717
+ ] = False,
1718
+ require_web_search: Annotated[
1719
+ Optional[bool],
1720
+ typer.Option(..., help="Enable web search tool calling"),
1721
+ ] = False,
1722
+ require_mcp: Annotated[
1723
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1724
+ ] = False,
1725
+ require_storage: Annotated[
1726
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1727
+ ] = False,
1728
+ database_namespace: Annotated[
1729
+ Optional[str],
1730
+ typer.Option(..., help="Use a specific database namespace"),
1731
+ ] = None,
1732
+ require_table_read: Annotated[
1733
+ list[str],
1734
+ typer.Option(..., help="Enable table read tools for a specific table"),
1735
+ ] = [],
1736
+ require_table_write: Annotated[
1737
+ list[str],
1738
+ typer.Option(..., help="Enable table write tools for a specific table"),
1739
+ ] = [],
1740
+ require_read_only_storage: Annotated[
1741
+ Optional[bool],
1742
+ typer.Option(..., help="Enable read only storage toolkit"),
1743
+ ] = False,
1744
+ require_time: Annotated[
1745
+ bool,
1746
+ typer.Option(
1747
+ ...,
1748
+ help="Enable time/datetime tools",
1749
+ ),
1750
+ ] = True,
1751
+ require_uuid: Annotated[
1752
+ bool,
1753
+ typer.Option(
1754
+ ...,
1755
+ help="Enable UUID generation tools",
1756
+ ),
1757
+ ] = False,
1758
+ require_document_authoring: Annotated[
1759
+ Optional[bool],
1760
+ typer.Option(..., help="Enable MeshDocument authoring"),
1761
+ ] = False,
1762
+ require_discovery: Annotated[
1763
+ Optional[bool],
1764
+ typer.Option(..., help="Enable discovery of agents and tools"),
1765
+ ] = False,
1766
+ working_directory: Annotated[
1767
+ Optional[str],
1768
+ typer.Option(..., help="The default working directory for shell commands"),
1769
+ ] = None,
1770
+ key: Annotated[
1771
+ str,
1772
+ typer.Option("--key", help="an api key to sign the token with"),
1773
+ ] = None,
1774
+ llm_participant: Annotated[
1775
+ Optional[str],
1776
+ typer.Option(..., help="Delegate LLM interactions to a remote participant"),
1777
+ ] = None,
1778
+ always_reply: Annotated[
1779
+ Optional[bool],
1780
+ typer.Option(..., help="Always reply"),
1781
+ ] = None,
1782
+ skill_dir: Annotated[
1783
+ list[str],
1784
+ typer.Option(..., help="an agent skills directory"),
1785
+ ] = [],
1786
+ shell_image: Annotated[
1787
+ Optional[str],
1788
+ typer.Option(..., help="an image tag to use to run shell commands in"),
1789
+ ] = None,
1790
+ delegate_shell_token: Annotated[
1791
+ Optional[bool],
1792
+ typer.Option(..., help="log all requests to the llm"),
1793
+ ] = False,
1794
+ log_llm_requests: Annotated[
1795
+ Optional[bool],
1796
+ typer.Option(..., help="log all requests to the llm"),
1797
+ ] = False,
1798
+ thread_path: Annotated[
1799
+ Optional[str],
1800
+ typer.Option(..., help="log all requests to the llm"),
1801
+ ] = None,
1802
+ message: Annotated[
1803
+ Optional[str],
1804
+ typer.Option(..., help="the input message to use"),
1805
+ ] = None,
1806
+ use_web_search: Annotated[
1807
+ Optional[bool],
1808
+ typer.Option(..., help="request the web search tool"),
1809
+ ] = None,
1810
+ use_image_gen: Annotated[
1811
+ Optional[bool],
1812
+ typer.Option(..., help="request the image gen tool"),
1813
+ ] = None,
1814
+ use_storage: Annotated[
1815
+ Optional[bool],
1816
+ typer.Option(..., help="request the storage tool"),
1817
+ ] = None,
1818
+ ):
1819
+ root = logging.getLogger()
1820
+ root.setLevel(logging.ERROR)
1821
+
1822
+ if database_namespace is not None:
1823
+ database_namespace = database_namespace.split("::")
1824
+
1825
+ key = await resolve_key(project_id=project_id, key=key)
1826
+ account_client = await get_client()
1827
+ try:
1828
+ project_id = await resolve_project_id(project_id=project_id)
1829
+ room = resolve_room(room)
1830
+
1831
+ jwt = os.getenv("MESHAGENT_TOKEN")
1832
+ if jwt is None:
1833
+ if agent_name is None:
1834
+ print(
1835
+ "[bold red]--agent-name must be specified when the MESHAGENT_TOKEN environment variable is not set[/bold red]"
1836
+ )
1837
+ raise typer.Exit(1)
1838
+
1839
+ token = ParticipantToken(
1840
+ name=agent_name,
1841
+ )
1842
+
1843
+ token.add_api_grant(ApiScope.agent_default(tunnels=require_computer_use))
1844
+
1845
+ token.add_role_grant(role=role)
1846
+ token.add_room_grant(room)
1847
+
1848
+ jwt = token.to_jwt(api_key=key)
1849
+
1850
+ async with RoomClient(
1851
+ protocol=WebSocketClientProtocol(
1852
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
1853
+ token=jwt,
1854
+ )
1855
+ ) as client:
1856
+ CustomChatbot = build_chatbot(
1857
+ computer_use=computer_use,
1858
+ require_computer_use=require_computer_use,
1859
+ model=model,
1860
+ rule=rule,
1861
+ toolkit=require_toolkit + toolkit,
1862
+ schema=require_schema + schema,
1863
+ rules_file=rules_file,
1864
+ local_shell=local_shell,
1865
+ shell=shell,
1866
+ apply_patch=apply_patch,
1867
+ image_generation=image_generation,
1868
+ web_search=web_search,
1869
+ mcp=mcp,
1870
+ storage=storage,
1871
+ require_apply_patch=require_apply_patch,
1872
+ require_web_search=require_web_search,
1873
+ require_local_shell=require_local_shell,
1874
+ require_shell=require_shell,
1875
+ require_image_generation=require_image_generation,
1876
+ require_mcp=require_mcp,
1877
+ require_storage=require_storage,
1878
+ require_table_read=require_table_read,
1879
+ require_table_write=require_table_write,
1880
+ require_read_only_storage=require_read_only_storage,
1881
+ require_time=require_time,
1882
+ require_uuid=require_uuid,
1883
+ room_rules_path=room_rules,
1884
+ require_document_authoring=require_document_authoring,
1885
+ require_discovery=require_discovery,
1886
+ working_directory=working_directory,
1887
+ llm_participant=llm_participant,
1888
+ always_reply=always_reply,
1889
+ database_namespace=database_namespace,
1890
+ skill_dirs=skill_dir,
1891
+ shell_image=shell_image,
1892
+ delegate_shell_token=delegate_shell_token,
1893
+ log_llm_requests=log_llm_requests,
1894
+ )
1895
+
1896
+ bot = CustomChatbot()
1897
+
1898
+ await bot.start(room=client)
1899
+
1900
+ _, pending = await asyncio.wait(
1901
+ [
1902
+ asyncio.create_task(client.protocol.wait_for_close()),
1903
+ asyncio.create_task(
1904
+ chat_with(
1905
+ participant_name=client.local_participant.get_attribute(
1906
+ "name"
1907
+ ),
1908
+ room=room,
1909
+ project_id=project_id,
1910
+ thread_path=thread_path,
1911
+ message=message,
1912
+ use_web_search=use_web_search,
1913
+ use_image_gen=use_image_gen,
1914
+ use_storage=use_storage,
1915
+ )
1916
+ ),
1917
+ ],
1918
+ return_when="FIRST_COMPLETED",
1919
+ )
1920
+
1921
+ for t in pending:
1922
+ t.cancel()
1923
+
1924
+ except asyncio.CancelledError:
1925
+ return
1926
+
1927
+ finally:
1928
+ await account_client.close()
1929
+
1930
+
1931
+ @app.async_command("use")
1932
+ async def use(
1933
+ *,
1934
+ project_id: ProjectIdOption,
1935
+ room: RoomOption,
1936
+ agent_name: Annotated[
1937
+ Optional[str], typer.Option(..., help="Name of the agent to call")
1938
+ ] = None,
1939
+ thread_path: Annotated[
1940
+ Optional[str],
1941
+ typer.Option(..., help="log all requests to the llm"),
1942
+ ] = None,
1943
+ message: Annotated[
1944
+ Optional[str],
1945
+ typer.Option(..., help="the input message to use"),
1946
+ ] = None,
1947
+ use_web_search: Annotated[
1948
+ Optional[bool],
1949
+ typer.Option(..., help="request the web search tool"),
1950
+ ] = None,
1951
+ use_image_gen: Annotated[
1952
+ Optional[bool],
1953
+ typer.Option(..., help="request the image gen tool"),
1954
+ ] = None,
1955
+ use_storage: Annotated[
1956
+ Optional[bool],
1957
+ typer.Option(..., help="request the storage tool"),
1958
+ ] = None,
1959
+ ):
1960
+ root = logging.getLogger()
1961
+ root.setLevel(logging.ERROR)
1962
+
1963
+ account_client = await get_client()
1964
+ try:
1965
+ project_id = await resolve_project_id(project_id=project_id)
1966
+ room = resolve_room(room)
1967
+
1968
+ await chat_with(
1969
+ participant_name=agent_name,
1970
+ room=room,
1971
+ project_id=project_id,
1972
+ thread_path=thread_path,
1973
+ message=message,
1974
+ use_web_search=use_web_search,
1975
+ use_image_gen=use_image_gen,
1976
+ use_storage=use_storage,
1977
+ )
1978
+
1979
+ except asyncio.CancelledError:
1980
+ return
1981
+
1982
+ finally:
1983
+ await account_client.close()