abstractagent 0.3.0__tar.gz → 0.3.1__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 (76) hide show
  1. abstractagent-0.3.1/PKG-INFO +112 -0
  2. abstractagent-0.3.1/README.md +99 -0
  3. {abstractagent-0.3.0 → abstractagent-0.3.1}/pyproject.toml +8 -1
  4. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/adapters/codeact_runtime.py +86 -5
  5. abstractagent-0.3.1/src/abstractagent/adapters/generation_params.py +82 -0
  6. abstractagent-0.3.1/src/abstractagent/adapters/media.py +45 -0
  7. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/adapters/memact_runtime.py +248 -10
  8. abstractagent-0.3.1/src/abstractagent/adapters/react_runtime.py +1612 -0
  9. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/agents/base.py +31 -0
  10. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/agents/codeact.py +36 -0
  11. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/agents/memact.py +37 -1
  12. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/agents/react.py +44 -6
  13. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/logic/builtins.py +58 -0
  14. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/logic/codeact.py +4 -1
  15. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/logic/memact.py +2 -1
  16. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/logic/react.py +37 -56
  17. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/tools/__init__.py +6 -0
  18. abstractagent-0.3.1/src/abstractagent.egg-info/PKG-INFO +112 -0
  19. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent.egg-info/SOURCES.txt +16 -5
  20. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_active_context_policy_adoption.py +4 -4
  21. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_agent_plan_review_modes.py +11 -48
  22. abstractagent-0.3.1/tests/test_generation_params_media_policies.py +28 -0
  23. abstractagent-0.3.1/tests/test_llm_call_payload_requires_prompt_or_messages.py +84 -0
  24. abstractagent-0.3.1/tests/test_open_attachment_tool_available.py +29 -0
  25. abstractagent-0.3.1/tests/test_react_active_attachments_disable_tools_first_iteration.py +144 -0
  26. abstractagent-0.3.1/tests/test_react_adapter_forwards_context_attachments_media.py +46 -0
  27. abstractagent-0.3.1/tests/test_react_delegate_agent_tool.py +85 -0
  28. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_react_logic.py +9 -0
  29. abstractagent-0.3.1/tests/test_react_loop_context_transcript_levels.py +281 -0
  30. abstractagent-0.3.1/tests/test_react_loop_followthrough_retry.py +99 -0
  31. abstractagent-0.3.1/tests/test_react_loop_side_effect_dedupe_comms.py +77 -0
  32. abstractagent-0.3.1/tests/test_react_loop_truncated_output_retry.py +209 -0
  33. abstractagent-0.3.1/tests/test_react_max_iterations_concludes.py +186 -0
  34. abstractagent-0.3.1/tests/test_react_planish_heuristic.py +29 -0
  35. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_react_workflow_system_prompt_and_tools_override.py +29 -0
  36. abstractagent-0.3.1/tests/test_react_workflow_toolset_refresh.py +73 -0
  37. abstractagent-0.3.1/tests/test_tool_observe_emits_result_for_tui.py +103 -0
  38. abstractagent-0.3.0/PKG-INFO +0 -133
  39. abstractagent-0.3.0/README.md +0 -120
  40. abstractagent-0.3.0/src/abstractagent/adapters/react_runtime.py +0 -1437
  41. abstractagent-0.3.0/src/abstractagent.egg-info/PKG-INFO +0 -133
  42. abstractagent-0.3.0/tests/test_prompt_deobservation.py +0 -95
  43. abstractagent-0.3.0/tests/test_react_runtime_parse_sanitizes_observation_transcripts.py +0 -66
  44. abstractagent-0.3.0/tests/test_react_runtime_tool_loop_guard.py +0 -148
  45. abstractagent-0.3.0/tests/test_react_runtime_verifier_policy.py +0 -97
  46. abstractagent-0.3.0/tests/test_toolcall_queue_and_review_prompt.py +0 -289
  47. {abstractagent-0.3.0 → abstractagent-0.3.1}/LICENSE +0 -0
  48. {abstractagent-0.3.0 → abstractagent-0.3.1}/setup.cfg +0 -0
  49. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/__init__.py +0 -0
  50. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/adapters/__init__.py +0 -0
  51. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/agents/__init__.py +0 -0
  52. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/logic/__init__.py +0 -0
  53. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/logic/types.py +0 -0
  54. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/repl.py +0 -0
  55. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/sandbox/__init__.py +0 -0
  56. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/sandbox/interface.py +0 -0
  57. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/sandbox/local.py +0 -0
  58. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/scripts/__init__.py +0 -0
  59. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/scripts/lmstudio_tool_eval.py +0 -0
  60. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/tools/code_execution.py +0 -0
  61. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent/tools/self_improve.py +0 -0
  62. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent.egg-info/dependency_links.txt +0 -0
  63. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent.egg-info/entry_points.txt +0 -0
  64. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent.egg-info/requires.txt +0 -0
  65. {abstractagent-0.3.0 → abstractagent-0.3.1}/src/abstractagent.egg-info/top_level.txt +0 -0
  66. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_agents_integration_llm.py +0 -0
  67. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_base_agent_trace_accessors.py +0 -0
  68. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_builtin_tool_specs_optional_defaults.py +0 -0
  69. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_codeact_logic.py +0 -0
  70. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_codeact_runtime_verifier_policy.py +0 -0
  71. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_memact_session_memory.py +0 -0
  72. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_memact_workflow.py +0 -0
  73. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_memory_recall_tool.py +0 -0
  74. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_run_start_seeds_runtime_provider_model.py +0 -0
  75. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_session_memory.py +0 -0
  76. {abstractagent-0.3.0 → abstractagent-0.3.1}/tests/test_tools_editing.py +0 -0
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: abstractagent
3
+ Version: 0.3.1
4
+ Summary: Agent implementations using AbstractRuntime and AbstractCore
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: abstractcore[tools]
9
+ Requires-Dist: abstractruntime
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=7.0; extra == "dev"
12
+ Dynamic: license-file
13
+
14
+ # AbstractAgent
15
+
16
+ Agent patterns (ReAct / CodeAct / MemAct) built on **AbstractRuntime** (durable execution) and **AbstractCore** (tools + LLM integration).
17
+
18
+ Start here: [`docs/getting-started.md`](docs/getting-started.md) (then [`docs/README.md`](docs/README.md) for the full index)
19
+
20
+ ## Documentation
21
+
22
+ - Getting started: [`docs/getting-started.md`](docs/getting-started.md)
23
+ - API reference: [`docs/api.md`](docs/api.md)
24
+ - FAQ / troubleshooting: [`docs/faq.md`](docs/faq.md)
25
+ - Architecture (diagrams): [`docs/architecture.md`](docs/architecture.md)
26
+ - Changelog: [`CHANGELOG.md`](CHANGELOG.md)
27
+ - Contributing: [`CONTRIBUTING.md`](CONTRIBUTING.md)
28
+ - Security: [`SECURITY.md`](SECURITY.md)
29
+ - Acknowledgements: [`ACKNOWLEDMENTS.md`](ACKNOWLEDMENTS.md)
30
+
31
+ ## What you get
32
+
33
+ - **ReAct**: tool-first Reason → Act → Observe loop
34
+ - **CodeAct**: executes Python (tool call or fenced ` ```python``` ` blocks)
35
+ - **MemAct**: memory-enhanced agent using runtime-owned Active Memory
36
+ - **Durable runs**: pause/resume via `run_id` + runtime stores
37
+ - **Tool control**: explicit tool bundles + per-run allowlists
38
+ - **Observability**: durable ledger of LLM calls, tool calls, and waits
39
+
40
+ ## Installation
41
+
42
+ From source (development):
43
+
44
+ ```bash
45
+ pip install -e .
46
+ ```
47
+
48
+ With dev dependencies:
49
+
50
+ ```bash
51
+ pip install -e ".[dev]"
52
+ ```
53
+
54
+ From PyPI:
55
+
56
+ ```bash
57
+ pip install abstractagent
58
+ ```
59
+
60
+ Note: the repository may be ahead of the latest published PyPI release. To verify what you installed:
61
+
62
+ ```bash
63
+ python -c "import importlib.metadata as md; print(md.version('abstractagent'))"
64
+ ```
65
+
66
+ ## Quick start (ReAct)
67
+
68
+ ```python
69
+ from abstractagent import create_react_agent
70
+
71
+ agent = create_react_agent(provider="ollama", model="qwen3:1.7b-q4_K_M")
72
+ agent.start("List the files in the current directory")
73
+ state = agent.run_to_completion()
74
+ print(state.output["answer"])
75
+ ```
76
+
77
+ ## Persistence (resume across restarts)
78
+
79
+ By default, the factory helpers use an in-memory runtime store. For resume across process restarts,
80
+ pass a persistent `RunStore`/`LedgerStore` (example below uses JSON files).
81
+
82
+ ```python
83
+ from abstractagent import create_react_agent
84
+ from abstractruntime.storage.json_files import JsonFileRunStore, JsonlLedgerStore
85
+
86
+ run_store = JsonFileRunStore(".runs")
87
+ ledger_store = JsonlLedgerStore(".runs")
88
+
89
+ agent = create_react_agent(run_store=run_store, ledger_store=ledger_store)
90
+ agent.start("Long running task")
91
+ agent.save_state("agent_state.json")
92
+
93
+ # ... later / after restart ...
94
+
95
+ agent2 = create_react_agent(run_store=run_store, ledger_store=ledger_store)
96
+ agent2.load_state("agent_state.json")
97
+ state = agent2.run_to_completion()
98
+ print(state.output["answer"])
99
+ ```
100
+
101
+ More details: [`docs/persistence.md`](docs/persistence.md)
102
+
103
+ ## CLI
104
+
105
+ This repository still installs a `react-agent` entrypoint, but it is **deprecated** and only prints a migration hint
106
+ (see `src/abstractagent/repl.py` and `pyproject.toml`).
107
+
108
+ Interactive UX lives in **AbstractCode**.
109
+
110
+ ## License
111
+
112
+ MIT (see `LICENSE`).
@@ -0,0 +1,99 @@
1
+ # AbstractAgent
2
+
3
+ Agent patterns (ReAct / CodeAct / MemAct) built on **AbstractRuntime** (durable execution) and **AbstractCore** (tools + LLM integration).
4
+
5
+ Start here: [`docs/getting-started.md`](docs/getting-started.md) (then [`docs/README.md`](docs/README.md) for the full index)
6
+
7
+ ## Documentation
8
+
9
+ - Getting started: [`docs/getting-started.md`](docs/getting-started.md)
10
+ - API reference: [`docs/api.md`](docs/api.md)
11
+ - FAQ / troubleshooting: [`docs/faq.md`](docs/faq.md)
12
+ - Architecture (diagrams): [`docs/architecture.md`](docs/architecture.md)
13
+ - Changelog: [`CHANGELOG.md`](CHANGELOG.md)
14
+ - Contributing: [`CONTRIBUTING.md`](CONTRIBUTING.md)
15
+ - Security: [`SECURITY.md`](SECURITY.md)
16
+ - Acknowledgements: [`ACKNOWLEDMENTS.md`](ACKNOWLEDMENTS.md)
17
+
18
+ ## What you get
19
+
20
+ - **ReAct**: tool-first Reason → Act → Observe loop
21
+ - **CodeAct**: executes Python (tool call or fenced ` ```python``` ` blocks)
22
+ - **MemAct**: memory-enhanced agent using runtime-owned Active Memory
23
+ - **Durable runs**: pause/resume via `run_id` + runtime stores
24
+ - **Tool control**: explicit tool bundles + per-run allowlists
25
+ - **Observability**: durable ledger of LLM calls, tool calls, and waits
26
+
27
+ ## Installation
28
+
29
+ From source (development):
30
+
31
+ ```bash
32
+ pip install -e .
33
+ ```
34
+
35
+ With dev dependencies:
36
+
37
+ ```bash
38
+ pip install -e ".[dev]"
39
+ ```
40
+
41
+ From PyPI:
42
+
43
+ ```bash
44
+ pip install abstractagent
45
+ ```
46
+
47
+ Note: the repository may be ahead of the latest published PyPI release. To verify what you installed:
48
+
49
+ ```bash
50
+ python -c "import importlib.metadata as md; print(md.version('abstractagent'))"
51
+ ```
52
+
53
+ ## Quick start (ReAct)
54
+
55
+ ```python
56
+ from abstractagent import create_react_agent
57
+
58
+ agent = create_react_agent(provider="ollama", model="qwen3:1.7b-q4_K_M")
59
+ agent.start("List the files in the current directory")
60
+ state = agent.run_to_completion()
61
+ print(state.output["answer"])
62
+ ```
63
+
64
+ ## Persistence (resume across restarts)
65
+
66
+ By default, the factory helpers use an in-memory runtime store. For resume across process restarts,
67
+ pass a persistent `RunStore`/`LedgerStore` (example below uses JSON files).
68
+
69
+ ```python
70
+ from abstractagent import create_react_agent
71
+ from abstractruntime.storage.json_files import JsonFileRunStore, JsonlLedgerStore
72
+
73
+ run_store = JsonFileRunStore(".runs")
74
+ ledger_store = JsonlLedgerStore(".runs")
75
+
76
+ agent = create_react_agent(run_store=run_store, ledger_store=ledger_store)
77
+ agent.start("Long running task")
78
+ agent.save_state("agent_state.json")
79
+
80
+ # ... later / after restart ...
81
+
82
+ agent2 = create_react_agent(run_store=run_store, ledger_store=ledger_store)
83
+ agent2.load_state("agent_state.json")
84
+ state = agent2.run_to_completion()
85
+ print(state.output["answer"])
86
+ ```
87
+
88
+ More details: [`docs/persistence.md`](docs/persistence.md)
89
+
90
+ ## CLI
91
+
92
+ This repository still installs a `react-agent` entrypoint, but it is **deprecated** and only prints a migration hint
93
+ (see `src/abstractagent/repl.py` and `pyproject.toml`).
94
+
95
+ Interactive UX lives in **AbstractCode**.
96
+
97
+ ## License
98
+
99
+ MIT (see `LICENSE`).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "abstractagent"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Agent implementations using AbstractRuntime and AbstractCore"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -23,3 +23,10 @@ where = ["src"]
23
23
 
