@turmind/halo 1.6.3 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +58 -67
  2. package/admin-out/404.html +1 -1
  3. package/admin-out/_next/static/chunks/989-5d7743ff56d00dd7.js +1 -0
  4. package/admin-out/_next/static/chunks/app/page-8881b4f87a3d5ae8.js +1 -0
  5. package/admin-out/index.html +1 -1
  6. package/admin-out/index.txt +4 -4
  7. package/bin/halo.js +4 -0
  8. package/bundled-docs/design/architecture.md +1 -1
  9. package/bundled-docs/design/canvas-extensions.md +2 -2
  10. package/bundled-docs/design/observability.md +7 -5
  11. package/bundled-docs/design/storage.md +8 -5
  12. package/bundled-docs/design/wechat.md +1 -1
  13. package/bundled-docs/dev/add-model-provider.md +2 -1
  14. package/bundled-docs/dev/api.md +2 -1
  15. package/bundled-docs/dev/deploy.md +114 -26
  16. package/bundled-docs/dev/desktop-packaging.md +13 -1
  17. package/bundled-docs/dev/env.md +3 -3
  18. package/bundled-docs/dev/logging.md +13 -2
  19. package/bundled-docs/dev/tools.md +2 -2
  20. package/bundled-docs/guide/agents.md +2 -2
  21. package/bundled-docs/guide/cli.md +20 -8
  22. package/bundled-docs/guide/getting-started.md +13 -5
  23. package/bundled-docs/guide/workspace.md +2 -0
  24. package/bundled-docs/requirements/agent.md +1 -1
  25. package/bundled-docs/requirements/explorer.md +14 -2
  26. package/bundled-docs/requirements/settings.md +2 -2
  27. package/dist/index.js +2565 -1449
  28. package/package.json +3 -2
  29. package/templates/models/gemini.yaml +93 -0
  30. package/admin-out/_next/static/chunks/989-ddba0eb72566da23.js +0 -1
  31. package/admin-out/_next/static/chunks/app/page-7ffcbddb49dc56c4.js +0 -1
  32. /package/admin-out/_next/static/{k5zuJrivSdbaPifBatwdr → kfp1cMc36J6hjZ06L9mm-}/_buildManifest.js +0 -0
  33. /package/admin-out/_next/static/{k5zuJrivSdbaPifBatwdr → kfp1cMc36J6hjZ06L9mm-}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -8,25 +8,43 @@
8
8
 
9
9
  **Throw in an idea. A team of agents builds it.**
10
10
 
11
- Halo is a multi-agent workspace you drive in plain language. A primary agent reads your intent, breaks the work down, and fans it out to sub-agents that run in parallel — while every reasoning step and tool call streams by live, and you interrupt, redirect, or take over whenever you like. Everything the team knows and produces is plain files in a workspace you can read, edit, `git clone`, and share. No hidden memory. No opaque state.
11
+ A self-hosted AI agent team that works in your project folder. Bring your own model key and drive it from a browser, Slack, Telegram or the terminal. A primary agent breaks the work down and fans it out to sub-agents that run in parallel; every tool call shows up live, and you can interrupt, redirect or take over at any point. Everything the team knows and produces is plain files in the workspace — no hidden memory, no opaque state.
12
12
 
13
13
  ![Halo admin — a landing page assembled by parallel sub-agents, previewing live in Canvas](assets/workspace-hero.jpg)
14
14
  <p align="center"><sub>One prompt — <i>“Build a landing page for our coffee roastery… split the sections across sub-agents in parallel, then assemble.”</i> Executors fan out, report back, and the assembled page previews in Canvas, all in one browser tab.</sub></p>
15
15
 
16
16
  ## Install
17
17
 
18
+ Halo runs on Linux (x64 / arm64), macOS and Windows, and needs **Node.js 22+**. Ubuntu 24.04's own `nodejs` package is 18 and won't work (`halo` refuses to start on it), so get 22 first:
19
+
20
+ ```bash
21
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs # Ubuntu / Debian
22
+ brew install node@22 # macOS (keg-only: add it to PATH as brew prints)
23
+ nvm install 22 # with nvm (or: fnm install 22)
24
+ ```
25
+
18
26
  ```bash
19
27
  npm install -g @turmind/halo # one binary, all subcommands
20
28
  halo setup # interactive: password / port / model keys / optional skills
21
29
  halo server start # → open http://localhost:9527
22
30
  ```
