Repository navigation
Releases: devoidfury/hotdog
Release list
v0.11.3
Loads of little fixes, model modality detection improvements, polishing on the webui now that I'm using it more heavily.
-
question tool - a pending question survives a tab refresh. A
questionblocked when the webui reconnects now re-renders its card after history instead of hanging with no UI to answer. Replay moved from the SessionManager's QUESTION-event buffer to the question bridge itself: on attach or session switch the server peeks the live pending question. -
webui -
wss://on https pages. The chat socket URL hardcodedws://, which browsers block as mixed content on an https page. -
webui - dropped the pulsing glow on the connection indicator
-
evals - added independent evals runner and a first easy series
kielbasa-1 -
webui - attachments render inside the user bubble. @-ref uploads echoed as an extra
SYSTEM_MESSAGEper file while the bubble itself showed the file-include part's raw JSON. The user-message event now carries the text plus afiles[]view, rendered as collapsible boxes in the bubble -- live, session replay, and cold log replay.SystemMessageEventloses itsfilesfield. -
Removed
--no-stream/streamconfig (breaking). The flag only gated display -- the request path always streamed -- so--no-streamruns silently dropped the answer. Output is now always streamed;--no-streamfails fast as an unknown flag and"stream"indefaults.jsonsurfaces as an unknown key inhotdog rescue. -
fetch tool returns images. A fetched PNG/JPEG/WebP/GIF now attaches to the tool result as a base64
image_url, exactly likeread-- a vision model can look at a chart or screenshot straight from a URL. Gated bymodelAcceptsImagesbefore any bytes are read (fail-closed on text-only models), capped at the shared 10MB image limit with a streaming byte cap so endless streams get cancelled, not buffered. Otherimage/*types (e.g. svg) keep the text path;HEADstill returns headers only. -
@-ref image attachments get their own size budget.
fileAttachment.maxFileSize(100KB) gated text and images alike, so screenshots over 100KB were silently dropped from @-refs even on a vision model -- whilereadsaw them fine under its 10MB cap. NewfileAttachment.maxImageSize(default 10MB, matchingread) applies to images; text budget unchanged. The webui Attach button shares the split:image/*uploads over the websocket are sized againstmaxImageSize. -
llama.cpp model-modality detection.
fetchModelsnow understands plain llama.cpp's/v1/modelsshape, not just llama-swap: context fromdata[].meta.n_ctx, vision from the ollama-stylecapabilitieslist,aliasesexpand. When the payload has no modality data it falls back to/props. Vision models on bare llama.cpp now reportinputModalities: ["text","image"]without hand-editing provider config; non-llama.cpp providers see no extra requests. -
--disable-env-scrubbingCLI flag: turns off env scrubbing for agent-spawned subprocesses (bash tool, MCP stdio); children inherit the full environment. Default stays scrubbed. -
provider health tracking. A dead provider looks idle -- the preferred placement target -- until a task locks onto it and burns its retries on connection-refused. Placement now consults reachability verdicts: a sweep every
providerHealthCheckIntervalSecs(default 30,0disables the timer) probes each provider with a resolvable url --/v1/modelsforfetchModels: trueproviders, which doubles as a live catalog refresh upserted into the running registry,/healthotherwise -- and connection-level task failures (refused/DNS/reset) demote a provider immediately, active even at0. Reachability is status-agnostic: a 500 or a stalled generation still proves the socket answers. Recovery clears through a successful probe or any task completing a turn, so demotion is never process-permanent. A down provider is skipped during multi-candidate placement unless every candidate is down (fail-open); pins are never rerouted.hotdog infoshows the per-providerup/downverdict. -
fix tool-result blindness on strict chat templates. Since the tool-result part refactor,
role: "tool"messages rode the wire as part arrays; llama.cpp/Ollama templates interpolate tool content as a raw string and choke, so some models effectively never saw tool results. Text-only tool content now collapses back to a plain\n-joined string on the wire; arrays with non-text parts (images) stay arrays. -
marker-mangler - chatML tokens are escaped too. ChatML control tokens (
<|im_start|>) in untrusted text are rewritten to a safe form and reversed on output -- chatML markers survive enough model templates to be common breakage.
Full Changelog: v0.11.2...v0.11.3
v0.11.2
-
Workflows: validate-is-save, file dispatch, and graph params. Manager tool
workflow_saveis gone.workflow_validatenow saves a valid design on the spot (as<name>.workflow.yaml, returning the path) and also acceptsfileto re-validate an existing graph.workflow_dispatchtakesfile(path or bare name resolved against the workflows dir) instead of pastedyaml, closing the loop so validation is a required step and designs avoid round-trip through the transcript. Graphs can declare a top-levelparamsblock (id -> default, empty = required) and reference values as{{params.x}}in any string;workflow_dispatch(args={...})and the CLI's repeatable--param key=valuefill them, making saved graphs reusable templates. Substitution runs on the parsed YAML tree, so values can't inject graph structure. -
fix regression in startup
--profiletool filtering, causing tools to be loaded based on the default profile regardless -
task-lanes - fix address issue causing cap+1 lanes on providers
-
fix frontmatter-only (empty body) task agent profile
-
updates to example profiles (meta updates, new manager)
Full Changelog: v0.11.1...v0.11.2
v0.11.1
Small but important fix to solve an issue causing hangs when using task-lanes (lanesRetryTimer storm)
Full Changelog: v0.11.0...v0.11.1
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-...
v0.10.0
-
[BRK] removed the profile-level
rolefield. The only "role" concepts that remain are the message-format wire encoding (Message.role, role-mapping) and plain prose in user-supplied context files.- Profile files with
role:frontmatter still parse -- the key is dropped and can never reach prompt assembly (covered by tests asserting a profile role can never leak into the system prompt). - Config keys
roleandtaskDefaultRoleand the--roleflag are gone; the{{ role }}placeholder was removed from the default system prompt template. If you had a role line, fold it into the profile body.
- Profile files with
-
[BRK] internals / hooks - pipeline handlers now patch the payload. Whatever a handler returns is written onto the payload, field by field, so every later handler and core read one thing: the payload. This replaces reading "the last handler's whole return value" at each call site, which silently dropped earlier patches — a
provider:requesthandler returning{ modelConfig }followed by one returning{ messages }used to lose the model. Same rule now coverscontext({ messages }),provider:request,provider:response,tool:result,tool:call,input, andcommand:dispatch. Non-object returns (a bare array, a string) are not patches and are ignored.runHookPipeline()no longer returns alastResultfield;results(every return, in order) andstoppedremain. The now-dead type-guard helpers (isGateActionBlock/Modify/Continue/Handled,isInputTransform,isInputHandled) are removed: call sites directly read the payload'saction. -
extensions - new opt-in tier:
@experimentalinextension_pathsloadssrc/experimental/(the setting replaces the default, so use["@extensions", "@experimental"]to keep the built-ins). First entry:file-watch, cooperative-editing awareness for a shared working tree — it tracks the files a session reads and writes, notices when the bytes on disk stop matching what the session believes, rides that onto each request as a harness system-notice, and blocks anoverwriteonto a file that changed underneath the session. -
fix - duplicate message delivery in the core agent loop: a UI host calling
run()afterSessionManagerhad already started the loop spawned a second consumer on the same queue, so one queued message could drive twoagent.run()calls (the loser threwAlreadyRunning).run()/runUntilCancelled()are now idempotent -- a later call joins the active loop instead of racing it. -
tools - a batch of core-tool and extension fixes found by auditing
src/extensions/:project_info: the non-git file listing was computed relative to the process CWD instead of the requested directory, so listing a directory from another launch dir produced absolute/../paths.web_search: theresultscount entry reported the number of output lines instead of the number of results (e.g. 3 results with descriptions read as 9).find:max_results: 0was accepted and silently returned "No files found" even when matches exist; it now falls back to the default like any other sub-minimum value. Thefile_typetool schema now documents the empty-file filter ("e") the code already supported.read: files above 1MB are now read with a single streaming pass (the whole-file read is kept for small files), so paginating a multi-GB log no longer materializes the entire file in memory per call.
-
config - removed two dead config keys:
coreTools.maxDiffSizeandhandoffTool.systemPrompt(declared in schema and docs, read by no code). Added the missinglayersblock to theloopconfig schema, which had leftloop.*settings outside the config resolution pipeline. -
questiontool: the schema no longer markskeyas required — it was always auto-generated from the prompt when omitted. -
hooks -
notifyHooks()is now awaitable: handlers start immediately in registration order (async ones run in parallel). Core call sites now await it where later code depends on the effect: the tool pipeline (TOOL_BEFORE_EXECUTE,AGENT_TOOL_CONTEXT,TOOL_AFTER_EXECUTE,TOOL_METRICS), turn boundaries (TURN_START/TURN_END), session create/swap, the bootstrap registration hooks, andSHUTDOWN_CLEANUP. Previously an asyncAGENT_TOOL_CONTEXThandler could complete its tool-context mount after theTOOL_CALLgate (e.g. the user-gate approval seam) had already run. Signature note:notifyHooks()returnsPromise<void>(wasvoid); unawaited call sites keep the old behavior.
Full Changelog: v0.9.2...v0.10.0
v0.9.2
Quick followup for the last release, since it's a big fix:
tool-call-repair- now handles both xml and chatml style tags, instead of just chatml style
Full Changelog: v0.9.1...v0.9.2
v0.9.1
-
added
tool-call-repairextension: recovers malformed tool calls that often occur with local backend. When it detects a dropped tool call, the response is rewritten in place and the agent's normal tool-execution path runs the calls.- Config:
toolCallRepair.enabled(defaulttrue),toolCallRepair.maxRepairsPerTurn(default2,-1= unlimited). - If you find any broken tool calls going forward, please make an issue on the tracker with the shape of the tool call, so that I can repro and fix it.
- Config:
-
internals
PROVIDER_RESPONSEbecame a real pipeline: handlers may return{ response }to replace the parsed response before the assistant message is built and tools execute.
Full Changelog: v0.9.0...v0.9.1
v0.9.0
-
[BRK] llm-client / config: the tool-result seam split into two independently-selected seams.
WireFormatowns the model-facing markup shape of harness wrappers (tool results, attached-file includes, system notices):modelToolFormat/--model-tool-formatbecamemodelWireFormat/--model-wire-format(default still"xml", but the shape -- and thefile-include/system-noticemarker protection -- now come from the autoloadedwire-format-xmlextension).RoleMappingowns where the internalharnessrole rides on the wire: the provider/model entrywireFormat: "system-first" | "developer"moved toroleMappingwith the same two ids, registered by the new autoloadedrole-mapping-default.- Tool results now travel as structured parts (tool/status/meta/error/hint/output) through context, session logs, hooks, and UI events instead of pre-rendered text. Rendering is up to each UI. The wire serializer is the only render point for the wire format. Fields are mangled before the format sees them.
- Wire breaks for extension authors: the
turn:endhook payload'sresultiscontent, same for the TOOL_RESULT output event;ToolResult.outputTagremoved;EXTENSION_PROVIDES.TOOL_FORMATSreplaced byWIRE_FORMATS+ROLE_MAPPINGS. A seam name configured but not registered by a handler throwsUnknown wire format/role mapping "<name>" - Config Migration: a stale
wireFormat: "developer"config fails on the first request withUnknown wire format "developer"-- rename the key toroleMapping/role-mapping.
-
added visual display protection from zero-width characters / bidi spoofing (which a malicious llm could use to hide a dangerous command in an innocent-looking
askprompt) by escaping these characters prior to rendering. -
ui-interactive-cli: bracketed pastes whose content is a single line once surrounding newlines are trimmed no longer become a
[Paste #N]marker -- the trimmed content is inserted inline (a pasted line with a trailing Enter behaves like typing it). -
added experimental sysbox layer for bash-tool-- kernel enforced resource limits, syscall-level sandboxing (seccomp launcher C, Landlock fence, capability probes, cgroup DoS limits). -
removed sysbox layer. Fun experiment but I was not confident enough shipping it. For real isolation run hotdog in a container or VM. More info at
docs/postmortems/sysbox.md -
approvals -
user-gate, a tool-call approval layer, above the spawn boundary.- Opt-in (
userGate.enableddefaults false -- withdefault: "ask"an on-by-default gate would prompt every user on every call). - Grammar is
toolortool.param=glob--bash.cmd=git,edit.paths=src,fetch.url=https://docs.example.com/*-- and path values reuseWorkspacerule matcher (same asworkspace.deny), matched against the absolute AND the workspace-relative path. - Precedence
deny>allow>default.denynever prompts and cannot be overridden mid-run.defaultcan be set toask(interactive),allowordeny-- allowlist-only, nothing prompts, unrecognized tools and unanalyzable bash deny too.defaultdenyrecommended in non-interactive mode, like on CI or with-p. - Bash commands are segmented on
;/&&/||/|/newline withcdretargeting later segments, and the analysis bails to an ask on anything that could hide an effect:$(...), backticks,${}/$VAR, parens, brace expansion, unquoted globs, here-documents,xargs,eval,env,find -exec/-delete, and inline-code interpreters (sh -c,python -c,node -e,awk...). - Fails closed -- no seam, non-interactive UI, throwing prompt, aborted run or empty answer all block, with the reason; malformed rule is a startup error and keeps every call blocked until fixed.
- What it does NOT stop: the command is not executed and the final operation isn't checked; nothing enforced at system level once the command is started.
- Nothing is written to config from a prompt; a session allow is in-memory only, keyed on the tool plus every extracted value.
- Opt-in (
-
added
/show-promptcommand to see the active system prompt, as an interactive slash command and theshow-promptsubcommand. -
cli - token usage line reworked; now also shows the context window size.
-
workspace - now warns and continues when some configured roots are missing; errors only when all roots are missing. Also fix workspace handling when a root is
/. -
extensions - an invalid
extension.jsonnow throws at load instead of being silently skipped. Fix nested extension import. -
sessions - resuming with an invalid session id now throws instead of silently starting a fresh session.
-
sessions - interrupted or missing tool results are healed on load, so a session killed mid tool-call no longer produces API errors on resume.
-
agent - fix per-model context-limit lookup; non-default models were falling back to the base limit.
-
compaction - an unrecognized model now resolves the default context limit instead of erroring and skipping compaction.
-
llm-client - connection errors now report the url being connected to.
-
question tool - more robust Ctrl-C handling; clearer error in one-shot mode when no user is connected to answer.
-
info - fix skills display.
-
internals
- tool-executor -
toolCtxis now built (andAGENT_TOOL_CONTEXTfires) BEFORE theTOOL_CALLgate pipeline, and the gate payload carriestoolCtx - bash-tool - add
truncated="true"result metadata info when output is truncated, same as find/grep/etc - hooks - unrecognized hook name now logs a warning instead of being silently ignored.
- tool-executor -
Full Changelog: v0.8.0...v0.9.0
v0.8.0
Added utils/jsx to hook into bun's jsx support as a react-like jsx view layer for the UI, and then refactored most of the webui to use that. There's also a bunch of little QoL things and bug fixes in this release.
-
llm-client - retries now honor a server
Retry-Afterheader (delta-seconds and HTTP-date forms, capped at 60s) instead of the fixed exponential backoff; absent or malformed headers fall back to the existing ladder -
llm-client - error responses are now read up to the 200K cap but quoted in the LlmError message up to 2K (with a
[truncated]marker), so a broken endpoint returning a full HTML page no longer dumps hundreds of KB into the sink, logs, and retry lines -
agent -
run()is now guarded against re-entrancy: a second overlappingrun()throwsAgentError.AlreadyRunninginstead of silently interleaving loop state (iteration count, abort controller, stream replay buffers) -
tool-executor - "Tool 'x' is not available for this agent" now suggests near-matching tools from the set the model was offered (case/separator differences first, then prefix/substring near-matches), so a misspelled or case-flipped tool name self-corrects in one retry
-
cli - unknown flags are now fatal (exit 1) with a "Did you mean" suggestion when a registered flag is a near-match, instead of a warning that let the run proceed with the flag silently dropped
-
session - a throwing channel event handler is now logged at debug level instead of being swallowed silently; other handlers still run
-
package - minimum bun bumped to 1.3.1 (the
--only-failuresflag used by the test scripts landed in 1.3.1) -
internals
- webui - rewrote in jsx
- marker mangler - compiled escape/unescape regexes are now built once per name pair instead of rebuilt per protected prefix on every escape() call
- cleanup session - remove unused SessionManager.deserialize and related unused serialize/deserialize hooks
- task-manager - drop unsafe last-set bus fallback
- task-manager - release the finished task's Agent reference from the registry when a task reaches a terminal state (completed/failed/cancelled); previously dead tasks pinned their full agent context for the manager's lifetime, an unbounded leak on long-lived hosts like the webui
- tool-utils - remove legacy parseToolArgs (its invalid-JSON
{input: ...}fallback was a footgun); subagents now use parseToolInput like every other tool
-
CLI colors now honor the NO_COLOR environment convention and TERM=dumb: color output is disabled regardless of config when either is present
Full Changelog: v0.7.3...v0.8.0
v0.7.3
This release includes some security fixes for grep/find, expands the default workspace path denylist to include cloud creds, and other improvements.
- find/grep tool - fix argument injection in the fd path: a model-supplied pattern starting with
-was parsed as an fd flag (e.g.--exec=rmmade fd run rm on every file found, recursively). Same issue with grep/rg. Dash-leading patterns now route to the find fallback, whose-name/-pathconsume the pattern as a literal, and the positional slot is additionally guarded with a--separator. - workspace denylist defaults expanded with cloud/credential directories:
.aws,.azure,.docker,.gnupg,.kube(alongside.ssh,.config,.git). Projects that legitimately keep a directory of one of these names inside a workspace root can carve it out viaworkspace.deny(rules are last-match-wins, so!.awsafter the positive rule disables it, or configure the exact rule set you want) - fetch tool SSRF blocklist now refuses Teredo (
2001:0000::/32) alongside the existing 6to4/NAT64 refusal -- a Teredo packet carries an unchecked (XOR-obfuscated) IPv4 destination; adjacent2001::allocations such as provider space (2001:4860::/32) and documentation (2001:db8::/32) are unaffected - websocket/webui: renaming a session now sets a display title instead of changing the session's profile; the profile keeps driving behavior (role, tools, model) and is still switched via the profile switcher. Titles are in-memory only and are dropped when a session is rebuilt from a cold log or the server restarts
- ui-session-review-cli - sessions delete subcommand now validates sessionId before confirmation gate
- handoff-tool: listed handoff files that exist and are under the new
handoffTool.autoIncludeFilesUnderBytesconfig (default 24KB) are now auto-inlined into the handoff message instead of just listed; oversized, binary, missing, and directory entries are listed with a note (size,binary,not found,directory, orpath rejected)
Full Changelog: v0.7.2...v0.7.3