AbstractAgent 0.3.15__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 (129) hide show
  1. {abstractagent-0.3.15/src/abstractagent.egg-info → abstractagent-0.3.17}/PKG-INFO +11 -10
  2. {abstractagent-0.3.15 → abstractagent-0.3.17}/README.md +7 -4
  3. {abstractagent-0.3.15 → abstractagent-0.3.17}/pyproject.toml +4 -7
  4. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/codeact_runtime.py +4 -137
  5. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/emit_inventory.py +0 -25
  6. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/memact_runtime.py +3 -138
  7. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/react_runtime.py +58 -302
  8. {abstractagent-0.3.15 → abstractagent-0.3.17/src/abstractagent.egg-info}/PKG-INFO +11 -10
  9. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent.egg-info/SOURCES.txt +1 -2
  10. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent.egg-info/requires.txt +3 -5
  11. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_c2447_visit_lane_honesty.py +2 -2
  12. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_history_monster_clamp.py +28 -15
  13. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_packaging_profiles.py +3 -3
  14. abstractagent-0.3.17/tests/test_react_history_window.py +64 -0
  15. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_loop_followthrough_retry.py +1 -3
  16. abstractagent-0.3.15/src/abstractagent/adapters/announced_calls.py +0 -463
  17. abstractagent-0.3.15/tests/test_announced_tool_reprompt.py +0 -751
  18. {abstractagent-0.3.15 → abstractagent-0.3.17}/LICENSE +0 -0
  19. {abstractagent-0.3.15 → abstractagent-0.3.17}/setup.cfg +0 -0
  20. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/__init__.py +0 -0
  21. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/__init__.py +0 -0
  22. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/generation_params.py +0 -0
  23. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/loop_hooks.py +0 -0
  24. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/media.py +0 -0
  25. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/native_loop_registry.py +0 -0
  26. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/progress.py +0 -0
  27. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/read_orchestration.py +0 -0
  28. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/tool_allowlist.py +0 -0
  29. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/tool_failure_hints.py +0 -0
  30. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/adapters/transcripts.py +0 -0
  31. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/__init__.py +0 -0
  32. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/base.py +0 -0
  33. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/codeact.py +0 -0
  34. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/defaults.py +0 -0
  35. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/memact.py +0 -0
  36. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/react.py +0 -0
  37. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/task_reset.py +0 -0
  38. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/agents/unattended.py +0 -0
  39. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/logic/__init__.py +0 -0
  40. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/logic/builtins.py +0 -0
  41. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/logic/codeact.py +0 -0
  42. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/logic/memact.py +0 -0
  43. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/logic/react.py +0 -0
  44. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/logic/types.py +0 -0
  45. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/metrics/__init__.py +0 -0
  46. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/metrics/prefix_reuse.py +0 -0
  47. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/repl.py +0 -0
  48. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/sandbox/__init__.py +0 -0
  49. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/sandbox/interface.py +0 -0
  50. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/sandbox/local.py +0 -0
  51. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/scripts/__init__.py +0 -0
  52. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/scripts/lmstudio_tool_eval.py +0 -0
  53. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/tools/__init__.py +0 -0
  54. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/tools/code_execution.py +0 -0
  55. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent/tools/self_improve.py +0 -0
  56. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent.egg-info/dependency_links.txt +0 -0
  57. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent.egg-info/entry_points.txt +0 -0
  58. {abstractagent-0.3.15 → abstractagent-0.3.17}/src/abstractagent.egg-info/top_level.txt +0 -0
  59. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_active_context_policy_adoption.py +0 -0
  60. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_adversary_hardening_2026_07_13.py +0 -0
  61. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_agent_plan_review_modes.py +0 -0
  62. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_agent_speculation_inheritance.py +0 -0
  63. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_agent_stream_inheritance.py +0 -0
  64. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_agents_integration_llm.py +0 -0
  65. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_base_agent_trace_accessors.py +0 -0
  66. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_batching_prompt_rule.py +0 -0
  67. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_budget_triage_tail.py +0 -0
  68. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_builtin_tool_specs_optional_defaults.py +0 -0
  69. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_claim_0029_residue_2026_07_14.py +0 -0
  70. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_codeact_logic.py +0 -0
  71. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_codeact_runtime_verifier_policy.py +0 -0
  72. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_delegate_session_route_composition.py +0 -0
  73. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_emit_inventory_drift.py +0 -0
  74. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_gated_remainder_wave_2026_07_14.py +0 -0
  75. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_generation_params_media_policies.py +0 -0
  76. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_improvement_wave_2026_07_13.py +0 -0
  77. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_llm_call_payload_requires_prompt_or_messages.py +0 -0
  78. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_lmstudio_tool_eval_metrics.py +0 -0
  79. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_loop_hooks.py +0 -0
  80. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_loop_hooks_0026_follow_ups.py +0 -0
  81. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_memact_codeact_guidance_durable_transcript.py +0 -0
  82. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_memact_session_memory.py +0 -0
  83. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_memact_workflow.py +0 -0
  84. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_memory_recall_tool.py +0 -0
  85. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_native_loop_registry.py +0 -0
  86. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_open_attachment_tool_available.py +0 -0
  87. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_operator_conclude.py +0 -0
  88. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_optimal_wave_batch2_2026_07_13.py +0 -0
  89. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_parse_prompt_cache_capture.py +0 -0
  90. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_prefix_reuse_metric.py +0 -0
  91. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_progress_detection.py +0 -0
  92. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_act_only_observation.py +0 -0
  93. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_active_attachments_disable_tools_first_iteration.py +0 -0
  94. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_adapter_forwards_context_attachments_media.py +0 -0
  95. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_delegate_agent_tool.py +0 -0
  96. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_election_fence_conformance.py +0 -0
  97. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_entity_dress_conformance.py +0 -0
  98. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_guidance_durable_transcript.py +0 -0
  99. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_logic.py +0 -0
  100. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_loop_aborted_generation_retry.py +0 -0
  101. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_loop_context_transcript_levels.py +0 -0
  102. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_loop_side_effect_dedupe_comms.py +0 -0
  103. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_loop_truncated_output_retry.py +0 -0
  104. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_max_iterations_concludes.py +0 -0
  105. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_phase_grant_boundary.py +0 -0
  106. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_planish_heuristic.py +0 -0
  107. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_prompt_prefix_stability.py +0 -0
  108. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_sight_lane_media_fold.py +0 -0
  109. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_turn_byte_stability.py +0 -0
  110. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_verifier_and_plan.py +0 -0
  111. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_visit_composition.py +0 -0
  112. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_visit_merge.py +0 -0
  113. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_workflow_system_prompt_and_tools_override.py +0 -0
  114. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_react_workflow_toolset_refresh.py +0 -0
  115. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_read_orchestration_guidance.py +0 -0
  116. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_reasoning_first_citizen_agent.py +0 -0
  117. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_review_failure_containment.py +0 -0
  118. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_run_start_seeds_runtime_provider_model.py +0 -0
  119. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_session_memory.py +0 -0
  120. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_sibling_loop_defects_batch.py +0 -0
  121. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_sibling_strict_transcripts_0011.py +0 -0
  122. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_stuck_streak_termination.py +0 -0
  123. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_tool_failure_hints.py +0 -0
  124. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_tool_loop_prefix_extension_a3.py +0 -0
  125. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_tool_observe_emits_result_for_tui.py +0 -0
  126. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_tools_editing.py +0 -0
  127. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_unseeded_render_limits.py +0 -0
  128. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_verifier_execution_preference.py +0 -0
  129. {abstractagent-0.3.15 → abstractagent-0.3.17}/tests/test_verifier_schema_strict.py +0 -0
