@brandon_9527/tcode 1.0.8 → 1.0.9

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 +3 -3
  2. package/dist/python-src/README.md +40 -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 +753 -40
  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 +224 -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,15 @@ 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],
316
338
  } if run_mode == "sandbox" else {
317
- "filetools": [ bash, read_file, write_file, glob, grep, edit, list_dir],
318
- "web": [web_search, web_fetch]
339
+ "read-only": [ read_file, glob, grep, list_dir],
340
+ "edit": [ edit, write_file],
341
+ "search": [ web_search, web_fetch],
342
+ "bash": [ bash ],
319
343
  }
320
344
 
321
345
  context = SkillAgentContext(
@@ -376,7 +400,8 @@ async def team_main():
376
400
  # prompt_name="leader_",
377
401
  # WORKSPACE=workspace
378
402
  # ),
379
- system_prompt=apply_prompt(leader, WORKSPACE=workspace),
403
+ # system_prompt=apply_prompt(leader, WORKSPACE=workspace),
404
+ system_prompt=build_system_prompt(),
380
405
  checkpointer=saver
381
406
  ).with_config({"recursion_limit": recursion_limit})
382
407
 
@@ -386,7 +411,7 @@ async def team_main():
386
411
  workspace = os.getcwd()
387
412
  run_mode = "local"
388
413
  session_id = "1"
389
-
414
+ llm = get_default_model(streaming=True)
390
415
  # agents_conf, toolkits, context, saver, instruction_manager
391
416
  agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
