Skip to content

th-b00115: hook-capable harnesses — gemini, qwen, cursor, copilot, droid, amp, pi - #600

Open
brentrager wants to merge 3 commits into
mainfrom
th-b00115-harness-hooked
Open

brentrager wants to merge 3 commits into
mainfrom
th-b00115-harness-hooked

Conversation

@brentrager

@brentrager brentrager commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Problem

SmoothFlow had four built-in harnesses. Brent asked for broader harness support, using cmux and orca as reference. This lane (harness-hooked, epic th-faa590) covers the CLIs that report real lifecycle signals (hooks, plugins, extensions), so their state comes from the CLI rather than from pane scraping.

What ships

  • 7 built-in manifests (crates/smooth-flow/harnesses/): gemini, qwen, cursor-agent, copilot, droid, amp, pi. Each has binary resolution that skips cmux shims, launch/resume argv, session-id mode, steer, and kill. Scrape patterns come from real boot screens where captured.
  • State wiring in the smooth-agent package: hooks overlays for gemini/qwen/droid/copilot/cursor, an Amp plugin (harness/amp/plugin.ts), and a Pi extension (harness/pi/extension.ts).
  • th pkg: gemini, qwen, droid, copilot, amp, and pi are new hook-only --harness targets (included in all). Cursor hooks merge into ~/.cursor/hooks.json through a flat-entry-aware merge. rm and status are exact.
  • Engine
    • Agent panes carry SMOOTH_FLOW_ID, and hooks post it back as flow_id. That binds CLIs that can't pre-assign a session id (Cursor, Droid, Amp) to the right row, with guards against nested or child agents.
    • Only the Claude table holds PermissionRequest open (so Droid's hook isn't blocked for 120 s). A mapped permission ask gets a hook-<uuid> id that flow.approve answers by keystroke. This overlaps th-3cabf6: harness conformance contract + th harness doctor #598 (th-3cabf6), which fixed the same un-approvable-ask bug. This PR now uses th-3cabf6: harness conformance contract + th harness doctor #598's exact rule: the id is assigned in the pure mapper, permission only, a question gets none. The delta kept here: the permission label is derived from the event name or notification_type. Gemini's ToolPermission and Droid's PermissionRequest send no reason, so th-3cabf6: harness conformance contract + th harness doctor #598 alone leaves them unapprovable. Whichever PR lands second takes a trivial textual conflict in map_manifest_event/mapped_outcome.
    • Steer now honors steer.submit_key and a new steer.submit_delay_ms. Gemini turns an immediate Enter into a newline; measured live, it needs at least 150 ms.
    • flow-hook.sh reads sessionId, conversation_id, and workspace_roots, and pre-answers stdout for harnesses that parse it.
  • Docs: a reference matrix in docs/Engineering/Harness-Manifests.md (orca + cmux file:line citations, plus each CLI's current docs), the design choices, and a proof table. The Harness-Packages table is updated.

Deliberately not copied from orca: answering Cursor's gate hooks with {"permission":"allow"}, which auto-approves every tool call.

Verification

  • cargo test -p smooai-smooth-flow: 156 passed. New tests cover the event table for every mapped event, flow_id binding rules, the held-vs-keystroke ask split, per-manifest launch/resume/resolve, and the steer knob.
  • cargo clippy adds no new warnings in touched files. cargo check --tests passes for smooth-cli and smooth-daemon.
  • Not run locally: the smooth-cli pkg tests and smooth-daemon route tests. Disk was at 6-11 GB machine-wide, too little to codegen th/the daemon, so CI is the first run of those.
  • flow-hook.test.sh 36/36; flow-plugins.test.mjs (Amp/Pi) ok.
  • Live, through a private SmoothFlow daemon (bundled 0.48.0, scratch HOME, port 8799, own tmux socket; Brent's daemon untouched):
    • Pi 0.85.1: launch, working, idle, steer, kill+resume on the same --session-id (conversation continued), and kill all proven from hooks.
    • Qwen 0.23.4: launch, idle, steer, resume, and a held PermissionRequest approved via th flow approve (command ran with no dialog) all proven.
    • Gemini 0.59.0: working/idle and the ToolPermission needs_you proven. This run exposed the steer-submit and un-approvable-ask gaps fixed here. The fixes are unit-tested; they were not re-run through a rebuilt daemon.
    • Cursor Agent, Copilot, Droid, Amp: parse/resolve/event-tested only. Each stops at a sign-in wall that was not pressed.

Known pre-existing flakes on origin/main (not this PR): opencode/plugin.test.mjs event ordering fails about half the time, and close_closes_the_pearl_through_th races on the process-global SMOOTH_TH_BIN.

Pearl: th-b00115 · Epic: th-faa590

🤖 Generated with Claude Code

brentrager and others added 2 commits September 14, 2026 17:27
…t, copilot, droid, amp, pi

SmoothFlow knew four CLIs. These seven expose real lifecycle signals (hooks,
plugins, extensions), so each gets a built-in manifest plus a th pkg overlay
that maps its native events onto /api/flow/hooks.

Engine: panes carry SMOOTH_FLOW_ID and hooks post it back as flow_id, so a
learned-id harness binds to its row without cwd guessing; only the Claude
table holds PermissionRequest open (a mapped ask gets a hook- id answered by
keystroke); steer honours steer.submit_key and the new submit_delay_ms
(Gemini folds an immediate Enter into the paste).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reference matrix (orca + cmux file:line citations and each CLI's current
docs), per-harness design choices, the engine changes, and exactly which
harnesses were proven live vs parse-tested.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3a56fcd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/smooth Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager enabled auto-merge (squash) September 14, 2026 21:32
Both lanes fixed un-approvable hook asks. Adopt #598's rule (id assigned in
the pure mapper, permission asks only; a question gets none) so the two merge
cleanly. The delta kept here: the permission label is derived from the event
name / notification_type, and only Claude-protocol harnesses hold
PermissionRequest open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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