@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
|
@@ -104,7 +104,7 @@ Extra rules:
|
|
|
104
104
|
- `view_image` also needs a vision-capable model.
|
|
105
105
|
- A skill with `requiresAccess` above the session level is hidden (no metadata, no `activate_skill`).
|
|
106
106
|
- Relay tools are only built for `full` sessions.
|
|
107
|
-
- Hidden from `workspace` / `readonly` sessions
|
|
107
|
+
- Hidden from `workspace` / `readonly` sessions only (Full sessions, the admin file explorer and the terminal see everything): a built-in list that is always included (`~/.halo/secrets`, `~/.aws`, `~/.ssh`, `~/.gnupg`, `~/.docker`, `~/.config/gh`, `~/.gitconfig`, `~/.git-credentials`, `~/.npmrc`, `~/.netrc`, the global evo/cron/runs databases, internal-session transcripts, logs, …), any extra paths added in `general.sandbox.hidden_dirs` / `hidden_files` (they add to the built-in list, never replace it), plus the workspace's own `.halo/sessions`, `.halo/logs`, `.halo/evo`, `.halo/halo.db*` (fixed in code). The rest of `.halo/` stays readable.
|
|
108
108
|
|
|
109
109
|
### Where a session's level comes from
|
|
110
110
|
|
|
@@ -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
|
|
|
@@ -138,7 +138,7 @@ params:
|
|
|
138
138
|
secrets: [] # rare for skills — server-side only, never substituted into shell_exec
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
The Settings page reads this declaration and renders inputs grouped under the skill's name. The user's value lands in `~/.halo/secrets/settings.yaml` at `<skill-id>.params.api_key`.
|
|
141
|
+
The Settings page reads this declaration and renders inputs grouped under the skill's name. The user's value lands in `~/.halo/secrets/settings.yaml` at `<skill-id>.params.api_key`. Among built-ins, `extension` declares a plain `hub_repo` param and `web-search` plain `fast_region` / `deep_region` params; `halo setup` asks only for built-in skills' `secret: true` params, so plain ones are set in Settings → Skills.
|
|
142
142
|
|
|
143
143
|
> Credentials **do not** belong directly in `settings.yaml` — use `<<ENV_NAME>>` placeholders with the real value in env vars, or type them once into the Settings page (which still encrypts in the UI but stores plaintext on disk). Don't write credentials in SKILL.md either. SKILL.md + the schema in `config.yaml` are meant to be shareable; values are the user's.
|
|
144
144
|
|
|
@@ -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 on AWS Bedrock (needs the machine's AWS credentials with Bedrock access) in two gears: fast (Amazon Nova grounding, default; US regions only) and deep (`--deep`, GPT-5.6 `web_search` via Bedrock Mantle, slower and token-expensive); regions set via its `fast_region` / `deep_region` params (Settings → Skills) (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); its `hub_repo` param (Settings → Skills) picks the hub `install <id>` pulls from (default turmind/halo-hub) |
|
|
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
|
|
@@ -6,7 +6,7 @@ How to verify an agent you just created behaves right, and how to exercise a ski
|
|
|
6
6
|
|
|
7
7
|
### 1. Open the agent
|
|
8
8
|
|
|
9
|
-
Activity Bar →
|
|
9
|
+
Activity Bar → `🤖 Agents` → click the agent you want to test.
|
|
10
10
|
|
|
11
11
|
### 2. Click Test
|
|
12
12
|
|
|
@@ -32,21 +32,20 @@ The Explorer's Chat panel is now ready with your agent pre-selected. Send a mess
|
|
|
32
32
|
|
|
33
33
|
- **Does it greet the way AGENT.md says?** Send "Hi". Check the reply matches the personality you wrote.
|
|
34
34
|
- **Does it call the right tools?** If the agent is supposed to read files, ask it to. Watch the inline tool-call card — it shows the tool name, arguments, and (expandable) output.
|
|
35
|
-
- **Is the system prompt what you expect?** Open the Sessions panel (Activity Bar →
|
|
35
|
+
- **Is the system prompt what you expect?** Open the Sessions panel (Activity Bar → `📨 Sessions`), select this session, toggle **Debug mode**, click the **Prompt** button in the viewer header. Full rendered system prompt (AGENT.md + USER.md + INSTRUCTIONS.md + skill metadata + tool list).
|
|
36
36
|
- **Is the model correct?** Debug mode's usage badges show `model` on every assistant turn.
|
|
37
37
|
|
|
38
38
|
### 5. Iterate
|
|
39
39
|
|
|
40
|
-
Edits
|
|
40
|
+
Edits are never retroactive — the turn that is running finishes on the old config — but a session is rebuilt from disk after every run, so the **next turn** of an existing session already picks them up:
|
|
41
41
|
|
|
42
42
|
| What you changed | When it takes effect |
|
|
43
43
|
|---|---|
|
|
44
|
-
| `AGENT.md` body | Next
|
|
45
|
-
| `
|
|
46
|
-
|
|
|
47
|
-
| Env vars (`<<ENV>>` placeholders) | Restart server, then next session spawn |
|
|
44
|
+
| `AGENT.md` body, `agent.yaml` tools / skills / model | Next turn (and any new session) |
|
|
45
|
+
| `settings.yaml` values | Next turn (or next `activate_skill` call for SKILL bodies) |
|
|
46
|
+
| Env vars (`<<ENV>>` placeholders) | Restart server, then next turn |
|
|
48
47
|
|
|
49
|
-
So the loop is: edit → `/session new` → re-test. No server restart for MD/YAML/settings changes.
|
|
48
|
+
So the loop is: edit → send another message (or `/session new` for a clean slate) → re-test. No server restart for MD/YAML/settings changes. Full table in [delegation-and-access.md](delegation-and-access.md#2-when-a-config-edit-takes-effect).
|
|
50
49
|
|
|
51
50
|
---
|
|
52
51
|
|
|
@@ -56,7 +55,7 @@ There is **no separate "test this skill" button**. Skills exist to be called by
|
|
|
56
55
|
|
|
57
56
|
### 1. Attach the skill to an agent
|
|
58
57
|
|
|
59
|
-
In the agent's Form view, the **Skills** section lists every skill in the workspace. Check the one you want to test
|
|
58
|
+
In the agent's Form view, the **Skills** section lists every skill in the workspace. Check the one you want to test (the form auto-saves).
|
|
60
59
|
|
|
61
60
|
Equivalent YAML edit:
|
|
62
61
|
|
|
@@ -114,22 +113,22 @@ The env var is unset. Halo renders the literal placeholder so you (and the agent
|
|
|
114
113
|
|
|
115
114
|
### Saved agent changes don't seem to apply
|
|
116
115
|
|
|
117
|
-
You might be looking at a
|
|
116
|
+
You might be looking at a turn that was already running when you saved — it finishes on the old config, and the edit applies from the next turn. If the next turn still behaves the old way, `/session new` to start fresh.
|
|
118
117
|
|
|
119
118
|
### Deleting a test session
|
|
120
119
|
|
|
121
|
-
Sessions →
|
|
120
|
+
Sessions → hover the session → trash icon. Or programmatically: `DELETE /api/sessions/logs/:id`. Deletion cascades to child sessions (sub-agents), their JSON files, and the SQLite rows. See [dev/api.md#session-logs](../dev/api.md#session-logs-unified).
|
|
122
121
|
|
|
123
122
|
### "Test button does something weird" — the session is orphaned
|
|
124
123
|
|
|
125
|
-
Since 1.5.3-alpha, Test no longer clears any stored session: it only opens a new draft tab (the old `localStorage.halo_session_<projectId>` key is gone). The new session is created by the first message you send from that tab. If you see an old conversation instead of an empty one, the draft tab isn't the one on screen. Click **+**
|
|
124
|
+
Since 1.5.3-alpha, Test no longer clears any stored session: it only opens a new draft tab (the old `localStorage.halo_session_<projectId>` key is gone). The new session is created by the first message you send from that tab. If you see an old conversation instead of an empty one, the draft tab isn't the one on screen. Click **+** below the session tabs to bring it back (an unused draft is reused).
|
|
126
125
|
|
|
127
126
|
---
|
|
128
127
|
|
|
129
128
|
## References
|
|
130
129
|
|
|
131
130
|
- Test button implementation: `testAgent` in [packages/admin/src/features/agents/agent-management-main.tsx](../../../packages/admin/src/features/agents/agent-management-main.tsx)
|
|
132
|
-
- `activate_skill` runtime: [packages/server/src/agents/agent-loader.ts
|
|
133
|
-
- Skill metadata → system prompt: [packages/server/src/agents/agent-loader.ts
|
|
134
|
-
- Chat session creation on first message: [packages/server/src/ws/handler.ts
|
|
131
|
+
- `activate_skill` runtime: [packages/server/src/agents/agent-loader.ts](../../../packages/server/src/agents/agent-loader.ts) (function `createSkillTool`)
|
|
132
|
+
- Skill metadata → system prompt: [packages/server/src/agents/agent-loader.ts](../../../packages/server/src/agents/agent-loader.ts) (function `buildSkillPrompt`)
|
|
133
|
+
- Chat session creation on first message: [packages/server/src/ws/handler.ts](../../../packages/server/src/ws/handler.ts) (function `handleChat`)
|
|
135
134
|
- End-to-end test scenarios for the session system: [test/session.md](../test/session.md)
|
|
@@ -21,17 +21,23 @@ Overall interface layout plus the three sidebar features.
|
|
|
21
21
|
- **Sidebar** is resizable
|
|
22
22
|
- **Bottom Panel** is resizable; the header has a Chat / Terminal switch
|
|
23
23
|
|
|
24
|
-
The
|
|
24
|
+
The docked Bottom Panel belongs to the Explorer tab; every other tab fills the whole right side (a floating panel stays visible on any tab).
|
|
25
25
|
|
|
26
26
|
## Activity Bar tabs
|
|
27
27
|
|
|
28
28
|
| Icon | Tab | Purpose |
|
|
29
29
|
|---|---|---|
|
|
30
30
|
| 📄 Files | Explorer | File tree + Monaco editor |
|
|
31
|
+
| 🌿 Branch | Source Control | Changes, commit, push, history (hidden for non-git workspaces) |
|
|
31
32
|
| 📨 Messages | Sessions | Session history + debug viewer |
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
33
|
+
| ⚡ Zap | Skills | Skill editing (mini workspace) |
|
|
34
|
+
| 🤖 Bot | Agents | Agent configuration (Form / Edit) |
|
|
35
|
+
| 💬 Chat bubble | Channels | Web / Telegram / Slack / Feishu / WeCom / WeChat accounts |
|
|
36
|
+
| ✨ Sparkles | Evolution | Self-evolution runs and review |
|
|
37
|
+
| 🕐 Clock | Cron | Scheduled agent runs |
|
|
38
|
+
| 🎚️ Sliders | Settings | Settings form (bottom of the bar) |
|
|
39
|
+
|
|
40
|
+
Below Settings sits the **quick toggles** gear. The thin bar under it has one segment per item: network (green = connected, amber = probing, red = disconnected; the gear turns red too), finish notification, and in the desktop app also pin-on-top and keep-screen-awake (amber when on). Hover for a summary. Click to open a panel where you switch them; it stays open until you click outside, press Esc, or click the gear again.
|
|
35
41
|
|
|
36
42
|
## Switching workspace
|
|
37
43
|
|
|
@@ -44,29 +50,30 @@ The Explorer top bar has a path input + 📁🔍 picker button:
|
|
|
44
50
|
- Single click to select / double click to enter
|
|
45
51
|
- Open button to confirm
|
|
46
52
|
|
|
47
|
-
The URL carries `?folder=/abs/path`, persisting the workspace across refreshes. When `folder` is absent, Halo
|
|
53
|
+
The URL carries `?folder=/abs/path`, persisting the workspace across refreshes. When `folder` is absent, Halo reopens the last workspace you used, or the home directory if there isn't one.
|
|
48
54
|
|
|
49
55
|
## Explorer (file tree)
|
|
50
56
|
|
|
51
57
|
VS Code style:
|
|
52
|
-
- Click: select
|
|
53
|
-
- Double click: open
|
|
58
|
+
- Click a file: select, then open it after a short delay (300 ms)
|
|
59
|
+
- Double click: open the file right away; on a folder, expand / collapse it
|
|
60
|
+
- Click a folder: expand / collapse
|
|
54
61
|
- Ctrl/Cmd + click: toggle multi-select
|
|
55
62
|
- Shift + click: range select
|
|
56
63
|
- Drag: move files/folders (multi-select drag supported)
|
|
57
64
|
|
|
58
|
-
Right-click menu: New File / New Folder / Rename / Delete /
|
|
65
|
+
Right-click menu: New File / New Folder / Open in Integrated Terminal / Reveal in File Manager (desktop app only) / Open to the Side / Download / Rename / Delete / Open as Workspace (folders).
|
|
59
66
|
|
|
60
67
|
**Quick Open**: `Cmd/Ctrl+P` opens fuzzy file search — server-side, full-project, independent of expansion state.
|
|
61
68
|
|
|
62
69
|
### Skipped directories
|
|
63
|
-
`node_modules`, `__pycache__
|
|
70
|
+
`.git`, `.DS_Store`, `node_modules`, `__pycache__`.
|
|
64
71
|
|
|
65
72
|
## Editor
|
|
66
73
|
|
|
67
74
|
Monaco, multi-tab:
|
|
68
75
|
- Drag tabs to reorder
|
|
69
|
-
- Unsaved tabs show
|
|
76
|
+
- Unsaved tabs show an amber dot
|
|
70
77
|
- Closing an unsaved tab prompts for confirmation
|
|
71
78
|
- **Auto-refresh**: detects agent-written file changes by mtime and updates contents
|
|
72
79
|
- **Tab persistence**: stored in localStorage, survives refreshes
|
|
@@ -78,11 +85,11 @@ Special-file previews:
|
|
|
78
85
|
|
|
79
86
|
### Preview extensions
|
|
80
87
|
|
|
81
|
-
File types the editor has no built-in viewer for (e.g. `.glb` 3D models) show a "no built-in preview" page with a link to [halo-hub](https://github.com/turmind/halo-hub), where ready-made viewer extensions live. Extensions are installed **server-wide** (`~/.halo/global/extensions/<id>/`) and take effect in every open tab immediately — no restart, no page reload.
|
|
88
|
+
File types the editor has no built-in viewer for (e.g. `.glb` 3D models) show a "no built-in preview" page with a link to [halo-hub](https://github.com/turmind/halo-hub), where ready-made viewer extensions live: 3D models (`glb` — `.glb` / `.gltf` / `.obj` / `.stl`), draw.io diagrams (`drawio`, editable), Excalidraw whiteboards (`excalidraw`, editable) and Jupyter notebooks (`ipynb`, read-only). Extensions are installed **server-wide** (`~/.halo/global/extensions/<id>/`) and take effect in every open tab immediately — no restart, no page reload.
|
|
82
89
|
|
|
83
90
|
Three ways to install:
|
|
84
91
|
- **Settings → Extensions**: upload the extension `.zip`; the same panel lists what's installed (version, file types) and removes extensions
|
|
85
|
-
- **Ask the agent**: `/extension install glb` pulls the latest `glb-v*` release from halo-hub; `/extension install /path/to/x.zip` or an `https://…zip` URL also work. `/extension list` and `/extension remove <id>` round it out (full-access sessions only)
|
|
92
|
+
- **Ask the agent**: `/extension install glb` pulls the latest `glb-v*` release from the configured hub — halo-hub unless you change **Settings → Skills → extension → `hub_repo`** (a GitHub / Gitea / Forgejo / GitLab repo URL, `owner/repo` for GitHub, or any git URL / local repo, which installs from `glb-v<x.y.z>` tags); `/extension install /path/to/x.zip` or an `https://…zip` URL also work. `/extension list` and `/extension remove <id>` round it out (full-access sessions only)
|
|
86
93
|
- **By hand**: drop an unpacked extension directory into `~/.halo/global/extensions/` — the server watches that directory
|
|
87
94
|
|
|
88
95
|
Once installed, matching files open in the extension's viewer. If more than one viewer can show a file, the preview header gets an **Open with** menu (per tab, not remembered). Extensions that declare the `save` capability can edit the file: the tab shows the usual dirty dot, and the header **Save** button writes it back (Ctrl/Cmd+S doesn't apply to extension tabs). If the file changed on disk in between, you're asked before overwriting.
|
|
@@ -91,13 +98,13 @@ Once installed, matching files open in the extension's viewer. If more than one
|
|
|
91
98
|
|
|
92
99
|
Bottom-panel Terminal tab:
|
|
93
100
|
- xterm.js frontend + node-pty backend
|
|
94
|
-
- Multi-instance (each
|
|
101
|
+
- Multi-instance (each terminal owns its own PTY), listed as a collapsible vertical tab strip on the right
|
|
95
102
|
- cwd = current project root
|
|
96
|
-
- Reconnect grace:
|
|
103
|
+
- Reconnect grace: 5 min (`timeout.terminal_grace`)
|
|
97
104
|
|
|
98
105
|
## Settings
|
|
99
106
|
|
|
100
|
-
`⚙️ Settings` tab. Form
|
|
107
|
+
`⚙️ Settings` tab. Form view:
|
|
101
108
|
- Auto-generated controls (text / number / password / toggle) per declared field
|
|
102
109
|
- Each row shows source (`workspace` / `inherited from global` / `unset`) + Reset
|
|
103
110
|
|
|
@@ -105,7 +112,10 @@ Scope: Global / Workspace toggle. Same key: workspace overrides global, leaf by
|
|
|
105
112
|
|
|
106
113
|
Sections are grouped by declarer:
|
|
107
114
|
- **System** — server-built-in knobs (session limits, compaction, sandbox, logging)
|
|
115
|
+
- **Security** — change password, log out
|
|
116
|
+
- **Extensions** — installed canvas preview extensions
|
|
108
117
|
- **Model Providers** — secrets declared by each `models/<id>.yaml` (AWS, Kimi, DeepSeek, …)
|
|
118
|
+
- **Agents** — params/secrets declared by each global agent's `agent-config.yaml`
|
|
109
119
|
- **Skills** — params/secrets declared by each `skills/<id>/config.yaml`
|
|
110
120
|
- **Orphans** — values in settings.yaml whose namespace isn't currently declared (uninstalled skill leftovers); manual cleanup only
|
|
111
121
|
|
|
@@ -127,11 +137,13 @@ See [requirements/settings.md](../requirements/settings.md), [secrets-and-creden
|
|
|
127
137
|
|---|---|
|
|
128
138
|
| `Cmd/Ctrl + P` | Quick Open |
|
|
129
139
|
| `Cmd/Ctrl + S` | Save file |
|
|
130
|
-
| `
|
|
140
|
+
| `Alt + W` | Close editor tab (browser; the desktop app uses `Cmd/Ctrl + W`) |
|
|
141
|
+
| `Cmd/Ctrl + \`` | Switch the bottom panel between Chat and Terminal |
|
|
142
|
+
| `Esc` | Leave editor maximize |
|
|
131
143
|
|
|
132
144
|
## Bottom panel switching
|
|
133
145
|
|
|
134
|
-
Two tabs in the header: Chat / Terminal
|
|
146
|
+
Two tabs in the header: Chat / Terminal, plus maximize and float / dock buttons. The panel opens on Chat after a refresh.
|
|
135
147
|
|
|
136
148
|
## Login / logout
|
|
137
149
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Agent — Requirements
|
|
2
2
|
|
|
3
|
-
Agent configuration management: creating, editing
|
|
3
|
+
Agent configuration management: creating, editing via Form / mini workspace (agent.yaml, AGENT.md).
|
|
4
4
|
|
|
5
5
|
## Layout
|
|
6
6
|
|
|
7
|
-
Left sidebar has two collapsible groups (Global / Workspace); right side is the editor:
|
|
7
|
+
Left sidebar has two collapsible groups (Global / Workspace — the latter only while a workspace is open); right side is the editor. Each group header has a `+` to create an agent in that scope, and the sidebar header has a refresh button:
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
┌─────────────────┬───────────────────────────────┐
|
|
@@ -20,22 +20,22 @@ Left sidebar has two collapsible groups (Global / Workspace); right side is the
|
|
|
20
20
|
|
|
21
21
|
## Core behaviour
|
|
22
22
|
|
|
23
|
-
###
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
-
|
|
23
|
+
### Form / Edit dual view
|
|
24
|
+
- **Form view** (default): data-driven form derived from the YAML fields (name / description / priority / model incl. thinking and capabilities / system_prompt / team / tools / skills). Edits auto-save to `agent.yaml` (debounced 500 ms). If `agent.yaml` fails to load or parse, the form is replaced by an error with a Retry button and nothing is auto-saved (an unparsable file is fixed in Edit view); below the form, a read-only preview of `AGENT.md` is shown when it has content
|
|
25
|
+
- **Edit view**: the **Edit** button opens a mini workspace (file tree + editor) rooted at the agent's folder, for editing `agent.yaml` / `AGENT.md` directly; **Back** returns to the form, which reloads from disk
|
|
26
|
+
- The chosen view is remembered per agent in `localStorage`
|
|
27
27
|
|
|
28
28
|
### CRUD
|
|
29
29
|
| Operation | API |
|
|
30
30
|
|---|---|
|
|
31
31
|
| List | `GET /api/agent-configs?projectId=xxx` |
|
|
32
32
|
| Create | `POST /api/agent-configs` (body: `{name, description, scope, projectId?}`) |
|
|
33
|
-
| Read YAML | `GET /api/agent-configs/:id/yaml` |
|
|
34
|
-
| Write YAML | `PUT /api/agent-configs/:id/yaml` |
|
|
35
|
-
| Delete | `DELETE /api/agent-configs/:id` |
|
|
33
|
+
| Read YAML | `GET /api/agent-configs/:id/yaml` (`scope=workspace` requires `projectId`, else 400) |
|
|
34
|
+
| Write YAML | `PUT /api/agent-configs/:id/yaml` (same `projectId` rule) |
|
|
35
|
+
| Delete | `DELETE /api/agent-configs/:id` (same `projectId` rule — never falls back to deleting the same-named global agent) |
|
|
36
36
|
| Toggle disabled | `PATCH /api/agent-configs/:id/toggle` → `{ ok, disabled }` |
|
|
37
37
|
|
|
38
|
-
Creating requires name
|
|
38
|
+
Creating requires a name (description is optional); the id is derived from the name. The backend uses `defaultAgentYaml(name, description)` to produce a full YAML and also scaffolds an `AGENT.md`.
|
|
39
39
|
|
|
40
40
|
### Scope (Global / Workspace)
|
|
41
41
|
- **Global**: `~/.halo/global/agents/<id>/agent.yaml` — shared across projects
|
|
@@ -45,18 +45,18 @@ Same id present in both scopes: workspace wins; the overridden global is greyed
|
|
|
45
45
|
|
|
46
46
|
**Cross-scope conflict**: creating a same-name agent in the other scope prompts about the overwrite behaviour.
|
|
47
47
|
|
|
48
|
-
**Delete protection**: at least one global agent must remain (server-enforced).
|
|
48
|
+
**Delete protection**: at least one global agent must remain (server-enforced). The sidebar offers delete (hover trash) on workspace agents only; global rows show a crown instead.
|
|
49
49
|
|
|
50
50
|
### Disable / Enable
|
|
51
|
-
- Toggle switch on each agent row in the admin sidebar. Disabled state is stored per workspace in the `disabled_items` table of `halo.db` (not in agent.yaml). Both global and workspace agents can be independently toggled per workspace.
|
|
51
|
+
- Toggle switch on each agent row in the admin sidebar (shown on hover while enabled; not offered for internal agents or when no workspace is open). Disabled state is stored per workspace in the `disabled_items` table of `halo.db` (not in agent.yaml). Both global and workspace agents can be independently toggled per workspace.
|
|
52
52
|
- Disabled agents are greyed out (opacity-40) with sub-text "disabled"; the toggle stays visible.
|
|
53
53
|
- Hidden from: the delegation roster, chat agent selector, `/workspace share` export.
|
|
54
54
|
- Still visible in the admin management sidebar for re-enabling.
|
|
55
55
|
|
|
56
56
|
### Tool selection
|
|
57
57
|
- **Session tools**: `start_session` / `session_list` / `query_session` / `interrupt_session` / `stop_session` / `archive_session` / `get_session_output` / `query_agent` — **not** selected by name; the whole bundle is granted automatically by a non-empty `team` (see below). Listing them under `agent.yaml tools` has no effect.
|
|
58
|
-
- **Workspace tools**: `file_read` / `file_write` / `file_edit` / `file_list` / `shell_exec` / `grep` / `glob` / `web_fetch`, returned by `GET /api/agent-configs/tools`
|
|
59
|
-
- **`activate_skill`**: auto-injected
|
|
58
|
+
- **Workspace tools**: `file_read` / `view_image` / `file_write` / `file_edit` / `file_list` / `shell_exec` / `grep` / `glob` / `web_fetch`, plus the single `relay_send` chip (grants the whole cross-workspace relay set, full-access sessions only), returned by `GET /api/agent-configs/tools`. `view_image` is dropped at runtime when the model can't take images (the form dims it)
|
|
59
|
+
- **`activate_skill`**: auto-injected when the YAML lists at least one usable skill (not disabled, allowed at the session's access level); loads the full SKILL.md on demand
|
|
60
60
|
|
|
61
61
|
### Team (delegation switch + whitelist)
|
|
62
62
|
- Optional `team: [id, …]` field in `agent.yaml`. A **non-empty** list is the on/off switch for delegation: it grants the whole session-tool bundle (`start_session` / `query_agent` / …) plus the prompt roster, AND scopes which agents this one may reach.
|
|
@@ -67,7 +67,7 @@ Same id present in both scopes: workspace wins; the overridden global is greyed
|
|
|
67
67
|
- **Picker only offers effective, enabled agents.** A chip shows iff the *effective* agent for that id is runnable — same resolve-then-check the runtime uses: a workspace agent shadows the same-id global (the overridden global is skipped), then anything disabled is dropped. So a global stays out of the picker when its workspace override is disabled, even though that global's own record isn't flagged disabled. Matches the chat / cron selectors and the runtime roster — you can never pick a teammate that can't actually run.
|
|
68
68
|
|
|
69
69
|
### Skill selection
|
|
70
|
-
`GET /api/skills?projectId=xxx` lists available skills; `agent.yaml`'s `skills` references them by id.
|
|
70
|
+
`GET /api/skills?projectId=xxx` lists available skills; `agent.yaml`'s `skills` references them by id. The form offers enabled skills only; ids that are referenced but not installed show as red ⚠ chips (click to remove).
|
|
71
71
|
|
|
72
72
|
### MD file editing
|
|
73
73
|
|
|
@@ -75,13 +75,14 @@ Same id present in both scopes: workspace wins; the overridden global is greyed
|
|
|
75
75
|
|---|---|---|
|
|
76
76
|
| AGENT.md | yes | Agent personality / behaviour (overrides YAML `system_prompt`) |
|
|
77
77
|
| INSTRUCTIONS.md | yes | User preferences (global or workspace scope) |
|
|
78
|
+
| INDEX.md | no | Project index, read-only through this API |
|
|
78
79
|
|
|
79
|
-
The
|
|
80
|
+
The admin edits `AGENT.md` through the Edit mini workspace; `INSTRUCTIONS.md` is not edited from this tab.
|
|
80
81
|
|
|
81
|
-
API: `GET/PUT /api/agent-configs/:id/md/:fileType`
|
|
82
|
+
API: `GET/PUT /api/agent-configs/:id/md/:fileType`, `GET /api/agent-configs/:id/md-all`
|
|
82
83
|
|
|
83
84
|
### Test button
|
|
84
|
-
-
|
|
85
|
+
- Opens a fresh chat draft tab (an untouched draft is reused) with this agent selected
|
|
85
86
|
- Dispatches a `halo:navigate` event to switch to explorer/chat
|
|
86
87
|
- The user chats with the selected agent in the main chat panel
|
|
87
88
|
- **Not rendered for internal agents** (`internal: true`) — they're delegated to by other agents, never driven directly
|
|
@@ -5,10 +5,9 @@ The primary surface for talking to an agent.
|
|
|
5
5
|
## Core behaviour
|
|
6
6
|
|
|
7
7
|
### Agent selection
|
|
8
|
-
-
|
|
9
|
-
- Lists every available agent (from `GET /api/agent-configs`; overridden and
|
|
10
|
-
-
|
|
11
|
-
- **Locked during an active session** — the agent is bound to the session; to change, start a new session (/session new)
|
|
8
|
+
- Dropdown in the composer's left control cluster (after the Debug button) selects which agent to use; hidden when only one agent is usable
|
|
9
|
+
- Lists every available agent (from `GET /api/agent-configs`; overridden, disabled and internal agents are hidden), highest priority first. A new session starts on the top one
|
|
10
|
+
- **Locked during an active session** (and while a response is streaming) — the agent is bound to the session; to change, start a new session (/session new)
|
|
12
11
|
- The Agents panel's "Test" button can also preselect an agent
|
|
13
12
|
|
|
14
13
|
### Access level
|
|
@@ -22,8 +21,8 @@ The primary surface for talking to an agent.
|
|
|
22
21
|
### Message rendering
|
|
23
22
|
- Markdown + code-block highlighting
|
|
24
23
|
- Tool-call card: expandable, shows tool name / input / output
|
|
25
|
-
- Sub-agent
|
|
26
|
-
-
|
|
24
|
+
- Sub-agent streams stay out of the chat: their reports land as a green callout, and Debug mode adds agent start/done markers labelled with the agent name (e.g. "Coder", "Researcher")
|
|
25
|
+
- A reply that has produced nothing yet shows a spinner with "Thinking..."
|
|
27
26
|
- The user bubble shows its send time (`HH:mm`, browser-local) at the left of its header row, after the expand chevron (see [User-message actions](#user-message-actions-expand--copy--delete)); hovering it gives the full date. Same bubble component as the Sessions tab, so both surfaces get it
|
|
28
27
|
- In Debug mode the system-prompt `context` row stays in the list but doesn't open an exchange of its own, so there is no blank strip above the first bubble
|
|
29
28
|
|
|
@@ -39,7 +38,7 @@ Actions on user-role turns: the blue sticky user bubble, plus the sub-agent-repo
|
|
|
39
38
|
- An expanded body is capped at 40vh and scrolls inside the bubble, because the bubbles are sticky.
|
|
40
39
|
- The compact-summary callout is collapsed by default and toggles from its own header, as before.
|
|
41
40
|
- **Copy** — copies the prompt text to the clipboard
|
|
42
|
-
- **Delete** (confirm dialog) — removes the whole exchange (the user turn + all responses up to the next user turn) with **two-layer semantics**: the LLM context (`rawMessages`) drops the turn physically — the model never sees it again, freeing context; the UI keeps the messages, rendered greyed-out with a "deleted" tag, as an audit trail. No undo; a deleted exchange loses its Delete button. Rejected with an
|
|
41
|
+
- **Delete** (confirm dialog) — removes the whole exchange (the user turn + all responses up to the next user turn) with **two-layer semantics**: the LLM context (`rawMessages`) drops the turn physically — the model never sees it again, freeing context; the UI keeps the messages, rendered greyed-out with a "deleted" tag, as an audit trail. No undo; a deleted exchange loses its Delete button. Rejected with an `Error:` message in the chat while the agent is running or compacting. Root sessions only (sub-session logs don't offer Delete). If the turn was already compacted out of raw context, only the UI marking happens (silent degrade). If a compact archived history while the panel was open, the action is refused with a plain explanatory notice (not an `Error:` bubble) — the panel's turn positions no longer match the active file; reopening the session re-anchors it and Delete works again. Design details in [design/session.md](../design/session.md#exchange-deletion-soft-ui--hard-raw), protocol in [design/ws.md](../design/ws.md).
|
|
43
42
|
|
|
44
43
|
### Archived history (scroll up to load)
|
|
45
44
|
Long sessions get their older exchanges moved out of the active log on compact, so opening a session stays fast no matter how long it has run. Loading older messages is **two-tiered** — a local, no-network tier first, then the network-backed archive:
|
|
@@ -49,19 +48,19 @@ Long sessions get their older exchanges moved out of the active log on compact,
|
|
|
49
48
|
- Only once the local window is fully expanded does the next scroll-to-top (or the "Load earlier messages" row that takes over the same slot) reach for the **network** tier: it loads one archived segment per gesture, oldest-newer order preserved, and becomes "No earlier messages" once everything is loaded. The two tiers can't be skipped or reordered — the archive row only appears once there's nothing left to expand locally
|
|
50
49
|
- Either tier preserves the reading position when older messages prepend — the view doesn't jump
|
|
51
50
|
- Loaded archive segments sit in a **collapsed** block ("Archived · N segment(s) · M messages") — the user scrolled up for older context, not to have hundreds of exchanges re-flow the view; expanding renders them like normal messages, growing **upward** so the viewport stays pinned to the newest end, with the expand/collapse toggle below the revealed content
|
|
52
|
-
- Archived exchanges are **read-only**: Copy still works, Delete is absent (the
|
|
51
|
+
- Archived exchanges are **read-only**: Copy still works, Delete is absent (they are no longer in the active log, so a turn position can't address them)
|
|
53
52
|
- Segments already loaded aren't re-fetched; a failed load can simply be retried with the same row
|
|
54
53
|
|
|
55
54
|
### Session tabs (right side)
|
|
56
|
-
The workspace's root sessions as **Chrome-style vertical tabs** on the right edge of the chat panel (`
|
|
55
|
+
The workspace's root sessions as **Chrome-style vertical tabs** on the right edge of the chat panel (`VerticalTabRow` rows inside a `ResizableSidebar`, default 200px). The list **is** the tab list: every session is "open", clicking a row shows it, and there is no close — deleting the session is the only way a tab goes away. Since 1.5.3-alpha this replaces the top tab strip, the History and New-session buttons beside the composer, and the "N previous sessions" link in the empty state.
|
|
57
56
|
|
|
58
57
|
- **Resize / collapse**: drag the left edge to resize (120–480px). Collapse from the header; the collapsed list is a `w-10` rail of square tabs, each showing the title's first letter plus the status dot in a corner, with a "+" at the bottom. Both settings are global preferences in `localStorage` (`halo_session_sidebar_open`, `halo_session_sidebar_width`); the list is open by default.
|
|
59
|
-
- **Rows**: one line per row,
|
|
58
|
+
- **Rows**: one line per row, status dot + title. Message count, time-ago and model are in the tooltip. `N msgs` counts the session's user turns **over its whole lifetime**, so compacting or archiving history never makes it go backwards.
|
|
60
59
|
- Each row leads with one fixed-size status dot (fixed so a state change never reflows a narrow list — the spinner it replaced did): **amber, pulsing** while a loaded tab's turn is running; **blue** when a loaded background tab is idle with unread output (output landed while it wasn't on screen; cleared when shown); **green** otherwise — including sessions no tab has loaded and tabs a reconnect released. The list endpoint's `status` is not used.
|
|
61
60
|
- Infinite scroll pages older sessions. A "+" footer starts a new session.
|
|
62
61
|
- **New session**: "+", `/session new` and `/clear` open a **draft tab**. A "New session" row sits on top while it is on screen, and an untouched draft is reused rather than duplicated. The session is created by the draft's first message or command. The previous session keeps streaming in its own tab.
|
|
63
62
|
- **Background tabs**: one WS connection carries every loaded tab, so a session that isn't on screen keeps receiving its turn. After a reconnect only the tab on screen is reattached; the others reload when clicked. Live capture and face (`<<<SHOW>>>`) markers only act for the tab on screen.
|
|
64
|
-
- **Delete (
|
|
63
|
+
- **Delete (trash icon)**: deletes the session after a confirm dialog in the UI language ("Delete this session? Its history cannot be recovered." / its Chinese counterpart). Deletion is the REST delete first, then the WS one, so sub-session files go too. The trash icon, like the **rename** pencil, appears only on hover — the active tab included.
|
|
65
64
|
- **Inline rename**: pencil → input in place, Enter/blur commits (`PATCH /sessions/logs/:id`), Esc cancels; empty or unchanged title is a no-op. Other session-list consumers refresh via the `session:changed` WS push
|
|
66
65
|
- **Persistence**: only the session on screen is remembered, per workspace, as `{active}` under `halo_chat_tabs_<projectId>`. A refresh restores and loads only that tab. The pre-tabs keys `halo_session_<projectId>` / `halo_session_id` are read once to seed it and then removed.
|
|
67
66
|
- **Switch loading**: clicking a row that hasn't loaded yet shows a "Loading session…" state in the message area, cleared only when the server's `state:snapshot` for that exact sessionId arrives (empty sessions included). Past 30s it degrades to "Slow network — still loading…" plus a Retry button (re-subscribes) — slow ≠ failed, nothing aborts on its own. Switching to an already-loaded tab is instant, and its scroll position is restored
|
|
@@ -72,9 +71,9 @@ The full command list is fetched from `GET /api/commands` per session and includ
|
|
|
72
71
|
|
|
73
72
|
| Command | Type | Purpose |
|
|
74
73
|
|---|---|---|
|
|
75
|
-
| `/help` |
|
|
74
|
+
| `/help` | server | List available commands |
|
|
76
75
|
| `/clear` | client | Alias for `/session new` (admin-UI shortcut, no server registration) |
|
|
77
|
-
| `/session new` | server | Start a new session |
|
|
76
|
+
| `/session new` | server | Start a new session (the admin handles it client-side as a new draft tab, like `/clear`) |
|
|
78
77
|
| `/session context` | server | Show context window usage, agent info |
|
|
79
78
|
| `/session compact` | server | LLM-summary compact of the conversation |
|
|
80
79
|
|
|
@@ -93,7 +92,7 @@ When `contextEnabled` is on (default), user messages are auto-prepended with:
|
|
|
93
92
|
- `[Currently viewing: path/to/file.ts]`
|
|
94
93
|
- `[Selected text in file.ts:10-25]\n\`\`\`...\n\`\`\``
|
|
95
94
|
|
|
96
|
-
### File attachments
|
|
95
|
+
### File attachments (images)
|
|
97
96
|
- Drag to chat input
|
|
98
97
|
- Clipboard paste
|
|
99
98
|
- File-picker button
|
|
@@ -101,7 +100,7 @@ When `contextEnabled` is on (default), user messages are auto-prepended with:
|
|
|
101
100
|
Images ride along as base64; multimodal supported. Pasted images are also persisted to `<workspace>/.halo/assets/web/inbound/web/<date>/` so a `[图片已保存: /abs/path]` marker survives page reload and renders as a click-to-preview chip (shared with the WeChat channel's inbound media flow).
|
|
102
101
|
|
|
103
102
|
### Inline media chips
|
|
104
|
-
Any message containing `[图片/视频/语音/文件 已保存: /path]` markers (WeChat + web) or a leading `MEDIA: /path` line (agent-emitted, e.g. from `wechat-send`) renders a compact chip with filename + icon. Clicking opens a full-size preview modal (image/video/audio inline, file → download link). The modal has a Download button (top-right, next to close) for image/video/audio; the media URL carries a per-open cache-buster (`&t=<timestamp>`) so overwritten files (same path, new bytes) always show current content. Paths inside the active workspace or under `/tmp/` are previewable; everything else degrades to a non-clickable chip.
|
|
103
|
+
Any message containing `[图片/视频/语音/文件 已保存: /path]` markers (WeChat + web) or a leading `MEDIA: /path` line (agent-emitted, e.g. from `wechat-send`) renders a compact chip with filename + icon. Clicking opens a full-size preview modal (image/video/audio inline, file → download link). The modal has a Download button (top-right, next to close) for image/video/audio; the media URL carries a per-open cache-buster (`&t=<timestamp>`) so overwritten files (same path, new bytes) always show current content. Paths inside the active workspace or under the OS temp dir (`/tmp/`) are previewable; everything else degrades to a non-clickable chip.
|
|
105
104
|
|
|
106
105
|
### Live capture (desktop only)
|
|
107
106
|
Lets the agent *see something live* on demand. Desktop client (Electron) only — the entry points never render in a plain browser. Borrows the meeting-app "share" model: the user binds one source, then the agent requests a frame when it actually needs to look.
|
|
@@ -115,7 +114,7 @@ Once bound, a frame is **not** attached to every message. Instead a one-line ins
|
|
|
115
114
|
Constraints:
|
|
116
115
|
- Shown only when the selected agent's model accepts image input (capture is pointless on a text-only model); switching to a text-only model auto-unbinds.
|
|
117
116
|
- Screen share needs macOS **Screen Recording** permission; the camera prompts for **Camera** permission on first use, with an "Open Settings" path if previously denied.
|
|
118
|
-
- Binding is **in-memory only** —
|
|
117
|
+
- Binding is **in-memory only** and shared by all chat tabs — a page reload or restart requires re-selecting.
|
|
119
118
|
|
|
120
119
|
### The agent's face (`self.html`)
|
|
121
120
|
A second channel beyond text: a visual space the agent drives in real time to express itself. Works everywhere (pure HTML/canvas, no Electron dependency) — desktop **and** plain browser.
|
|
@@ -123,7 +122,7 @@ A second channel beyond text: a visual space the agent drives in real time to ex
|
|
|
123
122
|
- **What it is.** A self-contained animated particle canvas at `<workspace>/.halo/canvas/self.html` — a breathing core that reacts to the cursor (knows when it's watched), can form words/CJK/ASCII-from-emoji, play choreographed sequences, and gesture (pulse/flash/shake). Zero external references (no CDN/remote fonts) — ships and runs offline.
|
|
124
123
|
- **Seeding.** Force-copied from `packages/server/templates/canvas/self.html` into every workspace on open (platform-owned, like built-in skills). The `self` built-in skill (wired into the default agent) teaches the agent it has this face and how to drive it.
|
|
125
124
|
- **Opening it.** The ✨ button in the chat-input toolbar opens the face in the editor preview (switches to Explorer, render-mode on → lands on the live face) and posts `self.intro()` once it has mounted, so there's always a greeting when a human turns to look. The greeting is driven solely by this open action — the page does **not** self-fire it on load — so it plays exactly once, on both first and subsequent opens. The user can also just open the file directly (no auto-greeting in that path).
|
|
126
|
-
- **Driving it.** The agent emits `<<<SHOW: …js… >>>` markers in a reply; Halo forwards the payload **verbatim** (it never parses it) to the open preview iframe via `postMessage`, where it's `eval`'d against the face's `self` API (`say`/`play`/`react`/`pulse`/`flash`/`shake`/`intro`) inside the sandboxed iframe. Markers are stripped from the rendered chat (like `<<<CAPTURE>>>`) — the user sees the face move, not the code. Multiple markers in one reply **queue and play in order**.
|
|
125
|
+
- **Driving it.** The agent emits `<<<SHOW: …js… >>>` markers in a reply; Halo forwards the payload **verbatim** (it never parses it) to the open preview iframe via `postMessage`, where it's `eval`'d against the face's `self` API (`say`/`play`/`react`/`pulse`/`flash`/`shake`/`intro`/`voice`) inside the sandboxed iframe. Markers are stripped from the rendered chat (like `<<<CAPTURE>>>`) — the user sees the face move, not the code. Multiple markers in one reply **queue and play in order**.
|
|
127
126
|
- **Engine vs. expression.** `self.html` is a stable *engine* (defines how the face can move); the agent expresses itself by sending runtime JS, **never** by editing the file — so the force-copy-on-open never clobbers anything meaningful. The engine only changes when the platform adds a new capability (template edit + `TEMPLATE_VERSION` bump).
|
|
128
127
|
- **No open preview = no-op.** `<<<SHOW>>>` only reaches a mounted preview; if the face isn't open the marker is silently dropped (the skill tells the agent to invite the user to open it rather than rely on a marker landing in the void).
|
|
129
128
|
- **Identity.** Deliberately nameless ("HELLO / A MIND / IS HERE / BEYOND WORDS") — the conversational identity is user-configurable and the model may not be Claude, so the face never hard-codes a name.
|