Skip to content

Releases: turmind/halo-agent

v2.1.0

Choose a tag to compare

@turmind turmind released this 27 Oct 21:52

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): run halo node --server <url> --root <folder> on your own computer (a laptop, a box behind NAT — no inbound port, no halo setup, no model keys) and the workspace's full-access agents can read, write, edit and search files there with eight node_* tools that mirror the local file tools; --allow-shell (off by default) also lets them run commands, unsandboxed, as your user. Without --root the 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 with node_list in tools: — the bundled default agent has it (workspaces that override the default agent add it themselves); cron jobs can use the tools too. --header passes credentials for an auth proxy in front of the server. macOS and Linux supported, Windows best-effort.

v2.0.8

Choose a tag to compare

@turmind turmind released this 27 Oct 09:46

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 without speaker, 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

Choose a tag to compare

@turmind turmind released this 21 Oct 16:07

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: true with code: 'io'. Before, a sign-in proxy in front of Halo (CloudFront / Midway, oauth2-proxy …) redirecting to its login page showed up as a bare Failed 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

Choose a tag to compare

@turmind turmind released this 20 Oct 11:37

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

Choose a tag to compare

@turmind turmind released this 18 Oct 16:00

Fixed

  • Self-evolution: on an npm install of Halo, evo runs never started and each one ended as timeout after about 36 minutes (three 12-minute heartbeat attempts). This has been the case since 1.6.0. The package was missing dist/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

Choose a tag to compare

@turmind turmind released this 18 Oct 13:32

Added

  • Extensions: live transcription now tells speakers apart. Each finished sentence from the transcribe proxy carries a speaker label ("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 into transcript.md / transcript.jsonl. Captions still being spoken carry no label.

v2.0.3

Choose a tag to compare

@turmind turmind released this 18 Oct 03:19

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 each auth: bearer remote 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, which os.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

Choose a tag to compare

@turmind turmind released this 17 Oct 22:07

Added

  • AgentCore mode: halo agentcore now also serves POST /invocations, so the same image can run as an AgentCore runtime with the HTTP protocol (port 8080, set HALO_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 optional session (a fixed session keeps the job's history from run to run), agent (for a new session) and id. By default the request waits up to 10 minutes and answers with the final reply and the files it named on MEDIA: lines. With Accept: text/event-stream the reply streams as it is written, with a keepalive every 15 s. With "async": true you get a receipt at once, and {"get": "<id>"} reads a job later. A client that disconnects doesn't stop the run. Sending the same id again returns that job instead of running it twice, also after the microVM was replaced, because jobs and their results are kept in the workspace's a2a.db on EFS. A new job on a session whose previous job hasn't finished takes it over; the earlier job then reads superseded, as it does when an A2A message or a relay_send takes the session over. A job cut off by a microVM replacement resumes on the next one, also on a session whose id starts with cron- (the hub skill reuses local cron session ids). Sessions run at the HALO_A2A_ACCESS level, as A2A tasks do.
  • AgentCore mode: HALO_AGENTCORE_NOTIFY names a command run when an /invocations job finishes. It gets the final reply on stdin and HALO_SESSION_ID, HALO_RUN_STATUS (ok / error) and HALO_WORKSPACE in 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 (the notify.py of the hub skill agentcore-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=1 each AgentCore runtime session id gets its own workspace <base>/<session id> on the EFS, created from <base>/.template on its first call. A root process mounts that folder at /workspace and 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_TOKENS and 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 (the agentcore-deploy skill creates its own EFS, access point and role for each deploy). Requests without a valid X-Amzn-Bedrock-AgentCore-Runtime-Session-Id header are refused with 400.
  • Observability: the standard OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true / false environment variable now overrides general.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 is PATCH /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.json file, read from the hub (Settings → Skills → extension → new hub_base, empty = halo-hub on GitHub) or from the address the extension's own manifest names in its new source field. One click downloads the new version, checks it against the sha256 in update.json and 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 restart with a copy button, a reminder that a systemd system unit needs sudo, 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, uses npm_config_registry when the server runs with an https npm mirror, and source builds show "Source build — not checked".

Changed

  • halo agentcore takes its port from HALO_PORT when -p isn'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 in update.json, and a version whose minHalo is newer than the installed Halo is refused with "upgrade Halo first: halo upgrade && halo server restart". A mirror (intranet server, S3, CDN) only needs hub_base set; a hub_repo that isn't on GitHub now needs hub_base set to install by id. Installing from a git URL or a local repo by id is gone — install the zip by path instead. /extension models still takes the newest models-v* release from hub_repo as 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 with 400 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=DEV now always prints the startup line saying it leaves workspace runtime.lock and boot cleanup to the owning server. At the default warn log 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 EBUSY and 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

Choose a tag to compare

@turmind turmind released this 17 Oct 04:23

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_resource for lack of resources, or aborted) is retried the same way. A self-hosted or third-party OpenAI-compatible endpoint must send a finish_reason in 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 shows failed and 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

Choose a tag to compare

@turmind turmind released this 16 Oct 20:08

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>.json that 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 v2 converts 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 ~/.halo is 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.db on 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 v2 once 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.output and last_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 v2 right after upgrading keeps this off the live path.
  • The test-chat session routes (/api/agent-configs/:id/sessions) still read the 1.x .json files only, so sessions created on 2.0 don't appear there.