23
31
 
24
- Bring any one model key — Anthropic, OpenAI (or any OpenAI-compatible endpoint), DeepSeek, Kimi, Qwen… `halo setup` asks for it and binds it to the default agent. An AWS account is **not** required; Bedrock is just one of the providers.
32
+ Bring any one model key — Anthropic, OpenAI (or any OpenAI-compatible endpoint), DeepSeek, Kimi, Qwen… An AWS account is **not** required; Bedrock is just one of the [providers](#models) (and can use your AWS credential chain instead of a key).
33
+
34
+ - **`sudo`**: with a system-wide Node (apt / NodeSource), `npm install -g` needs `sudo`; with nvm / fnm it doesn't.
35
+ - **In `halo setup`**: at *Configure model provider keys now?* pick **Configure…** (the default). Pick your provider and paste the key, choose **── Done** (the last row; ↑ wraps to it), then press Enter on *Switch default agent to …*.
36
+ - **On a remote box**: run `halo server start -d` (plain `start` stays in the foreground and dies with your SSH session), tunnel with `ssh -L 9527:localhost:9527 user@host`, and open `http://localhost:9527` on your machine. Don't open 9527 in a security group — see [Status & Limitations](#status--limitations).
37
+ - **Linux sandbox**: the `workspace` / `readonly` access levels need `sudo apt install bubblewrap`, and on Ubuntu 23.10+ / 24.04 also an [AppArmor profile](.halo/docs/dev/deploy.md#bubblewrap-on-ubuntu-2310-and-2404-apparmor). Without it `/api/health` shows `sandbox: null` and non-full sessions can't run shell.
38
+ - **Upgrade**: `halo upgrade && halo server restart`, or under systemd `halo upgrade && sudo systemctl restart halo` ([deploy.md Option C](.halo/docs/dev/deploy.md#option-c-systemd-for-the-npm-install)). Bundled docs, agents and skills refresh on the next start.
39
+ - **Docker / CI**: `halo setup --non-interactive`. `HALO_PASSWORD` is the *admin* password only; `HALO_DEFAULT_PROVIDER=<provider>` binds the built-in agents without a prompt. Model keys go in `~/.halo/secrets/settings.yaml`, e.g. `api_key: "<<DEEPSEEK_API_KEY>>"` under `deepseek.secrets` with `DEEPSEEK_API_KEY` set at runtime ([env.md](.halo/docs/dev/env.md)).
40
+ - **From source**: `pnpm install && pnpm build` (pnpm 11).
25
41
 
26
- Prefer a desktop app? Each [GitHub release](https://github.com/turmind/halo-agent/releases) ships a **macOS (Apple Silicon) `.dmg`** and a **Windows x64 `.exe`** that bundle the server + admin with their own Node runtime — no `npm` needed. Same features, same `~/.halo/` data layout, so you can switch between the two later.
42
+ **Desktop app**: [GitHub releases](https://github.com/turmind/halo-agent/releases) carry a Windows x64 `.exe` and a macOS Apple Silicon `.dmg` (not every release has both) that bundle the server, admin and their own Node runtime — no `npm`, same `~/.halo/` data, so you can switch later. They're unsigned (macOS "damaged" → `sudo xattr -dr com.apple.quarantine /Applications/Halo.app`; Windows SmartScreen may warn).
27
43
 
28
44
  > [!IMPORTANT]
29
- > **First message fails with `Could not load credentials from any providers`?** The built-in `default` agent ships pointing at AWS Bedrock, and you skipped the *"Bind the default agent to a provider you just configured?"* step in `halo setup`. Either re-run `halo setup`, or open **Agents → default** in the admin and switch its model provider. (Bedrock users with a working AWS credential chain are unaffected.) Details in [Quick Start](#quick-start).
45
+ > **First message fails with `Could not load credentials from any providers`?** The built-in agents ship pointing at AWS Bedrock, and the *"Bind the default agent to a provider you just configured?"* step in `halo setup` was skipped. Re-run `halo setup` — it rebinds every built-in agent, the internal `/evo` ones included — or switch the model provider under **Agents → default**.
46
+ >
47
+ > **On EC2 / ECS with an instance role there is no error**: an unbound install quietly uses Bedrock Claude on that AWS account instead. Check which model answers in the footer of `halo cli -v "hi"`, or under **Agents → default**.
30
48
 
31
49
  ## Why Halo
32
50
 
@@ -52,11 +70,11 @@ my-project/
52
70
  └─ src/ … # your actual project
53
71
  ```
54
72
 
55
- `git clone` the workspace and the recipient gets a complete, runnable agent team — not an export, the real thing. It's also what makes self-evolution possible: the evolution agent edits real files, and you review real diffs.
73
+ Commit `.halo/` with your code and whoever clones it gets the same agents, skills and knowledge — the real files, not an export (they bring their own model key); `/workspace share` zips the config without sessions, memory or secrets. Initializing the repo from Halo's Source Control panel writes a `.gitignore` for sessions, logs, `tmp/` and databases; in an existing repo, add those yourself, and keep real keys out of `.halo/settings.yaml` (use `<<ENV>>` placeholders).
56
74
 
57
75
  ### 🌐 One workspace, every screen
58
76
 
59
- Kick off a build in the browser, check progress from WeChat on the metro, drop a follow-up in Telegram or Slack, finish in the terminal. Every channel connects to the *same* workspace and session — channels are doors, the workspace is the room.
77
+ Kick off a build in the browser, check progress from WeChat on the metro, drop a follow-up in Telegram or Slack, finish in the terminal. Every channel connects to the *same* workspace — channels are doors, the workspace is the room — and a full-access account can `/session switch` to pick up a conversation started elsewhere.
60
78
 
61
79
  <p align="center">
62
80
  <img src="assets/wechat-phone.jpg" alt="Halo in WeChat" width="270" />
@@ -67,71 +85,32 @@ Kick off a build in the browser, check progress from WeChat on the metro, drop a
67
85
 
68
86
  ### 👁 Watch it think
69
87
 
70
- Every reasoning step, tool call, and file change streams in real time — including inside sub-agents. Interrupts are graceful (conversation repair, not a hard kill), and sub-agents report back when done. You stay in the loop instead of running and praying.
88
+ The primary agent's reasoning, every tool call (sub-agents' included) and every file change stream in real time; a sub-agent's full log opens from the **Sessions** tab. Interrupts are graceful (conversation repair, not a hard kill), and sub-agents report back when done. You stay in the loop instead of running and praying.
71
89
 
72
90
  ![Chat panel — the primary agent dispatching three executor sub-agents in parallel](assets/orchestration.jpg)
73
91
  <p align="center"><sub>The primary agent fans out three executors in parallel — every <code>start_session</code> and tool call visible as it happens.</sub></p>
74
92
 
75
- And when you'd rather have ambience than logs: [Halo City](#halo-city) renders the same runtime as a living pixel town.
76
-
77
93
  ## Also in the box
78
94
 
79
- - **11 model providers, one runtime** — provider and model set per-agent: Claude on Bedrock for the heavy lifting, a cheap regional model for routine sub-tasks. No code changes.
80
- - **IDE-grade admin** — chat + Monaco editor + file explorer + git panel + terminal (xterm.js) in one browser tab.
81
- - **Skills** — markdown skill definitions injected into prompts on demand; workspace-scoped or global, no code required.
95
+ - **Per-agent models** — each agent picks its own provider and model: Claude on Bedrock for the heavy lifting, a cheap model for routine sub-tasks. No code changes.
96
+ - **IDE-grade admin** — chat + Monaco editor + file explorer + git panel + terminal (xterm.js) in one browser tab, in four themes.
97
+ - **Skills and agents are files** — a skill is markdown injected on demand; an agent is a persona plus an `agent.yaml`. Workspace-scoped or global, no code required.
98
+ - **Preview extensions** — installable viewers and small apps for the editor: 3D models, draw.io, Excalidraw, Jupyter notebooks, a meeting recorder and more from [halo-hub](https://github.com/turmind/halo-hub). Install with `/extension install <id>` or a zip under **Settings → Extensions** ([design](.halo/docs/design/canvas-extensions.md)).
82
99
  - **Cron tasks** — scheduled agent runs (recurring or one-shot) whose output fans out to your chat channels.
83
- - **Permission isolation** — `full` / `workspace` / `readonly` access levels enforced by a bubblewrap sandbox (filesystem only — see [Status & Limitations](#status--limitations)).
84
- - **ACP adapter** — plug a halo workspace into Claude Code as a native ACP agent, or let one halo delegate to another.
100
+ - **Access levels** — `full` / `workspace` / `readonly` per session, enforced by an OS sandbox (bubblewrap on Linux, Seatbelt on macOS); a sub-agent never gets more access than its parent ([how levels are set](.halo/docs/guide/delegation-and-access.md#5-access-levels), [limits](#status--limitations)).
85
101
  - **Structured sessions** — hierarchical parent ↔ child sessions with async coordination and auto-reports on completion.
86
-
87
- ## Recent highlights
88
-
89
- - ☁️ **AgentCore runtime mode** — run Halo as an Amazon Bedrock AgentCore Runtime (A2A protocol) with `halo agentcore`, on an EFS-backed workspace.
90
- - 🎨 **Four UI themes** — dark, light, midnight, warm; synced server-side so every browser gets your pick.
91
- - ⌨️ **TUI overhaul** — reworked input, verbose mode, and persistent history in the standalone terminal client.
92
- - ✂️ **Graceful interrupts, fully surfaced** — interrupted tool calls are repaired and shown in the session UI instead of vanishing.
93
-
94
- ![Four themes — dark, light, midnight, warm](assets/themes.jpg)
95
-
96
- ## Quick Start
97
-
98
- Published on npm as [`@turmind/halo`](https://www.npmjs.com/package/@turmind/halo) — one binary, all subcommands. After the three install commands above, open **http://localhost:9527**.
99
-
100
- | Prerequisite | Notes |
101
- |---|---|
102
- | Node.js >= 22 | the only hard system requirement |
103
- | An API key for any supported model provider | entered during `halo setup`; AWS Bedrock users can leave keys unset and use the standard credential chain (env / `~/.aws` / instance role) instead |
104
- | pnpm >= 9 | source builds only |
105
-
106
- - **Bind your provider to the agent**: agents choose providers, and the built-in `default` agent ships pointing at AWS Bedrock. When you configure a different provider, `halo setup` ends by offering to rebind the built-in agents to it (with a single provider and no Bedrock keys, Enter accepts). Skipped it? Re-run `halo setup`, or switch once in **Agents → default → model provider**.
107
- - **Upgrade**: `halo upgrade && halo server restart`. The startup check refreshes bundled docs / agents / skills automatically when the on-disk template version is behind.
108
- - **Docker / CI**: `halo setup --non-interactive` and supply credentials via the `HALO_PASSWORD` env var; `HALO_DEFAULT_PROVIDER=<provider>` binds the built-in agents without a prompt.
109
- - **From source**: `pnpm install && pnpm build`.
110
-
111
- ### Talk to it from curl
112
-
113
- Every workspace can expose a token-authenticated HTTP + SSE endpoint — the Web channel, aka the "build your own UI" channel:
114
-
115
- 1. In the admin, open **Channels → Web → Add Account**, pick a workspace and access level, and copy the token (shown once).
116
- 2. Stream a conversation:
117
-
118
- ```bash
119
- curl -N -H "x-token: $TOKEN" -H "Content-Type: application/json" \
120
- -d '{"message":"What files are in this workspace?"}' \
121
- http://localhost:9527/api/web/chat
122
- ```
123
-
124
- The response is SSE frames — `session` / `thinking` / `tool_call` / `stream` / `complete` — full protocol in [`.halo/docs/guide/channels/web.md`](.halo/docs/guide/channels/web.md).
102
+ - **Beyond one workspace** — [relay](.halo/docs/design/relay.md) work to another workspace on the same server, talk [A2A v1.0](.halo/docs/design/a2a.md) with other agent servers, or run headless as an Amazon Bedrock [AgentCore](.halo/docs/design/agentcore.md) Runtime with `halo agentcore`.
103
+ - **Observability** — set one OTLP endpoint and traces, metrics and logs (GenAI semantic conventions) go to any OpenTelemetry collector; `/api/metrics` serves Prometheus gauges ([design](.halo/docs/design/observability.md)).
125
104
 
126
105
  ## Models
127
106
 
128
- Configured per-agent through one provider-agnostic runtime. AWS Bedrock Claude is the primary target; the rest are first-class.
107
+ Configured per-agent through one provider-agnostic runtime. AWS Bedrock Claude is the primary target; the rest are first-class. `/extension models` pulls updated provider configs from the hub without a Halo release.
129
108
 
130
109
  | Provider | Notes |
131
110
  |---|---|
132
111
  | **AWS Bedrock Claude** | Primary — Bedrock Invoke API |
133
112
  | AWS Bedrock Mantle | OpenAI GPT-class models via Bedrock |
134
- | AWS Bedrock OpenAI-compatible | GPT-6 / 5.6, Grok 4.6, Kimi K3 via the bedrock-runtime OpenAI Responses API |
113
+ | AWS Bedrock OpenAI-compatible | GPT, Grok, Kimi and GLM models via the bedrock-runtime OpenAI Responses API |
135
114
  | Anthropic | Direct API |
136
115
  | OpenAI | Direct / any OpenAI-compatible endpoint |
137
116
  | DeepSeek | |
@@ -142,12 +121,13 @@ Configured per-agent through one provider-agnostic runtime. AWS Bedrock Claude i
142
121
  | Hunyuan (Tencent) | |
143
122
  | Doubao (Volcengine) | |
144
123
  | Zhipu AI (GLM) | GLM-5.3 family; vision on the Flash models |
124
+ | Google Gemini | Gemini API (OpenAI-compatible endpoint) — 3.8 Flash, 3.5 Flash-Lite |
145
125
 
146
126
  ![Settings — all model providers, configurable per agent](assets/models.jpg)
147
127
 
148
128
  ## Channels
149
129
 
150
- Every channel shares the same workspace and session state. Onboarding guides live under [`.halo/docs/guide/channels/`](.halo/docs/guide/channels/).
130
+ Telegram, Slack, Feishu, WeCom and WeChat all connect outbound — no public URL or open port needed. New channel accounts start at `readonly`; each [onboarding guide](.halo/docs/guide/channels/) says who can reach the bot.
151
131
 
152
132
  | Channel | Transport | Notes |
153
133
  |---|---|---|
@@ -159,51 +139,62 @@ Every channel shares the same workspace and session state. Onboarding guides liv
159
139
  | **Feishu / Lark** | Long-connect | `appId` + `appSecret` |
160
140
  | **WeCom** | Long-connect | `botId` + `secret` (智能机器人, no HTTP send API) |
161
141
  | **WeChat** | QR bind | Scan to bind, mobile access |
162
- | **ACP adapter** | stdio JSON-RPC | Bridges ACP clients (Claude Code, etc.) onto the Web channel |
142
+ | **ACP adapter** | stdio JSON-RPC | `halo acp` bridges ACP editors (Zed, JetBrains IDEs) onto the Web channel — [guide](.halo/docs/guide/channels/acp.md) |
163
143
 
164
144
  ![Halo TUI — delegation and reporting streamed in the terminal](assets/tui.jpg)
165
145
  <p align="center"><sub>The TUI runs the same agent loop in your terminal — here delegating to an executor and consolidating its report, no server needed.</sub></p>
166
146
 
147
+ ### Talk to it from curl
148
+
149
+ The Web channel is a token-authenticated HTTP + SSE endpoint for your own UI or scripts. In the admin, open **Channels → Web → Add Account**, pick a workspace and access level, and copy the token (shown once). Then:
150
+
151
+ ```bash
152
+ curl -N -H "x-token: $TOKEN" -H "Content-Type: application/json" \
153
+ -d '{"message":"What files are in this workspace?"}' \
154
+ http://localhost:9527/api/web/chat
155
+ ```
156
+
157
+ The response is SSE frames — `session` / `thinking` / `tool_call` / `stream` / `complete` — full protocol in [`.halo/docs/guide/channels/web.md`](.halo/docs/guide/channels/web.md).
158
+
167
159
  ## Halo City
168
160
 
169
- A read-only pixel city that visualizes a live halo server: each workspace is a building, each session an animal citizen — at a desk when working, grabbing coffee or hitting the arcade when idle. Click any citizen to inspect the real thing: live session log, delegation chain, last tool call, token usage. Pure client-side canvas on a single polling endpoint — **zero model tokens burned**.
161
+ A read-only pixel city that visualizes a live halo server: each workspace is a building, each session an animal citizen — at a desk when working, grabbing coffee or hitting the arcade when idle. Click any citizen to inspect the real thing: live session log, delegation chain, last tool call, token usage. Pure client-side canvas on a single polling endpoint — **zero model tokens burned**. Plain static files in [`halo-city/`](halo-city/), no build — see the [design notes](.halo/docs/design/halo-city.md).
170
162
 
171
163
  ![Halo City street level — sub-agent citizens at work, one inspected](assets/halo-city-street.jpg)
172
164
  <p align="center"><sub>Street level: three sub-agent citizens heads-down in the <code>aurora-cafe</code> building; the inspector shows one executor's live log and delegation chain.</sub></p>
173
165
 
174
- Lives at [`halo-city/`](halo-city/) (plain static files, no build) — see the [design notes](.halo/docs/design/halo-city.md).
175
-
176
166
  ## Tech Stack
177
167
 
178
168
  - **Monorepo**: pnpm workspace (`core`, `server`, `admin`, `cli`, `desktop`, `acp-adapter`)
179
- - **Backend**: Hono + WebSocket, single Node.js process on port 9527
169
+ - **Backend**: Hono + WebSocket, single Node.js 22+ process on port 9527 (ESM, TypeScript strict)
180
170
  - **Frontend**: Next.js 15 static export, served directly by Hono
181
171
  - **Agent**: custom orchestration loop, provider-agnostic `ModelRuntime` interface
182
172
  - **Storage**: SQLite + Drizzle ORM — no external services to stand up
183
- - **Runtime**: Node.js 22+, ESM, TypeScript strict
184
173
 
185
174
  ## Docs
186
175
 
187
176
  - [`.halo/INDEX.md`](.halo/INDEX.md) — project overview + doc index
177
+ - [`.halo/docs/guide/`](.halo/docs/guide/README.md) — user guide
188
178
  - [`.halo/docs/requirements/overview.md`](.halo/docs/requirements/overview.md) — product concept
189
179
  - [`.halo/docs/design/architecture.md`](.halo/docs/design/architecture.md) — backend architecture
190
180
  - [`.halo/docs/design/evolution.md`](.halo/docs/design/evolution.md) — self-evolution design
191
181
  - [`.halo/docs/guide/channels/`](.halo/docs/guide/channels/) — per-channel onboarding guides
192
182
  - [`.halo/docs/dev/deploy.md`](.halo/docs/dev/deploy.md) — deployment (systemd / Nginx)
193
183
  - [`.halo/docs/dev/env.md`](.halo/docs/dev/env.md) — env vars, build commands
184
+ - [`CHANGELOG.md`](CHANGELOG.md) — what changed in each release
194
185
  - [`CLAUDE.md`](CLAUDE.md) — development instructions for Claude Code
195
186
 
196
187
  ## Status & Limitations
197
188
 
198
189
  Halo is young — treat it as an early-stage project:
199
190
 
200
- - **Sandbox isolates the filesystem, not the network.** The bubblewrap sandbox covers access levels and filesystem reach (host paths, `~/.aws`/`~/.ssh` masked), but does **not** isolate the network — code running inside it can still make outbound connections. The threat model is accidental damage and path escape by a trusted agent, **not** containment of a deliberately malicious skill exfiltrating data. Network isolation is on the roadmap.
201
- - **Test coverage is unit/regression-level, not end-to-end.** 1100+ tests across the four packages (core, server, cli, admin) run on every push via CI, covering core logic — path-boundary checks, session repair, channel message formatting, the TUI engine, and more. There's no end-to-end or integration suite yet.
202
- - **Windows hosts have no sandbox at all.** `bubblewrap` is Linux-only; on a Windows server every `workspace`-level session is effectively `full` (unrestricted shell + filesystem) and `readonly` sessions can read any path the halo process can. macOS and Linux-without-bwrap keep tool filtering and an app-level path fence, but not the OS-level isolation. Don't hand out non-`full` channel tokens on a Windows host expecting isolation — details in [dev/tools.md](.halo/docs/dev/tools.md).
203
- - **The admin is single-tenant.** One password, one JWT cookie, and that cookie *is* the server process: whoever holds it can read and write every workspace, run shell commands as the server user, and manage credentials for every channel. There are no roles or per-user permissions in the admin. Multi-user access is meant to go through channel accounts (Web / Telegram / Slack / Feishu / WeCom / WeChat) with their own access levels, not through sharing the admin login. Keep the admin port behind your firewall, VPN, or an authenticating reverse proxy.
191
+ - **The sandbox guards writes and credentials, not the network.** In `workspace` / `readonly` sessions, bubblewrap (Linux) or Seatbelt (macOS) limits writes to the workspace and temp dirs (`readonly`: temp dirs only) and hides `~/.halo/secrets`, `~/.ssh`, `~/.aws` and similar paths; the rest of the host stays readable and outbound connections work. The threat model is accidental damage and path escape by a trusted agent — not containing a malicious skill or extension, or a prompt-injected session sending out what it can read.
192
+ - **Windows hosts have no sandbox.** There a `workspace` session is effectively `full`, and `readonly` can read any path the halo process can — don't hand out non-`full` channel tokens on a Windows host expecting isolation ([dev/tools.md](.halo/docs/dev/tools.md)).
193
+ - **The admin is single-tenant, on plain HTTP, on all interfaces.** One password (5 failed logins lock that IP out for 15 minutes); whoever holds the admin cookie controls the server — every workspace, shell as the server user, every channel credential. Keep port 9527 behind a firewall, VPN or SSH tunnel, or a reverse proxy with TLS ([deploy.md §10](.halo/docs/dev/deploy.md#10-optional-nginx-reverse-proxy)), and give other people channel accounts with their own access levels instead of the admin login.
194
+ - **Tests are unit/regression-level.** CI runs them on every push and PR to `main`; there's no end-to-end or integration suite yet.
204
195
  - **APIs and on-disk formats may still change between versions.** Expect rough edges while things stabilize.
205
196
 
206
- If you hit something broken or surprising, please open an issue — early feedback is genuinely useful right now.
197
+ If you hit something broken or surprising, please open an issue — early feedback is genuinely useful right now. Security reports go through [SECURITY.md](SECURITY.md).
207
198
 
208
199
  ## Contributing
209
200
 
@@ -1 +1 @@
1
- <!DOCTYPE html><!--k5zuJrivSdbaPifBatwdr--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/e4af272ccee01ff0-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/7b736202f0b1dbfd.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/3b4dcd0ac6b8eae8.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-74f577c1b094f942.js"/><script src="/_next/static/chunks/1b86ba38-c29611e086b2ce25.js" async=""></script><script src="/_next/static/chunks/698-19872069fed27164.js" async=""></script><script src="/_next/static/chunks/main-app-340a5b58ed229c4d.js" async=""></script><script src="/_next/static/chunks/989-ddba0eb72566da23.js" async=""></script><script src="/_next/static/chunks/app/layout-ea161287d934ba96.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>Halo - Multi-Agent Workspace</title><meta name="description" content="Multi-agent collaboration workspace for complex project delivery"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/><link rel="icon" href="/icon.svg?3a6863548daca964" type="image/svg+xml" sizes="any"/><script>try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}</script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_d8ebd1 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-74f577c1b094f942.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[58321,[\"989\",\"static/chunks/989-ddba0eb72566da23.js\",\"177\",\"static/chunks/app/layout-ea161287d934ba96.js\"],\"ThemeProvider\"]\n3:I[24554,[\"989\",\"static/chunks/989-ddba0eb72566da23.js\",\"177\",\"static/chunks/app/layout-ea161287d934ba96.js\"],\"AppI18nProvider\"]\n4:I[63693,[],\"\"]\n5:I[74713,[],\"\"]\n6:I[23632,[],\"OutletBoundary\"]\n8:I[86889,[],\"AsyncMetadataOutlet\"]\na:I[23632,[],\"ViewportBoundary\"]\nc:I[23632,[],\"MetadataBoundary\"]\nd:\"$Sreact.suspense\"\nf:I[1935,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/7b736202f0b1dbfd.css\",\"style\"]\n:HL[\"/_next/static/css/3b4dcd0ac6b8eae8.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"k5zuJrivSdbaPifBatwdr\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/7b736202f0b1dbfd.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/3b4dcd0ac6b8eae8.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_d8ebd1 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L6\",null,{\"children\":[\"$L7\",[\"$\",\"$L8\",null,{\"promise\":\"$@9\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$d\",null,{\"fallback\":null,\"children\":\"$Le\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$f\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n7:null\n"])</script><script>self.__next_f.push([1,"10:I[88809,[],\"IconMark\"]\n9:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Halo - Multi-Agent Workspace\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Multi-agent collaboration workspace for complex project delivery\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon.svg?3a6863548daca964\",\"type\":\"image/svg+xml\",\"sizes\":\"any\"}],[\"$\",\"$L10\",\"4\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"e:\"$9:metadata\"\n"])</script></body></html>
1
+ <!DOCTYPE html><!--kfp1cMc36J6hjZ06L9mm_--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/e4af272ccee01ff0-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/7b736202f0b1dbfd.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/3b4dcd0ac6b8eae8.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-74f577c1b094f942.js"/><script src="/_next/static/chunks/1b86ba38-c29611e086b2ce25.js" async=""></script><script src="/_next/static/chunks/698-19872069fed27164.js" async=""></script><script src="/_next/static/chunks/main-app-340a5b58ed229c4d.js" async=""></script><script src="/_next/static/chunks/989-5d7743ff56d00dd7.js" async=""></script><script src="/_next/static/chunks/app/layout-ea161287d934ba96.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>Halo - Multi-Agent Workspace</title><meta name="description" content="Multi-agent collaboration workspace for complex project delivery"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/><link rel="icon" href="/icon.svg?3a6863548daca964" type="image/svg+xml" sizes="any"/><script>try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}</script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_d8ebd1 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-74f577c1b094f942.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[58321,[\"989\",\"static/chunks/989-5d7743ff56d00dd7.js\",\"177\",\"static/chunks/app/layout-ea161287d934ba96.js\"],\"ThemeProvider\"]\n3:I[24554,[\"989\",\"static/chunks/989-5d7743ff56d00dd7.js\",\"177\",\"static/chunks/app/layout-ea161287d934ba96.js\"],\"AppI18nProvider\"]\n4:I[63693,[],\"\"]\n5:I[74713,[],\"\"]\n6:I[23632,[],\"OutletBoundary\"]\n8:I[86889,[],\"AsyncMetadataOutlet\"]\na:I[23632,[],\"ViewportBoundary\"]\nc:I[23632,[],\"MetadataBoundary\"]\nd:\"$Sreact.suspense\"\nf:I[1935,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/7b736202f0b1dbfd.css\",\"style\"]\n:HL[\"/_next/static/css/3b4dcd0ac6b8eae8.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"kfp1cMc36J6hjZ06L9mm-\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/7b736202f0b1dbfd.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/3b4dcd0ac6b8eae8.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_d8ebd1 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L6\",null,{\"children\":[\"$L7\",[\"$\",\"$L8\",null,{\"promise\":\"$@9\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$d\",null,{\"fallback\":null,\"children\":\"$Le\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$f\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n7:null\n"])</script><script>self.__next_f.push([1,"10:I[88809,[],\"IconMark\"]\n9:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Halo - Multi-Agent Workspace\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Multi-agent collaboration workspace for complex project delivery\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon.svg?3a6863548daca964\",\"type\":\"image/svg+xml\",\"sizes\":\"any\"}],[\"$\",\"$L10\",\"4\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"e:\"$9:metadata\"\n"])</script></body></html>