feat(code): local in-process code mode + agentic-loop fixes - #409
Open
rschardosin wants to merge 11 commits into
Open
feat(code): local in-process code mode + agentic-loop fixes#409rschardosin wants to merge 11 commits into
rschardosin wants to merge 11 commits into
Conversation
Add `astonish code`: a fully local, in-process coding tool (like Claude Code / opencode) that runs the wired ChatAgent in the same process, executes built-in tools directly against the host filesystem, and never contacts a platform. Sandbox is forced off; safety comes from the per-tool approval prompt. Highlights: - RunCodeTUI + localAgentBackend driving the ADK runner in-process, reusing the shared SSE->event translator (mapSSEToEvents). - Opens without a model configured: placeholder LLM + in-TUI /model and /provider overlays that persist provider config to the local config file only (never a database). ProviderAdminBackend is an optional capability, so platform chat is unaffected. - Loads AGENTS.md/CLAUDE.md project guidance (agents.md convention) into the system prompt, discovered by walking up to the git root. - Redirects stdlib log / slog away from the alt-screen TUI so background log lines (e.g. ADK "unknown agent" warnings) don't corrupt the UI. Agentic-loop performance and correctness fixes (informed by analyzing real slow sessions): - Put tree-sitter navigation (repo_map, code_definition, code_references) on the main-thread tool allowlist and add a conditional "code navigation MUST" prompt rule; grep_search stays domain-agnostic. - Soft-cap unbounded read_file on large files to curb context bloat. - Fix must-read-before-edit false positive: read/edit/write all count as "seen", so consecutive edits no longer force needless re-reads. - Estimate context tokens locally when the provider reports no usage, so the header shows real utilization instead of "Context 0" (estimates update context occupancy but not cumulative usage). Also includes checkpoint/rollback groundwork and plan-mode enforcement. Docs and tests updated across affected packages.
- Show '✦ Astonish Code' title, working directory, and accurate tool-approval notice (auto-approve aware) in the code-mode welcome card - Add WorkingDir to tui backend.Info, populated by the local code backend - Include unrelated in-progress tool/tool-activity and plan-mode changes
Fix pasted images silently failing in local `astonish code` sessions, plus fit the welcome card on one line. Image paste pipeline fixes: - localAgentBackend.RunTurn dropped opts.Attachments and sent a text-only user message, so pasted images were collected in the composer but never reached the model. Build the user message via agent.NewTimestampedUserContentWithAttachments (base64 through the new agentAttachmentsFromBackend helper), matching the platform path. - An empty textarea.pasteMsg (Ctrl+V with an image-only clipboard) was rejected by the text-only detector and fell through as a no-op. Recognize the paste regardless of emptiness and route empty ones to tryPasteImage; swallow truly-empty pastes. - macOS clipboard reader rewritten from the fragile `the clipboard as «class PNGf»` AppleScript coercion to NSPasteboard via JXA + AppKit. The old coercion silently failed for some images (large, or lacking a public.png representation) — the tell was that resizing an image made paste start working. JXA reads public.png directly and re-encodes any other representation to PNG. Welcome card: - Shorten the code-mode approval notice and bottom hints so neither wraps onto a second line. Tests: add coverage for agentAttachmentsFromBackend and the paste-msg detector; update welcome-card assertions. Docs updated in terminal-app.md and pkg/launcher/AGENTS.md.
Add a 'Copy content' option to the EmbeddedFileViewer download dropdown so users can copy a report's raw markdown source. Shows transient 'Copied' feedback and is only offered for text content (not media). Includes unit tests for the copy flow and media exclusion.
Code mode runs on the user's own machine rather than a sandbox container, so the two native prerequisites are now provisioned automatically instead of being assumed pre-installed. Tree-sitter: embed the grammar C sources (~1.4 MB gzip) in the binary and compile libastonish-treesitter on first use, caching it under the user's config dir. The resolver tries an explicit override, next-to-exe, CWD, the built cache, and the container path before building. No compiler yields an actionable error and a grep fallback. `make treesitter-embed` regenerates the tarball. Ripgrep: new pkg/tools/ripgrep provisions rg — prefer a system rg, else download the pinned official 14.1.1 release for the host OS/arch, verify its SHA256 against a checked-in manifest, and cache it under the config dir. grep_search and find_files resolve rg through it; code mode kicks provisioning off in the background at startup. grep_search is now ripgrep-only: the pure-Go grep fallback (not gitignore-aware, no type/multiline/context support) is removed, since rg is guaranteed available. Also includes code-mode context-usage/resume work: estimated context fallback when providers report no usage, context-on-resume seeding, and related TUI plumbing. Tests cover the tree-sitter build+cache and resolver, ripgrep download/verify/ extract with checksum-mismatch rejection and PATH preference, and the estimated context transcript behavior. Docs updated (code-intelligence, terminal-app, tools AGENTS).
- Esc now cancels the streaming turn (Claude Code / OpenCode style) via shared cancelInFlightTurn; idle Esc never quits (Ctrl+C still quits idle). - Streaming footer hint shows 'esc cancel · ctrl+c cancel'. - /help now documents esc (cancel turn), ctrl+d (quit when empty), the /? alias, and stays in sync with the command palette. - Expand code-mode /provider catalog (SAP AI Core, LiteLLM) with a drift guard; add throttled mid-turn context estimate so the header advances during long tool loops. - Add tests (cancel, help sync, provider catalog) and update terminal-app docs + pkg/tui/AGENTS.md.
- Add process_read/write/kill/list to mainThreadToolAllowlist so the top-level coding agent can drive interactive PTY commands (waiting_for_input) directly, matching chat mode. - Disable the auto-pager in shell_command's PTY (PAGER/GIT_PAGER=cat, GIT_TERMINAL_PROMPT=0) so git/less-style CLIs don't hang; PTY interactivity is preserved. - Make shell_command cancellable: waitForShellSession selects on ctx.Done() and kills the child on Esc/turn cancel instead of waiting out the timeout. - Keep the TUI event loop responsive under bursts: per-item markdown render cache (keyed by width+content, cleared on resize) and bounded event coalescing that repaints once per batch. - Tests + architecture/AGENTS docs updated.
…ter diff, and supporting fixes Plan mode & planning methodology (Option B): - Rewrite PlanModeSystemContext with an investigate -> design -> verify methodology and a completeness bar; keep the hard-gate rules intact. - Add first-class per-phase Files ([]PlanFileChange) and Verify fields to announce_plan / PlanStepInfo / PlanState, rendered and round-tripped losslessly in PLAN.md (plan_document.go). - Strengthen the delegation "Planning strategy" block (dependency tracing + final verification discipline); regenerate the system-prompt golden file and bump the prompt-size ceiling accordingly. - Sync the TUI mirror (planModeSystemContext) and enrich guidanceCodeIntelligence. - Tests: plan_document_test, plan_state_test, plan_persistence_test, plan_tool_test, plan_mode_gate_test, system_prompt_contracts_test. TUI single-gutter file diff: - Collapse the dual old/new gutter into one line-number column colored by change kind (neutral/red/green); fix prefix/gap/continuation width math. - Update render tests and all "dual-gutter" doc comments to "single-gutter". Supporting changes accumulated on this branch: - Session PLAN.md persistence + compaction survival; sidecar cleanup on delete. - MCP file-based config, provider settings/local providers, tools cache, and related API/launcher wiring + tests. - Docs: plan-mode-enforcement-summary, terminal-app, smart-compaction, mcp, tools, TESTING, mcp-servers; add docs/analysis session review. Verified: go build ./..., package tests, golangci-lint (0 issues), tsc --noEmit.
Add safe-by-default authorization to `astonish code` (unsandboxed, runs on the host). Two BeforeToolCallback gates, active only in Normal mode when EnforceAuthorization is set and bypassed by --auto-approve/--yolo: - Tool-execution gate: agent.SafeTools is reused as the Normal-mode auto-allow baseline; any other tool (write_file, edit_file, shell_command, ...) prompts for authorization. Grants: Allow (once) / Always Allow (session) / Deny. - Folder-access gate: tools may only touch the project working dir and its subtree; out-of-project paths prompt. Path args extracted generically (path/file_path/working_dir/dir/search_path/paths) plus heuristic operands from free-form command args via new pkg/pathscope (single source of truth for extraction + containment). Grants: Allow (once) / Always Allow (session) / Deny. TUI overlay: cursor-navigable Allow / Always Allow / Deny list (up/down or j/k, Enter submits the highlighted option, default cursor on the safe first option; 1/2/3 and y/esc remain accelerators). Fix: one-shot folder "Allow" grants are now consumed at execution time via ConsumePathGrants, so "Allow" no longer silently behaves like "Always Allow" on later accesses to the same out-of-project path. Enforcement + grant bookkeeping live in pkg/agent/tool_authorization.go; the launcher threads WorkingDir + EnforceAuthorization and resets iteration grants per real user turn (IsApprovalResponse flag). Updates system prompt, terminal-app.md, plan-mode-enforcement-summary.md, README, and the pkg AGENTS.md notes. Regression tests added.
The folder-access gate mis-flagged commands whose QUOTED literal data merely contained path characters. splitCommand word-split on whitespace and shell operators even inside quotes, so a commit message like `git commit -m "fixes A / B"` was shredded and the bare "/" lifted out as a spurious out-of-project path operand — producing a false "wants to access files outside the project directory" prompt (and, via SetScopeRoot, a false hard reject for non-interactive callers). Fix: make splitCommand quote-aware. Single/double-quoted spans are one atomic token and quote marks are consumed; adjacent quoted/unquoted runs join like a real shell word. A quoted literal (message/prose/pattern) is now flagged only when its content is itself path-shaped: - `git commit -m "fixes A / B"` -> no path extracted (no prompt) - `cat "/etc/passwd"` -> still flagged (/etc/passwd) Also: skip the key=value env-assignment split for tokens containing whitespace (quoted literals may legitimately contain '='), and remove the now-redundant trimQuotes helper (quotes consumed by the tokenizer). Because pathscope is the single source of truth, this corrects both the interactive agent folder-access gate (pkg/agent) and the non-interactive shell guard (pkg/tools SetScopeRoot) at once. Security is unchanged for unquoted path shapes; the shell-in-shell (`sh -c "..."`) limitation is pre-existing and documented. Adds 9 regression cases to pathscope_test.go and updates terminal-app.md plus the pkg/agent and pkg/tools AGENTS.md notes.
…ools Add a full TUI overlay for configuring web search providers (Tavily, Firecrawl, Brave Search, Perplexity) directly from astonish code mode. Key changes: - Fix loadMCPConfig to accept *config.AppConfig so non-platform mode properly passes config through to mergeStandardServersWithConfig, enabling configured web search MCP servers to be loaded. - Add WebSearchAdminBackend interface to backend/backend.go with methods for listing providers, get/set current provider, clear, and list Perplexity models. - Implement WebSearchAdminBackend on localAgentBackend in tui_code.go handling config.yaml persistence and MCP server management. - Create web_search_picker.go with full TUI overlay (provider list, API key input, Perplexity model selection, clear option). - Wire /websearch slash command into app.go (keyboard dispatch, message handlers, overlay rendering, help text). - Fix MCP tool schema discovery to check ParametersJsonSchema first (used by ADK MCP tools) before falling back to Parameters field, fixing tools being cached without schemas. - Add unit tests for backend implementation and picker component. Users can now configure web search tools from within the TUI and they become available to the agent after starting a /new session.
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.
Summary
Adds
astonish code— a fully local, in-process coding tool (in the spirit of Claude Code / opencode / grok CLI). The single binary runs the wiredChatAgentin the same process and calls its built-in tools directly against the host filesystem in the working directory. There is no daemon, no HTTP, and no login. The sandbox is forced off; safety comes from the per-tool approval prompt (--auto-approve/--yoloto bypass).It reuses the existing agent runtime, TUI, providers, and event pipeline — this is plumbing, not a second agent implementation. Platform chat (
astonish chat) is unchanged.What's included
Code mode
RunCodeTUI+localAgentBackenddrive the ADK runner in-process, reusing the shared(type,data)→events.Eventtranslator (mapSSEToEvents) so the TUI renders identically to platform mode with no new mapping code./modeland new/provideroverlays configure the model/providers and persist to the local config file only (~/.config/astonish/config.yaml) — never a database. Provider management is exposed via an optionalProviderAdminBackend, so the platform backend is untouched.AGENTS.md(fallbackCLAUDE.md) following the agents.md convention — walking up from the working dir to the git root, nearest-wins, plus a global file — into the system prompt.log/slogaway from the alt-screen so background log lines (e.g. ADK "unknown agent" warnings) can't corrupt the UI;--debugsends them tocode-debug.log.Agentic-loop performance & correctness fixes
Informed by analyzing real slow/failing sessions:
repo_map,code_definition,code_references) plus a conditional "code navigation MUST" prompt rule.grep_searchstays domain-agnostic (no code advice in its description).read_filesoft-cap on large unbounded reads to curb context bloat (the dominant cause of slow inference).Context 0(estimates update context occupancy but are not accumulated into cumulative usage).Also included
Testing
go build ./...passes.golangci-lint run— 0 issues (also enforced by the pre-commit hook).Notes for reviewers
docs/architecture/terminal-app.md(code mode) anddocs/architecture/code-intelligence.md(tree-sitter main-thread + prompt rule).