@turmind/halo 1.5.4 → 1.5.6
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-e0faeff1c4a0b0b9.js +1 -0
- package/admin-out/_next/static/chunks/app/page-44f707ea8dad57da.js +1 -0
- package/admin-out/_next/static/chunks/{webpack-1a264efd965db7ab.js → webpack-edf248e07333566d.js} +1 -1
- package/admin-out/_next/static/css/b8c87b919758c52b.css +3 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +5 -5
- package/bundled-docs/design/agent.md +10 -8
- package/bundled-docs/design/agentcore.md +13 -10
- package/bundled-docs/design/architecture.md +24 -14
- package/bundled-docs/design/canvas-extensions.md +19 -4
- 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 +14 -14
- package/bundled-docs/design/slack.md +22 -20
- package/bundled-docs/design/storage.md +30 -29
- 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 +86 -57
- package/bundled-docs/dev/deploy.md +8 -6
- package/bundled-docs/dev/env.md +29 -21
- package/bundled-docs/dev/logging.md +15 -29
- package/bundled-docs/dev/previews.md +20 -11
- package/bundled-docs/dev/tools.md +26 -14
- 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 +10 -9
- package/bundled-docs/guide/cli.md +10 -8
- package/bundled-docs/guide/delegation-and-access.md +1 -1
- 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 +9 -7
- package/bundled-docs/guide/testing-agents-and-skills.md +14 -15
- package/bundled-docs/guide/workspace.md +29 -17
- package/bundled-docs/requirements/agent.md +19 -18
- package/bundled-docs/requirements/chat.md +16 -17
- package/bundled-docs/requirements/command.md +10 -10
- package/bundled-docs/requirements/editor.md +12 -10
- package/bundled-docs/requirements/explorer.md +10 -7
- package/bundled-docs/requirements/session.md +16 -17
- package/bundled-docs/requirements/settings.md +30 -18
- 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 +10587 -12088
- package/package.json +1 -1
- package/templates/agents/default/agent.yaml +1 -0
- package/templates/config.yaml +6 -0
- package/templates/skills/extension/SKILL.md +25 -14
- package/templates/skills/extension/config.yaml +9 -0
- package/templates/skills/extension/templates/ext.sh +166 -34
- package/templates/skills/web-search/SKILL.md +20 -6
- package/templates/skills/web-search/config.yaml +13 -0
- package/templates/skills/web-search/search.py +27 -6
- package/templates/skills/workspace/share.md +11 -3
- package/templates/skills/workspace/stage.py +85 -5
- package/admin-out/_next/static/chunks/989-92e9683ae8d39750.js +0 -1
- package/admin-out/_next/static/chunks/app/page-7a635969198fc62b.js +0 -1
- package/admin-out/_next/static/css/4ea5f674146e54d8.css +0 -3
- /package/admin-out/_next/static/{lm8Bz3ugTSORyuSXfyx56 → visjvN510yFK-Ff5zwx-0}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{lm8Bz3ugTSORyuSXfyx56 → visjvN510yFK-Ff5zwx-0}/_ssgManifest.js +0 -0
|
@@ -54,6 +54,8 @@ halo acp \
|
|
|
54
54
|
| `--token` | yes | Web-channel token. `full` access required for multi-workspace use |
|
|
55
55
|
| `--workspace` | yes | Absolute server-side path for this adapter |
|
|
56
56
|
| `--agent-id` | no | Halo agent profile to use when ACP `session/new` creates a fresh session. Default `default` (alias `--agent`) |
|
|
57
|
+
| `--scheme` | no | `http` (default) or `https` — use `https` for a server behind a TLS reverse proxy |
|
|
58
|
+
| `--header "Name: value"` | no | Repeatable. Extra HTTP header sent on every request (like `curl -H`), for auth in front of the server — proxy cookie, `CF-Access-*`, basic auth |
|
|
57
59
|
|
|
58
60
|
Each adapter process binds to **one** workspace. To drive multiple workspaces concurrently from one token, run multiple adapter processes — one per workspace.
|
|
59
61
|
|
|
@@ -139,7 +141,7 @@ For now: if you want the agent to see a Mac-side file, paste it into the prompt.
|
|
|
139
141
|
| `401` on first prompt | Token typo, or token was deleted from admin |
|
|
140
142
|
| `403` when launching with `--workspace /some/other/path` | Token is `readonly` / `workspace` access — use a `full` token or omit `--workspace` |
|
|
141
143
|
| Tool calls don't appear in Claude Code | Expected — halo doesn't translate every event back as ACP `tool_call`. See [docs/dev/acp-adapter.md](../../dev/acp-adapter.md) for the full mapping |
|
|
142
|
-
| Two `session/prompt` calls on the same id, second hangs | Halo queues messages when a session is busy; ACP adapter ends the response with `[queued]`. Wait for the first to finish |
|
|
144
|
+
| Two `session/prompt` calls on the same id, second hangs | Halo queues messages when a session is busy; ACP adapter ends the response with `[queued — session busy]`. Wait for the first to finish |
|
|
143
145
|
| `/workspace switch <path>` worked but other tokens broke | You changed the db-level default. Switch back with another `/workspace switch`, or stop using slash commands from the adapter |
|
|
144
146
|
|
|
145
147
|
## Reference
|
|
@@ -77,16 +77,16 @@ After approval the bot is visible in your Feishu tenant. Repeat this step **ever
|
|
|
77
77
|
|
|
78
78
|
## Step 7 — Add the account in halo admin
|
|
79
79
|
|
|
80
|
-
Open halo admin → **Channels** → **Feishu** → **Add
|
|
80
|
+
Open halo admin → **Channels** → **Feishu** → **Add Bot**:
|
|
81
81
|
|
|
82
82
|
| Field | Value |
|
|
83
83
|
|---|---|
|
|
84
84
|
| App ID | from Step 2 |
|
|
85
85
|
| App Secret | from Step 2 |
|
|
86
|
-
| Verification
|
|
87
|
-
| Encrypt
|
|
88
|
-
|
|
|
89
|
-
|
|
|
86
|
+
| Verification Token (optional) | leave blank (long-connect doesn't use it) |
|
|
87
|
+
| Encrypt Key (optional) | leave blank (long-connect doesn't encrypt the wire) |
|
|
88
|
+
| Bind to workspace | absolute path, e.g. `/home/ubuntu/my-project` |
|
|
89
|
+
| Name (optional) | a label for the account |
|
|
90
90
|
| Access level | `readonly` (default), `workspace`, or `full` |
|
|
91
91
|
| Language | `en` or `zh` |
|
|
92
92
|
|
|
@@ -109,7 +109,7 @@ In Feishu desktop / mobile:
|
|
|
109
109
|
1. Top-bar search → type the bot's name → click into the bot's profile → start a DM → send `hello` → expect a streamed reply
|
|
110
110
|
2. Add the bot to a group: open a group → 设置 → 群机器人 → add → `@halo hi` → expect a reply
|
|
111
111
|
|
|
112
|
-
If nothing happens, check halo server logs (
|
|
112
|
+
If nothing happens, check halo server logs (`~/.halo/global/logs/server.log`) for `[Feishu]` lines.
|
|
113
113
|
|
|
114
114
|
## How halo handles inbound
|
|
115
115
|
|
|
@@ -117,18 +117,20 @@ If nothing happens, check halo server logs (`/tmp/halo-server.log`) for `[feishu
|
|
|
117
117
|
- **Groups** — only `@`-mentions wake the bot up
|
|
118
118
|
- **Threads** — Feishu's `root_id` is used as the session boundary; replies stay in-thread
|
|
119
119
|
- **Files** — images go to the LLM as multimodal content; other files are saved under `<workspace>/.halo/assets/feishu/inbound/<accountId>/<date>/`
|
|
120
|
+
- **After a server restart** — if the agent was mid-task when halo restarted, its reply still reaches the most recent 1:1 DM the bot talked in; group threads reconnect on their next `@`-mention
|
|
120
121
|
- **Self-loop** — bot-authored events are dropped to prevent the bot replying to itself
|
|
121
122
|
|
|
122
123
|
## Slash commands
|
|
123
124
|
|
|
124
|
-
Same set as the other channels — type as plain text in a DM
|
|
125
|
+
Same set as the other channels — type as plain text, **in a 1:1 DM only** (in a group the text goes to the agent as a normal message):
|
|
125
126
|
|
|
126
127
|
| Command | Effect |
|
|
127
128
|
|---|---|
|
|
128
|
-
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` |
|
|
129
|
+
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` |
|
|
129
130
|
| `/agent <verb>` | Manage agents (`list` / `switch` / `desc` open to all; `delete` full; `create` / `update` via skill, full) |
|
|
130
131
|
| `/skill <verb>` | Manage skills (`list` / `desc` open; `disable` / `enable` workspace; `delete` full; `create` / `update` via skill, full) |
|
|
131
132
|
| `/workspace <verb>` | Workspace: `info` (all) / `switch <path>` (full) / `setup` / `tidy` (workspace) / `share` (full) |
|
|
133
|
+
| `/cron` `/acp` `/extension` | Skill-backed object commands (full access); `/evo [hint]` queues a self-evolution run |
|
|
132
134
|
| `/help` | List commands — object commands show only the verbs you can run |
|
|
133
135
|
|
|
134
136
|
## Cron jobs targeting Feishu
|
|
@@ -145,12 +147,12 @@ Search depends on the contact and `im:chat:readonly` scopes from Step 4. If auto
|
|
|
145
147
|
|
|
146
148
|
| Symptom | Cause / fix |
|
|
147
149
|
|---|---|
|
|
148
|
-
| `99991663` on Add
|
|
149
|
-
| `99991668` on Add
|
|
150
|
+
| `99991663` on Add Bot | App Secret wrong — copy it again from Step 2 |
|
|
151
|
+
| `99991668` on Add Bot | Bot capability not enabled — go back to Step 3 |
|
|
150
152
|
| Bot exists but receives no events | Step 5 mode is set to webhook (not long-connect), or Step 6 wasn't done |
|
|
151
153
|
| Permission errors when sending images / reading group info | New scope was approved but app version wasn't republished — go to Step 6 |
|
|
152
154
|
| Can't find the bot in Feishu search | Step 6 wasn't done. Search results are tenant-wide and only show **published** apps |
|
|
153
|
-
| Multiple halo instances reply to the same message | Only one process should hold the long-connect. Check `~/.halo/global/server.
|
|
155
|
+
| Multiple halo instances reply to the same message | Only one process should hold the long-connect. Check `~/.halo/global/server.lock` for the lock |
|
|
154
156
|
|
|
155
157
|
## Multi-tenant setup
|
|
156
158
|
|
|
@@ -83,14 +83,14 @@ In the Slack desktop client, fully quit and reopen (or `Cmd+R` to reload) — ot
|
|
|
83
83
|
|
|
84
84
|
## Step 7 — Add the account in halo admin
|
|
85
85
|
|
|
86
|
-
Open halo admin → **Channels** → **Slack** → **Add
|
|
86
|
+
Open halo admin → **Channels** → **Slack** → **Add Bot**:
|
|
87
87
|
|
|
88
88
|
| Field | Value |
|
|
89
89
|
|---|---|
|
|
90
|
-
| Bot
|
|
91
|
-
| App
|
|
92
|
-
|
|
|
93
|
-
|
|
|
90
|
+
| Bot Token (xoxb-…) | the `xoxb-…` from Step 5 |
|
|
91
|
+
| App Token (xapp-…) | the `xapp-…` from Step 2 |
|
|
92
|
+
| Bind to workspace | absolute path to the workspace this bot drives, e.g. `/home/ubuntu/my-project` |
|
|
93
|
+
| Name (optional) | e.g. "Engineering bot" |
|
|
94
94
|
| Access level | `readonly` (default), `workspace`, or `full` |
|
|
95
95
|
| Language | `en` or `zh` |
|
|
96
96
|
|
|
@@ -111,7 +111,7 @@ Pick the level with that in mind:
|
|
|
111
111
|
1. In Slack, click the bot's name in the sidebar to start a DM → type `hello` → expect a streamed reply
|
|
112
112
|
2. Invite the bot into a channel: `/invite @halo` → mention it: `@halo what files are in this workspace?` → expect a reply in the same thread
|
|
113
113
|
|
|
114
|
-
If nothing happens, check the halo server logs (
|
|
114
|
+
If nothing happens, check the halo server logs (`~/.halo/global/logs/server.log`) for `[Slack]` lines.
|
|
115
115
|
|
|
116
116
|
## How halo handles inbound
|
|
117
117
|
|
|
@@ -119,18 +119,20 @@ If nothing happens, check the halo server logs (`/tmp/halo-server.log`) for `[sl
|
|
|
119
119
|
- **Channels / groups** — only `@`-mentions wake the bot up, plain messages are ignored
|
|
120
120
|
- **Threads** — the bot replies in-thread; one halo session per thread, so a long thread stays in one conversation
|
|
121
121
|
- **Files** — images go to the LLM as multimodal content; other files are saved under `<workspace>/.halo/assets/slack/inbound/<accountId>/<date>/`
|
|
122
|
+
- **After a server restart** — if the agent was mid-task when halo restarted, its reply still reaches the most recent conversation (DM or thread) the bot talked in (one per bot); other threads reconnect on their next message
|
|
122
123
|
- **`bot_id` self-loop** — messages posted by any bot (including this one) are dropped, so the agent can't reply to itself
|
|
123
124
|
|
|
124
125
|
## Slash commands
|
|
125
126
|
|
|
126
|
-
Same set as the other channels — type
|
|
127
|
+
Same set as the other channels, but **only in a DM with the bot** — in a channel or thread the text goes to the agent as a normal message. Slack's own client intercepts a leading `/`, so type `!` instead (e.g. `!session list`); halo treats `!cmd` as `/cmd` and shows `!…` in its replies. The table below uses `/`:
|
|
127
128
|
|
|
128
129
|
| Command | Effect |
|
|
129
130
|
|---|---|
|
|
130
|
-
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` |
|
|
131
|
+
| `/session <verb>` | Session lifecycle: `new` / `list` / `switch <n>` / `stop` / `interrupt` / `compact` / `context` / `info` |
|
|
131
132
|
| `/agent <verb>` | Manage agents (`list` / `switch` / `desc` open to all; `delete` full; `create` / `update` via skill, full) |
|
|
132
133
|
| `/skill <verb>` | Manage skills (`list` / `desc` open; `disable` / `enable` workspace; `delete` full; `create` / `update` via skill, full) |
|
|
133
134
|
| `/workspace <verb>` | Workspace: `info` (all) / `switch <path>` (full) / `setup` / `tidy` (workspace) / `share` (full) |
|
|
135
|
+
| `/cron` `/acp` `/extension` | Skill-backed object commands (full access); `/evo [hint]` queues a self-evolution run |
|
|
134
136
|
| `/help` | List commands — object commands show only the verbs you can run |
|
|
135
137
|
|
|
136
138
|
## Cron jobs targeting Slack
|
|
@@ -149,10 +151,10 @@ The search depends on the `users:read` / `channels:read` scopes added in Step 4.
|
|
|
149
151
|
|---|---|
|
|
150
152
|
| **"向此应用发送消息的功能已关闭"** banner on the DM page | Step 6 wasn't done — open App Home, enable Messages Tab, reload Slack |
|
|
151
153
|
| Account created but no events arrive | Socket Mode token (`xapp-`) is wrong, or `connections:write` scope missing on it |
|
|
152
|
-
| `not_authed` / `invalid_auth` error on Add
|
|
154
|
+
| `not_authed` / `invalid_auth` error on Add Bot | Bot token (`xoxb-`) typo, or you pasted the App token in the Bot field by mistake |
|
|
153
155
|
| Bot replies to itself in a loop | Should never happen — halo drops all `bot_id`-tagged messages. If it does, file a bug |
|
|
154
156
|
| Search dropdown empty in cron form | Missing `users:read` / `channels:read` scopes — add them, then Reinstall to Workspace |
|
|
155
|
-
| Multiple halo instances reply to the same message | Only one process should hold the Socket Mode connection. Check `~/.halo/global/server.
|
|
157
|
+
| Multiple halo instances reply to the same message | Only one process should hold the Socket Mode connection. Check `~/.halo/global/server.lock` for the lock |
|
|
156
158
|
|
|
157
159
|
## Multi-workspace setup
|
|
158
160
|
|
|
@@ -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
|
|
|
@@ -114,7 +115,7 @@ 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
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`)
|