@@ -1,24 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractagent
3
- Version: 0.3.15
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
- Requires-Dist: jinja2>=3.1; extra == "dev"
19
18
  Provides-Extra: test
20
19
  Requires-Dist: pytest>=7.0; extra == "test"
21
- Requires-Dist: jinja2>=3.1; extra == "test"
22
20
  Dynamic: license-file
23
21
 
24
22
  # AbstractAgent
@@ -107,10 +105,13 @@ Native Python hardware profile cascades are available for deployment manifests:
107
105
  AbstractCore and AbstractRuntime profiles; AbstractAgent itself remains
108
106
  provider/runtime agnostic.
109
107
 
110
- AbstractAgent 0.3.15 requires `abstractcore[tools]>=2.13.41` and
111
- `AbstractRuntime>=0.5.0` (the runtime release that provides live token
112
- streaming, on top of turn grounding, in-flight cancellation and speculation
113
- 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.
114
115
 
115
116
  Note: the repository may be ahead of the latest published PyPI release. To verify what you installed:
116
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.15 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,32 +4,29 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "abstractagent"
7
- version = "0.3.15"
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",
27
- "jinja2>=3.1",
28
27
  ]
29
28
  test = [
30
29
  "pytest>=7.0",
31
- # Renders the vendored Qwen3.6 chat template (tests/test_announced_tool_reprompt.py).
32
- "jinja2>=3.1",
33
30
  ]
