@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.
Files changed (296) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/admin-out/404.html +1 -0
  4. package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_buildManifest.js +1 -0
  5. package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_ssgManifest.js +1 -0
  6. package/admin-out/_next/static/chunks/1d2d5650-810a3ca1ae20cab5.js +18 -0
  7. package/admin-out/_next/static/chunks/200.3b8f8a74f005a134.js +1 -0
  8. package/admin-out/_next/static/chunks/220.3dc41cd6269b88e6.js +1 -0
  9. package/admin-out/_next/static/chunks/298-d6b224b569da4de0.js +1 -0
  10. package/admin-out/_next/static/chunks/304eda7d.b1ee7f523653e027.js +1 -0
  11. package/admin-out/_next/static/chunks/330.3b784cf0dcd69163.js +1 -0
  12. package/admin-out/_next/static/chunks/442.f4302087bddb1ec1.js +1 -0
  13. package/admin-out/_next/static/chunks/443.d1b289781793f8de.js +25 -0
  14. package/admin-out/_next/static/chunks/503-7c6d9935df6e0cea.js +1 -0
  15. package/admin-out/_next/static/chunks/533.5f2d4bfe3d076095.js +1 -0
  16. package/admin-out/_next/static/chunks/579.a2718ddd395a2f3a.js +1 -0
  17. package/admin-out/_next/static/chunks/635-3dd0043ed0520b9d.js +1 -0
  18. package/admin-out/_next/static/chunks/65.859665ba6717967a.js +1 -0
  19. package/admin-out/_next/static/chunks/661.a4fe93f3f2a5e871.js +1 -0
  20. package/admin-out/_next/static/chunks/688.4e014ac6cfc7e152.js +1 -0
  21. package/admin-out/_next/static/chunks/868.0c95dfe283400a00.js +1 -0
  22. package/admin-out/_next/static/chunks/app/_not-found/page-8e35399c8fd5016d.js +1 -0
  23. package/admin-out/_next/static/chunks/app/layout-58f773336e6fce61.js +1 -0
  24. package/admin-out/_next/static/chunks/app/page-cc8043335282d745.js +1 -0
  25. package/admin-out/_next/static/chunks/db534642.09d3c650ff587175.js +1 -0
  26. package/admin-out/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js +1 -0
  27. package/admin-out/_next/static/chunks/framework-c3031ae54a63fe16.js +1 -0
  28. package/admin-out/_next/static/chunks/main-14b48e3c2588354f.js +1 -0
  29. package/admin-out/_next/static/chunks/main-app-483b436873410ddc.js +1 -0
  30. package/admin-out/_next/static/chunks/pages/_app-b2fc6e5f920d1b1a.js +1 -0
  31. package/admin-out/_next/static/chunks/pages/_error-f3696a8f3d67d150.js +1 -0
  32. package/admin-out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  33. package/admin-out/_next/static/chunks/webpack-8bd7ef88a6527feb.js +1 -0
  34. package/admin-out/_next/static/css/659eccb5db697b76.css +1 -0
  35. package/admin-out/_next/static/css/8fce4e1a54dac12a.css +3 -0
  36. package/admin-out/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  37. package/admin-out/_next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  38. package/admin-out/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  39. package/admin-out/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  40. package/admin-out/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  41. package/admin-out/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  42. package/admin-out/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  43. package/admin-out/favicon.ico +0 -0
  44. package/admin-out/icon.svg +18 -0
  45. package/admin-out/index.html +1 -0
  46. package/admin-out/index.txt +23 -0
  47. package/admin-out/monaco/vs/_commonjsHelpers-CT9FvmAN.js +1 -0
  48. package/admin-out/monaco/vs/abap-D-t0cyap.js +1 -0
  49. package/admin-out/monaco/vs/apex-CcIm7xu6.js +1 -0
  50. package/admin-out/monaco/vs/assets/css.worker-HnVq6Ewq.js +93 -0
  51. package/admin-out/monaco/vs/assets/editor.worker-Be8ye1pW.js +26 -0
  52. package/admin-out/monaco/vs/assets/html.worker-B51mlPHg.js +470 -0
  53. package/admin-out/monaco/vs/assets/json.worker-DKiEKt88.js +58 -0
  54. package/admin-out/monaco/vs/assets/ts.worker-CMbG-7ft.js +67731 -0
  55. package/admin-out/monaco/vs/azcli-BA0tQDCg.js +1 -0
  56. package/admin-out/monaco/vs/basic-languages/monaco.contribution.js +1 -0
  57. package/admin-out/monaco/vs/bat-C397hTD6.js +1 -0
  58. package/admin-out/monaco/vs/bicep-DF5aW17k.js +2 -0
  59. package/admin-out/monaco/vs/cameligo-plsz8qhj.js +1 -0
  60. package/admin-out/monaco/vs/clojure-Y2auQMzK.js +1 -0
  61. package/admin-out/monaco/vs/coffee-Bu45yuWE.js +1 -0
  62. package/admin-out/monaco/vs/cpp-CkKPQIni.js +1 -0
  63. package/admin-out/monaco/vs/csharp-CX28MZyh.js +1 -0
  64. package/admin-out/monaco/vs/csp-D8uWnyxW.js +1 -0
  65. package/admin-out/monaco/vs/css-CaeNmE3S.js +3 -0
  66. package/admin-out/monaco/vs/cssMode-CjiAH6dQ.js +1 -0
  67. package/admin-out/monaco/vs/cypher-DVThT8BS.js +1 -0
  68. package/admin-out/monaco/vs/dart-CmGfCvrO.js +1 -0
  69. package/admin-out/monaco/vs/dockerfile-CZqqYdch.js +1 -0
  70. package/admin-out/monaco/vs/ecl-30fUercY.js +1 -0
  71. package/admin-out/monaco/vs/editor/editor.main.css +1 -0
  72. package/admin-out/monaco/vs/editor/editor.main.js +5 -0
  73. package/admin-out/monaco/vs/editor.api-CalNCsUg.js +903 -0
  74. package/admin-out/monaco/vs/elixir-xjPaIfzF.js +1 -0
  75. package/admin-out/monaco/vs/flow9-DqtmStfK.js +1 -0
  76. package/admin-out/monaco/vs/freemarker2-Cz_sV6Md.js +3 -0
  77. package/admin-out/monaco/vs/fsharp-BOMdg4U1.js +1 -0
  78. package/admin-out/monaco/vs/go-D_hbi-Jt.js +1 -0
  79. package/admin-out/monaco/vs/graphql-CKUU4kLG.js +1 -0
  80. package/admin-out/monaco/vs/handlebars-OwglfO-1.js +1 -0
  81. package/admin-out/monaco/vs/hcl-DTaboeZW.js +1 -0
  82. package/admin-out/monaco/vs/html-Pa1xEWsY.js +1 -0
  83. package/admin-out/monaco/vs/htmlMode-Bz67EXwp.js +1 -0
  84. package/admin-out/monaco/vs/ini-CsNwO04R.js +1 -0
  85. package/admin-out/monaco/vs/java-CI4ZMsH9.js +1 -0
  86. package/admin-out/monaco/vs/javascript-PczUCGdz.js +1 -0
  87. package/admin-out/monaco/vs/jsonMode-DULH5oaX.js +7 -0
  88. package/admin-out/monaco/vs/julia-BwzEvaQw.js +1 -0
  89. package/admin-out/monaco/vs/kotlin-IUYPiTV8.js +1 -0
  90. package/admin-out/monaco/vs/language/css/monaco.contribution.js +1 -0
  91. package/admin-out/monaco/vs/language/html/monaco.contribution.js +1 -0
  92. package/admin-out/monaco/vs/language/json/monaco.contribution.js +1 -0
  93. package/admin-out/monaco/vs/language/typescript/monaco.contribution.js +1 -0
  94. package/admin-out/monaco/vs/less-C0eDYdqa.js +2 -0
  95. package/admin-out/monaco/vs/lexon-iON-Kj97.js +1 -0
  96. package/admin-out/monaco/vs/liquid-DqKjdPGy.js +1 -0
  97. package/admin-out/monaco/vs/loader.js +1368 -0
  98. package/admin-out/monaco/vs/lspLanguageFeatures-kM9O9rjY.js +4 -0
  99. package/admin-out/monaco/vs/lua-DtygF91M.js +1 -0
  100. package/admin-out/monaco/vs/m3-CsR4AuFi.js +1 -0
  101. package/admin-out/monaco/vs/markdown-C_rD0bIw.js +1 -0
  102. package/admin-out/monaco/vs/mdx-DEWtB1K5.js +1 -0
  103. package/admin-out/monaco/vs/mips-CiYP61RB.js +1 -0
  104. package/admin-out/monaco/vs/monaco.contribution-D2OdxNBt.js +1 -0
  105. package/admin-out/monaco/vs/monaco.contribution-DO3azKX8.js +1 -0
  106. package/admin-out/monaco/vs/monaco.contribution-EcChJV6a.js +1 -0
  107. package/admin-out/monaco/vs/monaco.contribution-qLAYrEOP.js +1 -0
  108. package/admin-out/monaco/vs/msdax-C38-sJlp.js +1 -0
  109. package/admin-out/monaco/vs/mysql-CdtbpvbG.js +1 -0
  110. package/admin-out/monaco/vs/nls.messages-loader.js +1 -0
  111. package/admin-out/monaco/vs/nls.messages.cs.js.js +17 -0
  112. package/admin-out/monaco/vs/nls.messages.de.js.js +17 -0
  113. package/admin-out/monaco/vs/nls.messages.es.js.js +17 -0
  114. package/admin-out/monaco/vs/nls.messages.fr.js.js +15 -0
  115. package/admin-out/monaco/vs/nls.messages.it.js.js +15 -0
  116. package/admin-out/monaco/vs/nls.messages.ja.js.js +17 -0
  117. package/admin-out/monaco/vs/nls.messages.js.js +10 -0
  118. package/admin-out/monaco/vs/nls.messages.ko.js.js +25 -0
  119. package/admin-out/monaco/vs/nls.messages.pl.js.js +17 -0
  120. package/admin-out/monaco/vs/nls.messages.pt-br.js.js +6 -0
  121. package/admin-out/monaco/vs/nls.messages.ru.js.js +17 -0
  122. package/admin-out/monaco/vs/nls.messages.tr.js.js +15 -0
  123. package/admin-out/monaco/vs/nls.messages.zh-cn.js.js +17 -0
  124. package/admin-out/monaco/vs/nls.messages.zh-tw.js.js +15 -0
  125. package/admin-out/monaco/vs/objective-c-CntZFaHX.js +1 -0
  126. package/admin-out/monaco/vs/pascal-r6kuqfl_.js +1 -0
  127. package/admin-out/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
  128. package/admin-out/monaco/vs/perl-DABw_TcH.js +1 -0
  129. package/admin-out/monaco/vs/pgsql-me_jFXeX.js +1 -0
  130. package/admin-out/monaco/vs/php-D_kh-9LK.js +1 -0
  131. package/admin-out/monaco/vs/pla-VfZjczW0.js +1 -0
  132. package/admin-out/monaco/vs/postiats-BBSzz8Pk.js +1 -0
  133. package/admin-out/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
  134. package/admin-out/monaco/vs/powershell-B-7ap1zc.js +1 -0
  135. package/admin-out/monaco/vs/protobuf-BmtuEB1A.js +2 -0
  136. package/admin-out/monaco/vs/pug-BRpRNeEb.js +1 -0
  137. package/admin-out/monaco/vs/python-Cr0UkIbn.js +1 -0
  138. package/admin-out/monaco/vs/qsharp-BzsFaUU9.js +1 -0
  139. package/admin-out/monaco/vs/r-f8dDdrp4.js +1 -0
  140. package/admin-out/monaco/vs/razor-BYAHOTkz.js +1 -0
  141. package/admin-out/monaco/vs/redis-fvZQY4PI.js +1 -0
  142. package/admin-out/monaco/vs/redshift-45Et0LQi.js +1 -0
  143. package/admin-out/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
  144. package/admin-out/monaco/vs/ruby-CZO8zYTz.js +1 -0
  145. package/admin-out/monaco/vs/rust-Bfetafyc.js +1 -0
  146. package/admin-out/monaco/vs/sb-3GYllVck.js +1 -0
  147. package/admin-out/monaco/vs/scala-foMgrKo1.js +1 -0
  148. package/admin-out/monaco/vs/scheme-CHdMtr7p.js +1 -0
  149. package/admin-out/monaco/vs/scss-C1cmLt9V.js +3 -0
  150. package/admin-out/monaco/vs/shell-ClXCKCEW.js +1 -0
  151. package/admin-out/monaco/vs/solidity-MZ6ExpPy.js +1 -0
  152. package/admin-out/monaco/vs/sophia-DWkuSsPQ.js +1 -0
  153. package/admin-out/monaco/vs/sparql-AUGFYSyk.js +1 -0
  154. package/admin-out/monaco/vs/sql-32GpJSV2.js +1 -0
  155. package/admin-out/monaco/vs/st-CuDFIVZ_.js +1 -0
  156. package/admin-out/monaco/vs/swift-n-2HociN.js +3 -0
  157. package/admin-out/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
  158. package/admin-out/monaco/vs/tcl-D74tq1nH.js +1 -0
  159. package/admin-out/monaco/vs/tsMode-CZz1Umrk.js +11 -0
  160. package/admin-out/monaco/vs/twig-C6taOxMV.js +1 -0
  161. package/admin-out/monaco/vs/typescript-DfOrAzoV.js +1 -0
  162. package/admin-out/monaco/vs/typespec-D-PIh9Xw.js +1 -0
  163. package/admin-out/monaco/vs/vb-Dyb2648j.js +1 -0
  164. package/admin-out/monaco/vs/wgsl-BhLXMOR0.js +298 -0
  165. package/admin-out/monaco/vs/workers-DcJshg-q.js +1 -0
  166. package/admin-out/monaco/vs/xml-CdsdnY8S.js +1 -0
  167. package/admin-out/monaco/vs/yaml-DYGvmE88.js +1 -0
  168. package/bin/halo.js +9 -0
  169. package/bundled-docs/design/agent.md +145 -0
  170. package/bundled-docs/design/architecture.md +270 -0
  171. package/bundled-docs/design/background-dispatch.md +151 -0
  172. package/bundled-docs/design/command.md +58 -0
  173. package/bundled-docs/design/cron.md +88 -0
  174. package/bundled-docs/design/evolution.md +383 -0
  175. package/bundled-docs/design/express-self.md +145 -0
  176. package/bundled-docs/design/feishu.md +224 -0
  177. package/bundled-docs/design/halo-city.md +112 -0
  178. package/bundled-docs/design/prompt-system.md +411 -0
  179. package/bundled-docs/design/session.md +321 -0
  180. package/bundled-docs/design/slack.md +212 -0
  181. package/bundled-docs/design/storage.md +578 -0
  182. package/bundled-docs/design/telegram.md +161 -0
  183. package/bundled-docs/design/web.md +222 -0
  184. package/bundled-docs/design/wechat.md +146 -0
  185. package/bundled-docs/design/ws.md +144 -0
  186. package/bundled-docs/dev/acp-adapter.md +337 -0
  187. package/bundled-docs/dev/add-channel.md +376 -0
  188. package/bundled-docs/dev/add-model-provider.md +386 -0
  189. package/bundled-docs/dev/api.md +686 -0
  190. package/bundled-docs/dev/deploy.md +198 -0
  191. package/bundled-docs/dev/desktop-packaging.md +327 -0
  192. package/bundled-docs/dev/env.md +158 -0
  193. package/bundled-docs/dev/logging.md +85 -0
  194. package/bundled-docs/dev/previews.md +225 -0
  195. package/bundled-docs/dev/tools.md +331 -0
  196. package/bundled-docs/guide/README.md +29 -0
  197. package/bundled-docs/guide/agents.md +264 -0
  198. package/bundled-docs/guide/channels/README.md +62 -0
  199. package/bundled-docs/guide/channels/acp.md +150 -0
  200. package/bundled-docs/guide/channels/feishu.md +154 -0
  201. package/bundled-docs/guide/channels/slack.md +156 -0
  202. package/bundled-docs/guide/channels/telegram.md +112 -0
  203. package/bundled-docs/guide/channels/web.md +149 -0
  204. package/bundled-docs/guide/channels/wechat.md +110 -0
  205. package/bundled-docs/guide/chat.md +112 -0
  206. package/bundled-docs/guide/cli.md +197 -0
  207. package/bundled-docs/guide/getting-started.md +93 -0
  208. package/bundled-docs/guide/secrets-and-credentials.md +161 -0
  209. package/bundled-docs/guide/sessions.md +104 -0
  210. package/bundled-docs/guide/skills.md +302 -0
  211. package/bundled-docs/guide/testing-agents-and-skills.md +136 -0
  212. package/bundled-docs/guide/workspace.md +131 -0
  213. package/bundled-docs/plans/cron-tasks.md +362 -0
  214. package/bundled-docs/plans/design-suggestions.md +203 -0
  215. package/bundled-docs/plans/issue-session-deep-nesting.md +208 -0
  216. package/bundled-docs/plans/launch-plan.md +111 -0
  217. package/bundled-docs/plans/md-and-agent-refactor.md +197 -0
  218. package/bundled-docs/plans/remote-mac-worker.md +196 -0
  219. package/bundled-docs/plans/roadmap.md +40 -0
  220. package/bundled-docs/plans/self-evolution.md +1379 -0
  221. package/bundled-docs/plans/todo.md +32 -0
  222. package/bundled-docs/plans/weixin-channel.md +255 -0
  223. package/bundled-docs/requirements/agent.md +92 -0
  224. package/bundled-docs/requirements/chat.md +81 -0
  225. package/bundled-docs/requirements/command.md +130 -0
  226. package/bundled-docs/requirements/editor.md +82 -0
  227. package/bundled-docs/requirements/explorer.md +89 -0
  228. package/bundled-docs/requirements/overview.md +62 -0
  229. package/bundled-docs/requirements/session.md +73 -0
  230. package/bundled-docs/requirements/settings.md +224 -0
  231. package/bundled-docs/requirements/skill.md +125 -0
  232. package/bundled-docs/requirements/source-control.md +82 -0
  233. package/bundled-docs/requirements/terminal.md +118 -0
  234. package/bundled-docs/requirements/workspace.md +80 -0
  235. package/bundled-docs/test/docs-seeding.md +101 -0
  236. package/bundled-docs/test/session.md +374 -0
  237. package/dist/index.js +26750 -0
  238. package/package.json +67 -0
  239. package/templates/INSTRUCTIONS.md +25 -0
  240. package/templates/agents/__apply_agent__/AGENT.md +229 -0
  241. package/templates/agents/__apply_agent__/agent.yaml +30 -0
  242. package/templates/agents/__evo_agent__/AGENT.md +323 -0
  243. package/templates/agents/__evo_agent__/agent.yaml +29 -0
  244. package/templates/agents/__score__/AGENT.md +177 -0
  245. package/templates/agents/__score__/agent.yaml +29 -0
  246. package/templates/agents/deep-executor/AGENT.md +60 -0
  247. package/templates/agents/deep-executor/agent.yaml +25 -0
  248. package/templates/agents/default/AGENT.md +38 -0
  249. package/templates/agents/default/agent.yaml +51 -0
  250. package/templates/agents/executor/AGENT.md +38 -0
  251. package/templates/agents/executor/agent.yaml +25 -0
  252. package/templates/aliases.yaml +26 -0
  253. package/templates/canvas/self.html +660 -0
  254. package/templates/config.yaml +69 -0
  255. package/templates/models/anthropic.yaml +47 -0
  256. package/templates/models/aws-bedrock-claude-invoke.yaml +156 -0
  257. package/templates/models/aws-bedrock-mantle.yaml +89 -0
  258. package/templates/models/deepseek.yaml +62 -0
  259. package/templates/models/doubao.yaml +137 -0
  260. package/templates/models/hunyuan.yaml +56 -0
  261. package/templates/models/kimi.yaml +41 -0
  262. package/templates/models/mimo-token-plan-china.yaml +61 -0
  263. package/templates/models/minimax.yaml +80 -0
  264. package/templates/models/openai.yaml +51 -0
  265. package/templates/models/qwen.yaml +78 -0
  266. package/templates/optional-skills/tavily-web-search/SKILL.md +91 -0
  267. package/templates/optional-skills/tavily-web-search/config.yaml +10 -0
  268. package/templates/prompts/all/TOOL_GUIDELINES.md +35 -0
  269. package/templates/prompts/all/TOOL_SHELL.md +4 -0
  270. package/templates/prompts/all/TOOL_SHELL.windows.md +7 -0
  271. package/templates/prompts/bootstrap/BOOTSTRAP.md +26 -0
  272. package/templates/prompts/root/WORKSPACE_MEMORY.md +11 -0
  273. package/templates/schema.sql +30 -0
  274. package/templates/skills/acp/SKILL.md +236 -0
  275. package/templates/skills/acp/templates/SKILL.md.tmpl +74 -0
  276. package/templates/skills/acp/templates/ask.py +176 -0
  277. package/templates/skills/acp/templates/config.yaml.tmpl +33 -0
  278. package/templates/skills/agent/SKILL.md +128 -0
  279. package/templates/skills/aws-knowledge/SKILL.md +82 -0
  280. package/templates/skills/aws-knowledge/scripts/aws_kb.py +61 -0
  281. package/templates/skills/cron/SKILL.md +217 -0
  282. package/templates/skills/cron/manage-cron.py +434 -0
  283. package/templates/skills/halo/SKILL.md +98 -0
  284. package/templates/skills/nova-web-search/SKILL.md +65 -0
  285. package/templates/skills/self/SKILL.md +139 -0
  286. package/templates/skills/send-file/SKILL.md +28 -0
  287. package/templates/skills/send-file/feishu.md +16 -0
  288. package/templates/skills/send-file/slack.md +7 -0
  289. package/templates/skills/send-file/telegram.md +9 -0
  290. package/templates/skills/send-file/web.md +6 -0
  291. package/templates/skills/send-file/wechat.md +8 -0
  292. package/templates/skills/skill/SKILL.md +131 -0
  293. package/templates/skills/workspace/SKILL.md +28 -0
  294. package/templates/skills/workspace/setup-tidy.md +265 -0
  295. package/templates/skills/workspace/share.md +187 -0
  296. 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.