@brandon_9527/tcode 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/python-src/.env +1 -1
  2. package/dist/python-src/README.md +48 -1
  3. package/dist/python-src/_workspace/.autodev/config.json +12 -0
  4. package/dist/python-src/_workspace/.autodev/cron/jobs.json +4 -0
  5. package/dist/python-src/entry.py +21 -1
  6. package/dist/python-src/main.py +763 -42
  7. package/dist/python-src/pyproject.toml +1 -0
  8. package/dist/python-src/run.sh +9 -0
  9. package/dist/python-src/src/agents/token_tracker.py +4 -4
  10. package/dist/python-src/src/claw/bus/queue.py +1 -1
  11. package/dist/python-src/src/claw/channels/__init__.py +2 -2
  12. package/dist/python-src/src/claw/channels/base.py +2 -2
  13. package/dist/python-src/src/claw/channels/feishu.py +57 -16
  14. package/dist/python-src/src/claw/channels/manager.py +2 -2
  15. package/dist/python-src/src/claw/config/__init__.py +3 -0
  16. package/dist/python-src/src/claw/config/loader.py +38 -0
  17. package/dist/python-src/src/claw/config/schema.py +14 -29
  18. package/dist/python-src/src/claw/cron/__init__.py +3 -0
  19. package/dist/python-src/src/claw/cron/service.py +171 -0
  20. package/dist/python-src/src/claw/cron/types_.py +14 -0
  21. package/dist/python-src/src/claw/heartbeat/__init__.py +2 -0
  22. package/dist/python-src/src/claw/heartbeat/service.py +55 -0
  23. package/dist/python-src/src/claw/run.py +82 -0
  24. package/dist/python-src/src/claw/tools/base.py +23 -0
  25. package/dist/python-src/src/claw/tools/channel.py +0 -0
  26. package/dist/python-src/src/claw/tools/cron.py +138 -0
  27. package/dist/python-src/src/claw/utils/__init__.py +2 -0
  28. package/dist/python-src/src/claw/utils/helpers.py +27 -0
  29. package/dist/python-src/src/core/context.py +158 -0
  30. package/dist/python-src/src/managers/manager_agent.py +9 -9
  31. package/dist/python-src/src/managers/manager_command.py +62 -0
  32. package/dist/python-src/src/managers/manager_context.py +1 -1
  33. package/dist/python-src/src/managers/manager_instruction.py +7 -7
  34. package/dist/python-src/src/managers/manager_skill.py +3 -3
  35. package/dist/python-src/src/managers/sandbox.py +3 -3
  36. package/dist/python-src/src/middlewares/dynamic_content.py +2 -2
  37. package/dist/python-src/src/middlewares/hitl.py +3 -3
  38. package/dist/python-src/src/middlewares/memory.py +2 -2
  39. package/dist/python-src/src/middlewares/subagents.py +4 -4
  40. package/dist/python-src/src/middlewares/summary.py +37 -37
  41. package/dist/python-src/src/stream/file_write_parser.py +3 -3
  42. package/dist/python-src/src/stream/formatter.py +19 -19
  43. package/dist/python-src/src/stream/handler.py +4 -4
  44. package/dist/python-src/src/stream/handler_with_tracker.py +10 -10
  45. package/dist/python-src/src/trackers/token/pricing.py +2 -2
  46. package/dist/python-src/src/trackers/token/report.py +4 -4
  47. package/dist/python-src/src/trackers/token/tracker.py +8 -8
  48. package/dist/python-src/src/tui/chatui.py +10 -10
  49. package/dist/python-src/src/tui/clawtui.py +230 -0
  50. package/dist/python-src/src/tui/commands/__init__.py +3 -0
  51. package/dist/python-src/src/tui/commands/base.py +6 -0
  52. package/dist/python-src/src/tui/commands/instruction.py +5 -0
  53. package/dist/python-src/src/tui/components/tlist.py +7 -7
  54. package/dist/python-src/src/tui/components/tscroll_panel.py +73 -44
  55. package/dist/python-src/src/tui/components/tscroll_panel_old.py +58 -0
  56. package/dist/python-src/src/tui/utils/trender.py +21 -21
  57. package/dist/python-src/uv.lock +1969 -1958
  58. package/package.json +1 -1
@@ -11,7 +11,8 @@ import asyncio
11
11
  import json
12
12
  import os
13
13
 
14
- from src.tui.chatui import LiveChatUI
14
+ # from src.tui.chatui import LiveChatUI
15
+ from src.tui.clawtui import LiveChatUI
15
16
  from src.middlewares.hitl import HumanInTheLoopMiddleware
16
17
 
17
18
  _ = load_dotenv(find_dotenv())
@@ -23,18 +24,33 @@ import warnings
23
24
  warnings.filterwarnings("ignore", category=DeprecationWarning)
24
25
  warnings.filterwarnings("ignore", message="Pydantic serializer warnings")