24
24
  [project.scripts]
25
25
  react-agent = "abstractagent.repl:main"
26
+
27
+ [tool.pytest.ini_options]
28
+ markers = [
29
+ "basic: Level A (contract/unit) tests",
30
+ "integration: Level B (integration/local) tests",
31
+ "e2e: Level C (end-to-end/real infra) tests (opt-in)",
32
+ ]
@@ -11,6 +11,8 @@ from abstractruntime import Effect, EffectType, RunState, StepPlan, WorkflowSpec
11
11
  from abstractruntime.core.vars import ensure_limits, ensure_namespaces
12
12
  from abstractruntime.memory.active_context import ActiveContextPolicy
13
13
 
14
+ from .generation_params import runtime_llm_params
15
+ from .media import extract_media_from_context
14
16
  from ..logic.codeact import CodeActLogic
15
17
 
16
18
 
@@ -215,6 +217,7 @@ def create_codeact_workflow(
215
217
  if keep < 200:
216
218
  keep = max_chars
217
219
  suffix = ""
220
+ #[WARNING:TRUNCATION] bounded message content for LLM payload
218
221
  return text[:keep].rstrip() + suffix
219
222
 
220
223
  out: List[Dict[str, str]] = []
@@ -335,7 +338,10 @@ def create_codeact_workflow(
335
338
 
336
339
  emit("plan_request", {"tools": allow})
337
340
 
338
- payload: Dict[str, Any] = {"prompt": prompt, "params": {"temperature": 0.2}}
341
+ payload: Dict[str, Any] = {"prompt": prompt, "params": runtime_llm_params(runtime_ns, extra={"temperature": 0.2})}
342
+ media = extract_media_from_context(context)
343
+ if media:
344
+ payload["media"] = media
339
345
  sys = _system_prompt(runtime_ns)
340
346
  if isinstance(sys, str) and sys.strip():
341
347
  payload["system_prompt"] = sys
@@ -431,11 +437,16 @@ def create_codeact_workflow(
431
437
  "messages": _sanitize_llm_messages(messages_view, limits=limits),
432
438
  "tools": list(tool_specs),
433
439
  }
440
+ media = extract_media_from_context(context)
441
+ if media:
442
+ payload["media"] = media
434
443
  sys = _system_prompt(runtime_ns) or req.system_prompt
435
444
  if isinstance(sys, str) and sys.strip():
436
445
  payload["system_prompt"] = sys
446
+ params: Dict[str, Any] = {}
437
447
  if req.max_tokens is not None:
438
- payload["params"] = {"max_tokens": req.max_tokens}
448
+ params["max_tokens"] = req.max_tokens
449
+ payload["params"] = runtime_llm_params(runtime_ns, extra=params)
439
450
 
440
451
  return StepPlan(
441
452
  node_id="reason",
@@ -560,6 +571,7 @@ def create_codeact_workflow(
560
571
  "remember",
561
572
  "remember_note",
562
573
  "compact_memory",
574
+ "delegate_agent",
563
575
  }
564
576
 
565
577
  tool_queue: List[Dict[str, Any]] = []
@@ -635,6 +647,68 @@ def create_codeact_workflow(
635
647
  next_node="handle_user_response",
636
648
  )
637
649
 
650
+ if name == "delegate_agent":
651
+ delegated_task = str(args.get("task") or "").strip()
652
+ delegated_context = str(args.get("context") or "").strip()
653
+
654
+ tools_raw = args.get("tools")
655
+ if tools_raw is None:
656
+ # Inherit the current allowlist, but avoid recursive delegation and avoid waiting on ask_user
657
+ # unless explicitly enabled.
658
+ child_allow = [t for t in allow if t not in {"delegate_agent", "ask_user"}]
659
+ else:
660
+ child_allow = _normalize_allowlist(tools_raw)
661
+
662
+ if not delegated_task:
663
+ temp["tool_results"] = {
664
+ "results": [
665
+ {
666
+ "call_id": str(tc.get("call_id") or ""),
667
+ "name": "delegate_agent",
668
+ "success": False,
669
+ "output": None,
670
+ "error": "delegate_agent requires a non-empty task",
671
+ }
672
+ ]
673
+ }
674
+ return StepPlan(node_id="act", next_node="observe")
675
+
676
+ combined_task = delegated_task
677
+ if delegated_context:
678
+ combined_task = f"{delegated_task}\n\nContext:\n{delegated_context}"
679
+
680
+ sub_vars: Dict[str, Any] = {
681
+ "context": {"task": combined_task, "messages": []},
682
+ "_runtime": {
683
+ "allowed_tools": list(child_allow),
684
+ "system_prompt_extra": (
685
+ "You are a delegated sub-agent.\n"
686
+ "- Focus ONLY on the delegated task.\n"
687
+ "- Use ONLY the allowed tools when needed.\n"
688
+ "- Do not ask the user questions; if blocked, state assumptions and proceed.\n"
689
+ "- Return a concise result suitable for the parent agent to act on.\n"
690
+ ),
691
+ },
692
+ "_limits": {"max_iterations": 10},
693
+ }
694
+
695
+ payload = {
696
+ "workflow_id": str(getattr(run, "workflow_id", "") or "codeact_agent"),
697
+ "vars": sub_vars,
698
+ "async": False,
699
+ "include_traces": False,
700
+ # Tool-mode wrapper so the parent receives a normal tool observation (no run failure on child failure).
701
+ "wrap_as_tool_result": True,
702
+ "tool_name": "delegate_agent",
703
+ "call_id": str(tc.get("call_id") or ""),
704
+ }
705
+ emit("delegate_agent", {"tools": list(child_allow), "call_id": payload.get("call_id")})
706
+ return StepPlan(
707
+ node_id="act",
708
+ effect=Effect(type=EffectType.START_SUBWORKFLOW, payload=payload, result_key="_temp.tool_results"),
709
+ next_node="observe",
710
+ )
711
+
638
712
  if name == "recall_memory":
639
713
  payload = dict(args)
640
714
  payload.setdefault("tool_name", "recall_memory")
@@ -730,13 +804,14 @@ def create_codeact_workflow(
730
804
  )
731
805
 
732
806
  def execute_code_node(run: RunState, ctx) -> StepPlan:
733
- _, _, _, temp, _ = ensure_codeact_vars(run)
807
+ _, _, runtime_ns, temp, _ = ensure_codeact_vars(run)
734
808
  code = temp.get("pending_code")
735
809
  if not isinstance(code, str) or not code.strip():
736
810
  return StepPlan(node_id="execute_code", next_node="reason")
737
811
 
738
812
  temp.pop("pending_code", None)
739
813
  emit("act", {"tool": "execute_python", "args": {"code": "(inline)", "timeout_s": 10.0}})
814
+ allow = _effective_allowlist(runtime_ns)
740
815
 
741
816
  return StepPlan(
742
817
  node_id="execute_code",
@@ -749,7 +824,8 @@ def create_codeact_workflow(
749
824
  "arguments": {"code": code, "timeout_s": 10.0},
750
825
  "call_id": "code",
751
826
  }
752
- ]
827
+ ],
828
+ "allowed_tools": list(allow),
753
829
  },
754
830
  result_key="_temp.tool_results",
755
831
  ),
