AbstractAgent 0.3.16__tar.gz → 0.3.17__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 (127) hide show
  1. {abstractagent-0.3.16/src/abstractagent.egg-info → abstractagent-0.3.17}/PKG-INFO +11 -8
  2. {abstractagent-0.3.16 → abstractagent-0.3.17}/README.md +7 -4
  3. {abstractagent-0.3.16 → abstractagent-0.3.17}/pyproject.toml +4 -4
  4. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/react_runtime.py +28 -2
  5. {abstractagent-0.3.16 → abstractagent-0.3.17/src/abstractagent.egg-info}/PKG-INFO +11 -8
  6. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent.egg-info/SOURCES.txt +1 -0
  7. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent.egg-info/requires.txt +3 -3
  8. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_history_monster_clamp.py +28 -15
  9. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_packaging_profiles.py +3 -3
  10. abstractagent-0.3.17/tests/test_react_history_window.py +64 -0
  11. {abstractagent-0.3.16 → abstractagent-0.3.17}/LICENSE +0 -0
  12. {abstractagent-0.3.16 → abstractagent-0.3.17}/setup.cfg +0 -0
  13. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/__init__.py +0 -0
  14. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/__init__.py +0 -0
  15. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/codeact_runtime.py +0 -0
  16. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/emit_inventory.py +0 -0
  17. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/generation_params.py +0 -0
  18. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/loop_hooks.py +0 -0
  19. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/media.py +0 -0
  20. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/memact_runtime.py +0 -0
  21. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/native_loop_registry.py +0 -0
  22. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/progress.py +0 -0
  23. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/read_orchestration.py +0 -0
  24. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/tool_allowlist.py +0 -0
  25. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/tool_failure_hints.py +0 -0
  26. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/adapters/transcripts.py +0 -0
  27. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/__init__.py +0 -0
  28. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/base.py +0 -0
  29. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/codeact.py +0 -0
  30. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/defaults.py +0 -0
  31. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/memact.py +0 -0
  32. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/react.py +0 -0
  33. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/task_reset.py +0 -0
  34. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/agents/unattended.py +0 -0
  35. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/logic/__init__.py +0 -0
  36. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/logic/builtins.py +0 -0
  37. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/logic/codeact.py +0 -0
  38. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/logic/memact.py +0 -0
  39. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/logic/react.py +0 -0
  40. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/logic/types.py +0 -0
  41. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/metrics/__init__.py +0 -0
  42. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/metrics/prefix_reuse.py +0 -0
  43. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/repl.py +0 -0
  44. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/sandbox/__init__.py +0 -0
  45. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/sandbox/interface.py +0 -0
  46. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/sandbox/local.py +0 -0
  47. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/scripts/__init__.py +0 -0
  48. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/scripts/lmstudio_tool_eval.py +0 -0
  49. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/tools/__init__.py +0 -0
  50. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/tools/code_execution.py +0 -0
  51. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent/tools/self_improve.py +0 -0
  52. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent.egg-info/dependency_links.txt +0 -0
  53. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent.egg-info/entry_points.txt +0 -0
  54. {abstractagent-0.3.16 → abstractagent-0.3.17}/src/abstractagent.egg-info/top_level.txt +0 -0
  55. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_active_context_policy_adoption.py +0 -0
  56. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_adversary_hardening_2026_07_13.py +0 -0
  57. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_agent_plan_review_modes.py +0 -0
  58. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_agent_speculation_inheritance.py +0 -0
  59. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_agent_stream_inheritance.py +0 -0
  60. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_agents_integration_llm.py +0 -0
  61. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_base_agent_trace_accessors.py +0 -0
  62. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_batching_prompt_rule.py +0 -0
  63. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_budget_triage_tail.py +0 -0
  64. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_builtin_tool_specs_optional_defaults.py +0 -0
  65. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_c2447_visit_lane_honesty.py +0 -0
  66. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_claim_0029_residue_2026_07_14.py +0 -0
  67. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_codeact_logic.py +0 -0
  68. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_codeact_runtime_verifier_policy.py +0 -0
  69. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_delegate_session_route_composition.py +0 -0
  70. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_emit_inventory_drift.py +0 -0
  71. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_gated_remainder_wave_2026_07_14.py +0 -0
  72. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_generation_params_media_policies.py +0 -0
  73. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_improvement_wave_2026_07_13.py +0 -0
  74. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_llm_call_payload_requires_prompt_or_messages.py +0 -0
  75. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_lmstudio_tool_eval_metrics.py +0 -0
  76. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_loop_hooks.py +0 -0
  77. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_loop_hooks_0026_follow_ups.py +0 -0
  78. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_memact_codeact_guidance_durable_transcript.py +0 -0
  79. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_memact_session_memory.py +0 -0
  80. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_memact_workflow.py +0 -0
  81. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_memory_recall_tool.py +0 -0
  82. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_native_loop_registry.py +0 -0
  83. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_open_attachment_tool_available.py +0 -0
  84. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_operator_conclude.py +0 -0
  85. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_optimal_wave_batch2_2026_07_13.py +0 -0
  86. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_parse_prompt_cache_capture.py +0 -0
  87. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_prefix_reuse_metric.py +0 -0
  88. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_progress_detection.py +0 -0
  89. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_act_only_observation.py +0 -0
  90. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_active_attachments_disable_tools_first_iteration.py +0 -0
  91. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_adapter_forwards_context_attachments_media.py +0 -0
  92. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_delegate_agent_tool.py +0 -0
  93. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_election_fence_conformance.py +0 -0
  94. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_entity_dress_conformance.py +0 -0
  95. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_guidance_durable_transcript.py +0 -0
  96. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_logic.py +0 -0
  97. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_loop_aborted_generation_retry.py +0 -0
  98. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_loop_context_transcript_levels.py +0 -0
  99. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_loop_followthrough_retry.py +0 -0
  100. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_loop_side_effect_dedupe_comms.py +0 -0
  101. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_loop_truncated_output_retry.py +0 -0
  102. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_max_iterations_concludes.py +0 -0
  103. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_phase_grant_boundary.py +0 -0
  104. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_planish_heuristic.py +0 -0
  105. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_prompt_prefix_stability.py +0 -0
  106. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_sight_lane_media_fold.py +0 -0
  107. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_turn_byte_stability.py +0 -0
  108. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_verifier_and_plan.py +0 -0
  109. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_visit_composition.py +0 -0
  110. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_visit_merge.py +0 -0
  111. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_workflow_system_prompt_and_tools_override.py +0 -0
  112. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_react_workflow_toolset_refresh.py +0 -0
  113. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_read_orchestration_guidance.py +0 -0
  114. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_reasoning_first_citizen_agent.py +0 -0
  115. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_review_failure_containment.py +0 -0
  116. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_run_start_seeds_runtime_provider_model.py +0 -0
  117. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_session_memory.py +0 -0
  118. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_sibling_loop_defects_batch.py +0 -0
  119. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_sibling_strict_transcripts_0011.py +0 -0
  120. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_stuck_streak_termination.py +0 -0
  121. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_tool_failure_hints.py +0 -0
  122. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_tool_loop_prefix_extension_a3.py +0 -0
  123. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_tool_observe_emits_result_for_tui.py +0 -0
  124. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_tools_editing.py +0 -0
  125. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_unseeded_render_limits.py +0 -0
  126. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_verifier_execution_preference.py +0 -0
  127. {abstractagent-0.3.16 → abstractagent-0.3.17}/tests/test_verifier_schema_strict.py +0 -0
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractagent
3
- Version: 0.3.16
3
+ Version: 0.3.17
4
4
  Summary: Agent implementations using AbstractRuntime and AbstractCore
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  Requires-Dist: abstractcore[tools]>=2.13.41
9
- Requires-Dist: abstractruntime>=0.5.0
9
+ Requires-Dist: abstractruntime>=0.7.0
10
10
  Provides-Extra: apple
