langchain-agentx-python 2.1.6__py3-none-any.whl → 2.1.7__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.1.6"
14
+ __version__ = "2.1.7"
15
15
 
16
16
  from .loop import ( # noqa: F401
17
17
  create_loop_agent,
@@ -48,6 +48,7 @@ TRANSITION_TOKEN_BUDGET_CONTINUATION = "token_budget_continuation"
48
48
  DEFAULT_TOKEN_BUDGET_MAX_CONTINUATIONS = 3
49
49
 
50
50
  # 错误结束 terminal_reason 集合(finish payload is_error 判据)
51
+ # D-EVT-3:含 aborted_* / hook_stopped,与 build_loop_terminal 对齐
51
52
  ERROR_TERMINAL_REASONS = frozenset(
52
53
  {
53
54
  TERMINAL_REASON_ERROR,
@@ -60,6 +61,9 @@ ERROR_TERMINAL_REASONS = frozenset(
60
61
  TERMINAL_REASON_MAX_TOKENS,
61
62
  TERMINAL_REASON_BLOCKING_LIMIT,
62
63
  TERMINAL_REASON_CLASSIFIER_DENIAL_LIMIT,
64
+ TERMINAL_REASON_ABORTED_STREAMING,
65
+ TERMINAL_REASON_ABORTED_TOOLS,
66
+ TERMINAL_REASON_HOOK_STOPPED,
63
67
  }
64
68
  )
65
69
 
@@ -42,6 +42,9 @@ class RunContext:
42
42
  child_tasks: set[asyncio.Task] = field(default_factory=set)
43
43
  child_threads: set[threading.Thread] = field(default_factory=set)
44
44
  cancel_requested: bool = False
45
+ # D-EVT-6:宿主在 cancel_tree / killAll 时写入;finish 投影读取;缺省 unknown。
46
+ # 取值:escape | workflow_stop | kill_agents | unknown;禁止从「是否有后台」猜测。
47
+ cancel_source: str | None = None
45
48
 
46
49
  def register_child(self, task: asyncio.Task) -> None:
47
50
  self.child_tasks.add(task)
@@ -13,6 +13,7 @@ loop/subagent/async_runner.py — 子 Agent 异步生命周期管理
13
13
 
14
14
  当前裁剪范围:
15
15
  P0:删除 C4;后台仅 C3。P2:killAll 仅遍历 _BACKGROUND_HANDLES(可 Task.cancel)。
16
+ P2 投影:lifecycle / _mark_cancelled_terminal → OOB task-notification(D-EVT-5)。
16
17
  """
17
18
 
18
19
  from __future__ import annotations
@@ -22,7 +23,7 @@ import logging
22
23
  import os
23
24
  import uuid
24
25
  from dataclasses import dataclass
25
- from typing import Any
26
+ from typing import Any, Literal
26
27
 
27
28
  from langchain_agentx.loop.exit.reason_codes import (
28
29
  TERMINAL_REASON_ABORTED_STREAMING,
@@ -106,11 +107,34 @@ def _is_cancel_like_result(result: Any) -> bool:
106
107
  return getattr(result, "error_kind", None) == "aborted"
107
108
 
108
109
 
110
+ def _emit_task_notification_bookend(
111
+ *,
112
+ task_id: str,
113
+ status: Literal["completed", "failed", "stopped"],
114
+ output_file: str = "",
115
+ summary: str = "",
116
+ agent_id: str | None = None,
117
+ terminal_reason: str | None = None,
118
+ ) -> None:
119
+ """D-EVT-5:后台终态 OOB bookend;同 task_id 去重在 sink 内完成。"""
120
+ from langchain_agentx.observability.events.oob_event_sink import get_oob_event_sink
121
+
122
+ get_oob_event_sink().emit_task_notification(
123
+ task_id=task_id,
124
+ status=status,
125
+ output_file=output_file,
126
+ summary=summary,
127
+ agent_id=agent_id,
128
+ terminal_reason=terminal_reason,
129
+ )
130
+
131
+
109
132
  def _mark_cancelled_terminal(
110
133
  *,
111
134
  task_id: str,
112
135
  output_file: str,
113
136
  task_runtime: Any | None,
137
+ agent_id: str | None = None,
114
138
  ) -> None:
115
139
  """写 cancelled 输出 + TaskRuntime STOPPED(对齐 TaskRuntime.cancel / CC killed)。"""
116
140
  try:
@@ -119,18 +143,27 @@ def _mark_cancelled_terminal(
119
143
  fp.write("cancelled")
120
144
  except Exception:
121
145
  logger.debug("failed to write cancelled output for task_id=%s", task_id, exc_info=True)
122
- if task_runtime is None:
123
- return
124
- try:
125
- task_runtime.mark_terminal(
126
- task_id,
127
- TaskStatus.STOPPED,
128
- "subagent cancelled",
129
- output_file=output_file,
130
- terminal_reason="cancelled",
131
- )
132
- except Exception:
133
- logger.debug("failed to mark cancelled terminal for task_id=%s", task_id, exc_info=True)
146
+ if task_runtime is not None:
147
+ try:
148
+ task_runtime.mark_terminal(
149
+ task_id,
150
+ TaskStatus.STOPPED,
151
+ "subagent cancelled",
152
+ output_file=output_file,
153
+ terminal_reason="cancelled",
154
+ )
155
+ except Exception:
156
+ logger.debug(
157
+ "failed to mark cancelled terminal for task_id=%s", task_id, exc_info=True
158
+ )
159
+ _emit_task_notification_bookend(
160
+ task_id=task_id,
161
+ status="stopped",
162
+ output_file=output_file,
163
+ summary="cancelled",
164
+ agent_id=agent_id,
165
+ terminal_reason="cancelled",
166
+ )
134
167
 
135
168
 
136
169
  async def _execute_background_lifecycle(
@@ -146,6 +179,7 @@ async def _execute_background_lifecycle(
146
179
  """共享后台生命周期:run_subagent → 写 output → task_runtime 终态。"""
147
180
  execution_ctx = ctx
148
181
  execution_ctx.is_async = False
182
+ agent_id = getattr(ctx, "agent_id", None)
149
183
  try:
150
184
  result = await run_subagent(
151
185
  ctx=execution_ctx,
@@ -160,37 +194,64 @@ async def _execute_background_lifecycle(
160
194
  task_id=task_id,
161
195
  output_file=output_file,
162
196
  task_runtime=task_runtime,
197
+ agent_id=agent_id,
163
198
  )
164
199
  return
165
200
  with open(output_file, "w", encoding="utf-8") as fp:
166
201
  fp.write(result.content)
202
+ summary = (
203
+ result.content[:500]
204
+ if result.content
205
+ else (result.error_message or "subagent completed")
206
+ )
207
+ notif_status: Literal["completed", "failed"] = (
208
+ "completed" if result.status == "completed" else "failed"
209
+ )
167
210
  if task_runtime is not None:
168
211
  status = TaskStatus.COMPLETED if result.status == "completed" else TaskStatus.FAILED
169
212
  task_runtime.mark_terminal(
170
213
  task_id,
171
214
  status,
172
- result.content[:500] if result.content else (result.error_message or "subagent completed"),
215
+ summary,
173
216
  output_file=output_file,
174
217
  terminal_reason=result.terminal_reason or None,
175
218
  )
219
+ _emit_task_notification_bookend(
220
+ task_id=task_id,
221
+ status=notif_status,
222
+ output_file=output_file,
223
+ summary=summary,
224
+ agent_id=agent_id,
225
+ terminal_reason=result.terminal_reason or None,
226
+ )
176
227
  except asyncio.CancelledError:
177
228
  _mark_cancelled_terminal(
178
229
  task_id=task_id,
179
230
  output_file=output_file,
180
231
  task_runtime=task_runtime,
232
+ agent_id=agent_id,
181
233
  )
182
234
  raise
183
235
  except Exception as exc:
184
236
  with open(output_file, "w", encoding="utf-8") as fp:
185
237
  fp.write(str(exc))
238
+ summary = f"Subagent background lifecycle failed: {exc}"
186
239
  if task_runtime is not None:
187
240
  task_runtime.mark_terminal(
188
241
  task_id,
189
242
  TaskStatus.FAILED,
190
- f"Subagent background lifecycle failed: {exc}",
243
+ summary,
191
244
  output_file=output_file,
192
245
  terminal_reason=TERMINAL_REASON_GRAPH_RUNTIME_ERROR,
193
246
  )
247
+ _emit_task_notification_bookend(
248
+ task_id=task_id,
249
+ status="failed",
250
+ output_file=output_file,
251
+ summary=summary,
252
+ agent_id=agent_id,
253
+ terminal_reason=TERMINAL_REASON_GRAPH_RUNTIME_ERROR,
254
+ )
194
255
 
195
256
 
196
257
  def register_background_agent(
@@ -309,6 +370,7 @@ def kill_background_agent(task_id: str) -> bool:
309
370
  task_id=task_id,
310
371
  output_file=handle.output_file,
311
372
  task_runtime=handle.task_runtime,
373
+ agent_id=handle.agent_id,
312
374
  )
313
375
  return True
314
376
 
@@ -10,13 +10,21 @@ from .events import (
10
10
  LangchainAgentEvent,
11
11
  LangchainAgentEventType,
12
12
  LangGraphToLangchainAgentEventAdapter,
13
+ OOB_EVENT_QUEUE_CAP,
14
+ OobEventSink,
13
15
  RecoveryAction,
14
16
  RecoveryActionResolver,
15
17
  RecoveryPolicy,
16
18
  RecoveryPolicyMode,
17
19
  coerce_recovery_policy,
18
20
  create_langgraph_event_adapter,
21
+ drain_oob_events,
19
22
  enrich_run_result,
23
+ ensure_oob_event_sink,
24
+ get_oob_event_sink,
25
+ install_oob_event_sink,
26
+ reset_oob_event_sink,
27
+ teardown_oob_event_sink,
20
28
  )
21
29
  from .replay.service import (
22
30
  export_call_chain,
@@ -62,6 +70,8 @@ __all__ = [
62
70
  "LogContext",
63
71
  "LoggingConfigBuilder",
64
72
  "OBS_SCHEMA_VERSION",
73
+ "OOB_EVENT_QUEUE_CAP",
74
+ "OobEventSink",
65
75
  "ObservabilityLoggerAdapter",
66
76
  "build_log_context",
67
77
  "configure_logging_once",
@@ -69,6 +79,12 @@ __all__ = [
69
79
  "LangchainAgentEventType",
70
80
  "LangGraphToLangchainAgentEventAdapter",
71
81
  "create_langgraph_event_adapter",
82
+ "drain_oob_events",
83
+ "ensure_oob_event_sink",
84
+ "get_oob_event_sink",
85
+ "install_oob_event_sink",
86
+ "reset_oob_event_sink",
87
+ "teardown_oob_event_sink",
72
88
  "RecoveryAction",
73
89
  "RecoveryActionResolver",
74
90
  "RecoveryPolicy",
@@ -22,6 +22,16 @@ from .factory import (
22
22
  ILangGraphEventAdapter,
23
23
  create_langgraph_event_adapter,
24
24
  )
25
+ from .oob_event_sink import (
26
+ OOB_EVENT_QUEUE_CAP,
27
+ OobEventSink,
28
+ drain_oob_events,
29
+ ensure_oob_event_sink,
30
+ get_oob_event_sink,
31
+ install_oob_event_sink,
32
+ reset_oob_event_sink,
33
+ teardown_oob_event_sink,
34
+ )
25
35
 
26
36
  __all__ = [
27
37
  "LangchainAgentEventType",
@@ -40,4 +50,12 @@ __all__ = [
40
50
  "enrich_run_result",
41
51
  "ILangGraphEventAdapter",
42
52
  "create_langgraph_event_adapter",
53
+ "OOB_EVENT_QUEUE_CAP",
54
+ "OobEventSink",
55
+ "drain_oob_events",
56
+ "ensure_oob_event_sink",
57
+ "get_oob_event_sink",
58
+ "install_oob_event_sink",
59
+ "reset_oob_event_sink",
60
+ "teardown_oob_event_sink",
43
61
  ]
@@ -139,6 +139,8 @@ class LangchainAgentEventType(Enum):
139
139
  TASK_NOTIFICATION_ENQUEUED = "task-notification-enqueued"
140
140
  TASK_NOTIFICATION_CONSUMED = "task-notification-consumed"
141
141
  TASK_TERMINATED = "task-terminated"
142
+ # D-EVT-5:OOB 后台终态书档(对标 CC system/task_notification)
143
+ TASK_NOTIFICATION = "task-notification"
142
144
 
143
145
 
144
146
  @dataclass
@@ -1249,6 +1251,22 @@ class AgentEventProjector:
1249
1251
  tool_name=name,
1250
1252
  session_id=str(child_session_id) if child_session_id else None,
1251
1253
  )
1254
+ # D-EVT-5′:图内 SUBAGENT_END 覆盖时登记;若 payload 含 task_id 则抑制同 id OOB
1255
+ end_task_id: str | None = None
1256
+ if isinstance(output, dict):
1257
+ raw_tid = output.get("task_id")
1258
+ if not isinstance(raw_tid, str):
1259
+ artifact = output.get("artifact")
1260
+ if isinstance(artifact, dict):
1261
+ raw_tid = artifact.get("task_id")
1262
+ if isinstance(raw_tid, str) and raw_tid.strip():
1263
+ end_task_id = raw_tid.strip()
1264
+ if end_task_id:
1265
+ from langchain_agentx.observability.events.oob_event_sink import (
1266
+ get_oob_event_sink,
1267
+ )
1268
+
1269
+ get_oob_event_sink().note_terminal_projected(end_task_id)
1252
1270
  self._on_subagent_end_boundary()
1253
1271
 
1254
1272
  self.state.current_tool = None
@@ -11,6 +11,7 @@ finish_event_payload_composer.py — finish 事件 data 组装协作者
11
11
  当前裁剪范围:
12
12
  - 不发射事件、不读 LangGraph raw event
13
13
  - 不处理 SubAgent 终态;model_retry 进度见 retry_progress.py(Phase 6)
14
+ - INTERRUPT 文案仅经 display_interrupt_text 投影(D-EVT-4),不入 messages
14
15
  """
15
16
 
16
17
  from __future__ import annotations
@@ -22,8 +23,11 @@ from langchain_core.messages import AIMessage
22
23
 
23
24
  from langchain_agentx.loop.exit.reason_codes import (
24
25
  ERROR_TERMINAL_REASONS,
26
+ TERMINAL_REASON_ABORTED_STREAMING,
27
+ TERMINAL_REASON_ABORTED_TOOLS,
25
28
  TERMINAL_REASON_COMPLETED,
26
29
  TERMINAL_REASON_ERROR,
30
+ TERMINAL_REASON_HOOK_STOPPED,
27
31
  TERMINAL_REASON_MAX_TOKENS,
28
32
  TRANSITION_API_ERROR_TERMINAL,
29
33
  TRANSITION_CONTEXT_WINDOW_RECOVERY_EXHAUSTED,
@@ -32,6 +36,7 @@ from langchain_agentx.loop.exit.reason_codes import (
32
36
  )
33
37
  from langchain_agentx.loop.exit.terminal import read_terminal_reason
34
38
  from langchain_agentx.loop.exit.withheld_error import resolve_overflow_kind
39
+ from langchain_agentx.loop.run_context import get_run_context
35
40
 
36
41
  _RECOVERY_EXHAUSTED_TRANSITIONS = frozenset(
37
42
  {
@@ -41,6 +46,17 @@ _RECOVERY_EXHAUSTED_TRANSITIONS = frozenset(
41
46
  }
42
47
  )
43
48
 
49
+ # D-EVT-2/4:用户取消 display 文案(对齐 CC INTERRUPT;禁止注入 messages)
50
+ DEFAULT_DISPLAY_INTERRUPT_TEXT = "[Request interrupted by user]"
51
+ ERROR_KIND_USER_CANCELLED = "user_cancelled"
52
+ ERROR_KIND_HOOK_STOPPED = "hook_stopped"
53
+ _USER_CANCELLED_TERMINAL_REASONS = frozenset(
54
+ {
55
+ TERMINAL_REASON_ABORTED_STREAMING,
56
+ TERMINAL_REASON_ABORTED_TOOLS,
57
+ }
58
+ )
59
+
44
60
  _SENSITIVE_PATTERNS: tuple[tuple[re.Pattern[str], str], ...] = (
45
61
  (re.compile(r"sk-[a-zA-Z0-9_-]{8,}", re.I), "<redacted_api_key>"),
46
62
  (re.compile(r"bearer\s+\S+", re.I), "Bearer <redacted>"),
@@ -157,7 +173,7 @@ class FinishEventPayloadComposer:
157
173
  messages=messages,
158
174
  )
159
175
 
160
- return {
176
+ payload: dict[str, Any] = {
161
177
  "graph_name": graph_name,
162
178
  "answer": answer,
163
179
  "terminal_reason": terminal_reason,
@@ -168,6 +184,10 @@ class FinishEventPayloadComposer:
168
184
  "error_kind": error_kind,
169
185
  "recovery_exhausted": recovery_exhausted,
170
186
  }
187
+ if error_kind == ERROR_KIND_USER_CANCELLED:
188
+ payload["display_interrupt_text"] = DEFAULT_DISPLAY_INTERRUPT_TEXT
189
+ payload["cancel_source"] = self._resolve_cancel_source(snap)
190
+ return payload
171
191
 
172
192
  @staticmethod
173
193
  def _resolve_terminal_reason(
@@ -265,6 +285,25 @@ class FinishEventPayloadComposer:
265
285
  if terminal_reason == TERMINAL_REASON_COMPLETED:
266
286
  return "unknown"
267
287
 
288
+ if terminal_reason in _USER_CANCELLED_TERMINAL_REASONS:
289
+ return ERROR_KIND_USER_CANCELLED
290
+
291
+ if terminal_reason == TERMINAL_REASON_HOOK_STOPPED:
292
+ return ERROR_KIND_HOOK_STOPPED
293
+
294
+ return "unknown"
295
+
296
+ @staticmethod
297
+ def _resolve_cancel_source(snap: dict[str, Any]) -> str:
298
+ """D-EVT-6:宿主写入优先;缺省 unknown;禁止猜测反推。"""
299
+ raw = snap.get("cancel_source")
300
+ if raw is not None and str(raw):
301
+ return str(raw)
302
+ ctx = get_run_context()
303
+ if ctx is not None:
304
+ source = getattr(ctx, "cancel_source", None)
305
+ if source is not None and str(source):
306
+ return str(source)
268
307
  return "unknown"
269
308
 
270
309
  @staticmethod
@@ -323,6 +362,9 @@ class FinishEventPayloadComposer:
323
362
 
324
363
 
325
364
  __all__ = [
365
+ "DEFAULT_DISPLAY_INTERRUPT_TEXT",
366
+ "ERROR_KIND_HOOK_STOPPED",
367
+ "ERROR_KIND_USER_CANCELLED",
326
368
  "FinishEventPayloadComposer",
327
369
  "sanitize_finish_error_message",
328
370
  ]
@@ -20,8 +20,15 @@ from dataclasses import dataclass
20
20
  from enum import Enum
21
21
  from typing import Any, Literal
22
22
 
23
+ from langchain_agentx.loop.exit.reason_codes import (
24
+ TERMINAL_REASON_ABORTED_STREAMING,
25
+ TERMINAL_REASON_ABORTED_TOOLS,
26
+ TERMINAL_REASON_HOOK_STOPPED,
27
+ )
23
28
  from langchain_agentx.loop.exit.terminal import read_terminal_reason
24
29
  from langchain_agentx.observability.events.finish_event_payload_composer import (
30
+ ERROR_KIND_HOOK_STOPPED,
31
+ ERROR_KIND_USER_CANCELLED,
25
32
  FinishEventPayloadComposer,
26
33
  )
27
34
 
@@ -53,6 +60,27 @@ FINISH_OUTCOME_KEYS = frozenset(
53
60
 
54
61
  RecoveryPolicyMode = Literal["interactive", "auto"]
55
62
 
63
+ _CANCEL_ERROR_KINDS = frozenset(
64
+ {
65
+ ERROR_KIND_USER_CANCELLED,
66
+ ERROR_KIND_HOOK_STOPPED,
67
+ }
68
+ )
69
+ _CANCEL_TERMINAL_REASONS = frozenset(
70
+ {
71
+ TERMINAL_REASON_ABORTED_STREAMING,
72
+ TERMINAL_REASON_ABORTED_TOOLS,
73
+ TERMINAL_REASON_HOOK_STOPPED,
74
+ }
75
+ )
76
+
77
+
78
+ def _is_cancel_outcome(outcome: "FinishOutcome") -> bool:
79
+ """用户取消 / hook 终局:禁止建议 compact(D-EVT-2 / CLI 投影)。"""
80
+ if outcome.error_kind in _CANCEL_ERROR_KINDS:
81
+ return True
82
+ return outcome.terminal_reason in _CANCEL_TERMINAL_REASONS
83
+
56
84
 
57
85
  @dataclass(frozen=True)
58
86
  class RecoveryPolicy:
@@ -149,6 +177,7 @@ class FinishOutcomeParser:
149
177
  snap = dict(state or {})
150
178
  terminal = snap.get("terminal") if isinstance(snap.get("terminal"), dict) else None
151
179
  if terminal is not None and "is_error" in terminal:
180
+ # from_terminal 已对缺失 error_kind 做 composer 对齐补推(D-EVT-2)
152
181
  return self.from_terminal(terminal, answer=answer)
153
182
  msgs = messages if messages is not None else snap.get("messages")
154
183
  if not isinstance(msgs, list):
@@ -178,11 +207,23 @@ class FinishOutcomeParser:
178
207
  *,
179
208
  answer: str = "",
180
209
  ) -> FinishOutcome:
210
+ terminal_reason = _optional_str(read_terminal_reason(terminal))
211
+ is_error = bool(terminal.get("is_error"))
212
+ error_kind = _optional_str(terminal.get("error_kind"))
213
+ if is_error and error_kind is None:
214
+ error_kind = FinishEventPayloadComposer._infer_error_kind(
215
+ terminal_reason=terminal_reason,
216
+ is_error=True,
217
+ withheld_error=None,
218
+ transition_reason=None,
219
+ terminal=terminal,
220
+ messages=None,
221
+ )
181
222
  return FinishOutcome(
182
- terminal_reason=_optional_str(read_terminal_reason(terminal)),
183
- is_error=bool(terminal.get("is_error")),
223
+ terminal_reason=terminal_reason,
224
+ is_error=is_error,
184
225
  recoverable=bool(terminal.get("recoverable")),
185
- error_kind=_optional_str(terminal.get("error_kind")),
226
+ error_kind=error_kind,
186
227
  recovery_exhausted=bool(terminal.get("recovery_exhausted")),
187
228
  errors=tuple(str(e) for e in (terminal.get("errors") or []) if e),
188
229
  answer=answer,
@@ -220,6 +261,8 @@ class RecoveryActionResolver:
220
261
  ) -> RecoveryAction:
221
262
  if not outcome.is_error:
222
263
  return RecoveryAction.NONE
264
+ if _is_cancel_outcome(outcome):
265
+ return RecoveryAction.FAIL
223
266
  if not outcome.recoverable:
224
267
  return RecoveryAction.FAIL
225
268
 
@@ -242,12 +285,13 @@ class RecoveryActionResolver:
242
285
 
243
286
  @staticmethod
244
287
  def blocks_auto_compact(outcome: FinishOutcome) -> bool:
245
- """Workflow 门禁:recovery_exhausted 后禁止再次 auto-compact(Phase 5.5)。"""
288
+ """Workflow 门禁:cancel / recovery_exhausted 后禁止再次 auto-compact"""
246
289
  if not outcome.is_error:
247
290
  return False
291
+ if _is_cancel_outcome(outcome):
292
+ return True
248
293
  return outcome.recovery_exhausted
249
294
 
250
-
251
295
  def enrich_run_result(
252
296
  result: dict[str, Any],
253
297
  *,
@@ -28,6 +28,7 @@ from langchain_agentx.observability.events.agent_event_projector import (
28
28
  LangchainAgentEvent,
29
29
  LangchainAgentEventType,
30
30
  )
31
+ from langchain_agentx.observability.events.oob_event_sink import drain_oob_events
31
32
 
32
33
 
33
34
  class LangGraphToLangchainAgentEventAdapter:
@@ -51,12 +52,19 @@ class LangGraphToLangchainAgentEventAdapter:
51
52
  async def adapt(
52
53
  self, events: AsyncIterator[Dict[str, Any]]
53
54
  ) -> AsyncIterator[LangchainAgentEvent]:
54
- """主转换函数:将 LangGraph 事件流转换为 LangChain AgentX 风格事件流。"""
55
+ """主转换函数:将 LangGraph 事件流转换为 LangChain AgentX 风格事件流。
56
+
57
+ 每批 raw 事件后合并 OOB task-notification(D-EVT-5 / D-EVT-10)。
58
+ """
55
59
  self._projector.reset_stream_closeout_diagnostics()
56
60
  try:
57
61
  async for event in events:
58
62
  async for oc_event in self._projector.consume(event):
59
63
  yield oc_event
64
+ for oob in drain_oob_events():
65
+ yield oob
66
+ for oob in drain_oob_events():
67
+ yield oob
60
68
  finally:
61
69
  self._projector.log_stream_closeout_diagnostics()
62
70
 
@@ -0,0 +1,183 @@
1
+ """
2
+ oob_event_sink.py — 后台 task 终态 OOB 事件队列(对标 CC sdkEventQueue)
3
+
4
+ 职责:
5
+ 入队 task-notification(completed|failed|stopped);按 task_id 去重(D-EVT-5′);
6
+ 供 Session/adapter drain 合并进 LangchainAgentEvent 流(D-EVT-10)。
7
+
8
+ 链路位置:
9
+ async_runner lifecycle / _mark_cancelled_terminal → emit
10
+ LangGraphToLangchainAgentEventAdapter.adapt / Workflow adapt_isolated → drain
11
+
12
+ 当前裁剪范围:
13
+ - 始终 enqueue(无 TUI/headless 分叉,D-EVT-10)
14
+ - cap=1000 丢最旧;不依赖 langchain_agentx_trace
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ from collections import deque
21
+ from contextvars import ContextVar, Token
22
+ from typing import Literal
23
+
24
+ from langchain_agentx.observability.events.agent_event_projector import (
25
+ LangchainAgentEvent,
26
+ LangchainAgentEventType,
27
+ )
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+ OOB_EVENT_QUEUE_CAP = 1000
32
+
33
+ TaskNotificationStatus = Literal["completed", "failed", "stopped"]
34
+
35
+ _current_sink: ContextVar["OobEventSink | None"] = ContextVar(
36
+ "oob_event_sink", default=None
37
+ )
38
+ _module_sink: OobEventSink | None = None
39
+
40
+
41
+ class OobEventSink:
42
+ """Out-of-band LangchainAgentEvent 队列(task-notification bookend)。"""
43
+
44
+ def __init__(self, *, capacity: int = OOB_EVENT_QUEUE_CAP) -> None:
45
+ self._capacity = max(1, int(capacity))
46
+ self._queue: deque[LangchainAgentEvent] = deque()
47
+ self._projected_task_ids: set[str] = set()
48
+ self._dropped = 0
49
+
50
+ @property
51
+ def dropped_count(self) -> int:
52
+ return self._dropped
53
+
54
+ @property
55
+ def pending_count(self) -> int:
56
+ return len(self._queue)
57
+
58
+ def clear(self) -> None:
59
+ """测试 / 会话重置:清空队列与去重登记。"""
60
+ self._queue.clear()
61
+ self._projected_task_ids.clear()
62
+ self._dropped = 0
63
+
64
+ def note_terminal_projected(self, task_id: str) -> None:
65
+ """D-EVT-5′:图内 SUBAGENT_END(或其它终态)已覆盖时登记,后续 OOB no-op。"""
66
+ tid = str(task_id or "").strip()
67
+ if tid:
68
+ self._projected_task_ids.add(tid)
69
+
70
+ def emit_task_notification(
71
+ self,
72
+ *,
73
+ task_id: str,
74
+ status: TaskNotificationStatus,
75
+ output_file: str = "",
76
+ summary: str = "",
77
+ tool_use_id: str | None = None,
78
+ agent_id: str | None = None,
79
+ terminal_reason: str | None = None,
80
+ ) -> bool:
81
+ """入队一条 task-notification;同 task_id 已投影则 no-op。返回是否入队。"""
82
+ tid = str(task_id or "").strip()
83
+ if not tid:
84
+ return False
85
+ if tid in self._projected_task_ids:
86
+ return False
87
+ self._projected_task_ids.add(tid)
88
+
89
+ data: dict[str, object] = {
90
+ "task_id": tid,
91
+ "status": status,
92
+ "output_file": output_file or "",
93
+ "summary": summary or "",
94
+ }
95
+ if tool_use_id is not None:
96
+ data["tool_use_id"] = tool_use_id
97
+ if agent_id is not None:
98
+ data["agent_id"] = agent_id
99
+ if terminal_reason is not None:
100
+ data["terminal_reason"] = terminal_reason
101
+
102
+ event = LangchainAgentEvent(
103
+ event_type=LangchainAgentEventType.TASK_NOTIFICATION,
104
+ data=data,
105
+ )
106
+ if len(self._queue) >= self._capacity:
107
+ self._queue.popleft()
108
+ self._dropped += 1
109
+ logger.debug(
110
+ "OobEventSink capacity=%s exceeded; dropped oldest (total_dropped=%s)",
111
+ self._capacity,
112
+ self._dropped,
113
+ )
114
+ self._queue.append(event)
115
+ return True
116
+
117
+ def drain(self) -> list[LangchainAgentEvent]:
118
+ """取出并清空当前队列(保留 projected_task_ids 去重)。"""
119
+ items = list(self._queue)
120
+ self._queue.clear()
121
+ return items
122
+
123
+
124
+ def get_oob_event_sink() -> OobEventSink:
125
+ """当前 ContextVar sink,否则模块级单例(D-EVT-10:始终可 enqueue)。"""
126
+ global _module_sink
127
+ current = _current_sink.get()
128
+ if current is not None:
129
+ return current
130
+ if _module_sink is None:
131
+ _module_sink = OobEventSink()
132
+ return _module_sink
133
+
134
+
135
+ def install_oob_event_sink(sink: OobEventSink | None = None) -> tuple[OobEventSink, Token]:
136
+ """安装 ContextVar 级 sink(测试 / 隔离会话)。"""
137
+ target = sink if sink is not None else OobEventSink()
138
+ token = _current_sink.set(target)
139
+ return target, token
140
+
141
+
142
+ def ensure_oob_event_sink() -> tuple[OobEventSink, Token | None]:
143
+ """若 ContextVar 已有 sink 则复用(嵌套 Workflow);否则安装新 sink。
144
+
145
+ 返回的 Token 为 None 表示未新装,teardown 时应跳过。
146
+ """
147
+ current = _current_sink.get()
148
+ if current is not None:
149
+ return current, None
150
+ sink, token = install_oob_event_sink()
151
+ return sink, token
152
+
153
+
154
+ def reset_oob_event_sink(token: Token) -> None:
155
+ _current_sink.reset(token)
156
+
157
+
158
+ def teardown_oob_event_sink(token: Token | None) -> None:
159
+ """clear + reset;token 为 None 时 no-op(嵌套复用父 sink)。"""
160
+ if token is None:
161
+ return
162
+ try:
163
+ get_oob_event_sink().clear()
164
+ finally:
165
+ reset_oob_event_sink(token)
166
+
167
+
168
+ def drain_oob_events() -> list[LangchainAgentEvent]:
169
+ """便捷:drain 当前 sink。"""
170
+ return get_oob_event_sink().drain()
171
+
172
+
173
+ __all__ = [
174
+ "OOB_EVENT_QUEUE_CAP",
175
+ "OobEventSink",
176
+ "TaskNotificationStatus",
177
+ "drain_oob_events",
178
+ "ensure_oob_event_sink",
179
+ "get_oob_event_sink",
180
+ "install_oob_event_sink",
181
+ "reset_oob_event_sink",
182
+ "teardown_oob_event_sink",
183
+ ]
@@ -28,6 +28,7 @@ from langchain_agentx.observability.events.agent_event_projector import (
28
28
  EventAdapterState,
29
29
  LangchainAgentEvent,
30
30
  )
31
+ from langchain_agentx.observability.events.oob_event_sink import drain_oob_events
31
32
  from langchain_agentx.observability.events.workflow_event_mapper import (
32
33
  WorkflowEventMapper,
33
34
  )
@@ -80,6 +81,8 @@ class WorkflowAwareLangGraphEventAdapter:
80
81
  try:
81
82
  async for mapped in self._project_raw_event(raw_event):
82
83
  yield mapped
84
+ for oob in drain_oob_events():
85
+ yield oob
83
86
  except (GeneratorExit, asyncio.CancelledError):
84
87
  raise
85
88
  except (WorkflowScopeResolutionError, ValueError) as exc:
@@ -108,6 +111,8 @@ class WorkflowAwareLangGraphEventAdapter:
108
111
  mapped = self._workflow_mapper.map(workflow_event)
109
112
  if mapped is not None:
110
113
  yield mapped
114
+ for oob in drain_oob_events():
115
+ yield oob
111
116
 
112
117
  async def _project_raw_event(
113
118
  self, raw_event: Dict[str, Any]
@@ -145,6 +150,8 @@ class WorkflowAwareLangGraphEventAdapter:
145
150
  async for raw_event in events:
146
151
  async for mapped in self._project_raw_event(raw_event):
147
152
  yield mapped
153
+ for oob in drain_oob_events():
154
+ yield oob
148
155
  emit_finalize = True
149
156
  except (GeneratorExit, asyncio.CancelledError):
150
157
  raise
@@ -158,6 +165,8 @@ class WorkflowAwareLangGraphEventAdapter:
158
165
  mapped = self._workflow_mapper.map(workflow_event)
159
166
  if mapped is not None:
160
167
  yield mapped
168
+ for oob in drain_oob_events():
169
+ yield oob
161
170
 
162
171
  def _sync_workflow_session_id(self, raw_event: Dict[str, Any]) -> None:
163
172
  """Deprecated: session 同步已内联至 _project_raw_event()。"""
@@ -123,6 +123,7 @@ class AgentSession:
123
123
  )
124
124
  self._cleanup_registry = CleanupRegistry()
125
125
  self._session_closing = False
126
+ self._oob_sink_token = None
126
127
 
127
128
  def register_cleanup(
128
129
  self,
@@ -143,8 +144,13 @@ class AgentSession:
143
144
  )
144
145
 
145
146
  async def __aenter__(self) -> "AgentSession":
147
+ from langchain_agentx.observability.events.oob_event_sink import (
148
+ install_oob_event_sink,
149
+ )
150
+
146
151
  if self._graph is None:
147
152
  self._graph = self._build_graph()
153
+ _, self._oob_sink_token = install_oob_event_sink()
148
154
  if self._plugin_loader is not None:
149
155
  load_result = await self._plugin_loader.load_all(container_type="interactive")
150
156
  load_result.log_errors(logger)
@@ -152,6 +158,10 @@ class AgentSession:
152
158
  return self
153
159
 
154
160
  async def __aexit__(self, *_) -> None:
161
+ from langchain_agentx.observability.events.oob_event_sink import (
162
+ teardown_oob_event_sink,
163
+ )
164
+
155
165
  self._session_closing = True
156
166
  self._cleanup_registry.mark_closing()
157
167
  try:
@@ -193,6 +203,8 @@ class AgentSession:
193
203
  )
194
204
  finally:
195
205
  self._cleanup_registry.clear()
206
+ teardown_oob_event_sink(self._oob_sink_token)
207
+ self._oob_sink_token = None
196
208
 
197
209
  async def run_loop(self, user_input: str) -> dict[str, Any]:
198
210
  result = await self._run_loop_turn(user_input=user_input)
@@ -356,6 +368,16 @@ class AgentSession:
356
368
  self._session_id,
357
369
  )
358
370
 
371
+ def drain_oob_agent_events(self) -> list[Any]:
372
+ """拉取 OOB task-notification(D-EVT-10)。
373
+
374
+ 经 LangchainAgentEvent adapter 消费时已自动 drain;raw ``stream_loop_events``
375
+ 宿主可在 kill / tick 后调用本方法合并到 UI。
376
+ """
377
+ from langchain_agentx.observability.events.oob_event_sink import drain_oob_events
378
+
379
+ return drain_oob_events()
380
+
359
381
  @property
360
382
  def last_finish_outcome(self) -> dict[str, Any] | None:
361
383
  """最近一次 run_loop / resume / stream 结束后的 finish_outcome 快照。
@@ -100,6 +100,7 @@ class ConversationSession:
100
100
  )
101
101
  self._cleanup_registry = CleanupRegistry()
102
102
  self._session_closing = False
103
+ self._oob_sink_token = None
103
104
 
104
105
  def register_cleanup(
105
106
  self,
@@ -120,6 +121,11 @@ class ConversationSession:
120
121
  )
121
122
 
122
123
  async def __aenter__(self) -> "ConversationSession":
124
+ from langchain_agentx.observability.events.oob_event_sink import (
125
+ install_oob_event_sink,
126
+ )
127
+
128
+ _, self._oob_sink_token = install_oob_event_sink()
123
129
  if self._plugin_loader is not None:
124
130
  load_result = await self._plugin_loader.load_all(container_type="conversation")
125
131
  load_result.log_errors(logger)
@@ -127,6 +133,10 @@ class ConversationSession:
127
133
  return self
128
134
 
129
135
  async def __aexit__(self, *_) -> None:
136
+ from langchain_agentx.observability.events.oob_event_sink import (
137
+ teardown_oob_event_sink,
138
+ )
139
+
130
140
  self._session_closing = True
131
141
  self._cleanup_registry.mark_closing()
132
142
  try:
@@ -168,6 +178,8 @@ class ConversationSession:
168
178
  )
169
179
  finally:
170
180
  self._cleanup_registry.clear()
181
+ teardown_oob_event_sink(self._oob_sink_token)
182
+ self._oob_sink_token = None
171
183
 
172
184
  async def run_turn(self, user_input: str) -> dict[str, Any]:
173
185
  """每次用户请求调用一次,graph 每次新建。"""
@@ -415,6 +415,12 @@ class BaseWorkflow(ABC):
415
415
  }
416
416
  parent_ctx = get_run_context() if parent_workflow is not None else None
417
417
  run_ctx, run_ctx_token = install_run_context(parent=parent_ctx)
418
+ from langchain_agentx.observability.events.oob_event_sink import (
419
+ ensure_oob_event_sink,
420
+ teardown_oob_event_sink,
421
+ )
422
+
423
+ _, oob_token = ensure_oob_event_sink()
418
424
 
419
425
  try:
420
426
  return await graph.ainvoke(initial_state, config=effective_config)
@@ -428,7 +434,10 @@ class BaseWorkflow(ABC):
428
434
  try:
429
435
  await self._drain_pending_memory_extractions()
430
436
  finally:
431
- self._active_run_config = previous_active
437
+ try:
438
+ teardown_oob_event_sink(oob_token)
439
+ finally:
440
+ self._active_run_config = previous_active
432
441
 
433
442
  def as_node(
434
443
  self,
@@ -575,6 +584,12 @@ class BaseWorkflow(ABC):
575
584
  }
576
585
  parent_ctx = get_run_context() if parent_workflow is not None else None
577
586
  run_ctx, run_ctx_token = install_run_context(parent=parent_ctx)
587
+ from langchain_agentx.observability.events.oob_event_sink import (
588
+ ensure_oob_event_sink,
589
+ teardown_oob_event_sink,
590
+ )
591
+
592
+ _, oob_token = ensure_oob_event_sink()
578
593
  try:
579
594
  graph = self.compile()
580
595
  effective_config = self._resolve_effective_run_config(
@@ -605,7 +620,10 @@ class BaseWorkflow(ABC):
605
620
  try:
606
621
  await self._drain_pending_memory_extractions()
607
622
  finally:
608
- self._active_run_config = previous_active
623
+ try:
624
+ teardown_oob_event_sink(oob_token)
625
+ finally:
626
+ self._active_run_config = previous_active
609
627
 
610
628
  def _build_langgraph_adapter_config(
611
629
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-agentx-python
3
- Version: 2.1.6
3
+ Version: 2.1.7
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=9kp41PlLsu1OkW2UNEbKapzGwd3QF4HmbnZsX16b0fQ,1614
1
+ langchain_agentx/__init__.py,sha256=YCgjkZ-ETOiTsxprdpJbKhr-a726t37vZ6KsVTZcr2M,1614
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
@@ -28,7 +28,7 @@ langchain_agentx/loop/__init__.py,sha256=DspRAfaf_j6NkMByH6TqG1jTis8gyFGAipREVfn
28
28
  langchain_agentx/loop/benchmark_frozen_config.py,sha256=YI4IaDVMBOEucF36F2oNv5mw1TqMG_rm_LJSpKAnFWM,13942
29
29
  langchain_agentx/loop/loop_abort.py,sha256=IWcbqDA1ye4k00e2-mRjCgOtQl15EDk9tTosbgR4haU,2459
30
30
  langchain_agentx/loop/message_contract.py,sha256=Twl83TneU2PRdxgGRBxs6S9_ovRT6cb5WeCUzd6oJnE,2196
31
- langchain_agentx/loop/run_context.py,sha256=effS742NxcKV6i7Te4LDItw8czKnB40sz_7FSQ9FuxY,8062
31
+ langchain_agentx/loop/run_context.py,sha256=dhNiLhvqD_QYacGMA0B5P3wWpSe3O6wU_XcVEzb86Z4,8303
32
32
  langchain_agentx/loop/config/__init__.py,sha256=WdRQVXuvGU4myaGOZ8guFFvUb66jfsAIp9DLANJGieg,1083
33
33
  langchain_agentx/loop/config/agent_config.py,sha256=tOnAHFor6dkle3FvUOVx0YsmiYVTH11UrWSyTmOLV0c,3100
34
34
  langchain_agentx/loop/config/interaction_profile.py,sha256=UWrlI3aTWjYRq1mDeXL-xL-iCrQU-bXDXhETP6q9icY,10020
@@ -62,7 +62,7 @@ langchain_agentx/loop/exit/__init__.py,sha256=3-CMz5ApkJeBc549vRfgNABenyYKVbib4I
62
62
  langchain_agentx/loop/exit/api_error_detection.py,sha256=QzyMTov4VrkpHmn288Tw7DN00hiyJNXMKlOrCbvsiAA,1479
63
63
  langchain_agentx/loop/exit/exit_logic.py,sha256=nJzerVtTmS1iIY3PyBRet7jZ9v9pi-jUO3mJwzqxcnw,13922
64
64
  langchain_agentx/loop/exit/query_source.py,sha256=b_VB13zxNbLAqUAuDt1E3SII3jNLagY-FdN6SbczTsI,2082
65
- langchain_agentx/loop/exit/reason_codes.py,sha256=jIakFkKSgfO2hPDxEMA95b1ekOd_pHlVb7yFU9vCu1M,4616
65
+ langchain_agentx/loop/exit/reason_codes.py,sha256=Nk1x2ipftRIRa4U1Z2goyXXWB9TsPFL9TW6WfvtMCEo,4810
66
66
  langchain_agentx/loop/exit/resolution.py,sha256=taIMHzAUtgrQoU9446-CUXBN0PyCujhl6wBQfbxR1qI,1427
67
67
  langchain_agentx/loop/exit/terminal.py,sha256=_DAZk-kS4Xd0nxMlayLo60-uj1Y5ozgBjLlWUP6RDdc,3524
68
68
  langchain_agentx/loop/exit/withheld_error.py,sha256=LfX3j7Rgd9VoMzlz7GOs9TPuRe7RXr_Gxweyotx3tqQ,1846
@@ -138,7 +138,7 @@ langchain_agentx/loop/stream/synthetic_events.py,sha256=Z3JT3iTuB9UQOoQXUprAYvaF
138
138
  langchain_agentx/loop/stream/transcript_gate.py,sha256=4AH3O0l8OrY_GQdqf-TOKOVS3selPLUC0jYLWQUTfkg,4690
139
139
  langchain_agentx/loop/subagent/__init__.py,sha256=V5G_R0STODOi2Of0ALq1ZxiMPllw_mxDhgb5bc9HuqA,2677
140
140
  langchain_agentx/loop/subagent/active_run_registry.py,sha256=KurhAMDTBnRkdxNidaMUynCTRzdDy67Tu2NwJt3UC_g,4310
141
- langchain_agentx/loop/subagent/async_runner.py,sha256=CU5tqieWzHDXz16KaQ1FYEIKsXdpkSf9LlUAdJsR9uw,11744
141
+ langchain_agentx/loop/subagent/async_runner.py,sha256=gGx8UMZbsnxUMPzc_7-XkWgd1EiuRcFAoOVUfjhFltE,13740
142
142
  langchain_agentx/loop/subagent/boundary_snapshot.py,sha256=9cQuqN29nJDRnHnx9c8b09KRmODE2zv15Z06QyjVJps,10008
143
143
  langchain_agentx/loop/subagent/cache_safe_params.py,sha256=-qcgdazEYRy6s4ILQuttpxraE3gsxq17Ro7LYBnuv3E,4460
144
144
  langchain_agentx/loop/subagent/context.py,sha256=skkSPvnshEN4Lh5kWoD7zjUtCOKcjdb4Ho1eM1apXFo,22297
@@ -201,7 +201,7 @@ langchain_agentx/memory/session/extractor.py,sha256=UUprKKtcMqn0pVhEWSnwCcc2tlB6
201
201
  langchain_agentx/memory/session/prompts.py,sha256=MA6UaNQsn9jrV78YuPbTTSKTmzvex_HY7kAaM9gVhUA,7690
202
202
  langchain_agentx/memory/session/session_memory.py,sha256=DdHkX-3rGOkyuxc3byq1hWNkHgucyo-n1jwUEWjxuP8,16642
203
203
  langchain_agentx/memory/session/tool_scope.py,sha256=DDhhzy_BEdZZe9SsisqZ9GOgMyt-aWUnCX-NH3QSPng,1684
204
- langchain_agentx/observability/__init__.py,sha256=UTV3th78Bw_b-Mo14EkxT7zpO_UoviHcZv17P5nCnY8,2177
204
+ langchain_agentx/observability/__init__.py,sha256=hKf_W4rTExbGQbK1ZyrrvmJ2aX-5Tx5hhY-Q8-gwkSg,2591
205
205
  langchain_agentx/observability/evaluation/__init__.py,sha256=yqkw8noWqD-7p6xnhlF-M4F2FwYukS2XDpEYoXFNYZ4,430
206
206
  langchain_agentx/observability/evaluation/retention_scheduler.py,sha256=8iKw9G8jImIaKfmGhO9Xtm3GhvSICuGwEZp7GGczDNo,2250
207
207
  langchain_agentx/observability/evaluation/service.py,sha256=E0MZIBY67aVDooOYuri3_vTM_uguXJxCxNqQ5rJxFOg,3660
@@ -214,21 +214,22 @@ langchain_agentx/observability/evaluation/checkers/degradation.py,sha256=80-oENY
214
214
  langchain_agentx/observability/evaluation/checkers/exit_quality.py,sha256=kcV2VYLeavinm5-hysCVhNfIjv2Gj_m4Xt65B403FoY,1274
215
215
  langchain_agentx/observability/evaluation/checkers/session_memory.py,sha256=jYDdIDUAhYktggsymkBADBunrSbxdC66veM-tZt3a3s,1579
216
216
  langchain_agentx/observability/evaluation/checkers/tool_behavior.py,sha256=o1nH8SeE9145H7ej4naahRcmgw_9st21dY1W3IQa2sg,2097
217
- langchain_agentx/observability/events/__init__.py,sha256=9IfY3IcALgQjxdsircWrwTCFuSwr8MTKBDstC8CXPHY,1051
217
+ langchain_agentx/observability/events/__init__.py,sha256=Zr9Iguf9XsJQLsCJy13YFdBtXHPEnI9aSWXWmA2MisA,1497
218
218
  langchain_agentx/observability/events/accumulated_text_scope.py,sha256=M4OvuQTgReF8SFrOmr-M5vyOJpzfvxj_DKD5bVpGb70,3169
219
219
  langchain_agentx/observability/events/agent_defaults.py,sha256=gmX0P__lux5xbCb8dtAlVCb3wZ7avaqFleoW4Q5anw4,1187
220
- langchain_agentx/observability/events/agent_event_projector.py,sha256=0xm_lWv5wtlhI05aL08yPSwTuA_Gj4cDXB00ni5bihI,62075
220
+ langchain_agentx/observability/events/agent_event_projector.py,sha256=eMri832yK_BZbwsB_APwUN9vu66zMpZVxTstChG5l0A,63010
221
221
  langchain_agentx/observability/events/api_retry_projection.py,sha256=XGb09sNOi6k1ZGW4YqMLBE8GDxNwNMprk62jV9TS9dQ,2433
222
222
  langchain_agentx/observability/events/factory.py,sha256=gqMATIK9tBQzaPWSPhCCSPuCq-WF562ZDTnFUaMm9Qc,1570
223
- langchain_agentx/observability/events/finish_event_payload_composer.py,sha256=W4jLFy6868GX122qljjeLJowsG7g26o4eKVbHEa7DJc,11481
224
- langchain_agentx/observability/events/finish_outcome.py,sha256=I1-1RKQYMRXA5Kw0wDPXFpjdrGS421RM2fIL3gAY2kA,8926
225
- langchain_agentx/observability/events/langchain_agentx_event_adapter.py,sha256=plPZpoqYwDFR_XgdjH5NMQnl_oO6-rixBNl4sGJ0XRw,7519
223
+ langchain_agentx/observability/events/finish_event_payload_composer.py,sha256=v7MyLNSZ_gR5u5WFxPJu_0MYNDvC9s9gI2iNNt9Vbr4,13189
224
+ langchain_agentx/observability/events/finish_outcome.py,sha256=igoueAwduzuuGDwg6u9AbBB-T2UBiqbUfNoxWCBYc_g,10401
225
+ langchain_agentx/observability/events/langchain_agentx_event_adapter.py,sha256=ujtD6G8erb9bwnHLvJYa-soWjzP99vjAE0tNKSMUGe8,7841
226
226
  langchain_agentx/observability/events/model_end_text_backfill.py,sha256=7RBnxWLi1Oe-Bw9WTYqN-B6IdytFlY-Jso1STtQr6rE,5157
227
+ langchain_agentx/observability/events/oob_event_sink.py,sha256=ijE2AI8gfoMPRPXDwQszNWi_aCKwODywpG884blo08E,5670
227
228
  langchain_agentx/observability/events/subagent_event_mapper.py,sha256=9M9cKzMz7_OXOod0JKtLb-FtUhAKiAVikKGRF6h6Ttk,6860
228
229
  langchain_agentx/observability/events/subagent_window_text_guard.py,sha256=myo4SSV2TKZjpyrCoe-g5beNGmZnYZefa8nqW76q0Xc,3101
229
230
  langchain_agentx/observability/events/tool_call_id_resolver.py,sha256=Mun8jNb91tbV7hvIUkFG9g6NJGHVMW0kHS7Zt6N2gUA,4530
230
231
  langchain_agentx/observability/events/tool_outcome_resolver.py,sha256=YqF9Io9-0xdIdz7iYQuJkY-PiUXLC0zR26Eb9n35L9g,4357
231
- langchain_agentx/observability/events/workflow_aware_event_adapter.py,sha256=GlKN4vQcnXUSC9vcBWbrg4B6Td3aglzcOr4zDUfsXXw,7490
232
+ langchain_agentx/observability/events/workflow_aware_event_adapter.py,sha256=AxJ_TjfZsUKHifGUl_3JvG5dDk8rc-IhuNLVw1ffhf8,7896
232
233
  langchain_agentx/observability/events/workflow_container_projector.py,sha256=0UdtZwKBM9k9yWV7gbbwMquT0I_cZlNqnjO0SCbvZ9s,22414
233
234
  langchain_agentx/observability/events/workflow_event_mapper.py,sha256=EA0GFWmrSIyqCOehixKnYvylF0NOj5RkRhdLA50_qrU,3703
234
235
  langchain_agentx/observability/logging/__init__.py,sha256=mDtPOxYZZFx8ezQCSwnmnfrdVi305DeJFzThwHBtnrY,508
@@ -292,11 +293,11 @@ langchain_agentx/provider/semantics/families/openai_finish_reason.py,sha256=t5eb
292
293
  langchain_agentx/provider/semantics/families/openai_interpreter.py,sha256=zT7EFfHJ5fmYZ_F59vDfyrYdEwcMJANxkoW1Pt3iocI,1915
293
294
  langchain_agentx/provider/semantics/families/openai_normalizer.py,sha256=oaf2Riz8EY6VQpc6SNKtsb-naMELw3gYV3IsnZoRBYU,2155
294
295
  langchain_agentx/session/__init__.py,sha256=uXxjBzhW1FtvSfDTTaGZPbyeSFRzRQb2MfxDumKViug,1234
295
- langchain_agentx/session/agent_session.py,sha256=bxjtuWDsWB3AK6Ij4jjnrCp6AtWn1NcS-2H5HieEWB0,23771
296
+ langchain_agentx/session/agent_session.py,sha256=NEIKJIjp-whbpmduSatBaF1b6YIcebvUYB1ginu12WE,24617
296
297
  langchain_agentx/session/cleanup_registry.py,sha256=aaPUn9psCwgZ27fB1qW7lzc1TeKaBvQAxhcHDiNs2Rs,4137
297
298
  langchain_agentx/session/conversation_factory.py,sha256=zsGBx9OwAOW21sqFsjK4d9mYLRRyxC5Bp1R9OCijxzI,5911
298
299
  langchain_agentx/session/conversation_recovery.py,sha256=OXzaxywRmqRwNA2Q1UNOpUvMlIjBzgdbsNPxPGHwoaA,5803
299
- langchain_agentx/session/conversation_session.py,sha256=xHbZfor4ZR9l0ANCgkZ2FUnzpY3cE6afHxZn6ZeKqQg,17125
300
+ langchain_agentx/session/conversation_session.py,sha256=aQJBZlQsgg2-q_it4qvRwhPkvKNODDZ1TDsrMXMwvCE,17563
300
301
  langchain_agentx/session/factory.py,sha256=qzZF8EkELFKPzQ-fRI2C72Usnqk_hASWB1GM8_mYjZ8,6172
301
302
  langchain_agentx/session/plan_mode.py,sha256=GjPLBG6eVrTB7M-CeMwLqkHCQDdD93cCgwIAvOF0wRM,4710
302
303
  langchain_agentx/session/plan_mode_attachments.py,sha256=byIJC2F3RTJ9BTWVhpbE1e1rRwahAH5anPKsMXFvUE8,13861
@@ -691,7 +692,7 @@ langchain_agentx/utils/permissions/filesystem.py,sha256=79vRSRPnPadHlDOOYvvbuJaJ
691
692
  langchain_agentx/utils/settings/__init__.py,sha256=UXzHZmrBJsO8ZpDmHfclHXpS7rfpmqRoXEnmTFbtVpE,172
692
693
  langchain_agentx/utils/settings/managed_path.py,sha256=MVcw4F9bTrhKqTLom8ZiiqSqCREMbGcAK9Lt8hkD67M,1155
693
694
  langchain_agentx/workflow/__init__.py,sha256=wgjJM2wW0DbFHjsNbq2bKYgFs3BIPFRuQbH8Ry5kfys,4324
694
- langchain_agentx/workflow/base.py,sha256=nAXw5iXFyL0s5NCRhpAHXs2uGb3vLZ3dHa1i2RbLXwc,32741
695
+ langchain_agentx/workflow/base.py,sha256=rzZTH0ciepTglXfhDMkhl1uiJV4d_bXkOgpHlmu-N0w,33409
695
696
  langchain_agentx/workflow/batch.py,sha256=rdE9xbQ2-SI3-6yNTsKoSylDoTD5B0djLSKwqy1BdZ8,5474
696
697
  langchain_agentx/workflow/catalog_registry.py,sha256=tYUny5FkHwQU-RRUxOdxaJhiRV_9VRXU636Yn_Gzt-g,2109
697
698
  langchain_agentx/workflow/dag.py,sha256=Y1IR1IwnM79Xuev6HZnOvAE3Q23MRnA9lu4vg2Cie48,4469
@@ -756,8 +757,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
756
757
  langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
757
758
  langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
758
759
  langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
759
- langchain_agentx_python-2.1.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
760
- langchain_agentx_python-2.1.6.dist-info/METADATA,sha256=Jk8RM9LRkZ7JXk8iU7fxbJeBgtLHKXiHUJ3tN14BrkU,24250
761
- langchain_agentx_python-2.1.6.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
762
- langchain_agentx_python-2.1.6.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
763
- langchain_agentx_python-2.1.6.dist-info/RECORD,,
760
+ langchain_agentx_python-2.1.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
761
+ langchain_agentx_python-2.1.7.dist-info/METADATA,sha256=l7wjAieBJ28Tvtsfo0pNg503DMYjggzW8kQHm8t4T3M,24250
762
+ langchain_agentx_python-2.1.7.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
763
+ langchain_agentx_python-2.1.7.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
764
+ langchain_agentx_python-2.1.7.dist-info/RECORD,,