Tool Runner

JSON tool call parser and async dispatcher for the coding agent — launches apps, controls Spotify, manages the workspace, and calls web tools.

Overview

The tool runner is used by the coding agent (and general chat agent) to parse structured JSON tool calls embedded in the LLM's output and execute them asynchronously. It is separate from the bracket command parser — tool calls use structured JSON, bracket commands use inline text syntax.

ModuleContents
tool_runner/parser.pyparse_tool_call_json(), strip_tool_call_json(), _scan_json_object().
tool_runner/executor.pyexecute_tool_call() — dispatches 30+ tools.
tool_runner/verifier.pyverify_tool_result() — validates tool output.

parse_tool_call_json()

Scans a raw LLM response string for the first valid JSON object containing a"tool" key and returns it as a dict. Handles partial JSON, markdown code fences, and embedded noise.

signature
def parse_tool_call_json(text: str) -> dict | None
example.py
from backend.services.tool_runner import parse_tool_call_json, strip_tool_call_json

raw = 'Let me check that. {"tool": "web_search", "args": {"query": "Python 3.13 features"}}'
tc = parse_tool_call_json(raw)
# {"tool": "web_search", "args": {"query": "Python 3.13 features"}}

# Remove the JSON blob from the display text
display_text = strip_tool_call_json(raw)
# "Let me check that."  

execute_tool_call()

Async dispatcher — takes a parsed tool call dict and returns a result string. The result is fed back into the LLM as a tool-result message for the next reasoning step.

signature
async def execute_tool_call(
    tc: dict,          # {"tool": "...", "args": {...}}
    db: Session,
    conversation_id: int,
) -> str               # short result string, max ~4000 chars
example.py
import asyncio
from backend.services.tool_runner import execute_tool_call
from backend.models.database import SessionLocal

db = SessionLocal()
result = asyncio.run(execute_tool_call(
    {"tool": "web_search", "args": {"query": "FastAPI streaming"}},
    db=db,
    conversation_id=1,
))
print(result)   # search results string
db.close()

verify_tool_result()

Validates a tool result string — checks it's non-empty, not an obvious error message, and within size limits. Returns True if the result is usable for the next LLM turn.

signature
def verify_tool_result(result: str) -> bool

Available tools

All tools available to the LLM via the tool runner:

Tool nameDescription
launch_appLaunch a desktop application by name.
list_appsReturn a list of known launchable apps.
spotify_playPlay a track or playlist by search query.
spotify_pausePause playback.
spotify_nextSkip to next track.
spotify_prevGo to previous track.
spotify_queueQueue a track by search query.
switch_audioSwitch default audio output device by name.
browse_urlOpen a URL in the default browser.
create_taskCreate a task with optional due date and priority.
create_eventCreate a calendar event with datetime and duration.
web_searchWeb search — returns text results.
web_researchDeep research query — structured result with sources.
dataset_searchFind public datasets matching a query.
web_fetchFetch and return the text content of a URL.
web_download_fileDownload a file from URL to a local path.
browser_openOpen a URL in a controlled browser session.
browser_readFetch the rendered DOM text of a URL.
workspace_readRead a file from the workspace directory.
workspace_read_base64Read a binary file as base64.
workspace_writeWrite text content to a workspace file.
workspace_write_base64Write binary content (base64) to a workspace file.
list_skillsList all available skill definitions.
create_agent_taskCreate a delegated agent task with a description.
take_screenshotCapture a screenshot.
get_active_windowGet the name of the currently focused window.
find_text_on_screenOCR — find text at screen coordinates.
click_atSimulate a mouse click at x, y.
type_textType text via keyboard simulation.

System controls

The following tools map to backend/services/system_controls.py:

ToolArgs
get_volumenone
set_volumelevel: int (0–100)
mute_audionone
unmute_audionone
get_brightnessnone
set_brightnesslevel: int (0–100)
lock_screennone
turn_off_displaynone
sleep_systemnone
get_clipboardnone
set_clipboardtext: str
get_system_infonone

Workspace tools

All workspace operations are scoped to the configured workspace root (WORKSPACE_ROOT in .env). Paths outside the root are rejected.

tool call examples
{"tool": "workspace_read",  "args": {"path": "src/main.py"}}
{"tool": "workspace_write", "args": {"path": "output.txt", "content": "hello world"}}
{"tool": "workspace_read_base64",  "args": {"path": "assets/logo.png"}}
{"tool": "workspace_write_base64", "args": {"path": "export.png", "content_base64": "iVBOR..."}}
📌
All workspace tool calls are written to the audit log (AuditLog table) with the tool name, args, and result length.

Browser tools

browser_open opens a URL in a managed headless browser session.browser_read fetches and returns rendered page text (DOM extraction, not raw HTML). Both calls are audited.

tool call example
{"tool": "browser_read", "args": {"url": "https://docs.python.org/3/library/asyncio.html"}}
// Returns up to 4000 chars of the rendered page text

Screen tools

Screen tools use the screen_perception service for screenshot, OCR, and UI automation. Results are truncated to 200 characters and JSON-encoded.

tool call example
{"tool": "take_screenshot",       "args": {}}
{"tool": "get_active_window",     "args": {}}
{"tool": "find_text_on_screen",   "args": {"text": "Submit"}}
{"tool": "click_at",              "args": {"x": 540, "y": 320}}
{"tool": "type_text",             "args": {"text": "Hello world"}}

Calendar and tasks

tool call examples
{"tool": "create_task", "args": {
    "title": "Review pull request",
    "due": "2025-06-15T09:00:00",
    "priority": "high"
}}

{"tool": "create_event", "args": {
    "title": "Team standup",
    "datetime": "2025-06-10T09:30:00",
    "duration": 15
}}

Both tools write to the SQLite tasks and calendar_eventstables and are automatically surfaced by the scheduler'scheck_upcoming_events() and check_overdue_tasks().