Repository navigation
Releases: turmind/halo-agent
Releases · turmind/halo-agent
Release list
v2.1.0
Added
- Models: the bundled provider list now matches the halo-hub release
models-v2026.10.27, so a new install offers these without/extension models: GPT-6.1 Terra (AWS Bedrock OpenAI-compatible,global.openai.gpt-6.1-terra; AWS Bedrock Mantle, us-east-1 only), Kimi K3.1 Pro, MiniMax M3.1 Flash (now MiniMax's default model), and Doubao Seed 2.1 Pro / Lite 260915 (Pro 260915 is now Doubao's default) and Seed 2.1 Turbo. - Admin: the bottom panel's tab bar shows the current session title (Chat) or terminal name (Terminal) in its middle — truncated when it doesn't fit, full text on hover, hidden on a too-narrow bar.
- Linked machines (
halo node): runhalo node --server <url> --root <folder>on your own computer (a laptop, a box behind NAT — no inbound port, nohalo setup, no model keys) and the workspace's full-access agents can read, write, edit and search files there with eightnode_*tools that mirror the local file tools;--allow-shell(off by default) also lets them run commands, unsandboxed, as your user. Without--rootthe whole machine is shared. Tokens are created per workspace in Admin → Channels → Linked machines, which shows each machine's online state live. Opt-in per agent withnode_listintools:— the bundled default agent has it (workspaces that override the default agent add it themselves); cron jobs can use the tools too.--headerpasses credentials for an auth proxy in front of the server. macOS and Linux supported, Windows best-effort.
v2.0.8
Changed
- Extensions: speaker labels in live transcription are now opt-in. The transcribe proxy only asks Amazon Transcribe for them, and only splits / labels finals by speaker, when the stream is opened with
speakers=1; by default captions come withoutspeaker, as before 2.0.4. The meeting recorder htrans 1.0.8 adds an "Identify speakers" checkbox next to the language picker, unticked by default.
v2.0.7
Fixed
- Extensions: when an extension reads or writes files (for example the meeting recorder htrans saving audio) and the user's Halo sign-in has expired, the extension is now told so: the fs reply carries
auth: truewithcode: 'io'. Before, a sign-in proxy in front of Halo (CloudFront / Midway, oauth2-proxy …) redirecting to its login page showed up as a bareFailed to fetch. A same-origin login page was worse: the write was treated as successful, so the data was silently lost. Halo's own 401 is reported the same way. Other failures (not found, denied, network, server errors) are reported as before.
v2.0.6
Fixed
- Chat (web): files an agent sends with a
MEDIA:line that live in another workspace, or anywhere else on the server, can now be previewed and downloaded. Before, the chip was clickable but the download failed ("File wasn't available on site"). Markdown previews showing an image by absolute path outside the workspace now load it the same way. Sensitive locations (~/.halo/secrets,~/.ssh,.git-credentials, Halo's global databases …) are still unreadable and show as unavailable, like a missing file.
v2.0.5
Fixed
- Self-evolution: on an npm install of Halo, evo runs never started and each one ended as
timeoutafter about 36 minutes (three 12-minute heartbeat attempts). This has been the case since 1.6.0. The package was missingdist/evo-wrapper.js, the process the server spawns for each run or apply. It now ships, and the bundle build fails if it is missing. When a wrapper fails to start, its error is now written to that run's log in the Evolution tab, and the server logs an[EvoSpawn]error.
v2.0.4
Added
- Extensions: live transcription now tells speakers apart. Each finished sentence from the transcribe proxy carries a
speakerlabel ("0","1"… — a number per stream, not a name), and a sentence spoken by several people arrives as one line per speaker; the meeting recorder extension htrans (1.0.5) shows it as "Speaker N" and writes it intotranscript.md/transcript.jsonl. Captions still being spoken carry no label.
v2.0.3
Fixed
- Settings → Extensions: the header now ends "Last checked HH:MM" with what the check found — "· N updates available" or "· all up to date" — so a check that finds nothing new no longer looks as if the button did nothing.
- Settings: Check for updates (Halo itself and extensions) no longer stays stuck on "Checking…" with the button disabled when the pushed result arrives before the request's own answer (seen behind a CDN). A status older than the one shown is now ignored.
- Settings: tokens for outbound A2A remotes (
a2a.secrets.<remote>for eachauth: bearerremote in~/.halo/secrets/a2a-remotes.yaml) are no longer listed under Orphans with a Remove button. They now have their own A2A remotes section in the System group. A token whose remote is no longer in the list is still an orphan. - Tests (contributors): on Windows the server and cli test suites redirected only
HOME, whichos.homedir()ignores there, so a test run wrote sample values (a.secrets.k,a.secrets.b,a.secrets.k1,b.params.k2,other.secrets.token) into the real%USERPROFILE%\.halo\secrets\settings.yaml. Tests now stay in their temp home on Windows too. If you ran the tests on Windows, remove those entries in Settings → Orphans.
v2.0.2
Added
- AgentCore mode:
halo agentcorenow also servesPOST /invocations, so the same image can run as an AgentCore runtime with the HTTP protocol (port 8080, setHALO_PORT=8080) for callers that can't speak A2A, such as EventBridge Scheduler or a script. A job is{"prompt": "…"}or{"promptFile": "<workspace path>"}, with an optionalsession(a fixed session keeps the job's history from run to run),agent(for a new session) andid. By default the request waits up to 10 minutes and answers with the final reply and the files it named onMEDIA:lines. WithAccept: text/event-streamthe reply streams as it is written, with a keepalive every 15 s. With"async": trueyou get a receipt at once, and{"get": "<id>"}reads a job later. A client that disconnects doesn't stop the run. Sending the sameidagain returns that job instead of running it twice, also after the microVM was replaced, because jobs and their results are kept in the workspace'sa2a.dbon EFS. A new job on a session whose previous job hasn't finished takes it over; the earlier job then readssuperseded, as it does when an A2A message or arelay_sendtakes the session over. A job cut off by a microVM replacement resumes on the next one, also on a session whose id starts withcron-(the hub skill reuses local cron session ids). Sessions run at theHALO_A2A_ACCESSlevel, as A2A tasks do. - AgentCore mode:
HALO_AGENTCORE_NOTIFYnames a command run when an/invocationsjob finishes. It gets the final reply on stdin andHALO_SESSION_ID,HALO_RUN_STATUS(ok/error) andHALO_WORKSPACEin its environment, runs outside the agent sandbox and is killed after 5 minutes. This is how a scheduled job pushes its result to Feishu, Slack, Telegram or WeCom (thenotify.pyof the hub skillagentcore-deploy) without the channel secrets ever reaching the agent. The microVM reports busy while the command runs, so it isn't reaped halfway. - AgentCore mode: with
HALO_AGENTCORE_SESSION_DIRS=1each AgentCore runtime session id gets its own workspace<base>/<session id>on the EFS, created from<base>/.templateon its first call. A root process mounts that folder at/workspaceand hides the rest of the EFS, then runs Halo there as an unprivileged user (uid/gid 1000, no capabilities) with only an allowlisted environment, so channel secrets stay out of the agent's reach; Halo's own tuning variables (HALO_SHELL_TIMEOUT,HALO_MODEL_TIMEOUT,HALO_MAX_CONTEXT_TOKENSand the like) pass through, model API keys don't — models run on Bedrock with the runtime role, or with a key written into the image's/home/node/.halo. If any of that setup fails, the runtime answers 503 instead of serving a half-isolated workspace. This keeps callers on one runtime from seeing each other's files by accident. It is not a hard boundary: the microVM offers no network isolation, so an agent that sets out to can still reach other sessions' folders on the same EFS. Give callers that don't trust each other separate runtimes (theagentcore-deployskill creates its own EFS, access point and role for each deploy). Requests without a validX-Amzn-Bedrock-AgentCore-Runtime-Session-Idheader are refused with 400. - Observability: the standard
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true/falseenvironment variable now overridesgeneral.observability.capture_content, so a container (an AgentCore runtime) can turn prompt and reply text on spans on or off from its environment alone, like the endpoint, headers and service name already could. - Settings → Extensions: every installed extension now has an on / off switch. An extension switched off behaves as if it were uninstalled: it no longer opens its file types, shows no Open with entry, activity-bar page or chat-bar button, its skills disappear and its settings section is hidden. Its files and settings values are kept, and it comes back exactly as it was when you switch it on again. A tab with unsaved changes stays open on it with a "turned off — save your changes" banner, and the "no built-in preview" page names an extension that is off for that file type. A fresh install starts on, an update keeps the switch, and removing an extension forgets it. The switch is stored in
~/.halo/global/extensions/.state.json; over the API it isPATCH /api/extensions/:id { enabled }. - Settings → Extensions: Check for updates in the header, with the time of the last check, and an Update button on every extension a newer version is out for. Each extension's version info comes from one small
update.jsonfile, read from the hub (Settings → Skills → extension → newhub_base, empty = halo-hub on GitHub) or from the address the extension's own manifest names in its newsourcefield. One click downloads the new version, checks it against the sha256 inupdate.jsonand its id and version, and installs it in place; open tabs move to the new version as with any upgrade, and an extension that is off stays off. A version that needs a newer Halo says "needs Halo X — upgrade Halo first" instead of offering the button, and an older version is never offered. A check runs when you open the list and the last one is over a day old, or when you press the button (at most once a minute); results are pushed to every open admin and nothing is checked on a timer. A source that can't be read shows one grey line on that row ("No update info at the source", "Update source unreachable: …"), and offline it stays that way until the next check. - Settings: the version line at the bottom of the sidebar now has Check for updates for Halo itself. When a newer release is on npm it shows "New version X", the command
halo upgrade && halo server restartwith a copy button, a reminder that a systemd system unit needssudo, and a link to the release notes; the desktop app links to the new installer instead. It only notifies — nothing is downloaded or restarted for you. It checks when Settings is opened (at most once a day) or on the button, usesnpm_config_registrywhen the server runs with an https npm mirror, and source builds show "Source build — not checked".
Changed
halo agentcoretakes its port fromHALO_PORTwhen-pisn't given (still 9000 by default)./extension install <id>now installs the version named by<hub_base>/<id>/update.json, the same file the admin's update check reads, instead of picking the newest<id>-v*release through the GitHub / Gitea / Forgejo / GitLab release API or a git tag. Downloads are https only, the package must match the sha256, id and version inupdate.json, and a version whoseminHalois newer than the installed Halo is refused with "upgrade Halo first: halo upgrade && halo server restart". A mirror (intranet server, S3, CDN) only needshub_baseset; ahub_repothat isn't on GitHub now needshub_baseset to install by id. Installing from a git URL or a local repo by id is gone — install the zip by path instead./extension modelsstill takes the newestmodels-v*release fromhub_repoas before.
Fixed
- Cron: creating or editing a job with a timezone that isn't a valid IANA name (e.g. a typo like
Asia/Shangai) is now refused with400 invalid timezone: <value>, and the admin form shows that error when you save. Before, the job was saved, showed as enabled, and never fired; the only trace was one warning in the server log. Jobs written by the cron skill helper (manage-cron.py --timezone) are still not checked and fall back to that one warning. - A dev server started with
HALO_BADGE=DEVnow always prints the startup line saying it leaves workspaceruntime.lockand boot cleanup to the owning server. At the defaultwarnlog level that line was swallowed, so the dev journal showed only the listen banner. - Replacing an extension no longer fails when the old copy can't be deleted right away (Windows reported
EBUSYand the upload or update answered 500 although the new version was already in place). The removal is retried and, if it still fails, only logged.
v2.0.1
Fixed
- Models reached over the OpenAI chat-completions format (DeepSeek, Kimi, GLM, Doubao, Hunyuan, Gemini, generic OpenAI-compatible endpoints): when the model's reply stream stops partway through without an error, the turn is now retried with the usual backoff, as it already was for Claude and the Anthropic API format. Before, it ended as an empty or cut-off reply, which a sub-agent reported as
(no output). On the retry the model thinks again from the start, so its thinking streams a second time. A DeepSeek reply the server cut off (finish_reason: insufficient_system_resourcefor lack of resources, oraborted) is retried the same way. A self-hosted or third-party OpenAI-compatible endpoint must send afinish_reasonin its stream: one that never does is now retried up to the limit on every turn before it fails. - Cron: a one-shot job still running when the schedule was reloaded or edited is no longer marked
missed; it ends with its real status. After a server restart, a one-shot whose run was cut off showsfailedand is disabled, instead of staying enabled past its time. A job whose schedule or timezone the scheduler can't parse logs one warning, not one on every check.
v2.0.0
Changed
- Sessions are stored in a new format. Each session is now two append-only logs,
<id>.ui.jsonl(what the chat shows) and<id>.raw.jsonl(the model's history), instead of one<id>.jsonthat was read, merged and rewritten whole on every save. A save now writes only what changed: on a 31 MB session a save blocked the server for about 400 ms and rewrote 31 MB; it now takes under 1 ms and appends a few KB. Title, turn count, tokens and the latest output now live only in the workspace database (halo.db); internal evolution / scoring sessions get a database of their own,~/.halo/global/internal-sessions/halo.db. - Long chat history is archived when a turn ends, once the active log passes 5 MB (was: during a compact, over 3 MB). A compaction summary is never where the archive cuts, so it stays next to the turn it summarises.
- An interrupted reply is saved once, under the same id it streamed with, so after a reload the partial answer appears once instead of being written again under a new id on every save.
- New command
halo sessions migrate v1 v2converts every 1.x session of a workspace at once. Stop the server first. - Less file-system work per request and per tool call, which matters most when the workspace or
~/.halois on network storage (NFS / EFS, as in AgentCore mode): a workspace's.halo/folders are set up once per server process instead of on every request; sandboxed file and shell tools look up the files they hide with one directory listing instead of one check per file (still on every call, so a sensitive file created later is still hidden); session saves no longer check the file before every write, and a save with nothing new no longer touches the disk; the log file's folder is no longer re-created before every line; AgentCore's health check and lease heartbeat each skip one lookup. If another process (a cron run, the TUI,halo cli -s) writes a session between this server's turns, the next turn first reads what it wrote instead of overwriting it — both the chat log and the model history keep every turn.
Fixed
- Deleting an exchange from the chat now finds its turn in the model's history by the message's arrival time instead of its text. Before, with the same short prompt sent more than once ("continue"), it could remove a different turn from the model's history than the one removed on screen. A message merged into one turn with others (sent while the agent was busy), or sent with a skill or an @scope, can now be deleted on its own. A message the model never received (lost to a restart while queued) is refused instead of matched by its text.
- A command an agent was running when the server restarted is now reported to it as cut off by the restart, with the output it had produced so far, instead of as an ordinary failure. Once a restart begins, agents start no new commands, model calls or automatic context compactions, so no tokens are spent on a reply the restart would cut off (a call already under way finishes or is cut off as before). Stopping an agent during that window still works. Their work resumes after the restart as before.
- After a restart, an agent that was idle while only its sub-agents were running is now told exactly that, with the ids of the sessions in its tree that were cut off. Before, it was told its own work had been interrupted mid-step.
- AgentCore mode: the workspace databases (
halo.db,a2a.db,runs.dbon EFS) now flush every commit to storage. Before, a commit could sit in the microVM's cache for seconds, so a microVM that died could lose its last task updates — a task the caller had just been given unknown to the next microVM, a completed task reported as FAILED, or an interrupted run not resumed. Costs about one storage flush per commit (~10 per turn plus one per tool call). Normal server mode is unchanged. - Claude on Bedrock, and models reached over the Anthropic API format (Anthropic, MiniMax, Qwen, Anthropic-compatible endpoints): when the model's reply stream stops partway through without an error, the turn is now retried with the usual backoff. Before, it ended as an empty reply, which a sub-agent reported as
(no output). On the retry the model thinks again from the start, so its thinking streams a second time. - Cron: the first scheduled run after a server restart, and a one-time run, no longer get skipped without any trace. About 1 in 10 first runs after a restart used to be skipped: no run, no error, no log line, and a one-time task never ran at all. This happened when the timer woke a fraction of a millisecond early and the scheduling library (croner 10.0.1) then recalculated past the run it was about to start. Halo now runs the timers itself and uses croner only to work out the run times. A timer that wakes early keeps waiting for the same run time. A "schedule invalid" or crashed scheduled run is now logged at warn level, so it shows up with the default log level.
Upgrade notes
- Session store 2.0: a 1.x session is converted the first time it is opened. After upgrading, stop the server and run
halo sessions migrate v1 v2once in every workspace root to convert the rest (it also finishes conversions cut off before their old files were removed); it exits non-zero if any session failed, and those sessions keep their old files. Each session's 1.x files are deleted once its new files have been read back and checked, so going back to 1.x afterwards shows converted sessions (and sessions created on 2.0) empty. - Workspace sqlite migration v3 adds
agent_sessions.outputandlast_activity_at. - While one process converts a 1.x session (the server, a cron run, the TUI), another opening the same session waits for it, up to 10 s, blocking that process meanwhile. Running
halo sessions migrate v1 v2right after upgrading keeps this off the live path. - The test-chat session routes (
/api/agent-configs/:id/sessions) still read the 1.x.jsonfiles only, so sessions created on 2.0 don't appear there.