Skip to content

wmh run: host-answered github_search connector tool#192

Closed
kfallah wants to merge 2 commits into
mainfrom
feat/cli-connector-tools
Closed

wmh run: host-answered github_search connector tool#192
kfallah wants to merge 2 commits into
mainfrom
feat/cli-connector-tools

Conversation

@kfallah

@kfallah kfallah commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What

Wires a github_search tool into wmh run so the built-in pi agent can pull GitHub context mid-run. It's the CLI counterpart to platform#340 (same host-answered tool in the hosted runner), consuming the wmh.connect library from #174.

export WMH_GITHUB_TOKEN=ghp_...
wmh run --task "summarize the open bugs in octocat/hello"

How (and why no secret reaches the sandbox)

Answered host-side: wmh run's LocalLiveDriver (in the CLI process) resolves the GitHub token from the environment, runs wmh.connect get_connector("github").pull(...), and returns the rendered ContextItems as the observation. On --harness-backend e2b the executor runs in the CLI process (the sandbox only carries frames), so WMH_GITHUB_TOKEN never enters the sandbox. Bare-local has no sandbox; the token stays local as usual.

  • wmh/harness/connector_tools.pyGITHUB_SEARCH ToolSpec; github_search_available(); github_search_fetch() rendering items into a capped observation, missing-token / ConnectError → clean is_error outcome (token never in the observation).
  • wmh/cli/agent_session.py_assemble gains extra_tools (appended as ToolSpec objects past the closed-set resolve_tools gate); LocalLiveDriver offers github_search only when a token resolves and dispatches it to the host-side fetch before the local jail executor.

Test plan

Full gate green: ruff, ty, pytest (1,965 passed). Inline tests (no network): render + capping, missing-token / ConnectError clean errors, token never in observation; tool offered iff a credential resolves; _execute dispatches github_search host-side, never the jail executor.

Scope

GitHub only, mirroring #340; Google/Slack/Notion/Brave are the same additive pattern. Token from WMH_GITHUB_TOKEN; no wmh connect CLI is added here.

🤖 Generated with Claude Code

The built-in pi agent can call github_search during `wmh run`. The CLI
process (the host) answers the tool call, resolving a GitHub token from
the environment and fetching via wmh.connect, then returns the rendered
items as the observation. On --harness-backend e2b the executor runs in
the CLI process (the sandbox only carries frames), so the token never
enters the sandbox: the CLI counterpart to platform#340's host-answered
model, using the wmh.connect library from #174.

- wmh/harness/connector_tools.py: GITHUB_SEARCH ToolSpec,
  github_search_available() (true iff a credential resolves host-side via
  WMH_GITHUB_TOKEN or a stored connector credential), and
  github_search_fetch() rendering ContextItems into a capped observation;
  missing token / ConnectError map to a clean is_error ToolOutcome.
- wmh/cli/agent_session.py: _assemble gains extra_tools (appended as
  ToolSpec objects past the closed-set resolve_tools gate); LocalLiveDriver
  offers github_search only when a token resolves and dispatches it to the
  host-side fetch before the local jail executor.

Co-Authored-By: Claude Fable 5 <[email protected]>
@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR wires a github_search connector tool into wmh run so the built-in pi agent can query GitHub issues/PRs mid-session. The token is resolved host-side in the CLI process and never forwarded to the sandbox — a clean security boundary.

  • wmh/harness/connector_tools.py (new): packages the GitHub connector as a ToolSpec + availability check + host-side fetch with capping, missing-credential, and ConnectError error paths all returning clean is_error outcomes.
  • wmh/cli/agent_session.py: _assemble gains extra_tools (bypasses the closed-set resolve_tools gate); LocalLiveDriver adds github_search only when a credential resolves and dispatches it before the jail executor.

Confidence Score: 5/5

Safe to merge — the token stays in the CLI process throughout, the new module is well-tested offline, and the changes to agent_session.py are additive with no effect on existing tool dispatch paths.

