feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers
Phase 2.1 — sandbox dispatch helper:
- strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the
host->container HTTP wire format. Connect=10s, read=150s timeouts mirror
legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway
tool. All errors surface as {"error": str} so the model can recover
instead of the run dying.
Phase 2.2 — C6 lock-protected JSONL writes:
- strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped
in _notes_lock so concurrent agents can't interleave half-written lines.
Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel
writes produce exactly 1000 valid JSON lines.
Phase 2.3 — thin-slice SDK wrappers (think + todo + notes):
- strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes
just enough surface (.agent_id) for legacy tools that close over
agent_state, sourced from ctx.context['agent_id'].
- strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think).
- strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/
pending/delete) with bulk-form preserved.
- strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/
delete) with asyncio.to_thread around the lock-protected file I/O.
Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK
local). Full suite still green.
Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper
must be runtime-importable because the SDK calls get_type_hints() to
derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit
returns are intentional, each a distinct documented failure mode).
Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
"""SDK function-tool wrapper for the legacy ``think`` tool.
|
||||
|
||||
Pattern: thin async wrapper that delegates to the legacy implementation
|
||||
in :mod:`strix.tools.thinking.thinking_actions`. The legacy function is
|
||||
sync and pure (no I/O), so we don't even need ``asyncio.to_thread``.
|
||||
|
||||
Validates the simplest tool-port pattern: legacy function in, JSON string
|
||||
out, no sandbox involvement.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from strix.tools._decorator import strix_tool
|
||||
from strix.tools.thinking.thinking_actions import think as _legacy_think
|
||||
|
||||
|
||||
@strix_tool(timeout=10)
|
||||
async def think(thought: str) -> str:
|
||||
"""Record a private chain-of-thought note without taking any action.
|
||||
|
||||
The "think" tool is the planning escape hatch for situations where a
|
||||
message-without-tool-call would otherwise halt the run (per the
|
||||
interactive-mode tool-call requirement). The thought itself is
|
||||
recorded but produces no side effects.
|
||||
|
||||
Args:
|
||||
thought: The agent's reasoning to record. Must be non-empty.
|
||||
"""
|
||||
result = _legacy_think(thought)
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
Reference in New Issue
Block a user