AbstractAgent 0.3.15__tar.gz → 0.3.16__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.
- {abstractagent-0.3.15/src/abstractagent.egg-info → abstractagent-0.3.16}/PKG-INFO +2 -4
- {abstractagent-0.3.15 → abstractagent-0.3.16}/README.md +1 -1
- {abstractagent-0.3.15 → abstractagent-0.3.16}/pyproject.toml +1 -4
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/codeact_runtime.py +4 -137
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/emit_inventory.py +0 -25
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/memact_runtime.py +3 -138
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/react_runtime.py +30 -300
- {abstractagent-0.3.15 → abstractagent-0.3.16/src/abstractagent.egg-info}/PKG-INFO +2 -4
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent.egg-info/SOURCES.txt +0 -2
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent.egg-info/requires.txt +0 -2
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_c2447_visit_lane_honesty.py +2 -2
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_loop_followthrough_retry.py +1 -3
- abstractagent-0.3.15/src/abstractagent/adapters/announced_calls.py +0 -463
- abstractagent-0.3.15/tests/test_announced_tool_reprompt.py +0 -751
- {abstractagent-0.3.15 → abstractagent-0.3.16}/LICENSE +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/setup.cfg +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/generation_params.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/loop_hooks.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/media.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/native_loop_registry.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/progress.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/read_orchestration.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/tool_allowlist.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/tool_failure_hints.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/adapters/transcripts.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/base.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/codeact.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/defaults.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/memact.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/react.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/task_reset.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/agents/unattended.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/logic/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/logic/builtins.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/logic/codeact.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/logic/memact.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/logic/react.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/logic/types.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/metrics/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/metrics/prefix_reuse.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/repl.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/sandbox/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/sandbox/interface.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/sandbox/local.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/scripts/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/scripts/lmstudio_tool_eval.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/tools/__init__.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/tools/code_execution.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent/tools/self_improve.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent.egg-info/dependency_links.txt +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent.egg-info/entry_points.txt +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/src/abstractagent.egg-info/top_level.txt +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_active_context_policy_adoption.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_adversary_hardening_2026_07_13.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_agent_plan_review_modes.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_agent_speculation_inheritance.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_agent_stream_inheritance.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_agents_integration_llm.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_base_agent_trace_accessors.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_batching_prompt_rule.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_budget_triage_tail.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_builtin_tool_specs_optional_defaults.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_claim_0029_residue_2026_07_14.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_codeact_logic.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_codeact_runtime_verifier_policy.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_delegate_session_route_composition.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_emit_inventory_drift.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_gated_remainder_wave_2026_07_14.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_generation_params_media_policies.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_history_monster_clamp.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_improvement_wave_2026_07_13.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_llm_call_payload_requires_prompt_or_messages.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_lmstudio_tool_eval_metrics.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_loop_hooks.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_loop_hooks_0026_follow_ups.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_memact_codeact_guidance_durable_transcript.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_memact_session_memory.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_memact_workflow.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_memory_recall_tool.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_native_loop_registry.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_open_attachment_tool_available.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_operator_conclude.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_optimal_wave_batch2_2026_07_13.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_packaging_profiles.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_parse_prompt_cache_capture.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_prefix_reuse_metric.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_progress_detection.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_act_only_observation.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_active_attachments_disable_tools_first_iteration.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_adapter_forwards_context_attachments_media.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_delegate_agent_tool.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_election_fence_conformance.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_entity_dress_conformance.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_guidance_durable_transcript.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_logic.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_loop_aborted_generation_retry.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_loop_context_transcript_levels.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_loop_side_effect_dedupe_comms.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_loop_truncated_output_retry.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_max_iterations_concludes.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_phase_grant_boundary.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_planish_heuristic.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_prompt_prefix_stability.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_sight_lane_media_fold.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_turn_byte_stability.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_verifier_and_plan.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_visit_composition.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_visit_merge.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_workflow_system_prompt_and_tools_override.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_react_workflow_toolset_refresh.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_read_orchestration_guidance.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_reasoning_first_citizen_agent.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_review_failure_containment.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_run_start_seeds_runtime_provider_model.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_session_memory.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_sibling_loop_defects_batch.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_sibling_strict_transcripts_0011.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_stuck_streak_termination.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_tool_failure_hints.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_tool_loop_prefix_extension_a3.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_tool_observe_emits_result_for_tui.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_tools_editing.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_unseeded_render_limits.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_verifier_execution_preference.py +0 -0
- {abstractagent-0.3.15 → abstractagent-0.3.16}/tests/test_verifier_schema_strict.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractagent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.16
|
|
4
4
|
Summary: Agent implementations using AbstractRuntime and AbstractCore
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -15,10 +15,8 @@ Requires-Dist: abstractcore[all-gpu]>=2.13.41; extra == "gpu"
|
|
|
15
15
|
Requires-Dist: abstractruntime[gpu]>=0.5.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,7 +105,7 @@ 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.
|
|
108
|
+
AbstractAgent 0.3.16 requires `abstractcore[tools]>=2.13.41` and
|
|
111
109
|
`AbstractRuntime>=0.5.0` (the runtime release that provides live token
|
|
112
110
|
streaming, on top of turn grounding, in-flight cancellation and speculation
|
|
113
111
|
inheritance). AbstractRuntime 0.5.0 itself requires AbstractCore 2.16.0 or newer.
|
|
@@ -84,7 +84,7 @@ 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.
|
|
87
|
+
AbstractAgent 0.3.16 requires `abstractcore[tools]>=2.13.41` and
|
|
88
88
|
`AbstractRuntime>=0.5.0` (the runtime release that provides live token
|
|
89
89
|
streaming, on top of turn grounding, in-flight cancellation and speculation
|
|
90
90
|
inheritance). AbstractRuntime 0.5.0 itself requires AbstractCore 2.16.0 or newer.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "abstractagent"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.16"
|
|
8
8
|
description = "Agent implementations using AbstractRuntime and AbstractCore"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -24,12 +24,9 @@ gpu = [
|
|
|
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
|
-
|
|
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
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|