Repository navigation
v0.11.0
[v0.11.0] - 2026-09-27
This one's a footlong! Biggest new features: optimizations for local models, local infra subagent fan-out, /profile switch, session rewind/forking, tab completions in the webui.
-
Workflows: multi-agent DAG orchestration. A workflow is a YAML graph of worker nodes -- saved as
config/workflows/<name>.workflow.yamlor designed by a manager -- where each node declares a profile, a model pin/requirements, inputs from upstream nodes, and a machine-checked accept gate: its declared output files must exist fresh inside the run dir plus a<node>.verdictfile whose first line ispass|fail|reject. CLI:hotdog workflow validate|render <file>,run <file> [--id <run-id>](foreground with live node lines, Ctrl-C cancels gracefully; one dir per run with an append-onlyrun.jsonlstate log;--idresumes a previous run, reusing only completed nodes that still verify on the filesystem, whichreconcilereports on), pluslist,status,reconcile,cancel. Manager toolsworkflow_validate/workflow_save/workflow_dispatch/workflow_statuslet an orchestrator profile design, persist, and dispatch graphs without polling -- completion posts to the delegating session on the task-completion path -- and/workflow//followup <node> <message>list, inspect, cancel, and steer runs. Configworkflows:path(default<configDir>/workflows),maxNodes(soft cap 8, hard ceiling 32),maxRuntimeMins(30). -
Profile-bound model groups. A worker profile can bind fanout itself: add a
groupfield (frontmatter or in-config) naming amodelGroupspool, and everydelegate_task/ workflow node routed to that profile places across the group without the manager explicitly naming a group. -
Cross-provider placement fanout (
modelGroups, providernoSpread). Spawn model resolution now produces an ordered placement plan instead of a single pick, and TaskManager admits it lane-aware: the first candidate whose provider lane has capacity wins, locked for the task's lifetime. Twodelegate_taskcalls with no model given now run in parallel across the providers carrying the default model's copies (warm llama-swap nodes preferred among those with room; the/runningpeek re-ranks queued tasks at placement time) instead of serializing behind one lane.modelGroupsdeclares interchangeable pools (mid-level: [qwen3.8-flash-next, llama-3.3-70b]), referenced asworker_model: "group:<name>"or the workflow nodegroupkey -- placement degrades to the next free member instead of queueing, while bare-name copies never degrade. Strict pins and provider-qualified explicit values never fan out;noSpread: trueon a provider keeps it off implicit fanout (explicit members and pins still land there). A multi-candidate task can return while stillqueuedunplaced (task_statusshows the intent, provider null, until a lane is granted), and a first worker on a cold copy pays one model swap by design.spawnTasknow refuses an id already held by a queued/running task instead of silently orphaning the live one. -
Empty-response-after-tools nudge. When a completion arrives with no text and no tool calls while the context tail is a tool result (a common local-model stall that silently abandoned the task), the harness persists a
system-noticeasking the model to continue and advances the loop instead of stopping. -
Loop detector (
src/extensions/loop-detect/, on by default). Hashes each tool call as (name, canonicalized arguments) and watches the tail for two spin patterns: k consecutive identical calls (default 3) and strict A/B/A/B ping-pong (default 4). Escalation follows the streak: asystem-noticenudge injected before the next request, a stronger nudge at 2x, thenagent.cancel()with a user-visible message at 3x. Every escalation fires theloop:detectedhook ({ agent, toolName, kind, streak, level }). Per-session state, dropped on context replacement (compaction/rewind//clear) and session end; tune via theloopDetectconfig block. -
/undo,/rewind [N],/fork [N] [prompt]-- turn-level time travel. A turn is a user message plus everything after it until the next user message (tool chains never split)./undodrops the last turn;/rewind [N]drops N (default 1). Rewinds persist: the session-log extension checkpoints the log with aresetentry followed by the surviving messages (append-only, undone history kept above the marker for audit) -- the previously deadresetreplay support in core finally has a writer, which also fixes resumed sessions replaying history that/clearremoved./fork [N] [prompt]branches a new session from N turns back (default 0 = full copy) and switches to it (interactive CLI re-attaches the channel; the source stays attached for/switchback); an optional prompt is enqueued by the UI only after its channel/socket is re-targeted onto the fork (interactive CLI and webui alike) -- core'sforkSessionnever touches the prompt, so the fork's first output can never fire before anyone is listening. Session-mutating commands (clear/undo/rewind/fork) are rejected while a run is active -- replacing context mid-turn corrupted both histories. The webui handles/forkconsistently with the interactive CLI: the ws layer registers the forked session's UI metadata, re-targets the requesting tab (sessionCreated + history replay), then enqueues the optional prompt there;forkSessionitself also refuses to fork a running session (direct-call guard) and mirrorscreate()'s current-session ordering aroundSESSION_CREATE. New core pieces:trimTurns(src/core/context/rewind.ts),context:rewoundhook,Agent.rewindContext(),SessionManager.forkSession()injected into agents as theforkSessionseam (same pattern asenqueueCallback). -
tool-call-repair - handle a second kind of problem, when the JSON can end up malformed - interrupting a hung stream could leave an assistant message with truncated tool-call arguments (invalid JSON) in history; a llama.cpp-style backend then throws error 500 every subsequent request ("Failed to parse tool call arguments as JSON ..."), bricking the session. Now removes broken tool calls and retries.
-
--json-schemastructured output for one-shot mode. can be a file path or inline JSON object. -
/profilecommand added (list + switch). New extensionsrc/extensions/profile-switch/adds:/profilelists profiles marking current;/profile <name>and/profile:<name>to switch. -
rescue subcommand - config diagnostics that survive a broken config -- a syntax error in defaults.json (the most common breakage: JSONC comments, trailing commas) would throw inside the early so every subcommand died with a one-line parse error and there was no way in. New core subcommand
hotdog rescue [fix]: prints the config-dir resolution chain, diagnoses defaults.json along the chain (explicit-foverrides), and for broken files shows the parse error with an engine-independent line/column caret locator, repairs BOM/line-and-block-comments/trailing-commas, and withfixrewrites the file keeping a.bak(never clobbers an existing backup); non-fixable breakage gets the marked position. When JSON parses it also warns on unknown top-level keys ("did you mean" via sharedsuggestCandidates), silently-won duplicates, and core+extension schema violations (validateConfigagainst raw normalized file + registry schemas), plus system_prompt.md/profiles-dir presence. -
[BRK?] bash-tool brace expansion fix --
BashTool.executewas previously spawned withshell: true, which uses/bin/sh(dash on Debian-ish systems, where was run); dash has no brace expansion, somkdir -p x/{mcp,openai,sse}created one literal{mcp,openai,sse}directory. Now uses 'bash' explicitly. Slight chance of breakage if you were using this in a slim container withshbut withoutbash. -
hotdog infoadded anExtensionssection: every extension discovered in the scan paths, shows(disabled)ifenabled: falsein config (incl. schema defaults like user-gate's off-by-default),(not loaded)if enabled but excluded (autoload off, list selection, orcreate()returning null -- e.g. subagents on non-manager profiles). -
$VARinterpolation in config files. a string that is exactly$VARor${VAR}resolves fromprocess.env. Whole-string only --"http://$HOST"/"cost $5"untouched. -
webui - tab completion in chat input -- Tab opens the popup, Tab/Shift+Tab/arrows cycle, Enter accepts (Escape first to submit raw), Esc/blur/edit dismiss, mousedown accepts without caret loss, invalidation token so a late reply can't reopen a dismissed menu.
-
tab completion for bare filepaths -- file-attachment completion matcher/handler now also trigger on bare path-looking words (
./x,/x, anything containing/) in addition to@-prefixed attachments; values are returned without the@in that case (prefix preserved, e.g../te->./test.txt). Plain prose liketest.txtwon't trigger (no/, no leading.); slash-containing prose likeand/ordoes trip the matcher but the handler yields no matches and thus an empty list (readdir of the nonexistentanddir hits the catch-all), so it should be harmless in practice. -
security
- env scrubber - widen to redact *_PWD and add extra list in config
- fetch-tool - widen blocklist for reserved addrs
- session-log - validate session id before writing
- marker-mangler - escape dashed-prefixed markers without terminator at end of string
-
fixes
- /compact N - no longer leaves orphaned tool messages, which can cause errors on some backends
- mcp-client - handle write/stdin errors without full hotdog crash
- avoid orphaning tool messages when interrupted (ctrl+C, or stop button)
- fix
--no-log, was writing log files anyway due to a regression during JS->TS rewrite - fix regression with
--compact-debug- The flag/config promisedcompaction.out.jsonbut nothing was ever written._handleCompactCommandnow dumps{ts, session_id, mode, keep_requested, strategy, settings, messages:{before,after}}tocompaction.out.jsonin the sessions dir - file-tools BOM/CRLF fidelity in the write tools. silent-corruption bug - bun keeps
\uFEFF/\r\nin utf-8 strings, so the edit trimmed-line fallback mixed bare LF into CRLF files and could drop the BOM when the first line was replaced; append/overwrite wrote model LF into CRLF files and overwrite always dropped the BOM. Fix = detect-on-read, re-apply-on-write - /loop + handoff - handoff now supercedes loop deterministically
- interactive cli - mid-string tab completion clobbered input -- fixed completing
@pacin/loop review this software, including @pac<TAB>replaced everything with@package.json
-
internals
- add session teardown hook
- steering queue refactor (followQueue->steer)
exitCommandsunused config key deleted- hook priority system to enable that loop/handoff fix
-
experimental/file-watch - notices are no longer injected into every LLM request: each external change is logged once as a persistent harness message in the session (quiet until the file is re-read or overwritten). Bash looks count as reads: a successful
cat,grep, print-modesed, orgit diff/git statusnaming a tracked path resolves its stale flag and write guard.
Full Changelog: v0.10.0...v0.11.0