11
11
  Requires-Dist: abstractcore[all-apple]>=2.13.41; extra == "apple"
12
- Requires-Dist: abstractruntime[apple]>=0.5.0; extra == "apple"
12
+ Requires-Dist: abstractruntime[apple]>=0.7.0; extra == "apple"
13
13
  Provides-Extra: gpu
14
14
  Requires-Dist: abstractcore[all-gpu]>=2.13.41; extra == "gpu"
15
- Requires-Dist: abstractruntime[gpu]>=0.5.0; extra == "gpu"
15
+ Requires-Dist: abstractruntime[gpu]>=0.7.0; extra == "gpu"
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: pytest>=7.0; extra == "dev"
18
18
  Provides-Extra: test
@@ -105,10 +105,13 @@ Native Python hardware profile cascades are available for deployment manifests:
105
105
  AbstractCore and AbstractRuntime profiles; AbstractAgent itself remains
106
106
  provider/runtime agnostic.
107
107
 
108
- AbstractAgent 0.3.16 requires `abstractcore[tools]>=2.13.41` and
109
- `AbstractRuntime>=0.5.0` (the runtime release that provides live token
110
- streaming, on top of turn grounding, in-flight cancellation and speculation
111
- inheritance). AbstractRuntime 0.5.0 itself requires AbstractCore 2.16.0 or newer.
108
+ AbstractAgent 0.3.17 requires `abstractcore[tools]>=2.13.41` and
109
+ `AbstractRuntime>=0.7.0` (the runtime release with the 50k-token history
110
+ window, which ReAct applies to its requests when a host sets
111
+ `_runtime.history_window_tokens`, as the entity visit does; 0.5.0 brought live
112
+ token streaming, on top of turn grounding, in-flight cancellation and
113
+ speculation inheritance). AbstractRuntime 0.7.0 itself requires AbstractCore
114
+ 2.18.0 or newer.
112
115
 
