@turmind/halo 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/admin-out/404.html +1 -1
  2. package/admin-out/_next/static/chunks/133-e2df20fc6362a02b.js +1 -0
  3. package/admin-out/_next/static/chunks/200.dd28be896c7765de.js +1 -0
  4. package/admin-out/_next/static/chunks/442.14eec8e615cc727b.js +1 -0
  5. package/admin-out/_next/static/chunks/515.b793a7455c5e8342.js +1 -0
  6. package/admin-out/_next/static/chunks/525.fe35ab8501121729.js +1 -0
  7. package/admin-out/_next/static/chunks/579.3ab0b2bb9244fc37.js +1 -0
  8. package/admin-out/_next/static/chunks/65.3aac567c42cec99c.js +1 -0
  9. package/admin-out/_next/static/chunks/896.2069ab525ef81643.js +1 -0
  10. package/admin-out/_next/static/chunks/935.91ddb965b44041c9.js +1 -0
  11. package/admin-out/_next/static/chunks/app/{layout-c2fa8035998e48d5.js → layout-e75df4cae90cf325.js} +1 -1
  12. package/admin-out/_next/static/chunks/app/page-ca15438e8bddb403.js +1 -0
  13. package/admin-out/_next/static/chunks/webpack-f80c9b8cca4447a3.js +1 -0
  14. package/admin-out/_next/static/css/{0d0ca7b3c8cc5562.css → f96c61f28fab6941.css} +1 -1
  15. package/admin-out/index.html +1 -1
  16. package/admin-out/index.txt +5 -5
  17. package/bundled-docs/design/agentcore.md +1 -0
  18. package/bundled-docs/design/architecture.md +5 -2
  19. package/bundled-docs/design/background-dispatch.md +1 -1
  20. package/bundled-docs/design/cron.md +1 -1
  21. package/bundled-docs/design/feishu.md +2 -2
  22. package/bundled-docs/design/goal-mode.md +2 -0
  23. package/bundled-docs/design/halo-city.md +3 -1
  24. package/bundled-docs/design/prompt-system.md +12 -13
  25. package/bundled-docs/design/session.md +26 -5
  26. package/bundled-docs/design/storage.md +3 -2
  27. package/bundled-docs/design/web.md +12 -2
  28. package/bundled-docs/design/wechat.md +3 -2
  29. package/bundled-docs/design/ws.md +3 -3
  30. package/bundled-docs/dev/acp-adapter.md +5 -5
  31. package/bundled-docs/dev/api.md +5 -4
  32. package/bundled-docs/dev/deploy.md +2 -2
  33. package/bundled-docs/dev/desktop-packaging.md +8 -4
  34. package/bundled-docs/dev/env.md +3 -3
  35. package/bundled-docs/dev/tools.md +17 -7
  36. package/bundled-docs/guide/channels/acp.md +3 -3
  37. package/bundled-docs/guide/channels/wechat.md +1 -0
  38. package/bundled-docs/requirements/settings.md +3 -3
  39. package/dist/index.js +6647 -6405
  40. package/package.json +1 -1
  41. package/templates/agents/__evo_agent__/AGENT.md +13 -38
  42. package/templates/agents/goal/AGENT.md +29 -11
  43. package/templates/models/deepseek.yaml +6 -2
  44. package/templates/prompts/all/RUNTIME.md +16 -0
  45. package/templates/prompts/root/WORKSPACE_MEMORY.md +3 -0
  46. package/templates/skills/aws-knowledge/SKILL.md +3 -0
  47. package/templates/skills/cron/SKILL.md +14 -12
  48. package/templates/skills/halo/SKILL.md +1 -1
  49. package/templates/skills/web-search/SKILL.md +4 -1
  50. package/admin-out/_next/static/chunks/200.3b8f8a74f005a134.js +0 -1
  51. package/admin-out/_next/static/chunks/212-70bd2971392bde84.js +0 -1
  52. package/admin-out/_next/static/chunks/442.68ed26e27eb1d84f.js +0 -1
  53. package/admin-out/_next/static/chunks/515.4d98fae302278913.js +0 -1
  54. package/admin-out/_next/static/chunks/525.2c10a4fd329d0d24.js +0 -1
  55. package/admin-out/_next/static/chunks/579.a2718ddd395a2f3a.js +0 -1
  56. package/admin-out/_next/static/chunks/65.859665ba6717967a.js +0 -1
  57. package/admin-out/_next/static/chunks/896.ed21ec1e0846d20d.js +0 -1
  58. package/admin-out/_next/static/chunks/935.543aeec8456b6061.js +0 -1
  59. package/admin-out/_next/static/chunks/app/page-71c576815b4a30d9.js +0 -1
  60. package/admin-out/_next/static/chunks/webpack-fc1574554e7b1108.js +0 -1
  61. /package/admin-out/_next/static/{UspaVl1mRKHJayF8tgex9 → nVEORygQbBeOf2cFl82FW}/_buildManifest.js +0 -0
  62. /package/admin-out/_next/static/{UspaVl1mRKHJayF8tgex9 → nVEORygQbBeOf2cFl82FW}/_ssgManifest.js +0 -0