The security boundary is correctly maintained: the GitHub token is resolved and consumed entirely in the CLI process, and github_search_fetch is dispatched before the jail executor so it never reaches the sandbox. Error paths are each tested without network access.

No files require special attention — connector_tools.py and the agent_session.py changes are straightforward and well-covered by the new tests.

Important Files Changed

Filename Overview
wmh/harness/connector_tools.py New module implementing the host-side github_search tool; credential resolution, observation capping, and error paths are all well-handled and tested.
wmh/cli/agent_session.py _assemble gains extra_tools, _connector_tools gates on availability, and _execute dispatches github_search host-side before the jail executor — correct security boundary.
wmh/harness/connector_tools_test.py Comprehensive offline tests covering render, capping, missing/corrupt credentials, ConnectError, limit coercion, and token-not-leaked invariant.
wmh/cli/agent_session_test.py New tests confirm tool gating, assembly injection, and host-side dispatch; integration with existing test suite is clean.
docs/reference/connect-library.md Accurate documentation of the new github_search live tool, security model, and argument set.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as CLI Process (LocalLiveDriver)
    participant Runner as Pi Runner (Node.js)
    participant LLM as LLM API (host-side bridge)
    participant GH as GitHub API

    CLI->>Runner: "session_start {system, tools=[bash,...,github_search], files}"
    Runner->>LLM: "llm_request {openai_body with github_search in tools}"
    LLM-->>Runner: tool_call: github_search(target, query)
    Runner->>CLI: "tool_request {name:github_search, arguments:{...}}"
    Note over CLI: _execute dispatches host-side (token never sent to runner)
    CLI->>CLI: load_connector_auth(github) token from env
    CLI->>GH: connector.pull(auth, query)
    GH-->>CLI: ContextItem[]
    CLI->>CLI: _render_items() capped observation
    CLI-->>Runner: "tool_result {content: rendered items}"
    Runner->>LLM: llm_request (continues turn)
    LLM-->>Runner: submit / next action
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI as CLI Process (LocalLiveDriver)
    participant Runner as Pi Runner (Node.js)
    participant LLM as LLM API (host-side bridge)
    participant GH as GitHub API

    CLI->>Runner: "session_start {system, tools=[bash,...,github_search], files}"
    Runner->>LLM: "llm_request {openai_body with github_search in tools}"
    LLM-->>Runner: tool_call: github_search(target, query)
    Runner->>CLI: "tool_request {name:github_search, arguments:{...}}"
    Note over CLI: _execute dispatches host-side (token never sent to runner)
    CLI->>CLI: load_connector_auth(github) token from env
    CLI->>GH: connector.pull(auth, query)
    GH-->>CLI: ContextItem[]
    CLI->>CLI: _render_items() capped observation
    CLI-->>Runner: "tool_result {content: rendered items}"
    Runner->>LLM: llm_request (continues turn)
    LLM-->>Runner: submit / next action
Loading

Reviews (2): Last reviewed commit: "Surface the real error for a corrupt git..." | Re-trigger Greptile

Comment thread wmh/harness/connector_tools.py
Greptile P1: github_search_available() offered the tool on a
ConnectError (a present-but-unusable credential), promising the fetch
would report the real error, but the fetch swallowed the same
ConnectError and returned the misleading "not configured" message. The
fetch now distinguishes an absent credential (-> "set WMH_GITHUB_TOKEN")
from a corrupt one (-> "credential is invalid: <reason>"), so
availability and fetch agree. Dropped the now-dead _resolve_auth helper;
added a regression test for the corrupt-credential path.

Co-Authored-By: Claude Fable 5 <[email protected]>
@kfallah

kfallah commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Closing as out of scope for the current WMH development stack.

@kfallah kfallah closed this Jul 20, 2026
@kfallah
kfallah deleted the feat/cli-connector-tools branch July 20, 2026 19:04
@kfallah

kfallah commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Closed: we do not want a dedicated per-provider connector tool (github_search) in the CLI. The runtime-consumption tool shape is being reconsidered; the wmh.connect library (#174) stands on its own for any future consumption mechanism. Branch deleted; recreate from #174's base if revisited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant