Releases: Wixely/DaggerAgent
Release list
v1.12.0: DaggerAgent 1.12.0
DaggerAgent 1.12.0
A Banter tab that saves, and reads in the order the steps happen.
The Bant tab could not change the server address. Its
is staticmarkup that survives replaceChildren(), and each render added another
submit listener, so one Save fired several POSTs at once - each stale
handler carrying the values from the render that made it. Whichever
landed last won, and the field reloaded with the old address. The
trigger options form had the same defect. Both now assign
form.onsubmit, which replaces the handler instead of stacking it.
The tab is now laid out as the dependency actually runs: 1. Server &
identity, 2. Enrol this machine, 3. Connect, with room settings
unnumbered at the end because none of them are needed to get into a
room. Enrolment redeems its one-time code against the server named in
step 1 rather than the last saved one, and step 3 says plainly when
there is no credential yet.
Spacing: every .endpoint-form rule is scoped under .endpoints-list and
this form is not, so the tab had been rendering with no field gaps,
default-size labels and .row-2 stacking instead of pairing. It has its
own .banter-pane styles now. The status pill's bg-success / bg-warning
/ bg-danger were referenced but never defined, so connected,
reconnecting and evicted all came out grey.
v1.11.0: DaggerAgent 1.11.0
DaggerAgent 1.11.0
Structured questions, and a Banter connection that tells the truth.
ask_user (#10's sibling for Dagger's own surfaces): a built-in tool
that asks whoever is driving the job a question with clickable
options - the same shape as Claude Code's AskUserQuestion and Banter's
ask_operator. In the web transcript it renders as a card: options
stacked as full-width buttons with radio/checkbox marks, descriptions,
a free-text field that always rides along, and single-select answering
on the click. In the interactive TUI the question renders with
numbered options and the input line answers it - a number picks,
comma-separated numbers for multi-select, anything else is free text,
an empty line skips. With nobody attached (CLI one-shot, trigger jobs,
Banter rooms, sub-agents) the tool answers immediately that there is
no one to ask. Wait bounded by Tools:AskUserTimeoutSeconds.
Banter mode, on SDK 0.2.0:
- Room tools ride along with Dagger's registry instead of being
replaced by it (#10): ask_operator, the delegator's open_side_room /
invite_agent, and server-granted tools, sourced from
BanterAgent.ToolsFor(room) each turn and dispatched back through the
SDK. New Banter:RoomTools switch for unattended fleets, and
Banter:AskTimeoutSeconds. - Honest connection state: "reconnecting (attempt n)" through an
outage (the SDK redials with jittered backoff, re-authenticates with
a fresh signed nonce, rejoins and re-announces); "evicted" with the
server's reason when the identity was revoked or reissued - terminal
on purpose, since automating re-enrolment would turn revocation into
a self-healing bypass. Auto-connect retries a server that is still
booting with capped, jittered backoff. - The Bant tab shows the attributes the server actually GRANTED next
to the requested ones, flagged when an admin override is in effect. - Standalone
dagger banterreports an eviction and its reason
instead of exiting as if someone pressed Ctrl+C.
Fix: an empty ConversationState.Model now means "the endpoint's
default decides" instead of overriding it with an empty model id,
which the OpenAI serializer rejects.
v1.10.0: DaggerAgent 1.10.0
DaggerAgent 1.10.0
Manage the Banter connection from the web UI: a Bant tab in service
mode (dagger serve) holds the room connection in-process, so no
second dagger banter process is needed.
- Status card: connection state, identity, rooms, key fingerprint and
last error, with Connect / Disconnect buttons. - Enrolment box: paste a one-time code from the Banter client's agents
page; the machine makes its own key (DPAPI-protected on Windows),
the enrolled nick and fingerprint come back, and the config is
updated to match - the leftover password is cleared, so Connect
just works. An existing key is never overwritten. - Config section persisted to runtime-config.json: server, user,
rooms, key path, password (legacy), LLM endpoint + model for room
turns, routing attributes, system prompt, and auto-connect on
service start (Banter:AutoConnect). - Scriptable at /agent/banter: GET, POST /config, /enrol, /connect,
/disconnect. The password never leaves the server (hasPassword
only); the key surfaces only as its path and public fingerprint. - New Banter:EndpointId pins room turns to a configured LLM endpoint
instead of the active default. - Fix: an empty ConversationState.Model now means "the endpoint's
default decides" instead of overriding it with an empty model id,
which the OpenAI serializer rejects ("Empty encoded value").
Verified in a real browser against a live Banter server: enrol from
the tab, key login (signed nonce), and a room mention answered through
a configured LM Studio endpoint.
v1.9.0: DaggerAgent 1.9.0
DaggerAgent 1.9.0
Banter mode (#9): DaggerAgent as an agent in a Banter room server.
dagger banter logs into a Banter server as an ordinary agent user,
joins rooms, and answers with DaggerAgent's own LLM/tool turn loop -
tools, sub-agents, job persistence and all. The Banter.Agents.Sdk
decides when to speak (delegation, @mentions, egress rules);
DaggerAgent decides what to say. One conversation per room.
Identity is key-backed, per the issue:
dagger banter --enrol <code>redeems the one-time enrolment code
from the Banter desktop client's agents page: it generates a P-256
keypair locally, sends only the public half, keeps the private key,
and prints the identity and key fingerprint. The code is spent
either way, and enrolment is its own invocation on purpose.- The key at rest is DPAPI-wrapped for the current user on Windows;
elsewhere it is the SDK's user-only-permissions file. Keys enrolled
by banter-warden still load. - An existing key file is never overwritten (it would strand the
identity), and a truncated or foreign key is reported as itself
before connecting rather than as "invalid credentials". - A configured password still works, but is refused when a key file
also exists - whichever silently won, the other would be the stale
credential nobody noticed.
Configuration lives in the new Banter appsettings section (server,
user, key file, rooms, routing attributes, model, system prompt);
--server, --user, --key, --pass and --rooms override.
Builds against Banter.Agents.Sdk 0.1.0 from the Wixely feed
(first published Banter release, tagged for this).
v1.8.0
Live tool progress end to end: SSE heartbeat, delegated-run visibility, ACP client with permission forwarding, Codex app-server support.
-
Live
tool_progresson the jobs SSE stream. While any tool call is in flight, a
frame each second lists every running call with its elapsed time; the web UI turns the
pending ellipsis into a live counter.ToolCallEventgainedCallIdand
ParentCallId, so a sub-agent's tools attribute to thespawn_subagentcall they run
under and render nested (spawn_subagent … 5.9s ↳ exec_shell 5.9s).tool_resultnow
carries the notifier's own measureddurationMs. A keep-alive comment goes out every
15s when nothing is running, so an idle-timeout proxy can no longer drop the connection
mid-turn — which cancelled the turn and killed any delegated CLI with it. -
delegate_to_claudestreams. Switched to--output-format stream-json; the run's
own tool calls surface live as children of the delegation (delegate_to_claude ↳ PowerShell 0.2s). The terminal result event carries the same envelope as before, so
session resume and usage logging are unchanged. A run killed mid-stream now salvages
the assistant text seen so far. Verified against claude 2.1.161. -
ACP client:
delegate_to_acp_<name>tools.Tools:AcpAgentsentries spawn
long-lived agents speaking ACP over stdio (e.g. another DaggerAgent viadagger acp),
pooled per (job, agent, cwd) — successive delegations continue one live session, no
per-call spawn, no session-id juggling. The agent's activity feeds the same
tool_progresspipeline. Gated byAllowCliDelegation. -
Permission forwarding.
PermissionPolicy: deny | allow | askper ACP agent.ask
routes the child agent's permission request to whoever is driving the job: the web UI
renders inline Allow/Reject buttons in the transcript (permission_requestframe,
POST /agent/permissions/resolve), and an agent-side ACP session forwards it upward as
asession/request_permissionof its own — editor ⇄ DaggerAgent ⇄ delegated agent.
Nobody driving, or no answer inPermissionTimeoutSeconds, falls back to deny. -
Codex app-server protocol.
Protocol: codex-app-serveron an agent entry drives
Codex's JSONL dialect (thread/turn/item, approvals answered accept/decline) behind the
same tool surface, pool, progress feed and permission path. Coded against the app-server
documentation and verified against a protocol-faithful stand-in; not yet smoke-tested
against a real codex binary.
v1.7.2
Same code as v1.7.0 and v1.7.1 — this release exists to fix how release notes are produced.
No functional change since v1.7.0: the only commits since are to the release
workflow itself, so the binaries are equivalent. v1.7.0 published its last commit
message as the release body and v1.7.1 published generated notes, both because the
annotation was read from the checked-out repo. actions/checkout resolves the ref to
a commit and force-fetches it over refs/tags/, so the annotation is unreachable
locally no matter how checkout is configured. The notes are now read over the API
instead. If you are reading this text on the release page, that fix works.
The full contents of the 1.7.x line:
-
Tool-call events.
IToolCallSinkpublishes a started/completed pair around every
tool invocation, carrying job id, depth, tool name, duration, outcome and result size.
Published fromLlmAgent.WrapTools, so it covers the streaming and non-streaming turn
paths alike — CLI mode and the non-streaming/jobsendpoints previously produced no
tool activity at all. Events carry no raw arguments:ArgsDigestis the argument names
plus a hash of the values, so a repeated call stays recognisable without arguments
reaching a display. Subscribe by resolvingToolCallSink. -
Tools.CliDelegationTimeoutSeconds(default 300, runtime-tunable). A delegated
claude/codex/copilot run was previously bounded only by the parent agent's cancellation. -
Tool-result offloading never fired.
OffloadingAIFunctiontestedresult as string,
butAIFunctionFactorymarshals return values through System.Text.Json and hands back a
JsonElement, so the test was null every time.MaxToolResultCharsand the
read_tool_result/head_tool_result/tail_tool_result/grep_tool_resultfamily
had been inert for every built-in tool. -
Delegated CLI sessions did not survive a restart.
CliSessionStorewas in-memory, so
a restart silently dropped every delegated conversation'ssession_idand the next call
cold-started an amnesiac CLI. Now backed by acli_sessionstable, cleared with its job. -
Timed-out child processes discarded their output.
exec_shelland CLI delegation read
their pipes with a cancellation token, andReadToEndAsyncthrows its whole buffer away
when that fires — so a command that ran the full timeout returned nothing at all. The
kill now closes the pipes and the captured output is returned with the error. -
Two high-severity advisories.
Microsoft.Data.Sqliteto 10.0.11 (clears
GHSA-2m69-gcr7-jv3q via SQLitePCLRaw 2.1.12) and a directMicrosoft.Bcl.Memory10.0.11
pin overriding the vulnerable 9.0.4 thatMicrosoft.ML.Tokenizers.Data.O200kBasepins
(GHSA-73j8-2gch-69rq). -
Release notes ignored the tag annotation (this release).
Offloading becoming active is a real behaviour change. At the default 16 000 characters a
wide grep or a large read_file now returns a placeholder and the model must fetch the
content via the tool-result tools. Raise Tools.MaxToolResultChars to tune it, or set 0
to disable.
v1.7.1
v1.7.0
Make tool-result offloading actually fire
OffloadingAIFunction tested "result as string" and returned early when it was
null. AIFunctionFactory marshals return values through System.Text.Json, so a
tool declared as returning string hands back a JsonElement and that test was
null every single time. Offloading has therefore never run: MaxToolResultChars
and the whole read_tool_result / head_tool_result / tail_tool_result /
grep_tool_result family have been inert for every built-in tool.
Demonstrated by passing a null store with a 100-char threshold and a 5 000-char
result: it returned the result inline and did not throw, so the offload branch
was never entered.
Use ToolResultText.AsText, added in the previous commit for the same reason.
Non-string JSON offloads on its raw text - a large object costs the same
context as a large string.
Note this changes live behaviour for the first time. At the default 16 000
chars a wide grep or a large read_file now returns a placeholder instead of
inlining, and the model has to fetch the content. Verified end to end that the
payload survives byte for byte and that grep_tool_result retrieves it, since
those consumers had never actually been exercised on this path. Raising
MaxToolResultChars tunes it without reverting the fix.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
v1.6.0
What's Changed
Full Changelog: v1.5.0...v1.6.0