@@ -67,7 +67,7 @@ Your deployment scripts should also prepend this line.
67
67
 
68
68
  ## Runtime directories
69
69
 
70
- - SQLite: `<workspace>/.halo/halo.db` (per-workspace, auto-created on first use) + global queues `~/.halo/global/evo.db` and `~/.halo/global/cron.db`
70
+ - SQLite: `<workspace>/.halo/halo.db` (per-workspace, auto-created on first use) + global queues `~/.halo/global/evo.db`, `~/.halo/global/cron.db` and `~/.halo/global/runs.db`
71
71
  - Session files: `.halo/sessions/{agentId}/{sessionId}.json`
72
72
  - Global config: `~/.halo/global/`
73
73
  - Per-project config: `<workspace>/.halo/`
@@ -101,7 +101,7 @@ Source: `packages/server/src/config.ts`
101
101
  | `HALO_CORS_ORIGINS` | empty (reflect any origin) | `config.yaml server.cors_origins` | CORS allowlist (comma-separated). Empty = reflect any incoming Origin so credentials work cross-origin. Set explicit list to enforce strict CORS. |
102
102
  | `HALO_FRONTEND_DIR` | `packages/admin/out` | — | Static frontend dir (resolved as absolute path from project root) |
103
103
  | `HALO_MAX_CONTEXT_TOKENS` | `200000` | — | Model max context |
104
- | `HALO_SHELL_TIMEOUT` | `600000` | `config.yaml timeout.shell_exec` | Shell command timeout (ms) |
104
+ | `HALO_SHELL_TIMEOUT` | `120000` | `config.yaml timeout.shell_exec` | Shell command timeout (ms) |
105
105
  | `HALO_WEB_FETCH_TIMEOUT` | `10000` | `config.yaml timeout.web_fetch` | web_fetch timeout |
106
106
  | `HALO_MODEL_TIMEOUT` | `1800000` | `config.yaml timeout.model_request` | Per model-call wall-clock cap (ms, 30 min). A bare fetch has no default timeout, so a half-open connection would hang the request forever; on expiry the call aborts and the agent loop retries with backoff. |
107
107
  | `HALO_SESSION_GRACE` | `300000` | `config.yaml timeout.session_grace` | WS-disconnect session grace |
@@ -126,7 +126,7 @@ settings.yaml only (no env override):
126
126
  - `general.server.trust_proxy` (default `false`) — trust `x-forwarded-for` for client IP resolution, scope: global (enable only behind a reverse proxy you control)
127
127
  - `general.sandbox.hidden_dirs` (default `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh,~/.halo/global/internal-sessions,~/.halo/global/logs`) — bwrap tmpfs overlays, scope: global
128
128
  - `general.sandbox.writable_dirs` (default empty) — dirs bind-mounted read-write inside the bwrap sandbox, for external CLIs that keep local state (e.g. `~/.kiro`); not applied to readonly sessions, scope: global
129
- - `general.sandbox.hidden_files` (default `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc,~/.halo/global/{evo,cron}.db` + their `-wal`/`-shm` files) — bwrap /dev/null binds, scope: global
129
+ - `general.sandbox.hidden_files` (default `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc,~/.halo/global/{evo,cron,runs}.db` + their `-wal`/`-shm` files) — bwrap /dev/null binds, scope: global
130
130
  - `general.logging.level` (default `warn`) — log level: debug | info | warn | error
131
131
 
132
132
  `<provider-id>.secrets.*` (server-only, hard-rejected by `{{}}` substitution; declared in `models/<provider-id>.yaml` `secrets:`):
@@ -68,7 +68,7 @@ Run a shell command. Full shell access.
68
68
  |---|---|---|---|
69
69
  | command | string | yes | Shell command |
70
70
 
71
- Timeout 600 s (`HALO_SHELL_TIMEOUT`). Max output 5 MB. The tool description surfaces the effective timeout to the agent and advises backgrounding (`nohup … &` + log polling) for longer tasks.
71
+ Timeout 120 s (`HALO_SHELL_TIMEOUT`). Max output 5 MB. The tool description surfaces the effective timeout to the agent and advises backgrounding (`nohup … &` + log polling) for longer tasks.
72
72
 
