@turmind/halo 1.5.3-alpha → 1.5.5
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.
- package/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/{133-33aa091fba853037.js → 422-cc74b1fdd3af5415.js} +1 -1
- package/admin-out/_next/static/chunks/989-bc743317498d3fbc.js +1 -0
- package/admin-out/_next/static/chunks/app/page-0a390059f1914664.js +1 -0
- package/admin-out/_next/static/chunks/{webpack-1a264efd965db7ab.js → webpack-edf248e07333566d.js} +1 -1
- package/admin-out/_next/static/css/b53542687ebd4c65.css +3 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +5 -5
- package/bundled-docs/design/agent.md +4 -4
- package/bundled-docs/design/agentcore.md +13 -10
- package/bundled-docs/design/architecture.md +24 -14
- package/bundled-docs/design/command.md +5 -5
- package/bundled-docs/design/cron.md +2 -2
- package/bundled-docs/design/evolution.md +34 -35
- package/bundled-docs/design/express-self.md +6 -4
- package/bundled-docs/design/feishu.md +18 -14
- package/bundled-docs/design/halo-city.md +5 -4
- package/bundled-docs/design/observability.md +9 -9
- package/bundled-docs/design/prompt-system.md +12 -12
- package/bundled-docs/design/session.md +12 -12
- package/bundled-docs/design/slack.md +22 -20
- package/bundled-docs/design/storage.md +18 -19
- package/bundled-docs/design/telegram.md +26 -8
- package/bundled-docs/design/web.md +3 -1
- package/bundled-docs/design/wechat.md +8 -6
- package/bundled-docs/design/wecom.md +23 -9
- package/bundled-docs/design/ws.md +6 -6
- package/bundled-docs/dev/acp-adapter.md +8 -8
- package/bundled-docs/dev/add-channel.md +12 -9
- package/bundled-docs/dev/add-model-provider.md +31 -20
- package/bundled-docs/dev/api.md +83 -57
- package/bundled-docs/dev/deploy.md +8 -6
- package/bundled-docs/dev/env.md +16 -11
- package/bundled-docs/dev/logging.md +15 -29
- package/bundled-docs/dev/previews.md +20 -11
- package/bundled-docs/dev/tools.md +24 -12
- package/bundled-docs/guide/README.md +1 -0
- package/bundled-docs/guide/agents.md +19 -53
- package/bundled-docs/guide/channels/README.md +7 -4
- package/bundled-docs/guide/channels/acp.md +3 -1
- package/bundled-docs/guide/channels/feishu.md +13 -11
- package/bundled-docs/guide/channels/slack.md +12 -10
- package/bundled-docs/guide/channels/telegram.md +21 -27
- package/bundled-docs/guide/channels/web.md +15 -11
- package/bundled-docs/guide/channels/wechat.md +15 -14
- package/bundled-docs/guide/channels/wecom.md +7 -2
- package/bundled-docs/guide/chat.md +11 -10
- package/bundled-docs/guide/cli.md +10 -8
- package/bundled-docs/guide/getting-started.md +3 -3
- package/bundled-docs/guide/secrets-and-credentials.md +3 -3
- package/bundled-docs/guide/sessions.md +3 -3
- package/bundled-docs/guide/skills.md +8 -6
- package/bundled-docs/guide/testing-agents-and-skills.md +14 -15
- package/bundled-docs/guide/workspace.md +27 -15
- package/bundled-docs/requirements/agent.md +19 -18
- package/bundled-docs/requirements/chat.md +19 -21
- package/bundled-docs/requirements/command.md +10 -10
- package/bundled-docs/requirements/editor.md +10 -9
- package/bundled-docs/requirements/explorer.md +10 -7
- package/bundled-docs/requirements/session.md +16 -17
- package/bundled-docs/requirements/settings.md +18 -14
- package/bundled-docs/requirements/skill.md +4 -4
- package/bundled-docs/requirements/source-control.md +4 -3
- package/bundled-docs/requirements/terminal.md +13 -13
- package/bundled-docs/requirements/workspace.md +19 -13
- package/dist/index.js +2038 -3558
- package/package.json +1 -1
- package/templates/config.yaml +6 -0
- package/templates/skills/workspace/share.md +11 -3
- package/templates/skills/workspace/stage.py +85 -5
- package/admin-out/_next/static/chunks/989-c6c7772db8a50926.js +0 -1
- package/admin-out/_next/static/chunks/app/page-5f41b086cd5ad353.js +0 -1
- package/admin-out/_next/static/css/8809d33379458514.css +0 -3
- /package/admin-out/_next/static/{Wqi_ugna9MYsiV7ECuawQ → cuVcldfdg1PXevaQMQSzb}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{Wqi_ugna9MYsiV7ECuawQ → cuVcldfdg1PXevaQMQSzb}/_ssgManifest.js +0 -0
|
@@ -24,27 +24,25 @@ Sending `/setcommands` to BotFather lets Telegram clients show a menu next to th
|
|
|
24
24
|
/setcommands → pick your bot → paste:
|
|
25
25
|
|
|
26
26
|
start - 开始 / start
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
compact - 压缩上下文 / compact context
|
|
32
|
-
ws - 查看/切换 workspace
|
|
27
|
+
session - 会话管理 / manage sessions
|
|
28
|
+
agent - 智能体 / manage agents
|
|
29
|
+
skill - 技能 / manage skills
|
|
30
|
+
workspace - 查看/切换 workspace
|
|
33
31
|
help - 帮助 / help
|
|
34
32
|
```
|
|
35
33
|
|
|
36
34
|
## Step 3 — Add the account in halo admin
|
|
37
35
|
|
|
38
|
-
Open halo admin → **Channels** → **Telegram** → **Add
|
|
36
|
+
Open halo admin → **Channels** → **Telegram** → **Add Bot**:
|
|
39
37
|
|
|
40
38
|
| Field | Value |
|
|
41
39
|
|---|---|
|
|
42
|
-
| Bot
|
|
43
|
-
|
|
|
44
|
-
|
|
|
40
|
+
| Bot Token (from @BotFather) | the `…:ABC…` from Step 1 |
|
|
41
|
+
| Bind to workspace | absolute path, e.g. `/home/ubuntu/my-project` |
|
|
42
|
+
| Name (optional) | a label for the account |
|
|
45
43
|
| Access level | `readonly` (default), `workspace`, or `full` |
|
|
46
44
|
| Language | `en` or `zh` |
|
|
47
|
-
| Allowed users |
|
|
45
|
+
| Allowed users (optional) | whitelist; comma-separated user IDs and/or `@usernames`. Empty = anyone can talk to the bot |
|
|
48
46
|
|
|
49
47
|
On submit halo calls Telegram's `getMe` to validate the token and auto-fill `botUsername`. If that call fails the account isn't created.
|
|
50
48
|
|
|
@@ -52,7 +50,7 @@ On submit halo calls Telegram's `getMe` to validate the token and auto-fill `bot
|
|
|
52
50
|
|
|
53
51
|
- **Empty** — anyone who finds the bot can chat with it. Fine for personal bots, dangerous if the bot is `full`-access
|
|
54
52
|
- **Numeric IDs** (e.g. `123456789`) — recommended. A user's id is stable and you can find it by sending `/start` to `@userinfobot` in Telegram
|
|
55
|
-
- **`@username`** — works for inbound filtering
|
|
53
|
+
- **`@username`** — works for inbound filtering (matched case-insensitively against the sender's current username, so it breaks if they rename)
|
|
56
54
|
|
|
57
55
|
For team bots prefer numeric IDs.
|
|
58
56
|
|
|
@@ -60,13 +58,15 @@ For team bots prefer numeric IDs.
|
|
|
60
58
|
|
|
61
59
|
In Telegram, search the bot's username (`@halo_dev_bot`) → press **Start** → say `hello` → expect a streamed reply.
|
|
62
60
|
|
|
63
|
-
If nothing happens, check halo server logs for `[
|
|
61
|
+
If nothing happens, check halo server logs for `[Telegram]` lines.
|
|
64
62
|
|
|
65
63
|
## How halo handles inbound
|
|
66
64
|
|
|
67
65
|
- **Private chats** — every message routes to the bot
|
|
68
|
-
- **Group chats** —
|
|
69
|
-
- **Photos
|
|
66
|
+
- **Group chats** — supported. Each member talks to their **own** session (the same one they use in DM with the bot), and the bot replies in the group. `allowedUsers` is checked against the sender, so non-whitelisted members are refused there too. By default Telegram's *privacy mode* only delivers `/command@yourbot` and replies to the bot's messages; to have the bot see every group message, send `/setprivacy` → **Disable** to BotFather (then re-add the bot to the group) or make it a group admin
|
|
67
|
+
- **Photos** — downloaded, sent to the model as vision input and saved under `<workspace>/.halo/assets/telegram/inbound/<accountId>/<date>/`; the saved path is appended to the message text
|
|
68
|
+
- **Documents / voice / round video** — downloaded (20 MB cap) to the same folder; the agent gets the saved path in the message text, not the content
|
|
69
|
+
- **After a server restart** — if the agent was mid-task when halo restarted, its reply still reaches the most recent private chat the user talked in; a group conversation reconnects on the user's next message there
|
|
70
70
|
- **Slash commands** — handled by halo, not by Telegram. The BotFather menu in Step 2 is just a UI hint
|
|
71
71
|
|
|
72
72
|
## Slash commands
|
|
@@ -74,31 +74,25 @@ If nothing happens, check halo server logs for `[telegram]` lines.
|
|
|
74
74
|
| Command | Effect |
|
|
75
75
|
|---|---|
|
|
76
76
|
| `/start` | Welcome message |
|
|
77
|
-
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` |
|
|
77
|
+
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` |
|
|
78
78
|
| `/agent <verb>` | Manage agents (`list` / `switch` / `desc` open to all; `delete` full; `create` / `update` via skill, full) |
|
|
79
79
|
| `/skill <verb>` | Manage skills (`list` / `desc` open; `disable` / `enable` workspace; `delete` full; `create` / `update` via skill, full) |
|
|
80
80
|
| `/workspace <verb>` | Workspace: `info` (all) / `switch <path>` (full) / `setup` / `tidy` (workspace) / `share` (full) |
|
|
81
|
+
| `/cron` `/acp` `/extension` | Skill-backed object commands (full access); `/evo [hint]` queues a self-evolution run |
|
|
81
82
|
| `/help` | List commands — object commands show only the verbs you can run |
|
|
82
83
|
|
|
83
84
|
## Cron jobs targeting Telegram
|
|
84
85
|
|
|
85
|
-
When a cron job is created from inside a Telegram chat, the dispatcher targets that chat. From the admin UI you can also enter chat IDs directly (comma-separated for fan-out).
|
|
86
|
-
|
|
87
|
-
1. Explicit chat IDs on the cron target row (if provided in the form)
|
|
88
|
-
2. Fan-out to every numeric ID in the account's `allowedUsers` whitelist
|
|
89
|
-
3. The most-recent inbound chat ID, as a single-recipient fallback
|
|
90
|
-
|
|
91
|
-
Want to fan out a daily report to your whole team? Add their numeric IDs to `allowedUsers` and create a cron without an explicit chat ID — halo delivers to each one and records per-recipient ✓/✗ in the cron run history.
|
|
86
|
+
When a cron job is created from inside a Telegram chat, the dispatcher targets that chat. From the admin UI you can also enter chat IDs directly (comma-separated for fan-out). A Telegram target **must** carry an explicit numeric chat ID (for a private chat it equals the user ID; a group's is negative) — there is no fallback to the whitelist or to the last inbound chat, and the dispatch fails without one. Per-recipient ✓/✗ is recorded in the cron run history. A report longer than 4000 characters is sent as several messages, in order.
|
|
92
87
|
|
|
93
88
|
## Common problems
|
|
94
89
|
|
|
95
90
|
| Symptom | Cause / fix |
|
|
96
91
|
|---|---|
|
|
97
|
-
| "Unauthorized" / `getMe` fails on Add
|
|
92
|
+
| "Unauthorized" / `getMe` fails on Add Bot | Bot token has a typo, or the bot was deleted in BotFather |
|
|
98
93
|
| Bot exists but doesn't reply | Check `allowedUsers` — if non-empty, your user must be in it |
|
|
99
|
-
| Bot used to work, now silent | Maybe two halo processes are running and stealing each other's `getUpdates` long-poll. Check `~/.halo/global/server.
|
|
100
|
-
| Group messages don't trigger the bot |
|
|
101
|
-
| Cron fan-out skips a whitelisted user | The user is listed as `@username`. Add their numeric ID instead |
|
|
94
|
+
| Bot used to work, now silent | Maybe two halo processes are running and stealing each other's `getUpdates` long-poll. Check `~/.halo/global/server.lock` |
|
|
95
|
+
| Group messages don't trigger the bot | Privacy mode is on (the default): only `/command@yourbot` and replies to the bot get through. Disable it with BotFather `/setprivacy` and re-add the bot, or make it a group admin |
|
|
102
96
|
|
|
103
97
|
## Multi-bot setup
|
|
104
98
|
|
|
@@ -13,13 +13,13 @@ The token is **the** credential — anyone holding it can talk to the agent at t
|
|
|
13
13
|
|
|
14
14
|
## Step 1 — Create an account
|
|
15
15
|
|
|
16
|
-
Open halo admin → **Channels** → **Web** → **
|
|
16
|
+
Open halo admin → **Channels** → **Web** → **Create Access**:
|
|
17
17
|
|
|
18
18
|
| Field | Value |
|
|
19
19
|
|---|---|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| Access level | `readonly` (default), `workspace`, or `
|
|
20
|
+
| Bind to workspace | absolute path, e.g. `/home/ubuntu/my-project` |
|
|
21
|
+
| Name (optional) | a label for the account |
|
|
22
|
+
| Access level | `readonly` (default), `workspace`, `full`, or `observer` (global read-only, for dashboards / metrics) |
|
|
23
23
|
| Language | `en` or `zh` |
|
|
24
24
|
|
|
25
25
|
Click **Create**. The success screen shows the auto-generated token **once** — copy it now, you can't retrieve it again. (You can always delete the account and create a new one if you lose the token.)
|
|
@@ -55,18 +55,20 @@ Parse the `type` field on each event to render text vs tool calls vs completion.
|
|
|
55
55
|
| Method | Path | Purpose |
|
|
56
56
|
|---|---|---|
|
|
57
57
|
| `POST /api/web/chat` | Send a message; SSE response |
|
|
58
|
+
| `POST /api/web/sessions` | Mint a new root session in the token's namespace; returns `{sessionId}` |
|
|
58
59
|
| `POST /api/web/stop` | Cancel the running task |
|
|
59
60
|
| `GET /api/web/history` | Fetch session message history |
|
|
60
61
|
| `GET /api/web/subscribe` | Reconnect to a running session's SSE stream |
|
|
62
|
+
| `GET /api/web/file?path=…` | Fetch a file from the bound workspace (path relative to it; `.halo` runtime state is refused) |
|
|
61
63
|
|
|
62
|
-
All
|
|
64
|
+
All six accept the same auth header.
|
|
63
65
|
|
|
64
66
|
### Per-request overrides
|
|
65
67
|
|
|
66
68
|
By default each token is locked to the workspace and active session set by the admin. External integrations (notably the [ACP adapter](acp.md)) need finer control:
|
|
67
69
|
|
|
68
70
|
- `workspace` — server-side absolute path, **gated on `accessLevel === 'full'`** (readonly / workspace tokens can't escape their bound workspace; the override is rejected)
|
|
69
|
-
- `sessionId` — explicit halo session id; lets clients
|
|
71
|
+
- `sessionId` — explicit halo session id; lets clients address a session across reconnects. **Gated on ownership**: `full` tokens may name any id; `readonly` / `workspace` tokens only ids under their own account (`web_<accountId>_*`, e.g. minted by `POST /api/web/sessions`) — anything else is `403`
|
|
70
72
|
- `agentId` — only used when the request creates a new session
|
|
71
73
|
|
|
72
74
|
These can be passed as POST body fields, headers (`x-workspace`, `x-session-id`, `x-agent-id`), or query params (`?workspace=…&sessionId=…&agentId=…`). POST body wins on conflict.
|
|
@@ -86,7 +88,7 @@ For browser apps you almost never want these — leave them off and use the per-
|
|
|
86
88
|
}
|
|
87
89
|
```
|
|
88
90
|
|
|
89
|
-
Images go to the LLM as multimodal content.
|
|
91
|
+
Images go to the LLM as multimodal content. Any other `mimeType` (audio, PDF, …) in the same array is not sent to the model: the server saves it to `<workspace>/.halo/assets/web/inbound/<accountId>/<date>/` and appends a `[语音已保存: <path>]` line to your message so the agent can open it with its file tools.
|
|
90
92
|
|
|
91
93
|
The standalone web-demo handles voice + arbitrary files for you; if you want that, use it as a reference implementation.
|
|
92
94
|
|
|
@@ -96,10 +98,11 @@ Slash commands are intercepted before they reach the agent — same set as every
|
|
|
96
98
|
|
|
97
99
|
| Command | Effect |
|
|
98
100
|
|---|---|
|
|
99
|
-
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` |
|
|
101
|
+
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` |
|
|
100
102
|
| `/agent <verb>` | Manage agents (`list` / `switch` / `desc` open to all; `delete` full; `create` / `update` via skill, full) |
|
|
101
103
|
| `/skill <verb>` | Manage skills (`list` / `desc` open; `disable` / `enable` workspace; `delete` full; `create` / `update` via skill, full) |
|
|
102
104
|
| `/workspace <verb>` | Workspace: `info` (all) / `switch <path>` (full) / `setup` / `tidy` (workspace) / `share` (full) |
|
|
105
|
+
| `/cron` `/acp` `/extension` | Skill-backed object commands (full access); `/evo [hint]` queues a self-evolution run |
|
|
103
106
|
| `/help` | List commands — object commands show only the verbs you can run |
|
|
104
107
|
|
|
105
108
|
Send a slash command exactly like a normal message — the server detects the leading `/`.
|
|
@@ -109,7 +112,8 @@ Send a slash command exactly like a normal message — the server detects the le
|
|
|
109
112
|
| Symptom | Cause / fix |
|
|
110
113
|
|---|---|
|
|
111
114
|
| `401` on every call | Missing `x-token` header, or token is for a deleted account |
|
|
112
|
-
| `
|
|
115
|
+
| `429` | 5 bad tokens from one IP within 15 minutes locks that IP out for 15 minutes (in-memory, cleared on restart) |
|
|
116
|
+
| `403` when passing `workspace=…` or a foreign `sessionId` | Token is `readonly` / `workspace` access — only `full` can override the workspace or address sessions it didn't mint |
|
|
113
117
|
| SSE stream hangs forever | Reverse proxy buffering. Disable buffering for `text/event-stream` (nginx: `proxy_buffering off`, Cloudflare: enable streaming) |
|
|
114
118
|
| Token leaked accidentally | Delete the account in admin, create a new one. The old token is invalidated immediately |
|
|
115
119
|
| Want to share one token across multiple users | Don't — every request would land on the same active session. Create one account per user / app |
|
|
@@ -139,9 +143,9 @@ Alternatively, the gear icon (header / login screen) opens **direct-connect mode
|
|
|
139
143
|
## Security notes
|
|
140
144
|
|
|
141
145
|
- **Tokens are unhashed** in `~/.halo/secrets/channels/channels.db`. Restrict that file to the user that runs halo
|
|
142
|
-
- **
|
|
146
|
+
- **Bad-token lockout only** — 5 invalid tokens from one IP in 15 minutes locks that IP out (`429`) for 15 minutes; there is no per-token request rate limit, so if you expose the API on the public internet put a reverse proxy with rate limits in front
|
|
143
147
|
- **Admin endpoints** (`POST /api/web/accounts`, `PATCH`, `DELETE`) require admin cookie auth, **not** the token. They're for the admin panel, not for the token holder
|
|
144
|
-
- A `full`-access token can call `/workspace <abs-path>` to switch the bound workspace database-side. If you don't want that, give out `workspace` or `readonly` tokens instead
|
|
148
|
+
- A `full`-access token can call `/workspace switch <abs-path>` to switch the bound workspace database-side. If you don't want that, give out `workspace` or `readonly` tokens instead
|
|
145
149
|
|
|
146
150
|
## Reference
|
|
147
151
|
|
|
@@ -21,18 +21,18 @@ Halo auto-creates `.halo/` inside the folder if it doesn't exist; the path itsel
|
|
|
21
21
|
|
|
22
22
|
## Step 2 — Open the admin scan flow
|
|
23
23
|
|
|
24
|
-
Open halo admin → **Channels** → **WeChat** → **Add
|
|
24
|
+
Open halo admin → **Channels** → **WeChat** → **Add Bot**.
|
|
25
25
|
|
|
26
26
|
The form is **just the workspace + label + access level + language** — no bot token field, because the token comes from the scan:
|
|
27
27
|
|
|
28
28
|
| Field | Value |
|
|
29
29
|
|---|---|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
30
|
+
| Bind to workspace | absolute path from Step 1, e.g. `/home/ubuntu/my-project` |
|
|
31
|
+
| Name (optional) | shown in admin's account list |
|
|
32
32
|
| Access level | `readonly` (default), `workspace`, or `full` |
|
|
33
33
|
| Language | `en` or `zh` |
|
|
34
34
|
|
|
35
|
-
Click **
|
|
35
|
+
Click **Next: Scan**. Halo hits `ilinkai.weixin.qq.com` to mint a fresh QR code and starts a poll loop waiting for the scan.
|
|
36
36
|
|
|
37
37
|
> **What the access level covers.** A WeChat account is bound to exactly one WeChat user — the one who scans — so there's no whitelist to configure; the scanner *is* the whitelist. The level you choose here is what that phone gets: `full` means whoever holds the unlocked phone has shell access as the server user. The same applies to invite QRs minted with `/qr full` from inside the chat — each scan creates a separate account at the level you named, so hand those out as you would an SSH key.
|
|
38
38
|
|
|
@@ -43,19 +43,19 @@ Click **Generate QR**. Halo hits `ilinkai.weixin.qq.com` to mint a fresh QR code
|
|
|
43
43
|
3. Aim at the QR shown in the admin UI
|
|
44
44
|
4. Tap **Confirm Login / 确认登录** on the phone
|
|
45
45
|
|
|
46
|
-
Within a few seconds the admin UI shows "
|
|
46
|
+
Within a few seconds the admin UI shows "Connected (<id>)" and closes the dialog. Behind the scenes:
|
|
47
47
|
|
|
48
48
|
- iLink returns the bot's token, the IDC-specific `baseUrl`, and your WeChat-side `ilink_user_id`
|
|
49
49
|
- Halo writes those into the new account row
|
|
50
50
|
- The long-poll loop starts immediately — the bot is now live
|
|
51
51
|
|
|
52
|
-
The
|
|
52
|
+
The server waits up to ~8 minutes for the scan and gives up after the QR has expired three times; the dialog then shows the error and a **Start over** button. If you cancel and come back later, just open the form again — a new QR is minted.
|
|
53
53
|
|
|
54
54
|
## Step 4 — Test it
|
|
55
55
|
|
|
56
56
|
The bot's name in your WeChat contacts is whatever the iLink platform set it to (typically a generic placeholder like `小助手`; you can rename it locally). Open the bot's chat in WeChat and send `hello` → expect a streamed reply.
|
|
57
57
|
|
|
58
|
-
If nothing happens, check halo server logs for `[
|
|
58
|
+
If nothing happens, check halo server logs for `[WeChat]` lines.
|
|
59
59
|
|
|
60
60
|
## How halo handles inbound
|
|
61
61
|
|
|
@@ -63,7 +63,8 @@ If nothing happens, check halo server logs for `[wechat]` lines.
|
|
|
63
63
|
- **Images** — passed to the LLM as multimodal content
|
|
64
64
|
- **Voice** — decrypted, saved under `<workspace>/.halo/assets/weixin/inbound/<accountId>/<date>/` (the `weixin` directory name is kept for backward compatibility with existing workspaces), path included in the agent's input
|
|
65
65
|
- **Video / files** — same as voice
|
|
66
|
-
- **Group chats** — not supported
|
|
66
|
+
- **Group chats** — not supported: the WeChat (iLink) bot is a one-to-one contact and the platform doesn't deliver group messages to it, so there is nothing for halo to handle
|
|
67
|
+
- **After a server restart** — if the agent was mid-task when halo restarted, its reply still reaches each user who has chatted with the bot (as long as the stored `context_token` is still accepted; otherwise it fails with `ret=-2` until they write again)
|
|
67
68
|
- **Self-loop** — bot-authored messages are filtered out
|
|
68
69
|
|
|
69
70
|
## Slash commands
|
|
@@ -72,12 +73,13 @@ Same set as the other channels — type as plain text in your WeChat DM with the
|
|
|
72
73
|
|
|
73
74
|
| Command | Effect |
|
|
74
75
|
|---|---|
|
|
75
|
-
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` |
|
|
76
|
+
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` |
|
|
76
77
|
| `/agent <verb>` | Manage agents (`list` / `switch` / `desc` open to all; `delete` full; `create` / `update` via skill, full) |
|
|
77
78
|
| `/skill <verb>` | Manage skills (`list` / `desc` open; `disable` / `enable` workspace; `delete` full; `create` / `update` via skill, full) |
|
|
78
79
|
| `/workspace <verb>` | Workspace: `info` (all) / `switch <path>` (full) / `setup` / `tidy` (workspace) / `share` (full) |
|
|
80
|
+
| `/cron` `/acp` `/extension` | Skill-backed object commands (full access); `/evo [hint]` queues a self-evolution run |
|
|
79
81
|
| `/help` | List commands — object commands show only the verbs you can run |
|
|
80
|
-
| `/qr [level]` | Generate an invite QR (
|
|
82
|
+
| `/qr [level]` | Generate an invite QR (`full`-access bot only; `level` = `readonly` (default) / `workspace` / `full`) |
|
|
81
83
|
|
|
82
84
|
## Cron jobs targeting WeChat
|
|
83
85
|
|
|
@@ -94,11 +96,10 @@ Fan-out across multiple WeChat users is not supported — one bot, one owner.
|
|
|
94
96
|
| Symptom | Cause / fix |
|
|
95
97
|
|---|---|
|
|
96
98
|
| QR shows but scan does nothing | iLink platform thinks the QR is for a different account. Cancel and regenerate |
|
|
97
|
-
| "
|
|
99
|
+
| "登录超时:二维码多次过期" / QR expired | Click **Start over** in the dialog and scan the new QR |
|
|
98
100
|
| Bot stops responding after a few hours | Long-poll likely lost its `syncBuf`. Restart the server; reconnect is automatic |
|
|
99
|
-
| Two halo processes both poll the same bot | Each process gets a copy of every message, replied twice. Make sure only one server runs — see `~/.halo/global/server.
|
|
100
|
-
| Group chat doesn't trigger the bot | Expected —
|
|
101
|
-
| Sent voice message returns no reply | Check that the account has `workspace` or `full` access; readonly cannot save inbound media |
|
|
101
|
+
| Two halo processes both poll the same bot | Each process gets a copy of every message, replied twice. Make sure only one server runs — see `~/.halo/global/server.lock` |
|
|
102
|
+
| Group chat doesn't trigger the bot | Expected — WeChat (iLink) doesn't support group chats for bots; talk to it one-to-one |
|
|
102
103
|
| Cron push or a delayed reply fails with `ret=-2 prepare failed` while fresh chat replies work | Three causes: the report exceeds 16 KB (halo chunks at 3500 chars, so this is rare now); the account has no stored `context_token` for the recipient yet — the gateway wants outbound to echo the user's latest inbound token, which halo persists on every inbound message since 1.1.8; or that token has **expired** — the gateway stops accepting it some time after the user's last message (the window isn't fixed; failures were seen 12 min and 79 min after the last inbound), and only a new inbound message from that user refreshes it. In all cases: send the bot any message once and the next push carries a fresh token. The error names which case it is (`context_token missing` / `context_token age 79m`), and a dropped chat reply now shows up as a `⚠️ WeChat delivery failed` notification in the session log (admin Sessions tab) instead of vanishing silently |
|
|
103
104
|
|
|
104
105
|
## Multi-workspace / multi-bot
|
|
@@ -40,7 +40,7 @@ Open halo admin → **Channels** → **WeCom** → **Add Bot**:
|
|
|
40
40
|
| Bot ID | from Step 3 |
|
|
41
41
|
| Secret | from Step 3 |
|
|
42
42
|
| Bind to workspace | absolute path, e.g. `/home/ubuntu/my-project` |
|
|
43
|
-
| Name |
|
|
43
|
+
| Name (optional) | a label for the account |
|
|
44
44
|
| Access level | `readonly` (default), `workspace`, or `full` |
|
|
45
45
|
| Language | `en` or `zh` — for hints and command replies |
|
|
46
46
|
|
|
@@ -91,16 +91,21 @@ When the agent emits a `MEDIA:<path>` line (a file under the workspace), halo up
|
|
|
91
91
|
|
|
92
92
|
Replies render **Markdown natively** — headings, bold, lists, quotes, links, code blocks and tables all display as formatted text.
|
|
93
93
|
|
|
94
|
+
### After a server restart
|
|
95
|
+
|
|
96
|
+
If the agent was mid-task when halo restarted, its reply still reaches the **most recent** chat the bot talked in (one per bot). Because the original message can no longer be answered directly, that reply arrives as a regular pushed message rather than as a reply to your message; once you write again, replies go back to normal.
|
|
97
|
+
|
|
94
98
|
## Slash commands
|
|
95
99
|
|
|
96
100
|
Same set as the other channels — type as plain text in a **single chat** (commands are ignored in groups, where the shared session belongs to everyone):
|
|
97
101
|
|
|
98
102
|
| Command | Effect |
|
|
99
103
|
|---|---|
|
|
100
|
-
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` |
|
|
104
|
+
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` |
|
|
101
105
|
| `/agent <verb>` | Manage agents (`list` / `switch` / `desc` open to all; `delete` full; `create` / `update` via skill, full) |
|
|
102
106
|
| `/skill <verb>` | Manage skills (`list` / `desc` open; `disable` / `enable` workspace; `delete` full; `create` / `update` via skill, full) |
|
|
103
107
|
| `/workspace <verb>` | Workspace: `info` (all) / `switch <path>` (full) / `setup` / `tidy` (workspace) / `share` (full) |
|
|
108
|
+
| `/cron` `/acp` `/extension` | Skill-backed object commands (full access); `/evo [hint]` queues a self-evolution run |
|
|
104
109
|
| `/help` | List commands — object commands show only the verbs you can run |
|
|
105
110
|
|
|
106
111
|
## Cron jobs targeting WeCom
|
|
@@ -28,16 +28,16 @@ A change applies from the next message you send, and the level is saved per sess
|
|
|
28
28
|
|
|
29
29
|
## Context injection
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
The input toolbar shows a context chip (the open file's name, or the selection) — click it to toggle; on by default. When on, Halo auto-injects:
|
|
32
32
|
- The currently open file path: `[Currently viewing: src/foo.ts]`
|
|
33
33
|
- The editor's current selection: `[Selected text in foo.ts:10-25]\n\`\`\`...\n\`\`\``
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Click the chip (it turns struck-through) if you don't want that context injected.
|
|
36
36
|
|
|
37
37
|
## `@` file mention
|
|
38
38
|
|
|
39
39
|
Typing `@` in the input opens a file search:
|
|
40
|
-
- Real-time fuzzy matching (
|
|
40
|
+
- Real-time fuzzy matching (120 ms debounce)
|
|
41
41
|
- Selecting inserts a path chip
|
|
42
42
|
- You can `@` multiple files back-to-back
|
|
43
43
|
|
|
@@ -49,7 +49,7 @@ The search scans the whole project on the server — independent of Explorer exp
|
|
|
49
49
|
- **Paste**: paste an image from the clipboard
|
|
50
50
|
- **Button**: click the 📎 on the left
|
|
51
51
|
|
|
52
|
-
Images are sent to the agent as base64, with multimodal support (Claude 4.6 can see images).
|
|
52
|
+
Chat attachments are images only — other dropped or pasted files are ignored. Images are sent to the agent as base64, with multimodal support (Claude 4.6 can see images).
|
|
53
53
|
|
|
54
54
|
## Slash commands
|
|
55
55
|
|
|
@@ -59,13 +59,14 @@ Most commands are noun-verb **object commands**: `/<obj> <verb> [args]`. Bare `/
|
|
|
59
59
|
|
|
60
60
|
| Command | Purpose |
|
|
61
61
|
|---|---|
|
|
62
|
-
| `/session <verb>` | Manage sessions — `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context
|
|
62
|
+
| `/session <verb>` | Manage sessions — `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` (session tree). All built-in, available to everyone. `new` starts a fresh conversation (old session stays in the sidebar; running sub-agents keep going); `compact` keeps the most recent N messages intact (N defaults to 5, `general.compact.keep_messages`); `context` shows token usage, agent info, and available tools |
|
|
63
63
|
| `/clear` | Admin-UI alias for `/session new` |
|
|
64
64
|
| `/agent <verb>` | Manage agents — `list` / `switch <name\|index>` / `desc` (built-in, open to all) · `delete` (built-in, full access) · `create` / `update` (handled by the `agent` skill, full access) |
|
|
65
65
|
| `/skill <verb>` | Manage skills — `list` / `desc` (built-in, open to all) · `disable` / `enable` (built-in, workspace access) · `delete` (built-in, full access) · `create` / `update` (handled by the `skill` skill, full access) |
|
|
66
66
|
| `/workspace <verb>` | Manage the workspace — `info` (built-in, open to all) · `switch <path>` (built-in, full access) · `setup` / `tidy` (workspace skill, workspace access; init / reorganize `.halo/` INDEX.md / INSTRUCTIONS.md / memory/) · `share` (workspace skill, full access; export a shareable bundle) |
|
|
67
67
|
| `/cron <verb>` | Scheduled agent runs — `create` / `list` / `update` / `enable` / `disable` / `delete` (cron skill, full access) |
|
|
68
68
|
| `/acp <verb>` | Talk to other agents over ACP — `kiro <q>` / `claude <q>` ask a local agent directly; `add` / `list` / `remove` manage generated `ask-<label>` bindings (acp skill, full access) |
|
|
69
|
+
| `/extension <verb>` | Canvas preview extensions — `install <id\|zip\|url>` / `list` / `remove <id>` (extension skill, full access) |
|
|
69
70
|
| `/evo [hint]` | Queue a self-evolution run on this session (full access only) |
|
|
70
71
|
| `/help` | List every command — object commands only show the verbs you can run |
|
|
71
72
|
|
|
@@ -103,8 +104,8 @@ Three ways to cut in while the agent is running:
|
|
|
103
104
|
The ring in the bottom-right of the input shows context window usage:
|
|
104
105
|
- Green: < 50%
|
|
105
106
|
- Yellow: 50–70%
|
|
106
|
-
- Orange: 70–
|
|
107
|
-
- Red: >
|
|
107
|
+
- Orange: 70–85%
|
|
108
|
+
- Red: > 85%
|
|
108
109
|
|
|
109
110
|
Reaching `compressAt` (default 90%, setting `general.compact.compress_at`) auto-triggers compact. If the LLM summary fails or times out, Halo falls back to a local compact (notice: `Auto-compacted N older messages (local fallback — LLM summary failed: <reason>)`) rather than retrying. An auto-compact has no overall time limit — it only times out after 10 minutes with no data from the model. While the agent is running you can't click the TokenRing (guarded by `isStreaming`).
|
|
110
111
|
|
|
@@ -112,9 +113,9 @@ Reaching `compressAt` (default 90%, setting `general.compact.compress_at`) auto-
|
|
|
112
113
|
|
|
113
114
|
Your sessions are listed as vertical tabs on the right edge of the chat panel:
|
|
114
115
|
- Lists the workspace's main sessions, newest first; scroll down to load older ones
|
|
115
|
-
- Click a tab to show that session. The one you left keeps running and streaming in its own tab
|
|
116
|
+
- Click a tab to show that session. The one you left keeps running and streaming in its own tab. Each tab's dot shows its state: pulsing amber while it works, blue when new output arrived in the background, green otherwise (including sessions not opened since the page loaded)
|
|
116
117
|
- **+** (or `/session new`) opens a fresh tab; the session is created when you send its first message
|
|
117
|
-
- Hover a tab to rename it (pencil)
|
|
118
|
+
- Hover a tab to rename it (pencil) or delete it (trash icon, after a confirmation) — deletion can't be undone
|
|
118
119
|
- Drag the list's left edge to make it wider or narrower, or collapse it to a thin strip of initials
|
|
119
120
|
- After a refresh, the session you were on reopens
|
|
120
121
|
|
|
@@ -126,6 +127,6 @@ Long messages you sent are collapsed to a one-line header: a chevron, the send t
|
|
|
126
127
|
|
|
127
128
|
**"Queued" hint**: the previous turn hasn't finished; your message is queued and will run next turn.
|
|
128
129
|
|
|
129
|
-
**"Rate limited, retrying in Xs..."**:
|
|
130
|
+
**"Rate limited, retrying in Xs..."**: the model provider throttled; automatic backoff, up to `general.agent.max_retries` attempts (default 5).
|
|
130
131
|
|
|
131
132
|
**A sudden `[Message from xxx]`**: a sub-agent finished and auto-reported back to the parent agent's conversation.
|
|
@@ -29,7 +29,7 @@ bundle flattens them.
|
|
|
29
29
|
# 2. build the upstream artifacts the bundle consumes
|
|
30
30
|
pnpm --filter @turmind/halo-core build
|
|
31
31
|
pnpm --filter @turmind/halo-server build
|
|
32
|
-
|
|
32
|
+
pnpm --filter @turmind/halo-admin build
|
|
33
33
|
# 3. stage the release bundle — HALO_RELEASE=1 stamps the bare version
|
|
34
34
|
# (0.1.2), NOT the default <version>-<sha>: a `-<sha>` suffix is a semver
|
|
35
35
|
# prerelease that `npm install` skips and never tags as `latest`.
|
|
@@ -136,6 +136,7 @@ Multi-turn conversation. Supports all standard Halo slash commands:
|
|
|
136
136
|
| `/session interrupt` | Interrupt the running turn now (aborts a command mid-run); any messages queued while busy then run as one follow-up turn |
|
|
137
137
|
| `/session compact` | Compact session context |
|
|
138
138
|
| `/session context` | Show context window + agent info |
|
|
139
|
+
| `/session info` | Show the session tree (descendants) |
|
|
139
140
|
| `/agent <verb>` | Manage agents: `list` / `switch <name\|index>` / `desc` / `delete` / `create` / `update` |
|
|
140
141
|
| `/skill <verb>` | Manage skills: `list` / `desc` / `disable` / `enable` / `delete` / `create` / `update` |
|
|
141
142
|
| `/workspace info` | Show current workspace |
|
|
@@ -143,6 +144,7 @@ Multi-turn conversation. Supports all standard Halo slash commands:
|
|
|
143
144
|
| `/workspace setup` / `/workspace tidy` / `/workspace share` | Set up / tidy the `.halo/` knowledge files, or export a shareable bundle (workspace skill) |
|
|
144
145
|
| `/cron <verb>` | Manage scheduled agent runs: `create` / `list` / `update` / `enable` / `disable` / `delete` |
|
|
145
146
|
| `/acp <verb>` | Ask other agents over ACP (`kiro <q>` / `claude <q>`) and manage `ask-*` bindings (`add` / `list` / `remove`) |
|
|
147
|
+
| `/extension <verb>` | Canvas preview extensions: `install` / `list` / `remove` (full access only) |
|
|
146
148
|
| `/evo [hint]` | Queue a self-evolution run on this session (full access only) |
|
|
147
149
|
| `/quit` | Exit |
|
|
148
150
|
|
|
@@ -216,7 +218,7 @@ same binary. `start -d` runs it in the background **with a bounded respawn
|
|
|
216
218
|
supervisor**: the detached process supervises the server and brings it back on
|
|
217
219
|
a crash (non-zero exit, or a kill by anything other than SIGTERM/SIGINT), at
|
|
218
220
|
most 5 times per 5 minutes with a 2s pause, logging each decision as
|
|
219
|
-
`[respawn] …` into `~/.halo/
|
|
221
|
+
`[respawn] …` into `~/.halo/logs/server.log`. `halo server stop` is
|
|
220
222
|
recognized as intentional and never respawns; `halo server stop|restart|status`
|
|
221
223
|
still target the server's own pid. Full policy in
|
|
222
224
|
[dev/deploy.md](../dev/deploy.md#crash-semantics--why-restarton-failure-is-load-bearing).
|
|
@@ -226,8 +228,9 @@ still target the server's own pid. Full policy in
|
|
|
226
228
|
```
|
|
227
229
|
packages/cli/
|
|
228
230
|
src/
|
|
229
|
-
index.ts — Entry: subcommand dispatch (tui/cli/agents/sessions/server/setup/acp), SIGINT/SIGTERM handling
|
|
231
|
+
index.ts — Entry: subcommand dispatch (tui/cli/agents/sessions/server/setup/upgrade/acp), SIGINT/SIGTERM handling
|
|
230
232
|
harness.ts — Shared agent harness wrapping SessionManager
|
|
233
|
+
server-supervisor.ts — Respawn policy for `halo server start -d`
|
|
231
234
|
cli.ts — Non-interactive: stdin/args → agent → stdout → exit
|
|
232
235
|
tui.tsx — Interactive entry: mounts the ink app
|
|
233
236
|
tui/
|
|
@@ -247,9 +250,8 @@ Session prefix: `cli_`. Sessions are persisted to `<workspace>/.halo/sessions/`
|
|
|
247
250
|
|
|
248
251
|
### Initialization
|
|
249
252
|
|
|
250
|
-
|
|
253
|
+
`tui` / `cli` / `agents` / `sessions` refuse to run ("~/.halo/global/ not initialized. Run halo setup first.") until `halo setup` has seeded `~/.halo/global/`. Then the CLI replicates the server's init sequence (`initRuntime()` in `harness.ts`):
|
|
251
254
|
1. `initLogger()` — redirect console to stderr + file logger
|
|
252
|
-
2. `
|
|
253
|
-
3. `
|
|
254
|
-
4. `
|
|
255
|
-
5. `new SessionManager(workspace)` — create agent session manager
|
|
255
|
+
2. `initBwrapCheck()` — probe sandbox availability
|
|
256
|
+
3. `setSandboxHiddenPaths()` — configure sandbox paths
|
|
257
|
+
4. `new SessionManager(workspace)` — create agent session manager (the workspace's `.halo/` is seeded via `ensureWorkspaceHalo`)
|
|
@@ -12,7 +12,7 @@ Open `http://localhost:9527` (or your deployment URL) in a browser and enter the
|
|
|
12
12
|
|
|
13
13
|
## Open a workspace
|
|
14
14
|
|
|
15
|
-
After login, you land in the workspace. A URL with `?folder=/abs/path` opens that directory directly; without `?folder`, Halo
|
|
15
|
+
After login, you land in the workspace. A URL with `?folder=/abs/path` opens that directory directly; without `?folder`, Halo reopens the last workspace you used, or your home directory if there isn't one — everything still works.
|
|
16
16
|
|
|
17
17
|
The Explorer top bar has two ways to switch workspace:
|
|
18
18
|
- **Type an absolute path** + Enter — invalid paths raise an error
|
|
@@ -22,7 +22,7 @@ Halo auto-creates a `.halo/` subdirectory in the target folder (for sessions, ag
|
|
|
22
22
|
|
|
23
23
|
## First conversation (bootstrap)
|
|
24
24
|
|
|
25
|
-
The first time a new user chats with the Default agent,
|
|
25
|
+
The first time a new user chats with the Default agent, it works in a few getting-to-know-you questions (if you open with a real task, it does the task first and asks over the next turns):
|
|
26
26
|
- What should I call you?
|
|
27
27
|
- What would you like to call me?
|
|
28
28
|
- Preferred communication style (formal/casual, English/other language, etc.)
|
|
@@ -79,7 +79,7 @@ After `halo setup`:
|
|
|
79
79
|
|
|
80
80
|
**When does "refreshed on upgrade" actually run?** `halo setup` always re-runs the seed; the server's startup check also re-runs it automatically when `~/.halo/global/.template-version` is behind the bundled `TEMPLATE_VERSION`. So the routine flow `halo upgrade && halo server restart` is enough — no need to remember `halo setup`.
|
|
81
81
|
|
|
82
|
-
**Server-refreshed**: `INSTRUCTIONS.md`, `prompts/`, `models/`, `docs/`, the built-in agent ids (`default`, `executor`, `deep-executor`, `__evo_agent__`, `__score__`, `__apply_agent__`), and the built-in skill ids (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `self`, `aws-knowledge`, `
|
|
82
|
+
**Server-refreshed**: `INSTRUCTIONS.md`, `prompts/`, `models/`, `docs/`, the built-in agent ids (`default`, `executor`, `deep-executor`, `goal`, `__evo_agent__`, `__score__`, `__apply_agent__`), and the built-in skill ids (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `self`, `aws-knowledge`, `web-search`, `halo`, `extension`). To customize one, copy it into the workspace scope (`<project>/.halo/...`) — workspace replaces global at runtime.
|
|
83
83
|
|
|
84
84
|
**Never overwritten**: anything else under `agents/` or `skills/` (your own creations), and everything under `secrets/`.
|
|
85
85
|
|
|
@@ -35,7 +35,7 @@ Goal: a skill called `caption-image` that calls `api.example.com` with an API ke
|
|
|
35
35
|
Beside `SKILL.md`:
|
|
36
36
|
|
|
37
37
|
```yaml
|
|
38
|
-
#
|
|
38
|
+
# skills/caption-image/config.yaml
|
|
39
39
|
params:
|
|
40
40
|
- key: api_key
|
|
41
41
|
description: API key for api.example.com
|
|
@@ -107,7 +107,7 @@ env vars (process.env.X) > workspace settings.yaml > global settings.yaml > sche
|
|
|
107
107
|
|
|
108
108
|
If `process.env.EXAMPLE_KEY` is unset and the value at `caption-image.params.api_key` is `<<EXAMPLE_KEY>>`:
|
|
109
109
|
- The agent sees the literal `<<EXAMPLE_KEY>>` in the rendered skill body
|
|
110
|
-
- The server logs `[
|
|
110
|
+
- The server logs `[MdVars] Env var "EXAMPLE_KEY" not set — keeping <<EXAMPLE_KEY>> literal`
|
|
111
111
|
- API calls the agent makes will fail with a clear error (usually HTTP 401) and the error message will reveal the literal, so the user can tell what to fix
|
|
112
112
|
|
|
113
113
|
This is intentional: empty strings hide the misconfiguration; literals make it visible.
|
|
@@ -155,7 +155,7 @@ Free-form, but consistency helps. Common patterns in Halo:
|
|
|
155
155
|
| Env var | Purpose |
|
|
156
156
|
|---|---|
|
|
157
157
|
| `HALO_PASSWORD` | Server login password |
|
|
158
|
-
| `
|
|
158
|
+
| `HALO_MAX_CONTEXT_TOKENS` | Server default context window (272000) |
|
|
159
159
|
| `AWS_REGION` / `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | AWS / Bedrock |
|
|
160
160
|
|
|
161
161
|
For your own skills, `UPPER_SNAKE` prefixed by the product is idiomatic (`NANO_BANANA_KEY`, `EXAMPLE_API_TOKEN`). The placeholder is case-sensitive — `<<example_key>>` won't match `EXAMPLE_KEY`.
|
|
@@ -24,7 +24,7 @@ The main agent (the one you talk to directly) sits at the top; any sub-agents it
|
|
|
24
24
|
|
|
25
25
|
- Collapse/expand: click the arrow
|
|
26
26
|
- Count badge `+3`: 3 descendants
|
|
27
|
-
-
|
|
27
|
+
- ⏹ icon: session was stopped (stopped_at is set)
|
|
28
28
|
|
|
29
29
|
## Message viewer
|
|
30
30
|
|
|
@@ -48,7 +48,7 @@ Top-right Bug icon toggles it. When on you also see:
|
|
|
48
48
|
|
|
49
49
|
## System prompt viewer
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Next to the Debug toggle, a `Prompt` button appears once the session has a recorded system prompt; click to expand the full system prompt (AGENT.md + INSTRUCTIONS.md + INDEX.md + TOOL_GUIDELINES + skill metadata).
|
|
52
52
|
|
|
53
53
|
Use it to see exactly what context the agent was given for a given turn.
|
|
54
54
|
|
|
@@ -71,7 +71,7 @@ Refreshing the browser or a network blip:
|
|
|
71
71
|
|
|
72
72
|
## Deleting a session
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Hover a session and click its trash icon (Sessions page or a chat tab), then confirm:
|
|
75
75
|
- Cascade delete in SQLite (parent + all descendants)
|
|
76
76
|
- `.halo/sessions/{agentId}/{sid}.json` removed
|
|
77
77
|
- Sidebar entry removed
|
|
@@ -30,7 +30,7 @@ Putting those in AGENT.md would bloat the system prompt. Skills use **progressiv
|
|
|
30
30
|
|
|
31
31
|
Workspace overrides global (same id).
|
|
32
32
|
|
|
33
|
-
**Built-in skills** (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `self`, `aws-knowledge`, `
|
|
33
|
+
**Built-in skills** (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `self`, `aws-knowledge`, `web-search`, `halo`, `extension`) are server-shipped and **force-overwritten on every server startup**. Local edits to these directories under `~/.halo/global/skills/` will be lost. To customize one, copy it into `<project>/.halo/skills/<id>/` (workspace replaces global) and edit there. Other skills under `~/.halo/global/skills/` — including any the user created via the admin UI — are untouched by the seeder.
|
|
34
34
|
|
|
35
35
|
## SKILL.md format
|
|
36
36
|
|
|
@@ -63,7 +63,7 @@ Follow the checklist in checklist.md...
|
|
|
63
63
|
|
|
64
64
|
## Open the Skills panel
|
|
65
65
|
|
|
66
|
-
Left-side Activity Bar →
|
|
66
|
+
Left-side Activity Bar → `⚡ Skills` icon.
|
|
67
67
|
|
|
68
68
|
Click any skill and the right pane becomes a mini workspace:
|
|
69
69
|
- File tree (every file in the skill directory) on the left
|
|
@@ -73,9 +73,9 @@ You can edit SKILL.md, add scripts, templates, example docs.
|
|
|
73
73
|
|
|
74
74
|
## Create / delete
|
|
75
75
|
|
|
76
|
-
`+`
|
|
76
|
+
Click the `+` on the **Global** or **Workspace** group header in the sidebar (the group you click decides the scope) and type a name.
|
|
77
77
|
|
|
78
|
-
Delete:
|
|
78
|
+
Delete: hover a skill and click its trash icon (after a confirmation). The toggle next to it disables / enables the skill.
|
|
79
79
|
|
|
80
80
|
## Letting an agent use a skill
|
|
81
81
|
|
|
@@ -158,7 +158,9 @@ Halo seeds these skills on every startup (the ids in `BUILTIN_SKILL_IDS`, `packa
|
|
|
158
158
|
| send-file | Deliver an image/video/file as a channel attachment by emitting `MEDIA:<absolute_path>` — works on Web / WeChat / Telegram / Slack / Feishu (no command; model-activated, workspace access) |
|
|
159
159
|
| self | The agent's own visual space (`.halo/canvas/self.html`) for self-expression (no command; model-activated, full access) |
|
|
160
160
|
| aws-knowledge | Query the official AWS Knowledge MCP server for up-to-date AWS docs (no command, `user-invocable: false`; model auto-activates) |
|
|
161
|
-
|
|
|
161
|
+
| web-search | Real-time web search in two gears: fast (Amazon Nova grounding, default) and deep (`--deep`, GPT-5.6 `web_search` via Bedrock Mantle, slower and token-expensive) (no command, `user-invocable: false`; model auto-activates) |
|
|
162
|
+
| halo | Platform self-knowledge — config directories, MD scopes, which bundled doc answers what (no command, `user-invocable: false`; model auto-activates) |
|
|
163
|
+
| extension | Install / list / remove canvas preview extensions — backs `/extension` (full access) |
|
|
162
164
|
|
|
163
165
|
All live under `~/.halo/global/skills/` and are force-overwritten on startup. To customize one, copy it into `<project>/.halo/skills/<id>/` and edit there (workspace overrides global). `tavily-web-search` remains an optional (non-seeded) skill.
|
|
164
166
|
|
|
@@ -278,7 +280,7 @@ nano-banana:
|
|
|
278
280
|
|
|
279
281
|
1. **Merge order**: workspace `<project>/.halo/settings.yaml` deep-merges over global `~/.halo/secrets/settings.yaml` (field-level merge, not block replacement)
|
|
280
282
|
2. **Env vars**: `<<ENV_NAME>>` inside a value is replaced with `process.env.ENV_NAME` at render time. **If the env var is missing, the `<<ENV_NAME>>` literal is kept** so agents can see what's unset and the user can fix it
|
|
281
|
-
3. **Unknown placeholders** stay as-is (e.g. `{{foo}}`, `{{nano-banana.params.missing-key}}`); the server logs a `[
|
|
283
|
+
3. **Unknown placeholders** stay as-is (e.g. `{{foo}}`, `{{nano-banana.params.missing-key}}`); the server logs a `[MdVars]` line saying it left the placeholder as-is
|
|
282
284
|
4. **Key charset**: `[\w-]`, hyphens allowed (e.g. `nano-banana`)
|
|
283
285
|
|
|
284
286
|
### Why put credentials in env vars
|