34
31
 
35
32
  [tool.setuptools.packages.find]
@@ -31,17 +31,6 @@ from .generation_params import (
31
31
  verifier_response_schema,
32
32
  )
33
33
  from .media import extract_media_from_context
34
- from .announced_calls import (
35
- classify_no_call_reply,
36
- is_not_an_answer,
37
- no_call_error_text,
38
- no_call_stop_reason,
39
- reprompt_text,
40
- resolve_checks,
41
- tool_calls_from_reasoning,
42
- verbatim_reply,
43
- visible_content,
44
- )
45
34
  from .transcripts import (
46
35
  assistant_tool_calls_payload,
47
36
  ensure_tool_call_ids,
@@ -690,14 +679,6 @@ def create_codeact_workflow(
690
679
  if req.max_tokens is not None:
691
680
  params["max_tokens"] = req.max_tokens
692
681
  payload["params"] = runtime_llm_params(runtime_ns, extra=params)
693
- # Ledger record of a re-prompt (mission AGX; twin of react_runtime).
694
- _reprompt = run.vars.get("_temp", {}).get("reprompt") if isinstance(run.vars.get("_temp"), dict) else None
695
- if isinstance(_reprompt, dict) and _reprompt.get("reason"):
696
- payload["_runtime_observability"] = {
697
- "reprompted": str(_reprompt.get("reason")),
698
- "reprompt_detail": str(_reprompt.get("detail") or ""),
699
- "reprompted_cycle": _reprompt.get("cycle"),
700
- }
701
682
 
702
683
  return StepPlan(
703
684
  node_id="reason",
@@ -715,10 +696,6 @@ def create_codeact_workflow(
715
696
  content, tool_calls = logic.parse_response(response)
716
697
 
717
698
  temp.pop("llm_response", None)
718
- # One re-prompt per step (mission AGX): popped once, here.
719
- prior_reprompt = temp.pop("reprompt", None)
720
- if not isinstance(prior_reprompt, dict):
721
- prior_reprompt = None
722
699
  # Fence decision computed BEFORE the emit (wave-F P3: on the fenced
723
700
  # path the parse payload said has_tool_calls=False with no code
724
701
  # signal, then the loop executed code — an action a common-core
@@ -814,13 +791,7 @@ def create_codeact_workflow(
814
791
  # the canonical commit signal, same raw step + payload as ReAct.
815
792
  # The fenced-code path deliberately stays out — no tool batch is
816
793
  # proposed there; `parse`'s additive `has_code` carries it.
817
- _ptc: Dict[str, Any] = {"count": len(tool_calls)}
818
- _from_reasoning = tool_calls_from_reasoning(response)
819
- if _from_reasoning:
820
- _ptc["from_reasoning"] = _from_reasoning
821
- if prior_reprompt is not None:
822
- _ptc["after_reprompt"] = str(prior_reprompt.get("reason") or "")
823
- emit("parse_tool_calls", _ptc)
794
+ emit("parse_tool_calls", {"count": len(tool_calls)})
824
795
  return StepPlan(node_id="parse", next_node="act")
825
796
 
826
797
  # Empty response is an invalid step: recover with a bounded retry that carries evidence.
@@ -906,89 +877,6 @@ def create_codeact_workflow(
906
877
  temp["pending_code"] = fenced_code
907
878
  return StepPlan(node_id="parse", next_node="execute_code")
908
879
 
909
- # A reply with NO tool call that is not an answer (mission AGX; twin
910
- # of react_runtime.parse_node, see its comment and announced_calls):
911
- # an announcement ("Let me verify ...") or tool-call markup that could
912
- # not run. Re-prompt ONCE with the reply verbatim; a second failure
913
- # ends the step with a visible error instead of publishing either.
914
- _check_announced, _check_unrunnable = resolve_checks(runtime_ns, suppressed=suppress_loop_tail(runtime_ns))
915
- _specs_now = runtime_ns.get("tool_specs") if isinstance(runtime_ns, dict) else None
916
- _tools_offered = isinstance(_specs_now, list) and bool(_specs_now)
917
- no_call_reason, no_call_detail = classify_no_call_reply(
918
- response,
919
- content,
920
- tools_offered=_tools_offered,
921
- check_announcement=_check_announced,
922
- check_unrunnable=_check_unrunnable,
923
- )
924
- if no_call_reason:
925
- _cycle_i = int(scratchpad.get("iteration", 0) or 0)
926
- _limits_ns = run.vars.get("_limits") if isinstance(run.vars.get("_limits"), dict) else {}
927
- _max_it = max(1, resolve_max_iterations(_limits_ns, scratchpad))
928
- raw_reply = verbatim_reply(response, fallback=str(content or ""))
929
- temp["pending_tool_calls"] = []
930
- last_iteration = _cycle_i >= _max_it
931
- if prior_reprompt is not None or last_iteration:
932
- # Second failure, or no iteration left to re-prompt in: end the
933
- # step with a visible error and go to the budget terminal (this
934
- # loop's conclusion), which answers with the error and a
935
- # `stop_reason.code = "no_tool_call"` — never with this reply,
936
- # nor with an earlier turn's pre-call narration.
937
- reprompted = prior_reprompt is not None
938
- error_text = no_call_error_text(no_call_reason, no_call_detail, reprompted=reprompted)
939
- context["messages"].append(
940
- _new_message(
941
- ctx,
942
- role="assistant",
943
- content=visible_content(response),
944
- metadata={"kind": "reprompt_failed_reply", "cycle": _cycle_i, "reason": no_call_reason},
945
- )
946
- )
947
- scratchpad["no_tool_call_stop"] = {
948
- "reason": no_call_reason,
949
- "detail": no_call_detail,
950
- "cycle": _cycle_i,
951
- "error": error_text,
952
- "reprompted": reprompted,
953
- }
954
- if reprompted:
955
- emit(
956
- "parse_reprompt_failed",
957
- {"cycle": _cycle_i, "reason": no_call_reason, "detail": no_call_detail, "error": error_text},
958
- )
959
- else:
960
- emit(
961
- "parse_reprompt_skipped",
962
- {"cycle": _cycle_i, "reason": no_call_reason, "detail": no_call_detail, "why": "iteration_budget"},
963
- )
964
- return StepPlan(node_id="parse", next_node="max_iterations")
965
- # Visible content stays the assistant turn; the verbatim reply is
966
- # QUOTED in the corrective (templates strip `<think>` from history).
967
- context["messages"].append(
968
- _new_message(
969
- ctx,
970
- role="assistant",
971
- content=visible_content(response),
972
- metadata={"kind": "reprompted_reply", "cycle": _cycle_i, "reason": no_call_reason},
973
- )
974
- )
975
- context["messages"].append(
976
- _new_message(
977
- ctx,
978
- role="user",
979
- content=reprompt_text(
980
- no_call_reason,
981
- tools_offered=_tools_offered,
982
- suppressed=suppress_loop_tail(runtime_ns),
983
- reply=raw_reply,
984
- ),
985
- metadata={"kind": "reprompt", "cycle": _cycle_i, "reason": no_call_reason},
986
- )
987
- )
988
- temp["reprompt"] = {"reason": no_call_reason, "detail": no_call_detail, "cycle": _cycle_i}
989
- emit("parse_reprompt", {"cycle": _cycle_i, "reason": no_call_reason, "detail": no_call_detail})
990
- return StepPlan(node_id="parse", next_node="reason")
991
-
992
880
  # Default: treat as a final answer even without an explicit FINAL marker.
993
881
  if raw:
994
882
  context["messages"].append(_new_message(ctx, role="assistant", content=raw))
@@ -1910,9 +1798,6 @@ def create_codeact_workflow(
1910
1798
  }
1911
1799
  if has_skips:
1912
1800
  complete_output["review_skipped_details"] = list(skipped_reviews)
1913
- _no_call = scratchpad.get("no_tool_call_stop")
1914
- if isinstance(_no_call, dict):
1915
- complete_output["no_tool_call_stop"] = dict(_no_call)
1916
1801
  return StepPlan(node_id="done", complete_output=complete_output)
1917
1802
 
1918
1803
  def max_iterations_node(run: RunState, ctx) -> StepPlan:
@@ -1931,19 +1816,9 @@ def create_codeact_workflow(
1931
1816
  # Full conclusion-call parity with ReAct stays gated on 0021; this is
1932
1817
  # the honesty half. `.get`, not [] (fable5 2026-07-13): content-less
1933
1818
  # host-seeded messages raised KeyError at the terminal.
1934
- _no_call = scratchpad.get("no_tool_call_stop") if isinstance(scratchpad.get("no_tool_call_stop"), dict) else None
1935
- # An unrecovered no-call reply (mission AGX) ends with its visible error.
1936
- answer = str(_no_call.get("error") or "") if _no_call else ""
1937
- for msg in ([] if answer else reversed(messages)):
1938
- # A re-prompted (failed) reply is never "last words" (mission AGX).
1939
- # A turn that carried tool calls is pre-call narration, not an
1940
- # answer (review 28 D3: "Searching." was published).
1941
- if (
1942
- isinstance(msg, dict)
1943
- and msg.get("role") == "assistant"
1944
- and not is_not_an_answer(msg)
1945
- and not msg.get("tool_calls")
1946
- ):
1819
+ answer = ""
1820
+ for msg in reversed(messages):
1821
+ if isinstance(msg, dict) and msg.get("role") == "assistant":
1947
1822
  text = str(msg.get("content") or "")
1948
1823
  if text.strip():
1949
1824
  answer = text
@@ -1968,14 +1843,6 @@ def create_codeact_workflow(
1968
1843
  # Always-present at BOTH terminals (wave-F P3: the batch-3
1969
1844
  # "always-present bool" claim was false at this one).
1970
1845
  "review_skipped": bool(scratchpad.get("review_skipped")),
1971
- **(
1972
- {
1973
- "no_tool_call_stop": dict(_no_call),
1974
- "stop_reason": no_call_stop_reason(_no_call, iterations=max_iterations),
1975
- }
1976
- if _no_call
1977
- else {}
1978
- ),
1979
1846
  },
1980
1847
  )
1981
1848
 
@@ -85,21 +85,6 @@ REACT_STEPS = COMMON_STEPS | frozenset(
85
85
  "parse_retry_empty",
86
86
  "parse_retry_plan_only",
87
87
  "parse_retry_truncated",
88
- # Mission AGX (2026-09-26): a reply with no tool call that announced
89
- # tool use, or carried tool calls that could not run. `parse_reprompt`
90
- # = the ONE re-prompt with the verbatim reply, payload {cycle, reason:
91
- # announced_tool_use|unrunnable_tool_call, detail};
92
- # `parse_reprompt_failed` = the re-prompted reply failed the same way,
93
- # the step ended with a visible error and the loop went to its
94
- # conclusion, payload {cycle, reason, detail, error};
95
- # `parse_reprompt_skipped` = no iteration left to re-prompt in, payload
96
- # {cycle, reason, detail, why}; `conclusion_announcement_dropped` = the
97
- # conclusion reply was itself an announcement and was not published,
98
- # payload {preview}.
99
- "parse_reprompt",
100
- "parse_reprompt_failed",
101
- "parse_reprompt_skipped",
102
- "conclusion_announcement_dropped",
103
88
  # 0017 work half (2026-07-21), nudge-then-stop (2026-08-21): fires on
104
89
  # every detection of a consecutive-identical or A-B-A-B tool-batch
105
90
  # streak; payload {kind: repeat|oscillation, span, key, answered,
@@ -140,11 +125,6 @@ REACT_STEPS = COMMON_STEPS | frozenset(
140
125
 
141
126
  CODEACT_STEPS = COMMON_STEPS | frozenset(
142
127
  {
143
- # Mission AGX: one re-prompt with the verbatim reply, and its failure
144
- # (see REACT_STEPS for the payloads).
145
- "parse_reprompt",
146
- "parse_reprompt_failed",
147
- "parse_reprompt_skipped",
148
128
  "parse_retry_empty",
149
129
  # Read-orchestration advice — payload {path, mode, enforcement}.
150
130
  # `enforcement: "advise"` means the model was told how to read this
@@ -165,11 +145,6 @@ CODEACT_STEPS = COMMON_STEPS | frozenset(
165
145
 
166
146
  MEMACT_STEPS = COMMON_STEPS | frozenset(
167
147
  {
168
- # Mission AGX: one re-prompt with the verbatim reply, and its failure
169
- # (see REACT_STEPS for the payloads).
170
- "parse_reprompt",
171
- "parse_reprompt_failed",
172
- "parse_reprompt_skipped",
173
148
  "compose",
174
149
  "compose_query",
175
150
  "finalize",
@@ -26,17 +26,6 @@ from .generation_params import (
26
26
  suppress_loop_tail,
27
27
  )
28
28
  from .media import extract_media_from_context
29
- from .announced_calls import (
30
- classify_no_call_reply,
31
- is_not_an_answer,
32
- no_call_error_text,
33
- no_call_stop_reason,
34
- reprompt_text,
35
- resolve_checks,
36
- tool_calls_from_reasoning,
37
- verbatim_reply,
38
- visible_content,
39
- )
40
29
  from .transcripts import (
41
30
  assistant_tool_calls_payload,
42
31
  ensure_tool_call_ids,
@@ -665,14 +654,6 @@ def create_memact_workflow(
665
654
  if req.max_tokens is not None:
666
655
  params["max_tokens"] = req.max_tokens
667
656
  payload["params"] = runtime_llm_params(runtime_ns, extra=params)
668
- # Ledger record of a re-prompt (mission AGX; twin of react_runtime).
669
- _reprompt = run.vars.get("_temp", {}).get("reprompt") if isinstance(run.vars.get("_temp"), dict) else None
670
- if isinstance(_reprompt, dict) and _reprompt.get("reason"):
671
- payload["_runtime_observability"] = {
672
- "reprompted": str(_reprompt.get("reason")),
673
- "reprompt_detail": str(_reprompt.get("detail") or ""),
674
- "reprompted_cycle": _reprompt.get("cycle"),
675
- }
676
657
 
677
658
  return StepPlan(
678
659
  node_id="reason",
@@ -685,10 +666,6 @@ def create_memact_workflow(
685
666
  response = temp.get("llm_response", {})
686
667
  content, tool_calls = logic.parse_response(response)
687
668
  temp.pop("llm_response", None)
688
- # One re-prompt per step (mission AGX): popped once, here.
689
- prior_reprompt = temp.pop("reprompt", None)
690
- if not isinstance(prior_reprompt, dict):
691
- prior_reprompt = None
692
669
 
693
670
  # COMMON CORE parse payload (0028 contract wave, 2026-07-14): every
694
671
  # loop guarantees has_tool_calls + tool_calls + content_preview.
@@ -728,98 +705,9 @@ def create_memact_workflow(
728
705
  temp["pending_tool_calls"] = [tc.__dict__ for tc in tool_calls]
729
706
  # tool_proposed on all three loops (0026 follow-up, 2026-07-15):
730
707
  # the canonical commit signal, same raw step + payload as ReAct.
731
- _ptc: Dict[str, Any] = {"count": len(tool_calls)}
732
- _from_reasoning = tool_calls_from_reasoning(response)
733
- if _from_reasoning:
734
- _ptc["from_reasoning"] = _from_reasoning
735
- if prior_reprompt is not None:
736
- _ptc["after_reprompt"] = str(prior_reprompt.get("reason") or "")
737
- emit("parse_tool_calls", _ptc)
708
+ emit("parse_tool_calls", {"count": len(tool_calls)})
738
709
  return StepPlan(node_id="parse", next_node="act")
739
710
 
740
- # A reply with NO tool call that is not an answer (mission AGX; twin
741
- # of react_runtime.parse_node, see its comment and announced_calls):
742
- # an announcement ("Let me verify ...") or tool-call markup that could
743
- # not run. Re-prompt ONCE with the reply verbatim; a second failure
744
- # ends the step with a visible error instead of publishing either.
745
- _check_announced, _check_unrunnable = resolve_checks(runtime_ns, suppressed=suppress_loop_tail(runtime_ns))
746
- _specs_now = runtime_ns.get("tool_specs") if isinstance(runtime_ns, dict) else None
747
- _tools_offered = isinstance(_specs_now, list) and bool(_specs_now)
748
- no_call_reason, no_call_detail = classify_no_call_reply(
749
- response,
750
- content,
751
- tools_offered=_tools_offered,
752
- check_announcement=_check_announced,
753
- check_unrunnable=_check_unrunnable,
754
- )
755
- if no_call_reason:
756
- _cycle_i = int(scratchpad.get("iteration", 0) or 0)
757
- _limits_ns = run.vars.get("_limits") if isinstance(run.vars.get("_limits"), dict) else {}
758
- _max_it = max(1, resolve_max_iterations(_limits_ns, scratchpad))
759
- raw_reply = verbatim_reply(response, fallback=str(content or ""))
760
- temp["pending_tool_calls"] = []
761
- last_iteration = _cycle_i >= _max_it
762
- if prior_reprompt is not None or last_iteration:
763
- # Second failure, or no iteration left to re-prompt in: end the
764
- # step with a visible error and go to the budget terminal (this
765
- # loop's conclusion), which answers with the error and a
766
- # `stop_reason.code = "no_tool_call"` — never with this reply,
767
- # nor with an earlier turn's pre-call narration.
768
- reprompted = prior_reprompt is not None
769
- error_text = no_call_error_text(no_call_reason, no_call_detail, reprompted=reprompted)
770
- context["messages"].append(
771
- _new_message(
772
- ctx,
773
- role="assistant",
774
- content=visible_content(response),
775
- metadata={"kind": "reprompt_failed_reply", "cycle": _cycle_i, "reason": no_call_reason},
776
- )
777
- )
778
- scratchpad["no_tool_call_stop"] = {
779
- "reason": no_call_reason,
780
- "detail": no_call_detail,
781
- "cycle": _cycle_i,
782
- "error": error_text,
783
- "reprompted": reprompted,
784
- }
785
- if reprompted:
786
- emit(
787
- "parse_reprompt_failed",
788
- {"cycle": _cycle_i, "reason": no_call_reason, "detail": no_call_detail, "error": error_text},
789
- )
790
- else:
791
- emit(
792
- "parse_reprompt_skipped",
793
- {"cycle": _cycle_i, "reason": no_call_reason, "detail": no_call_detail, "why": "iteration_budget"},
794
- )
795
- return StepPlan(node_id="parse", next_node="max_iterations")
796
- # Visible content stays the assistant turn; the verbatim reply is
797
- # QUOTED in the corrective (templates strip `<think>` from history).
798
- context["messages"].append(
799
- _new_message(
800
- ctx,
801
- role="assistant",
802
- content=visible_content(response),
803
- metadata={"kind": "reprompted_reply", "cycle": _cycle_i, "reason": no_call_reason},
804
- )
805
- )
806
- context["messages"].append(
807
- _new_message(
808
- ctx,
809
- role="user",
810
- content=reprompt_text(
811
- no_call_reason,
812
- tools_offered=_tools_offered,
813
- suppressed=suppress_loop_tail(runtime_ns),
814
- reply=raw_reply,
815
- ),
816
- metadata={"kind": "reprompt", "cycle": _cycle_i, "reason": no_call_reason},
817
- )
818
- )
819
- temp["reprompt"] = {"reason": no_call_reason, "detail": no_call_detail, "cycle": _cycle_i}
820
- emit("parse_reprompt", {"cycle": _cycle_i, "reason": no_call_reason, "detail": no_call_detail})
821
- return StepPlan(node_id="parse", next_node="reason")
822
-
823
711
  # Tool-free: draft answer becomes input to the envelope finalization call.
824
712
  temp["draft_answer"] = str(content or "").strip()
825
713
  scratchpad["tool_retry_count"] = 0
@@ -1458,11 +1346,6 @@ def create_memact_workflow(
1458
1346
  # Loudness parity (fable5 2026-07-13): a skipped finalize must
1459
1347
  # be visible in the run OUTPUT, not only the emit lane.
1460
1348
  "finalize_skipped": bool(scratchpad.get("finalize_skipped")),
1461
- **(
1462
- {"no_tool_call_stop": dict(scratchpad["no_tool_call_stop"])}
1463
- if isinstance(scratchpad.get("no_tool_call_stop"), dict)
1464
- else {}
1465
- ),
1466
1349
  },
1467
1350
  )
1468
1351
 
@@ -1479,20 +1362,10 @@ def create_memact_workflow(
1479
1362
  # finalize path would have polished). `.get`, not [] (fable5
1480
1363
  # 2026-07-13): content-less messages raised here.
1481
1364
  temp_ns = run.vars.get("_temp") if isinstance(run.vars.get("_temp"), dict) else {}
1482
- _no_call = scratchpad.get("no_tool_call_stop") if isinstance(scratchpad.get("no_tool_call_stop"), dict) else None
1483
- # An unrecovered no-call reply (mission AGX) ends with its visible error.
1484
- answer = str(_no_call.get("error") or "") if _no_call else str(temp_ns.get("draft_answer") or "").strip()
1365
+ answer = str(temp_ns.get("draft_answer") or "").strip()
1485
1366
  if not answer:
1486
1367
  for msg in reversed(messages):
1487
- # A re-prompted (failed) reply is never "last words" (mission AGX).
1488
- # A turn that carried tool calls is pre-call narration, not an
1489
- # answer (review 28 D3: "Searching." was published).
1490
- if (
1491
- isinstance(msg, dict)
1492
- and msg.get("role") == "assistant"
1493
- and not is_not_an_answer(msg)
1494
- and not msg.get("tool_calls")
1495
- ):
1368
+ if isinstance(msg, dict) and msg.get("role") == "assistant":
1496
1369
  text = str(msg.get("content") or "")
1497
1370
  if text.strip():
1498
1371
  answer = text
@@ -1514,14 +1387,6 @@ def create_memact_workflow(
1514
1387
  "outcome": "iteration_budget",
1515
1388
  # Always-present at BOTH terminals (wave-F P3 shape parity).
1516
1389
  "finalize_skipped": bool(scratchpad.get("finalize_skipped")),
1517
- **(
1518
- {
1519
- "no_tool_call_stop": dict(_no_call),
1520
- "stop_reason": no_call_stop_reason(_no_call, iterations=max_iterations),
1521
- }
1522
- if _no_call
1523
- else {}
1524
- ),
1525
1390
  },
1526
1391
  )
1527
1392