25
26
 
27
+ from src.claw.config.loader import load_config, get_data_dir
28
+ from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
29
+ from src.claw.channels.manager import ChannelManager
30
+ from src.claw.cron.service import CronService
31
+ from src.claw.cron.types_ import CronJob
32
+ from src.claw.heartbeat.service import HeartbeatService
26
33
 
34
+ from src.claw.tools.base import AgentContext
35
+ from pathlib import Path
27
36
 
37
+ from src.middlewares.skill import SkillMiddleware
38
+ from src.core.deepagents import create_deep_agent, get_default_model
39
+ from src.core.context import build_system_prompt
28
40
 
29
- # # ========== 关键修改1:配置logging,让INFO级别日志能输出 ==========
30
- # import logging
31
- # logging.basicConfig(
32
- # level=logging.CRITICAL, # 设置日志级别为WARNING(显示WARNING/ERROR等)
33
- # format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", # 日志格式(可选,更易读)
34
- # handlers=[logging.StreamHandler()] # 输出到控制台
35
- # )
36
41
 
37
- # logger = logging.getLogger(__name__)
42
+
43
+
44
+
45
+ # ========== 关键修改1:配置logging,让INFO级别日志能输出 ==========
46
+ import logging
47
+ logging.basicConfig(
48
+ level=logging.CRITICAL, # 设置日志级别为WARNING(显示WARNING/ERROR等)
49
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", # 日志格式(可选,更易读)
50
+ handlers=[logging.StreamHandler()] # 输出到控制台
51
+ )
52
+
53
+ logger = logging.getLogger(__name__)
38
54
 
39
55
 
40
56
 
@@ -66,26 +82,30 @@ def get_weather(city: str) -> str:
66
82
 
67
83
 
68
84
 
69
- llm = ChatOpenAI(
70
- model=os.getenv("DEFAULT_MODEL"),
71
- base_url=os.getenv("OPENAI_API_BASE"),
72
- api_key=os.getenv("OPENAI_API_KEY"),
73
- streaming=True,
74
- temperature=0.7,
75
- # 开启百炼的 thinking 模式
76
- # 新版 LangChain 推荐用 extra_body 直接作为参数(而非嵌套在 model_kwargs)
77
- # extra_body = {
78
- # "enable_thinking": True,
79
- # "thinking_budget": 1000, # 增大预算,适配 Agent 多步思考
80
- # },
81
- model_kwargs={
82
- # 使流式返回的最后一个数据包包含Token消耗信息
83
- "stream_options": {"include_usage": True}
84
- }
85
- )
85
+ # llm = ChatOpenAI(
86
+ # model=os.getenv("DEFAULT_MODEL"),
87
+ # base_url=os.getenv("OPENAI_API_BASE"),
88
+ # api_key=os.getenv("OPENAI_API_KEY"),
89
+ # streaming=True,
90
+ # temperature=0.7,
91
+ # # 开启百炼的 thinking 模式
92
+ # # 新版 LangChain 推荐用 extra_body 直接作为参数(而非嵌套在 model_kwargs)
93
+ # # extra_body = {
94
+ # # "enable_thinking": True,
95
+ # # "thinking_budget": 1000, # 增大预算,适配 Agent 多步思考
96
+ # # },
97
+ # model_kwargs={
98
+ # # 使流式返回的最后一个数据包包含Token消耗信息
99
+ # "stream_options": {"include_usage": True}
100
+ # }
101
+ # )
102
+
103
+ # llm = get_default_model(streaming=True)
86
104
 
87
105
  async def agent_ui():
88
106
 