@@ -794,6 +870,7 @@ def create_codeact_workflow(
794
870
  # Keep a bounded preview for huge tool outputs to avoid bloating traces/ledgers.
795
871
  preview = rendered
796
872
  if len(preview) > 1000:
873
+ #[WARNING:TRUNCATION] bounded preview for observability payloads
797
874
  preview = preview[:1000] + f"\n… (truncated, {len(rendered):,} chars total)"
798
875
  emit("observe", {"tool": name, "success": success, "result": preview})
799
876
  context["messages"].append(
@@ -870,6 +947,7 @@ def create_codeact_workflow(
870
947
  if keep < 200:
871
948
  keep = max_chars
872
949
  suffix = ""
950
+ #[WARNING:TRUNCATION] bounded transcript blocks for prompt reconstruction
873
951
  return s[:keep].rstrip() + suffix
874
952
 
875
953
  def _format_allowed_tools() -> str:
@@ -1002,8 +1080,11 @@ def create_codeact_workflow(
1002
1080
  "prompt": prompt,
1003
1081
  "response_schema": schema,
1004
1082
  "response_schema_name": "CodeActVerifier",
1005
- "params": {"temperature": 0.2},
1083
+ "params": runtime_llm_params(runtime_ns, extra={"temperature": 0.2}),
1006
1084
  }
1085
+ media = extract_media_from_context(context)
1086
+ if media:
1087
+ payload["media"] = media
1007
1088
  sys = _system_prompt(runtime_ns)
1008
1089
  if sys is not None:
1009
1090
  payload["system_prompt"] = sys
@@ -0,0 +1,82 @@
1
+ """Helpers for consistent generation params in AbstractAgent adapters.
2
+
3
+ These adapters build `EffectType.LLM_CALL` payloads for AbstractRuntime. We want
4
+ to expose a uniform `(temperature, seed)` interface across agents while keeping
5
+ backward compatibility with older runs that may not have these keys in
6
+ `vars["_runtime"]`.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, Dict, Optional
12
+
13
+
14
+ def normalize_seed(seed: Any) -> Optional[int]:
15
+ """Return a provider-ready seed or None when unset/random.
16
+
17
+ Policy:
18
+ - None or any negative value -> None (meaning: do not send seed).
19
+ - bool values are ignored (JSON booleans are ints in Python).
20
+ - numeric-ish values -> int(seed) if >= 0.
21
+ """
22
+ try:
23
+ if seed is None or isinstance(seed, bool):
24
+ return None
25
+ seed_i = int(seed)
26
+ return seed_i if seed_i >= 0 else None
27
+ except Exception:
28
+ return None
29
+
30
+
31
+ def runtime_llm_params(
32
+ runtime_ns: Dict[str, Any],
33
+ *,
34
+ extra: Optional[Dict[str, Any]] = None,
35
+ default_temperature: float = 0.7,
36
+ ) -> Dict[str, Any]:
37
+ """Merge `runtime_ns` sampling controls into an LLM_CALL params dict.
38
+
39
+ Precedence:
40
+ 1) `runtime_ns.temperature` / `runtime_ns.seed` when present
41
+ 2) `extra.temperature` / `extra.seed` (step-specific defaults)
42
+ 3) `default_temperature` (only for temperature)
43
+ """
44
+ out: Dict[str, Any] = dict(extra or {})
45
+
46
+ # Temperature: always provide a float (provider-agnostic).
47
+ temp_val = runtime_ns.get("temperature") if isinstance(runtime_ns, dict) else None
48
+ if temp_val is None:
49
+ temp_val = out.get("temperature")
50
+ if temp_val is None:
51
+ temp_val = default_temperature
52
+ try:
53
+ out["temperature"] = float(temp_val)
54
+ except Exception:
55
+ out["temperature"] = float(default_temperature)
56
+
57
+ # Seed: only include when explicitly set (>= 0).
58
+ seed_val = runtime_ns.get("seed") if isinstance(runtime_ns, dict) else None
59
+ if seed_val is None:
60
+ seed_val = out.get("seed")
61
+ seed_norm = normalize_seed(seed_val)
62
+ if seed_norm is not None:
63
+ out["seed"] = seed_norm
64
+ else:
65
+ out.pop("seed", None)
66
+
67
+ # Pass-through media policies (runtime-owned defaults).
68
+ #
69
+ # This keeps thin clients simple: they can set `_runtime.audio_policy` (and
70
+ # optional language hints) once at run start, and all LLM_CALL steps inherit it.
71
+ if isinstance(runtime_ns, dict):
72
+ audio_policy = runtime_ns.get("audio_policy")
73
+ if "audio_policy" not in out and isinstance(audio_policy, str) and audio_policy.strip():
74
+ out["audio_policy"] = audio_policy.strip()
75
+
76
+ stt_language = runtime_ns.get("stt_language")
77
+ if stt_language is None:
78
+ stt_language = runtime_ns.get("audio_language")
79
+ if "stt_language" not in out and isinstance(stt_language, str) and stt_language.strip():
80
+ out["stt_language"] = stt_language.strip()
81
+
82
+ return out
@@ -0,0 +1,45 @@
1
+ """Helpers for attachment/media plumbing in runtime-backed agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+
8
+ def extract_media_from_context(context: Dict[str, Any]) -> Optional[List[Any]]:
9
+ """Return a normalized `media` list from a runtime `context` dict.
10
+
11
+ Supported keys (best-effort):
12
+ - `context["attachments"]`: preferred (artifact refs)
13
+ - `context["media"]`: legacy/alternate
14
+ """
15
+ raw = context.get("attachments")
16
+ if raw is None:
17
+ raw = context.get("media")
18
+
19
+ if isinstance(raw, tuple):
20
+ items = list(raw)
21
+ else:
22
+ items = raw
23
+
24
+ if not isinstance(items, list) or not items:
25
+ return None
26
+
27
+ out: List[Any] = []
28
+ for item in items:
29
+ if isinstance(item, str):
30
+ s = item.strip()
31
+ if s:
32
+ out.append(s)
33
+ continue
34
+
35
+ if isinstance(item, dict):
36
+ # Prefer artifact refs; accept both {"$artifact": "..."} and {"artifact_id": "..."}.
37
+ aid = item.get("$artifact")
38
+ if not (isinstance(aid, str) and aid.strip()):
39
+ aid = item.get("artifact_id")
40
+ if isinstance(aid, str) and aid.strip():
41
+ out.append(dict(item))
42
+ continue
43
+
44
+ return out or None
45
+