113
116
  Note: the repository may be ahead of the latest published PyPI release. To verify what you installed:
114
117
 
@@ -84,10 +84,13 @@ Native Python hardware profile cascades are available for deployment manifests:
84
84
  AbstractCore and AbstractRuntime profiles; AbstractAgent itself remains
85
85
  provider/runtime agnostic.
86
86
 
87
- AbstractAgent 0.3.16 requires `abstractcore[tools]>=2.13.41` and
88
- `AbstractRuntime>=0.5.0` (the runtime release that provides live token
89
- streaming, on top of turn grounding, in-flight cancellation and speculation
90
- inheritance). AbstractRuntime 0.5.0 itself requires AbstractCore 2.16.0 or newer.
87
+ AbstractAgent 0.3.17 requires `abstractcore[tools]>=2.13.41` and
88
+ `AbstractRuntime>=0.7.0` (the runtime release with the 50k-token history
89
+ window, which ReAct applies to its requests when a host sets
90
+ `_runtime.history_window_tokens`, as the entity visit does; 0.5.0 brought live
91
+ token streaming, on top of turn grounding, in-flight cancellation and
92
+ speculation inheritance). AbstractRuntime 0.7.0 itself requires AbstractCore
93
+ 2.18.0 or newer.
91
94
 
92
95
  Note: the repository may be ahead of the latest published PyPI release. To verify what you installed:
93
96
 
@@ -4,23 +4,23 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "abstractagent"
7
- version = "0.3.16"
7
+ version = "0.3.17"
8
8
  description = "Agent implementations using AbstractRuntime and AbstractCore"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  "abstractcore[tools]>=2.13.41",
13
- "abstractruntime>=0.5.0",
13
+ "abstractruntime>=0.7.0",
14
14
  ]
15
15
 
16
16
  [project.optional-dependencies]
17
17
  apple = [
18
18
  "abstractcore[all-apple]>=2.13.41",
19
- "abstractruntime[apple]>=0.5.0",
19
+ "abstractruntime[apple]>=0.7.0",
20
20
  ]
21
21
  gpu = [
22
22
  "abstractcore[all-gpu]>=2.13.41",
23
- "abstractruntime[gpu]>=0.5.0",
23
+ "abstractruntime[gpu]>=0.7.0",
24
24
  ]
