power-loop 6.52.0__tar.gz → 6.53.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. {power_loop-6.52.0 → power_loop-6.53.0}/PKG-INFO +1 -1
  2. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/__init__.py +1 -1
  3. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/pipeline.py +12 -3
  4. power_loop-6.53.0/power_loop/runtime/bash_capture.py +38 -0
  5. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/default_tools.py +5 -0
  6. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop.egg-info/PKG-INFO +1 -1
  7. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop.egg-info/SOURCES.txt +1 -0
  8. {power_loop-6.52.0 → power_loop-6.53.0}/LICENSE +0 -0
  9. {power_loop-6.52.0 → power_loop-6.53.0}/README.md +0 -0
  10. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/__init__.py +0 -0
  11. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/__init__.py +0 -0
  12. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/anthropic_factory.py +0 -0
  13. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/capabilities.py +0 -0
  14. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/interface.py +0 -0
  15. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/llm_factory.py +0 -0
  16. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/llm_tooling.py +0 -0
  17. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/llm_utils.py +0 -0
  18. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/multimodal.py +0 -0
  19. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/_vendor/llm_client/proxy_fallback.py +0 -0
  20. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/agent/__init__.py +0 -0
  21. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/agent/follow_up.py +0 -0
  22. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/agent/sink.py +0 -0
  23. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/agent/stateful_loop.py +0 -0
  24. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/agent/system_prompt.py +0 -0
  25. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/agent/types.py +0 -0
  26. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/__init__.py +0 -0
  27. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/errors.py +0 -0
  28. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/event_payloads.py +0 -0
  29. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/events.py +0 -0
  30. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/handlers.py +0 -0
  31. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/hook_contexts.py +0 -0
  32. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/hooks.py +0 -0
  33. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/messages.py +0 -0
  34. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/protocols.py +0 -0
  35. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contracts/tools.py +0 -0
  36. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/__init__.py +0 -0
  37. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/_redact.py +0 -0
  38. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/jsonl_sink.py +0 -0
  39. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/logging_sink.py +0 -0
  40. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/mcp.py +0 -0
  41. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/metrics_sink.py +0 -0
  42. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/contrib/otel_sink.py +0 -0
  43. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/agent_context.py +0 -0
  44. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/events.py +0 -0
  45. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/hooks.py +0 -0
  46. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/phase.py +0 -0
  47. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/runner.py +0 -0
  48. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/core/state.py +0 -0
  49. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/py.typed +0 -0
  50. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/background.py +0 -0
  51. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/blackboard.py +0 -0
  52. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/budget.py +0 -0
  53. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/cancellation.py +0 -0
  54. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/capability_probe.py +0 -0
  55. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/compact.py +0 -0
  56. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/env.py +0 -0
  57. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/exec_backend.py +0 -0
  58. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/fold.py +0 -0
  59. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/fold_adapter.py +0 -0
  60. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/history_projector.py +0 -0
  61. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/history_sanitize.py +0 -0
  62. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/human_input.py +0 -0
  63. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/image_recall.py +0 -0
  64. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/memory.py +0 -0
  65. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/notes.py +0 -0
  66. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/provider.py +0 -0
  67. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/representation.py +0 -0
  68. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/retry.py +0 -0
  69. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/runtime_state.py +0 -0
  70. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/session_store.py +0 -0
  71. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/skills.py +0 -0
  72. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/spec.py +0 -0
  73. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/steer.py +0 -0
  74. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/stop_policy.py +0 -0
  75. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/__init__.py +0 -0
  76. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/backends/__init__.py +0 -0
  77. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/backends/mysql.py +0 -0
  78. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/backends/postgres.py +0 -0
  79. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/backends/sqlite.py +0 -0
  80. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/capabilities.py +0 -0
  81. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/db.py +0 -0
  82. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/dialect.py +0 -0
  83. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/factory.py +0 -0
  84. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/schema.py +0 -0
  85. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/store.py +0 -0
  86. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/store/types.py +0 -0
  87. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/structured.py +0 -0
  88. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/stub_provider.py +0 -0
  89. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/timers.py +0 -0
  90. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/todos.py +0 -0
  91. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/runtime/usage_estimate.py +0 -0
  92. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/__init__.py +0 -0
  93. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/blackboard.py +0 -0
  94. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/command_policy.py +0 -0
  95. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/command_rules.py +0 -0
  96. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/default_manifest.py +0 -0
  97. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/registry.py +0 -0
  98. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/tools/spawn_agent.py +0 -0
  99. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/__init__.py +0 -0
  100. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/admission.py +0 -0
  101. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/api.py +0 -0
  102. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/engine.py +0 -0
  103. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/introspect.py +0 -0
  104. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/journal.py +0 -0
  105. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/result.py +0 -0
  106. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/resume.py +0 -0
  107. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/runner.py +0 -0
  108. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/spec.py +0 -0
  109. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/subprocess_executor.py +0 -0
  110. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/tool.py +0 -0
  111. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop/workflow/worker.py +0 -0
  112. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop.egg-info/dependency_links.txt +0 -0
  113. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop.egg-info/requires.txt +0 -0
  114. {power_loop-6.52.0 → power_loop-6.53.0}/power_loop.egg-info/top_level.txt +0 -0
  115. {power_loop-6.52.0 → power_loop-6.53.0}/pyproject.toml +0 -0
  116. {power_loop-6.52.0 → power_loop-6.53.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: power-loop
3
- Version: 6.52.0
3
+ Version: 6.53.0
4
4
  Summary: Embeddable agent execution kernel — LLM loop, hooks, events, tools, dynamic sub-agents.
5
5
  Author-email: zhangran <zhangran24@126.com>
6
6
  License: MIT
@@ -15,7 +15,7 @@ Stability tiers
15
15
  无版本承诺,可随时变更或删除。
16
16
  """
17
17
 
18
- __version__ = "6.52.0"
18
+ __version__ = "6.53.0"
19
19
 
20
20
  # Public LLM contract (SDK-free) re-exported so callers (e.g. writing llm.* hooks or
21
21
  # a custom LLMService) don't reach into the internal vendored transport package (H3.4).
@@ -931,6 +931,8 @@ class AgentPipeline:
931
931
  elif row.get("role") == "tool":
932
932
  results[str(row.get("tool_call_id") or "")] = i
933
933
  protected = {cid for cid, _, _ in calls[-hot_tail:]} if hot_tail > 0 else set()
934
+ # Optional host protection for individual calls (e.g. bash loading persistent skills).
935
+ protected.update(getattr(self, "_context_protected_call_ids", ()))
934
936
  keep = set(self.config.insend_distill_keep_tools or ())
935
937
  freed_chars = 0
936
938
  for cid, i, j in calls:
@@ -951,7 +953,8 @@ class AgentPipeline:
951
953
  except Exception: # a broken projector must not stop the run or starve later calls
952
954
  logger.warning("in-send distillation failed for %s", name, exc_info=True)
953
955
  continue
954
- seq = seqs[i] if i < len(seqs) else None
956
+ # recall_send needs the result row; it includes the matching call arguments.
957
+ seq = seqs[ri] if ri < len(seqs) else None
955
958
  coord = (f"recall_send(send_index={self.send_index}, seq={seq})"
956
959
  if self.send_index is not None and seq is not None else "recall_send")
957
960
  new_content = f"{self._DISTILL_MARK}{self.send_index} seq={seq}; {coord}] {line}"
@@ -1022,11 +1025,17 @@ class AgentPipeline:
1022
1025
 
1023
1026
  # Fuse uses total prompt size, including the fixed prompt and history prefix.
1024
1027
  dt = self.config.insend_distill_tokens
1025
- if dt is not None and int(dt) > 0 and round_index > 0:
1028
+ if dt is not None and int(dt) > 0 and (
1029
+ round_index > 0 or getattr(self, "_context_prompt_tokens_estimate", None) is not None
1030
+ ):
1026
1031
  real = int((self.ctx.token_usage or {}).get("prompt_tokens", 0) or 0)
1027
1032
  if real and self._send_baseline_prompt is None:
1028
1033
  self._send_baseline_prompt = real # diagnostics only; never subtracted from trigger
1029
- total = real or self._estimate_history_tokens()
1034
+ # Optional host estimate after context retirement. Never mutate provider
1035
+ # usage: the latter is billing evidence for the previous request.
1036
+ current_estimate = getattr(self, "_context_prompt_tokens_estimate", None)
1037
+ total = (max(0, int(current_estimate)) if current_estimate is not None
1038
+ else real or self._estimate_history_tokens())
1030
1039
  if total >= int(dt):
1031
1040
  freed = self._distill_oldest_tool_rows(
1032
1041
  0, int(self.config.insend_distill_hot_tail or 0),
@@ -0,0 +1,38 @@
1
+ """Opt-in capture of normalized terminal output before bash display truncation.
2
+
3
+ This preserves every line delivered by the persistent terminal reader, not raw PTY
4
+ bytes (ANSI cleanup and terminal line normalization still apply). No capture by default.
5
+ """
6
+ from contextvars import ContextVar
7
+ from pathlib import Path
8
+
9
+ current_bash_capture: ContextVar['BashOutputCapture | None'] = ContextVar('bash_output_capture', default=None)
10
+
11
+
12
+ class BashOutputCapture:
13
+ def __init__(self, path: Path):
14
+ self.path = Path(path)
15
+ self.error: str | None = None
16
+ self.lines = 0
17
+
18
+ def __enter__(self):
19
+ self.stream = self.path.open('x', encoding='utf-8')
20
+ self.token = current_bash_capture.set(self)
21
+ return self
22
+
23
+ def write_line(self, line: str) -> None:
24
+ if self.error:
25
+ return
26
+ try:
27
+ self.stream.write(line + '\n')
28
+ self.lines += 1
29
+ except OSError as exc:
30
+ # Never interrupt command draining and leave a shell running with stale output.
31
+ self.error = type(exc).__name__
32
+
33
+ def __exit__(self, *exc):
34
+ current_bash_capture.reset(self.token)
35
+ try:
36
+ self.stream.close()
37
+ except OSError as error:
38
+ self.error = type(error).__name__
@@ -491,6 +491,9 @@ class BashSession:
491
491
  # head+tail (via _truncate_lines), so a command emitting millions of lines must not buffer
492
492
  # them all. Keep the first _BASH_HEAD_KEEP fully + the last _BASH_TAIL_KEEP in a ring,
493
493
  # dropping the middle (both bounds comfortably exceed _truncate_lines' 30+170 window).
494
+ from power_loop.runtime.bash_capture import current_bash_capture
495
+
496
+ capture = current_bash_capture.get()
494
497
  head: list[str] = []
495
498
  tail: deque[str] = deque(maxlen=_BASH_TAIL_KEEP)
496
499
  dropped = 0
@@ -499,6 +502,8 @@ class BashSession:
499
502
 
500
503
  def _accumulate(line: str) -> None:
501
504
  nonlocal dropped
505
+ if capture is not None:
506
+ capture.write_line(line)
502
507
  if len(head) < _BASH_HEAD_KEEP:
503
508
  head.append(line)
504
509
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: power-loop
3
- Version: 6.52.0
3
+ Version: 6.53.0
4
4
  Summary: Embeddable agent execution kernel — LLM loop, hooks, events, tools, dynamic sub-agents.
5
5
  Author-email: zhangran <zhangran24@126.com>
6
6
  License: MIT
@@ -49,6 +49,7 @@ power_loop/core/pipeline.py
49
49
  power_loop/core/runner.py
50
50
  power_loop/core/state.py
51
51
  power_loop/runtime/background.py
52
+ power_loop/runtime/bash_capture.py
52
53
  power_loop/runtime/blackboard.py
53
54
  power_loop/runtime/budget.py
54
55
  power_loop/runtime/cancellation.py
File without changes
File without changes
File without changes
File without changes