4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
214 lines
6.8 KiB
Python
214 lines
6.8 KiB
Python
"""Ports (structural Protocols) the agentic turn engine talks to.
|
|
|
|
These are the seams that keep ``agent/`` decoupled from any concrete surface.
|
|
The interactive shell implements them as adapters over its ``Session``,
|
|
Rich console, tool registry, and grounding caches; the headless adapters in
|
|
:mod:`core.agent_harness.turns.headless_dispatch` implement minimal in-memory versions for API / test runs.
|
|
|
|
Nothing here imports ``interactive_shell``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Iterable, Sequence
|
|
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
|
|
from core.agent_harness.turns.turn_results import ShellTurnResult, ToolCallingTurnResult
|
|
|
|
if TYPE_CHECKING:
|
|
pass
|
|
|
|
# A tool-loop event callback: ``(kind, data)`` where kind is e.g. "tool_start".
|
|
ToolEventObserver = Callable[[str, dict[str, Any]], None]
|
|
|
|
# Confirmation prompt: given a summary, return the user's response string.
|
|
ConfirmFn = Callable[[str], str]
|
|
|
|
|
|
@runtime_checkable
|
|
class OutputSink(Protocol):
|
|
"""Where the engine renders user-facing output."""
|
|
|
|
def print(self, message: str = "") -> None:
|
|
"""Print one line of (markup-bearing) text."""
|
|
|
|
def render_response_header(self, label: str) -> None:
|
|
"""Render the assistant response header (e.g. a labelled rule)."""
|
|
|
|
def render_error(self, message: str) -> None:
|
|
"""Render an error/notice line."""
|
|
|
|
def stream(
|
|
self,
|
|
*,
|
|
label: str,
|
|
chunks: Iterable[str],
|
|
suppress_if_starts_with: str | None = None,
|
|
) -> str:
|
|
"""Stream ``chunks`` to the surface under ``label`` and return the text."""
|
|
|
|
|
|
@runtime_checkable
|
|
class SessionStore(Protocol):
|
|
"""Mutable per-session state the engine reads and writes.
|
|
|
|
``Session`` satisfies this structurally. The fields mirror what the
|
|
action driver, the three-path engine, and the gather loop touch.
|
|
"""
|
|
|
|
# --- turn-context snapshot fields (see core.agent_harness.turns.turn_snapshot.TurnSnapshotSource) ---
|
|
cli_agent_messages: list[tuple[str, str]]
|
|
configured_integrations_known: bool
|
|
|
|
# Read-only here; ``Session`` stores a tuple. A property matches
|
|
# covariantly, so any concrete ``Sequence[str]`` implementation satisfies it.
|
|
@property
|
|
def configured_integrations(self) -> Sequence[str]:
|
|
raise NotImplementedError
|
|
|
|
last_state: dict[str, Any] | None
|
|
last_synthetic_observation_path: str | None
|
|
reasoning_effort: Any | None
|
|
|
|
# --- turn execution state ---
|
|
history: list[dict[str, Any]]
|
|
last_command_observation: str | None
|
|
session_id: str
|
|
|
|
# --- gather caches ---
|
|
resolved_integrations_cache: dict[str, Any] | None
|
|
github_repo_scope: tuple[str, str] | None
|
|
|
|
def record(self, kind: str, text: str, *, ok: bool = True) -> None:
|
|
"""Append a record of an executed action/turn to the session log."""
|
|
|
|
|
|
@runtime_checkable
|
|
class ToolProvider(Protocol):
|
|
"""Supplies the action-agent tools and the per-turn tool-event observer."""
|
|
|
|
def action_tools(
|
|
self,
|
|
*,
|
|
confirm_fn: ConfirmFn | None,
|
|
is_tty: bool | None,
|
|
resolved_integrations: dict[str, Any] | None = None,
|
|
) -> list[Any]:
|
|
"""Return the agent tools available for this turn.
|
|
|
|
When ``resolved_integrations`` is supplied it is the turn's single
|
|
resolved-integration view (from ``TurnSnapshot``); the provider builds
|
|
tools from it instead of resolving again, so tools and the prompt agree.
|
|
"""
|
|
|
|
def tool_resources(self) -> dict[str, Any]:
|
|
"""Return non-serializable resources for tools that opt into runtime context."""
|
|
|
|
def observer(self, *, message: str) -> ToolEventObserver:
|
|
"""Return a tool-event observer for this turn (e.g. terminal renderer)."""
|
|
|
|
|
|
@runtime_checkable
|
|
class ToolRegistry(Protocol):
|
|
"""Resolves the registered tools available to a named surface."""
|
|
|
|
def tools_for_surface(self, surface: str) -> list[Any]:
|
|
"""Return the registered tools for ``surface`` (e.g. ``"action"``)."""
|
|
|
|
def tool_map_for_surface(self, surface: str) -> dict[str, Any]:
|
|
"""Return the registered tools for ``surface`` keyed by tool name."""
|
|
|
|
|
|
@runtime_checkable
|
|
class ErrorReporter(Protocol):
|
|
"""Reports caught exceptions (telemetry / logging)."""
|
|
|
|
def report(self, exc: BaseException, *, context: str, expected: bool = False) -> None:
|
|
raise NotImplementedError
|
|
|
|
|
|
@runtime_checkable
|
|
class PromptContextProvider(Protocol):
|
|
"""Supplies grounding text for the conversational assistant prompt.
|
|
|
|
The grounding corpora (CLI reference, repo map, docs, investigation-flow,
|
|
environment) are surface/repo content; the shell adapter wires its grounding
|
|
caches, the headless adapter returns empty strings.
|
|
"""
|
|
|
|
def cli_reference(self) -> str:
|
|
raise NotImplementedError
|
|
|
|
def agents_md(self) -> str:
|
|
raise NotImplementedError
|
|
|
|
def investigation_flow(self) -> str:
|
|
raise NotImplementedError
|
|
|
|
def environment_block(self) -> str:
|
|
raise NotImplementedError
|
|
|
|
def suggested_synthetic_prompt(self) -> str:
|
|
raise NotImplementedError
|
|
|
|
def log_diagnostics(self, reason: str) -> None:
|
|
raise NotImplementedError
|
|
|
|
|
|
@runtime_checkable
|
|
class ReasoningClientProvider(Protocol):
|
|
"""Provides the streaming reasoning LLM client for the assistant answer."""
|
|
|
|
def get(self) -> Any | None:
|
|
raise NotImplementedError
|
|
|
|
|
|
@runtime_checkable
|
|
class RunRecordFactory(Protocol):
|
|
"""Builds the opaque per-answer LLM-run record (telemetry) from raw inputs."""
|
|
|
|
def build(self, *, client: Any, prompt: str, response_text: str, started: float) -> Any:
|
|
raise NotImplementedError
|
|
|
|
|
|
# Bound direct-answer callable (no tools):
|
|
# ``answer(text, *, confirm_fn, is_tty, tool_observation, turn_plan) -> LLM-run record | None``.
|
|
StreamAnswerFn = Callable[..., Any]
|
|
|
|
# Bound evidence-gather callable:
|
|
# ``gather(text, *, is_tty, turn_plan) -> str | None``.
|
|
EvidenceGatherer = Callable[..., "str | None"]
|
|
|
|
# Bound action tool-calling driver:
|
|
# ``execute_actions(text, *, confirm_fn, is_tty, turn_plan) -> ToolCallingTurnResult``.
|
|
ExecuteActions = Callable[..., ToolCallingTurnResult]
|
|
|
|
|
|
@runtime_checkable
|
|
class TurnAccounting(Protocol):
|
|
"""Records analytics/telemetry for a turn and finalizes the result."""
|
|
|
|
def record_action_result(self, action_result: ToolCallingTurnResult) -> None:
|
|
raise NotImplementedError
|
|
|
|
def finalize(self, result: ShellTurnResult) -> ShellTurnResult:
|
|
raise NotImplementedError
|
|
|
|
|
|
__all__ = [
|
|
"StreamAnswerFn",
|
|
"ConfirmFn",
|
|
"ErrorReporter",
|
|
"EvidenceGatherer",
|
|
"ExecuteActions",
|
|
"OutputSink",
|
|
"PromptContextProvider",
|
|
"ReasoningClientProvider",
|
|
"RunRecordFactory",
|
|
"SessionStore",
|
|
"ToolEventObserver",
|
|
"ToolProvider",
|
|
"ToolRegistry",
|
|
"TurnAccounting",
|
|
]
|