73
73
  **Windows output encoding.** The Windows path (`sandbox.ts`) prepends `chcp 65001` so cmd built-ins (`echo`, …) emit UTF-8, then captures raw bytes (`encoding: 'buffer'`) and decodes them strict-UTF-8 with a GBK fallback. This is because native Win32 console tools (`ipconfig`, `systeminfo`, …) ignore `chcp` and still emit the OEM code page (GBK/CP936 on zh-CN); decoding such bytes as UTF-8 produced mojibake. The strict-UTF-8 attempt passes genuine UTF-8 through untouched and only falls back to GBK when the bytes aren't valid UTF-8 (GBK double-byte sequences almost always aren't). mac/Linux are unaffected (UTF-8 throughout).
74
74
 
@@ -83,7 +83,7 @@ Regex content search. Returns `file:line:content`.
83
83
  | include | string | no | Glob-like filename filter, e.g. `*.ts`, `*.{ts,tsx}`, or a comma-separated list `*.ts,*.tsx` |
84
84
  | max_results | number | no | Max matching lines (default 50) |
85
85
 
86
- Skips: `node_modules` / `.git` / `.next` / `dist` / `.halo` / binary files.
86
+ Skips: `node_modules` / `.git` / `.next` / `dist` / binary files. `.halo/` itself IS walked (it holds the agent's knowledge base — memory/, docs/, INSTRUCTIONS.md, skills/); only its machine-generated subtrees `sessions/ logs/ evo/ tmp/ assets/ canvas/` are skipped, and only when the direct parent is `.halo` — a project's own `logs/` or `tmp/` is still searched.
87
87
 
88
88
  ### glob
89
89
 
@@ -129,6 +129,16 @@ Enforcement layers:
129
129
 
130
130
  Dependency: `bubblewrap` (`apt install bubblewrap`) on Linux. Without it, only layers 2 + 3 are active.
131
131
 
132
+ **Windows has no sandbox.** There is no bwrap equivalent, so `sandbox.ts` (`normalizeOptsForPlatform`) promotes every non-full call to `full` before it reaches layer 1 or layer 3 — `assertPathAllowed()` returns immediately without checking the workspace boundary or the hidden-path lists. Only layer 2 (tool filtering) survives, because `isBwrapCached()` is always false there. Net effect on Windows:
133
+
134
+ | Level | Effective behavior on Windows |
135
+ |---|---|
136
+ | `full` | Unchanged |
137
+ | `workspace` | **Same as `full`** — all 9 tools, `shell_exec` runs unsandboxed, file tools can read/write any path the halo process can |
138
+ | `readonly` | 5 read-only tools (no write / shell / fetch), but **no path boundary** — `file_read` / `grep` / `glob` can reach anything on disk, including `~/.halo/secrets/` and `.halo/sessions/` |
139
+
140
+ This is a known, unfixed gap (not a bug in a specific route): don't hand out `workspace` / `readonly` channel tokens on a Windows-hosted server expecting isolation — treat them as `full` and `read-anything` respectively. Fixing it properly needs a Windows path-boundary check in `assertPathAllowed` and a Windows equivalent of the shell sandbox, neither of which exists yet.
141
+
132
142
  ### Sandbox hidden paths
133
143
 
134
144
  Sensitive directories and files are hidden from workspace/readonly sessions via bwrap overlays (and the same lists gate `assertPathAllowed` on the no-bwrap fallback). Paths that don't exist on the filesystem are silently skipped. Two categories coexist:
@@ -138,7 +148,7 @@ Sensitive directories and files are hidden from workspace/readonly sessions via
138
148
  | Setting | Default | Method |
139
149
  |---|---|---|
140
150
  | `hidden_dirs` | `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh,~/.halo/global/internal-sessions,~/.halo/global/logs` | `--tmpfs` overlay (empty directory) |
141
- | `hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc,~/.halo/global/{evo,cron}.db` + their `-wal`/`-shm` files | `--ro-bind /dev/null` (empty file) |
151
+ | `hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc,~/.halo/global/{evo,cron,runs}.db` + their `-wal`/`-shm` files | `--ro-bind /dev/null` (empty file) |
142
152
  | `writable_dirs` | (empty) | `--bind` read-write — for external CLIs that keep local state (e.g. `~/.kiro`); not applied to readonly sessions |
143
153
 
144
154
  Changes take effect immediately — `config.ts` reads settings.yaml via an mtime-watched lazy cache, so the next `shell_exec` reads the latest values. These keys are `globalOnly` in the schema — a workspace `settings.yaml` cannot override them, since they define the security boundary agents run inside.
@@ -163,7 +173,7 @@ Per-channel defaults:
163
173
  `grep` and `glob` read the first 512 bytes looking for a null byte and skip binaries.
164
174
 
165
175
  ### Tool result budget
166
- The orchestrator truncates tool results over 8000 chars and appends a `[Content truncated]` hint telling the agent to use `grep` for a targeted search.
176
+ The orchestrator truncates tool results over 8000 chars and appends a `[Content truncated]` hint telling the agent to use `grep` for a targeted search. `activate_skill` results are exempt — a SKILL.md body is instructions, not data, and the built-in acp / cron / self skills exceed 8K.
167
177
 
168
178
  ## Session tools
169
179
 
@@ -280,7 +290,7 @@ In-memory sessions return `session.output`; released sessions read the `output`
280
290
 
281
291
  ### query_agent
282
292
 
283
- Get an agent's full details: AGENT.md, model config, tools, skills. Use it before `start_session` to decide if an agent fits. Team-gated: an agent can only inspect agents on its own roster (the `team` whitelist — see [agent roster](../design/prompt-system.md#agent-roster)); querying a non-team agent is rejected.
293
+ Show an agent's name, description, model, tool list and skill descriptions — enough to decide whether it fits before `start_session`. Does not include AGENT.md (read `.halo/agents/<id>/AGENT.md` for behavior rules). Team-gated: an agent can only inspect agents on its own roster (the `team` whitelist — see [agent roster](../design/prompt-system.md#agent-roster)); querying a non-team agent is rejected.
284
294
 
285
295
  | Arg | Type | Required | Description |
286
296
  |---|---|---|---|
@@ -389,11 +399,11 @@ There is **no implicit default tool set**: `filterTools()` (in `agent-loader.ts`
389
399
 
390
400
  | Config key | Default | Purpose |
391
401
  |---|---|---|
392
- | `timeout.shellExec` | 600,000 ms | Shell command timeout |
402
+ | `timeout.shellExec` | 120,000 ms | Shell command timeout |
393
403
  | `timeout.webFetch` | 10,000 ms | HTTP timeout |
394
404
  | `limits.shellOutputBuffer` | 5 MB | Shell output buffer |
395
405
  | `limits.webFetchMaxBody` | 50 KB | web_fetch body cap |
396
406
  | `limits.grepDefaultMax` | 50 | Default grep result cap |
397
- | `limits.toolResultMax` | 8,000 chars | Tool result truncation threshold |
407
+ | `limits.toolResultMax` | 8,000 chars | Tool result truncation threshold (`activate_skill` exempt) |
398
408
 
399
409
  Defined in `packages/server/src/config.ts`.
@@ -53,7 +53,7 @@ halo acp \
53
53
  | `--port` | yes | Halo server port |
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
- | `--agent-id` | no | Halo agent profile to use when ACP `session/new` creates a fresh session. Default `default` |
56
+ | `--agent-id` | no | Halo agent profile to use when ACP `session/new` creates a fresh session. Default `default` (alias `--agent`) |
57
57
 
58
58
  Each adapter process binds to **one** workspace. To drive multiple workspaces concurrently from one token, run multiple adapter processes — one per workspace.
59
59
 
@@ -98,7 +98,7 @@ The question is passed verbatim — including the other agent's own slash comman
98
98
  |---|---|---|
99
99
  | `initialize` | ✅ | Declares `protocolVersion: 1`, `loadSession: true`, no auth methods |
100
100
  | `authenticate` | ✅ (no-op) | Token is passed via launch flags; ACP-side auth has nothing to do |
101
- | `session/new` | ✅ | Mints a sessionId of shape `web_acp_<ts>_<rand>`. Halo creates the row lazily on first `/web/chat` |
101
+ | `session/new` | ✅ | Calls `POST /api/web/sessions`; halo mints `web_<accountId>_<ts>_<rand>` and creates the row immediately |
102
102
  | `session/load` | ✅ | Verifies the supplied id still exists on the halo server, then registers it locally |
103
103
  | `session/prompt` | ✅ | Forwards text + image content blocks. Resource / embedded-context blocks log a stderr warning and are dropped (see "Reverse fs" below) |
104
104
  | `session/cancel` | ✅ | Aborts the in-flight HTTP/SSE stream and POSTs `/web/stop` |
@@ -108,7 +108,7 @@ The question is passed verbatim — including the other agent's own slash comman
108
108
 
109
109
  ### Session id model
110
110
 
111
- ACP `sessionId` **is** the halo session id — there's no extra mapping layer. When `session/new` mints `web_acp_<ts>_<rand>`, that exact string is what gets created in `agent_sessions` (lazily on first `/web/chat`). The ACP client persists ids itself; the adapter holds no on-disk state. Losing the adapter's in-memory map on restart is harmless because the conversation lives on the halo server.
111
+ ACP `sessionId` **is** the halo session id — there's no extra mapping layer. `session/new` asks the server to mint one (`POST /api/web/sessions`); it lands in the token's own `web_<accountId>_` namespace and the `agent_sessions` row exists immediately, so readonly / workspace tokens can drive it too. The ACP client persists ids itself; the adapter holds no on-disk state. Losing the adapter's in-memory map on restart is harmless because the conversation lives on the halo server.
112
112
 
113
113
  The ACP client is the source of truth for "which sessions are mine" — a Mac-side Claude Code knows about *its* sessions, the EC2-side halo agent doesn't need to enumerate them.
114
114
 
@@ -97,6 +97,7 @@ Fan-out across multiple WeChat users is not supported — one bot, one owner.
97
97
  | 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.pid` |
98
98
  | Group chat doesn't trigger the bot | Expected — groups aren't supported in v1 |
99
99
  | Sent voice message returns no reply | Check that the account has `workspace` or `full` access; readonly cannot save inbound media |
100
+ | Cron push fails with `ret=-2 prepare failed` while chat replies work | Two causes: the report exceeds 16 KB (halo chunks at 3500 chars, so this is rare now), or 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. Send the bot any message once and the next cron push carries it |
100
101
 
101
102
  ## Multi-workspace / multi-bot
102
103
 
@@ -18,7 +18,7 @@ general: # built-in declarer (server itself)
18
18
  ...
19
19
  sandbox:
20
20
  hidden_dirs: "~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh"
21
- hidden_files: "~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc"
21
+ hidden_files: "~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc,~/.halo/global/runs.db"
22
22
  writable_dirs: "" # e.g. ~/.kiro,~/.local/share/kiro-cli — rw bind-mounts in bwrap; ignored for readonly sessions
23
23
  logging:
24
24
  level: warn
@@ -93,7 +93,7 @@ secrets: []
93
93
 
94
94
  ### General — built-in
95
95
 
96
- Declared in [packages/server/src/settings-schema.ts](../../../packages/server/src/settings-schema.ts) `generalSection()`. The server itself is the implicit declarer. Keys: `language`, `agent.*`, `server.*`, `session.*`, `compact.*`, `sandbox.*`, `logging.*`, `evolution.*`, `limits.*`.
96
+ Declared in [packages/server/src/settings-schema.ts](../../../packages/server/src/settings-schema.ts) `generalSection()`. The server itself is the implicit declarer. Keys: `language`, `agent.*`, `server.*`, `session.*`, `compact.*`, `sandbox.*`, `logging.*`, `evolution.*`, `limits.*`. All `general.*` keys are `globalOnly`: `config.ts` resolves them through `settingsValue()` against `~/.halo/secrets/settings.yaml` only, so a workspace `settings.yaml` cannot override them. Per-workspace layering applies to namespaced `params` / `secrets` (`getServerSecret(ns, key, workspaceRoot)`, `substituteSecrets`).
97
97
 
98
98
  `server.trust_proxy` (boolean, default `false`, `globalOnly`): whether the brute-force rate limiter (`middleware/brute-force.ts` `getClientIp`) trusts the `x-forwarded-for` header for client IP resolution. Direct-connect deployments leave it `false` and get the socket address. Behind a reverse proxy (nginx / Cloudflare / etc.), set it to `true` so the real client IP is honored instead of the proxy's — but only when that proxy is one you control and rewrites the header itself, otherwise a client can forge XFF to dodge lockouts.
99
99
 
@@ -121,7 +121,7 @@ Declared in [packages/server/src/settings-schema.ts](../../../packages/server/sr
121
121
  | Global | `~/.halo/secrets/settings.yaml` | Base |
122
122
  | Workspace | `<project>/.halo/settings.yaml` | Overrides global, key by key |
123
123
 
124
- Read order: `<schema default> <- <global> <- <workspace>`.
124
+ Read order: `<schema default> <- <global> <- <workspace>` (workspace layer applies to namespaced `params` / `secrets` only — see General above).
125
125
 
126
126
  The Settings page shows source badges per field:
127
127
  - `workspace` (green-blue, override applied here)