Feat/agon everywhere#232
Merged
Merged
Conversation
The v1 HeadlessTurnBrainClient does one engine dispatch per turn and declares
clientCapabilities:'unsupported'. This adds the v2 brain the BrainClient contract
always anticipated: a bounded ReAct loop that pulls client-lent tools mid-turn.
- registerCapability/unregisterCapability store tools a client (the browser panel)
lends the brain; runTurn runs the loop: dispatch engine → parse a __AGON_TOOL__
{name,input} sentinel → yield capability-request → await provideCapabilityResult
→ feed the result back into the transcript → repeat → final answer.
- Destructive tools (CapabilitySpec.isDestructive) first yield approval-request and
await provideApproval; approve-session/deny-session are remembered for the turn-
brain so the same tool isn't gated twice. 'abort' ends the turn.
- Engine-agnostic: parseAgentToolCall is forgiving (tolerates prose/fences, a
garbled/absent sentinel reads as a final answer). Bounded by MAX_AGENT_STEPS;
every capability/approval await is abort-aware and times out (no hung brain).
- An agent screenshot (a 'data:' result) is decoded and shown to the engine as
vision on the next dispatch, not dumped as text.
- Declares clientCapabilities:'supported', approvalArbitration:'host-only'.
24 unit tests: the loop (read tool, approval gate, deny/abort, approve-session,
unknown tool, step limit), the control surface (acks, detach cleanup, mid-turn
cancel), and the pure helpers (forgiving sentinel parse, prompt/transcript build).
⚔️ Forged by [Agon](https://github.com/KERNlang/agon)
Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…ts + bind the agentic brain agon serve now binds AgenticTurnBrainClient, and AgonServe routes the agent control plane so the browser panel can lend tools and answer the brain mid-turn. - AgonServe: POST /register-capability, /unregister-capability, /capability-result, /approval. These call the brain DIRECTLY (never chained on turnTail), so a capability-request the live turn is awaiting can be answered while handleSend still holds the per-session write lock — the no-deadlock property the design depends on. - The turn drain flushes the ledger immediately after a capability-request/ approval-request so it reaches the panel without the ~50ms coalesce wait. - Client-supplied capability specs are validated (string name+description) before reaching the brain; the approval decision enum is validated too. - serve.kern binds the agentic brain (degrades to single-dispatch with no tools) and trims the base system prompt — the agent role + tool protocol now come from buildAgentSystemPrompt; the old "you cannot click or type" guidance is gone. serve-command tests: control-endpoint validation (a valid register returns 'accepted', proving the agentic brain is bound, not the 'unsupported' v1), and an end-to-end wire round-trip proving /send blocks on a capability-request (over SSE) until /capability-result lands — no deadlock. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…lient ownership 6-engine agon review of the agent brain + wire. Real findings fixed; false positives dismissed with reasoning. - FAIL-SAFE approval gate (claude, important): the gate now keys on !isReadOnly, not the client's isDestructive flag. A tool must be EXPLICITLY read-only to skip approval — a mis-/under-declared mutating tool is gated, never silently run. The engine-facing label is aligned to isReadOnly so a mutating tool is never shown as "(read-only)". Since the engine picks tools partly from untrusted page text, this closes a prompt-injection bypass of the "ask before acting" gate. - Per-client OWNERSHIP enforcement (codex 0.97/0.98, kimi 0.85): provideCapabilityResult and provideApproval now accept a reply ONLY from the client the request was routed to (capability owner / turn submitter) — enforcing the declared host-only arbitration instead of merely advertising it. A second token-holding client can no longer spoof another's tool result or approve its destructive action; a mismatch is rejected WITHOUT consuming the pending entry. unregisterCapability is ownership-checked too. - Spec size cap (minimax): a registered capability spec is bounded at 4 KiB so it can't bloat every subsequent dispatch's system prompt; inputSchema render is sliced. Dismissed (verified): close() timer "leak" (close aborts controllers first, firing onAbort→clearTimeout), description-mandatory mismatch (it's required by the type), timeout-ends-turn (deliberate fail-fast: a client timeout means the browser is gone, not engine-recoverable like deny/unknown). Two new tests: the fail-safe gate (a tool with neither flag is still gated) and ownership (a non-owner client's result is rejected; the owner's is accepted). ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…ool call Field report: glm-5.2 replied "Let me navigate to your LinkedIn profile…" with NO __AGON_TOOL__ line, so the loop treated the prose as a final answer and ended — the agent SAID it would act and then did nothing. This is the weak-engine tool-call reliability risk; two mitigations: - Far more forceful agent protocol prompt: explicit "do NOT narrate then stop — narration does nothing, only a tool line acts", with concrete __AGON_TOOL__ examples. - Narration recovery in runTurn: when a reply has no tool call but looksLikeActionIntent (a short "Let me…/I'll…" + an action verb, head-only match so a real prose answer isn't caught), nudge the engine to actually emit the tool line instead of ending. Bounded by MAX_NARRATION_RETRIES (2) so a chatty engine can't loop; after the budget it surfaces the prose as the answer. 3 new tests: looksLikeActionIntent (preamble vs real answer), the nudge→act path, and the give-up-after-budget path (no infinite loop). Engines that DO emit tool calls (claude/codex) are unaffected — the nudge only fires on no-tool + action-intent. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
The browser engine selector was fed registry.listIds() — every engine definition on disk, including ones with no API key/binary and ones the user explicitly hid or removed. Switch to registry.activeIds(config): available (an API key env var is set OR the CLI binary is on PATH) AND not in hiddenEngines/removedEngines, honoring engineActivationMode. Canonicalize the bound default via resolveId so an alias-started serve (--engine kimi) never double-lists against the canonical id (kimi-for-coding-*), and force-include it on a FRESH array (no in-place mutation of the method's return) so it always shows as the current selection even if its availability check is borderline. 6-engine agon review closed all importants: availableIds ignored the activation/visibility rules; the unshift mutated the method's return; an alias could double-list the same engine. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…`agon serve` A second client of a running `agon serve`: a turn typed in the TERMINAL is answered by serve's agentic brain using your browser. Page-tool calls (navigate/readPage/screenshot/click/type) route to the side panel that registered them — independent of who submitted the turn — so they run in your Chrome. Approvals route to the submitter (us): a page-changing action prompts in the terminal (y/a/n/s), or --auto-approve; read-only tools need no approval. A blocking POST /send drives the turn while an SSE /events reader renders live events and answers our approval-requests. The connection is auto-discovered from the 0600 serve files (or --url/--token). Requires the side panel open + attached — that is where the page tools live; with none registered the brain just answers in text. BrainEvent 'approval-request' gains an optional targetClientId (mirrors capability-request), stamped to the turn submitter, so the prompt routes to ONE surface — the browser panel skips a terminal-driven turn's approval. Hardened: SSE failure surfaced (the turn no longer runs blind), /approval res.ok checked, non-TTY stdin denied rather than hung, the post-send drain waits for the answer (bounded), frame parsing stays O(n), and the real session id comes from /attach. 26 unit tests for the pure helpers (discovery, SSE framing, rendering, approval routing). ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
… Cesar `/chrome <task>` in the REPL drives a browser-agent turn and renders it inline. The bridge is resolved automatically: reuse a live `agon serve` if one is up, else EMBED one in-process (the same agentic brain) on an ephemeral loopback port and write the 0600 connection file the extension's side panel auto-connects through — so there's no separate `agon serve` terminal. The embedded brain is the agentic ReAct brain, never Cesar, so a browser turn can't write the live session (the split-brain reservation); its RESULT is fed back to Cesar instead (the /research, /council continuation path). Page-changing actions are approved through the REPL's own permission-ask Y/N UI (the mechanism Cesar already uses), not a raw prompt; read-only tools (read/screenshot) run without asking. Reuses agon drive's two-connection client (blocking /send + SSE tail) with REPL-native render + approval. Wired across the REPL surfaces (SLASH_COMMANDS, parser, dispatch case, handler export). The embedded bridge is a re-entrancy-safe per-REPL singleton with synchronous exit cleanup; the origin allowlist is restricted to chrome-/moz-extension://, the live-bridge probe is bounded + identity-checked (sessionId match), every bridge fetch is abort-linked, the approval prompt is raced against abort (no Ctrl-C hang), and a no-result turn never feeds Cesar stale context. 6-engine agon review: all blocking + high-confidence findings closed. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.