@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,161 @@
|
|
|
1
|
+
# Secrets and Credentials
|
|
2
|
+
|
|
3
|
+
How to give a skill or agent an API key / token / password without committing it to a repo or hard-coding it in a skill body.
|
|
4
|
+
|
|
5
|
+
## The model: schema vs. value
|
|
6
|
+
|
|
7
|
+
Halo's settings system mirrors VSCode's `contributes.configuration` pattern:
|
|
8
|
+
|
|
9
|
+
| Side | What lives there | Where |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| **Schema** (declaration) | "This provider/skill needs a key called X, here's its description, and here's whether it's a secret" | Inside the package: `models/<provider-id>.yaml` `secrets:` section · `skills/<skill-id>/config.yaml` `params:` / `secrets:` sections |
|
|
12
|
+
| **Value** | The user's actual key | `~/.halo/secrets/settings.yaml` (global) or `<workspace>/.halo/settings.yaml` (workspace overlay) |
|
|
13
|
+
|
|
14
|
+
Removing or disabling a package only takes its **schema** out of the registry; values stay in `settings.yaml` and re-attach automatically if the same id comes back. The Settings page surfaces leftover values in an "Unclaimed" list so the user can prune them on their own schedule.
|
|
15
|
+
|
|
16
|
+
## Two kinds of declared fields
|
|
17
|
+
|
|
18
|
+
| Kind | Reachable from agent? | Storage path | Render |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| **`secrets`** | Never. Server-side only — model providers, OAuth app credentials, signing keys. | `<id>.secrets.<key>` | UI masks the value; the placeholder renderer rejects `{{<id>.secrets.…}}` so nothing leaks into prompts. |
|
|
21
|
+
| **`params`** | Yes, via `{{<id>.params.<key>}}`. Use this for keys a skill needs to inject into its own `shell_exec` (e.g. Tavily search). | `<id>.params.<key>` | Substituted at `shell_exec` time inside the skill's command. |
|
|
22
|
+
|
|
23
|
+
The boundary is enforced by:
|
|
24
|
+
- [packages/server/src/prompts/md-vars.ts](../../../packages/server/src/prompts/md-vars.ts) — `renderMdBody` only replaces `{{<id>.params.<key>}}`; anything else stays literal.
|
|
25
|
+
- [packages/server/src/tools/workspace-tools.ts](../../../packages/server/src/tools/workspace-tools.ts) — `substituteSecrets` does the same on every `shell_exec` call.
|
|
26
|
+
|
|
27
|
+
A malicious agent that tries `curl -H "Bearer {{aws-bedrock-claude-invoke.secrets.secret_access_key}}"` gets the literal placeholder, not the value.
|
|
28
|
+
|
|
29
|
+
## Worked example: a skill that needs an API key
|
|
30
|
+
|
|
31
|
+
Goal: a skill called `caption-image` that calls `api.example.com` with an API key.
|
|
32
|
+
|
|
33
|
+
### Step 1 — declare in the skill's `config.yaml`
|
|
34
|
+
|
|
35
|
+
Beside `SKILL.md`:
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# extensions/skills/caption-image/config.yaml
|
|
39
|
+
params:
|
|
40
|
+
- key: api_key
|
|
41
|
+
description: API key for api.example.com
|
|
42
|
+
description_zh: api.example.com 的 API Key
|
|
43
|
+
default: <<EXAMPLE_KEY>> # placeholder shown in UI when value is unset
|
|
44
|
+
secret: true # UI masks the input + API response
|
|
45
|
+
- key: base_url
|
|
46
|
+
description: Endpoint base URL
|
|
47
|
+
default: https://api.example.com
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The schema is what the package ships. The Settings page reads it and renders inputs.
|
|
51
|
+
|
|
52
|
+
### Step 2 — set the value
|
|
53
|
+
|
|
54
|
+
Two options:
|
|
55
|
+
|
|
56
|
+
- **From the Settings page** — open the `caption-image` section, type the key, save. The value is written to `~/.halo/secrets/settings.yaml` at `caption-image.params.api_key`.
|
|
57
|
+
- **Via env var** — leave the value as `<<EXAMPLE_KEY>>` (the default) and `export EXAMPLE_KEY=...` on the host. Both work; env vars are friendlier for ops automation, settings are friendlier for one-off keys.
|
|
58
|
+
|
|
59
|
+
In either case the rendered value travels through the normal `<<ENV_NAME>>` → `process.env.ENV_NAME` resolution.
|
|
60
|
+
|
|
61
|
+
### Step 3 — reference from `SKILL.md`
|
|
62
|
+
|
|
63
|
+
Inside the skill's body you can write the **short form** — Halo auto-qualifies it with the skill id when `activate_skill` fires:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
---
|
|
67
|
+
name: Caption Image
|
|
68
|
+
description: Generate a caption for an image using the Example API
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
# Caption Image
|
|
72
|
+
|
|
73
|
+
Call the endpoint `{{params.base_url}}/caption`:
|
|
74
|
+
|
|
75
|
+
- Method: POST
|
|
76
|
+
- Header: `Authorization: Bearer {{params.api_key}}`
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Behind the scenes the activation rewrites `{{params.base_url}}` → `{{caption-image.params.base_url}}` before the body is shown to the agent. By the time `shell_exec` runs the curl, `workspace-tools` substitutes the real value.
|
|
80
|
+
|
|
81
|
+
You can also write the long form `{{caption-image.params.api_key}}` directly if you prefer it explicit (e.g. when one skill references another's params).
|
|
82
|
+
|
|
83
|
+
## Workspace overlay
|
|
84
|
+
|
|
85
|
+
`<workspace>/.halo/settings.yaml` deep-merges on top of the global file, key by key. So a project-specific Tavily key would live at:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
# /home/me/myproject/.halo/settings.yaml
|
|
89
|
+
tavily-search:
|
|
90
|
+
params:
|
|
91
|
+
api_key: tvly-project-specific-…
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
…and overrides whatever sits at `tavily-search.params.api_key` in `~/.halo/secrets/settings.yaml`. The Settings UI shows source badges (`workspace` / `inherited from global` / `unset`) and an explicit Reset button to drop a workspace override.
|
|
95
|
+
|
|
96
|
+
## Precedence
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
env vars (process.env.X) > workspace settings.yaml > global settings.yaml > schema default
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`<<ENV_NAME>>` substitution happens **at render time** — right before the skill/agent body is handed to the LLM, and at `shell_exec` time inside values that came from a `{{<id>.params.<key>}}` lookup.
|
|
103
|
+
|
|
104
|
+
**Trust boundary**: `<<ENV>>` is only honored inside settings-resolved values. Cmd text the agent writes directly is not scanned — `shell_exec "echo <<HOME>>"` keeps the literal. This prevents an agent from naming an env var and forcing the server to dump it.
|
|
105
|
+
|
|
106
|
+
## Missing env var
|
|
107
|
+
|
|
108
|
+
If `process.env.EXAMPLE_KEY` is unset and the value at `caption-image.params.api_key` is `<<EXAMPLE_KEY>>`:
|
|
109
|
+
- The agent sees the literal `<<EXAMPLE_KEY>>` in the rendered skill body
|
|
110
|
+
- The server logs `[md-vars] Env var "EXAMPLE_KEY" not set — keeping <<EXAMPLE_KEY>> literal`
|
|
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
|
+
|
|
113
|
+
This is intentional: empty strings hide the misconfiguration; literals make it visible.
|
|
114
|
+
|
|
115
|
+
## Placeholder rules
|
|
116
|
+
|
|
117
|
+
In an MD body, `{{xxx}}` is resolved as:
|
|
118
|
+
|
|
119
|
+
1. **No dots** → built-in (`args`, `workspace_root`, `working_dir`, `now`, `user_name`, `ai_name`, `agent_name`)
|
|
120
|
+
2. **Matches `<id>.params.<key>`** → settings tree lookup, then `<<ENV>>` expansion
|
|
121
|
+
3. **Anything else** → kept literal + warning logged
|
|
122
|
+
|
|
123
|
+
| In MD body | Result |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `{{caption-image.params.api_key}}` | Replaced with the value (after `<<ENV>>` resolution) |
|
|
126
|
+
| `{{params.api_key}}` inside a SKILL.md | Auto-rewritten to `{{<this-skill-id>.params.api_key}}` at `activate_skill` time |
|
|
127
|
+
| `{{aws-bedrock-claude-invoke.secrets.access_key_id}}` | Kept as literal — the renderer hard-rejects `secrets.*` paths |
|
|
128
|
+
| `{{general.compact.keep_messages}}` | Same — kept literal |
|
|
129
|
+
| `{{args}}`, `{{workspace_root}}`, etc. | Built-ins, replaced |
|
|
130
|
+
|
|
131
|
+
This whitelist is the single defence preventing prompt-injection-style theft of server-side secrets via skill content.
|
|
132
|
+
|
|
133
|
+
## What NOT to do
|
|
134
|
+
|
|
135
|
+
| Anti-pattern | Why it's bad | Right fix |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| Paste the key directly into `SKILL.md` | The SKILL is usually git-tracked or shared; the key leaks | Use `{{params.api_key}}` |
|
|
138
|
+
| Paste the key into `settings.yaml` and commit it | The file holds real secrets | Set the value to `<<KEY>>` + `export KEY=...` |
|
|
139
|
+
| Store the key in `AGENT.md` | Same reason | Use a `params:` declaration on the agent or a skill it depends on |
|
|
140
|
+
| Put a model-provider key under a skill's `params:` | Any agent could borrow it via `shell_exec` | Declare it as `secrets:` in the model provider yaml |
|
|
141
|
+
| Reference `{{<id>.secrets.x}}` from a skill body | Hard-rejected — kept as literal | If a skill genuinely needs server-side credentials, that's a sign it should be a server tool, not a `shell_exec` skill |
|
|
142
|
+
|
|
143
|
+
## Rotating a credential
|
|
144
|
+
|
|
145
|
+
1. Generate the new value
|
|
146
|
+
2. Either edit it in the Settings page (writes `settings.yaml` immediately), or update the env var and restart the server
|
|
147
|
+
3. Existing running sessions keep the old value baked into already-rendered SKILL bodies of previous `activate_skill` calls. They pick up the new value on the next activation.
|
|
148
|
+
|
|
149
|
+
No code change. No yaml schema change.
|
|
150
|
+
|
|
151
|
+
## Env var naming conventions
|
|
152
|
+
|
|
153
|
+
Free-form, but consistency helps. Common patterns in Halo:
|
|
154
|
+
|
|
155
|
+
| Env var | Purpose |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `HALO_PASSWORD` | Server login password |
|
|
158
|
+
| `HALO_MODEL_ID` / `HALO_MODEL_PROVIDER` / `HALO_MAX_CONTEXT_TOKENS` | Server defaults |
|
|
159
|
+
| `AWS_REGION` / `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | AWS / Bedrock |
|
|
160
|
+
|
|
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`.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Sessions — User Guide
|
|
2
|
+
|
|
3
|
+
A session is "one continuous conversation with an agent". It includes your conversation with the main agent plus any sub-agents the main agent spawns.
|
|
4
|
+
|
|
5
|
+
## Sessions panel
|
|
6
|
+
|
|
7
|
+
Left-side Activity Bar → `📨 Sessions` (message icon).
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────┬──────────────────────────┐
|
|
11
|
+
│ Sessions │ Message viewer │
|
|
12
|
+
│─────────────────│ │
|
|
13
|
+
│ ▼ My task │ │
|
|
14
|
+
│ 📨 user msg │ │
|
|
15
|
+
│ ▶ └ 🤖 coder │ │
|
|
16
|
+
│ ▶ └ 🤖 sleeper │ │
|
|
17
|
+
│ 📨 Old chat │ │
|
|
18
|
+
└─────────────────┴──────────────────────────┘
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Hierarchy tree
|
|
22
|
+
|
|
23
|
+
The main agent (the one you talk to directly) sits at the top; any sub-agents it delegates to are shown indented.
|
|
24
|
+
|
|
25
|
+
- Collapse/expand: click the arrow
|
|
26
|
+
- Count badge `+3`: 3 descendants
|
|
27
|
+
- Red ⏹ icon: session was stopped (stopped_at is set)
|
|
28
|
+
|
|
29
|
+
## Message viewer
|
|
30
|
+
|
|
31
|
+
Click a session on the left to open the viewer:
|
|
32
|
+
- User messages and assistant replies (Markdown)
|
|
33
|
+
- Tool-call cards (expandable for input/output)
|
|
34
|
+
- Timestamps and agent-name badges
|
|
35
|
+
|
|
36
|
+
## Debug mode
|
|
37
|
+
|
|
38
|
+
Top-right Bug icon toggles it. When on you also see:
|
|
39
|
+
|
|
40
|
+
| Colour | Type | Content |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| Purple | Context | Full system prompt |
|
|
43
|
+
| Blue | Tool Call | Full tool input JSON |
|
|
44
|
+
| Green/Red | Tool Result | Full tool output |
|
|
45
|
+
| — | Usage | Token counts / latency / cache hit% / model ID |
|
|
46
|
+
|
|
47
|
+
**Essential for debugging and reviewing agent behaviour.**
|
|
48
|
+
|
|
49
|
+
## System prompt viewer
|
|
50
|
+
|
|
51
|
+
In Debug mode the top-right gets an extra `Prompt` button; click to expand the full system prompt (AGENT.md + INSTRUCTIONS.md + INDEX.md + TOOL_GUIDELINES + skill metadata).
|
|
52
|
+
|
|
53
|
+
Use it to see exactly what context the agent was given for a given turn.
|
|
54
|
+
|
|
55
|
+
## Non-destructive /session new
|
|
56
|
+
|
|
57
|
+
Typing `/session new` in the chat:
|
|
58
|
+
1. Saves the current session to disk
|
|
59
|
+
2. Creates a background handler to keep the old session's in-flight events flowing
|
|
60
|
+
3. UI resets to an empty session
|
|
61
|
+
4. **The old session's sub-agents keep running** (not killed)
|
|
62
|
+
|
|
63
|
+
When you switch back, the background-finished events are already in the file and render normally.
|
|
64
|
+
|
|
65
|
+
## Refresh / reconnect
|
|
66
|
+
|
|
67
|
+
Refreshing the browser or a network blip:
|
|
68
|
+
- SessionManager keeps the session alive for the grace period (5 minutes)
|
|
69
|
+
- On reconnect, messageLog + in-flight streaming state are restored from disk
|
|
70
|
+
- If the agent is still running, live streaming resumes; if it finished, the full history loads
|
|
71
|
+
|
|
72
|
+
## Deleting a session
|
|
73
|
+
|
|
74
|
+
Right-click a session → Delete:
|
|
75
|
+
- Cascade delete in SQLite (parent + all descendants)
|
|
76
|
+
- `.halo/sessions/{agentId}/{sid}.json` removed
|
|
77
|
+
- Sidebar entry removed
|
|
78
|
+
|
|
79
|
+
**Not recoverable** — be careful.
|
|
80
|
+
|
|
81
|
+
## Session tools (agent-side)
|
|
82
|
+
|
|
83
|
+
Agents can manage other sessions via tools. The whole bundle is granted automatically when an agent declares a non-empty `team` in `agent.yaml` (no separate tool checklist); empty or absent `team` = no delegation:
|
|
84
|
+
|
|
85
|
+
| Tool | Purpose |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `start_session` | Start a sub-agent, asynchronous, auto-reports on completion |
|
|
88
|
+
| `session_list` | List the current session's children |
|
|
89
|
+
| `query_session` | Send a message to a session (idle = run now, busy = queue) |
|
|
90
|
+
| `interrupt_session` | Interrupt + re-run |
|
|
91
|
+
| `stop_session` | Stop (no re-run, recoverable) |
|
|
92
|
+
| `archive_session` | Cascade archive (not recoverable) |
|
|
93
|
+
| `get_session_output` | Read a session's latest turn output |
|
|
94
|
+
| `query_agent` | Inspect another agent (team-gated to the roster) |
|
|
95
|
+
|
|
96
|
+
See [dev/tools.md](../dev/tools.md).
|
|
97
|
+
|
|
98
|
+
## Scenarios
|
|
99
|
+
|
|
100
|
+
**Figuring out why an agent messed up**: open the session → Debug mode → scroll through tool-call cards to see inputs and outputs.
|
|
101
|
+
|
|
102
|
+
**Reusing a historical task**: find a relevant session → click `Prompt` → copy the system prompt into a new agent's AGENT.md.
|
|
103
|
+
|
|
104
|
+
**Following multi-level delegation**: within the depth-16 cap, the indented tree is easy to follow; click any sub-session to see exactly what it did.
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Skills — User Guide
|
|
2
|
+
|
|
3
|
+
A skill is a "knowledge pack" an agent can reference — a detailed instruction set plus optional templates or scripts.
|
|
4
|
+
|
|
5
|
+
## Why skills
|
|
6
|
+
|
|
7
|
+
To extract capabilities an agent uses occasionally but needs full instructions for. For example:
|
|
8
|
+
- "Code Review": review code against a checklist
|
|
9
|
+
- "Deploy check": the pre-deploy checklist
|
|
10
|
+
- "Translate to academic English": a specific translation flow
|
|
11
|
+
|
|
12
|
+
Putting those in AGENT.md would bloat the system prompt. Skills use **progressive disclosure**:
|
|
13
|
+
1. On startup the agent only sees each skill's name + description
|
|
14
|
+
2. When the user triggers the skill or the agent decides it needs it, it calls `activate_skill` to load the full body
|
|
15
|
+
|
|
16
|
+
## Directory layout
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
~/.halo/global/skills/ # Global skills
|
|
20
|
+
code-review/
|
|
21
|
+
SKILL.md
|
|
22
|
+
checklist.md
|
|
23
|
+
examples/review-sample.md
|
|
24
|
+
|
|
25
|
+
<project>/.halo/skills/ # Workspace skills
|
|
26
|
+
deploy-check/
|
|
27
|
+
SKILL.md
|
|
28
|
+
scripts/verify.sh
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Workspace overrides global (same id).
|
|
32
|
+
|
|
33
|
+
**Built-in skills** (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `self`, `aws-knowledge`, `nova-web-search`) 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
|
+
|
|
35
|
+
## SKILL.md format
|
|
36
|
+
|
|
37
|
+
```markdown
|
|
38
|
+
---
|
|
39
|
+
name: code-review
|
|
40
|
+
description: Review code for correctness, performance, and style
|
|
41
|
+
command: /review # optional: also register a slash command
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Code Review
|
|
45
|
+
|
|
46
|
+
Review the code for:
|
|
47
|
+
|
|
48
|
+
1. Correctness bugs
|
|
49
|
+
2. Performance issues
|
|
50
|
+
3. Style / consistency with codebase
|
|
51
|
+
|
|
52
|
+
Follow the checklist in checklist.md...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Frontmatter fields**:
|
|
56
|
+
| Field | Required | Purpose |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| name | yes | kebab-case, = directory name (lowercase / digits / hyphens, ≤64) |
|
|
59
|
+
| description | yes | One-line description (what agents see at discovery time) |
|
|
60
|
+
| command | no | Also register as a slash command (`/xxx` user-triggerable) — opt-in, unlike the upstream Agent Skills standard where the directory name automatically becomes a command |
|
|
61
|
+
|
|
62
|
+
**Body**: the full instructions, returned when the agent calls `activate_skill(skill_id)`.
|
|
63
|
+
|
|
64
|
+
## Open the Skills panel
|
|
65
|
+
|
|
66
|
+
Left-side Activity Bar → `🪄 Skills` icon.
|
|
67
|
+
|
|
68
|
+
Click any skill and the right pane becomes a mini workspace:
|
|
69
|
+
- File tree (every file in the skill directory) on the left
|
|
70
|
+
- Monaco editor + Markdown preview on the right
|
|
71
|
+
|
|
72
|
+
You can edit SKILL.md, add scripts, templates, example docs.
|
|
73
|
+
|
|
74
|
+
## Create / delete
|
|
75
|
+
|
|
76
|
+
`+` button on the sidebar — fill in name / description / scope.
|
|
77
|
+
|
|
78
|
+
Delete: right-click → Delete.
|
|
79
|
+
|
|
80
|
+
## Letting an agent use a skill
|
|
81
|
+
|
|
82
|
+
Edit agent.yaml:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
skills:
|
|
86
|
+
- code-review
|
|
87
|
+
- deploy-check
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
After Save, from the next conversation:
|
|
91
|
+
- The system prompt gets `<available_skills> <skill><name>...</name><id>...</id>... </skill> </available_skills>`
|
|
92
|
+
- The agent automatically receives the `activate_skill` tool
|
|
93
|
+
|
|
94
|
+
When the agent decides a skill fits, it calls `activate_skill(skill_id='code-review')`, receives the full SKILL.md, and follows the instructions.
|
|
95
|
+
|
|
96
|
+
## Skill-as-Command
|
|
97
|
+
|
|
98
|
+
When SKILL.md frontmatter has a `command` field, the user can slash-trigger it:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
User: /review src/foo.ts
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Halo renders the SKILL.md body (user args reach it via `$ARGUMENTS` / `$1`–`$9` placeholders — they are not appended to the end) and sends it as a message for the agent:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
[Skill activated: /review]
|
|
108
|
+
|
|
109
|
+
{SKILL.md body, with $ARGUMENTS → "src/foo.ts"}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The agent follows the skill's instructions.
|
|
113
|
+
|
|
114
|
+
## Resource files
|
|
115
|
+
|
|
116
|
+
Files in the same directory as SKILL.md are listed at the end of `activate_skill`'s return:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Resource files in skill directory:
|
|
120
|
+
- checklist.md
|
|
121
|
+
- examples/review-sample.md
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The agent uses `file_read` to load them on demand.
|
|
125
|
+
|
|
126
|
+
## Settings integration
|
|
127
|
+
|
|
128
|
+
For configurable parameters (API keys, regions, model presets), declare them in a `config.yaml` next to `SKILL.md` and reference them from the body with `{{params.x}}` (short form — auto-qualified to `{{<skill-id>.params.x}}` at activation). See [Placeholders (template variables)](#placeholders-template-variables) below.
|
|
129
|
+
|
|
130
|
+
`config.yaml` example (sits beside `SKILL.md`):
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
params:
|
|
134
|
+
- key: api_key
|
|
135
|
+
description: API key for the Example service
|
|
136
|
+
default: <<EXAMPLE_KEY>>
|
|
137
|
+
secret: true
|
|
138
|
+
secrets: [] # rare for skills — server-side only, never substituted into shell_exec
|
|
139
|
+
```
|
|
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`.
|
|
142
|
+
|
|
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
|
+
|
|
145
|
+
See the full credential model at [secrets-and-credentials.md](secrets-and-credentials.md).
|
|
146
|
+
|
|
147
|
+
## Built-in skills
|
|
148
|
+
|
|
149
|
+
Halo seeds these skills on every startup (the ids in `BUILTIN_SKILL_IDS`, `packages/server/src/init.ts`):
|
|
150
|
+
|
|
151
|
+
| ID | Purpose |
|
|
152
|
+
|---|---|
|
|
153
|
+
| agent | Create / update agents — backs the `create` / `update` verbs of `/agent` |
|
|
154
|
+
| skill | Create / update skills — backs the `create` / `update` verbs of `/skill` |
|
|
155
|
+
| workspace | Workspace maintenance — backs `/workspace setup` / `tidy` (init / reorganize `.halo/` INDEX.md / INSTRUCTIONS.md / memory/) and `/workspace share` (export a shareable bundle) |
|
|
156
|
+
| cron | Create / list / update / enable / disable / delete scheduled agent runs — backs `/cron` |
|
|
157
|
+
| acp | Talk to other agents over ACP (`/acp kiro\|claude <q>`) and manage `ask-<label>` bindings for halo-to-halo delegation (`/acp add\|list\|remove`) |
|
|
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
|
+
| self | The agent's own visual space (`.halo/canvas/self.html`) for self-expression (no command; model-activated, full access) |
|
|
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
|
+
| nova-web-search | Real-time web search via Amazon Nova 2 Lite's nova_grounding (no command, `user-invocable: false`; model auto-activates) |
|
|
162
|
+
|
|
163
|
+
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
|
+
|
|
165
|
+
## SKILL.md field reference
|
|
166
|
+
|
|
167
|
+
Frontmatter fields (Halo dialect of the Agent Skills standard):
|
|
168
|
+
|
|
169
|
+
```markdown
|
|
170
|
+
---
|
|
171
|
+
name: code-review # required, kebab-case, = directory name
|
|
172
|
+
description: Review code changes # required, one-line description (agents use this to decide when to activate)
|
|
173
|
+
command: /review # optional, register as a slash command (opt-in)
|
|
174
|
+
requiresAccess: workspace # optional (Halo), object-level access gate: full | workspace | readonly
|
|
175
|
+
verbs: # optional (Halo extension), subcommand declarations
|
|
176
|
+
- { name: list, builtin: true, desc: List things }
|
|
177
|
+
- { name: create, requiresAccess: full, desc: Create a thing }
|
|
178
|
+
disable-model-invocation: true # optional (standard), command stays but the model can't auto-activate
|
|
179
|
+
user-invocable: false # optional (standard), never becomes a slash command; model can still activate
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
# Skill Body...
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Field notes**:
|
|
186
|
+
|
|
187
|
+
| Field | Required | Purpose |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| name | yes | kebab-case, = directory name; appears in the agent system prompt's `<available_skills>` block |
|
|
190
|
+
| description | yes | Helps agents decide when to activate |
|
|
191
|
+
| command | no | Register a slash command — see Skill-as-Command above |
|
|
192
|
+
| requiresAccess | no | Halo-specific access gate (`full` / `workspace` / `readonly`) for the whole skill |
|
|
193
|
+
| verbs | no | **Halo extension** — list of `{ name, builtin?, requiresAccess?, desc? }`. `builtin: true` marks a verb handled by platform code (declarative; actual routing lives in `SUBCOMMAND_ROUTES`); skill verbs take their access gate from this declaration. The verb reaches the body via `$1` |
|
|
194
|
+
| disable-model-invocation | no | Standard — `true` keeps the slash command but the skill isn't injected for the model (no auto-activation) |
|
|
195
|
+
| user-invocable | no | Standard — `false` means no slash command ever; the model can still activate it |
|
|
196
|
+
|
|
197
|
+
> **Disable / Enable**: managed per workspace in the `disabled_items` table of `halo.db` (not in SKILL.md). Toggle via admin sidebar or `/skill disable|enable`; disabled skills are excluded from system prompt injection, activate_skill tool, agent form picker, and `/workspace share` export. Still visible in admin sidebar (dimmed + toggle switch).
|
|
198
|
+
|
|
199
|
+
**Minimal SKILL.md**: frontmatter with `name` + `description` + body; everything else is optional.
|
|
200
|
+
|
|
201
|
+
**What `activate_skill` returns**:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
# Skill: {name}
|
|
205
|
+
|
|
206
|
+
{body}
|
|
207
|
+
|
|
208
|
+
Resource files in skill directory:
|
|
209
|
+
- checklist.md
|
|
210
|
+
- examples/review.md
|
|
211
|
+
...
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**When a skill auto-injects**: whenever `agent.yaml`'s `skills` lists that skill's id. Skills not in the list are invisible to the agent (but the user can still trigger via `/command` if registered).
|
|
215
|
+
|
|
216
|
+
## Placeholders (template variables)
|
|
217
|
+
|
|
218
|
+
SKILL.md body supports `{{var}}` placeholders, rendered on activation. AGENT.md uses the same syntax.
|
|
219
|
+
|
|
220
|
+
User command-line args additionally use the **standard** syntax `$ARGUMENTS` / `$1`–`$9` (quote-aware; `\$` escapes; `$5.00` / `$12` / `$PATH` are left alone). The two systems coexist without cross-translation: `$…` carries user args, `{{…}}` carries Halo-injected values. For object commands, the verb arrives as `$1`.
|
|
221
|
+
|
|
222
|
+
### Built-ins (no dot)
|
|
223
|
+
|
|
224
|
+
| Variable | Description |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `{{args}}` | User args from a slash command. Empty string when invoked via `activate_skill` |
|
|
227
|
+
| `{{workspace_root}}` | Current workspace absolute path |
|
|
228
|
+
| `{{working_dir}}` | Agent's working_dir (defaults to workspace_root) |
|
|
229
|
+
| `{{now}}` | ISO8601 timestamp |
|
|
230
|
+
| `{{user_name}}` / `{{ai_name}}` | From USER.md frontmatter |
|
|
231
|
+
| `{{agent_name}}` | Current agent's display name |
|
|
232
|
+
|
|
233
|
+
### Settings paths (dotted)
|
|
234
|
+
|
|
235
|
+
`{{<id>.params.<key>}}` looks up a declared param and substitutes its value. Only `params` paths are substituted — `secrets` paths and anything else (`general.*`, top-level keys) stay as literal text. The `<id>` is the namespace declared in `models/<provider>.yaml` or `skills/<skill-id>/config.yaml`.
|
|
236
|
+
|
|
237
|
+
**Short form inside SKILL.md** — drop the namespace; activation auto-qualifies it for you:
|
|
238
|
+
|
|
239
|
+
```markdown
|
|
240
|
+
---
|
|
241
|
+
name: Nano Banana Caption
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
Call API `{{params.base_url}}` with key `{{params.api_key}}`.
|
|
245
|
+
Max `{{params.max_files}}` files per run.
|
|
246
|
+
Files: {{args}}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
If the skill id is `nano-banana`, those become `{{nano-banana.params.base_url}}` / `{{nano-banana.params.api_key}}` / `{{nano-banana.params.max_files}}` before reaching the agent.
|
|
250
|
+
|
|
251
|
+
**`<skill-dir>/config.yaml`** declares what's needed:
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
params:
|
|
255
|
+
- key: api_key
|
|
256
|
+
description: Nano Banana API key
|
|
257
|
+
default: <<NANO_BANANA_KEY>>
|
|
258
|
+
secret: true
|
|
259
|
+
- key: base_url
|
|
260
|
+
description: API endpoint
|
|
261
|
+
default: https://api.nano-banana.example
|
|
262
|
+
- key: max_files
|
|
263
|
+
description: Max files per review pass
|
|
264
|
+
default: "10"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**`~/.halo/secrets/settings.yaml`** holds the values (created by the Settings UI):
|
|
268
|
+
|
|
269
|
+
```yaml
|
|
270
|
+
nano-banana:
|
|
271
|
+
params:
|
|
272
|
+
api_key: <<NANO_BANANA_KEY>>
|
|
273
|
+
base_url: https://api.nano-banana.example
|
|
274
|
+
max_files: 10
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Resolution rules
|
|
278
|
+
|
|
279
|
+
1. **Merge order**: workspace `<project>/.halo/settings.yaml` deep-merges over global `~/.halo/secrets/settings.yaml` (field-level merge, not block replacement)
|
|
280
|
+
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 `[md-vars] Unknown placeholder "…"` warning
|
|
282
|
+
4. **Key charset**: `[\w-]`, hyphens allowed (e.g. `nano-banana`)
|
|
283
|
+
|
|
284
|
+
### Why put credentials in env vars
|
|
285
|
+
|
|
286
|
+
So settings.yaml can go to git or be shared:
|
|
287
|
+
- `<<NANO_BANANA_KEY>>` only describes "there should be a key here", it doesn't expose the value
|
|
288
|
+
- Deploy with `export NANO_BANANA_KEY=xxx`; the agent only sees the real value at render time
|
|
289
|
+
- Unset env var → the agent sees the literal `<<NANO_BANANA_KEY>>`, making it obvious what's missing
|
|
290
|
+
|
|
291
|
+
### Two activation paths
|
|
292
|
+
|
|
293
|
+
- Skill-as-command (`/review xxx`): `{{args}}` = the user's args
|
|
294
|
+
- `activate_skill(skill_id)` tool: `{{args}}` = empty string
|
|
295
|
+
|
|
296
|
+
Settings and env placeholders render identically either way.
|
|
297
|
+
|
|
298
|
+
### AGENT.md placeholders
|
|
299
|
+
|
|
300
|
+
AGENT.md runs through the same renderer; on session start, placeholders in the agent personality are substituted before injection. Useful for wiring API config into an agent ("You use `{{weather-skill.params.endpoint}}`"). AGENT.md must use the **fully-qualified** form (no short-form rewrite happens here — that's a skill-only convenience).
|
|
301
|
+
|
|
302
|
+
Implementation: [md-vars.ts](../../../packages/server/src/prompts/md-vars.ts) `renderMdBody` — the shared entry point for both skill and agent rendering.
|