@turmind/halo 0.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.
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/admin-out/404.html +1 -0
- package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_buildManifest.js +1 -0
- package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_ssgManifest.js +1 -0
- package/admin-out/_next/static/chunks/1d2d5650-810a3ca1ae20cab5.js +18 -0
- package/admin-out/_next/static/chunks/200.3b8f8a74f005a134.js +1 -0
- package/admin-out/_next/static/chunks/220.3dc41cd6269b88e6.js +1 -0
- package/admin-out/_next/static/chunks/298-d6b224b569da4de0.js +1 -0
- package/admin-out/_next/static/chunks/304eda7d.b1ee7f523653e027.js +1 -0
- package/admin-out/_next/static/chunks/330.3b784cf0dcd69163.js +1 -0
- package/admin-out/_next/static/chunks/442.f4302087bddb1ec1.js +1 -0
- package/admin-out/_next/static/chunks/443.d1b289781793f8de.js +25 -0
- package/admin-out/_next/static/chunks/503-7c6d9935df6e0cea.js +1 -0
- package/admin-out/_next/static/chunks/533.5f2d4bfe3d076095.js +1 -0
- package/admin-out/_next/static/chunks/579.a2718ddd395a2f3a.js +1 -0
- package/admin-out/_next/static/chunks/635-3dd0043ed0520b9d.js +1 -0
- package/admin-out/_next/static/chunks/65.859665ba6717967a.js +1 -0
- package/admin-out/_next/static/chunks/661.a4fe93f3f2a5e871.js +1 -0
- package/admin-out/_next/static/chunks/688.4e014ac6cfc7e152.js +1 -0
- package/admin-out/_next/static/chunks/868.0c95dfe283400a00.js +1 -0
- package/admin-out/_next/static/chunks/app/_not-found/page-8e35399c8fd5016d.js +1 -0
- package/admin-out/_next/static/chunks/app/layout-58f773336e6fce61.js +1 -0
- package/admin-out/_next/static/chunks/app/page-cc8043335282d745.js +1 -0
- package/admin-out/_next/static/chunks/db534642.09d3c650ff587175.js +1 -0
- package/admin-out/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js +1 -0
- package/admin-out/_next/static/chunks/framework-c3031ae54a63fe16.js +1 -0
- package/admin-out/_next/static/chunks/main-14b48e3c2588354f.js +1 -0
- package/admin-out/_next/static/chunks/main-app-483b436873410ddc.js +1 -0
- package/admin-out/_next/static/chunks/pages/_app-b2fc6e5f920d1b1a.js +1 -0
- package/admin-out/_next/static/chunks/pages/_error-f3696a8f3d67d150.js +1 -0
- package/admin-out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/admin-out/_next/static/chunks/webpack-8bd7ef88a6527feb.js +1 -0
- package/admin-out/_next/static/css/659eccb5db697b76.css +1 -0
- package/admin-out/_next/static/css/8fce4e1a54dac12a.css +3 -0
- package/admin-out/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- package/admin-out/_next/static/media/21350d82a1f187e9-s.woff2 +0 -0
- package/admin-out/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- package/admin-out/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- package/admin-out/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- package/admin-out/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- package/admin-out/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- package/admin-out/favicon.ico +0 -0
- package/admin-out/icon.svg +18 -0
- package/admin-out/index.html +1 -0
- package/admin-out/index.txt +23 -0
- package/admin-out/monaco/vs/_commonjsHelpers-CT9FvmAN.js +1 -0
- package/admin-out/monaco/vs/abap-D-t0cyap.js +1 -0
- package/admin-out/monaco/vs/apex-CcIm7xu6.js +1 -0
- package/admin-out/monaco/vs/assets/css.worker-HnVq6Ewq.js +93 -0
- package/admin-out/monaco/vs/assets/editor.worker-Be8ye1pW.js +26 -0
- package/admin-out/monaco/vs/assets/html.worker-B51mlPHg.js +470 -0
- package/admin-out/monaco/vs/assets/json.worker-DKiEKt88.js +58 -0
- package/admin-out/monaco/vs/assets/ts.worker-CMbG-7ft.js +67731 -0
- package/admin-out/monaco/vs/azcli-BA0tQDCg.js +1 -0
- package/admin-out/monaco/vs/basic-languages/monaco.contribution.js +1 -0
- package/admin-out/monaco/vs/bat-C397hTD6.js +1 -0
- package/admin-out/monaco/vs/bicep-DF5aW17k.js +2 -0
- package/admin-out/monaco/vs/cameligo-plsz8qhj.js +1 -0
- package/admin-out/monaco/vs/clojure-Y2auQMzK.js +1 -0
- package/admin-out/monaco/vs/coffee-Bu45yuWE.js +1 -0
- package/admin-out/monaco/vs/cpp-CkKPQIni.js +1 -0
- package/admin-out/monaco/vs/csharp-CX28MZyh.js +1 -0
- package/admin-out/monaco/vs/csp-D8uWnyxW.js +1 -0
- package/admin-out/monaco/vs/css-CaeNmE3S.js +3 -0
- package/admin-out/monaco/vs/cssMode-CjiAH6dQ.js +1 -0
- package/admin-out/monaco/vs/cypher-DVThT8BS.js +1 -0
- package/admin-out/monaco/vs/dart-CmGfCvrO.js +1 -0
- package/admin-out/monaco/vs/dockerfile-CZqqYdch.js +1 -0
- package/admin-out/monaco/vs/ecl-30fUercY.js +1 -0
- package/admin-out/monaco/vs/editor/editor.main.css +1 -0
- package/admin-out/monaco/vs/editor/editor.main.js +5 -0
- package/admin-out/monaco/vs/editor.api-CalNCsUg.js +903 -0
- package/admin-out/monaco/vs/elixir-xjPaIfzF.js +1 -0
- package/admin-out/monaco/vs/flow9-DqtmStfK.js +1 -0
- package/admin-out/monaco/vs/freemarker2-Cz_sV6Md.js +3 -0
- package/admin-out/monaco/vs/fsharp-BOMdg4U1.js +1 -0
- package/admin-out/monaco/vs/go-D_hbi-Jt.js +1 -0
- package/admin-out/monaco/vs/graphql-CKUU4kLG.js +1 -0
- package/admin-out/monaco/vs/handlebars-OwglfO-1.js +1 -0
- package/admin-out/monaco/vs/hcl-DTaboeZW.js +1 -0
- package/admin-out/monaco/vs/html-Pa1xEWsY.js +1 -0
- package/admin-out/monaco/vs/htmlMode-Bz67EXwp.js +1 -0
- package/admin-out/monaco/vs/ini-CsNwO04R.js +1 -0
- package/admin-out/monaco/vs/java-CI4ZMsH9.js +1 -0
- package/admin-out/monaco/vs/javascript-PczUCGdz.js +1 -0
- package/admin-out/monaco/vs/jsonMode-DULH5oaX.js +7 -0
- package/admin-out/monaco/vs/julia-BwzEvaQw.js +1 -0
- package/admin-out/monaco/vs/kotlin-IUYPiTV8.js +1 -0
- package/admin-out/monaco/vs/language/css/monaco.contribution.js +1 -0
- package/admin-out/monaco/vs/language/html/monaco.contribution.js +1 -0
- package/admin-out/monaco/vs/language/json/monaco.contribution.js +1 -0
- package/admin-out/monaco/vs/language/typescript/monaco.contribution.js +1 -0
- package/admin-out/monaco/vs/less-C0eDYdqa.js +2 -0
- package/admin-out/monaco/vs/lexon-iON-Kj97.js +1 -0
- package/admin-out/monaco/vs/liquid-DqKjdPGy.js +1 -0
- package/admin-out/monaco/vs/loader.js +1368 -0
- package/admin-out/monaco/vs/lspLanguageFeatures-kM9O9rjY.js +4 -0
- package/admin-out/monaco/vs/lua-DtygF91M.js +1 -0
- package/admin-out/monaco/vs/m3-CsR4AuFi.js +1 -0
- package/admin-out/monaco/vs/markdown-C_rD0bIw.js +1 -0
- package/admin-out/monaco/vs/mdx-DEWtB1K5.js +1 -0
- package/admin-out/monaco/vs/mips-CiYP61RB.js +1 -0
- package/admin-out/monaco/vs/monaco.contribution-D2OdxNBt.js +1 -0
- package/admin-out/monaco/vs/monaco.contribution-DO3azKX8.js +1 -0
- package/admin-out/monaco/vs/monaco.contribution-EcChJV6a.js +1 -0
- package/admin-out/monaco/vs/monaco.contribution-qLAYrEOP.js +1 -0
- package/admin-out/monaco/vs/msdax-C38-sJlp.js +1 -0
- package/admin-out/monaco/vs/mysql-CdtbpvbG.js +1 -0
- package/admin-out/monaco/vs/nls.messages-loader.js +1 -0
- package/admin-out/monaco/vs/nls.messages.cs.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.de.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.es.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.fr.js.js +15 -0
- package/admin-out/monaco/vs/nls.messages.it.js.js +15 -0
- package/admin-out/monaco/vs/nls.messages.ja.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.js.js +10 -0
- package/admin-out/monaco/vs/nls.messages.ko.js.js +25 -0
- package/admin-out/monaco/vs/nls.messages.pl.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.pt-br.js.js +6 -0
- package/admin-out/monaco/vs/nls.messages.ru.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.tr.js.js +15 -0
- package/admin-out/monaco/vs/nls.messages.zh-cn.js.js +17 -0
- package/admin-out/monaco/vs/nls.messages.zh-tw.js.js +15 -0
- package/admin-out/monaco/vs/objective-c-CntZFaHX.js +1 -0
- package/admin-out/monaco/vs/pascal-r6kuqfl_.js +1 -0
- package/admin-out/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
- package/admin-out/monaco/vs/perl-DABw_TcH.js +1 -0
- package/admin-out/monaco/vs/pgsql-me_jFXeX.js +1 -0
- package/admin-out/monaco/vs/php-D_kh-9LK.js +1 -0
- package/admin-out/monaco/vs/pla-VfZjczW0.js +1 -0
- package/admin-out/monaco/vs/postiats-BBSzz8Pk.js +1 -0
- package/admin-out/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
- package/admin-out/monaco/vs/powershell-B-7ap1zc.js +1 -0
- package/admin-out/monaco/vs/protobuf-BmtuEB1A.js +2 -0
- package/admin-out/monaco/vs/pug-BRpRNeEb.js +1 -0
- package/admin-out/monaco/vs/python-Cr0UkIbn.js +1 -0
- package/admin-out/monaco/vs/qsharp-BzsFaUU9.js +1 -0
- package/admin-out/monaco/vs/r-f8dDdrp4.js +1 -0
- package/admin-out/monaco/vs/razor-BYAHOTkz.js +1 -0
- package/admin-out/monaco/vs/redis-fvZQY4PI.js +1 -0
- package/admin-out/monaco/vs/redshift-45Et0LQi.js +1 -0
- package/admin-out/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
- package/admin-out/monaco/vs/ruby-CZO8zYTz.js +1 -0
- package/admin-out/monaco/vs/rust-Bfetafyc.js +1 -0
- package/admin-out/monaco/vs/sb-3GYllVck.js +1 -0
- package/admin-out/monaco/vs/scala-foMgrKo1.js +1 -0
- package/admin-out/monaco/vs/scheme-CHdMtr7p.js +1 -0
- package/admin-out/monaco/vs/scss-C1cmLt9V.js +3 -0
- package/admin-out/monaco/vs/shell-ClXCKCEW.js +1 -0
- package/admin-out/monaco/vs/solidity-MZ6ExpPy.js +1 -0
- package/admin-out/monaco/vs/sophia-DWkuSsPQ.js +1 -0
- package/admin-out/monaco/vs/sparql-AUGFYSyk.js +1 -0
- package/admin-out/monaco/vs/sql-32GpJSV2.js +1 -0
- package/admin-out/monaco/vs/st-CuDFIVZ_.js +1 -0
- package/admin-out/monaco/vs/swift-n-2HociN.js +3 -0
- package/admin-out/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
- package/admin-out/monaco/vs/tcl-D74tq1nH.js +1 -0
- package/admin-out/monaco/vs/tsMode-CZz1Umrk.js +11 -0
- package/admin-out/monaco/vs/twig-C6taOxMV.js +1 -0
- package/admin-out/monaco/vs/typescript-DfOrAzoV.js +1 -0
- package/admin-out/monaco/vs/typespec-D-PIh9Xw.js +1 -0
- package/admin-out/monaco/vs/vb-Dyb2648j.js +1 -0
- package/admin-out/monaco/vs/wgsl-BhLXMOR0.js +298 -0
- package/admin-out/monaco/vs/workers-DcJshg-q.js +1 -0
- package/admin-out/monaco/vs/xml-CdsdnY8S.js +1 -0
- package/admin-out/monaco/vs/yaml-DYGvmE88.js +1 -0
- package/bin/halo.js +9 -0
- package/bundled-docs/design/agent.md +145 -0
- package/bundled-docs/design/architecture.md +270 -0
- package/bundled-docs/design/background-dispatch.md +151 -0
- package/bundled-docs/design/command.md +58 -0
- package/bundled-docs/design/cron.md +88 -0
- package/bundled-docs/design/evolution.md +383 -0
- package/bundled-docs/design/express-self.md +145 -0
- package/bundled-docs/design/feishu.md +224 -0
- package/bundled-docs/design/halo-city.md +112 -0
- package/bundled-docs/design/prompt-system.md +411 -0
- package/bundled-docs/design/session.md +321 -0
- package/bundled-docs/design/slack.md +212 -0
- package/bundled-docs/design/storage.md +578 -0
- package/bundled-docs/design/telegram.md +161 -0
- package/bundled-docs/design/web.md +222 -0
- package/bundled-docs/design/wechat.md +146 -0
- package/bundled-docs/design/ws.md +144 -0
- package/bundled-docs/dev/acp-adapter.md +337 -0
- package/bundled-docs/dev/add-channel.md +376 -0
- package/bundled-docs/dev/add-model-provider.md +386 -0
- package/bundled-docs/dev/api.md +686 -0
- package/bundled-docs/dev/deploy.md +198 -0
- package/bundled-docs/dev/desktop-packaging.md +327 -0
- package/bundled-docs/dev/env.md +158 -0
- package/bundled-docs/dev/logging.md +85 -0
- package/bundled-docs/dev/previews.md +225 -0
- package/bundled-docs/dev/tools.md +331 -0
- package/bundled-docs/guide/README.md +29 -0
- package/bundled-docs/guide/agents.md +264 -0
- package/bundled-docs/guide/channels/README.md +62 -0
- package/bundled-docs/guide/channels/acp.md +150 -0
- package/bundled-docs/guide/channels/feishu.md +154 -0
- package/bundled-docs/guide/channels/slack.md +156 -0
- package/bundled-docs/guide/channels/telegram.md +112 -0
- package/bundled-docs/guide/channels/web.md +149 -0
- package/bundled-docs/guide/channels/wechat.md +110 -0
- package/bundled-docs/guide/chat.md +112 -0
- package/bundled-docs/guide/cli.md +197 -0
- package/bundled-docs/guide/getting-started.md +93 -0
- package/bundled-docs/guide/secrets-and-credentials.md +161 -0
- package/bundled-docs/guide/sessions.md +104 -0
- package/bundled-docs/guide/skills.md +302 -0
- package/bundled-docs/guide/testing-agents-and-skills.md +136 -0
- package/bundled-docs/guide/workspace.md +131 -0
- package/bundled-docs/plans/cron-tasks.md +362 -0
- package/bundled-docs/plans/design-suggestions.md +203 -0
- package/bundled-docs/plans/issue-session-deep-nesting.md +208 -0
- package/bundled-docs/plans/launch-plan.md +111 -0
- package/bundled-docs/plans/md-and-agent-refactor.md +197 -0
- package/bundled-docs/plans/remote-mac-worker.md +196 -0
- package/bundled-docs/plans/roadmap.md +40 -0
- package/bundled-docs/plans/self-evolution.md +1379 -0
- package/bundled-docs/plans/todo.md +32 -0
- package/bundled-docs/plans/weixin-channel.md +255 -0
- package/bundled-docs/requirements/agent.md +92 -0
- package/bundled-docs/requirements/chat.md +81 -0
- package/bundled-docs/requirements/command.md +130 -0
- package/bundled-docs/requirements/editor.md +82 -0
- package/bundled-docs/requirements/explorer.md +89 -0
- package/bundled-docs/requirements/overview.md +62 -0
- package/bundled-docs/requirements/session.md +73 -0
- package/bundled-docs/requirements/settings.md +224 -0
- package/bundled-docs/requirements/skill.md +125 -0
- package/bundled-docs/requirements/source-control.md +82 -0
- package/bundled-docs/requirements/terminal.md +118 -0
- package/bundled-docs/requirements/workspace.md +80 -0
- package/bundled-docs/test/docs-seeding.md +101 -0
- package/bundled-docs/test/session.md +374 -0
- package/dist/index.js +26750 -0
- package/package.json +67 -0
- package/templates/INSTRUCTIONS.md +25 -0
- package/templates/agents/__apply_agent__/AGENT.md +229 -0
- package/templates/agents/__apply_agent__/agent.yaml +30 -0
- package/templates/agents/__evo_agent__/AGENT.md +323 -0
- package/templates/agents/__evo_agent__/agent.yaml +29 -0
- package/templates/agents/__score__/AGENT.md +177 -0
- package/templates/agents/__score__/agent.yaml +29 -0
- package/templates/agents/deep-executor/AGENT.md +60 -0
- package/templates/agents/deep-executor/agent.yaml +25 -0
- package/templates/agents/default/AGENT.md +38 -0
- package/templates/agents/default/agent.yaml +51 -0
- package/templates/agents/executor/AGENT.md +38 -0
- package/templates/agents/executor/agent.yaml +25 -0
- package/templates/aliases.yaml +26 -0
- package/templates/canvas/self.html +660 -0
- package/templates/config.yaml +69 -0
- package/templates/models/anthropic.yaml +47 -0
- package/templates/models/aws-bedrock-claude-invoke.yaml +156 -0
- package/templates/models/aws-bedrock-mantle.yaml +89 -0
- package/templates/models/deepseek.yaml +62 -0
- package/templates/models/doubao.yaml +137 -0
- package/templates/models/hunyuan.yaml +56 -0
- package/templates/models/kimi.yaml +41 -0
- package/templates/models/mimo-token-plan-china.yaml +61 -0
- package/templates/models/minimax.yaml +80 -0
- package/templates/models/openai.yaml +51 -0
- package/templates/models/qwen.yaml +78 -0
- package/templates/optional-skills/tavily-web-search/SKILL.md +91 -0
- package/templates/optional-skills/tavily-web-search/config.yaml +10 -0
- package/templates/prompts/all/TOOL_GUIDELINES.md +35 -0
- package/templates/prompts/all/TOOL_SHELL.md +4 -0
- package/templates/prompts/all/TOOL_SHELL.windows.md +7 -0
- package/templates/prompts/bootstrap/BOOTSTRAP.md +26 -0
- package/templates/prompts/root/WORKSPACE_MEMORY.md +11 -0
- package/templates/schema.sql +30 -0
- package/templates/skills/acp/SKILL.md +236 -0
- package/templates/skills/acp/templates/SKILL.md.tmpl +74 -0
- package/templates/skills/acp/templates/ask.py +176 -0
- package/templates/skills/acp/templates/config.yaml.tmpl +33 -0
- package/templates/skills/agent/SKILL.md +128 -0
- package/templates/skills/aws-knowledge/SKILL.md +82 -0
- package/templates/skills/aws-knowledge/scripts/aws_kb.py +61 -0
- package/templates/skills/cron/SKILL.md +217 -0
- package/templates/skills/cron/manage-cron.py +434 -0
- package/templates/skills/halo/SKILL.md +98 -0
- package/templates/skills/nova-web-search/SKILL.md +65 -0
- package/templates/skills/self/SKILL.md +139 -0
- package/templates/skills/send-file/SKILL.md +28 -0
- package/templates/skills/send-file/feishu.md +16 -0
- package/templates/skills/send-file/slack.md +7 -0
- package/templates/skills/send-file/telegram.md +9 -0
- package/templates/skills/send-file/web.md +6 -0
- package/templates/skills/send-file/wechat.md +8 -0
- package/templates/skills/skill/SKILL.md +131 -0
- package/templates/skills/workspace/SKILL.md +28 -0
- package/templates/skills/workspace/setup-tidy.md +265 -0
- package/templates/skills/workspace/share.md +187 -0
- package/templates/skills/workspace/stage.py +487 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Agent Tool Reference
|
|
2
|
+
|
|
3
|
+
Agents have two tool categories: workspace tools (files, shell, search) and session tools (managing other sessions). Workspace tools are enabled by name in `agent.yaml`'s `tools` list; the session-tool bundle is granted automatically by a non-empty `team` (see [Session tools](#session-tools)).
|
|
4
|
+
|
|
5
|
+
## Workspace tools
|
|
6
|
+
|
|
7
|
+
File: `packages/server/src/tools/workspace-tools.ts`
|
|
8
|
+
|
|
9
|
+
> **Path resolution**: `file_read` / `file_write` / `file_edit` / `file_list`'s `path` argument resolves as: relative → workspace root; absolute → as-is; `~/` → home. The actual reachable set depends on the session's access level (see "Access level" below).
|
|
10
|
+
|
|
11
|
+
### file_read
|
|
12
|
+
|
|
13
|
+
Read file content.
|
|
14
|
+
|
|
15
|
+
| Arg | Type | Required | Description |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| path | string | yes | File path (relative / absolute / `~/`) |
|
|
18
|
+
| offset | integer | no | 1-based line to start at (default 1) |
|
|
19
|
+
| limit | integer | no | Number of lines to return (default 2000) |
|
|
20
|
+
|
|
21
|
+
Returns: string (`cat -n` format, 1-based line-number prefix per line). Files over 2 MB read without an explicit `offset`/`limit` range are **rejected** — grep to locate the section first, or page through with `offset`+`limit`.
|
|
22
|
+
|
|
23
|
+
### file_write
|
|
24
|
+
|
|
25
|
+
Write a file (creates parent dirs on demand).
|
|
26
|
+
|
|
27
|
+
| Arg | Type | Required | Description |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| path | string | yes | File path |
|
|
30
|
+
| content | string | yes | Content |
|
|
31
|
+
|
|
32
|
+
### file_edit
|
|
33
|
+
|
|
34
|
+
Replace a string in a file (exact match).
|
|
35
|
+
|
|
36
|
+
| Arg | Type | Required | Description |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| path | string | yes | File path |
|
|
39
|
+
| old_string | string | yes | The exact text to find |
|
|
40
|
+
| new_string | string | yes | The replacement |
|
|
41
|
+
|
|
42
|
+
### view_image
|
|
43
|
+
|
|
44
|
+
Read an image file and return it as a vision content block. Supports png/jpg/jpeg/gif/webp, max 5 MB.
|
|
45
|
+
|
|
46
|
+
| Arg | Type | Required | Description |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| path | string | yes | Image file path |
|
|
49
|
+
|
|
50
|
+
Returns: image content block (base64-encoded) for multimodal processing.
|
|
51
|
+
|
|
52
|
+
**Vision gating**: this tool is only injected into the agent's tool list when the underlying model declares `capabilities.image: true` in its provider manifest. For text-only models (DeepSeek and others), `view_image` is silently dropped at `createWorkspaceTools()` time so the model never sees it — calling it would otherwise produce a 400 from the provider.
|
|
53
|
+
|
|
54
|
+
### file_list
|
|
55
|
+
|
|
56
|
+
List directory entries. Output uses emoji prefixes (`📁` / `📄`). Skips `node_modules` and `.git`.
|
|
57
|
+
|
|
58
|
+
| Arg | Type | Required | Description |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| path | string | no | Directory path (default: workspace root) |
|
|
61
|
+
| recursive | boolean | no | Walk the whole subtree (default: false). DFS so entries stay grouped by parent. Capped at 500 entries — past that, a `[truncated]` line is appended and the agent is steered to `glob` instead. |
|
|
62
|
+
|
|
63
|
+
### shell_exec
|
|
64
|
+
|
|
65
|
+
Run a shell command. Full shell access.
|
|
66
|
+
|
|
67
|
+
| Arg | Type | Required | Description |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| command | string | yes | Shell command |
|
|
70
|
+
|
|
71
|
+
Timeout 120 s (`HALO_SHELL_TIMEOUT`). Max output 5 MB.
|
|
72
|
+
|
|
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
|
+
|
|
75
|
+
### grep
|
|
76
|
+
|
|
77
|
+
Regex content search. Returns `file:line:content`.
|
|
78
|
+
|
|
79
|
+
| Arg | Type | Required | Description |
|
|
80
|
+
|---|---|---|---|
|
|
81
|
+
| pattern | string | yes | Regex |
|
|
82
|
+
| path | string | no | Search dir or a single file (default: workspace root) |
|
|
83
|
+
| include | string | no | Glob-like filename filter, e.g. `*.ts`, `*.{ts,tsx}`, or a comma-separated list `*.ts,*.tsx` |
|
|
84
|
+
| max_results | number | no | Max matching lines (default 50) |
|
|
85
|
+
|
|
86
|
+
Skips: `node_modules` / `.git` / `.next` / `dist` / `.halo` / binary files.
|
|
87
|
+
|
|
88
|
+
### glob
|
|
89
|
+
|
|
90
|
+
Find files by glob.
|
|
91
|
+
|
|
92
|
+
| Arg | Type | Required | Description |
|
|
93
|
+
|---|---|---|---|
|
|
94
|
+
| pattern | string | yes | Glob, e.g. `**/*.ts`, `src/**/*.tsx` |
|
|
95
|
+
| path | string | no | Starting dir (default: workspace root) |
|
|
96
|
+
|
|
97
|
+
Returns paths relative to workspace root, alphabetically. Same skip list as grep.
|
|
98
|
+
|
|
99
|
+
### web_fetch
|
|
100
|
+
|
|
101
|
+
HTTP request.
|
|
102
|
+
|
|
103
|
+
| Arg | Type | Required | Description |
|
|
104
|
+
|---|---|---|---|
|
|
105
|
+
| url | string | yes | URL |
|
|
106
|
+
| method | string | no | HTTP method (default GET) |
|
|
107
|
+
| headers | object | no | Optional request headers |
|
|
108
|
+
|
|
109
|
+
Timeout 10 s. Max body 50 KB (truncated if larger). Returns status + content-type + response body.
|
|
110
|
+
|
|
111
|
+
## Security
|
|
112
|
+
|
|
113
|
+
### Access level (per-session, dynamic)
|
|
114
|
+
|
|
115
|
+
Each session carries `accessLevel: 'readonly' | 'workspace' | null` (persisted in `agent_sessions.access_level`). Sub-sessions inherit their parent's access level. Access level is re-evaluated on every user message — if the channel account's access level has changed, the agent instance is rebuilt with the new tool set and sandbox config.
|
|
116
|
+
|
|
117
|
+
| Level | DB value | Tools (with bwrap) | Tools (without bwrap) | Sandbox |
|
|
118
|
+
|---|---|---|---|---|
|
|
119
|
+
| `null` (full) | `full` | All 9 tools | All 9 tools | None |
|
|
120
|
+
| `workspace` | `workspace` | All 9 tools | All 9 tools | bwrap: workspace rw, sensitive paths hidden |
|
|
121
|
+
| `readonly` | `readonly` | All 9 tools | file_read, view_image, file_list, grep, glob (5 tools) | bwrap: workspace ro, sensitive paths hidden |
|
|
122
|
+
|
|
123
|
+
`view_image` is additionally gated on `capabilities.image` (see its section above). Models without vision support get the same lists minus `view_image` — so a non-vision model on `readonly` ends up with 4 tools, not 5.
|
|
124
|
+
|
|
125
|
+
Enforcement layers:
|
|
126
|
+
1. **OS sandbox (bwrap)**: `--ro-bind / /` mounts the entire filesystem read-only, then configurable overlays hide sensitive paths (`--tmpfs` for directories, `--ro-bind /dev/null` for files). Workspace level adds `--bind` (rw) for the workspace directory. `--tmpfs /tmp` provides isolated writable temp per invocation. Tool execution uses `execFileAsync` (not shell) to prevent escape. Error messages are sanitized to strip sandbox internals — the agent never sees bwrap flags or mount details.
|
|
127
|
+
2. **Tool filtering (bwrap fallback only)**: when bwrap is unavailable, `createWorkspaceTools()` returns a reduced 5-tool set for readonly (no file_write, file_edit, shell_exec, web_fetch). Workspace retains all tools.
|
|
128
|
+
3. **App-level path validation (bwrap fallback only)**: `assertPathAllowed()` validates every path against workspace + `~/.halo/global/`; `shell_exec` is blocked entirely without bwrap for non-full sessions.
|
|
129
|
+
|
|
130
|
+
Dependency: `bubblewrap` (`apt install bubblewrap`) on Linux. Without it, only layers 2 + 3 are active.
|
|
131
|
+
|
|
132
|
+
### Sandbox hidden paths
|
|
133
|
+
|
|
134
|
+
Sensitive directories and files are hidden from workspace/readonly sessions via bwrap overlays. Paths that don't exist on the filesystem are silently skipped.
|
|
135
|
+
|
|
136
|
+
Configured in `settings.yaml` under `general.sandbox`:
|
|
137
|
+
|
|
138
|
+
| Setting | Default | Method |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `hidden_dirs` | `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker` | `--tmpfs` overlay (empty directory) |
|
|
141
|
+
| `hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig` | `--ro-bind /dev/null` (empty file) |
|
|
142
|
+
|
|
143
|
+
Changes take effect immediately — `config.ts` reads settings.yaml via an mtime-watched lazy cache, so the next `shell_exec` reads the latest values. A workspace-level `settings.yaml` can override these the same way it overrides any other key (no global-scope lock).
|
|
144
|
+
|
|
145
|
+
`/tmp` is not in the hidden list — it receives a standalone `--tmpfs` mount for process isolation (each bwrap invocation gets its own empty `/tmp`), not for hiding secrets.
|
|
146
|
+
|
|
147
|
+
Per-channel defaults:
|
|
148
|
+
- **Web** — inherits account's `access_level` (default `full`)
|
|
149
|
+
- **Telegram** — inherits account's `access_level` (default `readonly`)
|
|
150
|
+
- **WeChat** — inherits account's `access_level` (default `readonly`)
|
|
151
|
+
|
|
152
|
+
### Binary file detection
|
|
153
|
+
`grep` and `glob` read the first 512 bytes looking for a null byte and skip binaries.
|
|
154
|
+
|
|
155
|
+
### Tool result budget
|
|
156
|
+
The orchestrator truncates tool results over 8000 chars and appends a `[Content truncated]` hint telling the agent to use `grep` for a targeted search.
|
|
157
|
+
|
|
158
|
+
## Session tools
|
|
159
|
+
|
|
160
|
+
Session management tools for agents. **Not enabled by name** — the whole bundle (the eight tools below) is granted automatically the moment an agent declares a **non-empty `team`** in `agent.yaml`; an absent/empty `team` means no delegation (no session tools, no roster). Listing these under `tools:` has no effect. The `team` ids also scope who's reachable via `start_session` / `query_agent`. See [agent roster](../design/prompt-system.md#agent-roster).
|
|
161
|
+
|
|
162
|
+
**Own-tree scoping**: the five tools that take an existing `session_id` — `query_session`, `interrupt_session`, `stop_session`, `archive_session`, `get_session_output` — only act on sessions in the **caller's own session tree** (same root id, i.e. the same left-most `>` segment). A `session_id` from an unrelated tree is refused with `{"code": 1, "error": "session <id> not found"}` (phrased as not-found so it doesn't leak whether a foreign session exists). This keeps a multi-user/multi-channel shared workspace — where one `SessionManager` holds every user's trees — from letting one agent stop / archive / read another user's sessions. In-tree parent ↔ child ↔ sibling coordination is unaffected. See [session.md → By-id tool scoping](../design/session.md#by-id-tool-scoping).
|
|
163
|
+
|
|
164
|
+
### start_session
|
|
165
|
+
|
|
166
|
+
Start a sub-agent session asynchronously. When the sub-agent finishes, its **wrap-up reply** (the closing summary — not the mid-task progress narration) is auto-delivered back to the caller's conversation. A long summary is **cut head-kept / tail-dropped** at `limits.autoReportMax` (default 8,192 chars, settings `general.limits.auto_report_chars`) with a `[Report truncated: N chars total, showing first M. Use get_session_output("<id>") for the full result.]` marker — so the caller can tell a short answer from a cut-off one, and knows the **tail** is what's missing. Call `get_session_output` for the full untruncated reply. (Before 0.1.5 the auto-report concatenated every text segment of the turn, including mid-task filler.)
|
|
167
|
+
|
|
168
|
+
**Arguments**
|
|
169
|
+
|
|
170
|
+
| Arg | Type | Required | Description |
|
|
171
|
+
|---|---|---|---|
|
|
172
|
+
| `agent_id` | string | yes | Agent ID (e.g. `"coder"`) |
|
|
173
|
+
| `message` | string | yes | Task description |
|
|
174
|
+
| `system_prompt_context` | string | no | Extra context prepended to the initial message |
|
|
175
|
+
| `working_dir` | string | no | Sub-agent's focus directory (absolute or workspace-relative; default: project root). It's persistent session identity (stored in the DB, restored on resume), so the directory-chain `.halo/INSTRUCTIONS.md` along the path root→dir is baked into the sub-agent's **system prompt every turn** (it never forgets the directory's rules), and the prompt is tagged with this focus. Does **not** change where tools run. |
|
|
176
|
+
|
|
177
|
+
**Output (JSON string)**
|
|
178
|
+
|
|
179
|
+
- Success: `{"code": 0, "session_id": "<childSessionId>"}`
|
|
180
|
+
- Unknown agent: `{"code": 1, "error": "agent \"<id>\" not found..."}`
|
|
181
|
+
- Depth exceeded: `{"code": 1, "error": "Maximum nesting depth (N) reached..."}`
|
|
182
|
+
- Working dir invalid: `{"code": 1, "error": "working_dir \"...\" is outside the workspace"}` (or does-not-exist / not-a-directory)
|
|
183
|
+
|
|
184
|
+
### session_list
|
|
185
|
+
|
|
186
|
+
List direct child sessions of the current session and their status.
|
|
187
|
+
|
|
188
|
+
No arguments. Returns JSON:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"code": 0,
|
|
193
|
+
"sessions": [
|
|
194
|
+
{
|
|
195
|
+
"id": "root>sid_xxx",
|
|
196
|
+
"agentId": "coder",
|
|
197
|
+
"agentName": "Coder",
|
|
198
|
+
"title": "Implement login page",
|
|
199
|
+
"description": "Implement login page",
|
|
200
|
+
"status": "running",
|
|
201
|
+
"createdAt": 1714000000000
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"count": 1
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`status`: `running` / `idle` / `stopped`. Archived sessions are excluded.
|
|
209
|
+
|
|
210
|
+
`title` is the human-assigned label (set by renaming the session in the admin
|
|
211
|
+
sidebar), read from the per-session jsonl log so it matches what the UI shows.
|
|
212
|
+
It falls back to `description` (the `start_session` task summary) when no title
|
|
213
|
+
was set, so the field is never empty — lets a caller dispatch work by title.
|
|
214
|
+
|
|
215
|
+
### query_session
|
|
216
|
+
|
|
217
|
+
Send a message to another session. Idle = immediate run, busy = queued + soft interrupt. Reply is delivered asynchronously.
|
|
218
|
+
|
|
219
|
+
| Arg | Type | Required | Description |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| `target_session_id` | string | yes | Target session ID |
|
|
222
|
+
| `message` | string | yes | Message content |
|
|
223
|
+
|
|
224
|
+
**Busy = soft interrupt (merge-answer parity)**: when the target is busy, `query_session` enqueues the message **and** requests a soft interrupt (same as a user message arriving mid-turn) — the in-flight turn unwinds after its current tool, then every message that landed alongside it drains as **one merged turn**. So a sub-agent asked two questions while busy answers them **together**, not one-by-one — matching how root folds two user messages. No message is dropped. (Before 0.1.4 a busy `query_session` was pure no-interrupt enqueue, which made sub-agents reply one question at a time.)
|
|
225
|
+
|
|
226
|
+
**Queue cap (backpressure)**: when the target is busy, `query_session` is rejected with `{"code": 1, "error": "...message queue is full (N/max)..."}` once the target's queued **agent-sourced** messages reach `session.maxQueueSize` (default 256, settings `general.session.max_queue_size`). The cap counts **only agent→agent entries** — user messages share the same queue but are immune to backpressure (a human can't hand-type up to the cap, and counting them would let user chatter consume the agents' budget). `interrupt_session` is a deliberate action and bypasses the cap entirely.
|
|
227
|
+
|
|
228
|
+
### interrupt_session
|
|
229
|
+
|
|
230
|
+
Equivalent to `query_session` **plus an immediate abort** of the in-flight turn. The message is enqueued and traced right away (exactly like `query_session`); aborting then makes the queue drain **now** rather than after the current turn finishes, so the enqueued message is folded into the very next merged turn. The abort is hard — it propagates to `shell_exec` and SIGTERMs a command mid-execution (contrast the soft interrupt `query_session` and a busy user message trigger, which wait for the current `tool_result`). For a **compound** command (`sleep 60 && …`) under `full` access, the kill reaches the real worker because the command runs as a **process-group leader** and the whole group is signalled — before 0.1.4 the abort only hit the wrapping `/bin/sh`, leaving the worker to orphan and run to completion (which made `interrupt_session` look like it didn't interrupt). See [session.md → Process-group kill on abort](../design/session.md#process-group-kill-on-abort). Conversation history is preserved (repaired, not discarded), and `interrupt_session` bypasses the `query_session` queue cap.
|
|
231
|
+
|
|
232
|
+
| Arg | Type | Required | Description |
|
|
233
|
+
|---|---|---|---|
|
|
234
|
+
| `session_id` | string | yes | Session to interrupt |
|
|
235
|
+
| `message` | string | yes | Message to run after interruption |
|
|
236
|
+
|
|
237
|
+
### stop_session
|
|
238
|
+
|
|
239
|
+
Abort the current task of a running session. Any queued messages are **not** dropped — they are folded into the conversation history before the abort, so nothing said while the session was busy is lost. The session stays usable — later `query_session` calls continue the conversation.
|
|
240
|
+
|
|
241
|
+
| Arg | Type | Required | Description |
|
|
242
|
+
|---|---|---|---|
|
|
243
|
+
| `session_id` | string | yes | Session to stop |
|
|
244
|
+
|
|
245
|
+
### archive_session
|
|
246
|
+
|
|
247
|
+
**Cascade** archive a session and every descendant. Aborts running work, clears queued messages. Archived sessions disappear from `session_list` and cannot be reached by `query_session`. Only use it when the whole subtree is done.
|
|
248
|
+
|
|
249
|
+
| Arg | Type | Required | Description |
|
|
250
|
+
|---|---|---|---|
|
|
251
|
+
| `session_id` | string | yes | Session to archive |
|
|
252
|
+
|
|
253
|
+
### get_session_output
|
|
254
|
+
|
|
255
|
+
Read the **complete, untruncated** text of an agent session's reply to its **most recent message** — the full response spanning every step taken for that message (one message can drive many steps: narration → tool calls → more narration), which is more than the possibly-cut auto-report. Scoped to that one message's reply, not the session's whole history. Excludes tool calls/results and thinking — those only ever stream to the UI, never into the output.
|
|
256
|
+
|
|
257
|
+
| Arg | Type | Required | Description |
|
|
258
|
+
|---|---|---|---|
|
|
259
|
+
| `session_id` | string | yes | Session to read |
|
|
260
|
+
|
|
261
|
+
Implementation: a turn (one `runAgentTurn`, processing one inbound message) accumulates text into **two** per-turn buffers, both reset at the turn's start:
|
|
262
|
+
|
|
263
|
+
- `session.output` — **all** assistant text of the turn (mid-task filler + wrap-up). This is what `get_session_output` returns and what is persisted to disk.
|
|
264
|
+
- `session.finalOutput` — **only** the wrap-up reply (text emitted when `stopReason !== 'tool_use'`, flagged by the agent-loop `final` event field). This feeds the auto-report to the parent (`tryReportToParent`), falling back to `session.output` when the turn ended without a closing message.
|
|
265
|
+
|
|
266
|
+
In-memory sessions return `session.output`; released sessions read the `output` field from `.halo/sessions/{agentId}/{sid}.json`. `get_session_output` never truncates — only the auto-report does (see [start_session](#start_session)). (Split introduced in 0.1.5; before that both reads shared one `session.output`.)
|
|
267
|
+
|
|
268
|
+
### query_agent
|
|
269
|
+
|
|
270
|
+
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.
|
|
271
|
+
|
|
272
|
+
| Arg | Type | Required | Description |
|
|
273
|
+
|---|---|---|---|
|
|
274
|
+
| `agent_id` | string | yes | Agent to query |
|
|
275
|
+
|
|
276
|
+
### activate_skill
|
|
277
|
+
|
|
278
|
+
**Auto-injected — not declared in `agent.yaml tools`.** Generated by `createSkillTool()` whenever the YAML lists `skills`. Disabled skills (per workspace DB `disabled_items` table) are excluded from injection.
|
|
279
|
+
|
|
280
|
+
| Arg | Type | Required | Description |
|
|
281
|
+
|---|---|---|---|
|
|
282
|
+
| `skill_id` | string | yes | Skill to activate |
|
|
283
|
+
|
|
284
|
+
Returns: full SKILL.md content (body + resource files list). For progressive disclosure — the system prompt only contains skill metadata (name + description); the agent calls this tool on demand.
|
|
285
|
+
|
|
286
|
+
## Self-review tool
|
|
287
|
+
|
|
288
|
+
### draft
|
|
289
|
+
|
|
290
|
+
**Opt-in self-review.** Declare `draft` in `agent.yaml`'s `tools:` to give an agent a way to critique its own answer before committing. Built by `createDraftTool()` (`tools/draft-tool.ts`); no global switch — agents that don't list it never see it.
|
|
291
|
+
|
|
292
|
+
| Arg | Type | Required | Description |
|
|
293
|
+
|---|---|---|---|
|
|
294
|
+
| `content` | string | yes | The complete draft answer. Required and must be non-empty |
|
|
295
|
+
|
|
296
|
+
The tool **description is deliberately plain** — "Submit a draft answer for self-review; returns a checklist that critiques the draft." It does NOT say *when* to call it (no "call this for complex questions / when unsure"). An earlier version that did embed such self-referential guidance made some models (notably GPT-5.x) thrash. Steering toward *using* draft belongs in the agent's `system_prompt` (e.g. the bundled `default` agents say "hold your answers to a high standard… don't reply off the cuff"), not in the tool description. Empty `content` is rejected with an error **without consuming a draft round**.
|
|
297
|
+
|
|
298
|
+
**Why it exists:** the agent loop only makes another model call when the model emits a `tool_use` block. A plain-text answer (`end_turn`) is single-pass — `thinking` runs *before* the answer in the same call, so the model never gets to look at its *finished* answer and revise it. `draft` closes that gap without touching the loop: the model writes its answer into `content` (materialised into the conversation as a `tool_use` block — uncapped, never echoed back), and the tool_result hands back an adversarial review checklist (framed as a hostile reviewer: list every factual claim and tag its source, verify the unverified ones with tools *now*, check directness/tone, flag gaps). The next model call then critiques that now-concrete draft and either revises (calls `draft` again) or writes the final answer.
|
|
299
|
+
|
|
300
|
+
**Bounded by a per-turn counter**, not a prompt instruction (which would just be context noise). After 3 drafts in one turn the tool soft-lands: it stops returning the checklist and tells the model to finalise. The counter lives in the tool's closure; `SessionManager.runAgentTurn` calls the tool's `reset()` at the top of every turn-attempt (so a retry gets a fresh budget). The agent instance is reused across turns, so without this reset the budget would leak across the whole session.
|
|
301
|
+
|
|
302
|
+
**Scope note:** `draft` improves answers the model *knows* it should be careful about (it must choose to call the tool). It can't catch over-confident answers where the model doesn't realise it's wrong — that gap needs a post-turn judge (out of scope here).
|
|
303
|
+
|
|
304
|
+
## Tool assignment
|
|
305
|
+
|
|
306
|
+
Workspace tools are enabled strictly by name in `agent.yaml`'s `tools` list:
|
|
307
|
+
|
|
308
|
+
```yaml
|
|
309
|
+
tools:
|
|
310
|
+
- file_read
|
|
311
|
+
- shell_exec
|
|
312
|
+
skills:
|
|
313
|
+
- code-review # auto-injects activate_skill
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Tools not listed are not injected. Session/delegation tools do **not** go in `tools:` — they ride on a non-empty `team` (see [Session tools](#session-tools) above). `activate_skill` is auto-injected whenever the YAML lists `skills` (no need to put it in `tools`).
|
|
317
|
+
|
|
318
|
+
There is **no implicit default tool set**: `filterTools()` (in `agent-loader.ts`) returns only the tools whose names appear in `agent.yaml`'s `tools:` list. If the field is absent or empty, the agent has zero workspace tools. The admin UI's "Create agent" form scaffolds a fresh agent with an empty `tools: []` for the same reason — fill it in deliberately. The `default` agent's bundled `agent.yaml` lists the common set (`file_read` / `file_write` / `file_edit` / `view_image` / `file_list` / `shell_exec` / `grep` / `glob` / `web_fetch`) that most agents will want, plus `draft` (see Self-review tool above); copy that line if you're starting from scratch.
|
|
319
|
+
|
|
320
|
+
## Config
|
|
321
|
+
|
|
322
|
+
| Config key | Default | Purpose |
|
|
323
|
+
|---|---|---|
|
|
324
|
+
| `timeout.shellExec` | 120,000 ms | Shell command timeout |
|
|
325
|
+
| `timeout.webFetch` | 10,000 ms | HTTP timeout |
|
|
326
|
+
| `limits.shellOutputBuffer` | 5 MB | Shell output buffer |
|
|
327
|
+
| `limits.webFetchMaxBody` | 50 KB | web_fetch body cap |
|
|
328
|
+
| `limits.grepDefaultMax` | 50 | Default grep result cap |
|
|
329
|
+
| `limits.toolResultMax` | 8,000 chars | Tool result truncation threshold |
|
|
330
|
+
|
|
331
|
+
Defined in `packages/server/src/config.ts`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# User Guide
|
|
2
|
+
|
|
3
|
+
Aimed at Halo end-users. Pick the doc that matches your situation.
|
|
4
|
+
|
|
5
|
+
## Suggested order
|
|
6
|
+
|
|
7
|
+
1. [getting-started.md](getting-started.md) — opening Halo for the first time (login, workspace, first conversation)
|
|
8
|
+
2. [workspace.md](workspace.md) — interface layout, Activity Bar, keyboard shortcuts
|
|
9
|
+
3. [chat.md](chat.md) — everything the chat panel does (agent selection, slash commands, `@` file mentions, context injection)
|
|
10
|
+
4. [agents.md](agents.md) — creating, editing, configuring agents
|
|
11
|
+
5. [skills.md](skills.md) — skill concepts, creating skills, attaching them to agents
|
|
12
|
+
6. [sessions.md](sessions.md) — viewing session history, debug mode, multi-agent collaboration
|
|
13
|
+
|
|
14
|
+
## Channels — talk to your agent from somewhere other than the admin UI
|
|
15
|
+
|
|
16
|
+
[channels/README.md](channels/README.md) is the index. Per-channel onboarding (how to register the bot, what credentials to paste in admin):
|
|
17
|
+
|
|
18
|
+
- [Web](channels/web.md) — token-based HTTP + SSE for browsers / custom clients
|
|
19
|
+
- [Telegram](channels/telegram.md) — BotFather token, long-poll
|
|
20
|
+
- [Slack](channels/slack.md) — Socket Mode (no public webhook needed)
|
|
21
|
+
- [Feishu / Lark](channels/feishu.md) — long-connect with appId + appSecret
|
|
22
|
+
- [WeChat](channels/wechat.md) — QR-scan iLink bot
|
|
23
|
+
- [ACP](channels/acp.md) — Claude Code or other ACP clients driving a remote halo
|
|
24
|
+
|
|
25
|
+
## Related docs
|
|
26
|
+
|
|
27
|
+
- Development: [dev/](../dev/) — environment, API, tool reference
|
|
28
|
+
- Architecture: [design/](../design/) — implementation details and data flow
|
|
29
|
+
- Requirements: [requirements/](../requirements/) — per-module feature definitions
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Agents — User Guide
|
|
2
|
+
|
|
3
|
+
The agent is Halo's core abstraction: personality (AGENT.md) + config (agent.yaml) + tool set.
|
|
4
|
+
|
|
5
|
+
## Three agent kinds
|
|
6
|
+
|
|
7
|
+
| Kind | Description |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Built-in | Server-shipped agents (`default`, `executor`, `deep-executor` + the three internal `__evo_agent__` / `__score__` / `__apply_agent__`). Live under `~/.halo/global/agents/<id>/`. **Force-overwritten on every server startup** — local edits to these files will be lost on the next start. To customize: copy into the workspace scope (workspace replaces global) and edit there. |
|
|
10
|
+
| Global | Any other agent under `~/.halo/global/agents/<id>/`, e.g. one you created via the admin UI. Shared across projects. Never overwritten by the server. |
|
|
11
|
+
| Workspace | `<project>/.halo/agents/<id>/`, private to the current project; same-id workspace wins over global. Override is **whole-folder**: the workspace agent folder replaces the global one entirely, so copy *both* `agent.yaml` and `AGENT.md` when customizing — a folder with only `AGENT.md` has no model config and won't load. |
|
|
12
|
+
|
|
13
|
+
## Open the Agents panel
|
|
14
|
+
|
|
15
|
+
Click the `👥 Agents` icon in the Activity Bar.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────┬──────────────────────────┐
|
|
19
|
+
│ Agents │ Form / YAML / MD Editor │
|
|
20
|
+
│─────────────────│ │
|
|
21
|
+
│ ▼ Global (2) │ │
|
|
22
|
+
│ 🤖 Default │ │
|
|
23
|
+
│ 🤖 researcher │ │
|
|
24
|
+
│ ▼ Workspace (1) │ │
|
|
25
|
+
│ 🤖 coder │ │
|
|
26
|
+
└─────────────────┴──────────────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Create an agent
|
|
30
|
+
|
|
31
|
+
Top-of-sidebar `+` button; modal asks for:
|
|
32
|
+
- **Name**: display name (e.g. "Coder")
|
|
33
|
+
- **Description**: one-line description
|
|
34
|
+
- **Scope**: Global / Workspace
|
|
35
|
+
|
|
36
|
+
The backend calls `defaultAgentYaml(name, description)` to generate a minimal `agent.yaml`.
|
|
37
|
+
|
|
38
|
+
## Edit an agent
|
|
39
|
+
|
|
40
|
+
Click an agent in the sidebar to open the right-hand editor. Three views:
|
|
41
|
+
|
|
42
|
+
### Form view
|
|
43
|
+
Data-driven form for name / description / model / tools / skills / thinking — click Save.
|
|
44
|
+
|
|
45
|
+
**Model section** has four fields:
|
|
46
|
+
- `provider` — dropdown populated from `~/.halo/global/models/*.yaml`
|
|
47
|
+
- `id` — combobox (input + datalist); preset options filtered by current provider, also accepts manual input of any Bedrock model ID
|
|
48
|
+
- `endpoint` — combobox (input + datalist); full endpoint URL (e.g. `https://bedrock-runtime.ap-northeast-1.amazonaws.com`), supports custom proxy URLs
|
|
49
|
+
- `maxTokens` — optional; defaults to `maxOutputTokens` in the provider yaml
|
|
50
|
+
|
|
51
|
+
Switching provider auto-resets the model id to the first model of the new provider.
|
|
52
|
+
|
|
53
|
+
**Capability buttons** (Prompt Caching / Thinking) appear when the selected model matches a registry entry with declared capabilities. For manually entered model IDs not in the registry, default presets are shown (5min/1hour for caching, Low/Medium/High/Max for thinking).
|
|
54
|
+
|
|
55
|
+
### YAML view
|
|
56
|
+
Edit `agent.yaml` directly (Monaco). Use this for advanced settings (like `context.maxTokens`, `promptCaching`).
|
|
57
|
+
|
|
58
|
+
### MD view
|
|
59
|
+
Edit `AGENT.md` (personality) and `INSTRUCTIONS.md` (preferences).
|
|
60
|
+
- `AGENT.md` takes precedence over YAML's `system_prompt`
|
|
61
|
+
- `INSTRUCTIONS.md` is injected into every agent, not just this one
|
|
62
|
+
- When a workspace is open, the top-right toggles **Global / Workspace**; both MDs can be edited independently of which scope the current agent lives in. Switching to a non-existent file shows `(new)`; saving auto-creates the dir/file.
|
|
63
|
+
- AGENT.md supports `{{var}}` placeholders (`{{<skill-id>.params.<key>}}` / `<<ENV>>` / built-ins) rendered on agent start — see [skills.md placeholder section](skills.md#placeholders-template-variables)
|
|
64
|
+
|
|
65
|
+
## Tool configuration
|
|
66
|
+
|
|
67
|
+
What workspace tools an agent can use lives in the YAML `tools` list:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
tools:
|
|
71
|
+
- file_read
|
|
72
|
+
- file_write
|
|
73
|
+
- shell_exec
|
|
74
|
+
team: # a non-empty team is what enables delegation
|
|
75
|
+
- executor # the agent ids this one may spawn
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Workspace tools**: `file_read / file_write / file_edit / file_list / shell_exec / grep / glob / web_fetch` — listed by name under `tools`.
|
|
79
|
+
|
|
80
|
+
**Session tools**: `start_session / session_list / query_session / interrupt_session / stop_session / archive_session / get_session_output / query_agent` — **not** listed under `tools`. The whole bundle is granted automatically the moment an agent declares a non-empty `team`; an empty/absent `team` means no delegation (no session tools, no roster). To let a sub-agent delegate further, give it its own `team`. Add the agent's own id to its `team` to enable parallel self-spawn.
|
|
81
|
+
|
|
82
|
+
Form view lets you check workspace tools on/off and pick the team; there's no separate session-tools checklist — delegation rides entirely on the team.
|
|
83
|
+
|
|
84
|
+
## Skill mounting
|
|
85
|
+
|
|
86
|
+
`skills: [skill_id_1, skill_id_2]` — Halo injects skill metadata into the prompt, and the agent calls `activate_skill` to load the full SKILL.md when needed.
|
|
87
|
+
|
|
88
|
+
## Thinking mode
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
model:
|
|
92
|
+
thinking:
|
|
93
|
+
enabled: true
|
|
94
|
+
effort: medium # low / medium / high / xhigh / max
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
When enabled, the agent "thinks" before answering — useful for hard problems. Debug mode in the Session Viewer shows the full thinking content.
|
|
98
|
+
|
|
99
|
+
## Priority
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
priority: 50
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Higher = higher in the list, default 0. The default agent seed is `priority: 99`.
|
|
106
|
+
|
|
107
|
+
Two effects:
|
|
108
|
+
1. **Sort weight** — agents with higher priority appear first in the chat dropdown.
|
|
109
|
+
2. **Default selection** — when the user opens chat without an active session and hasn't manually picked an agent, the highest-priority agent is auto-selected. To override the seed `default`, raise another agent's priority above 99.
|
|
110
|
+
|
|
111
|
+
## Test
|
|
112
|
+
|
|
113
|
+
Top-right `Test` button:
|
|
114
|
+
1. Sets this agent as the chat panel's selected agent
|
|
115
|
+
2. Jumps to the Explorer's chat tab
|
|
116
|
+
3. Start chatting (in the real environment, not a sandbox)
|
|
117
|
+
|
|
118
|
+
Better than the old built-in test chat — it has full workspace tools + session persistence.
|
|
119
|
+
|
|
120
|
+
Internal agents (`internal: true`, e.g. self-evolution agents) have no Test button and never appear in the chat agent selector or `/session new`'s default pick — they're delegated to by other agents, never driven directly. They remain editable in the management sidebar's collapsed **Internal** group.
|
|
121
|
+
|
|
122
|
+
## Delete
|
|
123
|
+
|
|
124
|
+
Right-click → Delete. Constraints:
|
|
125
|
+
- The last global agent cannot be deleted (server-enforced)
|
|
126
|
+
- Workspace agents delete freely
|
|
127
|
+
|
|
128
|
+
## Common workflows
|
|
129
|
+
|
|
130
|
+
**Copy a global agent into workspace for customisation**: edit it in Form view, switch scope to Workspace, Save — creates an independent copy under `<project>/.halo/agents/<id>/`.
|
|
131
|
+
|
|
132
|
+
**Multi-agent collaboration**: the Default agent sees its teammates in the prompt roster, starts them with `start_session`, and the sub-agent auto-reports on completion. See [sessions.md](sessions.md).
|
|
133
|
+
|
|
134
|
+
## agent.yaml field reference
|
|
135
|
+
|
|
136
|
+
Full field list grouped by section. Form view covers about 80%; the rest requires YAML.
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
name: Coder # required, display name
|
|
140
|
+
description: Full-stack coder # optional, one-line description
|
|
141
|
+
|
|
142
|
+
# Sort weight (higher first). Default 0. The default agent is seeded with 99.
|
|
143
|
+
priority: 0
|
|
144
|
+
|
|
145
|
+
model:
|
|
146
|
+
provider: aws-bedrock-claude-invoke # required, matches ~/.halo/global/models/<provider>.yaml
|
|
147
|
+
id: global.anthropic.claude-sonnet-4-6 # default from settings.yaml params.model.default
|
|
148
|
+
endpoint: https://bedrock-runtime.us-west-2.amazonaws.com # full endpoint URL; supports custom proxy
|
|
149
|
+
maxTokens: 16384 # optional, max output tokens (default from provider yaml)
|
|
150
|
+
|
|
151
|
+
# Prompt caching — lower cost on repeated system prompts
|
|
152
|
+
# Values: true / '5m' (5-minute TTL) / '1h' (1-hour TTL)
|
|
153
|
+
promptCaching: 1h
|
|
154
|
+
|
|
155
|
+
# Thinking mode (Claude 4.x extended thinking)
|
|
156
|
+
thinking:
|
|
157
|
+
enabled: true
|
|
158
|
+
effort: medium # low / medium / high / xhigh / max
|
|
159
|
+
# budget: medium # legacy alias for effort (either is accepted)
|
|
160
|
+
|
|
161
|
+
# Personality prompt injected into the LLM. Ignored when AGENT.md is present.
|
|
162
|
+
system_prompt: |
|
|
163
|
+
You are...
|
|
164
|
+
|
|
165
|
+
# Context window
|
|
166
|
+
context:
|
|
167
|
+
maxTokens: 200000 # max context (default 200000)
|
|
168
|
+
compressAt: 0.8 # auto-compact trigger (0.8 = compact when 80% full)
|
|
169
|
+
|
|
170
|
+
# Tool allowlist (strict by name; unlisted tools are not injected).
|
|
171
|
+
# Session/delegation tools are NOT listed here — see `team` below.
|
|
172
|
+
tools:
|
|
173
|
+
- file_read
|
|
174
|
+
- file_write
|
|
175
|
+
- shell_exec
|
|
176
|
+
|
|
177
|
+
# Available skills (referenced by ID)
|
|
178
|
+
# When YAML lists skills, the agent automatically receives the activate_skill tool
|
|
179
|
+
skills:
|
|
180
|
+
- code-review
|
|
181
|
+
|
|
182
|
+
# Delegation: a non-empty `team` is the on/off switch. It grants the whole
|
|
183
|
+
# session-tool bundle (start_session, query_agent, …) AND scopes which agents
|
|
184
|
+
# this one may delegate to. Omit / leave empty = no delegation.
|
|
185
|
+
team:
|
|
186
|
+
- executor
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> **Disable / Enable**: managed per workspace in the `disabled_items` table of `halo.db` (not in agent.yaml). Toggle via admin sidebar; disabled agents are hidden from the delegation roster, chat selector, and `/workspace share` export. Still visible in admin sidebar (dimmed + toggle switch).
|
|
191
|
+
|
|
192
|
+
**Field source**: `packages/server/src/agents/agent-loader.ts`, `AgentYamlConfig` interface.
|
|
193
|
+
|
|
194
|
+
**Full tool list**: see [dev/tools.md](../dev/tools.md).
|
|
195
|
+
|
|
196
|
+
**Common edits**:
|
|
197
|
+
- Agent handling long tasks → `context.maxTokens: 500000` + `context.compressAt: 0.85`
|
|
198
|
+
- Save cost → `model.promptCaching: '1h'`
|
|
199
|
+
- Complex reasoning → `thinking.enabled: true` + `thinking.effort: high`
|
|
200
|
+
- Read-only agent → keep only `file_read` / `file_list` / `grep` / `glob` in `tools`
|
|
201
|
+
|
|
202
|
+
## AGENT.md placeholders
|
|
203
|
+
|
|
204
|
+
AGENT.md supports `{{var}}` placeholders, rendered on session start. Uses the same renderer as SKILL.md.
|
|
205
|
+
|
|
206
|
+
Built-ins (`{{workspace_root}}`, `{{user_name}}`, …) + settings paths (`{{<skill-id>.params.<key>}}`) + env vars (`<<ENV_NAME>>`). Full rules in [skills.md placeholder section](skills.md#placeholders-template-variables).
|
|
207
|
+
|
|
208
|
+
> AGENT.md uses **fully-qualified** paths (`{{<skill-id>.params.<key>}}`). The short-form auto-rewrite (`{{params.<key>}}`) is a SKILL.md-only convenience.
|
|
209
|
+
|
|
210
|
+
**Example**:
|
|
211
|
+
|
|
212
|
+
```markdown
|
|
213
|
+
You are the Nano Banana client assistant.
|
|
214
|
+
API endpoint: {{nano-banana.params.base_url}}
|
|
215
|
+
API key: {{nano-banana.params.api_key}}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Combined with `~/.halo/secrets/settings.yaml`:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
nano-banana:
|
|
222
|
+
params:
|
|
223
|
+
base_url: https://api.nano-banana.example
|
|
224
|
+
api_key: <<NANO_BANANA_KEY>>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
…where the schema lives at `extensions/skills/nano-banana/config.yaml`.
|
|
228
|
+
|
|
229
|
+
With `export NANO_BANANA_KEY=sk-xxx` in the env, the agent's system prompt gets the real key on start.
|
|
230
|
+
|
|
231
|
+
## Model registry — adding a new provider
|
|
232
|
+
|
|
233
|
+
Halo ships with `aws-bedrock-claude-invoke` only. Hooking up OpenAI / direct Anthropic / another Bedrock variant takes two steps:
|
|
234
|
+
|
|
235
|
+
### 1. Declare the provider
|
|
236
|
+
|
|
237
|
+
Create `~/.halo/global/models/<providerId>.yaml`:
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
id: openai # required; matches the filename
|
|
241
|
+
displayName: OpenAI
|
|
242
|
+
description: OpenAI Chat Completions
|
|
243
|
+
models:
|
|
244
|
+
- id: gpt-4o
|
|
245
|
+
displayName: GPT-4o
|
|
246
|
+
maxOutputTokens: 16384
|
|
247
|
+
capabilities:
|
|
248
|
+
promptCaching:
|
|
249
|
+
ttlPresets:
|
|
250
|
+
- { value: 5m, label: 5min }
|
|
251
|
+
# No thinking block → UI automatically hides the Thinking button
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### 2. Implement the runtime
|
|
255
|
+
|
|
256
|
+
Add a case in the switch inside [packages/server/src/agents/model-runtime.ts](../../../packages/server/src/agents/model-runtime.ts) returning a class that implements `ModelRuntime` (`messages` + `run()`). Interface detail in [design/agent.md](../design/agent.md#agent-instance).
|
|
257
|
+
|
|
258
|
+
### Usage
|
|
259
|
+
|
|
260
|
+
Set `model.provider: openai` in your agent.yaml; the Provider dropdown in Form view lists it automatically.
|
|
261
|
+
|
|
262
|
+
### Current state
|
|
263
|
+
|
|
264
|
+
Only `aws-bedrock-claude-invoke` ships an implementation. If you drop a provider yaml without a runtime implementation, session spawn fails with `Unknown provider "..."`.
|