Skip to content

Commit cb23c7e

Browse files
authored
Update base.py
1 parent 0d28e23 commit cb23c7e

1 file changed

Lines changed: 54 additions & 3 deletions

File tree

python_agent_harness/tools/base.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import threading
66
from abc import ABC, abstractmethod
7-
from typing import Any
7+
from typing import Any, Protocol, runtime_checkable
88

99
from ..models import ToolSpec
1010

@@ -36,6 +36,57 @@ def wait(self) -> str:
3636
return self._result or ""
3737

3838

39+
@runtime_checkable
40+
class ToolRuntime(Protocol):
41+
"""The session surface a ``ToolContext`` proxies to.
42+
43+
``ToolContext`` needs only this slice of the full ``Session`` —
44+
a project directory, a cancel event, and a handful of callbacks
45+
for user questions, diff/todo recording, skill lookup, sub-agent
46+
delegation, and plan-mode exit. Typing the context against this
47+
protocol (instead of ``Any``) documents the real dependency and
48+
lets a tool be exercised with a lightweight fake runtime, no full
49+
``Session`` required::
50+
51+
class FakeRuntime:
52+
project_dir = "/tmp"
53+
cancel_event = threading.Event()
54+
def ask_questions(self, questions): return "..."
55+
... # only the methods the tool under test touches
56+
57+
ctx = ToolContext(FakeRuntime())
58+
tool.run(args, ctx)
59+
60+
``ToolContext`` still accepts ``None`` and objects implementing only
61+
part of this surface (its methods guard each call), so the protocol
62+
describes the *complete* runtime while partial fakes remain valid.
63+
64+
``project_dir`` / ``cancel_event`` are declared as read-only
65+
properties (``ToolContext`` only ever reads them): that admits both
66+
a plain-attribute implementation (the real ``Session``) and a
67+
property-backed one (test doubles), whereas a plain mutable-attribute
68+
declaration would be invariant and reject a ``property``.
69+
"""
70+
71+
@property
72+
def project_dir(self) -> str: ...
73+
74+
@property
75+
def cancel_event(self) -> threading.Event: ...
76+
77+
def ask_questions(self, questions: list[dict]) -> str: ...
78+
79+
def record_diff(self, diff_text: str) -> None: ...
80+
81+
def update_todos(self, todos: list[dict]) -> None: ...
82+
83+
def find_skill(self, name: str) -> str | None: ...
84+
85+
def run_subagent(self, subagent_type: str, description: str, prompt: str) -> str: ...
86+
87+
def plan_exit(self) -> str: ...
88+
89+
3990
class ToolContext:
4091
"""Runtime context handed to tools.
4192
@@ -44,7 +95,7 @@ class ToolContext:
4495
proxy to the session when present; defaults are safe no-ops.
4596
"""
4697

47-
def __init__(self, session: Any = None) -> None:
98+
def __init__(self, session: ToolRuntime | None = None) -> None:
4899
self.session = session
49100

50101
@property
@@ -81,7 +132,7 @@ def plan_exit(self) -> str:
81132
return "Not in plan mode; PlanExit has no effect. Continue as normal."
82133

83134
@property
84-
def cancel_event(self) -> Any:
135+
def cancel_event(self) -> threading.Event | None:
85136
"""Session cancel event (set when the user presses Ctrl-C)."""
86137
if self.session and hasattr(self.session, "cancel_event"):
87138
return self.session.cancel_event

0 commit comments

Comments
 (0)