25
25
  dev = [
26
26
  "pytest>=7.0",
@@ -24,6 +24,7 @@ from typing import Any, Callable, Dict, List, Optional
24
24
  from abstractcore.tools import ToolCall
25
25
  from abstractruntime import Effect, EffectType, RunState, StepPlan, WorkflowSpec
26
26
  from abstractruntime.core.vars import ensure_limits, ensure_namespaces
27
+ from abstractruntime.session_history import window_transcript
27
28
  from abstractruntime.turn_grounding import stamp_user_turn_grounding, strip_turn_grounding
28
29
 
29
30
  from .generation_params import (
@@ -1659,6 +1660,31 @@ def create_react_workflow(
1659
1660
  out.append(d)
1660
1661
  return out
1661
1662
 
1663
+ def _history_window_view(messages: List[Dict[str, Any]], runtime_ns: Any) -> List[Dict[str, Any]]:
1664
+ """The transcript a model request carries when the host asks for THE
1665
+ runtime history window (`_runtime.history_window_tokens`, set by the
1666
+ entity visit's BRIDGE; operator ruling 2026-09-28, ADR-0026).
1667
+
1668
+ `abstractruntime.session_history.window_transcript`: the newest whole
1669
+ turns up to that many tokens (a tool result stays with its turn), a
1670
+ labeled #TRUNCATION notice when older turns were dropped. The turn in
1671
+ progress starts at `_runtime.history_window_turn_start` (set by the
1672
+ host) and is kept whole, so an ask_user answer or operator guidance
1673
+ inside it never splits off the turn's own question and tool results. The durable
1674
+ `context.messages` is never touched — only this request's view — and
1675
+ the window's receipt is recorded at `_runtime.session_history`.
1676
+ Unset = the whole transcript (the plain task lane's full-context policy).
1677
+ """
1678
+ if not isinstance(runtime_ns, dict) or runtime_ns.get("history_window_tokens") is None:
1679
+ return messages
1680
+ window = window_transcript(
1681
+ messages,
1682
+ max_tokens=runtime_ns["history_window_tokens"],
1683
+ current_turn_start=runtime_ns.get("history_window_turn_start"),
1684
+ )
1685
+ runtime_ns["session_history"] = dict(window.report)
1686
+ return list(window)
1687
+
1662
1688
  def _sanitize_llm_messages(
1663
1689
  messages: Any, *, limits: Optional[Dict[str, Any]] = None
1664
1690
  ) -> List[Dict[str, Any]]:
@@ -1874,7 +1900,7 @@ def create_react_workflow(
1874
1900
 
1875
1901
 
1876
1902
  payload: Dict[str, Any] = {"prompt": ""}
1877
- sanitized_messages = _sanitize_llm_messages(messages_view, limits=limits)
1903
+ sanitized_messages = _sanitize_llm_messages(_history_window_view(messages_view, runtime_ns), limits=limits)
1878
1904
  if sanitized_messages:
1879
1905
  payload["messages"] = sanitized_messages
1880
1906
  else:
@@ -3765,7 +3791,7 @@ def create_react_workflow(
3765
3791
  )
3766
3792
 
3767
3793
  payload: Dict[str, Any] = {"prompt": ""}
3768
- sanitized_messages = _sanitize_llm_messages(messages_view, limits=limits)
3794
+ sanitized_messages = _sanitize_llm_messages(_history_window_view(messages_view, runtime_ns), limits=limits)
3769
3795
  if sanitized_messages:
3770
3796
  payload["messages"] = sanitized_messages
3771
3797
  else:
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractagent
3
- Version: 0.3.16
3
+ Version: 0.3.17
4
4
  Summary: Agent implementations using AbstractRuntime and AbstractCore
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  Requires-Dist: abstractcore[tools]>=2.13.41
9
- Requires-Dist: abstractruntime>=0.5.0
9
+ Requires-Dist: abstractruntime>=0.7.0
10
10
  Provides-Extra: apple
11
11
  Requires-Dist: abstractcore[all-apple]>=2.13.41; extra == "apple"
12
- Requires-Dist: abstractruntime[apple]>=0.5.0; extra == "apple"
12
+ Requires-Dist: abstractruntime[apple]>=0.7.0; extra == "apple"
13
13
  Provides-Extra: gpu
14
14
  Requires-Dist: abstractcore[all-gpu]>=2.13.41; extra == "gpu"
15
- Requires-Dist: abstractruntime[gpu]>=0.5.0; extra == "gpu"
15
+ Requires-Dist: abstractruntime[gpu]>=0.7.0; extra == "gpu"
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: pytest>=7.0; extra == "dev"
18
18
  Provides-Extra: test
@@ -105,10 +105,13 @@ Native Python hardware profile cascades are available for deployment manifests:
105
105
  AbstractCore and AbstractRuntime profiles; AbstractAgent itself remains
106
106
  provider/runtime agnostic.
107
107
 
108
- AbstractAgent 0.3.16 requires `abstractcore[tools]>=2.13.41` and
109
- `AbstractRuntime>=0.5.0` (the runtime release that provides live token
110
- streaming, on top of turn grounding, in-flight cancellation and speculation
111
- inheritance). AbstractRuntime 0.5.0 itself requires AbstractCore 2.16.0 or newer.
108
+ AbstractAgent 0.3.17 requires `abstractcore[tools]>=2.13.41` and
109
+ `AbstractRuntime>=0.7.0` (the runtime release with the 50k-token history
110
+ window, which ReAct applies to its requests when a host sets
111
+ `_runtime.history_window_tokens`, as the entity visit does; 0.5.0 brought live
112
+ token streaming, on top of turn grounding, in-flight cancellation and
113
+ speculation inheritance). AbstractRuntime 0.7.0 itself requires AbstractCore
114
+ 2.18.0 or newer.
112
115
 
113
116
  Note: the repository may be ahead of the latest published PyPI release. To verify what you installed:
114
117
 
@@ -90,6 +90,7 @@ tests/test_react_delegate_agent_tool.py
90
90
  tests/test_react_election_fence_conformance.py
91
91
  tests/test_react_entity_dress_conformance.py
92
92
  tests/test_react_guidance_durable_transcript.py
93
+ tests/test_react_history_window.py
93
94
  tests/test_react_logic.py
94
95
  tests/test_react_loop_aborted_generation_retry.py
95
96
  tests/test_react_loop_context_transcript_levels.py
@@ -1,16 +1,16 @@
1
1
  abstractcore[tools]>=2.13.41
2
- abstractruntime>=0.5.0
2
+ abstractruntime>=0.7.0
3
3
 
4
4
  [apple]
5
5
  abstractcore[all-apple]>=2.13.41
6
- abstractruntime[apple]>=0.5.0
6
+ abstractruntime[apple]>=0.7.0
7
7
 
8
8
  [dev]
9
9
  pytest>=7.0
10
10
 
11
11
  [gpu]
12
12
  abstractcore[all-gpu]>=2.13.41
13
- abstractruntime[gpu]>=0.5.0
13
+ abstractruntime[gpu]>=0.7.0
14
14
 
15
15
  [test]
16
16
  pytest>=7.0
@@ -241,18 +241,23 @@ def _poison_tools(run: Any, effect: Effect, dnn: Any = None) -> EffectOutcome:
241
241
 
242
242
  def test_poisoned_visit_transcript_recovers_on_the_next_turn(tmp_path: Path) -> None:
243
243
  """End-to-end on the REAL merged lane (the incident's lane): turn 1
244
- poisons the durable transcript with a ~495k tool message; turn 2's wire
245
- payload carries a labeled 32k stub instead — the session recovers with
246
- NO manual surgery while the durable record keeps the full truth."""
244
+ poisons the durable transcript with a ~495k tool message. Within turn 1
245
+ the result rides the next call clamped to OVERSIZED_MESSAGE_CLAMP_CHARS
246
+ (the newest turn is kept whole by the window). From turn 2 on, the visit
247
+ BRIDGE's history window (`_runtime.history_window_tokens`, runtime 0.7,
248
+ ADR-0026) drops the poisoned turn whole — call, result and reply — because
249
+ it no longer fits beside a newer turn: the request is the labeled notice
250
+ and the new words. The session recovers with NO manual surgery while the
251
+ durable record keeps the full truth."""
247
252
  pytest.importorskip("abstractmemory")
248
253
  from abstractruntime.identity.entity_runtime import open_entity_runtime
249
254
  from abstractruntime.identity.visit_workflow import (
250
255
  HARVEST_NODE,
251
- VISIT_HISTORY_TOOL_RESULT_CAP_CHARS,
252
256
  VISITOR_WAIT_KEY,
253
257
  ReactMiddle,
254
258
  build_visit_workflow,
255
259
  )
260
+ from abstractruntime.turn_grounding import strip_turn_grounding
256
261
 
257
262
  home_dir = _make_home(tmp_path)
258
263
  llm = _ScriptedLLM([
@@ -312,20 +317,28 @@ def test_poisoned_visit_transcript_recovers_on_the_next_turn(tmp_path: Path) ->
312
317
  assert state.status == RunStatus.WAITING
313
318
  assert len(llm.calls) == 3
314
319
 
315
- wire = llm.calls[2]["messages"]
316
- wire_tool = [m for m in wire if m.get("role") == "tool"]
317
- assert len(wire_tool) == 1
318
- clamped = wire_tool[0]["content"]
319
- assert len(clamped) <= VISIT_HISTORY_TOOL_RESULT_CAP_CHARS + 200
320
+ # Within turn 1, the call after the read carried the clamped stub.
321
+ in_turn_tool = [m for m in llm.calls[1]["messages"] if m.get("role") == "tool"]
322
+ assert len(in_turn_tool) == 1
323
+ clamped = in_turn_tool[0]["content"]
324
+ assert len(clamped) <= OVERSIZED_MESSAGE_CLAMP_CHARS + 200
320
325
  assert clamped.startswith("[read_file]: --- shared/Screenshot_2026-08-01"), "head kept - labeled, never dropped"
321
326
  assert "chars elided: oversized tool result" in clamped
322
- # The visitor's words and the entity's own prose are untouched.
323
- user_msgs = [m for m in wire if m.get("role") == "user"]
324
- assert any("Are you still with me?" in str(m.get("content")) for m in user_msgs)
325
- assert any(m.get("role") == "assistant" and "I looked at the file." == m.get("content") for m in wire)
326
- # The whole request shrank from monster-class to sane.
327
+
328
+ # Turn 2: the poisoned turn is out of the request, whole, and says so.
329
+ wire = llm.calls[2]["messages"]
330
+ assert [m for m in wire if m.get("role") == "tool"] == []
331
+ assert not any(m.get("tool_calls") for m in wire)
332
+ assert len(wire) == 1 and wire[0]["role"] == "user"
333
+ # One head envelope (the turn's own), then the labeled drop notice.
334
+ assert wire[0]["content"].count("<runtime_metadata>") == 1
335
+ assert strip_turn_grounding(wire[0]["content"]).startswith("[#TRUNCATION: 4 earlier message(s)")
336
+ assert "Are you still with me?" in wire[0]["content"]
337
+ report = ert.runtime.get_state(run_id).vars["_runtime"]["session_history"]
338
+ assert report["dropped_messages"] == 4 and report["replayed_messages"] == 1
339
+ # The whole request shrank from monster-class to one short message.
327
340
  total = sum(len(str(m.get("content") or "")) for m in wire)
328
- assert total < 100_000, f"recovered payload still huge: {total}"
341
+ assert total < 2_000, f"recovered payload still huge: {total}"
329
342
 
330
343
  # And the durable record STILL holds the full truth (ADR-0026).
331
344
  stored = ert.runtime.get_state(run_id).vars["context"]["messages"]
@@ -19,7 +19,7 @@ def test_base_dependency_floors_match_gateway_alignment() -> None:
19
19
  deps = set(_project()["dependencies"])
20
20
 
21
21
  assert "abstractcore[tools]>=2.13.41" in deps
22
- assert "abstractruntime>=0.5.0" in deps
22
+ assert "abstractruntime>=0.7.0" in deps
23
23
 
24
24
 
25
25
  def test_hardware_profile_extras_are_core_runtime_cascades() -> None:
@@ -27,11 +27,11 @@ def test_hardware_profile_extras_are_core_runtime_cascades() -> None:
27
27
 
28
28
  assert set(extras["apple"]) == {
29
29
  "abstractcore[all-apple]>=2.13.41",
30
- "abstractruntime[apple]>=0.5.0",
30
+ "abstractruntime[apple]>=0.7.0",
31
31
  }
32
32
  assert set(extras["gpu"]) == {
33
33
  "abstractcore[all-gpu]>=2.13.41",
34
- "abstractruntime[gpu]>=0.5.0",
34
+ "abstractruntime[gpu]>=0.7.0",
35
35
  }
36
36
  assert "all-apple" not in extras
37
37
  assert "all-gpu" not in extras
@@ -0,0 +1,64 @@
1
+ """ReAct sends THE runtime history window when the host asks for it
2
+ (`_runtime.history_window_tokens`; runtime 0.7.0's entity visit sets 50k).
3
+ Unset, the whole transcript rides as before. The durable transcript is never
4
+ windowed."""
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from abstractagent.adapters.react_runtime import create_react_workflow
11
+ from abstractagent.logic.react import ReActLogic
12
+ from abstractcore.tools import ToolDefinition
13
+ from abstractruntime.core.models import Effect, EffectType, RunState, RunStatus
14
+ from abstractruntime.core.runtime import EffectOutcome, Runtime
15
+ from abstractruntime.storage.in_memory import InMemoryLedgerStore, InMemoryRunStore
16
+ from abstractruntime.turn_grounding import strip_turn_grounding
17
+
18
+ OLD = [m for i in range(6) for m in ({"role": "user", "content": f"old {i} " + "w " * 2000},
19
+ {"role": "assistant", "content": f"reply {i}"})]
20
+
21
+
22
+ def _run(runtime_vars: Dict[str, Any]) -> tuple:
23
+ captured: List[Dict[str, Any]] = []
24
+
25
+ def llm(run: RunState, effect: Effect, dnn: Optional[str]) -> EffectOutcome:
26
+ captured.append(json.loads(json.dumps(effect.payload)))
27
+ return EffectOutcome.completed({"content": "done", "tool_calls": []})
28
+
29
+ rt = Runtime(run_store=InMemoryRunStore(), ledger_store=InMemoryLedgerStore(),
30
+ effect_handlers={EffectType.LLM_CALL: llm})
31
+ wf = create_react_workflow(logic=ReActLogic(tools=[ToolDefinition(name="t", description="t", parameters={})]),
32
+ workflow_id="react_window", provider="stub", model="stub", allowed_tools=["t"])
33
+ messages = [dict(m) for m in OLD] + [{"role": "user", "content": "now"}]
34
+ run_id = rt.start(workflow=wf, vars={"context": {"task": "now", "messages": messages}, "_runtime": dict(runtime_vars)})
35
+ state = rt.tick(workflow=wf, run_id=run_id, max_steps=40)
36
+ assert state.status == RunStatus.COMPLETED
37
+ return captured[0]["messages"], state
38
+
39
+
40
+ def test_the_window_key_windows_the_request_and_records_it() -> None:
41
+ wire, state = _run({"history_window_tokens": 3000})
42
+ report = state.vars["_runtime"]["session_history"]
43
+ assert report["max_tokens"] == 3000 and report["dropped_messages"] > 0
44
+ assert len(wire) == report["replayed_messages"] < len(OLD) + 1
45
+ assert strip_turn_grounding(wire[0]["content"]).startswith("[#TRUNCATION: ")
46
+ assert strip_turn_grounding(wire[-1]["content"]) == "now"
47
+ stored = state.vars["context"]["messages"]
48
+ assert len(stored) >= len(OLD) + 1 and stored[0]["content"].startswith("old 0 ") # durable transcript whole
49
+
50
+
51
+ def test_without_the_key_the_whole_transcript_rides() -> None:
52
+ wire, state = _run({})
53
+ assert "session_history" not in state.vars["_runtime"]
54
+ assert len(wire) == len(OLD) + 1 and wire[0]["content"].startswith("old 0 ")
55
+
56
+
57
+ def test_the_turn_in_progress_is_kept_whole_from_the_hosts_turn_start() -> None:
58
+ """`_runtime.history_window_turn_start` marks where the turn in progress
59
+ began: from there on it is one turn, kept whole (and reported oversize),
60
+ however many user-role messages the loop added inside it."""
61
+ wire, state = _run({"history_window_tokens": 3000, "history_window_turn_start": 0})
62
+ report = state.vars["_runtime"]["session_history"]
63
+ assert report["dropped_messages"] == 0 and report["oversize_turn_kept"] is True
64
+ assert len(wire) == len(OLD) + 1 and wire[0]["content"].startswith("old 0 ")
File without changes
File without changes