You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.