langchain-agentx-python 2.4.1__py3-none-any.whl → 2.4.4__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__ = "2.4.1"
14
+ __version__ = "2.4.4"
15
15
 
16
16
  from .mcp.call import McpToolCaller
17
17
  from .mcp.client_manager import McpClientManager, McpConnectedClient
@@ -25,7 +25,10 @@ if TYPE_CHECKING:
25
25
  _WORKER_CONTAINER_TYPES = frozenset(
26
26
  {"background_task", "background", "batch", "worker", "sdk"}
27
27
  )
28
- _INTERACTIVE_HEADLESS_MODES = frozenset({"auto", "dontAsk", "bypassPermissions"})
28
+ # acceptEdits:headless 子图继承父写快路径(与 auto 同属写链放行 mode)
29
+ _INTERACTIVE_HEADLESS_MODES = frozenset(
30
+ {"auto", "dontAsk", "bypassPermissions", "acceptEdits"}
31
+ )
29
32
  _HITL_PROFILE_IDS = frozenset({"code-interactive", "code-plan"})
30
33
  _HEADLESS_PROFILE_IDS = frozenset(
31
34
  {"qa-auto", "workflow-auto", "worker-deny", "interactive-headless"}
@@ -120,7 +123,8 @@ class InteractionProfileResolver:
120
123
  if mode in ("default", "plan"):
121
124
  raise ValueError(
122
125
  f"interactive + headless=True cannot use permission_mode={mode!r}; "
123
- "use auto, dontAsk, or bypassPermissions (print/script entry)"
126
+ "use auto, dontAsk, bypassPermissions, or acceptEdits "
127
+ "(interactive-headless / print/script entry)"
124
128
  )
125
129
  if mode not in _INTERACTIVE_HEADLESS_MODES:
126
130
  raise ValueError(
@@ -985,6 +985,7 @@ class LoopGraphBuilder:
985
985
  system_prompt=self._base_prompt,
986
986
  capabilities=self._services.capabilities,
987
987
  cwd=runtime_cwd,
988
+ permission_mode=self._config.permission_mode,
988
989
  boundary_snapshot=BoundarySnapshotPropagator.from_workspace_boundary(
989
990
  boundary=self._workspace_boundary,
990
991
  runtime_cwd=runtime_cwd,
@@ -1048,6 +1049,7 @@ class LoopGraphBuilder:
1048
1049
  system_prompt=base_prompt,
1049
1050
  capabilities=self._services.capabilities,
1050
1051
  cwd=runtime_cwd,
1052
+ permission_mode=self._config.permission_mode,
1051
1053
  boundary_snapshot=BoundarySnapshotPropagator.from_workspace_boundary(
1052
1054
  boundary=self._workspace_boundary,
1053
1055
  runtime_cwd=runtime_cwd,
@@ -5,8 +5,17 @@ loop/runtime/context_factory.py — RuntimeContextFactory 继承规则单点
5
5
  继承优先级(单点定义):显式 override > SubagentConfig 默认 > 父 ctx 字段。
6
6
  在整体链路中的位置:SubagentOrchestrator.invoke_subagent() 调用。
7
7
  CC 对照:src/utils/forkedAgent.ts createSubagentContext() 中的字段合并逻辑。
8
- 当前裁剪范围:v1 model / max_steps / available_tools / agent_depth 继承;permission_mode 直通;
8
+ 当前裁剪范围:v1 model / max_steps / available_tools / agent_depth 继承;
9
+ permission_mode 经 read_parent_permission_mode 写入 AgentLoopConfig(derive 侧);
10
+ orchestrator 再经 resolve_definition_permission_mode 合并 definition/overrides 后
11
+ 注入 SubagentContext.permission_mode(构图 SSOT;见 dual-track / parent-wins bug 单);
9
12
  ``subagent_effective_cwd`` 由 ``SubagentExecutionPaths.prepare`` 注入(不在此 mkdir)。
13
+
14
+ 编排消费说明(防双轨幻觉):
15
+ ``derive`` 返回完整 ``AgentLoopConfig``,但 ``create_subagent_graph`` **不直接吃**该对象;
16
+ orchestrator 当前只抽出并注入子 ctx 的字段:``model`` / ``max_steps`` /
17
+ ``agent_depth`` / ``permission_mode``(及 path/workspace 旁路)。子图权限权威在
18
+ ``SubagentContext.permission_mode``,勿在 graph 再读父 runnable。
10
19
  """
11
20
 
12
21
  from __future__ import annotations
@@ -83,16 +92,22 @@ class RuntimeContextFactory:
83
92
  else list(parent_ctx.available_tools or [])
84
93
  )
85
94
 
86
- # 权限:override > 父 ctx.tool_flags["permission_mode"] > "default"
87
- parent_permission = (
88
- getattr(parent_ctx, "permission_mode", None)
89
- or getattr(getattr(parent_ctx, "tool_flags", None), "get", lambda x: None)("permission_mode")
90
- or "default"
95
+ # 权限:override > read_parent_permission_mode > "default"(AgentLoopConfig 侧)。
96
+ # 子图有效 mode(含 definition 父优先)由 orchestrator._resolve_effective_permission_mode
97
+ # 写入 SubagentContext.permission_mode;勿把本字段当作 definition merge 结果。
98
+ from langchain_agentx.tool_runtime.prompt.session_mode import (
99
+ read_parent_permission_mode,
91
100
  )
92
- resolved_permission = (
101
+
102
+ parent_permission = (
93
103
  overrides.permission_mode
94
104
  if overrides.permission_mode is not None
95
- else parent_permission
105
+ else read_parent_permission_mode(parent_ctx)
106
+ )
107
+ resolved_permission = (
108
+ parent_permission.strip()
109
+ if isinstance(parent_permission, str) and parent_permission.strip()
110
+ else "default"
96
111
  )
97
112
  workspace_root = getattr(parent_ctx, "workspace_root", None)
98
113
  parent_agent_home = getattr(parent_ctx, "agent_home_segment", None)
@@ -150,6 +165,8 @@ class RuntimeContextFactory:
150
165
  # 优先读 subagent.agent_depth(嵌套 L1→L2 若只写顶层会误成 0)。
151
166
  child_depth = int(getattr(parent_ctx, "agent_depth", 0) or 0) + 1
152
167
 
168
+ # 完整 AgentLoopConfig 供编排/后台路径复用;构图 SSOT 仍经 SubagentContext
169
+ # (orchestrator 仅透传 model/max_steps/agent_depth/permission_mode 等)。
153
170
  return AgentLoopConfig(
154
171
  model=resolved_model,
155
172
  session_id=getattr(parent_ctx, "session_id", "") or "",
@@ -54,6 +54,7 @@ from langchain_agentx.loop.subagent.fork_worktree_notice import build_worktree_n
54
54
  from langchain_agentx.loop.message_contract import to_record
55
55
  from langchain_agentx.tool_runtime.prompt.session_mode import (
56
56
  read_parent_loop_config,
57
+ read_parent_permission_mode,
57
58
  resolve_subagent_headless,
58
59
  )
59
60
  from langchain_agentx.tool_runtime.session_store import AgentSessionStore
@@ -82,6 +83,8 @@ class SubagentContext:
82
83
  fork_from_parent: bool = False # True = fork 模式,initial_messages 含父会话历史
83
84
  is_async: bool = False # True = 走异步路径(coordinate/fork-async)
84
85
  headless: bool = True # CC 对齐:async 强制 True;sync 继承父 loop headless
86
+ permission_mode: str | None = None
87
+ """子图 permission_mode SSOT(derive/ctx 工厂写入;create_subagent_graph 只读本字段)。"""
85
88
 
86
89
  # 共享字段(从父会话继承)
87
90
  session_store: Any = None # SessionStore
@@ -177,6 +180,7 @@ def create_subagent_context(
177
180
  is_builtin_subagent: bool = False,
178
181
  recovery: RecoveryPolicy | RecoveryPolicyMode | None = None,
179
182
  inherit_hooks: bool | frozenset[HookEvent] | None = True,
183
+ permission_mode: str | None = None,
180
184
  ) -> SubagentContext:
181
185
  """工厂函数:从父上下文创建子 Agent 上下文
182
186
 
@@ -199,6 +203,7 @@ def create_subagent_context(
199
203
  fork_worktree_parent_cwd: 父 Agent 工作目录(须在 ``run_with_cwd_override`` 子 cwd 生效前由编排层捕获)。
200
204
  fork_worktree_path: 子 Agent 的 worktree 根路径(通常即 ``path_prep.worktree_dir``)。
201
205
  thinking_generation_enabled: 是否启用 request-side thinking(由 SubagentOrchestrator 根据 mode 决定)
206
+ permission_mode: 显式子 mode(orchestrator 传 derive 结果);None 时从父 runnable/tool_flags 读一次
202
207
 
203
208
  Returns:
204
209
  SubagentContext 实例
@@ -301,6 +306,12 @@ def create_subagent_context(
301
306
  is_async=is_async,
302
307
  )
303
308
 
309
+ # permission_mode SSOT:显式(derive)> 父 runnable/tool_flags 读一次;构图不再重读父
310
+ if isinstance(permission_mode, str) and permission_mode.strip():
311
+ resolved_permission_mode: str | None = permission_mode.strip()
312
+ else:
313
+ resolved_permission_mode = read_parent_permission_mode(parent_ctx)
314
+
304
315
  resolved_query_source = (
305
316
  query_source
306
317
  if isinstance(query_source, str) and query_source.strip()
@@ -331,6 +342,7 @@ def create_subagent_context(
331
342
  fork_from_parent=fork_from_parent,
332
343
  is_async=is_async,
333
344
  headless=resolved_headless,
345
+ permission_mode=resolved_permission_mode,
334
346
  session_store=session_store,
335
347
  tool_registry=getattr(parent_ctx, "tool_registry", None),
336
348
  tool_loader=getattr(parent_ctx, "tool_loader", None),
@@ -403,6 +415,8 @@ class ForkedParentRuntime:
403
415
  cwd: str | None = None
404
416
  thinking_generation_enabled: bool | None = None
405
417
  boundary_snapshot: BoundarySnapshot | None = None
418
+ permission_mode: str | None = None
419
+ """父 loop permission_mode;后台 fork 写入 SubagentContext SSOT。"""
406
420
 
407
421
 
408
422
  def _clone_parent_session_store(parent_store: Any) -> Any:
@@ -510,6 +524,12 @@ class SubagentContextFactory:
510
524
  fork_from_parent=True,
511
525
  is_async=False,
512
526
  headless=True,
527
+ permission_mode=(
528
+ parent_runtime.permission_mode.strip()
529
+ if isinstance(parent_runtime.permission_mode, str)
530
+ and parent_runtime.permission_mode.strip()
531
+ else None
532
+ ),
513
533
  session_store=session_store,
514
534
  tool_registry=parent_runtime.tool_registry,
515
535
  tool_loader=parent_runtime.tool_loader,
@@ -0,0 +1,73 @@
1
+ """loop/subagent/definition_permission.py — 子 Agent 定义 permissionMode 父优先合并
2
+
3
+ 职责:官网 Permission modes 规则核(纯函数);不读 AppState、不写 ctx。
4
+ 链路:SubagentConfig / overrides.declared + 父 mode → orchestrator → SubagentContext SSOT。
5
+ 裁剪:auto_available 本期恒 True(LOCK-10);无 plugin 忽略面;非法枚举由 loader 告警后置 None。
6
+
7
+ 规范化枚举见 ``tool_runtime.prompt.session_mode.normalize_definition_permission_mode``
8
+ (避免 tools.loader → loop 反向依赖)。
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from langchain_agentx.tool_runtime.prompt.session_mode import (
14
+ normalize_definition_permission_mode,
15
+ )
16
+
17
+ _ELEVATED_PARENT_MODES = frozenset(
18
+ {
19
+ "bypassPermissions",
20
+ "acceptEdits",
21
+ "auto",
22
+ }
23
+ )
24
+
25
+
26
+ def _normalize_parent_mode(parent_mode: str | None) -> str | None:
27
+ """父 mode:blank→None;manual→default;其余保留(含合法枚举)。"""
28
+ if parent_mode is None:
29
+ return None
30
+ if not isinstance(parent_mode, str):
31
+ return None
32
+ stripped = parent_mode.strip()
33
+ if not stripped:
34
+ return None
35
+ if stripped == "manual":
36
+ return "default"
37
+ return stripped
38
+
39
+
40
+ def resolve_definition_permission_mode(
41
+ *,
42
+ parent_mode: str | None,
43
+ declared: str | None,
44
+ auto_available: bool = True,
45
+ ) -> str | None:
46
+ """合并父会话 mode 与子定义声明(官网 Permission modes / Nested 链式)。
47
+
48
+ LOCK-2:父 ∈ elevated → 忽略子声明。
49
+ LOCK-3:子 bypass 且父非 bypass → 保持父。
50
+ LOCK-10:本期 auto_available 默认 True。
51
+ """
52
+ parent = _normalize_parent_mode(parent_mode)
53
+ declared_n = normalize_definition_permission_mode(declared)
54
+
55
+ if declared_n is None:
56
+ return parent
57
+
58
+ if parent in _ELEVATED_PARENT_MODES:
59
+ return parent
60
+
61
+ if declared_n == "bypassPermissions" and parent != "bypassPermissions":
62
+ return parent
63
+
64
+ if declared_n == "auto" and not auto_available:
65
+ return parent
66
+
67
+ return declared_n
68
+
69
+
70
+ __all__ = [
71
+ "normalize_definition_permission_mode",
72
+ "resolve_definition_permission_mode",
73
+ ]
@@ -24,6 +24,41 @@ if TYPE_CHECKING:
24
24
  from .context import SubagentContext
25
25
 
26
26
 
27
+ def resolve_subagent_permission_mode(
28
+ *,
29
+ headless: bool,
30
+ inherited: str | None,
31
+ ) -> str:
32
+ """解析子图 permission_mode。
33
+
34
+ headless 时继承父 mode(父 ``auto`` → 子 ``auto``,写快路径才可通);
35
+ 无父 mode 时回落 ``dontAsk``(无交互面)。非 headless 无父 mode 回落 ``default``。
36
+
37
+ 对照:docs/bug/headless-subagent-dontask-blocks-write-roots-2026-10-22.md
38
+ """
39
+ if isinstance(inherited, str) and inherited.strip():
40
+ return inherited.strip()
41
+ return "dontAsk" if headless else "default"
42
+
43
+
44
+ def resolve_subagent_container_type(
45
+ *,
46
+ headless: bool,
47
+ permission_mode: str,
48
+ ) -> str:
49
+ """解析子图 container_type,与 ``InteractionProfileResolver`` 合法三元组对齐。
50
+
51
+ ``sdk`` 剖面强制 ``permission_mode=dontAsk``;若子图继承了 ``auto`` /
52
+ ``acceptEdits`` / ``bypassPermissions``,须改走 ``interactive`` + headless
53
+ (interactive-headless),否则 normalize 会拒掉。
54
+ """
55
+ if not headless:
56
+ return "interactive"
57
+ if permission_mode == "dontAsk":
58
+ return "sdk"
59
+ return "interactive"
60
+
61
+
27
62
  def create_subagent_graph(
28
63
  ctx: SubagentContext,
29
64
  loader: Any,
@@ -58,9 +93,10 @@ def create_subagent_graph(
58
93
  sync_tool_policy_roots_from_workspace,
59
94
  )
60
95
  from langchain_agentx.tool_runtime.loader import ToolRuntimeLoader
61
- from langchain_agentx.tool_runtime.prompt.session_mode import read_parent_permission_mode
62
96
 
63
- inherited_permission_mode = read_parent_permission_mode(ctx)
97
+ # permission_mode SSOT:只读 SubagentContext(derive/create_subagent_context 写入)
98
+ # 勿再 read_parent_permission_mode — docs/bug/subagent-permission-mode-dual-track-ssot-2026-10-22.md
99
+ inherited_permission_mode = ctx.permission_mode
64
100
 
65
101
  if ctx.boundary_snapshot is not None and isinstance(loader, ToolRuntimeLoader):
66
102
  sync_tool_policy_roots_from_workspace(
@@ -89,6 +125,18 @@ def create_subagent_graph(
89
125
  else:
90
126
  session_id = f"subagent:{ctx.subagent_type}:{ctx.agent_id}"
91
127
 
128
+ # 勿无条件 headless→dontAsk / 一律 sdk:会盖掉父 auto,且与 sdk 剖面冲突
129
+ # (docs/bug/headless-subagent-dontask-blocks-write-roots-2026-10-22.md)
130
+ sub_headless = bool(ctx.headless)
131
+ sub_permission_mode = resolve_subagent_permission_mode(
132
+ headless=sub_headless,
133
+ inherited=inherited_permission_mode,
134
+ )
135
+ sub_container_type = resolve_subagent_container_type(
136
+ headless=sub_headless,
137
+ permission_mode=sub_permission_mode,
138
+ )
139
+
92
140
  # Phase 3: config-first 模式
93
141
  config = AgentLoopConfig(
94
142
  session_id=session_id,
@@ -128,13 +176,9 @@ def create_subagent_graph(
128
176
  fork_from_parent=bool(ctx.fork_from_parent),
129
177
  ),
130
178
  max_steps=ctx.max_steps,
131
- headless=bool(ctx.headless),
132
- container_type="sdk" if ctx.headless else "interactive",
133
- permission_mode=(
134
- "dontAsk"
135
- if ctx.headless
136
- else (inherited_permission_mode or "default")
137
- ),
179
+ headless=sub_headless,
180
+ permission_mode=sub_permission_mode,
181
+ container_type=sub_container_type,
138
182
  name=f"subagent:{ctx.subagent_type}:{ctx.agent_id[:8]}",
139
183
  )
140
184
  services = LoopServices(
@@ -26,6 +26,9 @@ from langchain_agentx.loop.runtime.subagent_execution_paths import (
26
26
  resolve_subagent_workspace_root,
27
27
  )
28
28
  from langchain_agentx.loop.subagent.context import SubagentContext, create_subagent_context
29
+ from langchain_agentx.loop.subagent.definition_permission import (
30
+ resolve_definition_permission_mode,
31
+ )
29
32
  from langchain_agentx.loop.subagent.hooks_pipeline import (
30
33
  _apply_agent_frontmatter_hooks as _pipeline_apply_agent_hooks,
31
34
  _apply_skill_fork_hooks as _pipeline_apply_skill_hooks,
@@ -37,6 +40,7 @@ from langchain_agentx.loop.subagent.mcp_child_servers import McpChildServerSessi
37
40
  from langchain_agentx.loop.subagent.prompt import enhance_system_prompt_with_env
38
41
  from langchain_agentx.tool_runtime.prompt.session_mode import (
39
42
  read_parent_loop_config,
43
+ read_parent_permission_mode,
40
44
  resolve_subagent_headless,
41
45
  )
42
46
  from langchain_agentx.utils.cwd import run_with_cwd_override
@@ -433,6 +437,11 @@ class SubagentOrchestrator:
433
437
  thinking_generation_enabled=thinking_generation_enabled,
434
438
  is_builtin_subagent=getattr(subagent_config, "is_builtin", False),
435
439
  inherit_hooks=True if inherit_hooks is None else inherit_hooks,
440
+ permission_mode=self._resolve_effective_permission_mode(
441
+ parent_ctx=ctx,
442
+ overrides=overrides,
443
+ subagent_config=subagent_config,
444
+ ),
436
445
  )
437
446
  _apply_skill_fork_progress_host(
438
447
  subagent_ctx,
@@ -709,6 +718,11 @@ class SubagentOrchestrator:
709
718
  thinking_generation_enabled=thinking_generation_enabled,
710
719
  is_builtin_subagent=getattr(subagent_config, "is_builtin", False),
711
720
  inherit_hooks=True if inherit_hooks is None else inherit_hooks,
721
+ permission_mode=self._resolve_effective_permission_mode(
722
+ parent_ctx=ctx,
723
+ overrides=overrides,
724
+ subagent_config=subagent_config,
725
+ ),
712
726
  )
713
727
  _apply_skill_fork_progress_host(
714
728
  subagent_ctx,
@@ -854,6 +868,25 @@ class SubagentOrchestrator:
854
868
  )
855
869
  return overrides, runtime_ctx, prepared
856
870
 
871
+ @staticmethod
872
+ def _resolve_effective_permission_mode(
873
+ *,
874
+ parent_ctx: "ToolExecutionContext",
875
+ overrides: SubagentContextOverrides,
876
+ subagent_config: "SubagentConfig",
877
+ ) -> str | None:
878
+ """LOCK-8/9:overrides > definition×parent merge > 仅父;禁止裸传 derive。"""
879
+ parent_mode = read_parent_permission_mode(parent_ctx)
880
+ if overrides.permission_mode is not None:
881
+ declared = overrides.permission_mode
882
+ else:
883
+ declared = getattr(subagent_config, "permission_mode", None)
884
+ return resolve_definition_permission_mode(
885
+ parent_mode=parent_mode,
886
+ declared=declared,
887
+ auto_available=True,
888
+ )
889
+
857
890
  @staticmethod
858
891
  def _resolve_child_loader_headless(
859
892
  *,
@@ -1,9 +1,10 @@
1
1
  """
2
- prompt/session_mode.py — 会话 headless 推断(对齐 CC shouldAvoidPermissionPrompts)
2
+ prompt/session_mode.py — 会话 headless / permission_mode 共享原语
3
3
 
4
4
  职责:
5
5
  在 session / loop 创建时推断是否应避免交互式 permission prompt;
6
- 子代理派生时继承/强制 headless(对齐 CC runAgent.ts agentGetAppState)。
6
+ 子代理派生时继承/强制 headless(对齐 CC runAgent.ts agentGetAppState);
7
+ 规范化子 Agent 定义 permissionMode(供 loader 与 loop merge 共用,避免 tools→loop)。
7
8
  """
8
9
 
9
10
  from __future__ import annotations
@@ -13,6 +14,17 @@ from typing import Any
13
14
 
14
15
  _HEADLESS_CONTAINER_TYPES = frozenset({"background", "batch", "worker", "sdk"})
15
16
 
17
+ _DEFINITION_PERMISSION_MODES = frozenset(
18
+ {
19
+ "default",
20
+ "acceptEdits",
21
+ "auto",
22
+ "dontAsk",
23
+ "bypassPermissions",
24
+ "plan",
25
+ }
26
+ )
27
+
16
28
 
17
29
  def infer_session_headless(
18
30
  *,
@@ -64,10 +76,19 @@ def read_parent_loop_config(parent_ctx: Any) -> tuple[bool | None, str | None]:
64
76
 
65
77
 
66
78
  def read_parent_permission_mode(parent_ctx: Any) -> str | None:
67
- """从父 ctx / runnable_config 读取 permission_mode(子 Agent compile 继承用)。"""
79
+ """从父 ctx / tool_flags / runnable_config 读取 permission_mode(子 Agent 继承用)。
80
+
81
+ 读序:显式字段 > tool_flags > configurable > loop_config。
82
+ 与 RuntimeContextFactory.derive / create_subagent_context 共用,避免双轨分歧。
83
+ """
68
84
  direct = getattr(parent_ctx, "permission_mode", None)
69
85
  if isinstance(direct, str) and direct.strip():
70
86
  return direct.strip()
87
+ tool_flags = getattr(parent_ctx, "tool_flags", None)
88
+ if isinstance(tool_flags, dict):
89
+ flag_mode = tool_flags.get("permission_mode")
90
+ if isinstance(flag_mode, str) and flag_mode.strip():
91
+ return flag_mode.strip()
71
92
  runnable_config = getattr(parent_ctx, "runnable_config", None)
72
93
  if not isinstance(runnable_config, dict):
73
94
  return None
@@ -83,6 +104,25 @@ def read_parent_permission_mode(parent_ctx: Any) -> str | None:
83
104
  return None
84
105
 
85
106
 
107
+ def normalize_definition_permission_mode(value: str | None) -> str | None:
108
+ """规范化子声明 permissionMode:blank→None;manual→default;非法→None。
109
+
110
+ 供 Agent markdown loader 与 ``resolve_definition_permission_mode`` 共用。
111
+ """
112
+ if value is None:
113
+ return None
114
+ if not isinstance(value, str):
115
+ return None
116
+ stripped = value.strip()
117
+ if not stripped:
118
+ return None
119
+ if stripped == "manual":
120
+ return "default"
121
+ if stripped not in _DEFINITION_PERMISSION_MODES:
122
+ return None
123
+ return stripped
124
+
125
+
86
126
  def resolve_subagent_headless(
87
127
  *,
88
128
  parent_headless: bool | None,
@@ -8,7 +8,8 @@ tools/agent/loader.py — 用户自定义 Agent 加载器
8
8
  CC 对照:src/tools/AgentTool/loadAgentsDir.ts getAgentDefinitionsWithOverrides()
9
9
  + parseAgentFromMarkdown()
10
10
  当前裁剪范围:v1 Markdown 解析 + YAML frontmatter + 优先级合并;
11
- tools/disallowedTools/maxTurns/background/mcpServers/requiredMcpServers 字段支持;
11
+ tools/disallowedTools/maxTurns/background/mcpServers/requiredMcpServers/
12
+ permissionMode 字段支持(非法枚举 → None + warning,LOCK-11);
12
13
  agents 目录下 ``.md`` 扩展名 **大小写不敏感**(``*.MD`` / ``*.Md`` 与 ``*.md`` 同等);
13
14
  v2 memoization / file watcher
14
15
 
@@ -23,6 +24,7 @@ CC 对照:src/tools/AgentTool/loadAgentsDir.ts getAgentDefinitionsWithOverride
23
24
  disallowedTools: [Edit] # 可选,denylist(优先级高于 tools)
24
25
  maxTurns: 30 # 可选,default_max_steps
25
26
  background: false # 可选,allow_async
27
+ permissionMode: dontAsk # 可选;与父 mode 合并见 resolve_definition_permission_mode
26
28
  hooks: {...} # 可选,与 skill frontmatter 同结构(子 loop SUBAGENT_STOP)
27
29
  ---
28
30
  系统提示词 body...
@@ -203,6 +205,10 @@ class AgentDefinitionLoader:
203
205
  mcp_servers = mcp_parser.parse_servers(
204
206
  frontmatter.get("mcpServers", frontmatter.get("mcp_servers"))
205
207
  )
208
+ permission_mode = _parse_permission_mode(
209
+ frontmatter.get("permissionMode", frontmatter.get("permission_mode")),
210
+ file_path=file_path,
211
+ )
206
212
 
207
213
  return SubagentConfig(
208
214
  name=name.strip(),
@@ -221,6 +227,7 @@ class AgentDefinitionLoader:
221
227
  mcp_servers=mcp_servers,
222
228
  hooks=hooks,
223
229
  hooks_root=str(file_path.parent) if hooks else None,
230
+ permission_mode=permission_mode,
224
231
  )
225
232
 
226
233
  def _split_frontmatter(self, text: str) -> tuple[dict | None, str]:
@@ -380,6 +387,31 @@ def _parse_agent_memory_scope(raw: Any, file_path: Path):
380
387
  return scope # type: AgentMemoryScope
381
388
 
382
389
 
390
+ def _parse_permission_mode(raw: Any, *, file_path: Path) -> str | None:
391
+ """解析 frontmatter permissionMode;非法 → None + warning(LOCK-11)。"""
392
+ from langchain_agentx.tool_runtime.prompt.session_mode import (
393
+ normalize_definition_permission_mode,
394
+ )
395
+
396
+ if raw is None:
397
+ return None
398
+ if not isinstance(raw, str):
399
+ logger.warning(
400
+ "Agent file %s invalid permissionMode %r, ignoring",
401
+ file_path,
402
+ raw,
403
+ )
404
+ return None
405
+ normalized = normalize_definition_permission_mode(raw)
406
+ if normalized is None and raw.strip():
407
+ logger.warning(
408
+ "Agent file %s invalid permissionMode %r, ignoring",
409
+ file_path,
410
+ raw,
411
+ )
412
+ return normalized
413
+
414
+
383
415
  def _describe_tools(tools_raw: Any, disallowed_raw: Any) -> str:
384
416
  if tools_raw is None or tools_raw == ["*"]:
385
417
  base = "All tools"
@@ -51,3 +51,5 @@ class SubagentConfig:
51
51
  """Frontmatter ``hooks:``(与 skill 结构相同);子 loop 由 orchestrator 注册。"""
52
52
  hooks_root: str | None = None
53
53
  """Agent 定义文件所在目录(``CLAUDE_AGENT_DIR`` / command env 根)。"""
54
+ permission_mode: str | None = None
55
+ """Frontmatter ``permissionMode``;与父 mode 经 ``resolve_definition_permission_mode`` 合并后写入 SSOT。"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-agentx-python
3
- Version: 2.4.1
3
+ Version: 2.4.4
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=Zd9ZNHTop9xajkMeEv5kx083EBWUjZUH5LfYbtWXTXQ,2014
1
+ langchain_agentx/__init__.py,sha256=gX9uh5HhQBKt0DLInVlFKZmLGU6UFTHBPQmjVNSMRKo,2014
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
@@ -34,7 +34,7 @@ langchain_agentx/loop/run_context.py,sha256=QPnd3M36uq8UqD9HrZyydDkG6jAbTGx9IP1n
34
34
  langchain_agentx/loop/config/__init__.py,sha256=0pf3Mml7LZpQ1C3_AtROFQClrBQegaGCG4YCTfSr9v8,1137
35
35
  langchain_agentx/loop/config/agent_config.py,sha256=tOnAHFor6dkle3FvUOVx0YsmiYVTH11UrWSyTmOLV0c,3100
36
36
  langchain_agentx/loop/config/compaction_config.py,sha256=MmXqY47-9nPDhdXvnU92Xp0XeaZ605ErQwX9d2emEkM,1176
37
- langchain_agentx/loop/config/interaction_profile.py,sha256=UWrlI3aTWjYRq1mDeXL-xL-iCrQU-bXDXhETP6q9icY,10020
37
+ langchain_agentx/loop/config/interaction_profile.py,sha256=eAOzYDkk08WauAemXkb2AQD_Ok_9iZppFh5CFDA4bVE,10195
38
38
  langchain_agentx/loop/config/loop_config.py,sha256=Y7c5pluniarT3eKD8T51c8CWfTDJo_Fi3vJJDtQHGA4,16762
39
39
  langchain_agentx/loop/config/loop_runtime_overlay.py,sha256=PuB4vFdqCHXRbAFhAJIrtwq9pUIrHafyn0ApD4s06KU,2440
40
40
  langchain_agentx/loop/config/model_context_resolver.py,sha256=o9RR0Vnu07TD4Y3V8jk5V1NfkMcPcLjE8uaaLOe22Qo,4067
@@ -71,7 +71,7 @@ langchain_agentx/loop/exit/terminal.py,sha256=_DAZk-kS4Xd0nxMlayLo60-uj1Y5ozgBjL
71
71
  langchain_agentx/loop/exit/withheld_error.py,sha256=LfX3j7Rgd9VoMzlz7GOs9TPuRe7RXr_Gxweyotx3tqQ,1846
72
72
  langchain_agentx/loop/graph/__init__.py,sha256=9pUUqn7G87n3lV45iYIU24j0wcTHyCD--SSdtv3urh4,136
73
73
  langchain_agentx/loop/graph/builtin_loop_control.py,sha256=31o4H6SDGC_vECSliXecuVEnzlD1ahlVZ5JzEpxYY04,9248
74
- langchain_agentx/loop/graph/factory.py,sha256=cBioPj3uIqDsDAPha1g4dbR3ZGiHffr9NcdqUT22goE,113570
74
+ langchain_agentx/loop/graph/factory.py,sha256=ec13qBe_CVHXK2U15_75R2RdIGn9o7JefDr9M9FHyEo,113686
75
75
  langchain_agentx/loop/graph/graph_edges.py,sha256=kW6Npx-nMIPzcaisCxVPkTwf5Y9GOKy1NWCNkJWeJBQ,47370
76
76
  langchain_agentx/loop/graph/reactive_compact_node.py,sha256=633b5hTnR3BK8GBEKWhzOwGB0BQrVRKozNxDomWFjLo,8762
77
77
  langchain_agentx/loop/graph/runtime_tools_node.py,sha256=wQkbc0ophyefpPZEc0q1vKoJOIdS4S6d6hSsrkdRLDk,7387
@@ -133,7 +133,7 @@ langchain_agentx/loop/prompt/tool_aware_sections.py,sha256=vST7Z5y2eoITTTcsBo9ol
133
133
  langchain_agentx/loop/prompt/using_your_tools.py,sha256=NVLscfx2jzCHOx5R1jcoTwcOMsFkPx2Y9p_XD0Von3U,5251
134
134
  langchain_agentx/loop/runtime/__init__.py,sha256=7FEIC1nvNHIbe2azfJzg5AWH9GWYfCQopQWx8a5OMqM,555
135
135
  langchain_agentx/loop/runtime/context.py,sha256=dJHQvhs79c6xJZMrkB6-aUNFVax2R7V20Oj48nFePJs,1242
136
- langchain_agentx/loop/runtime/context_factory.py,sha256=cjrC29wfeVs_b25kqJ_nn9bBHfBz7NPTK6hIcIptJDA,8193
136
+ langchain_agentx/loop/runtime/context_factory.py,sha256=KWD0ojHxhE3PgG-TBrY6KsBDjDpYPrhgD_B11rE66rg,9404
137
137
  langchain_agentx/loop/runtime/subagent_execution_paths.py,sha256=Bgyiom48uta1FZcnIE7fIxyrD-VFCRsw7s8IxIxmLb4,4033
138
138
  langchain_agentx/loop/stream/graph_driver.py,sha256=qMZHEw3wJ19Tfbz31-VPLy8Mt6e90xKougIjAoaEFCE,3935
139
139
  langchain_agentx/loop/stream/helpers.py,sha256=b2sqtwrAa5ic5B5AzNqItU8pmDHw13iGs3hBiShSIZk,1390
@@ -146,18 +146,19 @@ langchain_agentx/loop/subagent/active_run_registry.py,sha256=KurhAMDTBnRkdxNidaM
146
146
  langchain_agentx/loop/subagent/async_runner.py,sha256=ZJu6DztRmg0SNEapEuPyQbsk8SprXiMKWDWDHb9L7yQ,14124
147
147
  langchain_agentx/loop/subagent/boundary_snapshot.py,sha256=9cQuqN29nJDRnHnx9c8b09KRmODE2zv15Z06QyjVJps,10008
148
148
  langchain_agentx/loop/subagent/cache_safe_params.py,sha256=-qcgdazEYRy6s4ILQuttpxraE3gsxq17Ro7LYBnuv3E,4460
149
- langchain_agentx/loop/subagent/context.py,sha256=tR2QQfNBKci0_rOm6e_p5wNXlo1gZntHpgkFfIuTLHE,24000
149
+ langchain_agentx/loop/subagent/context.py,sha256=cSyXYu9l5MnHQMEJkCQFTZKhn3pnIR6nGgGKzd_vhr8,25099
150
+ langchain_agentx/loop/subagent/definition_permission.py,sha256=_Y30FA0EDTgqpqjw5luIiwbpABLo17myC3aY4NWbGIw,2188
150
151
  langchain_agentx/loop/subagent/display_projector.py,sha256=AKsGWS1c9reH0kuD5u4oQ24KsBgyt7X10iZM3ere6Fs,3504
151
152
  langchain_agentx/loop/subagent/fork_boilerplate.py,sha256=2Uh3cwegL7V-DvtLIxjX7ik5uW5mXfY2NBzP-biREso,5005
152
153
  langchain_agentx/loop/subagent/fork_ports.py,sha256=hZATCvlYOxNVZtR3PoOVbI1GkA0KhHEyux6lUIjG5_4,1323
153
154
  langchain_agentx/loop/subagent/fork_worktree_notice.py,sha256=WKAJVxvRMJfbzRe_07Mgrm_zQIQCYx4RwsUnWSSOnkg,1389
154
155
  langchain_agentx/loop/subagent/forked_runner.py,sha256=dgzXA3nw9lG-3Dx24ovulPCK3ztRStmO_e8IubQ_MIo,7083
155
- langchain_agentx/loop/subagent/graph.py,sha256=io964LA8JIzaTSkphLklmnJcX5w3McwSj0MwhFqlH4g,5542
156
+ langchain_agentx/loop/subagent/graph.py,sha256=Cjij51QWUwqO4bJtXYUSWfLidM7URJ73FB81bZLILV0,7151
156
157
  langchain_agentx/loop/subagent/hook_attachment_messages.py,sha256=em3VXaUfURou_YzxXrnJwK5hNtGtp3Vu0I6HJhC9pd8,1451
157
158
  langchain_agentx/loop/subagent/hooks_compose.py,sha256=enPKRsAbqjB5HqACE-YFzM8K3Job7azi4wpqI1kmgl8,2515
158
159
  langchain_agentx/loop/subagent/hooks_pipeline.py,sha256=4OweLS52TRbcaUO56YpE-qX71ZPCjPRS0j9KzIcx5ls,6399
159
160
  langchain_agentx/loop/subagent/mcp_child_servers.py,sha256=UJ9FtIZroYvP6KfNLUnHKbb7bqg2qSmo15MkzHcVKME,4395
160
- langchain_agentx/loop/subagent/orchestrator.py,sha256=yWmLUBu43PQjjlx_bPeYddvj6AxnViPpfr8_lKklgo0,49313
161
+ langchain_agentx/loop/subagent/orchestrator.py,sha256=ceMY0wjSDsmnzcofz1ln6orZqfBGr3SgLHTQJaI1k7M,50672
161
162
  langchain_agentx/loop/subagent/parent_hook_snapshot.py,sha256=cwBC2eXvx1evc6QPpI42zq-iKThpObyoyWmkDNj9E7Q,2095
162
163
  langchain_agentx/loop/subagent/progress.py,sha256=nef3VVT9QmB2ZWCDAO86cmtVwCJIR8lXMr6jczQKqOY,6650
163
164
  langchain_agentx/loop/subagent/progress_dispatcher.py,sha256=KD5EdfoGplIaT8xvt5vgfXgSLPbvn9KqvY85wLpOX7A,4603
@@ -540,7 +541,7 @@ langchain_agentx/tool_runtime/prompt/headless.py,sha256=CcRNmshgICZY4RJjKWHqE2cw
540
541
  langchain_agentx/tool_runtime/prompt/legacy_resolver_adapter.py,sha256=wFMSSaJrOxGxEfPrRfSD_JK2izvrV0IRmwpOUkAwB0A,1915
541
542
  langchain_agentx/tool_runtime/prompt/messages.py,sha256=IYkQPRSHTW87pZ6u31phWX45ooAG0cqn3OnZ4UyJPOU,1569
542
543
  langchain_agentx/tool_runtime/prompt/race.py,sha256=kNNHxP1CLI3ysVx8T0i8UM_ya190837bSngJYhEsTP4,11326
543
- langchain_agentx/tool_runtime/prompt/session_mode.py,sha256=Zzje9JdzD1zzFFP8jz_mryiFCgnyc8DFm_bwNe6hBZw,3440
544
+ langchain_agentx/tool_runtime/prompt/session_mode.py,sha256=jc8nprtKiXfTI3Q3u3vtKuvh4DKoOSqAXfBKnHGFSno,4706
544
545
  langchain_agentx/tool_runtime/prompt/types.py,sha256=mvPk6HSxKlngihpRnNut1VDbmKsAejHxCKvihzXIREA,5334
545
546
  langchain_agentx/tool_runtime/resolvers/__init__.py,sha256=3nr1ouLHAF1O17Zfg1yKrZ5qAezDwRdVSWKN0lu-Iik,818
546
547
  langchain_agentx/tool_runtime/resolvers/agent_session.py,sha256=ZcQga1itCTX2TgNoIaCfVzV20w7nQjSkgDYRU4AWHto,3855
@@ -557,7 +558,7 @@ langchain_agentx/tools/agent/backend.py,sha256=10cpfnDHbbaCRkqEtyoQupPsXf25HsvNS
557
558
  langchain_agentx/tools/agent/builtin_subagent_loader.py,sha256=pIc-FcUrrnxjPmJT8lGy_xdgcMa-s6Tb39qpCQPaytw,3665
558
559
  langchain_agentx/tools/agent/cwd_resolution.py,sha256=glM_2k8Nd_N5IrCCJ5GHTPzVmo43SHigGf5wh2RK0NA,4802
559
560
  langchain_agentx/tools/agent/limits.py,sha256=UHjwkwf6ONeRw3FMu-uA4ueekG5jr9_e3EDhPcZ61NU,3828
560
- langchain_agentx/tools/agent/loader.py,sha256=BCR5VWbkWan4aR1Rrg7xnPaceL4oVsG1Td0SBM5HWQU,15342
561
+ langchain_agentx/tools/agent/loader.py,sha256=KyKNFtllYIo7kwcf0Ky8_yLz3VQzAcUbsjNfIyZPhNc,16511
561
562
  langchain_agentx/tools/agent/memory_paths.py,sha256=5EASlk9hQf3440tm5NnZ8MXO4Lg1_9pcd8KBUkV0Ilc,3235
562
563
  langchain_agentx/tools/agent/memory_prompt.py,sha256=jK0mDCL0tEzflyKhcorusKxhayUQYG0G4zyF_M83gRE,2251
563
564
  langchain_agentx/tools/agent/memory_snapshot.py,sha256=gmygz7mC-Zlck_nhhuBe6QfhTOMBIS5HMcYDr-oT-2M,14100
@@ -575,7 +576,7 @@ langchain_agentx/tools/agent/built_in/plan.py,sha256=sTq3r6NZg-9mhqXU9gvEPwpVSeS
575
576
  langchain_agentx/tools/agent/built_in/statusline_setup.py,sha256=LuC1O6UVLaKRmcGR7GoANv790vMWyHN2O1nY83Fhdvg,2639
576
577
  langchain_agentx/tools/agent/built_in/verification.py,sha256=y956nSeUGak-lrF1_CpZMHtlqOhHhhzhtp7_uDnXQLU,5671
577
578
  langchain_agentx/tools/agent/registry/__init__.py,sha256=Ts5_OJzTnbiqZDxXqMI1UnFaWxXvaQgMIGM6UMgY51s,643
578
- langchain_agentx/tools/agent/registry/config.py,sha256=BBQnI2s3it8ZK8pKGxHs3WR0TwtJLoU6R_q5Gj3zgjg,2192
579
+ langchain_agentx/tools/agent/registry/config.py,sha256=6I7OGKP_EBIRBZpnClSCTU7tq65dIHQuzgNxdbqEewY,2353
579
580
  langchain_agentx/tools/agent/registry/registry.py,sha256=knii6XWFZvPkcqzTK3Y4PX5evGcDmto4SsZ7VzUsPSI,1544
580
581
  langchain_agentx/tools/ask_user_question/__init__.py,sha256=bDHVPc4dHBw___7rbaTxClsbDpQ-jLt5Obj8R2VBLAc,444
581
582
  langchain_agentx/tools/ask_user_question/constants.py,sha256=rqlL31BTSNY2spn8f2i-MtKjy3y3QoLFAeqenFmYe6Q,1283
@@ -838,8 +839,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
838
839
  langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
839
840
  langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
840
841
  langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
841
- langchain_agentx_python-2.4.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
842
- langchain_agentx_python-2.4.1.dist-info/METADATA,sha256=S3olBaOihP0sVvV9jwsr4jyqGWc5ktRS2AdD0WIzD0g,23971
843
- langchain_agentx_python-2.4.1.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
844
- langchain_agentx_python-2.4.1.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
845
- langchain_agentx_python-2.4.1.dist-info/RECORD,,
842
+ langchain_agentx_python-2.4.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
843
+ langchain_agentx_python-2.4.4.dist-info/METADATA,sha256=yKso1obM03vdYTdmgAAiSaPpXk2sVBOUFq8_CzLm9MQ,23971
844
+ langchain_agentx_python-2.4.4.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
845
+ langchain_agentx_python-2.4.4.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
846
+ langchain_agentx_python-2.4.4.dist-info/RECORD,,