Changelog
The canonical changelog lives in the CLI package — cli/CHANGELOG.md — and is rendered below. Entries under Unreleased are shipping on the main branch and not yet tagged.
All notable changes to Glue CLI will be documented in this file.
[Unreleased]
Added
- MCP OAuth that actually works on real servers. Glue now follows the current MCP authorization spec end-to-end (RFC 9728 protected resource metadata + RFC 8414 authorization server metadata + RFC 7591 DCR + PKCE), with the bits that make it usable in practice:
- Auto-detect on 401. A remote MCP server returning
401 Unauthorizedwith aWWW-Authenticate: Bearer …challenge triggers OAuth automatically — you no longer need to setauth: {kind: oauth}in~/.glue/config.yamlup front. The first successful login writes that back for you. - Silent token refresh. When the access token expires, Glue runs the refresh-token grant transparently and retries the request. The user only sees the OAuth flow when the refresh token itself has expired or been revoked.
McpAwaitingAuthconnection state. Servers that fail with 401 are parked in a new dedicated state — distinct fromMcpDead— that does not consume the reconnect budget or arm a retry timer. Status bar readsMCP:1🔑when the only unhealthy servers are waiting on auth, vsMCP:1⚠for mixed/dead.- Context-aware
/mcppanel actions. Selecting an HTTP/WS server in the/mcppanel surfacesAuthenticate,Re-authenticate, orSign outdepending on credential state. stdio servers never get auth actions. - Auto-reconnect after sign-in. Once the loopback callback captures the authorization code and tokens are stored, Glue immediately reconnects the parked server without any extra command.
- Cached discovery URLs. After the first successful login the server's
resource_metadata_urlandauthorization_serverare written into~/.glue/config.yamlso subsequent sessions skip the discovery probe. - Granular credential invalidation. A new
invalidateMcpAuthhelper drops just the part of the credential that failed (tokens,client, or all), so a refresh-token failure doesn't force a re-DCR. McpAuthFlowRunnershared across the CLI command, the slash command, and the auto-triggered flow — one place that owns discovery → DCR → loopback → token exchange → persist.- Bind-then-register order so DCR registers the exact loopback redirect URL with the bound port. Strict auth servers (SmartBear) that require an exact
redirect_urimatch — RFC 8252 says they shouldn't, many do — now work. With DCR available, Glue re-registers on every login to keep the URI in sync; the cachedclient_idpath is reserved for pre-registered (no-DCR) servers.
- Auto-detect on 401. A remote MCP server returning
AgentNoticeevent inglue_core— provider-neutral soft- degradation signal carrying amessage+kind(info/warning). Rendered as a dim system line in the TUI, sent as a marker-prefixedAgentMessageChunkUpdateover ACP, and printed to stderr with a glyph prefix in--printmode. First user: the tool-less Ollama soft fallback below.ToolsNotSupportedExceptioninglue_core— typed exception thrown byOllamaClientwhen the daemon returns400with a body matchingdoes not support tools, replacing the previous rawException. Other adapters can throw the same shape if they ever expose tool-less models.web_browser.navigateaccepts await_untilparameter so the agent can self-correct:"load"(default),"domcontentloaded"(fastest, DOM parsed),"networkalmostidle"(≤2 in-flight for 500 ms), or"networkidle"(strict). Invalid values are rejected before the browser is provisioned. Implemented inpackages/glue_harness/lib/src/tools/web_browser_tool.dart.
Changed
glue serverenamed toglue acp— the ACP server subcommand now matches the convention used by every other ACP-speaking agent (opencode acp,goose acp, …). No backward-compat alias: editors with"args": ["serve"]in~/.config/zed/settings.json,~/.jetbrains/acp.json, or equivalent must update to"args": ["acp"]. Help text, the WebSocket startup banner, and docs/advanced/acp-server all reflect the new name.- Dart SDK floor bumped to 3.12.0 across every package (
cli,glue_core,glue_strategies,glue_runtimes,glue_harness,glue_server). Enables the 3.12 private-named-parameter syntax and is required for pub workspaces. - Monorepo is now a pub workspace. A root
pubspec.yamllists all six packages and each member opts in withresolution: workspace.dart pub getfrom the repo root resolves the whole tree into a single sharedpubspec.lockand.dart_tool/. Per-packagedart pub getstill works. - Analyzer config consolidated. One canonical
analysis_options.yamlat the repo root; each package'sanalysis_options.yamlis reduced toinclude: ../../analysis_options.yaml. Picks the looserunawaited_futures: falseeverywhere (was mixed) and addsunnecessary_to_list_in_spreads: trueto every package (previously cli-only). - Build output relocated to
<repo>/dist/glue(wascli/dist/bundle/bin/glue).just cli::buildproduces a single AOT binary at the repo root and the intermediatebundle/is cleaned up. CI release workflow renames per-platform (dist/glue-linux-x64, etc.) and uploads from there.just cli::installnow symlinks<repo>/dist/glue→~/.local/bin/glue. - Constructor boilerplate trimmed via Dart 3.12 private initialising formals. ~30 constructors across
cli/,glue_harness,glue_strategies,glue_runtimes, andglue_servermigrated from: _field = fieldinitializer lists tothis._fieldparameters. Call-site API is unchanged (the public parameter name is the field name without the underscore). unawaited(...)wrappers removed inlib/. Withunawaited_futures: falsethe wrappers were pure noise; 17 call sites acrosscli/lib,glue_harness,glue_server, andglue_strategieswere unwrapped. Test files were left alone (intent-signaling use is still useful there).
Fixed
- Tool-less Ollama models no longer crash the agent. When the active model lacks function-calling support (e.g.
ollama/deepseek-coder-v2:16b,ollama/qwen2.5:7b), Glue used to surface Ollama's400 "does not support tools"as a raw exception mid-session. Now the agent loop catches this case, disables tools for the rest of the session, emits a one-line notice into the transcript, and continues in chat-only mode. The same fallback fires at boot when the catalog declares the model withouttoolscapability, skipping the wasted first turn. Subagent spawns with a tool-less model override degrade rather than killing the parent session.glue doctoradds a new info finding ("Agent model") when the active model is catalogued without tool support. web_browsernavigateno longer times out on ad/tracker-heavy sites. Default wait condition changed from Puppeteer's strictestUntil.networkIdle(requires the page to make zero network requests for 500 ms — never resolves on sites with continuous beacons like nrk.no, GA, etc.) toUntil.load(the pageloadevent fires). Matches Playwright's default and the de-facto MCP browser-server convention; Playwright's own docs flagnetworkidleas "DISCOURAGED". Previously, navigating to such sites surfaced aTimeoutException: Navigation Timeout Exceeded: 30000ms exceededto the agent even though the page had visibly loaded and was fully usable.bin/glue servetests no longer break when run from the workspace root.cli/test/bin/glue_serve_test.dartandglue_serve_ws_test.dartpreviously hardcodedDirectory.current.pathas the working dir for the spawnedglueprocess, which fails when the test runner is launched from the repo root viadart test cli. A_cliPackageRoot()helper now resolves the cli package from either cwd.
Removed
- Dormant
OllamaShow+ToolCapabilityProbescaffolding — a 7-lens swarm review of the original hard-fail-with-probe design concluded the runtime catch approach is strictly better (matches Continue.dev, Aider/LiteLLM, Codex, OpenCode; no per-boot/api/showround-trip; no second source of capability truth). The probe code is replaced by the soft-fallback path above. - Dead
win32direct dependency dropped fromcli/pubspec.yaml— it was declared but never imported. (The fourcode_assets/hooks/mason_logger/native_toolchain_cpub-outdated warnings remain: they're pulled in transitively viacli_completion → mason_logger → win32and can't move until those packages bump upstream.)
[0.6.0] - 2026-05-20
Added
- TTY/
NO_COLOR-aware brand markers + colorized MCP / session output — every brand glyph (●,✓,!,✗,·) and every styled string from command output now routes through a newstyledOrPlain()helper that collapses ANSI to plain text when stdout is not a terminal or whenNO_COLORis set.glue mcp list,glue mcp tools,glue mcp auth status,glue mcp add,glue session list,glue session show, andglue session apply/exportnow render with the same brand-dot header, bold ids, and severity-marker status lines asglue catalogandglue doctor. Existing surfaces (--where,catalog *,doctor,serve) had their inline.styledchains migrated tostyledOrPlainso the same piping safety applies everywhere. Output ofglue mcp list | grep enabledis now grep-friendly with no ANSI noise. - CLI output formatting style guide — new
docs/design/cli-output-formatting.mdcodifies the brand vocabulary (●for headers,✓/!/✗/·for status), the four-layer rendering pipeline (Command→*_format.dart→styledOrPlain→Styled), output shape per command class (diagnostic vs action vs pure-config), stdout/stderr discipline, JSON/--print/NO_COLORsemantics, and how to test the formatter. Includes a "drift register" table tracking which surfaces are fully on the spec. Linked fromCLAUDE.mdso new command work picks it up. - OTLP
session.idresource attribute — every Glue invocation now emits a stable per-processsession.id(formatglue-<base36-ts>-<base36-rand>) on every OTLP/HTTP trace export, so observability backends that follow the OpenInference convention (llmflow, Phoenix, Langfuse, Helicone, Opik) can group multiple traces from the same Glue session under one "session" view. Implemented inpackages/glue_harness/lib/src/observability/otlp_http_trace_sink.dartas alate finalfield initialised once per process. /mcp toolsandglue mcp toolsnow list every server when no argument is given — output is grouped by server with per-server status annotations (connectedis unmarked;connecting,reconnecting,disconnected,dead, anddisabledget a tag in parentheses), and a friendly per-server reason when a server has no tools to show (e.g.disabled; enable to list tools). The single-server form (glue mcp tools <id>) still exits 1 when the named server is disabled, so existing scripts that gate on the exit code keep working. CLI and slash share a single formatter incli/lib/src/commands/mcp_tools_format.dart, withformatMcpToolsByServertaking pure value objects so the renderer is unit-testable without spinning up a realMcpClientPool. CLI variant now waits for all selected servers to settle (with a 10 s cap), so the no-arg listing prints a single coherent snapshot rather than only the first server to respond.glue mcp add --helpships five worked examples — Playwright via npx, GitHub via docker with a PAT env var, Context7 hosted HTTP with no auth, GitHub Copilot hosted HTTP with bearer, and a generic OAuth-via-DCR server. Discoverable via the command's description in the help output.- Interactive
/mcppanel — pressingEnteron a server row in the status panel now opens an action submenu with Reconnect, Enable/Disable for this session, View tools, Copy server ID, and Show last error (when a failure is recorded). Actions reuse the pool's existingreconnect()/toggle()methods, so the panel and the slash subcommands stay interchangeable. Tools and error views render as scrollable read-only modals. - Tab completion for
/mcpsubcommands and server IDs —/mcp <TAB>enumerates subcommands,/mcp reconnect|toggle|tools <TAB>enumerates configured server IDs (case-insensitive prefix), and/mcp auth login|logout <TAB>filters down to HTTP/WebSocket servers (stdio can't OAuth). AddsmcpSubcommandCandidates,mcpAuthSubcommandCandidates, andmcpServerIdCandidatestoarg_completers.dart, wired through the standardSlashArgCompleteroverride onMcpSlashCommand. - Automatic MCP reconnect with backoff — when an MCP server's initial handshake or transport fails, the pool now transitions to
McpReconnectingand schedules a retry via the existingmcpBackoffhelper (exponential with jitter, 500ms→30s over 10 attempts by default; tunable viamcp.reconnect.*inconfig.yaml). Only aftermax_attemptsconsecutive failures does the server land inMcpDead.McpPoolServerDisconnectedEventnow carries the populatedreconnectAttempt+nextAttemptInfields it always had room for, so the status bar and panel can render the retry countdown. Manual/mcp reconnect <id>and/mcp toggle <id>cancel any pending retry timer and reset the attempt counter — pool tests cover both paths. Closes the docs-vs-reality gap flagged in #26. - Editor Integration (ACP) docs + branded
glue serveoutput — newwebsite/docs/advanced/acp-server.mdpage covers what ACP is, the twoglue servetransports (stdio for editors, WebSocket for browser/notebook clients), the full flag reference, and copy-pasteable configs for Zed (official), JetBrains AI Assistant 2025.3+ (official), VS Code (formulahendry.acp-client), Neovim (agentic.nvim+ alternatives), Emacs (agent-shell), and marimo/use-acp/agent-client-kernel.glue serve --helpnow ends with ausageFooterpointing at that page.glue serve --portprints a brand-styled startup banner (● glue serve+ indentedurl/auth/docs/stoprows) instead of the old single-line[glue serve]log, matching the shape ofglue catalog showandglue doctor. Wired into the Advanced sidebar between Web Tools and MCP Servers. glue mcp add | remove | enable | disable— manage MCP server entries from the shell instead of hand-editing~/.glue/config.yaml.addtakes--transport stdio|http|ws, accepts stdio commands after a--separator (glue mcp add foo --transport stdio -- node srv.js), HTTP/WS servers via--url, env vars via-e KEY=value, and a--disabledflag to park a server until you runglue mcp enable <id>.removeclears stored bearer/OAuth credentials by default (opt out with--keep-credentials). Mutations go through the newMcpConfigWriterso comments, key order, and formatting inconfig.yamlare preserved. Verb set mirrors Gemini CLI and Copilot CLI; transport grammar mirrors Claude Code / Amp.glue catalog openandglue catalog edit—openlaunches the configuredcatalog.remote_url(or the canonical GitHub raw URL) in the default browser viaopen/xdg-open/rundll32;--printemits the URL only for piping.editopens~/.glue/cache/models.yaml(or$GLUE_CATALOG_CACHE) in$EDITORwith inherited stdio, warns and hints atglue catalog refreshwhen the cache is missing, and errors out when$EDITORis unset. Both reuse the brand-dot header and✓ · ! ✗severity markers fromrefresh/show/pathso the catalog surface stays visually consistent.
Fixed
glue catalog refreshnow writes YAML, not single-line JSON. The remote-catalog sanitizer used to round-trip the upstream document throughjsonEncodeafter stripping credential-leak vectors — technically valid YAML, but unreadable inglue catalog edit. It now usesYamlEditorto surgically remove disallowed provider fields and clampauth.api_key: nonein place, preserving the upstream's block structure and comments.glue mcp tools <server>no longer hangs for ~10 s when the server is disabled. The transient pool skips disabled servers duringconnectAll, so the command was waiting for the connect/error event that never came. It now short-circuits with a clear warning pointing atglue mcp enable <id>.- In-app transcript selection and copy — drag-select text in the output zone, release to copy to the clipboard. Selection is anchored to
(blockId, plain-text offset)so it survives streaming chunks and terminal resize without pointing at the wrong text. Drag is reported via xterm?1002button-event tracking; SGR modifiers/motion are surfaced onMouseEvent. Shift-drag is passed through to the terminal as a native-selection escape hatch. - Double-click selects a word, triple-click selects a line — click-chain detection (300 ms window, cell-exact) mirrors the convention used by VS Code / Zed / token-editor. Word boundaries use the same three-class model as token-editor (whitespace / word / punctuation), so
foo_barselects as one identifier, a lone.between identifiers selects just the dot, and CJK / emoji / combining marks stay atomic. Both gestures auto-copy. Ctrl+Shift+Ccopies the current selection —Ctrl+Cis left alone so it still cancels in-flight agent work (you often select text because the agent is misbehaving).Escclears an active selection without falling through to autocomplete-dismiss or cancel-agent.- OSC52 clipboard transport — when running under tmux or SSH, clipboard writes go through OSC52 (with the tmux passthrough envelope when
TMUXis set) instead of relying on hostpbcopy/clip/wl-copy. Host commands stay the default outside multiplexers; OSC52 also acts as the fallback when host commands fail. Payloads >74 KB skip OSC52 cleanly. - Transient copy-confirmation toast — successful copies surface a narrow charcoal chip at the top-right of the output viewport (
✓ Copied 3 lines, yellow glyph on dim text) for ~1.8 s. Failures show a red-glyph variant for ~3.5 s. Painted directly into the viewport as a content-sized rect so it doesn't blank the row of transcript behind it, and never written to_blocksor the session log — the transcript stays clean.
Internal
- New
cli/lib/src/terminal/brand.dartcentralises the brand dot (●in RGB 250,204,21) and the✓ · ! ✗severity markers used byglue catalog,glue doctor,glue serve, andglue --where. Replaces the private copies that had been duplicated acrosscatalog_command.dartanddoctor.dart, so all branded surfaces share a single source. - New
cli/lib/src/app/transcript_selection.darthouses the coordinate model (TranscriptPosition,TranscriptSelection), drag gesture state, char-class helpers (classify,findClassRange), and theClickChainsynthesiser. Render pipeline keeps a per-frameplainOutputLinesshadow + line→block anchor list to support hit-testing and plain-text extraction without touchingLayout.paintOutputViewport. applySelectionHighlight(inansi_utils.dart) is a new cell-aware ANSI splicer used to wrap selected ranges with reverse-video while preserving wide glyphs and combining marks.
[0.4.1] - 2026-05-19
Added
Bootstrap error classification —
BootstrapExceptionnow carries a typedkind(auth,network,saml,missingBinary,prep,upload,cloneBundle,clone,checkout,unknown) and aremediationHint. The clone path pattern-matches git stderr to assign the right kind, so failures surface as "sandbox couldn't authenticate to the remote — switch to bundle bootstrap or inject an HTTPS token" instead ofBootstrapException(stage: clone, exit: 128).Bare-repo refusal —
buildHostBundlerefuses bare/mirror clones with a clear message instead of producing a silently empty bundle.Submodule warning — bundle bootstrap warns when host has
.gitmodulessince submodule contents are not transferred (gitlink pointer only). Recursive submodule fetch would require re-introducing sandbox-side auth, so this stays a warning until a full solution lands.glue doctorhost-git check — surfaces a warning at the Runtime section when host git is missing, since that disables bundle bootstrap and forces all cloud sessions through the clone-from-remote fallback.glue sessionCLI subcommand surface —list,show,diff,apply,export.applydefaults to creating a branchglue/<session-id>from current HEAD and runsgit am --3way(falls back togit apply --3wayfor working-tree-only patches); pass--in-placeto apply on the current branch (Q6 default resolved). Refuses to apply truncated patches.Session meta now persists runtime info —
runtime_id,sandbox_id,runtime_bootstrap_sha,runtime_remote_url,runtime_patch_path,runtime_closed_at. Lets/session, theglue session …commands, and a future cleanup sweep reason about prior cloud sessions without scanning the filesystem./sessionshows cloud runtime info when the session is running in a cloud sandbox (runtime id, sandbox id, where the patch will land on close).findOrphanedRuntimeSessionshelper — detects cloud sessions whoseruntime_closed_atis null and start time is > 24h old (likely leaked sandboxes), exposed for a futureglue runtime cleanupcommand.Cloud runtime bootstrap captures the host working tree via a git bundle, replacing the clone-from-remote-only path that lost uncommitted edits, unpushed commits, untracked files, and locked out non-git workspaces entirely. New host-side helper builds a single-commit bundle in a temp
--git-diroverlay (host's actual.gitis never touched), uploads it to the sandbox via the runtime's existing writeFile primitive, and clones from the bundle inside. Bundle SHA becomes thebootstrapShaso the diff layer is unchanged. Per-runtime upload caps: Daytona 200 MB, Modal 30 MB, Sprites 3 MB. Hosts without git, or bundles exceeding the cap, fall back to clone-from-remote. Resolves W1–W5, T1–T4, A1–A4 from the correctness plan + Q4 default (no.glueignore, respect host.gitignoreviagit add -A).Runtime diff outcomes are typed —
RuntimeSession.diffSinceBootstrapnow returns a sealedRuntimeDiffOutcome(Success/Empty/Unavailable(reason)) instead of a nullable string. Surfaces show a warning at session shutdown when the diff couldn't be captured (Sprites resumed without a baseline, Modal sandbox auto-terminated, runtime workspace isn't a git repo, etc.) — no more silent nulls.runtime.patch.meta.jsonsidecar — every saved runtime patch now has a metadata sidecar withruntime_id,sandbox_id,bootstrap_sha,remote_url,runtime_cwd,format,captured_at,size_bytes, andtruncated. Apply tools andglue session …(forthcoming) read this instead of re-inferring context from the patch body.Patch size cap — runtime patches are capped at 50 MB by default; a larger diff is written to
runtime.patch.truncatedwith a visible warning so the user can investigate without flooding the session directory.Sprites dirty-resume refusal — resuming a sprite whose
/workspacehas uncommitted changes from a previous session now refuses with a remediation message instead of silently producing a null baseline (which dropped every subsequent diff). Resolves Q1 indocs/plans/2026-05-19-cloud-runtimes-correctness-plan.md.Modal sandbox death detection —
diffSinceBootstrappreflight-checks the sidecar before attemptinggit diff, so an auto-terminated Modal sandbox produces a clearexecutorDeadwarning instead of a generic transport exception.Runtime command events — every executor (host / docker / daytona / sprites / modal) now emits
RuntimeCommandStarted/RuntimeCommandCompleted/RuntimeCommandFailed/RuntimeCommandCancelledwhen constructed with aRuntimeEventSink. Threaded throughRuntimeFactory.create({eventSink}). Opt-in — null sink is free.End-of-session workspace diff for cloud runtimes — cloud
RuntimeSessions implementdiffSinceBootstrap()by runninggit -C /workspace diff <bootstrapSha>inside the sandbox on session shutdown; the result is saved to<session-dir>/runtime.patchso the user can review (or apply) the agent's edits after a cloud run.
Changed
- Runtime workspace diff is now an mbox (
runtime.mbox, notruntime.patch) produced bygit format-patch --binary -M -Cplus a working-treegit diff --binary -M -C HEAD. Anadd -Npreamble guarantees untracked files survive. Result: agent commits keep their authorship + message (apply withgit am --3way), binary files round-trip byte-for-byte, renames stay as renames, and files the agent created but didn'tgit addno longer vanish. Round-trip integration test verifies all three (packages/glue_runtimes/test/common/diff_roundtrip_test.dart). Resolves Q3 default.
[0.4.0] - 2026-05-18
Added
- Cloud runtimes — three remote sandbox adapters, transparent to the agent (same
bash, file tools, background jobs as host/Docker).- Daytona (
runtime: daytona) — REST over the control plane; per-sandboxtoolboxProxyUrldiscovered automatically; US + EU regions. Workspace bootstrapped via git clone or tarball into/workspace.DAYTONA_API_KEYfor auth. - Sprites (
runtime: sprites) — persistent Fly.io sandbox via thespriteCLI. Resumes by name; auto-sleeps when idle. Authenticates throughsprite login. - Modal (
runtime: modal) — Modal sandbox via an embedded Python sidecar speaking JSON-RPC over stdin/stdout (Modal's sandbox primitive is Python-SDK-only). Sandbox auto-terminates onsandbox_timeout_secondsto cap runaway billing.
- Daytona (
- Runtime selection via
runtime:YAML key orGLUE_RUNTIMEenv var (precedence: env → YAML → legacydocker.enabledfallback). /runtimeslash command — shows the active runtime, its key config (image / sandbox name / API base URL), and registered cloud adapters.glue doctorruntime checks — per-runtime block for host / docker / daytona / sprites / modal; reports API-key presence, CLI install/auth status, and config sanity.RuntimeFactory.register(name, adapter)— pluggable cloud adapter registration.cli/bin/glue.dartregisters daytona / sprites / modal at startup; downstream forks can register their own.RuntimeSessionumbrella type — bundlesexecutor+workspace- sandbox metadata (
id,sandboxId,bootstrapSha,resumed) +close()lifecycle hook used to stop cloud sandboxes on session end.
- sandbox metadata (
Changed
- Architecture refactor to support cloud runtimes:
- File tools (
ReadFileTool,WriteFileTool,EditFileTool,ListDirectoryTool) now route throughWorkspaceinstead ofdart:io.GrepToolroutes throughCommandExecutor. ShellJobManageroperates onRunningCommandHandleinstead ofdart:io.Processdirectly, so background jobs work uniformly across runtimes.- New shared
TransportWorkspace+RuntimeFsTransportlets each cloud adapter implement just a thin transport — workspace logic (path translation,WorkspaceAccessError, list anchoring) is centralized. - Workspace bootstrap (git clone or tarball + SHA recording) lives in shared
WorkspaceBootstrapused by all three cloud adapters.
- File tools (
- Monorepo split:
glue_runtimes/package houses the cloud adapters and shared cloud-runtime utilities. The cli depends on it only forregister*Runtime()— no cloud SDK leaks intoglue_harness. RuntimeApiExceptionunified the previous per-adapter*ApiExceptionclasses; carriesruntimeId+endpointfor observability.
[0.3.0] - 2026-05-15
Added
- MCP (Model Context Protocol) client — connect to any MCP server and surface its tools to the agent alongside Glue's built-ins.
- stdio transport with scrubbed environment (allowlist + explicit
env:only) so user secrets don't leak into spawned servers. - Streamable HTTP transport (
2025-03-26spec): single POST, server picks JSON vs SSE response. Bearer auth, capturedMcp-Session-Idechoed back on subsequent calls. - WebSocket transport for
ws:///wss://servers. - OAuth 2.1 with discovery (RFC 8414), Dynamic Client Registration (RFC 7591), PKCE, loopback redirect. Tokens stored encrypted in
CredentialStore. Runglue mcp auth login <server>. McpClientPoolwith eager non-blocking connect, exponential backoff with jitter for reconnect, crash-loop detection, and livetools/list_changedreactivity.- Namespaced tools:
<serverId>__<bareName>(e.g.playwright__browser_navigate). Native tool names always win on conflict.
- stdio transport with scrubbed environment (allowlist + explicit
glue mcpCLI —list,tools <server>,auth set --bearer,auth login,auth logout,auth status./mcpslash commands — status panel,list,tools <server>,reconnect <server>,toggle <server>,auth login|logout|status,help. Status bar shows anMCP:N⚠badge when servers are unhealthy.tool_policyfor MCP — globauto_approve/denypatterns scoped to the namespaced tool name; routes through the samePermissionGateas native tools.
Changed
- App decomposition — the
cli/lib/src/app/part-of split has been collapsed back into a singleapp.dart. The part-of files were cosmetic (extension methods onAppwith full access to its private state), so the split was hiding coupling rather than reducing it.app.dartis now one honest 2100-line class. - Slash command refactor finished —
SlashCommandContextlost the last domain-leaking callbacks (forkSession,resumeFromMeta).HistoryCommandandResumeCommandnow compose primitives throughctx.session.fork/resume+ctx.conversation.resetForReplay()+appendReplayEntries(...)directly. NewConversationView.resetForReplayandappendReplayEntriesmethods own the transcript-shape state. PanelControllerrenamed toModalSurfaceacross the codebase.
[0.2.0] - 2026-05-04
Added
- Harness-layers architecture — Glue is now a four-layer monorepo:
cli/(surface) →packages/glue_harness/(orchestration) →packages/glue_strategies/(provider/shell/web adapters) →packages/glue_core/(pure data types). A separatepackages/glue_server/ships the ACP-over-stdio/WS daemon. Seedocs/plans/2026-04-29-harness-layers.mdfor the full split. glue serve— ACP (Agent Client Protocol) server with stdio and WebSocket transports (--stdio,--port N). Typed content blocks, resource_links, diff support, image inputs, OAuth device flow, andsession/usage_summary.- Prompt caching across Anthropic + OpenAI + OpenRouter — the adapters now opt into provider-specific cache directives, surfaced via
/usageand the OTel spans. - End-to-end token usage tracking — main agent, subagents, and title generator each report into
UsageStats;/usageshows the per-role breakdown; resume carries totals over instead of restarting at zero. - Thinking-token streaming for reasoning-mode models (Anthropic thinking, OpenAI o-series).
- Native Gemini provider — first-class
GeminiProvideradapter inpackages/glue_strategies/talking to the Gemini Developer API (generativelanguage.googleapis.com) viastreamGenerateContentSSE. Function calling, imageinlineData, system prompts, and thinking-modethoughtSignatureround-trip are all wired in. Auth viaGEMINI_API_KEYonly (Vertex / Google-account login intentionally out of scope). - Two-press SIGINT in
--print/--jsonmode — first Ctrl+C cancels the in-flight agent stream so the JSON envelope can includecancelled: trueand the OTel span records the cancellation; the process exits 130. A second Ctrl+C during teardown hard-exits 130. Replaces the previous loop withStreamIteratorso cancellation flows from the SIGINT handler into the agent'sasync*generator. Seedocs/reference/sigint-handling.md. /copyslash command — copies the most recent assistant block to the system clipboard via the existing fallback chain (pbcopy/clip/wl-copy/xclip/xsel). Reports byte count on success; mid-turn/copyqualifies the message as "partial in-flight response"./modelaliases/models— single command surface; both names open the picker, and/model <query>switches directly.- Catalog refresh (2026-04) — adds OpenAI o3 and o4-mini reasoning models, Mistral magistral-medium-latest plus a Small 4 promotion, Groq Llama 4 Scout (default) and Maverick, Ollama qwen3-coder-next:80b, Ollama llama4:8b, and a new "reasoning" profile.
enabledflag onModelDef— surfaces that aren't yet wired up at runtime can hide entries via the picker without removing them from the catalog.- Native
OllamaAdapter— Ollama now has a dedicatedProviderAdapter+OllamaClientthat talks to/api/chatdirectly instead of riding the OpenAI-compat adapter at/v1/chat/completions. Errors correctly attribute to Ollama (no more "OpenAI API error 404" on missing Ollama models), and Ollama-specific request options now have a home in the native client. options.num_ctxinjection —OllamaClientnow setsoptions.num_ctx = min(ModelDef.contextWindow, 131072)on every request when the catalog knows the model's context window. Fixes the silent agent-loop truncation when Ollama falls back to its 2048-token default. Clamped at 128K so catalog entries that claim 1M contexts don't OOM mid-range GPUs. Uncatalogued passthrough models (user-typed tags) omitoptionsand get Ollama's default.- Exact-match model resolver — new
lib/src/catalog/model_resolver.dartis the single source of truth for turning a user-typed identifier into aModelRef. Explicit<provider>/<id>inputs are never fuzzy-matched: catalogued refs return the catalog entry, uncatalogued refs pass through to the provider verbatim. Bare inputs require an exact match againstidor display name; ambiguous bare inputs return the candidate list and unknown bare inputs return a clear error. The previous substring fallback silently rewrotegemma4intogemma4:26b; it's gone. - Status bar +
/infoshow the wire address — the right-hand status segment now reads<provider> · <apiId>(what the provider actually receives), not the internal catalog key./infoexpands to<name> — <provider>/<apiId>for catalogued models and falls back to<provider>/<modelId>for uncatalogued passthrough. SurfacesapiIddrift (e.g. Groq'sgpt-oss-120bgoes out asopenai/gpt-oss-120b). - Ollama dynamic discovery —
lib/src/providers/ollama_discovery.dartmerges Ollama's/api/tagsinto the/modelpicker. Catalogued+pulled entries render normally; catalogued but not-pulled show[pull]; tag-only locally-installed models show[local]and are synthesised into picker rows. 2 s timeout, 30 s in-memory cache. Fail-soft: picker falls back to the bundled catalog silently when the daemon is down. - "Pull this model?" confirmation flow — selecting a not-installed Ollama tag (via the
/modelpicker or by typingollama/<tag>directly) opens aConfirmModal. On Yes, Glue streamsPOST /api/pullprogress as system messages and only switches the active model after a{"status":"success"}frame. On No or on pull failure, the active model stays put. - Clean
ConfigErrorsurface at startup —bin/glue.dartnow catchesConfigErrorandModelRefParseException, writes a single-lineError: …to stderr, and exits with code 78 (EX_CONFIG) instead of dumping a Dart "Unhandled exception" stack trace.
Changed
glue --resumenow mirrors the common CLI pattern used by Claude and Copilot. Bareglue --resumeopens the resume panel. Passing an argument (glue --resume <id>orglue --resume=<id>) resumes that session directly. Trailing positional text is preserved as the next prompt, soglue --resume <id> "continue here"resumes and immediately submitscontinue here. Print mode still rejects bare--resumebecause the panel is interactive-only.- Ollama no longer masquerades as OpenAI-compat. The
ollamaprovider indocs/reference/models.yamlnow usesadapter: ollama(notadapter: openai + compatibility: ollama), andbase_urldrops the legacy/v1suffix. Catalog regenerated. /modelpicker picks the user's intent, not a substring match. See "Exact-match model resolver" under Added. Errors list candidates for ambiguous inputs instead of silently choosing one.
Fixed
- Docker executor test now skips when the daemon is down. The skip guard changed from
docker --version(CLI-only check) todocker info(requires the daemon), so the test stops failing on machines that have Docker installed but not running.
Removed
CompatibilityProfile.ollama— dead after the native adapter move.CompatibilityProfile.fromString('ollama')now falls through toopenai(kept for a forgiving parse; the native adapter handles the real wiring).
[0.1.1] — 2026-04-20
Added
- Build metadata injection —
just buildnow passesGLUE_BUILD_TIME,GLUE_GIT_SHA,GLUE_GIT_DIRTY, andGLUE_BUILT_BYtodart compilevia--defineflags.BuildInforeads them at startup;glue --versionprints a compact summary (e.g.glue v0.1.1 (a1b2c3d, 2026-04-20T…)),glue --version --debugprints a detailed block, and--debugemits a banner to stderr before the TUI launches. Dev builds (dart run) fall back to(dev).just releasenow builds after the tag commit so the binary carries a clean SHA. glue config init— non-interactive config initializer that writes an annotated v2config.yamltemplate to the resolved Glue home (~/.glue/config.yamlor$GLUE_HOME/config.yaml). Supports--forceoverwrite/reset behavior, and/config initnow delegates to the same real config writer instead of creating an empty./config.yaml.glue config pathandglue config validate— scriptable config utilities for printing the resolvedconfig.yamlpath and validating the active config/provider credential setup.glue doctor— read-only install/config diagnostic command that reports resolved paths, parse errors for config/preferences/credentials/catalog files, active config validation, malformed session files, orphaned temp files, and returns non-zero when errors are found. Output uses the Glue brand header (yellow●), bold section headings, and coloured severity glyphs (✓/·/!/✗). Informational findings (e.g., empty session directories missingconversation.jsonl) are hidden by default — pass--verbose/-vto surface them.- DuckDuckGo search provider — zero-config
duckduckgosearch backend that scrapes the HTML endpoint (html.duckduckgo.com/html/), decodes theuddgredirect parameter to surface clean result URLs, and requires no API key. Registered in the default search provider chain. Includes unit tests for the HTML parser and an opt-in live integration test (dart test --run-skipped -t integration test/integration/duckduckgo_search_integration_test.dart). - Hyperbrowser backend for
web_browser— newhyperbrowserbrowser backend provisions Hyperbrowser cloud sessions (POST /api/session), connects over the returnedwsEndpointCDP WebSocket URL, surfaces the live view URL in browser tool output, and stops the remote session on disposal (PUT /api/session/{id}/stop). Configured withweb.browser.backend: hyperbrowserplusHYPERBROWSER_API_KEYorweb.browser.hyperbrowser.api_key. Includes docs/config examples and an opt-in live smoke test:dart test --run-skipped -t hyperbrowser test/integration/hyperbrowser_e2e_test.dart. - Anchor Browser backend for
web_browser— newanchorbrowser backend provisions Anchor Browser cloud sessions, connects over the returned CDP WebSocket URL, includes the live view URL in browser tool output, and stops the remote session on disposal. Configured withweb.browser.backend: anchorplusANCHOR_API_KEYorweb.browser.anchor.api_key. Includes docs/config examples and an opt-in live smoke test:dart test --run-skipped -t anchor_browser test/integration/anchor_browser_e2e_test.dart. ModelDef.apiId— optional field indocs/reference/models.yamldecoupling the stable catalog key from the mutable upstream identifier. Defaults to the YAML key when omitted (zero churn for simple entries). Adapters sendmodel.apiIdon the wire; the YAML key stays as the URL-safe, user-facing identifier in configs, sessions, and the/modelpicker. Motivation: upstream renames (e.g. Groq dropping Qwen3-Coder) no longer invalidate user configs. Matches precedent from OpenRouter, LiteLLM, Continue.dev, MLflow, and Docker.- Ollama catalog registry integration test —
test/integration/ollama_catalog_registry_test.dartverifies every Ollama catalog tag resolves viaregistry.ollama.ai. Opt-in viadart test --run-skipped -t ollama_registry; skipped by default to stay hermetic. Catches future drift when model authors rename tags. glue --where— printsGLUE_HOMEand every resolved path (config.yaml,preferences.json,credentials.json,models.yaml,sessions/,logs/,cache/,skills/,plans/) with an exists / not-yet marker. Useful on a fresh install to see where Glue will read and write.$GLUE_HOMEenvironment variable — overrides the default~/.glueroot. Every on-disk path (sessions, logs, cache, credentials, config) moves with it. Documented indocs/reference/config-yaml.mdand the Installation / Configuration docs.- Installer script at
getglue.dev/install.sh— POSIXsh, detectslinux|macos|windows × x64|arm64, pulls the matching binary from the latest GitHub Release, verifies the.sha256, drops it in~/.local/bin(overridable via--diror$GLUE_INSTALL_DIR). Supports--version vX.Y.Zfor pinned installs. ToolCallPhase.cancelled— distinct fromdenied(never ran) anderror(ran but failed). Agent cancel now marks every in-flight tool — including ones awaiting approval — ascancelledinstead of overloadingerror.- TUI behavior contract —
docs/reference/tui-behavior.mdcodifies the alt-screen, scrollback, resize, tool-phase, spinner, focus-priority, wrapping, and glyph rules the TUI follows today. - CLI prompt arguments and print mode — pass prompts directly from the command line for non-interactive use:
glue "review my code"— positional prompt argumentglue -p "query"— print mode, streams response to stdout without TUIglue -p --json "query"— JSON output for scriptingglue -r <session-id>— resume a specific session by ID- Model aliases:
glue -m opusresolves toclaude-opus-4-6
- DevTools instrumentation — consolidated observability with
dart:developerintegration:DevToolsSinkbridges observability spans todeveloper.log()anddeveloper.postEvent()for Dart DevTools visibilityGlueDevutility: CPU profiler tags (tagRender,tagLlmStream,tagToolExec,tagAgentLoop), Timeline helpers, DevTools URL helper- TTFB (time to first byte) tracking in
ObservedLlmClient - Tool call name and response preview tracking in observability spans
- Session replay improvements — proper tool_call/tool_result grouping during session resume and fork:
- Assistant messages now include their associated tool calls
- Tool results are properly paired with their tool_use IDs
- Orphaned
tool_resultmessages are filtered from conversation history, preventing Anthropic API 400 errors
- Subagent output improvements — richer grouped output display:
_SubagentEntryclass with optional JSON pretty-printing for expandable tool result content_SubagentGroupshows current tool name during execution- Expanded view renders indented JSON for structured results
- Multimodal tool results — tools can now return images (e.g. browser screenshots) as native content blocks instead of base64 text. This reduces token usage from ~738K text tokens to ~1,600 vision tokens per screenshot.
ContentPartsealed class hierarchy (TextPart,ImagePart) for structured content in tool results.Tool.execute()returnsList<ContentPart>— single dispatch method for all content types (text and images).ForwardingToolbase class for decorators (Go embedding pattern) — newToolmethods auto-forward to all decorators.- Provider-native image formats: Anthropic inline
imageblocks intool_result, OpenAI/Ollama follow-up user messages withimage_url/imagesarrays. WebBrowserToolscreenshots now returnImagePartinstead of base64 text strings.
- Observability & debug system — pluggable telemetry with zero changes to business logic. Three wrapper layers instrument all activity without polluting core code:
LoggingHttpClient— wrapshttp.Client, logs every outbound HTTP request (method, URL, status, duration).ObservedLlmClient— wrapsLlmClient, tracks provider, model, message count, token usage, tool calls, and latency per generation.ObservedTool— wraps eachTool, tracks execution time, args, and output size.
/debugslash command — toggles verbose debug mode at runtime. Also available via--debug/-dCLI flag orGLUE_DEBUG=1env var.- File-based debug logging — daily-rotating log files at
~/.glue/logs/glue-debug-YYYY-MM-DD.logwith timestamped entries for HTTP calls, LLM generations, tool executions, and span lifecycle. Observabilityfacade — composite dispatcher that fans out to multiple sinks. Sinks are independently enabled/disabled. The facade provideslog(),startSpan(),flush(), andclose()methods.ObservabilityConfig— configuration model for debug and telemetry settings, parsed from config file and env vars following the existing resolution chain.- Terminology standardization — established canonical glossary (
docs/architecture/glossary.md). Two-level hierarchy: Project (registered directory) → Session (resumable agent conversation). Sessions are independent and carry their own model, worktree, branch, and conversation history. No intermediate "workspace" layer — matches the data model used by Claude Code, Codex CLI, and Cline. - Enhanced
SessionMeta(schema v2) — sessions now store rich metadata:project_path,worktree_path,branch,base_branch,repo_remote,head_sha,title,tags,pr_url,pr_status,token_count,cost,summary. All new fields are optional; schema v1 files are read with permissive defaults. AddedSessionMeta.fromJsonfactory for consistent deserialization andSessionStore.updateMeta()for mid-session metadata writes. Timestamps now consistently use UTC. - Model registry & picker — curated
ModelRegistrycatalog of 10 models across Anthropic, OpenAI, Mistral, and Ollama with capability, cost, and speed metadata./modelwith no args opens a selectable panel picker grouped by provider;/model <name>does fuzzy lookup by ID or display name. Only models with configured API keys are shown. GlueConfig.copyWith— immutable config update for provider/model switching.LlmClientFactory.createFromEntry— create an LLM client directly from aModelEntry.- Spinner animation in status bar during LLM streaming — braille dot pattern cycles at 80ms instead of static
●indicator. - Collapsible subagent output — subagent activity is now grouped by task into compact summary lines (e.g.
↳ [1/3] task… (5 steps…)) that update in-place. Click a group to expand/collapse its full step log. - Alt+Backspace deletes previous word (same as Ctrl+W).
- Alt+Left / Alt+Right word-level cursor navigation in input editor.
KeyEventandCharEventcarry analtflag for modifier-aware input handling.web_fetchtool — fetches a URL and returns clean markdown for the LLM. Three-stage pipeline: (1) tryAccept: text/markdownheader, (2) HTML fetch → Readability-style content extraction → HTML-to-markdown conversion, (3) optional Jina Reader API fallback. Configurable timeout, max bytes, and token budget. Auto-approved (read-only).web_searchtool — searches the web via configurable providers (Brave, Tavily, Firecrawl) with unified result model. Auto-detects provider from available API keys (priority: Brave → Tavily → Firecrawl) with automatic fallback on error. Supports explicit provider selection via parameter. Configured viaweb.search.*in config.yaml orBRAVE_API_KEY/TAVILY_API_KEY/FIRECRAWL_API_KEYenv vars.WebConfig— web tool configuration model withWebFetchConfigandWebSearchConfig, wired intoGlueConfigwith env var and config file resolution following existing patterns.- Hidden aliases for slash commands —
SlashCommandnow supportshiddenAliasesthat resolve on execution but are excluded from autocomplete and/help./qis now a hidden alias for/exit. - Multi-shell support — unified
CommandExecutorabstraction withHostExecutorthat respects the user's shell via$SHELL,GLUE_SHELL/GLUE_SHELL_MODEenv vars, orshell.*in config.yaml. Supports bash, zsh, fish, pwsh with correct flag mapping for interactive/login/non-interactive modes. - Docker sandbox —
DockerExecutorruns agent commands in ephemeraldocker run --rmcontainers with bind-mounted directories. Uses cidfile-based container termination with retry for race conditions.ExecutorFactoryhandles Docker availability detection with automatic host fallback. Configurable viadocker.*in config.yaml orGLUE_DOCKER_*env vars. - Session-scoped Docker mounts —
SessionStatepersists directory whitelist additions instate.jsonper session, merged with config mounts at executor creation. /modelscommand — lists available models from the current provider (Ollama/api/tags, OpenAI/v1/models, Anthropic/v1/models). Shows model name, size (Ollama), and marks current.- E2E integration tests — headless agent loop tests via
AgentRunnerwith real Ollama (qwen3:1.7b). Tagged@e2e, skipped by default, run withdart test --run-skipped -t e2e. Retry wrapper handles small-model non-determinism. - Tool call intent indicator — the UI now shows
▶ Tool: write_file (preparing…)as soon as the LLM begins generating a tool call, rather than waiting for the full arguments to stream. Tool calls progress through visible phases: preparing → running → done (or denied/error). Eliminates the "hanging spinner" feel during large tool argument generation. ToolCallStartLLM chunk — Anthropic and OpenAI clients now yield aToolCallStartchunk atcontent_block_start/ first tool delta, surfacing the tool name before arguments finish streaming.- Mistral LLM provider — fourth provider alongside Anthropic, OpenAI, and Ollama. Uses OpenAI-compatible API with Mistral-specific base URL. Configurable via
MISTRAL_API_KEYenv var ormistral.api_keyin config.yaml. - Agent Skills (
agentskills.iospec) — discover and activate reusable skill definitions from.glue/skills/(project-local),~/.glue/skills/(global), and extra paths via config/env. Skill parser validates YAML frontmatter.skilltool lists or activates skills./skillsslash command opens a two-paneSplitPanelModalbrowser. Configurable viaskills.pathsin config.yaml orGLUE_SKILLS_PATHSenv var. - Browser tool infrastructure —
BrowserManagerwith pluggableBrowserProviderabstraction for Chrome DevTools Protocol connections. Five provider implementations: local Chrome, Docker container, Browserbase (cloud), Browserless (cloud), and Steel (cloud).BrowserConfigwith auto-detection priority chain. - PDF text extraction —
web_fetchnow handles PDF URLs with a two-stage pipeline: (1) direct text extraction from PDF bytes, (2) OCR fallback via Mistral Pixtral or OpenAI GPT-4o vision models for scanned/image-heavy PDFs. - GitHub Actions CI/CD — six workflows: Dart checks (analyze, format, test), multi-OS matrix (Ubuntu/macOS/Windows), docs build validation, nightly e2e integration tests, release tag builds, and auto-labeling. Dependabot configured for Dart and GitHub Actions dependency updates.
Changed
- Web search and browser automation support are constructed lazily during startup.
ServiceLocator.create()now wires memoized lazy factories intoWebSearchToolandWebBrowserTool, soSearchRouter, browser provider selection, andBrowserManagerare only built on first valid tool use. - Model catalog refreshed against live provider availability (April 2026).
- Mistral default →
devstral-latest(agentic coding model). Addedmistral-medium-latest. Keptmistral-large-latest,mistral-small-latest. - Ollama default →
qwen3-coder:30b(community consensus, 256K context, dedicated tool-call parser). Recommended list:qwen3-coder:30b,qwen3.6:35b,gemma4:26b,devstral-small-2:24b,qwen2.5-coder:32b,qwen3:8b. Six popular non-agentic families (mistral:7b,gemma3:12b,codellama:13b,codegemma:7b,starcoder2:15b,deepseek-coder:33b) included asrecommended: falsewith notes explaining why they're not suitable for tool loops. - Groq default →
openai/gpt-oss-120b(reasoning + coding). Addedgpt-oss-20b,llama-3.1-8b-instant. Keptllama-3.3-70b-versatile. - Slash-bearing catalog keys slugified:
groq/openai/gpt-oss-120b→groq/gpt-oss-120b(upstreamopenai/gpt-oss-120bnow lives inapi_id). Same treatment for Groqgpt-oss-20band OpenRouter's three slash-keyed entries.
- Mistral default →
- Resize preserves scroll position.
UserResizeno longer snaps the transcript back to the tail; the render pipeline clamps any out-of-range offset after the viewport changes. Ctrl+Endjumps to the bottom and resumes follow-tail. PlainEndstays reserved for the line editor (cursor-to-end-of-line)./statusis now a hidden alias of/info. One command in help + autocomplete; muscle memory for/statuskeeps working.- Release workflow overhaul — five-way matrix (
linux-x64 / linux-arm64 / macos-x64 / macos-arm64 / windows-x64.exe), per-asset.sha256, aggregatedSHA256SUMS, smoke-test of every binary, prerelease auto-flag for tags containing-. - GH Action version audit.
actions/cache v4 → v5(Node 24); everything else already on current major. - Status bar reformatted: bold mode indicator on left, model/mode/ cwd as right-aligned segments with
│separators. GlueConfig.load()no longer acceptscliProviderparameter (provider inferred from model). Removed--providerflag (frees-pfor print mode).- Terminal parser now decodes CSI modifier parameters (
;3= Alt,;5= Ctrl) from extended arrow key sequences. - Terminal parser handles ESC + byte sequences for Alt+char and Alt+Backspace (macOS Terminal convention: ESC prefix = Alt modifier).
- Eager tool call emission —
AgentToolCallevents are emitted during LLM streaming (not after stream end), so auto-approved tools can start executing while the model may still be finishing the response. - Dart analyzer hardening — expanded
analysis_options.yamlwithalways_use_package_imports,strict-casts,strict-raw-types,avoid_dynamic_calls,prefer_const_constructors,unawaited_futures,discarded_futures, and other safety/style rules on top ofpackage:lints/recommended.yaml. Converted all relative imports topackage:glue/imports across 51 files. Applieddart fixauto-fixes for const correctness, unnecessary lambdas, and parentheses. - Default model strings removed from
GlueConfig—_defaultModel()now delegates toModelRegistry.defaultModelId(). - Subagent updates use a grouped data model (
_SubagentGroup) instead of individual conversation entries — reduces output noise during multi-agent orchestration.
Fixed
- Spinner no longer stuck after cancel.
_cancelAgentImplnow stops the spinner before flipping mode;_cancelBashImplmirrors the pattern defensively. - Tool phase no longer stuck on
awaiting approvalafter cancel — approval-modal-open cancels now transition tocancelledtoo. - Cancel no longer corrupts conversation — cancelling (Escape) while a tool was executing left the conversation with
tool_useblocks but no matchingtool_resultmessages, causing the next API call to fail with a 400 error.ensureToolResultsComplete()now injects synthetic[cancelled by user]results for any unmatched tool calls. - Unused
callIdparameter in_ConversationEntry.toolResult— was accepted but silently discarded; removed the dead parameter. /modelswitch now updates_config(provider + model) viacopyWith, fixing stale config bug where session metadata and subagent spawning read outdated values.- ANSI codes in split panel highlight — reverse-video selection highlight in
SplitPanelModalnow strips ANSI escape sequences before applying highlight. - Skill discovery trailing-slash safety — skill directory paths ending with
/no longer produce empty skill names. - Exit message styling — exit prompt now shows the yellow diamond brand mark with session ID.
- Sema scripting skill documentation examples corrected (8 runtime errors fixed in documented code).
Removed
codestral-latestfrom Mistral catalog — FIM-lineage model; enumerates tools but narrates instead of calling them in agent loops. Replaced bydevstral-latest.qwen/qwen3-coderfrom Groq catalog — Groq no longer serves this model; the entry would 404. Replaced byopenai/gpt-oss-120b.llama3.2:latestfrom Ollama catalog — general chat model, not coding/tool-focused.devstral:latestfrom Ollama catalog — replaced by the pinneddevstral-small-2:24b(known version, known SWE-bench scores).- Interaction modes (
code/architect/ask), plan-mode UI,GLUE_INTERACTION_MODEenv var. - OTEL / Langfuse / DevTools observability sinks —
~/.glue/logs/spans-YYYY-MM-DD.jsonlis the single local trace log now.
[0.1.0] — Initial development
Added
- TUI application shell with raw-mode terminal, layout zones (output/overlay/status/input), and 60fps render throttling.
- Readline-style line editor with history, Emacs keybindings (Ctrl+A/E/K/U/W), and cursor movement.
- AgentCore ReAct loop with streaming LLM ↔ tool execution, parallel tool calls, and token counting.
- LLM providers: Anthropic Messages API (SSE), OpenAI Chat Completions (SSE), Ollama (NDJSON) — each with per-provider tool schema encoding and message mapping.
- SSE stream decoder for chunked event parsing.
- LlmClientFactory for provider instantiation from config.
- GlueConfig resolution chain: CLI args → env vars → ~/.glue/config.yaml → defaults.
- Headless AgentRunner with configurable approval policies (allowlist, always-approve, always-deny).
- AgentManager for subagent orchestration with depth-limited recursive spawning.
spawn_subagentandspawn_parallel_subagentstools.- Built-in tools:
read_file,write_file,edit_file(multi-line find-and-replace),bash(with configurable timeout),grep,list_directory. - Slash command system with
/help,/model,/clear,/resume,/info, and tab-completing autocomplete overlay. @filereference expansion — type@to get fuzzy-matched file hints (recursive, with directory browsing), expanded inline on submit.- Inline confirmation modal for tool approval (hotkeys + arrow navigation).
- Full-screen panel modal with scrolling, barrier rendering, and dismiss.
- Session persistence —
~/.glue/sessions/with conversation logging, listing, and/resumecommand. - ConfigStore with mtime-based caching and atomic saves.
- DebugLogger for
~/.glue/debug.log. - Auto-loads
AGENTS.mdandCLAUDE.mdinto system prompt. - Bash mode —
!prefix for shell passthrough with background job lifecycle management (ShellJobManager). bashMaxLinesconfig setting and box-drawn bash output renderer.- Markdown table rendering with box-drawing characters.
- Animated mascot splash screen with liquid simulation.
- Mascot explodes into goo particles when clicked repeatedly.
- Status bar with mode indicator, model name, working directory, scroll position, and token count.
- Status bar padding accounts for ANSI escape sequence lengths.
- Scroll/resize events routed through event bus.
- Render throttling at ~60fps to reduce flicker during streaming.
Fixed
- Ollama tool results use tool name instead of call ID.
- OpenAI tool call arguments serialized as JSON instead of Dart
toString(). /modelcommand now actually switches the LLM client.- BashTool uses
Process.startwith proper kill on timeout. - ConfigStore uses mtime-based caching with atomic saves and
update()API. - AtFileHint caches directory listing to avoid blocking UI on every keystroke.
- Expanded
@filecontent stored in conversation entries for session logging. - Fire-and-forget
.then()replaced with proper async error handling.