langchain-agentx-python 1.2.1__py3-none-any.whl → 1.2.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.
@@ -11,7 +11,7 @@ from langchain_agentx import create_loop_agent
11
11
  ```
12
12
  """
13
13
 
14
- __version__ = "1.2.1"
14
+ __version__ = "1.2.2"
15
15
 
16
16
  from .loop import ( # noqa: F401
17
17
  create_loop_agent,
@@ -249,6 +249,9 @@ class BackgroundTask:
249
249
  pid: int
250
250
  """后台进程 PID。"""
251
251
 
252
+ command: str = ""
253
+ """原始命令(用于终态语义解析;亦持久化至 task_dir/command.txt)。"""
254
+
252
255
  sandboxed: bool = False
253
256
  """后台任务是否在沙箱模式下执行。"""
254
257
 
@@ -365,6 +368,7 @@ class LocalTmpdirSandboxBackend:
365
368
  task_id=task.task_id,
366
369
  output_path=task.output_path,
367
370
  pid=task.pid,
371
+ command=request.command,
368
372
  sandboxed=True,
369
373
  sandbox_temp_dir=temp_dir,
370
374
  )
@@ -1556,12 +1560,17 @@ class BashBackend:
1556
1560
  with open(pid_path, "w") as f:
1557
1561
  f.write(str(proc.pid))
1558
1562
 
1563
+ command_path = os.path.join(task_dir, "command.txt")
1564
+ with open(command_path, "w", encoding="utf-8") as f:
1565
+ f.write(command)
1566
+
1559
1567
  output_file.close()
1560
1568
 
1561
1569
  return BackgroundTask(
1562
1570
  task_id=task_id,
1563
1571
  output_path=output_path,
1564
1572
  pid=proc.pid,
1573
+ command=command,
1565
1574
  )
1566
1575
 
1567
1576
  def get_background_status(self, task: BackgroundTask) -> BackgroundTaskStatus:
@@ -1619,23 +1628,25 @@ class BashBackend:
1619
1628
  )
1620
1629
 
1621
1630
  @staticmethod
1622
- def build_foreground_snapshot(result: ExecuteResult) -> BashTaskSnapshot:
1631
+ def build_foreground_snapshot(
1632
+ result: ExecuteResult,
1633
+ *,
1634
+ terminal_status: str,
1635
+ ) -> BashTaskSnapshot:
1636
+ """构建 foreground 快照;终态由 semantics.resolve_terminal_task_status 决定。"""
1623
1637
  t0 = time.time()
1624
1638
  sm = BashTaskStateMachine(task_id="foreground", task_mode="foreground")
1625
1639
  sm.created_ts = t0
1626
1640
  sm.transition_to("running")
1627
1641
  sm.started_ts = time.time()
1628
- if result.interrupted:
1629
- sm.transition_to("interrupted")
1630
- elif result.exit_code == 0:
1631
- sm.transition_to("completed")
1632
- else:
1633
- sm.transition_to("failed")
1642
+ sm.transition_to(terminal_status)
1634
1643
  sm.ended_ts = time.time()
1635
1644
  sm.exit_code = result.exit_code
1636
1645
  return sm.to_snapshot()
1637
1646
 
1638
1647
  def build_background_snapshot(self, task: BackgroundTask) -> BashTaskSnapshot:
1648
+ from .semantics import resolve_background_terminal_status
1649
+
1639
1650
  status = self.get_background_status(task)
1640
1651
  sm = BashTaskStateMachine(task_id=task.task_id, task_mode="background")
1641
1652
  sm.created_ts = time.time()
@@ -1647,17 +1658,36 @@ class BashBackend:
1647
1658
  if status.running:
1648
1659
  return sm.to_snapshot()
1649
1660
  sm.ended_ts = time.time()
1650
- if status.exit_code == -2:
1651
- sm.transition_to("cancelled")
1652
- elif status.exit_code == 0:
1653
- sm.transition_to("completed")
1654
- elif status.exit_code is None:
1655
- sm.transition_to("interrupted")
1656
- else:
1657
- sm.transition_to("failed")
1661
+ command = task.command or self._read_persisted_background_command(task)
1662
+ stdout = self._read_background_output_text(task.output_path)
1663
+ terminal_status = resolve_background_terminal_status(
1664
+ exit_code=status.exit_code,
1665
+ command=command,
1666
+ stdout=stdout,
1667
+ )
1668
+ sm.transition_to(terminal_status)
1658
1669
  sm.exit_code = status.exit_code
1659
1670
  return sm.to_snapshot()
1660
1671
 
1672
+ @staticmethod
1673
+ def _read_persisted_background_command(task: BackgroundTask) -> str:
1674
+ command_path = os.path.join(os.path.dirname(task.output_path), "command.txt")
1675
+ try:
1676
+ with open(command_path, encoding="utf-8") as f:
1677
+ return f.read().strip()
1678
+ except OSError:
1679
+ return ""
1680
+
1681
+ @staticmethod
1682
+ def _read_background_output_text(output_path: str) -> str:
1683
+ if not os.path.exists(output_path):
1684
+ return ""
1685
+ try:
1686
+ with open(output_path, encoding="utf-8", errors="replace") as f:
1687
+ return f.read()
1688
+ except OSError:
1689
+ return ""
1690
+
1661
1691
  def cancel_background_task(self, task: BackgroundTask) -> bool:
1662
1692
  """
1663
1693
  取消后台任务:发信号终止进程,并标记为 cancelled(exit_code 文件写 -2)。
@@ -143,16 +143,10 @@ class BashResultPresenter:
143
143
  )
144
144
 
145
145
  summary = description_text or f"Ran: {command[:60]}"
146
- # 构建 payload:合并 stdout 和 stderr(与 CC mapToolResultToToolResultBlockParam 对齐)
147
146
  stdout_text = result.stdout or ""
148
147
  stderr_text = result.stderr or ""
149
- if stdout_text and stderr_text:
150
- payload = f"{stdout_text}\n{stderr_text}"
151
- elif stderr_text:
152
- payload = stderr_text
153
- else:
154
- payload = stdout_text or "(No output)"
155
- hints = self._build_hints(result)
148
+ payload, skip_interpretation_hint = self._resolve_foreground_payload(result)
149
+ hints = self._build_hints(result, skip_interpretation=skip_interpretation_hint)
156
150
  if result.output_truncated and result.overflow_file:
157
151
  import os
158
152
 
@@ -200,14 +194,7 @@ class BashResultPresenter:
200
194
  tool_name=tool_name,
201
195
  summary=summary,
202
196
  payload=payload,
203
- blocks=[
204
- ToolContentBlock(
205
- type="status",
206
- text=f"task:{result.task_status}",
207
- meta={"task_mode": result.task_mode, "task_exit_code": result.task_exit_code},
208
- ),
209
- ToolContentBlock(type="text", text=payload),
210
- ],
197
+ blocks=[ToolContentBlock(type="text", text=payload)],
211
198
  hints=hints or None,
212
199
  meta=self._attach_observability({
213
200
  "exit_code": result.exit_code,
@@ -316,10 +303,36 @@ class BashResultPresenter:
316
303
  meta=self._attach_observability({"exit_code": result.exit_code, "command": command}, result),
317
304
  )
318
305
 
319
- def _build_hints(self, result: BashToolOutput) -> list[ToolHint]:
320
- hints: list[ToolHint] = []
306
+ def _resolve_foreground_payload(self, result: BashToolOutput) -> tuple[str, bool]:
307
+ """
308
+ CC BashToolResultMessage:空输出时优先展示 returnCodeInterpretation。
321
309
 
310
+ Returns:
311
+ (payload, skip_interpretation_hint) — payload 已含 interpretation 时不再重复 hint。
312
+ """
313
+ stdout_text = result.stdout or ""
314
+ stderr_text = result.stderr or ""
315
+ if stdout_text.strip() or stderr_text.strip():
316
+ if stdout_text and stderr_text:
317
+ return f"{stdout_text}\n{stderr_text}", False
318
+ if stderr_text:
319
+ return stderr_text, False
320
+ return stdout_text, False
322
321
  if result.return_code_interpretation:
322
+ return result.return_code_interpretation, True
323
+ if result.no_output_expected:
324
+ return "Done", False
325
+ return "(No output)", False
326
+
327
+ def _build_hints(
328
+ self,
329
+ result: BashToolOutput,
330
+ *,
331
+ skip_interpretation: bool = False,
332
+ ) -> list[ToolHint]:
333
+ hints: list[ToolHint] = []
334
+
335
+ if result.return_code_interpretation and not skip_interpretation:
323
336
  hints.append(ToolHint(message=result.return_code_interpretation))
324
337
 
325
338
  if result.destructive_warning:
@@ -17,7 +17,8 @@ from __future__ import annotations
17
17
  import re
18
18
  from dataclasses import dataclass
19
19
 
20
- # tree/find/ls 在 Windows Git Bash 等环境常不可用(exit 127);CC 引导用 Glob 专用工具。
20
+ # tree/find/ls 在 Windows Git Bash 等环境常不可用(exit 127)。
21
+ # SDK 扩展(相对 CC DEFAULT_SEMANTIC 硬失败):软成功 + Glob hint,便于跨平台引导。
21
22
  _FILE_LISTING_COMMANDS = re.compile(r"^(?:tree|find|ls)\b")
22
23
  _FILE_LISTING_EXIT_127_HINT = (
23
24
  "Command not found (exit 127). "
@@ -86,6 +87,46 @@ class CommandResult:
86
87
  message: str | None = None
87
88
 
88
89
 
90
+ def resolve_terminal_task_status(
91
+ *,
92
+ interrupted: bool,
93
+ exit_code: int,
94
+ semantic: CommandResult,
95
+ ) -> str:
96
+ """
97
+ 由 interpret_command_result 派生任务终态(SSOT)。
98
+
99
+ 对应 CC BashTool.call:interpretCommandResult → throw ShellError 或成功返回;
100
+ SDK 额外保留 task_status 供 observability,但不得再用 raw exit_code 独立判定。
101
+ """
102
+ if interrupted:
103
+ return "interrupted"
104
+ if semantic.is_error and exit_code != 0:
105
+ return "failed"
106
+ return "completed"
107
+
108
+
109
+ def resolve_background_terminal_status(
110
+ *,
111
+ exit_code: int | None,
112
+ command: str,
113
+ stdout: str = "",
114
+ ) -> str:
115
+ """
116
+ 后台任务终态:cancelled/interrupted 走协议码;其余与 foreground 共用 semantics SSOT。
117
+ """
118
+ if exit_code == -2:
119
+ return "cancelled"
120
+ if exit_code is None:
121
+ return "interrupted"
122
+ semantic = interpret_command_result(command, exit_code, stdout)
123
+ return resolve_terminal_task_status(
124
+ interrupted=False,
125
+ exit_code=exit_code,
126
+ semantic=semantic,
127
+ )
128
+
129
+
89
130
  def interpret_command_result(
90
131
  command: str,
91
132
  exit_code: int,
@@ -67,7 +67,7 @@ from .security import (
67
67
  detect_infinite_output,
68
68
  detect_sleep_block,
69
69
  )
70
- from .semantics import interpret_command_result
70
+ from .semantics import interpret_command_result, resolve_terminal_task_status
71
71
 
72
72
 
73
73
  def _as_bash_input_dict(data: dict[str, Any] | BashToolInput) -> dict[str, Any]:
@@ -728,12 +728,20 @@ class BashRuntimeTool(RuntimeTool):
728
728
 
729
729
  # 危险命令警告(非阻断)
730
730
  destructive_warning = detect_destructive_patterns(command)
731
- snapshot = self._backend.build_foreground_snapshot(result)
732
731
 
733
- # 退出码语义解析
732
+ # 退出码语义(SSOT:与 CC interpretCommandResult 一致,驱动 ok/error 与 task_status)
734
733
  cmd_result = interpret_command_result(
735
734
  command, result.exit_code, result.stdout
736
735
  )
736
+ terminal_status = resolve_terminal_task_status(
737
+ interrupted=result.interrupted,
738
+ exit_code=result.exit_code,
739
+ semantic=cmd_result,
740
+ )
741
+ snapshot = self._backend.build_foreground_snapshot(
742
+ result,
743
+ terminal_status=terminal_status,
744
+ )
737
745
 
738
746
  # 真正错误时抛异常(pipeline 捕获 → error envelope)
739
747
  if cmd_result.is_error and result.exit_code != 0 and not result.sandbox_violation_message:
@@ -881,6 +889,8 @@ class BashRuntimeTool(RuntimeTool):
881
889
  "interrupted": result.interrupted,
882
890
  "task_mode": result.task_mode,
883
891
  "task_status": result.task_status,
892
+ "return_code_interpretation": result.return_code_interpretation,
893
+ "no_output_expected": result.no_output_expected,
884
894
  }
885
895
 
886
896
  def to_auto_classifier_input(self, data: dict[str, Any]) -> str | None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-agentx-python
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: LangChain/LangGraph-based agent utilities for CodeBaseX.
5
5
  Author-email: GoodMood2008 <GoodMood2008@users.noreply.github.com>
6
6
  License: Apache License
@@ -1,4 +1,4 @@
1
- langchain_agentx/__init__.py,sha256=2slxrsn6pOSUn5oww3M4IxJKjH6UFPL1vfKusoiXytI,1678
1
+ langchain_agentx/__init__.py,sha256=gLxjoC92OvVi3mha5vefLza8Zsq5gQWFey0SROZ93nQ,1678
2
2
  langchain_agentx/command/__init__.py,sha256=Ej260S5uFQcmEtGZQG_NH7BYjHoStrpBLtGUsBTxyZk,631
3
3
  langchain_agentx/command/allowed_tools.py,sha256=TVA0VM8rm98H-QbLK_GRiX5RhEAZFxKawliMi4kk96A,3359
4
4
  langchain_agentx/command/context.py,sha256=DIGOGPBw5UGDBm0WNNJlKx1h_9rCRmcdROv4DT61Qug,731
@@ -477,7 +477,7 @@ langchain_agentx/tools/ask_user_question/validators.py,sha256=gUYuuKJNrExWGvhFlm
477
477
  langchain_agentx/tools/bash/__init__.py,sha256=molPHFDylE2wWG_aQx7fb0ciAx3wWpdXzIixTZV5ne4,214
478
478
  langchain_agentx/tools/bash/ast_security.py,sha256=-MlvQ559dR3vCactzAEnNAFEJB-QhZDXmDIqpPPpqR0,20874
479
479
  langchain_agentx/tools/bash/auto_mode_adapter.py,sha256=7-yb_kGmpytBYkD2Hn5YWPIceo_YN4PatEKj8O7HKsM,4243
480
- langchain_agentx/tools/bash/backend.py,sha256=_F1KEyEbNP20F9l2jzEFwEtm8KEgke2V4WTb1fETyss,62651
480
+ langchain_agentx/tools/bash/backend.py,sha256=6aTUefDv-z743kmlkVyt7mwtSiSJQFMue7f6FYcsl_I,63767
481
481
  langchain_agentx/tools/bash/bash_hardening.py,sha256=KEYKRC7kS_to4mzXuChvCmOIIb4dSUkOb9K1MBDtWDc,29296
482
482
  langchain_agentx/tools/bash/bash_runtime_contract.py,sha256=s8vrvMl7405KLGw2hPFbzlzWohm5JX3c9cImMBgqh8I,1511
483
483
  langchain_agentx/tools/bash/boundary_check.py,sha256=3B_Epe2NEJtLcNM3anNiCdOJdyNj_B3mOZ0mT_0bnpY,8010
@@ -494,17 +494,17 @@ langchain_agentx/tools/bash/output_utils.py,sha256=WYU2vVOjo9HQKSeQ_z88KxJsrvlFm
494
494
  langchain_agentx/tools/bash/path_security.py,sha256=L7zFXoyBDYLtd2bVLIeMg-xuCkSD7-Tt2EKlvKR_ssI,94984
495
495
  langchain_agentx/tools/bash/prompt.py,sha256=28WBRLt5G6e-OUildOFbeEyw7eL3iI8T40pEwmWVYxo,12527
496
496
  langchain_agentx/tools/bash/read_only_validation.py,sha256=rtNW6nK7sQMzR6LGc420kUd5wE30DnYX8jjuW7hUFWA,24822
497
- langchain_agentx/tools/bash/result_presenter.py,sha256=bUqpvcP2mo0FfOnI2v3Gu9mQGBATQQrHRL5dDYiv-yU,14420
497
+ langchain_agentx/tools/bash/result_presenter.py,sha256=OkJid40AmrAsRxhY7jSaw_PgRMoIfMcj6BNXaqcryTo,14946
498
498
  langchain_agentx/tools/bash/sandbox_decision.py,sha256=T_JaTfPADDcH2mRtrpKTBbIVo2Q6CHkzg_aUXCuDhLQ,4612
499
499
  langchain_agentx/tools/bash/security.py,sha256=YM34IUvNMDskoEA-WTG7hiuziY90DAFqQngpntGOtEc,7807
500
500
  langchain_agentx/tools/bash/sed_edit_parser.py,sha256=tTDLMj5f5C4nkcf1Ykn9uMKJtEQNm3ejrKQOal2sI9s,7855
501
501
  langchain_agentx/tools/bash/sed_validation.py,sha256=HA-zWe8Wo4LbgC9x8ZMQLzkd73i4IFeay0GhNb2qM20,5704
502
502
  langchain_agentx/tools/bash/semantic_matrix.py,sha256=rWO31jB2TxOSmCAMYKe3bkQ6YOHU7pCMX0--OABP5kM,3184
503
- langchain_agentx/tools/bash/semantics.py,sha256=JCewxOYCTGgYUsXXuCrc92ZWcIVyuqJZXTZzmN9xSGk,4602
503
+ langchain_agentx/tools/bash/semantics.py,sha256=pkf4URTulAOVL7m04Rq9JJF7z0J2mTEsGWrjyoY9r6o,5769
504
504
  langchain_agentx/tools/bash/shell_locator.py,sha256=AUnCZS8gmZurCs8rL4y0MMOS9UZimfg5rQ5At38mZKU,6296
505
505
  langchain_agentx/tools/bash/shell_quoting.py,sha256=K4JLVGwU8VpdQzfQB1o3xsq4HoCKHew1pry8hHChcoA,4498
506
506
  langchain_agentx/tools/bash/task_runtime.py,sha256=hikVC9YugtvpLf4nDaPlX_GFem1YQvDRwN0ShRflUYU,3568
507
- langchain_agentx/tools/bash/tool.py,sha256=U5iMC7ey9mDVWDSpk4cqn_u6fUWDi7A1mjeyiPBdSZU,44480
507
+ langchain_agentx/tools/bash/tool.py,sha256=OWPEyJQdqw2h2Abr9ZzLN0Vk0tS-2gIAGQt58CCXBms,44976
508
508
  langchain_agentx/tools/bash/windows_shell_quoting.py,sha256=0RzLnTKyFrJ536QzqPZvSyUcQWN794XRlAq2RdHeKHo,1615
509
509
  langchain_agentx/tools/edit/__init__.py,sha256=PiiOHUODUzTRQxxPdE7ZMTjd3vfdzm9LEcn1lpVtg8o,203
510
510
  langchain_agentx/tools/edit/backend.py,sha256=JrikYafZDGMQ2JjXRxbnv1VSHVGRUdLmZN__nnqkM9o,3159
@@ -651,8 +651,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
651
651
  langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
652
652
  langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
653
653
  langchain_agentx/workspace/view.py,sha256=Ip02CZNI-ZjF5k0OYT3q7RUR_auMEw83VJ6rQsaBcYU,4588
654
- langchain_agentx_python-1.2.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
655
- langchain_agentx_python-1.2.1.dist-info/METADATA,sha256=I_uJTiEUapAJ5QTOeOb9Jf40fZPpKNQ4D8zOzO96HMw,24846
656
- langchain_agentx_python-1.2.1.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
657
- langchain_agentx_python-1.2.1.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
658
- langchain_agentx_python-1.2.1.dist-info/RECORD,,
654
+ langchain_agentx_python-1.2.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
655
+ langchain_agentx_python-1.2.2.dist-info/METADATA,sha256=SFntj7m8EPCZwMIFq8WVSfPi55YiHnr5WHpCc87QU9M,24846
656
+ langchain_agentx_python-1.2.2.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
657
+ langchain_agentx_python-1.2.2.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
658
+ langchain_agentx_python-1.2.2.dist-info/RECORD,,