392
417
  agent = _build_team(
@@ -476,11 +501,15 @@ def build_team():
476
501
  agents_conf = agent_manager.get_all_conf()
477
502
 
478
503
  toolkits = {
479
- "filetools": [ shell, read_file, write_file, glob, grep, edit, list_dir],
480
- "web": [web_search, web_fetch]
504
+ "read-only": [ read_file, glob, grep, list_dir],
505
+ "edit": [ edit, write_file],
506
+ "search": [ web_search, web_fetch],
507
+ "bash": [ shell],
481
508
  } if run_mode == "sandbox" else {
482
- "filetools": [ bash, read_file, write_file, glob, grep, edit, list_dir],
483
- "web": [web_search, web_fetch]
509
+ "read-only": [ read_file, glob, grep, list_dir],
510
+ "edit": [ edit, write_file],
511
+ "search": [ web_search, web_fetch],
512
+ "bash": [ bash ],
484
513
  }
485
514
 
486
515
  context = SkillAgentContext(
@@ -543,7 +572,8 @@ def build_team():
543
572
  middleware=[
544
573
  DynamicContentMiddleware(),
545
574
  ],
546
- system_prompt=apply_prompt(leader, WORKSPACE=workspace),
575
+ # system_prompt=apply_prompt(leader, WORKSPACE=workspace),
576
+ system_prompt=build_system_prompt(),
547
577
  checkpointer=saver
548
578
  ).with_config({"recursion_limit": recursion_limit})
549
579
 
@@ -553,7 +583,7 @@ def build_team():
553
583
  workspace = os.getcwd()
554
584
  run_mode = "local"
555
585
  session_id = "1"
556
-
586
+ llm = get_default_model(streaming=True)
557
587
  # agents_conf, toolkits, context, saver, instruction_manager
558
588
  agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
559
589
  agent = _build_team(
@@ -653,18 +683,701 @@ async def run_once(prompt:str = None, verbose: bool=True):
653
683
 
654
684
  finally:
655
685
  pass
686
+
687
+
688
+ def claw_main():
689
+ """ 以 claw mode 运行 """
690
+
691
+ from functools import partial
692
+ from pathlib import Path
693
+ import asyncio
694
+ import sys
695
+ import os
696
+
697
+ from src.claw.config.loader import load_config, get_data_dir
698
+ from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
699
+ from src.claw.channels.manager import ChannelManager
700
+ from src.claw.cron.service import CronService
701
+ from src.claw.cron.types_ import CronJob
702
+ from src.claw.heartbeat.service import HeartbeatService
703
+
704
+ from src.claw.tools.base import AgentContext
705
+ from src.claw.tools.cron import (
706
+ add_cron_job,
707
+ list_cron_jobs,
708
+ remove_cron_job
709
+ )
710
+ from src.tools.tools import (
711
+ SkillAgentContext,
712
+ shell,
713
+ bash,
714
+ write_file,
715
+ read_file,
716
+ list_dir,
717
+ glob,
718
+ grep,
719
+ edit
720
+ )
721
+
722
+ from src.tools.web import (
723
+ web_search,
724
+ web_fetch
725
+ )
726
+
727
+ from src.managers.sandbox import Container
728
+
729
+ from dotenv import load_dotenv, find_dotenv
730
+ from langchain.agents import create_agent
731
+ from langchain_openai import ChatOpenAI
732
+ from langgraph.checkpoint.memory import InMemorySaver
733
+ from langchain_core.tools import BaseTool
734
+
735
+
736
+ from src.core.deepagents import create_deep_agent, get_default_model
737
+ from src.managers.manager_context import ContextManager
738
+ from src.utils.prompt import apply_template, apply_prompt
739
+ from src.prompts.prompts import leader
740
+
741
+ from src.tui.clawtui import LiveChatUI
742
+
743
+ _ = load_dotenv(find_dotenv())
744
+
745
+ WORKSPACE = Path.cwd()
746
+
747
+
748
+ """
749
+ "appId": "cli_a909847e1278dcbd",
750
+ "appSecret": "bE0usuE0MKUJVDWOo9olib5X8PKv66pK",
751
+ """
752
+ channel_conf_path = WORKSPACE / "channel_config.json"
753
+ if not channel_conf_path.exists():
754
+ with open(channel_conf_path, "w") as f:
755
+ data = {
756
+ "channels": {
757
+ "feishu": {
758
+ "enabled": True,
759
+ "appId": "",
760
+ "appSecret": "",
761
+ "encryptKey": "",
762
+ "verificationToken": "",
763
+ "allowFrom": []
764
+ }
765
+ }
766
+ }
767
+ json.dump(data, f)
768
+
769
+ config = load_config(
770
+ config_path=channel_conf_path
771
+ )
772
+ config.channels.feishu.app_id = os.getenv("APP_ID", "")
773
+ config.channels.feishu.app_secret = os.getenv("APP_SECRET", "")
774
+
775
+ def _prepare(workspace, run_mode, session_id):
776
+ saver = InMemorySaver()
777
+
778
+ # 1. 构建上下文管理器
779
+ dockerfile_path = os.path.join(Path(__file__).parent, "resources", "dockerfiles", "sandbox", "Dockerfile")
780
+ context_manager = ContextManager(
781
+ dockerfile_path=dockerfile_path,
782
+ mode=run_mode
783
+ )
784
+
785
+ context_manager.create_environment(
786
+ session_id=session_id,
787
+ workspace_path=workspace
788
+ )
789
+
790
+ # 2. 指令管理器
791
+ instruction_manager = context_manager.get_instruction_manager(
792
+ session_id=session_id
793
+ )
794
+
795
+ # 3. 代理管理器
796
+ agent_manager = context_manager.get_agent_manager(
797
+ session_id=session_id
798
+ )
799
+
800
+ # 4. 沙箱
801
+ sandbox = context_manager.get_container(
802
+ session_id=session_id
803
+ )
804
+
805
+ # 5. 工具箱
806
+
807
+ toolkits = {
808
+ "read-only": [ read_file, glob, grep, list_dir],
809
+ "edit": [ edit, write_file],
810
+ "search": [ web_search, web_fetch],
811
+ "bash": [ shell],
812
+ } if run_mode == "sandbox" else {
813
+ "read-only": [ read_file, glob, grep, list_dir],
814
+ "edit": [ edit, write_file],
815
+ "search": [ web_search, web_fetch],
816
+ "bash": [ bash ],
817
+ }
656
818
 
819
+ # 6. 配置
820
+ agents_conf = agent_manager.get_all_conf()
821
+
822
+ context = AgentContext(
823
+ working_directory=workspace,
824
+ sandbox=sandbox,
825
+ channel=None,
826
+ chat_id=None,
827
+ cron_service=None,
828
+ workspace=workspace,
829
+ )
830
+
831
+ return agents_conf, toolkits, context, saver, instruction_manager
832
+
833
+ 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):
834
+
835
+ team_conf = agents_conf.setdefault(domain, {"members": {}})
836
+
837
+ agents = []
838
+ for member_name, member in team_conf["members"].items():
839
+
840
+ tools = []
841
+ for tool_name in member["tools"]:
842
+ tools.extend(toolkits.get(tool_name, []))
843
+
844
+ # print(f"Building agent {member_name}")
845
+ # print(f"Tools: {tools}")
846
+ # print(f"Member: {member}")
847
+ # sys.exit()
848
+
849
+ llm_conf = member.get("llm", None)
850
+ model = ChatOpenAI(**llm_conf) if llm_conf else get_default_model(streaming=False)
851
+
852
+ agent = {
853
+ "name": member_name,
854
+ "description": member["description"],
855
+ "system_prompt": apply_template(
856
+ agents_config=agents_conf,
857
+ domain=domain,
858
+ prompt_name=member_name,
859
+ WORKSPACE=workspace
860
+ ),
861
+ "context_schema": SkillAgentContext,
862
+ "tools": tools,
863
+ "model": model
864
+ }
865
+
866
+ agents.append(agent)
867
+
868
+ tools = []
869
+ for tool_name in ["filetools", "web"]:
870
+ tools.extend([tool for tool in toolkits.get(tool_name, [])])
871
+
872
+ tools.extend([add_cron_job, list_cron_jobs, remove_cron_job])
873
+
874
+ team = create_deep_agent(
875
+ model=llm,
876
+ subagents=agents,
877
+ tools=tools,
878
+ middleware=[SkillMiddleware(workspace=workspace, home_path=str(Path.home()))],
879
+ context_schema=AgentContext,
880
+ system_prompt=apply_prompt(leader, WORKSPACE=workspace),
881
+ # system_prompt=build_system_prompt(),
882
+ checkpointer=saver
883
+ ).with_config({"recursion_limit": recursion_limit})
884
+
885
+ return team
657
886
 
658
887
 
888
+ workspace = os.getcwd()
889
+ run_mode = "local"
890
+ session_id = "1"
891
+
892
+ llm = get_default_model(streaming=True)
893
+ agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
894
+ agent = _build_team(
895
+ agents_conf=agents_config,
896
+ domain="coding",
897
+ llm=llm,
898
+ toolkits=toolkits,
899
+ workspace=workspace,
900
+ saver=saver,
901
+ store=None,
902
+ recursion_limit=1000
903
+ )
904
+
905
+ # 创建事件总线
906
+ bus = MessageBus()
907
+ # 创建周期性任务
908
+ cron_store_path = Path(os.getcwd()) /".autodev"/"cron"/"jobs.json"
909
+ cron_store_path.parent.mkdir(parents=True, exist_ok=True)
910
+ cron = CronService(cron_store_path)
911
+
912
+ # 设置周期回调函数
913
+ async def on_cron_job(job: CronJob) -> str | None:
914
+ """ Execute a cron job through the agent. """
915
+ session_key = f"cron:{job.id}"
916
+ channel = job.payload.channel or "cli"
917
+ chat_id = job.payload.to or "direct"
918
+ response = await agent.ainvoke(
919
+ {"messages": [{"role": "user", "content": job.payload.message}]},
920
+ context=AgentContext(
921
+ working_directory=workspace,
922
+ sandbox=None,
923
+ channel=channel,
924
+ chat_id=chat_id,
925
+ cron_service=cron,
926
+ workspace=workspace
927
+ )
928
+ )
659
929
 
930
+ resp = response["messages"][-1].content
931
+
932
+ if job.payload.deliver and job.payload.to:
933
+ from src.claw.bus.events import OutboundMessage
934
+ await bus.publish_outbound(
935
+ OutboundMessage(
936
+ channel=channel,
937
+ chat_id=chat_id,
938
+ content=resp or ""
939
+ ))
940
+
941
+ return resp
942
+
943
+ cron.on_job = on_cron_job
944
+
945
+ # 创建心跳服务
946
+ async def on_heartbeat(prompt: str) -> str:
947
+ """ Execute a heartbeat through the agent. """
948
+ print(f"execute Heartbeat prompt: {prompt}")
949
+ response = await agent.ainvoke(
950
+ {"messages": [{"role": "user", "content": prompt}]},
951
+ context=AgentContext(
952
+ working_directory=workspace,
953
+ sandbox=None,
954
+ channel="cli",
955
+ chat_id="direct",
956
+ cron_service=cron,
957
+ workspace=workspace,
958
+ )
959
+ )
960
+
961
+ resp = response["messages"][-1].content
962
+ return resp
963
+
964
+ heartbeat = HeartbeatService(
965
+ workspace=Path(workspace),
966
+ on_heartbeat=on_heartbeat,
967
+ interval_s=10,
968
+ enabled=True
969
+ )
970
+
971
+ # 创建渠道管理器
972
+ channels = ChannelManager(
973
+ config,
974
+ bus=bus,
975
+ )
976
+
977
+ ui = LiveChatUI(
978
+ agent=agent,
979
+ saver=saver,
980
+ workspace=str(workspace),
981
+ instruction_manager=instruction_manager,
982
+ channels=channels,
983
+ heartbeat=heartbeat,
984
+ bus=bus,
985
+ cron=cron,
986
+ context=context
987
+ )
988
+
989
+ asyncio.run(ui.alisten())
990
+
991
+ # await ui.alisten()
992
+
993
+
994
+
995
+ async def _process_message(msg: InboundMessage, agent:Any, bus: MessageBus, cron: CronService) -> OutboundMessage | None:
996
+ """ """
997
+ if msg.channel == "system":
998
+ response = await _process_system_message(msg, agent, cron)
999
+ await bus.publish_outbound(response)
1000
+
1001
+ print(f"user>{msg.content}")
1002
+ session_key = f"{msg.channel}:{msg.chat_id}"
1003
+ config = {"configurable": {"thread_id": session_key}}
1004
+
1005
+ response = await agent.ainvoke(
1006
+ {"messages": [{"role": "user", "content": msg.content}]},
1007
+ config=config,
1008
+ context=AgentContext(
1009
+ working_directory=Path.cwd(),
1010
+ sandbox=None,
1011
+ channel=msg.channel,
1012
+ chat_id=msg.chat_id,
1013
+ cron_service=cron,
1014
+ workspace=Path.cwd(),
1015
+ )
1016
+ )
1017
+ print(f"Bot>{response['messages'][-1].content}")
1018
+
1019
+ response = OutboundMessage(
1020
+ channel=msg.channel,
1021
+ chat_id=msg.chat_id,
1022
+ content=response["messages"][-1].content,
1023
+ metadata=msg.metadata or {}
1024
+ )
1025
+
1026
+ await bus.publish_outbound(response)
1027
+
1028
+
1029
+ async def _process_system_message(msg: InboundMessage, agent: Any, cron: CronService) -> OutboundMessage | None:
1030
+ """
1031
+ Process a system message (e.g. subagent announce).
1032
+ """
1033
+ # logger.info(f"Processing system message from {msg.sender_id}")
1034
+
1035
+ # Parse origin from chat_id (format: "channel:chat_id")
1036
+ if ":" in msg.chat_id:
1037
+ parts = msg.chat_id.split(":", 1)
1038
+ origin_channel = parts[0]
1039
+ origin_chat_id = parts[1]
1040
+ else:
1041
+ # Fallback
1042
+ origin_channel = "cli"
1043
+ origin_chat_id = msg.chat_id
1044
+
1045
+ # Use the origin session for context
1046
+ session_key = f"{origin_channel}:{origin_chat_id}"
1047
+ config = {"configurable": {"thread_id": session_key}}
1048
+ response = await agent.ainvoke(
1049
+ {"message": msg.content},
1050
+ config=config,
1051
+ context=AgentContext(
1052
+ working_directory=Path.cwd(),
1053
+ sandbox=None,
1054
+ channel=origin_channel,
1055
+ chat_id=origin_chat_id,
1056
+ cron_service=cron,
1057
+ workspace=Path.cwd(),
1058
+ )
1059
+ )
1060
+
1061
+ return OutboundMessage(
1062
+ channel=origin_channel,
1063
+ chat_id=origin_chat_id,
1064
+ content=response["messages"][-1].content,
1065
+ metadata=msg.metadata or {}
1066
+ )
1067
+
1068
+ def claw_terminal():
1069
+ """ 以 claw mode 运行 """
1070
+
1071
+ from functools import partial
1072
+ from pathlib import Path
1073
+ import asyncio
1074
+ import sys
1075
+ import os
1076
+
1077
+ from src.claw.config.loader import load_config, get_data_dir
1078
+ from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
1079
+ from src.claw.channels.manager import ChannelManager
1080
+ from src.claw.cron.service import CronService
1081
+ from src.claw.cron.types_ import CronJob
1082
+ from src.claw.heartbeat.service import HeartbeatService
1083
+
1084
+ from src.claw.tools.base import AgentContext
1085
+ from src.claw.tools.cron import (
1086
+ add_cron_job,
1087
+ list_cron_jobs,
1088
+ remove_cron_job
1089
+ )
1090
+ from src.tools.tools import (
1091
+ SkillAgentContext,
1092
+ shell,
1093
+ bash,
1094
+ write_file,
1095
+ read_file,
1096
+ list_dir,
1097
+ glob,
1098
+ grep,
1099
+ edit
1100
+ )
1101
+
1102
+ from src.tools.web import (
1103
+ web_search,
1104
+ web_fetch
1105
+ )
1106
+
1107
+ from src.managers.sandbox import Container
1108
+
1109
+ from dotenv import load_dotenv, find_dotenv
1110
+ from langchain.agents import create_agent
1111
+ from langchain_openai import ChatOpenAI
1112
+ from langgraph.checkpoint.memory import InMemorySaver
1113
+ from langchain_core.tools import BaseTool
1114
+
1115
+
1116
+ from src.core.deepagents import create_deep_agent, get_default_model
1117
+ from src.managers.manager_context import ContextManager
1118
+ from src.utils.prompt import apply_template, apply_prompt
1119
+ from src.prompts.prompts import leader
1120
+ from functools import partial
1121
+
1122
+ from src.tui.clawtui import LiveChatUI
1123
+
1124
+ _ = load_dotenv(find_dotenv())
1125
+
1126
+ WORKSPACE = Path.cwd()
1127
+
1128
+ conf_path = WORKSPACE / "config.json"
1129
+ if not conf_path.exists():
1130
+ with open(conf_path, "w") as f:
1131
+ data = {
1132
+ "channels": {
1133
+ "feishu": {
1134
+ "enabled": True,
1135
+ "appId": "cli_a909847e1278dcbd",
1136
+ "appSecret": "bE0usuE0MKUJVDWOo9olib5X8PKv66pK",
1137
+ "encryptKey": "",
1138
+ "verificationToken": "",
1139
+ "allowFrom": []
1140
+ }
1141
+ }
1142
+ }
1143
+ json.dump(data, f)
1144
+
1145
+ config = load_config(
1146
+ config_path=conf_path
1147
+ )
1148
+
1149
+ def _prepare(workspace, run_mode, session_id):
1150
+ saver = InMemorySaver()
1151
+
1152
+ # 1. 构建上下文管理器
1153
+ dockerfile_path = os.path.join(Path(__file__).parent, "resources", "dockerfiles", "sandbox", "Dockerfile")
1154
+ context_manager = ContextManager(
1155
+ dockerfile_path=dockerfile_path,
1156
+ mode=run_mode
1157
+ )
1158
+
1159
+ context_manager.create_environment(
1160
+ session_id=session_id,
1161
+ workspace_path=workspace
1162
+ )
1163
+
1164
+ # 2. 指令管理器
1165
+ instruction_manager = context_manager.get_instruction_manager(
1166
+ session_id=session_id
1167
+ )
1168
+
1169
+ # 3. 代理管理器
1170
+ agent_manager = context_manager.get_agent_manager(
1171
+ session_id=session_id
1172
+ )
1173
+
1174
+ # 4. 沙箱
1175
+ sandbox = context_manager.get_container(
1176
+ session_id=session_id
1177
+ )
1178
+
1179
+ # 5. 工具箱
1180
+ toolkits = {
1181
+ "read-only": [ read_file, glob, grep, list_dir],
1182
+ "edit": [ edit, write_file],
1183
+ "search": [ web_search, web_fetch],
1184
+ "bash": [ shell],
1185
+ } if run_mode == "sandbox" else {
1186
+ "read-only": [ read_file, glob, grep, list_dir],
1187
+ "edit": [ edit, write_file],
1188
+ "search": [ web_search, web_fetch],
1189
+ "bash": [ bash ],
1190
+ }
1191
+
1192
+ # 6. 配置
1193
+ agents_conf = agent_manager.get_all_conf()
1194
+
1195
+ context = AgentContext(
1196
+ working_directory=workspace,
1197
+ sandbox=sandbox,
1198
+ channel=None,
1199
+ chat_id=None,
1200
+ cron_service=None,
1201
+ workspace=workspace,
1202
+ )
1203
+
1204
+ return agents_conf, toolkits, context, saver, instruction_manager
1205
+
1206
+ 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):
1207
+
1208
+ team_conf = agents_conf.setdefault(domain, {"members": {}})
1209
+
1210
+ agents = []
1211
+ for member_name, member in team_conf["members"].items():
1212
+
1213
+ tools = []
1214
+ for tool_name in member["tools"]:
1215
+ tools.extend(toolkits.get(tool_name, []))
1216
+
1217
+ llm_conf = member.get("llm", None)
1218
+ model = ChatOpenAI(**llm_conf) if llm_conf else get_default_model(streaming=False)
1219
+
1220
+ agent = {
1221
+ "name": member_name,
1222
+ "description": member["description"],
1223
+ "system_prompt": apply_template(
1224
+ agents_config=agents_conf,
1225
+ domain=domain,
1226
+ prompt_name=member_name,
1227
+ WORKSPACE=workspace
1228
+ ),
1229
+ "context_schema": SkillAgentContext,
1230
+ "tools": tools,
1231
+ "model": model
1232
+ }
1233
+
1234
+ agents.append(agent)
1235
+
1236
+ tools = []
1237
+ for tool_name in ["filetools", "web"]:
1238
+ tools.extend([tool for tool in toolkits.get(tool_name, [])])
1239
+
1240
+ team = create_deep_agent(
1241
+ model=llm,
1242
+ subagents=agents,
1243
+ tools=tools,
1244
+ context_schema=AgentContext,
1245
+ # system_prompt=apply_prompt(leader, WORKSPACE=workspace),
1246
+ system_prompt=build_system_prompt(),
1247
+ checkpointer=saver
1248
+ ).with_config({"recursion_limit": recursion_limit})
1249
+
1250
+ return team
1251
+
1252
+
1253
+ workspace = os.getcwd()
1254
+ run_mode = "local"
1255
+ session_id = "1"
1256
+ llm = get_default_model(streaming=True)
1257
+ agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
1258
+ agent = _build_team(
1259
+ agents_conf=agents_config,
1260
+ domain="coding",
1261
+ llm=llm,
1262
+ toolkits=toolkits,
1263
+ workspace=workspace,
1264
+ saver=saver,
1265
+ store=None,
1266
+ recursion_limit=1000
1267
+ )
1268
+
1269
+ # 创建事件总线
1270
+ bus = MessageBus()
1271
+ # 创建周期性任务
1272
+ cron_store_path = Path(os.getcwd()) /".autodev"/"cron"/"jobs.json"
1273
+ cron_store_path.parent.mkdir(parents=True, exist_ok=True)
1274
+ cron = CronService(cron_store_path)
1275
+
1276
+ # 设置周期回调函数
1277
+ async def on_cron_job(job: CronJob) -> str | None:
1278
+ """ Execute a cron job through the agent. """
1279
+ session_key = f"cron:{job.id}"
1280
+ channel = job.payload.channel or "cli"
1281
+ chat_id = job.payload.to or "direct"
1282
+ response = await agent.ainvoke(
1283
+ {"messages": [{"role": "user", "content": job.payload.message}]},
1284
+ context=AgentContext(
1285
+ working_directory=workspace,
1286
+ sandbox=None,
1287
+ channel=channel,
1288
+ chat_id=chat_id,
1289
+ cron_service=cron,
1290
+ workspace=workspace
1291
+ )
1292
+ )
1293
+
1294
+ resp = response["messages"][-1].content
1295
+
1296
+ if job.payload.deliver and job.payload.to:
1297
+ from src.claw.bus.events import OutboundMessage
1298
+ await bus.publish_outbound(
1299
+ OutboundMessage(
1300
+ channel=channel,
1301
+ chat_id=chat_id,
1302
+ content=resp or ""
1303
+ ))
1304
+
1305
+ return resp
1306
+
1307
+ cron.on_job = on_cron_job
1308
+
1309
+ # 创建心跳服务
1310
+ async def on_heartbeat(prompt: str) -> str:
1311
+ """ Execute a heartbeat through the agent. """
1312
+ print(f"execute Heartbeat prompt: {prompt}")
1313
+ response = await agent.ainvoke(
1314
+ {"messages": [{"role": "user", "content": prompt}]},
1315
+ context=AgentContext(
1316
+ working_directory=workspace,
1317
+ sandbox=None,
1318
+ channel="cli",
1319
+ chat_id="direct",
1320
+ cron_service=cron,
1321
+ workspace=workspace,
1322
+ )
1323
+ )
1324
+
1325
+ resp = response["messages"][-1].content
1326
+ return resp
1327
+
1328
+ heartbeat = HeartbeatService(
1329
+ workspace=Path(workspace),
1330
+ on_heartbeat=on_heartbeat,
1331
+ interval_s=10,
1332
+ enabled=True
1333
+ )
1334
+
1335
+ # 创建渠道管理器
1336
+ channels = ChannelManager(
1337
+ config,
1338
+ bus=bus,
1339
+ )
1340
+
1341
+
1342
+ bus.subscribe_inbound("feishu", partial(_process_message, agent=agent, bus=bus, cron=cron))
1343
+
1344
+ async def run():
1345
+ try:
1346
+ await cron.start()
1347
+ await heartbeat.start()
1348
+ await asyncio.gather(
1349
+ bus.dispatch_inbound(),
1350
+ channels.start_all()
1351
+ )
1352
+ except KeyboardInterrupt:
1353
+ print("\nShutting down ...")
1354
+ heartbeat.stop()
1355
+ await cron.stop()
1356
+ await channels.stop_all()
1357
+ bus.stop()
1358
+
1359
+ asyncio.run(run())
1360
+
1361
+
1362
+
1363
+
1364
+
1365
+
1366
+
1367
+
660
1368
 
661
1369
  if __name__ == "__main__":
662
1370
  # pass
663
- asyncio.run(teminal_chat())
1371
+ # asyncio.run(teminal_chat())
664
1372
  # asyncio.run(agent_ui())
665
1373
  # asyncio.run(team_main())
666
1374
  # asyncio.run(asingle_agent())
667
1375
 
1376
+ # asyncio.run(claw_main())
1377
+ claw_main()
1378
+ # claw_terminal()
1379
+
1380
+
668
1381
 
669
1382
 
670
1383