107
+ llm = get_default_model(streaming=True)
108
+
89
109
  agent = create_agent(
90
110
  model=llm,
91
111
  tools=[get_weather],
@@ -179,7 +199,7 @@ async def asingle_agent():
179
199
  """
180
200
 
181
201
  saver = InMemorySaver()
182
-
202
+ llm = get_default_model(streaming=True)
183
203
  agent = create_agent(
184
204
  model=llm,
185
205
  tools=tools,
@@ -311,11 +331,17 @@ async def team_main():
311
331
  agents_conf = agent_manager.get_all_conf()
312
332
 
313
333
  toolkits = {
314
- "filetools": [ shell, read_file, write_file, glob, grep, edit, list_dir],
315
- "web": [web_search, web_fetch]
334
+ "read-only": [ read_file, glob, grep, list_dir],
335
+ "edit": [ edit, write_file],
336
+ "search": [ web_search, web_fetch],
337
+ "bash": [ shell],
338
+ "web": [ web_search, web_fetch],
316
339
  } if run_mode == "sandbox" else {
317
- "filetools": [ bash, read_file, write_file, glob, grep, edit, list_dir],
318
- "web": [web_search, web_fetch]
340
+ "read-only": [ read_file, glob, grep, list_dir],
341
+ "edit": [ edit, write_file],
342
+ "search": [ web_search, web_fetch],
343
+ "bash": [ bash ],
344
+ "web": [ web_search, web_fetch],
319
345
  }
320
346
 
321
347
  context = SkillAgentContext(
@@ -362,7 +388,7 @@ async def team_main():
362
388
  agents.append(agent)
363
389
 
364
390
  tools = []
365
- for tool_name in ["filetools", "web"]:
391
+ for tool_name in ["read-only", "edit", "search", "bash", "web"]:
366
392
  tools.extend([tool for tool in toolkits.get(tool_name, [])])
367
393
 
368
394
  team = create_deep_agent(
@@ -376,7 +402,8 @@ async def team_main():
376
402
  # prompt_name="leader_",
377
403
  # WORKSPACE=workspace
378
404
  # ),
379
- system_prompt=apply_prompt(leader, WORKSPACE=workspace),
405
+ # system_prompt=apply_prompt(leader, WORKSPACE=workspace),
406
+ system_prompt=build_system_prompt(),
380
407
  checkpointer=saver
381
408
  ).with_config({"recursion_limit": recursion_limit})
382
409
 
@@ -386,7 +413,7 @@ async def team_main():
386
413
  workspace = os.getcwd()
387
414
  run_mode = "local"
388
415
  session_id = "1"
389
-
416
+ llm = get_default_model(streaming=True)
390
417
  # agents_conf, toolkits, context, saver, instruction_manager
391
418
  agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
392
419
  agent = _build_team(
@@ -476,11 +503,17 @@ def build_team():
476
503
  agents_conf = agent_manager.get_all_conf()
477
504
 
478
505
  toolkits = {
479
- "filetools": [ shell, read_file, write_file, glob, grep, edit, list_dir],
480
- "web": [web_search, web_fetch]
506
+ "read-only": [ read_file, glob, grep, list_dir],
507
+ "edit": [ edit, write_file],
508
+ "search": [ web_search, web_fetch],
509
+ "bash": [ shell],
510
+ "web": [ web_search, web_fetch],
481
511
  } if run_mode == "sandbox" else {
482
- "filetools": [ bash, read_file, write_file, glob, grep, edit, list_dir],
483
- "web": [web_search, web_fetch]
512
+ "read-only": [ read_file, glob, grep, list_dir],
513
+ "edit": [ edit, write_file],
514
+ "search": [ web_search, web_fetch],
515
+ "bash": [ bash ],
516
+ "web": [ web_search, web_fetch],
484
517
  }
485
518
 
486
519
  context = SkillAgentContext(
@@ -526,7 +559,7 @@ def build_team():
526
559
  agents.append(agent)
527
560
 
528
561
  tools = []
529
- for tool_name in ["filetools", "web"]:
562
+ for tool_name in ["read-only", "edit", "search", "bash", "web"]:
530
563
  tools.extend([tool for tool in toolkits.get(tool_name, [])])
531
564
 
532
565
  team = create_deep_agent(
@@ -543,7 +576,8 @@ def build_team():
543
576
  middleware=[
544
577
  DynamicContentMiddleware(),
545
578
  ],
546
- system_prompt=apply_prompt(leader, WORKSPACE=workspace),
579
+ # system_prompt=apply_prompt(leader, WORKSPACE=workspace),
580
+ system_prompt=build_system_prompt(),
547
581
  checkpointer=saver
548
582
  ).with_config({"recursion_limit": recursion_limit})
549
583
 
@@ -553,7 +587,7 @@ def build_team():
553
587
  workspace = os.getcwd()
554
588
  run_mode = "local"
555
589
  session_id = "1"
556
-
590
+ llm = get_default_model(streaming=True)
557
591
  # agents_conf, toolkits, context, saver, instruction_manager
558
592
  agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
559
593
  agent = _build_team(
@@ -653,18 +687,705 @@ async def run_once(prompt:str = None, verbose: bool=True):
653
687
 
654
688
  finally:
655
689
  pass
690
+
691
+
692
+ def claw_main():
693
+ """ 以 claw mode 运行 """
694
+
695
+ from functools import partial
696
+ from pathlib import Path
697
+ import asyncio
698
+ import sys
699
+ import os
700
+
701
+ from src.claw.config.loader import load_config, get_data_dir
702
+ from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
703
+ from src.claw.channels.manager import ChannelManager
704
+ from src.claw.cron.service import CronService
705
+ from src.claw.cron.types_ import CronJob
706
+ from src.claw.heartbeat.service import HeartbeatService
707
+
708
+ from src.claw.tools.base import AgentContext
709
+ from src.claw.tools.cron import (
710
+ add_cron_job,
711
+ list_cron_jobs,
712
+ remove_cron_job
713
+ )
714
+ from src.tools.tools import (
715
+ SkillAgentContext,
716
+ shell,
717
+ bash,
718
+ write_file,
719
+ read_file,
720
+ list_dir,
721
+ glob,
722
+ grep,
723
+ edit
724
+ )
725
+
726
+ from src.tools.web import (
727
+ web_search,
728
+ web_fetch
729
+ )
730
+
731
+ from src.managers.sandbox import Container
732
+
733
+ from dotenv import load_dotenv, find_dotenv
734
+ from langchain.agents import create_agent
735
+ from langchain_openai import ChatOpenAI
736
+ from langgraph.checkpoint.memory import InMemorySaver
737
+ from langchain_core.tools import BaseTool
738
+
739
+
740
+ from src.core.deepagents import create_deep_agent, get_default_model
741
+ from src.managers.manager_context import ContextManager
742
+ from src.utils.prompt import apply_template, apply_prompt
743
+ from src.prompts.prompts import leader
744
+
745
+ from src.tui.clawtui import LiveChatUI
746
+
747
+ _ = load_dotenv(find_dotenv())
748
+
749
+ WORKSPACE = Path.cwd()
750
+
751
+
752
+ """
753
+ "appId": "cli_a909847e1278dcbd",
754
+ "appSecret": "bE0usuE0MKUJVDWOo9olib5X8PKv66pK",
755
+ """
756
+ channel_conf_path = WORKSPACE / "channel_config.json"
757
+ if not channel_conf_path.exists():
758
+ with open(channel_conf_path, "w") as f:
759
+ data = {
760
+ "channels": {
761
+ "feishu": {
762
+ "enabled": True,
763
+ "appId": "",
764
+ "appSecret": "",
765
+ "encryptKey": "",
766
+ "verificationToken": "",
767
+ "allowFrom": []
768
+ }
769
+ }
770
+ }
771
+ json.dump(data, f)
772
+
773
+ config = load_config(
774
+ config_path=channel_conf_path
775
+ )
776
+ config.channels.feishu.app_id = os.getenv("APP_ID", "")
777
+ config.channels.feishu.app_secret = os.getenv("APP_SECRET", "")
778
+
779
+ def _prepare(workspace, run_mode, session_id):
780
+ saver = InMemorySaver()
781
+
782
+ # 1. 构建上下文管理器
783
+ dockerfile_path = os.path.join(Path(__file__).parent, "resources", "dockerfiles", "sandbox", "Dockerfile")
784
+ context_manager = ContextManager(
785
+ dockerfile_path=dockerfile_path,
786
+ mode=run_mode
787
+ )
788
+
789
+ context_manager.create_environment(
790
+ session_id=session_id,
791
+ workspace_path=workspace
792
+ )
793
+
794
+ # 2. 指令管理器
795
+ instruction_manager = context_manager.get_instruction_manager(
796
+ session_id=session_id
797
+ )
798
+
799
+ # 3. 代理管理器
800
+ agent_manager = context_manager.get_agent_manager(
801
+ session_id=session_id
802
+ )
803
+
804
+ # 4. 沙箱
805
+ sandbox = context_manager.get_container(
806
+ session_id=session_id
807
+ )
808
+
809
+ # 5. 工具箱
810
+
811
+ toolkits = {
812
+ "read-only": [ read_file, glob, grep, list_dir],
813
+ "edit": [ edit, write_file],
814
+ "search": [ web_search, web_fetch],
815
+ "bash": [ shell],
816
+ "web": [ web_search, web_fetch],
817
+ } if run_mode == "sandbox" else {
818
+ "read-only": [ read_file, glob, grep, list_dir],
819
+ "edit": [ edit, write_file],
820
+ "search": [ web_search, web_fetch],
821
+ "bash": [ bash ],
822
+ "web": [ web_search, web_fetch],
823
+ }
656
824
 
825
+ # 6. 配置
826
+ agents_conf = agent_manager.get_all_conf()
827
+
828
+ context = AgentContext(
829
+ working_directory=workspace,
830
+ sandbox=sandbox,
831
+ channel=None,
832
+ chat_id=None,
833
+ cron_service=None,
834
+ workspace=workspace,
835
+ )
836
+
837
+ return agents_conf, toolkits, context, saver, instruction_manager
838
+
839
+ def _build_team(agents_conf: Dict[str, Any], domain: str, llm: ChatOpenAI, toolkits: Dict[str, List[BaseTool]], workspace: str, saver=None, store=None, recursion_limit=1000):
840
+
841
+ team_conf = agents_conf.setdefault(domain, {"members": {}})
842
+
843
+ agents = []
844
+ for member_name, member in team_conf["members"].items():
845
+
846
+ tools = []
847
+ for tool_name in member["tools"]:
848
+ tools.extend(toolkits.get(tool_name, []))
849
+
850
+ # print(f"Building agent {member_name}")
851
+ # print(f"Tools: {tools}")
852
+ # print(f"Member: {member}")
853
+ # sys.exit()
854
+
855
+ llm_conf = member.get("llm", None)
856
+ model = ChatOpenAI(**llm_conf) if llm_conf else get_default_model(streaming=False)
857
+
858
+ agent = {
859
+ "name": member_name,
860
+ "description": member["description"],
861
+ "system_prompt": apply_template(
862
+ agents_config=agents_conf,
863
+ domain=domain,
864
+ prompt_name=member_name,
865
+ WORKSPACE=workspace
866
+ ),
867
+ "context_schema": SkillAgentContext,
868
+ "tools": tools,
869
+ "model": model
870
+ }
871
+
872
+ agents.append(agent)
873
+
874
+ tools = []
875
+ for tool_name in ["read-only", "edit", "search", "bash", "web"]:
876
+ tools.extend([tool for tool in toolkits.get(tool_name, [])])
877
+
878
+ tools.extend([add_cron_job, list_cron_jobs, remove_cron_job])
879
+
880
+ team = create_deep_agent(
881
+ model=llm,
882
+ subagents=agents,
883
+ tools=tools,
884
+ middleware=[SkillMiddleware(workspace=workspace, home_path=str(Path.home()))],
885
+ context_schema=AgentContext,
886
+ system_prompt=apply_prompt(leader, WORKSPACE=workspace),
887
+ # system_prompt=build_system_prompt(),
888
+ checkpointer=saver
889
+ ).with_config({"recursion_limit": recursion_limit})
890
+
891
+ return team
657
892
 
658
893
 
894
+ workspace = os.getcwd()
895
+ run_mode = "local"
896
+ session_id = "1"
897
+
898
+ llm = get_default_model(streaming=True)
899
+ agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
900
+ agent = _build_team(
901
+ agents_conf=agents_config,
902
+ domain="coding",
903
+ llm=llm,
904
+ toolkits=toolkits,
905
+ workspace=workspace,
906
+ saver=saver,
907
+ store=None,
908
+ recursion_limit=1000
909
+ )
910
+
911
+ # 创建事件总线
912
+ bus = MessageBus()
913
+ # 创建周期性任务
914
+ cron_store_path = Path(os.getcwd()) /".autodev"/"cron"/"jobs.json"
915
+ cron_store_path.parent.mkdir(parents=True, exist_ok=True)
916
+ cron = CronService(cron_store_path)
917
+
918
+ # 设置周期回调函数
919
+ async def on_cron_job(job: CronJob) -> str | None:
920
+ """ Execute a cron job through the agent. """
921
+ session_key = f"cron:{job.id}"
922
+ channel = job.payload.channel or "cli"
923
+ chat_id = job.payload.to or "direct"
924
+ response = await agent.ainvoke(
925
+ {"messages": [{"role": "user", "content": job.payload.message}]},
926
+ context=AgentContext(
927
+ working_directory=workspace,
928
+ sandbox=None,
929
+ channel=channel,
930
+ chat_id=chat_id,
931
+ cron_service=cron,
932
+ workspace=workspace
933
+ )
934
+ )
659
935
 
936
+ resp = response["messages"][-1].content
937
+
938
+ if job.payload.deliver and job.payload.to:
939
+ from src.claw.bus.events import OutboundMessage
940
+ await bus.publish_outbound(
941
+ OutboundMessage(
942
+ channel=channel,
943
+ chat_id=chat_id,
944
+ content=resp or ""
945
+ ))
946
+
947
+ return resp
948
+
949
+ cron.on_job = on_cron_job
950
+
951
+ # 创建心跳服务
952
+ async def on_heartbeat(prompt: str) -> str:
953
+ """ Execute a heartbeat through the agent. """
954
+ print(f"execute Heartbeat prompt: {prompt}")
955
+ response = await agent.ainvoke(
956
+ {"messages": [{"role": "user", "content": prompt}]},
957
+ context=AgentContext(
958
+ working_directory=workspace,
959
+ sandbox=None,
960
+ channel="cli",
961
+ chat_id="direct",
962
+ cron_service=cron,
963
+ workspace=workspace,
964
+ )
965
+ )
966
+
967
+ resp = response["messages"][-1].content
968
+ return resp
969
+
970
+ heartbeat = HeartbeatService(
971
+ workspace=Path(workspace),
972
+ on_heartbeat=on_heartbeat,
973
+ interval_s=10,
974
+ enabled=True
975
+ )
976
+
977
+ # 创建渠道管理器
978
+ channels = ChannelManager(
979
+ config,
980
+ bus=bus,
981
+ )
982
+
983
+ ui = LiveChatUI(
984
+ agent=agent,
985
+ saver=saver,
986
+ workspace=str(workspace),
987
+ instruction_manager=instruction_manager,
988
+ channels=channels,
989
+ heartbeat=heartbeat,
990
+ bus=bus,
991
+ cron=cron,
992
+ context=context
993
+ )
994
+
995
+ asyncio.run(ui.alisten())
996
+
997
+ # await ui.alisten()
998
+
999
+
1000
+
1001
+ async def _process_message(msg: InboundMessage, agent:Any, bus: MessageBus, cron: CronService) -> OutboundMessage | None:
1002
+ """ """
1003
+ if msg.channel == "system":
1004
+ response = await _process_system_message(msg, agent, cron)
1005
+ await bus.publish_outbound(response)
1006
+
1007
+ print(f"user>{msg.content}")
1008
+ session_key = f"{msg.channel}:{msg.chat_id}"
1009
+ config = {"configurable": {"thread_id": session_key}}
1010
+
1011
+ response = await agent.ainvoke(
1012
+ {"messages": [{"role": "user", "content": msg.content}]},
1013
+ config=config,
1014
+ context=AgentContext(
1015
+ working_directory=Path.cwd(),
1016
+ sandbox=None,
1017
+ channel=msg.channel,
1018
+ chat_id=msg.chat_id,
1019
+ cron_service=cron,
1020
+ workspace=Path.cwd(),
1021
+ )
1022
+ )
1023
+ print(f"Bot>{response['messages'][-1].content}")
1024
+
1025
+ response = OutboundMessage(
1026
+ channel=msg.channel,
1027
+ chat_id=msg.chat_id,
1028
+ content=response["messages"][-1].content,
1029
+ metadata=msg.metadata or {}
1030
+ )
1031
+
1032
+ await bus.publish_outbound(response)
1033
+
1034
+
1035
+ async def _process_system_message(msg: InboundMessage, agent: Any, cron: CronService) -> OutboundMessage | None:
1036
+ """
1037
+ Process a system message (e.g. subagent announce).
1038
+ """
1039
+ # logger.info(f"Processing system message from {msg.sender_id}")
1040
+
1041
+ # Parse origin from chat_id (format: "channel:chat_id")
1042
+ if ":" in msg.chat_id:
1043
+ parts = msg.chat_id.split(":", 1)
1044
+ origin_channel = parts[0]
1045
+ origin_chat_id = parts[1]
1046
+ else:
1047
+ # Fallback
1048
+ origin_channel = "cli"
1049
+ origin_chat_id = msg.chat_id
1050
+
1051
+ # Use the origin session for context
1052
+ session_key = f"{origin_channel}:{origin_chat_id}"
1053
+ config = {"configurable": {"thread_id": session_key}}
1054
+ response = await agent.ainvoke(
1055
+ {"message": msg.content},
1056
+ config=config,
1057
+ context=AgentContext(
1058
+ working_directory=Path.cwd(),
1059
+ sandbox=None,
1060
+ channel=origin_channel,
1061
+ chat_id=origin_chat_id,
1062
+ cron_service=cron,
1063
+ workspace=Path.cwd(),
1064
+ )
1065
+ )
1066
+
1067
+ return OutboundMessage(
1068
+ channel=origin_channel,
1069
+ chat_id=origin_chat_id,
1070
+ content=response["messages"][-1].content,
1071
+ metadata=msg.metadata or {}
1072
+ )
1073
+
1074
+ def claw_terminal():
1075
+ """ 以 claw mode 运行 """
1076
+
1077
+ from functools import partial
1078
+ from pathlib import Path
1079
+ import asyncio
1080
+ import sys
1081
+ import os
1082
+
1083
+ from src.claw.config.loader import load_config, get_data_dir
1084
+ from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
1085
+ from src.claw.channels.manager import ChannelManager
1086
+ from src.claw.cron.service import CronService
1087
+ from src.claw.cron.types_ import CronJob
1088
+ from src.claw.heartbeat.service import HeartbeatService
1089
+
1090
+ from src.claw.tools.base import AgentContext
1091
+ from src.claw.tools.cron import (
1092
+ add_cron_job,
1093
+ list_cron_jobs,
1094
+ remove_cron_job
1095
+ )
1096
+ from src.tools.tools import (
1097
+ SkillAgentContext,
1098
+ shell,
1099
+ bash,
1100
+ write_file,
1101
+ read_file,
1102
+ list_dir,
1103
+ glob,
1104
+ grep,
1105
+ edit
1106
+ )
1107
+
1108
+ from src.tools.web import (
1109
+ web_search,
1110
+ web_fetch
1111
+ )
1112
+
1113
+ from src.managers.sandbox import Container
1114
+
1115
+ from dotenv import load_dotenv, find_dotenv
1116
+ from langchain.agents import create_agent
1117
+ from langchain_openai import ChatOpenAI
1118
+ from langgraph.checkpoint.memory import InMemorySaver
1119
+ from langchain_core.tools import BaseTool
1120
+
1121
+
1122
+ from src.core.deepagents import create_deep_agent, get_default_model
1123
+ from src.managers.manager_context import ContextManager
1124
+ from src.utils.prompt import apply_template, apply_prompt
1125
+ from src.prompts.prompts import leader
1126
+ from functools import partial
1127
+
1128
+ from src.tui.clawtui import LiveChatUI
1129
+
1130
+ _ = load_dotenv(find_dotenv())
1131
+
1132
+ WORKSPACE = Path.cwd()
1133
+
1134
+ conf_path = WORKSPACE / "config.json"
1135
+ if not conf_path.exists():
1136
+ with open(conf_path, "w") as f:
1137
+ data = {
1138
+ "channels": {
1139
+ "feishu": {
1140
+ "enabled": True,
1141
+ "appId": "cli_a909847e1278dcbd",
1142
+ "appSecret": "bE0usuE0MKUJVDWOo9olib5X8PKv66pK",
1143
+ "encryptKey": "",
1144
+ "verificationToken": "",
1145
+ "allowFrom": []
1146
+ }
1147
+ }
1148
+ }
1149
+ json.dump(data, f)
1150
+
1151
+ config = load_config(
1152
+ config_path=conf_path
1153
+ )
1154
+
1155
+ def _prepare(workspace, run_mode, session_id):
1156
+ saver = InMemorySaver()
1157
+
1158
+ # 1. 构建上下文管理器
1159
+ dockerfile_path = os.path.join(Path(__file__).parent, "resources", "dockerfiles", "sandbox", "Dockerfile")
1160
+ context_manager = ContextManager(
1161
+ dockerfile_path=dockerfile_path,
1162
+ mode=run_mode
1163
+ )
1164
+
1165
+ context_manager.create_environment(
1166
+ session_id=session_id,
1167
+ workspace_path=workspace
1168
+ )
1169
+
1170
+ # 2. 指令管理器
1171
+ instruction_manager = context_manager.get_instruction_manager(
1172
+ session_id=session_id
1173
+ )
1174
+
1175
+ # 3. 代理管理器
1176
+ agent_manager = context_manager.get_agent_manager(
1177
+ session_id=session_id
1178
+ )
1179
+
1180
+ # 4. 沙箱
1181
+ sandbox = context_manager.get_container(
1182
+ session_id=session_id
1183
+ )
1184
+
1185
+ # 5. 工具箱
1186
+ toolkits = {
1187
+ "read-only": [ read_file, glob, grep, list_dir],
1188
+ "edit": [ edit, write_file],
1189
+ "search": [ web_search, web_fetch],
1190
+ "bash": [ shell],
1191
+ "web": [ web_search, web_fetch],
1192
+ } if run_mode == "sandbox" else {
1193
+ "read-only": [ read_file, glob, grep, list_dir],
1194
+ "edit": [ edit, write_file],
1195
+ "search": [ web_search, web_fetch],
1196
+ "bash": [ bash ],
1197
+ "web": [ web_search, web_fetch],
1198
+ }
1199
+
1200
+ # 6. 配置
1201
+ agents_conf = agent_manager.get_all_conf()
1202
+
1203
+ context = AgentContext(
1204
+ working_directory=workspace,
1205
+ sandbox=sandbox,
1206
+ channel=None,
1207
+ chat_id=None,
1208
+ cron_service=None,
1209
+ workspace=workspace,
1210
+ )
1211
+
1212
+ return agents_conf, toolkits, context, saver, instruction_manager
1213
+
1214
+ def _build_team(agents_conf: Dict[str, Any], domain: str, llm: ChatOpenAI, toolkits: Dict[str, List[BaseTool]], workspace: str, saver=None, store=None, recursion_limit=1000):
1215
+
1216
+ team_conf = agents_conf.setdefault(domain, {"members": {}})
1217
+
1218
+ agents = []
1219
+ for member_name, member in team_conf["members"].items():
1220
+
1221
+ tools = []
1222
+ for tool_name in member["tools"]:
1223
+ tools.extend(toolkits.get(tool_name, []))
1224
+
1225
+ llm_conf = member.get("llm", None)
1226
+ model = ChatOpenAI(**llm_conf) if llm_conf else get_default_model(streaming=False)
1227
+
1228
+ agent = {
1229
+ "name": member_name,
1230
+ "description": member["description"],
1231
+ "system_prompt": apply_template(
1232
+ agents_config=agents_conf,
1233
+ domain=domain,
1234
+ prompt_name=member_name,
1235
+ WORKSPACE=workspace
1236
+ ),
1237
+ "context_schema": SkillAgentContext,
1238
+ "tools": tools,
1239
+ "model": model
1240
+ }
1241
+
1242
+ agents.append(agent)
1243
+
1244
+ tools = []
1245
+ for tool_name in ["read-only", "edit", "search", "bash", "web"]:
1246
+ tools.extend([tool for tool in toolkits.get(tool_name, [])])
1247
+
1248
+ team = create_deep_agent(
1249
+ model=llm,
1250
+ subagents=agents,
1251
+ tools=tools,
1252
+ context_schema=AgentContext,
1253
+ # system_prompt=apply_prompt(leader, WORKSPACE=workspace),
1254
+ system_prompt=build_system_prompt(),
1255
+ checkpointer=saver
1256
+ ).with_config({"recursion_limit": recursion_limit})
1257
+
1258
+ return team
1259
+
1260
+
1261
+ workspace = os.getcwd()
1262
+ run_mode = "local"
1263
+ session_id = "1"
1264
+ llm = get_default_model(streaming=True)
1265
+ agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
1266
+ agent = _build_team(
1267
+ agents_conf=agents_config,
1268
+ domain="coding",
1269
+ llm=llm,
1270
+ toolkits=toolkits,
1271
+ workspace=workspace,
1272
+ saver=saver,
1273
+ store=None,
1274
+ recursion_limit=1000
1275
+ )
1276
+
1277
+ # 创建事件总线
1278
+ bus = MessageBus()
1279
+ # 创建周期性任务
1280
+ cron_store_path = Path(os.getcwd()) /".autodev"/"cron"/"jobs.json"
1281
+ cron_store_path.parent.mkdir(parents=True, exist_ok=True)
1282
+ cron = CronService(cron_store_path)
1283
+
1284
+ # 设置周期回调函数
1285
+ async def on_cron_job(job: CronJob) -> str | None:
1286
+ """ Execute a cron job through the agent. """
1287
+ session_key = f"cron:{job.id}"
1288
+ channel = job.payload.channel or "cli"
1289
+ chat_id = job.payload.to or "direct"
1290
+ response = await agent.ainvoke(
1291
+ {"messages": [{"role": "user", "content": job.payload.message}]},
1292
+ context=AgentContext(
1293
+ working_directory=workspace,
1294
+ sandbox=None,
1295
+ channel=channel,
1296
+ chat_id=chat_id,
1297
+ cron_service=cron,
1298
+ workspace=workspace
1299
+ )
1300
+ )
1301
+
1302
+ resp = response["messages"][-1].content
1303
+
1304
+ if job.payload.deliver and job.payload.to:
1305
+ from src.claw.bus.events import OutboundMessage
1306
+ await bus.publish_outbound(
1307
+ OutboundMessage(
1308
+ channel=channel,
1309
+ chat_id=chat_id,
1310
+ content=resp or ""
1311
+ ))
1312
+
1313
+ return resp
1314
+
1315
+ cron.on_job = on_cron_job
1316
+
1317
+ # 创建心跳服务
1318
+ async def on_heartbeat(prompt: str) -> str:
1319
+ """ Execute a heartbeat through the agent. """
1320
+ print(f"execute Heartbeat prompt: {prompt}")
1321
+ response = await agent.ainvoke(
1322
+ {"messages": [{"role": "user", "content": prompt}]},
1323
+ context=AgentContext(
1324
+ working_directory=workspace,
1325
+ sandbox=None,
1326
+ channel="cli",
1327
+ chat_id="direct",
1328
+ cron_service=cron,
1329
+ workspace=workspace,
1330
+ )
1331
+ )
1332
+
1333
+ resp = response["messages"][-1].content
1334
+ return resp
1335
+
1336
+ heartbeat = HeartbeatService(
1337
+ workspace=Path(workspace),
1338
+ on_heartbeat=on_heartbeat,
1339
+ interval_s=10,
1340
+ enabled=True
1341
+ )
1342
+
1343
+ # 创建渠道管理器
1344
+ channels = ChannelManager(
1345
+ config,
1346
+ bus=bus,
1347
+ )
1348
+
1349
+
1350
+ bus.subscribe_inbound("feishu", partial(_process_message, agent=agent, bus=bus, cron=cron))
1351
+
1352
+ async def run():
1353
+ try:
1354
+ await cron.start()
1355
+ await heartbeat.start()
1356
+ await asyncio.gather(
1357
+ bus.dispatch_inbound(),
1358
+ channels.start_all()
1359
+ )
1360
+ except KeyboardInterrupt:
1361
+ print("\nShutting down ...")
1362
+ heartbeat.stop()
1363
+ await cron.stop()
1364
+ await channels.stop_all()
1365
+ bus.stop()
1366
+
1367
+ asyncio.run(run())
1368
+
1369
+
1370
+
1371
+
1372
+
1373
+
1374
+
1375
+
660
1376
 
661
1377
  if __name__ == "__main__":
662
1378
  # pass
663
- asyncio.run(teminal_chat())
1379
+ # asyncio.run(teminal_chat())
664
1380
  # asyncio.run(agent_ui())
665
1381
  # asyncio.run(team_main())
666
1382
  # asyncio.run(asingle_agent())
667
1383
 
1384
+ # asyncio.run(claw_main())
1385
+ claw_main()
1386
+ # claw_terminal()
1387
+
1388
+
668
1389
 
669
1390
 
670
1391