@turmind/halo 1.5.6 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin-out/404.html +1 -1
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +1 -1
- package/bundled-docs/design/agent.md +7 -7
- package/bundled-docs/design/architecture.md +12 -11
- package/bundled-docs/design/storage.md +2 -1
- package/bundled-docs/dev/add-model-provider.md +33 -24
- package/bundled-docs/dev/deploy.md +1 -1
- package/bundled-docs/dev/desktop-packaging.md +4 -3
- package/bundled-docs/dev/env.md +1 -1
- package/bundled-docs/guide/agents.md +2 -2
- package/bundled-docs/guide/cli.md +5 -4
- package/bundled-docs/guide/getting-started.md +1 -1
- package/bundled-docs/requirements/settings.md +2 -1
- package/dist/index.js +409 -388
- package/package.json +1 -1
- package/templates/models/anthropic.yaml +1 -0
- package/templates/models/aws-bedrock-claude-invoke.yaml +1 -0
- package/templates/models/aws-bedrock-mantle.yaml +1 -0
- package/templates/models/aws-bedrock-openai.yaml +1 -0
- package/templates/models/deepseek.yaml +1 -0
- package/templates/models/doubao.yaml +1 -0
- package/templates/models/hunyuan.yaml +1 -0
- package/templates/models/kimi.yaml +1 -0
- package/templates/models/mimo-token-plan-china.yaml +1 -0
- package/templates/models/minimax.yaml +1 -0
- package/templates/models/openai.yaml +1 -0
- package/templates/models/qwen.yaml +1 -0
- package/templates/models/zhipu.yaml +1 -0
- /package/admin-out/_next/static/{visjvN510yFK-Ff5zwx-0 → rTD3zkMvzwtqUwzhz6QJH}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{visjvN510yFK-Ff5zwx-0 → rTD3zkMvzwtqUwzhz6QJH}/_ssgManifest.js +0 -0
package/admin-out/404.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--rTD3zkMvzwtqUwzhz6QJH--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/e4af272ccee01ff0-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/b8c87b919758c52b.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-edf248e07333566d.js"/><script src="/_next/static/chunks/1b86ba38-c29611e086b2ce25.js" async=""></script><script src="/_next/static/chunks/698-19872069fed27164.js" async=""></script><script src="/_next/static/chunks/main-app-340a5b58ed229c4d.js" async=""></script><script src="/_next/static/chunks/989-e0faeff1c4a0b0b9.js" async=""></script><script src="/_next/static/chunks/app/layout-5ec6c5f713a8562b.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>Halo - Multi-Agent Workspace</title><meta name="description" content="Multi-agent collaboration workspace for complex project delivery"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/><link rel="icon" href="/icon.svg?3a6863548daca964" type="image/svg+xml" sizes="any"/><script>try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}</script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_f367f3 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-edf248e07333566d.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[58321,[\"989\",\"static/chunks/989-e0faeff1c4a0b0b9.js\",\"177\",\"static/chunks/app/layout-5ec6c5f713a8562b.js\"],\"ThemeProvider\"]\n3:I[24554,[\"989\",\"static/chunks/989-e0faeff1c4a0b0b9.js\",\"177\",\"static/chunks/app/layout-5ec6c5f713a8562b.js\"],\"AppI18nProvider\"]\n4:I[63693,[],\"\"]\n5:I[74713,[],\"\"]\n6:I[23632,[],\"OutletBoundary\"]\n8:I[86889,[],\"AsyncMetadataOutlet\"]\na:I[23632,[],\"ViewportBoundary\"]\nc:I[23632,[],\"MetadataBoundary\"]\nd:\"$Sreact.suspense\"\nf:I[1935,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/b8c87b919758c52b.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"rTD3zkMvzwtqUwzhz6QJH\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/b8c87b919758c52b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_f367f3 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L6\",null,{\"children\":[\"$L7\",[\"$\",\"$L8\",null,{\"promise\":\"$@9\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$d\",null,{\"fallback\":null,\"children\":\"$Le\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$f\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n7:null\n"])</script><script>self.__next_f.push([1,"10:I[88809,[],\"IconMark\"]\n9:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Halo - Multi-Agent Workspace\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Multi-agent collaboration workspace for complex project delivery\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon.svg?3a6863548daca964\",\"type\":\"image/svg+xml\",\"sizes\":\"any\"}],[\"$\",\"$L10\",\"4\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"e:\"$9:metadata\"\n"])</script></body></html>
|
package/admin-out/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--rTD3zkMvzwtqUwzhz6QJH--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/e4af272ccee01ff0-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/b8c87b919758c52b.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/659eccb5db697b76.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-edf248e07333566d.js"/><script src="/_next/static/chunks/1b86ba38-c29611e086b2ce25.js" async=""></script><script src="/_next/static/chunks/698-19872069fed27164.js" async=""></script><script src="/_next/static/chunks/main-app-340a5b58ed229c4d.js" async=""></script><script src="/_next/static/chunks/989-e0faeff1c4a0b0b9.js" async=""></script><script src="/_next/static/chunks/app/layout-5ec6c5f713a8562b.js" async=""></script><script src="/_next/static/chunks/1d2d5650-d8d130c85727fdbb.js" async=""></script><script src="/_next/static/chunks/422-cc74b1fdd3af5415.js" async=""></script><script src="/_next/static/chunks/app/page-44f707ea8dad57da.js" async=""></script><meta name="next-size-adjust" content=""/><title>Halo - Multi-Agent Workspace</title><meta name="description" content="Multi-agent collaboration workspace for complex project delivery"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/><link rel="icon" href="/icon.svg?3a6863548daca964" type="image/svg+xml" sizes="any"/><script>try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}</script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_f367f3 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><div class="flex h-screen w-screen items-center justify-center bg-[var(--background)]"><div class="text-sm text-[var(--muted-foreground)]">Loading...</div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-edf248e07333566d.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[58321,[\"989\",\"static/chunks/989-e0faeff1c4a0b0b9.js\",\"177\",\"static/chunks/app/layout-5ec6c5f713a8562b.js\"],\"ThemeProvider\"]\n3:I[24554,[\"989\",\"static/chunks/989-e0faeff1c4a0b0b9.js\",\"177\",\"static/chunks/app/layout-5ec6c5f713a8562b.js\"],\"AppI18nProvider\"]\n4:I[63693,[],\"\"]\n5:I[74713,[],\"\"]\n6:I[5444,[],\"ClientPageRoot\"]\n7:I[73711,[\"560\",\"static/chunks/1d2d5650-d8d130c85727fdbb.js\",\"422\",\"static/chunks/422-cc74b1fdd3af5415.js\",\"989\",\"static/chunks/989-e0faeff1c4a0b0b9.js\",\"974\",\"static/chunks/app/page-44f707ea8dad57da.js\"],\"default\"]\na:I[23632,[],\"OutletBoundary\"]\nc:I[86889,[],\"AsyncMetadataOutlet\"]\ne:I[23632,[],\"ViewportBoundary\"]\n10:I[23632,[],\"MetadataBoundary\"]\n11:\"$Sreact.suspense\"\n13:I[1935,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/b8c87b919758c52b.css\",\"style\"]\n:HL[\"/_next/static/css/659eccb5db697b76.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"rTD3zkMvzwtqUwzhz6QJH\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/b8c87b919758c52b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_f367f3 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]}]]}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L6\",null,{\"Component\":\"$7\",\"searchParams\":{},\"params\":{},\"promises\":[\"$@8\",\"$@9\"]}],[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/659eccb5db697b76.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"$La\",null,{\"children\":[\"$Lb\",[\"$\",\"$Lc\",null,{\"promise\":\"$@d\"}]]}]]}],{},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[[\"$\",\"$Le\",null,{\"children\":\"$Lf\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$L10\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$11\",null,{\"fallback\":null,\"children\":\"$L12\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$13\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:{}\n9:\"$0:f:0:1:2:children:1:props:children:0:props:params\"\n"])</script><script>self.__next_f.push([1,"f:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nb:null\n"])</script><script>self.__next_f.push([1,"14:I[88809,[],\"IconMark\"]\nd:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Halo - Multi-Agent Workspace\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Multi-agent collaboration workspace for complex project delivery\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon.svg?3a6863548daca964\",\"type\":\"image/svg+xml\",\"sizes\":\"any\"}],[\"$\",\"$L14\",\"4\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"12:\"$d:metadata\"\n"])</script></body></html>
|
package/admin-out/index.txt
CHANGED
|
@@ -14,7 +14,7 @@ e:I[23632,[],"ViewportBoundary"]
|
|
|
14
14
|
:HL["/_next/static/media/e4af272ccee01ff0-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
15
15
|
:HL["/_next/static/css/b8c87b919758c52b.css","style"]
|
|
16
16
|
:HL["/_next/static/css/659eccb5db697b76.css","style"]
|
|
17
|
-
0:{"P":null,"b":"
|
|
17
|
+
0:{"P":null,"b":"rTD3zkMvzwtqUwzhz6QJH","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/b8c87b919758c52b.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"dark","suppressHydrationWarning":true,"children":[["$","head",null,{"children":["$","script",null,{"dangerouslySetInnerHTML":{"__html":"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}"}}]}],["$","body",null,{"className":"__variable_f367f3 font-sans antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L6",null,{"Component":"$7","searchParams":{},"params":{},"promises":["$@8","$@9"]}],[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/659eccb5db697b76.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$La",null,{"children":["$Lb",["$","$Lc",null,{"promise":"$@d"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Le",null,{"children":"$Lf"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$L10",null,{"children":["$","div",null,{"hidden":true,"children":["$","$11",null,{"fallback":null,"children":"$L12"}]}]}]]}],false]],"m":"$undefined","G":["$13",[]],"s":false,"S":true}
|
|
18
18
|
8:{}
|
|
19
19
|
9:"$0:f:0:1:2:children:1:props:children:0:props:params"
|
|
20
20
|
f:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
|
@@ -4,11 +4,11 @@ Agent instances, lifecycle, tools, and message formats.
|
|
|
4
4
|
|
|
5
5
|
## Agent instance
|
|
6
6
|
|
|
7
|
-
Each session is 1:1 with a `ModelRuntime`. `ModelRuntime` is a provider-agnostic interface; `agent.yaml`'s `model.provider` selects the concrete implementation.
|
|
7
|
+
Each session is 1:1 with a `ModelRuntime`. `ModelRuntime` is a provider-agnostic interface; `agent.yaml`'s `model.provider` names a provider, and that provider yaml's `runtime:` field selects the concrete implementation.
|
|
8
8
|
|
|
9
9
|
**Files**:
|
|
10
|
-
- [packages/server/src/agents/model-runtime.ts](../../../packages/server/src/agents/model-runtime.ts) — the interface
|
|
11
|
-
- [packages/server/src/agents/bedrock-agent.ts](../../../packages/server/src/agents/bedrock-agent.ts) — the `aws-bedrock-claude-invoke`
|
|
10
|
+
- [packages/server/src/agents/model-runtime.ts](../../../packages/server/src/agents/model-runtime.ts) — the interface, `resolveProviderRuntime(providerId)` (provider yaml → `runtime` name) and the `createModelRuntime(runtime, cfg)` dispatcher
|
|
11
|
+
- [packages/server/src/agents/bedrock-agent.ts](../../../packages/server/src/agents/bedrock-agent.ts) — the `bedrock-invoke` runtime, used by the `aws-bedrock-claude-invoke` provider (Bedrock InvokeModelWithResponseStream, streaming)
|
|
12
12
|
- [packages/server/src/agents/anthropic-stream.ts](../../../packages/server/src/agents/anthropic-stream.ts) — `AnthropicStreamAccumulator` (stream events → `ModelCallResult`, shared by every Anthropic-Messages provider) + `fetchAnthropicStream` (the HTTP + SSE `callModel` body of anthropic / mimo / minimax / qwen); `sse.ts` is the generic SSE → JSON reader underneath
|
|
13
13
|
|
|
14
14
|
### State
|
|
@@ -31,9 +31,9 @@ Each provider's SDK client and config details are encapsulated inside its runtim
|
|
|
31
31
|
|
|
32
32
|
### Adding a new provider
|
|
33
33
|
|
|
34
|
-
1. Write the model manifest and capabilities at `~/.halo/global/models/<providerId>.yaml` (see `aws-bedrock-claude-invoke.yaml` for shape)
|
|
35
|
-
2.
|
|
36
|
-
3. Nothing else changes — session-manager
|
|
34
|
+
1. Write the model manifest and capabilities at `~/.halo/global/models/<providerId>.yaml` (see `aws-bedrock-claude-invoke.yaml` for shape), with `runtime:` naming the implementation
|
|
35
|
+
2. Reusing an existing wire protocol (e.g. another Anthropic- or OpenAI-compatible gateway)? Set `runtime:` to that runtime's name (`anthropic-messages`, `openai-chat`, …) and stop. A new protocol: add its name to `MODEL_RUNTIME_NAMES` and a case to the switch in `model-runtime.ts` returning a class that implements `ModelRuntime`
|
|
36
|
+
3. Nothing else changes — session-manager resolves `agent.yaml`'s `model.provider` → the yaml's `runtime` → the class
|
|
37
37
|
|
|
38
38
|
## Agent build pipeline (SessionManager.buildAgentInstance)
|
|
39
39
|
|
|
@@ -44,7 +44,7 @@ Each provider's SDK client and config details are encapsulated inside its runtim
|
|
|
44
44
|
5. Inject the MD layers (USER.md / AGENT.md / INSTRUCTIONS.md chain / INDEX.md)
|
|
45
45
|
6. **Render AGENT.md placeholders**: `{{var}}` / `{{<skill-id>.params.<key>}}` / `<<ENV>>` substitution (see [prompt-system.md](prompt-system.md#placeholder-rendering-pipeline))
|
|
46
46
|
7. Inject skill metadata (use `activate_skill` to load the full body on demand)
|
|
47
|
-
8. `createModelRuntime(providerId, {modelId, endpoint, systemPrompt, tools, ...})`
|
|
47
|
+
8. `createModelRuntime(resolveProviderRuntime(providerId), {modelId, endpoint, systemPrompt, tools, ...})` — the provider yaml must exist and carry a known `runtime:`, else the build throws; credentials (`resolveApiKey` / `resolveAwsCredentials`) stay keyed by the provider id
|
|
48
48
|
|
|
49
49
|
See [prompt-system.md](prompt-system.md).
|
|
50
50
|
|
|
@@ -63,7 +63,7 @@ Five concerns were split into their own files (SessionManager keeps thin pass-th
|
|
|
63
63
|
|
|
64
64
|
### ModelRuntime — LLM interaction layer (provider-agnostic)
|
|
65
65
|
|
|
66
|
-
Files: `agents/model-runtime.ts` (interface + dispatcher), `agents/bedrock-agent.ts` (the `aws-bedrock-claude-invoke`
|
|
66
|
+
Files: `agents/model-runtime.ts` (interface + dispatcher), `agents/bedrock-agent.ts` (the `bedrock-invoke` runtime behind the `aws-bedrock-claude-invoke` provider).
|
|
67
67
|
|
|
68
68
|
**Interface**:
|
|
69
69
|
|
|
@@ -72,23 +72,24 @@ interface ModelRuntime {
|
|
|
72
72
|
messages: AnthropicMessage[]
|
|
73
73
|
run(input, opts?): AsyncGenerator<AgentEvent>
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
resolveProviderRuntime(providerId: string): string // provider yaml → `runtime`
|
|
76
|
+
createModelRuntime(runtime: string, cfg: ModelRuntimeConfig): ModelRuntime
|
|
76
77
|
```
|
|
77
78
|
|
|
78
|
-
**Dispatch**: `agent.yaml`'s `model.provider` is looked up against `~/.halo/global/models/<providerId>.yaml`; the dispatcher's switch
|
|
79
|
+
**Dispatch**: `agent.yaml`'s `model.provider` is looked up against `~/.halo/global/models/<providerId>.yaml`; that yaml's `runtime:` field names the implementation (wire protocol), and the dispatcher's switch matches on the runtime name only. The provider `id` stays the identity (model picker, `model.provider`, `<id>.secrets.*`). Missing yaml / missing `runtime:` / unknown runtime → the session build throws.
|
|
79
80
|
|
|
80
81
|
**Modality capabilities**: Each model in the manifest declares `capabilities.image` / `capabilities.video` / `capabilities.audio` (boolean). SessionManager checks `modelSupportsImage()` at session creation in two places: (1) `createWorkspaceTools()` is passed `supportsVision` so the `view_image` tool is dropped from the tool list when the model can't ingest vision blocks — no exposed tool, no errant call, no provider 400; (2) user-supplied images on inbound messages are stripped at `buildInput()` with a text notice. Query functions: `config.ts` exports `modelSupportsImage()` / `modelSupportsVideo()` / `modelSupportsAudio()`.
|
|
81
82
|
|
|
82
|
-
**Current providers
|
|
83
|
-
- `aws-bedrock-claude-invoke` → `BedrockAgent` (uses the Bedrock InvokeModelWithResponseStream API — text / thinking chunks reach the UI as they are generated)
|
|
84
|
-
- `anthropic` / `mimo-token-plan-china` → `AnthropicAgent`, `minimax` → `MiniMaxAgent`, `qwen` → `QwenAgent` (Anthropic Messages API over HTTP with `stream: true`; the SSE is folded by the same `AnthropicStreamAccumulator` as Bedrock via `fetchAnthropicStream` in `anthropic-stream.ts`, so they stream too)
|
|
85
|
-
- The OpenAI-family (`openai`
|
|
86
|
-
- Mantle (`aws-bedrock-mantle` / `aws-bedrock-openai`) → `MantleAgent`, OpenAI Responses API over HTTP with `stream: true`; `readResponsesStream` (module-level in `mantle-agent.ts`) reports `response.output_text.delta` frames live and takes the full final response object off the terminal `response.completed` / `response.incomplete` frame, so the existing `output[]` parse runs unchanged (bedrock-mantle sends no `[DONE]`, bedrock-runtime does — `readSseJson` skips it either way)
|
|
83
|
+
**Current providers** (provider id → `runtime` → class):
|
|
84
|
+
- `aws-bedrock-claude-invoke` → `bedrock-invoke` → `BedrockAgent` (uses the Bedrock InvokeModelWithResponseStream API — text / thinking chunks reach the UI as they are generated)
|
|
85
|
+
- `anthropic` / `mimo-token-plan-china` → `anthropic-messages` → `AnthropicAgent`, `minimax` → `MiniMaxAgent`, `qwen` → `QwenAgent` (vendor runtimes keep the vendor name; Anthropic Messages API over HTTP with `stream: true`; the SSE is folded by the same `AnthropicStreamAccumulator` as Bedrock via `fetchAnthropicStream` in `anthropic-stream.ts`, so they stream too)
|
|
86
|
+
- The OpenAI-family (`openai` → `openai-chat` → `OpenAIAgent`; `deepseek` / `kimi` / `zhipu` / `doubao` / `hunyuan` → same-named vendor runtimes) → `chat/completions` over HTTP with `stream: true` + `stream_options.include_usage`; the `chat.completion.chunk` SSE is folded back into the non-streaming `choices[0].message` shape by `fetchChatCompletionStream` in `openai-chat-stream.ts` (a transport helper, not a base class; message/tool conversion and result parsing are shared via `openai-chat-format.ts`, while each agent keeps its own request body / thinking / usage math), so they stream too
|
|
87
|
+
- Mantle (`aws-bedrock-mantle` / `aws-bedrock-openai`) → `bedrock-mantle` → `MantleAgent`, OpenAI Responses API over HTTP with `stream: true`; `readResponsesStream` (module-level in `mantle-agent.ts`) reports `response.output_text.delta` frames live and takes the full final response object off the terminal `response.completed` / `response.incomplete` frame, so the existing `output[]` parse runs unchanged (bedrock-mantle sends no `[DONE]`, bedrock-runtime does — `readSseJson` skips it either way)
|
|
87
88
|
- Every provider streams; the `onDelta` hook is still optional per provider, so a future non-streaming one just ignores it
|
|
88
89
|
|
|
89
90
|
**Adding a new provider**:
|
|
90
|
-
1. Add a manifest at `models/<providerId>.yaml` (include modality flags)
|
|
91
|
-
2.
|
|
91
|
+
1. Add a manifest at `models/<providerId>.yaml` (include modality flags) with `runtime:` — an existing runtime name when the wire protocol is already supported (then you're done)
|
|
92
|
+
2. For a new protocol: add the name to `MODEL_RUNTIME_NAMES` and a case in `model-runtime.ts` returning your runtime class
|
|
92
93
|
3. Implement `callModel(signal, onDelta?)` (returns `Promise<ModelCallResult>`; call `onDelta` per chunk if the API streams, otherwise ignore it) and maintain `messages`
|
|
93
94
|
|
|
94
95
|
**Core loop** (`AgentLoop.run()`, shared by all providers):
|
|
@@ -254,7 +255,7 @@ SQLite only holds metadata indexes; all content lives on the filesystem.
|
|
|
254
255
|
| `~/.halo/global/skills/{id}/SKILL.md` | SkillRoutes | Skill definition (global scope) |
|
|
255
256
|
| `~/.halo/secrets/settings.yaml` | SettingsRoutes | Global settings |
|
|
256
257
|
| `<project>/.halo/settings.yaml` | SettingsRoutes | Per-project overrides |
|
|
257
|
-
| `~/.halo/global/models/<provider>.yaml` | Manual edit | Model registry — one file per provider, scanned at startup
|
|
258
|
+
| `~/.halo/global/models/<provider>.yaml` | Manual edit | Model registry — one file per provider, scanned at startup; its `runtime:` field picks the implementation class |
|
|
258
259
|
| `~/.halo/global/prompts/{bootstrap,all,root}/*.md` | init.ts seed + user | System prompts |
|
|
259
260
|
| `~/.halo/global/logs/server.log` | Logger | Server logs (10 MB rotation) |
|
|
260
261
|
| `~/.halo/secrets/channels/channels.db` | All channels | Unified channel accounts (Web, Telegram, WeChat, Slack, Feishu, WeCom) — see [storage.md](storage.md#channel_accounts) |
|
|
@@ -425,6 +425,7 @@ Path: `~/.halo/global/models/<providerId>.yaml` — one file per provider. All f
|
|
|
425
425
|
```yaml
|
|
426
426
|
# aws-bedrock-claude-invoke.yaml
|
|
427
427
|
id: aws-bedrock-claude-invoke # required, must match filename and agent.yaml model.provider
|
|
428
|
+
runtime: bedrock-invoke # required, implementation class (wire protocol) — see below
|
|
428
429
|
displayName: AWS Bedrock Claude (Invoke API) # shown in UI
|
|
429
430
|
displayName_zh: AWS Bedrock Claude(Invoke API) # optional zh override
|
|
430
431
|
description: Invokes Bedrock via InvokeModel (non-streaming)
|
|
@@ -498,7 +499,7 @@ Existing agent yaml values always win over these defaults — switching provider
|
|
|
498
499
|
|
|
499
500
|
`agent.yaml`'s `model.provider` must match a provider's `id` — otherwise session spawn fails.
|
|
500
501
|
|
|
501
|
-
Routing: `
|
|
502
|
+
Routing: `id` is the provider identity (model picker, `agent.yaml` `model.provider`, `<id>.secrets.*`); `runtime:` picks the implementation class. `resolveProviderRuntime(providerId)` reads `runtime` from the registry entry, then `createModelRuntime(runtime, cfg)` dispatches on the runtime name only — the provider id is never matched against it. Runtimes: `bedrock-invoke` (BedrockAgent), `bedrock-mantle` (MantleAgent, OpenAI Responses), `anthropic-messages` (AnthropicAgent), `openai-chat` (OpenAIAgent, Chat Completions), and the vendor subclasses under their own names (`kimi`, `deepseek`, `minimax`, `qwen`, `hunyuan`, `doubao`, `zhipu`). Several providers can share one runtime (`anthropic` + `mimo-token-plan-china`; `aws-bedrock-mantle` + `aws-bedrock-openai`). No yaml for the id, no `runtime:` (a seed older than template v82), or an unknown name → session build throws naming the provider and yaml path. See [design/agent.md](../design/agent.md#agent-instance).
|
|
502
503
|
|
|
503
504
|
## Skill schema (`config.yaml`)
|
|
504
505
|
|
|
@@ -4,33 +4,37 @@ How to wire up a new LLM provider (OpenAI-compatible, a local model server — a
|
|
|
4
4
|
|
|
5
5
|
## Reality check
|
|
6
6
|
|
|
7
|
-
Halo is **not a plugin system** for providers today.
|
|
7
|
+
Halo is **not a plugin system** for providers today. A provider yaml's `id` is the provider identity (model picker, `agent.yaml` `model.provider`, settings namespace `<id>.secrets.*`); its `runtime:` field names the implementation class — the wire protocol. `createModelRuntime` (in `model-runtime.ts`) switches on the **runtime name only**; the provider id is never matched against it.
|
|
8
|
+
|
|
9
|
+
A provider whose upstream speaks a protocol Halo already has (Anthropic Messages, OpenAI Chat Completions, …) is **one new YAML manifest** with `runtime:` set to that name — no code. A new wire protocol means:
|
|
8
10
|
- One new runtime class extending `AgentLoop`
|
|
9
11
|
- One new YAML manifest on disk
|
|
10
|
-
- One case added to
|
|
11
|
-
- API key wiring in
|
|
12
|
+
- One name added to `MODEL_RUNTIME_NAMES` + one case added to the switch in `model-runtime.ts`
|
|
13
|
+
- API key wiring is automatic: `secrets:` in the manifest (see step 4)
|
|
12
14
|
|
|
13
|
-
No recompile of the frontend — the Form view's Provider dropdown reads the manifest at startup and picks up the new provider automatically. Server code
|
|
15
|
+
No recompile of the frontend — the Form view's Provider dropdown reads the manifest at startup and picks up the new provider automatically. Server code needs a rebuild only when you added a runtime.
|
|
14
16
|
|
|
15
17
|
Total effort: typically a few hours depending on how weird the upstream API is about tool use and response format.
|
|
16
18
|
|
|
17
19
|
### Existing providers
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
21
|
+
Runtime names (`runtime:` value → class): `bedrock-invoke` → `BedrockAgent`, `bedrock-mantle` → `MantleAgent` (OpenAI Responses API), `anthropic-messages` → `AnthropicAgent`, `openai-chat` → `OpenAIAgent` (Chat Completions); the vendor subclasses keep their vendor name (`kimi`, `deepseek`, `minimax`, `qwen`, `hunyuan`, `doubao`, `zhipu`). No implementation class reads the provider id — the endpoint (required in `agent.yaml`) carries everything host-specific.
|
|
22
|
+
|
|
23
|
+
| Provider ID | `runtime` | Class | File | Notes |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| `aws-bedrock-claude-invoke` | `bedrock-invoke` | `BedrockAgent` | `bedrock-agent.ts` | Anthropic Messages API via AWS SDK |
|
|
26
|
+
| `kimi` | `kimi` | `KimiAgent` | `kimi-agent.ts` | OpenAI-compatible, streaming via `fetchChatCompletionStream` (usage arrives in its own `choices: []` chunk), thinking (reasoning_content). Cached tokens via `cachedPromptTokens` (every known usage key — which one Kimi reports isn't pinned). Two API generations, branched on modelId: **K3** — vision (base64 data URLs only, no public-URL input), thinking always-on server-side, sole knob is top-level `reasoning_effort` (enum currently just `"max"`); **K2.6** — no vision, no `reasoning_effort` field at all, only the `thinking:{type:'enabled'|'disabled'}` toggle. Never send the K2.x `thinking` parameter to K3 |
|
|
27
|
+
| `deepseek` | `deepseek` | `DeepSeekAgent` | `deepseek-agent.ts` | OpenAI-compatible, streaming via `fetchChatCompletionStream` (first chunk has `content: null` + `reasoning_content: ''`; the usage chunk still carries `choices`), thinking (reasoning_content). Vision per model: `deepseek-flash` (V4.1-Flash) sees `image_url` data URLs; `deepseek-v4-pro` accepts the block but ignores it (live-probed 2026-09-24 with a 64×64 blue PNG) — registry `image` flag gates it |
|
|
28
|
+
| `minimax` | `minimax` | `MiniMaxAgent` | `minimax-agent.ts` | Anthropic Messages API (x-api-key), streaming via `fetchAnthropicStream` — note `message_start.usage` is all zeros and the real input / cache counts arrive in `message_delta.usage` (the accumulator takes them from there). Two thinking schemes, branched on registry `thinking.mode`: **M3** (default) — vision, thinking off by default and enabled via `thinking:{type:'adaptive'}` (no levels, no budget_tokens), no `promptCaching` in the manifest on purpose (official docs scope `cache_control` to M2.x only; M3 has passive automatic caching); **M2.7** — no vision, thinking always-on (manual budget_tokens), prompt caching (5m, M2.7 doesn't report cache_creation_input_tokens) |
|
|
29
|
+
| `qwen` | `qwen` | `QwenAgent` | `qwen-agent.ts` | Aliyun Bailian Anthropic-compatible API, streaming via `fetchAnthropicStream` (SSE frames come as `data:{…}` with no space after the colon — the reader accepts both), thinking enable/disable both honored, prompt caching (5m only), vision on Plus, max no vision |
|
|
30
|
+
| `hunyuan` | `hunyuan` | `HunyuanAgent` | `hunyuan-agent.ts` | Tencent Hy3 preview via OpenAI-compatible (tokenhub.tencentmaas.com), streaming via `fetchChatCompletionStream` (`stream_options.include_usage` accepted; usage in its own `choices: []` chunk), reasoning_effort low/high (no_think = omit), automatic prompt caching (cached_tokens), no vision |
|
|
31
|
+
| `doubao` | `doubao` | `DoubaoAgent` | `doubao-agent.ts` | ByteDance Doubao Seed-2.0 (pro/lite/mini/code) on Volcengine Ark, OpenAI-compatible, streaming via `fetchChatCompletionStream` (usage in its own `choices: []` chunk) + `thinking:{type:'enabled'/'disabled'}` (no `auto`), automatic prompt caching, no vision |
|
|
32
|
+
| `zhipu` | `zhipu` | `ZhipuAgent` | `zhipu-agent.ts` | Zhipu GLM-5.x on open.bigmodel.cn, OpenAI-compatible, streaming via `fetchChatCompletionStream` (tool-call arguments arrive complete in one fragment; usage chunk carries `choices`). Output cap via `max_tokens` only (`max_completion_tokens` is silently ignored). Thinking branched on modelId: **glm-5.3\*** always thinks — always sends `thinking:{type:'enabled',clear_thinking:false}`, `reasoning_effort` only when low/high/max (`disabled` / other efforts 400); other ids (glm-5.2) honour `thinking:{type:'disabled'}`. Vision per model: `glm-5.3-flash` / `glm-5.3-flashx` see `image_url` data URLs; `glm-5.3` / `glm-5.2` are text-only and 400 on any image part (code 1210, classified `multimodal_4xx` so leftover history images degrade). Automatic caching, `prompt_tokens_details.cached_tokens` (included in `prompt_tokens`, subtracted). |
|
|
33
|
+
| `openai` | `openai-chat` | `OpenAIAgent` | `openai-agent.ts` | **Generic OpenAI-compatible**. Use for native OpenAI or any third-party OpenAI gateway (incl. Ollama / llama.cpp). User fills endpoint + model id; capabilities openly editable in the form. Streaming via `fetchChatCompletionStream` — always sends `stream: true` + `stream_options: { include_usage: true }` (OpenAI's own field; Ollama ≥ 0.3 / vLLM / LM Studio accept it too). A gateway that rejects unknown fields would 400 — accepted trade-off, no non-streaming fallback. Forgiving about cache field names (`cachedPromptTokens`: first value > 0 of `prompt_tokens_details.cached_tokens`, top-level `cached_tokens`, `prompt_cache_hit_tokens`, `cache_read_tokens`) and reasoning field names (`reasoning_content` for OpenAI o-series/DeepSeek, `reasoning` for Ollama/llama.cpp). |
|
|
34
|
+
| `anthropic` | `anthropic-messages` | `AnthropicAgent` | `anthropic-agent.ts` | **Generic Anthropic Messages**. Use for api.anthropic.com or any Anthropic-compatible gateway. User fills endpoint + model id. Streaming via `fetchAnthropicStream` (the gateway must support `stream: true` SSE — the reference Anthropic behaviour). Standard `cache_control: ephemeral` with optional `ttl: '1h'`. |
|
|
35
|
+
| `mimo-token-plan-china` | `anthropic-messages` | `AnthropicAgent` | (reuses `anthropic-agent.ts`) | Xiaomi MiMo (UI name "Xiaomi MiMo"; the id keeps its historical name so existing configs don't break) — Anthropic-compatible gateway, streams like `anthropic` (gateway built for Claude Code, which always streams; not live-probed); no dedicated class, just `runtime: anthropic-messages` in its yaml (`defaultEndpoint` `token-plan-cn.xiaomimimo.com/anthropic`). The key type picks the endpoint: Token Plan `tp-`/`ttp-` keys only work on `token-plan-{cn,sgp,ams}.xiaomimimo.com/anthropic`, pay-as-you-go `sk-` keys only on `api.xiaomimimo.com/anthropic` — all four ship as `endpointPresets` (pay-as-you-go live-probed 2026-09-24; sgp / ams from the docs only). V2.6 (`mimo-v2.6-pro` / `-flash`, both vision, 1M / 131072) is current; `mimo-v2.5(-pro)` kept in the template until the official 2026-10-21 deprecation, then drop |
|
|
36
|
+
| `aws-bedrock-mantle` | `bedrock-mantle` | `MantleAgent` | `mantle-agent.ts` | OpenAI GPT-6.1 Sol, GPT-6 (Astra / Sol / Luna) and GPT-5.6 (Sol / Terra / Luna) on Amazon Bedrock via the **OpenAI Responses API** (not Completions/Converse/InvokeModel). Vision, reasoning via `reasoning.effort` (low/med/high/xhigh/max, no budget_tokens; on 5.6 `max` is accepted live but absent from the model's own validation list — measured ~6% more reasoning_tokens than xhigh; on Astra it's on the official list), `text.verbosity` (low/medium/high, same on both generations). **Region split** (live-probed 2026-09-24): us-east-1 serves GPT-6.1 Sol + GPT-6 Sol / Luna + every 5.6 model (404s Astra; GPT-6.1 Sol probed 2026-09-30, us-east-1 only); us-east-2 serves only the 5.6 trio; us-west-2 serves Astra + 5.6 Terra / Luna. Template ships all three as `endpointPresets`, default is now us-east-1 (`openai.gpt-6-sol` default model); Astra agents pick us-west-2 in the form; `extractRegionFromHost` falls back to us-east-1. All GPT-6 variants: 1,050,000 context / 128,000 max output, same effort (none/low/medium/high/xhigh/max) + verbosity surface; input beyond 272K tokens bills at 2x, so cap `context.maxTokens` in agent.yaml unless that's intended. Never surfaces reasoning summaries — `output[].reasoning.summary` stays empty even with `reasoning.summary=detailed` while `reasoning_tokens` counts up (verified live), so no thinking text in the UI is upstream behavior, not a parsing gap. Streams (`stream: true`, Responses API SSE folded by `readResponsesStream` in the same file): `response.output_text.delta` frames go out live, the terminal `response.completed` / `response.incomplete` frame carries the full final response object; this host sends **no** `data: [DONE]` (live-probed 2026-09-29). |
|
|
37
|
+
| `aws-bedrock-openai` | `bedrock-mantle` | `MantleAgent` | (reuses `mantle-agent.ts`) | Frontier models on Bedrock via the **bedrock-runtime** host's OpenAI Responses surface (`https://bedrock-runtime.<region>.amazonaws.com/openai/v1` — AWS's recommended endpoint for new apps; Mantle is the compatibility one). Same wire format, so its yaml just says `runtime: bedrock-mantle` + `extractRegionFromHost` accepts both hostnames (SigV4 signs with the host's region — a mismatched region 401s "Credential should be scoped to a valid region"). Rules that differ from Mantle: model ids must be cross-Region inference profiles (`global.*` / `us.*`; bare ids 400 "on-demand throughput isn't supported"); **no region split** — live-probed 2026-09-24, all nine `global.*` ids answer 200 from us-east-1 / us-west-2 / eu-west-1 / ap-northeast-1 alike; **no server-side tools** — `tools:[{type:"web_search"}]` 400s "web search is not supported for this request", which is why the web-search skill's deep gear stays on Mantle. Ships the same seven **GPT-6.1 / 6 / 5.6** entries as the Mantle template (`global.openai.gpt-6.1-sol`, `global.openai.gpt-6-{astra,sol,luna}`, `global.openai.gpt-5.6-{sol,terra,luna}`; identical effort none/low/medium/high/xhigh/max + verbosity surface per each model's own validation error — except Astra and GPT-6.1 Sol 400 on `none`, which the runtime never sends), plus **Grok 4.6** (500K ctx, 2x input price past 200K, max output undisclosed → 64000 carry-over, effort **only** low/medium/high/xhigh — `none`/`max` 400, so no Max preset; vision OK; $2/$6) and **Kimi K3** (1M ctx / 131072 out, effort none…max all accepted, ladder advisory on short prompts; vision OK; $3/$15). All: implicit prompt caching verified (run 1 `cache_write_tokens`, runs 2+ `cached_tokens` ≈ prefix), no reasoning summaries. Streams like Mantle (same event vocabulary) but this host **does** append a trailing `data: [DONE]`, which `readSseJson` skips; grok-4.6 emits a `reasoning` output item (no summary text deltas) before the `message` item. |
|
|
34
38
|
|
|
35
39
|
---
|
|
36
40
|
|
|
@@ -155,6 +159,7 @@ Create `packages/server/templates/models/<provider>.yaml` (deployed to `~/.halo/
|
|
|
155
159
|
|
|
156
160
|
```yaml
|
|
157
161
|
id: myprovider # required, must match filename and agent.yaml model.provider
|
|
162
|
+
runtime: myprovider # required, implementation class — an existing name (e.g. openai-chat) or your new one
|
|
158
163
|
displayName: My Provider
|
|
159
164
|
description: Short description
|
|
160
165
|
defaultEndpoint: https://api.example.com/v1
|
|
@@ -190,15 +195,17 @@ Shape source: [packages/server/templates/models/aws-bedrock-claude-invoke.yaml](
|
|
|
190
195
|
|
|
191
196
|
### 3. Wire the dispatcher
|
|
192
197
|
|
|
193
|
-
|
|
198
|
+
Skip this step when the manifest's `runtime:` names an existing runtime. For a new one, edit [packages/server/src/agents/model-runtime.ts](../../../packages/server/src/agents/model-runtime.ts) — add the name to `MODEL_RUNTIME_NAMES`, then the import and switch case (keyed by the runtime name, not the provider id):
|
|
194
199
|
|
|
195
200
|
```ts
|
|
196
201
|
import { MyProviderAgent } from './myprovider-agent.js'
|
|
197
202
|
|
|
203
|
+
export const MODEL_RUNTIME_NAMES = [/* … */, 'myprovider'] as const
|
|
204
|
+
|
|
198
205
|
case 'myprovider':
|
|
199
206
|
return new MyProviderAgent({
|
|
200
207
|
modelId: cfg.modelId,
|
|
201
|
-
endpoint: cfg.endpoint
|
|
208
|
+
endpoint: cfg.endpoint, // required — agent.yaml model.endpoint
|
|
202
209
|
apiKey: cfg.apiKey ?? '',
|
|
203
210
|
systemPrompt: cfg.systemPrompt,
|
|
204
211
|
tools: cfg.tools,
|
|
@@ -239,7 +246,7 @@ If a user wants to keep the key in an env var, they leave the `default` (`<<MY_P
|
|
|
239
246
|
|
|
240
247
|
### 5. Bump template version
|
|
241
248
|
|
|
242
|
-
In [packages/server/src/init.ts](../../../packages/server/src/init.ts), increment `TEMPLATE_VERSION` so new manifests and settings deploy on
|
|
249
|
+
In [packages/server/src/init.ts](../../../packages/server/src/init.ts), increment `TEMPLATE_VERSION` so new manifests and settings deploy on the next server start or `halo cli` / `halo tui` run (both call `refreshTemplatesIfOutdated`).
|
|
243
250
|
|
|
244
251
|
### 6. Use it
|
|
245
252
|
|
|
@@ -265,12 +272,14 @@ Or via the Form view: the Provider dropdown will include the new provider once t
|
|
|
265
272
|
5. Check the usage badges: `inputTokens` (non-cached) + `cacheRead` should equal total prompt tokens; token ring should show correct context window percentage
|
|
266
273
|
6. If the provider supports thinking, verify `reasoning_content` appears and tool loop works across multiple turns
|
|
267
274
|
|
|
268
|
-
If something's wrong, server logs under `~/.halo/global/logs/server.log` are the first place to look.
|
|
275
|
+
If something's wrong, server logs under `~/.halo/global/logs/server.log` are the first place to look. Session build throws a `[model-runtime]` error naming the provider and its yaml path when no manifest has that `id` (`Unknown provider "..."`), when the manifest has no `runtime:` (stale seed — run `halo setup` or restart the server), or when `runtime:` names something not in `MODEL_RUNTIME_NAMES` (the error lists the valid names).
|
|
269
276
|
|
|
270
277
|
---
|
|
271
278
|
|
|
272
279
|
## When to use the generic `openai` / `anthropic` provider instead of writing a new class
|
|
273
280
|
|
|
281
|
+
(A curated provider of your own for a stock-protocol vendor is just a manifest with `runtime: openai-chat` or `runtime: anthropic-messages` — `mimo-token-plan-china` is that case.)
|
|
282
|
+
|
|
274
283
|
Two of the providers above are deliberately generic — they take whatever `endpoint` + `model id` the user fills in the form and don't ship a curated model list. Use them when:
|
|
275
284
|
|
|
276
285
|
- The new vendor speaks **stock OpenAI chat completions** or **stock Anthropic Messages** with no quirks worth coding around. Examples: self-hosted vLLM, third-party OpenAI gateway.
|
|
@@ -391,7 +400,7 @@ Keeping it a hard-coded switch is deliberate for now:
|
|
|
391
400
|
- The Agent management UI reads the manifest directly; a runtime-registered plugin would need to notify the frontend dynamically
|
|
392
401
|
- One provider lands every few months, not every week
|
|
393
402
|
|
|
394
|
-
|
|
403
|
+
The switch is keyed by runtime name (one case per wire protocol / vendor class), so providers that share a protocol never add a case. If / when the switch becomes unwieldy, the next step would be a module-load registry — each runtime exports a `register(runtimeName, factory)` call that the dispatcher discovers at boot. Until then, editing the switch is simpler than managing a registry.
|
|
395
404
|
|
|
396
405
|
---
|
|
397
406
|
|
|
@@ -224,7 +224,7 @@ This installs the `halo` binary on `$PATH`. Subcommands available:
|
|
|
224
224
|
### Upgrade flow
|
|
225
225
|
|
|
226
226
|
1. `halo upgrade` — bumps the on-disk npm package
|
|
227
|
-
2. `halo server restart` — server's startup check sees `~/.halo/global/.template-version` is behind the new bundled `TEMPLATE_VERSION`, runs `ensureHaloHome` automatically, then starts. Refreshes `docs/`, built-in agents, built-in skills, system prompts, and the model registry. User-owned files (USER.md, custom agents/skills, INSTRUCTIONS.md overrides) are left alone. See `init.ts` for the per-category overwrite policy.
|
|
227
|
+
2. `halo server restart` — server's startup check sees `~/.halo/global/.template-version` is behind the new bundled `TEMPLATE_VERSION`, runs `ensureHaloHome` automatically, then starts (`halo cli` / `halo tui` run the same check on start, so a CLI-only install refreshes without a server). Refreshes `docs/`, built-in agents, built-in skills, system prompts, and the model registry. User-owned files (USER.md, custom agents/skills, INSTRUCTIONS.md overrides) are left alone. See `init.ts` for the per-category overwrite policy.
|
|
228
228
|
|
|
229
229
|
### Public release command reference
|
|
230
230
|
|
|
@@ -374,9 +374,10 @@ Still good practice: build server + admin **before** `pnpm dist:arm64`.
|
|
|
374
374
|
|
|
375
375
|
- **Changing anything under `templates/` requires bumping `TEMPLATE_VERSION`
|
|
376
376
|
(`packages/server/src/init.ts`) — otherwise the change never reaches existing
|
|
377
|
-
installs.** Server startup
|
|
378
|
-
reseed) when the on-disk
|
|
379
|
-
|
|
377
|
+
installs.** Server startup (and `halo cli` / `halo tui` start) only re-runs
|
|
378
|
+
`ensureHaloHome` (the template reseed) when the on-disk
|
|
379
|
+
`~/.halo/global/.template-version` is *strictly less than* the compiled
|
|
380
|
+
`TEMPLATE_VERSION` (`init.ts` `refreshTemplatesIfOutdated`). Equal → the
|
|
380
381
|
whole reseed is skipped, so a template edit with no version bump is invisible
|
|
381
382
|
to every machine that already has the prior version stamped. This bit the
|
|
382
383
|
`team`-delegation rewrite: `default/AGENT.md` gained the "team whitelist"
|
package/bundled-docs/dev/env.md
CHANGED
|
@@ -82,7 +82,7 @@ Three config file types, precedence **env vars > config.yaml / settings.yaml > c
|
|
|
82
82
|
| `~/.halo/secrets/settings.yaml` | User | Provider secrets, skill params, `general.*` server knobs (session, compaction, limits, sandbox, logging) — "preferences" |
|
|
83
83
|
| `<project>/.halo/settings.yaml` | Project | Overrides of provider / skill `params` and `secrets`; every `general.*` key is global-only (a workspace value is rejected by the API and ignored at runtime) |
|
|
84
84
|
|
|
85
|
-
`init.ts` seeds these on first run with a per-category policy. Refresh trigger: `halo setup` always re-runs the seed, and the
|
|
85
|
+
`init.ts` seeds these on first run with a per-category policy. Refresh trigger: `halo setup` always re-runs the seed, and the startup check re-runs it automatically when `~/.halo/global/.template-version` is behind the bundled `TEMPLATE_VERSION` — on server start and at the start of `halo cli` / `halo tui` (`init.ts` `refreshTemplatesIfOutdated`, called from `index.ts` and the CLI's `harness.ts` `initRuntime`; a never-seeded home is left to `halo setup`).
|
|
86
86
|
|
|
87
87
|
- **Always overwritten** (platform-owned, refreshed when the template version moves): `~/.halo/global/{prompts,models,docs}/`, `INSTRUCTIONS.md`, the built-in agent ids (`default`, `executor`, `deep-executor`, `goal`, `__evo_agent__`, `__score__`, `__apply_agent__`), built-in skill ids (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `aws-knowledge`, `web-search`, `self`, `halo`, `extension`).
|
|
88
88
|
- **Built-in agents** keep the user's `model:` and `context:` blocks on overwrite — the admin UI lets users change which model an agent uses and its context limits, and that choice survives upgrades.
|
|
@@ -227,6 +227,6 @@ With `export NANO_BANANA_KEY=sk-xxx` in the env, the agent's system prompt gets
|
|
|
227
227
|
|
|
228
228
|
## Model registry — adding a new provider
|
|
229
229
|
|
|
230
|
-
Halo ships 13 providers (`aws-bedrock-claude-invoke`, `aws-bedrock-mantle`, `aws-bedrock-openai`, `anthropic`, `openai`, `kimi`, `deepseek`, `minimax`, `qwen`, `hunyuan`, `doubao`, `zhipu`, `mimo-token-plan-china`); each one is a yaml under `~/.halo/global/models/`
|
|
230
|
+
Halo ships 13 providers (`aws-bedrock-claude-invoke`, `aws-bedrock-mantle`, `aws-bedrock-openai`, `anthropic`, `openai`, `kimi`, `deepseek`, `minimax`, `qwen`, `hunyuan`, `doubao`, `zhipu`, `mimo-token-plan-china`); each one is a yaml under `~/.halo/global/models/` whose `runtime:` field names the implementation in [packages/server/src/agents/model-runtime.ts](../../../packages/server/src/agents/model-runtime.ts) (e.g. `anthropic-messages`, `openai-chat`, `bedrock-invoke`). For any OpenAI- or Anthropic-compatible endpoint you don't need new code: pick `openai` or `anthropic` as the provider and fill in the endpoint and model id in Form view.
|
|
231
231
|
|
|
232
|
-
A
|
|
232
|
+
A provider that speaks an existing wire protocol needs only a yaml manifest with that `runtime:`; a new protocol also needs a runtime class. The step-by-step is in [dev/add-model-provider.md](../dev/add-model-provider.md). A provider yaml without `runtime:` (or with a name Halo doesn't know) makes session spawn fail with an error naming the yaml; for a bundled provider that means the seed is stale — run `halo setup` or restart the Halo server.
|
|
@@ -251,7 +251,8 @@ Session prefix: `cli_`. Sessions are persisted to `<workspace>/.halo/sessions/`
|
|
|
251
251
|
### Initialization
|
|
252
252
|
|
|
253
253
|
`tui` / `cli` / `agents` / `sessions` refuse to run ("~/.halo/global/ not initialized. Run halo setup first.") until `halo setup` has seeded `~/.halo/global/`. Then the CLI replicates the server's init sequence (`initRuntime()` in `harness.ts`):
|
|
254
|
-
1. `
|
|
255
|
-
2. `
|
|
256
|
-
3. `
|
|
257
|
-
4. `
|
|
254
|
+
1. `refreshTemplatesIfOutdated()` — the server's template startup check: when `~/.halo/global/.template-version` is behind the bundled `TEMPLATE_VERSION` (typically right after `halo upgrade`), re-seed `~/.halo/global/` before anything reads the models registry; logs `[CLI] Templates outdated (vX → vY), refreshing ~/.halo/global/` to stderr. Runs once per upgrade — afterwards the stamp matches and it's a single file read
|
|
255
|
+
2. `initLogger()` — redirect console to stderr + file logger
|
|
256
|
+
3. `initBwrapCheck()` — probe sandbox availability
|
|
257
|
+
4. `setSandboxHiddenPaths()` — configure sandbox paths
|
|
258
|
+
5. `new SessionManager(workspace)` — create agent session manager (the workspace's `.halo/` is seeded via `ensureWorkspaceHalo`)
|
|
@@ -77,7 +77,7 @@ After `halo setup`:
|
|
|
77
77
|
└── channels/channels.db ← per-channel account state
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
**When does "refreshed on upgrade" actually run?** `halo setup` always re-runs the seed; the server's startup check also re-runs it automatically when `~/.halo/global/.template-version` is behind the bundled `TEMPLATE_VERSION`. So the routine flow `halo upgrade && halo server restart` is enough — no need to remember `halo setup`.
|
|
80
|
+
**When does "refreshed on upgrade" actually run?** `halo setup` always re-runs the seed; the server's startup check — and the same check at the start of `halo cli` / `halo tui` — also re-runs it automatically when `~/.halo/global/.template-version` is behind the bundled `TEMPLATE_VERSION`. So the routine flow `halo upgrade && halo server restart` is enough — no need to remember `halo setup` — and a CLI-only user just runs `halo cli` / `halo tui` after `halo upgrade`.
|
|
81
81
|
|
|
82
82
|
**Server-refreshed**: `INSTRUCTIONS.md`, `prompts/`, `models/`, `docs/`, the built-in agent ids (`default`, `executor`, `deep-executor`, `goal`, `__evo_agent__`, `__score__`, `__apply_agent__`), and the built-in skill ids (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`, `self`, `aws-knowledge`, `web-search`, `halo`, `extension`). To customize one, copy it into the workspace scope (`<project>/.halo/...`) — workspace replaces global at runtime.
|
|
83
83
|
|
|
@@ -62,7 +62,8 @@ Schema lives **inside the package**, alongside its other files:
|
|
|
62
62
|
### Provider secrets — `models/<provider-id>.yaml`
|
|
63
63
|
|
|
64
64
|
```yaml
|
|
65
|
-
id: aws-bedrock-claude-invoke
|
|
65
|
+
id: aws-bedrock-claude-invoke # provider identity — also the settings namespace below
|
|
66
|
+
runtime: bedrock-invoke # implementation class; not part of the settings schema
|
|
66
67
|
displayName: AWS Bedrock Claude (Invoke API)
|
|
67
68
|
defaultEndpoint: https://bedrock-runtime.us-west-2.amazonaws.com
|
|
68
69
|
|