langchain-agentx-python 2.4.6__py3-none-any.whl → 2.4.8__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.
- langchain_agentx/__init__.py +1 -1
- langchain_agentx/loop/graph/factory.py +20 -2
- langchain_agentx/loop/subagent/context.py +2 -0
- langchain_agentx/loop/subagent/runner.py +1 -1
- langchain_agentx/tool_runtime/errors.py +4 -8
- langchain_agentx/tool_runtime/loader.py +4 -1
- langchain_agentx/tool_runtime/loop_loader.py +6 -0
- langchain_agentx/tool_runtime/session_store.py +8 -0
- langchain_agentx/tools/bash/backend.py +237 -119
- langchain_agentx/tools/bash/bash_exec_assembly.py +146 -0
- langchain_agentx/tools/bash/shell_quoting.py +24 -3
- langchain_agentx/tools/bash/shell_snapshot.py +205 -0
- langchain_agentx/tools/bash/tool.py +21 -1
- langchain_agentx/tools/registry_names.py +7 -0
- langchain_agentx/tools/workflow/__init__.py +18 -0
- langchain_agentx/tools/workflow/agents/__init__.py +14 -0
- langchain_agentx/tools/workflow/agents/definition.py +38 -0
- langchain_agentx/tools/workflow/agents/kernel_bridge.py +162 -0
- langchain_agentx/tools/workflow/agents/ports.py +31 -0
- langchain_agentx/tools/workflow/agents/prompt.py +35 -0
- langchain_agentx/tools/workflow/agents/structured_output.py +178 -0
- langchain_agentx/tools/workflow/agents/tool_pool.py +122 -0
- langchain_agentx/tools/workflow/agents/toolset.py +38 -0
- langchain_agentx/tools/workflow/agents/transcript_store.py +122 -0
- langchain_agentx/tools/workflow/agents/worker_runner.py +105 -0
- langchain_agentx/tools/workflow/backend.py +307 -0
- langchain_agentx/tools/workflow/constants.py +86 -0
- langchain_agentx/tools/workflow/engine/__init__.py +15 -0
- langchain_agentx/tools/workflow/engine/budget.py +28 -0
- langchain_agentx/tools/workflow/engine/cache_key.py +82 -0
- langchain_agentx/tools/workflow/engine/cancel_gate.py +72 -0
- langchain_agentx/tools/workflow/engine/concurrency.py +71 -0
- langchain_agentx/tools/workflow/engine/coordinator.py +176 -0
- langchain_agentx/tools/workflow/engine/hooks.py +401 -0
- langchain_agentx/tools/workflow/engine/journal.py +77 -0
- langchain_agentx/tools/workflow/engine/nested.py +28 -0
- langchain_agentx/tools/workflow/engine/paths.py +45 -0
- langchain_agentx/tools/workflow/engine/run_registry.py +156 -0
- langchain_agentx/tools/workflow/errors.py +53 -0
- langchain_agentx/tools/workflow/models.py +98 -0
- langchain_agentx/tools/workflow/observe/__init__.py +1 -0
- langchain_agentx/tools/workflow/observe/events.py +51 -0
- langchain_agentx/tools/workflow/observe/task_bridge.py +168 -0
- langchain_agentx/tools/workflow/prompt.py +47 -0
- langchain_agentx/tools/workflow/registry/__init__.py +6 -0
- langchain_agentx/tools/workflow/registry/bundled.py +10 -0
- langchain_agentx/tools/workflow/registry/discovery.py +88 -0
- langchain_agentx/tools/workflow/registry/models.py +17 -0
- langchain_agentx/tools/workflow/runtime/__init__.py +6 -0
- langchain_agentx/tools/workflow/runtime/entry_check.py +75 -0
- langchain_agentx/tools/workflow/runtime/executor.py +150 -0
- langchain_agentx/tools/workflow/runtime/forbidden_apis.py +104 -0
- langchain_agentx/tools/workflow/runtime/import_policy.py +70 -0
- langchain_agentx/tools/workflow/runtime/meta_validation.py +138 -0
- langchain_agentx/tools/workflow/runtime/script_gate.py +38 -0
- langchain_agentx/tools/workflow/tool.py +274 -0
- langchain_agentx/workspace/config.py +12 -0
- {langchain_agentx_python-2.4.6.dist-info → langchain_agentx_python-2.4.8.dist-info}/METADATA +1 -1
- {langchain_agentx_python-2.4.6.dist-info → langchain_agentx_python-2.4.8.dist-info}/RECORD +62 -18
- {langchain_agentx_python-2.4.6.dist-info → langchain_agentx_python-2.4.8.dist-info}/LICENSE +0 -0
- {langchain_agentx_python-2.4.6.dist-info → langchain_agentx_python-2.4.8.dist-info}/WHEEL +0 -0
- {langchain_agentx_python-2.4.6.dist-info → langchain_agentx_python-2.4.8.dist-info}/top_level.txt +0 -0
langchain_agentx/__init__.py
CHANGED
|
@@ -1593,13 +1593,25 @@ class LoopGraphBuilder:
|
|
|
1593
1593
|
patch["compaction_pipeline_meta"] = meta
|
|
1594
1594
|
meta["compact_progress"] = progress_events
|
|
1595
1595
|
|
|
1596
|
+
def _clear_file_reads_after_message_compaction(self, patch: dict[str, Any]) -> None:
|
|
1597
|
+
"""消息被 compaction 改写后清空 last_read(对齐 CC ``readFileState.clear()``)。
|
|
1598
|
+
|
|
1599
|
+
防止 Read dedup 返回 file_unchanged 却断言「内容仍在本会话」(A2)。
|
|
1600
|
+
"""
|
|
1601
|
+
if "messages" not in patch:
|
|
1602
|
+
return
|
|
1603
|
+
store = getattr(self, "_session_store", None)
|
|
1604
|
+
clearer = getattr(store, "clear_file_read_state", None)
|
|
1605
|
+
if callable(clearer):
|
|
1606
|
+
clearer()
|
|
1607
|
+
|
|
1596
1608
|
def _apply_prune_and_compress(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
1597
1609
|
if self._session_memory_compact_bridge is not None:
|
|
1598
1610
|
compacted_messages = self._session_memory_compact_bridge.try_compact(
|
|
1599
1611
|
list(state.get("messages") or [])
|
|
1600
1612
|
)
|
|
1601
1613
|
if compacted_messages is not None:
|
|
1602
|
-
|
|
1614
|
+
patch = {
|
|
1603
1615
|
"messages": compacted_messages,
|
|
1604
1616
|
"collapse_retry_available": True,
|
|
1605
1617
|
"compaction_pipeline_meta": {
|
|
@@ -1607,10 +1619,13 @@ class LoopGraphBuilder:
|
|
|
1607
1619
|
"session_memory_compaction": "applied",
|
|
1608
1620
|
},
|
|
1609
1621
|
}
|
|
1622
|
+
self._clear_file_reads_after_message_compaction(patch)
|
|
1623
|
+
return patch
|
|
1610
1624
|
patch = self._loop_context_compaction.run(state, model=self._model)
|
|
1611
1625
|
compaction_meta = patch.get("compaction_pipeline_meta")
|
|
1612
1626
|
if isinstance(compaction_meta, dict):
|
|
1613
1627
|
patch["collapse_retry_available"] = bool(compaction_meta.get("tokens_freed_sum", 0))
|
|
1628
|
+
self._clear_file_reads_after_message_compaction(patch)
|
|
1614
1629
|
return patch
|
|
1615
1630
|
|
|
1616
1631
|
async def _apply_prune_and_compress_async(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -1619,7 +1634,7 @@ class LoopGraphBuilder:
|
|
|
1619
1634
|
list(state.get("messages") or [])
|
|
1620
1635
|
)
|
|
1621
1636
|
if compacted_messages is not None:
|
|
1622
|
-
|
|
1637
|
+
patch = {
|
|
1623
1638
|
"messages": compacted_messages,
|
|
1624
1639
|
"collapse_retry_available": True,
|
|
1625
1640
|
"compaction_pipeline_meta": {
|
|
@@ -1627,6 +1642,8 @@ class LoopGraphBuilder:
|
|
|
1627
1642
|
"session_memory_compaction": "applied",
|
|
1628
1643
|
},
|
|
1629
1644
|
}
|
|
1645
|
+
self._clear_file_reads_after_message_compaction(patch)
|
|
1646
|
+
return patch
|
|
1630
1647
|
progress_events: list[dict[str, Any]] = []
|
|
1631
1648
|
seen_progress = False
|
|
1632
1649
|
|
|
@@ -1660,6 +1677,7 @@ class LoopGraphBuilder:
|
|
|
1660
1677
|
compaction_meta = patch.get("compaction_pipeline_meta")
|
|
1661
1678
|
if isinstance(compaction_meta, dict):
|
|
1662
1679
|
patch["collapse_retry_available"] = bool(compaction_meta.get("tokens_freed_sum", 0))
|
|
1680
|
+
self._clear_file_reads_after_message_compaction(patch)
|
|
1663
1681
|
return patch
|
|
1664
1682
|
|
|
1665
1683
|
def _run_prune_and_compress_with_trace(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -117,6 +117,8 @@ class SubagentContext:
|
|
|
117
117
|
loop_compilation_context: LoopCompilationContext | None = None
|
|
118
118
|
can_use_tool: Callable[[str, dict[str, Any]], bool] | None = None
|
|
119
119
|
register_transcript: bool = True
|
|
120
|
+
transcript_store: Any | None = None
|
|
121
|
+
"""可选落盘后端;None → runner 使用 InMemoryTranscriptStore。Workflow 注入 workflows/<runId>/。"""
|
|
120
122
|
|
|
121
123
|
# Thinking 控制字段(request-side 生成意图,与 UI 展示意图分离)
|
|
122
124
|
thinking_generation_enabled: bool = True # 子代理 request-side thinking 生成开关
|
|
@@ -864,7 +864,7 @@ async def _run_subagent_body(
|
|
|
864
864
|
resolved_model = _resolve_subagent_model(ctx, model)
|
|
865
865
|
|
|
866
866
|
if ctx.register_transcript and ctx.agent_id:
|
|
867
|
-
store = InMemoryTranscriptStore()
|
|
867
|
+
store = getattr(ctx, "transcript_store", None) or InMemoryTranscriptStore()
|
|
868
868
|
transcript: SubagentTranscriptManager | NoOpTranscriptManager = SubagentTranscriptManager(
|
|
869
869
|
store=store,
|
|
870
870
|
agent_id=ctx.agent_id,
|
|
@@ -20,7 +20,6 @@ from typing import Literal
|
|
|
20
20
|
|
|
21
21
|
from .limits import ERROR_TRUNCATE_HALF, ERROR_TRUNCATE_THRESHOLD
|
|
22
22
|
from .messages import (
|
|
23
|
-
DENIAL_WORKAROUND_GUIDANCE,
|
|
24
23
|
INTERRUPT_MESSAGE_FOR_TOOL_USE,
|
|
25
24
|
auto_reject_message,
|
|
26
25
|
)
|
|
@@ -68,10 +67,6 @@ def truncate_error_text(text: str) -> str:
|
|
|
68
67
|
return _DEFAULT_TRUNCATOR.truncate(text)
|
|
69
68
|
|
|
70
69
|
|
|
71
|
-
def _llm_summary_already_formatted(message: str) -> bool:
|
|
72
|
-
return "STOP and explain" in message or DENIAL_WORKAROUND_GUIDANCE in message
|
|
73
|
-
|
|
74
|
-
|
|
75
70
|
# ---------------------------------------------------------------------------
|
|
76
71
|
# 框架级异常类型
|
|
77
72
|
# ---------------------------------------------------------------------------
|
|
@@ -236,12 +231,13 @@ def blocked_envelope(
|
|
|
236
231
|
"""
|
|
237
232
|
构造 status="blocked" 的 ToolResultEnvelope。
|
|
238
233
|
|
|
239
|
-
|
|
240
|
-
模型可见 summary
|
|
234
|
+
对齐 CC ``permissionDecision.message → tool_result content``:
|
|
235
|
+
模型可见 summary 优先使用决策拒因(message / llm_summary);
|
|
236
|
+
仅当拒因空时回退 AUTO_REJECT_MESSAGE(与 CC headless 无细文案时一致)。
|
|
241
237
|
"""
|
|
242
238
|
if llm_summary is not None:
|
|
243
239
|
summary_text = truncate_error_text(llm_summary)
|
|
244
|
-
elif
|
|
240
|
+
elif isinstance(message, str) and message.strip():
|
|
245
241
|
summary_text = truncate_error_text(message)
|
|
246
242
|
else:
|
|
247
243
|
summary_text = truncate_error_text(auto_reject_message(tool_name))
|
|
@@ -96,10 +96,11 @@ class ToolRuntimeLoader:
|
|
|
96
96
|
agent_home_segment: str = ".langchain_agentx",
|
|
97
97
|
include_agent: bool = True,
|
|
98
98
|
include_skill: bool = False,
|
|
99
|
+
include_workflow: bool = False,
|
|
99
100
|
agent_registry: Any | None = None,
|
|
100
101
|
) -> "ToolRuntimeLoader":
|
|
101
102
|
"""
|
|
102
|
-
注册默认工具集(Read/Grep/Glob/Bash + 可选 Agent/Skill)。
|
|
103
|
+
注册默认工具集(Read/Grep/Glob/Bash + 可选 Agent/Skill/Workflow)。
|
|
103
104
|
|
|
104
105
|
向后兼容:在 loader 自身 registry 上注册(单 loop 模式)。
|
|
105
106
|
create_loop_agent 应优先使用 ``spawn_loop_loader`` 获得 per-loop 隔离。
|
|
@@ -109,6 +110,7 @@ class ToolRuntimeLoader:
|
|
|
109
110
|
agent_home_segment: agent home 子目录名(如 .claude、.cursor)
|
|
110
111
|
include_agent: 是否包含 Agent 工具
|
|
111
112
|
include_skill: 是否包含 Skill 工具
|
|
113
|
+
include_workflow: 是否包含 Workflow 工具(默认 False,结项前不进默认池)
|
|
112
114
|
agent_registry: 可选的自定义 agent 注册表
|
|
113
115
|
"""
|
|
114
116
|
loop = self.spawn_loop_loader(
|
|
@@ -118,6 +120,7 @@ class ToolRuntimeLoader:
|
|
|
118
120
|
loop.register_default_tools(
|
|
119
121
|
include_agent=include_agent,
|
|
120
122
|
include_skill=include_skill,
|
|
123
|
+
include_workflow=include_workflow,
|
|
121
124
|
agent_registry=agent_registry,
|
|
122
125
|
)
|
|
123
126
|
for tool in loop.registry.list():
|
|
@@ -86,6 +86,7 @@ class LoopLoader:
|
|
|
86
86
|
*,
|
|
87
87
|
include_agent: bool = True,
|
|
88
88
|
include_skill: bool = False,
|
|
89
|
+
include_workflow: bool = False,
|
|
89
90
|
agent_registry: Any | None = None,
|
|
90
91
|
) -> "LoopLoader":
|
|
91
92
|
from pathlib import Path
|
|
@@ -198,6 +199,11 @@ class LoopLoader:
|
|
|
198
199
|
)
|
|
199
200
|
)
|
|
200
201
|
|
|
202
|
+
if include_workflow:
|
|
203
|
+
from langchain_agentx.tools.workflow import WorkflowRuntimeTool
|
|
204
|
+
|
|
205
|
+
self.register(WorkflowRuntimeTool(config=cfg))
|
|
206
|
+
|
|
201
207
|
return self
|
|
202
208
|
|
|
203
209
|
def attach_mcp(
|
|
@@ -163,6 +163,14 @@ class AgentSessionStore:
|
|
|
163
163
|
"""用与 LangGraph state 中 last_read 条目同构的 dict 覆盖该路径。"""
|
|
164
164
|
self._file_read_state[path] = FileReadRecord.from_bridge_dict(path, entry)
|
|
165
165
|
|
|
166
|
+
def clear_file_read_state(self) -> None:
|
|
167
|
+
"""清空文件读缓存(对齐 CC ``readFileState.clear()`` on compact)。
|
|
168
|
+
|
|
169
|
+
compaction 蒸发消息后若不清理,Read dedup 会返回 ``file_unchanged`` stub
|
|
170
|
+
却断言「内容仍在本会话」(BUG-20261107-28 A2)。
|
|
171
|
+
"""
|
|
172
|
+
self._file_read_state.clear()
|
|
173
|
+
|
|
166
174
|
def replace_all_file_reads_from_bridge_map(self, data: dict[str, Any]) -> None:
|
|
167
175
|
"""整体替换文件读映射(对应 ToolStateBridge.set_last_read_map)。"""
|
|
168
176
|
self._file_read_state.clear()
|