@turmind/halo 0.2.1 → 0.2.3

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.
@@ -1 +1 @@
1
- <!DOCTYPE html><!--iSrc6429pyTB0lK_EE5oH--><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/66df341f99220f5c.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-47520cdc55be63e7.js"/><script src="/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js" async=""></script><script src="/_next/static/chunks/635-3dd0043ed0520b9d.js" async=""></script><script src="/_next/static/chunks/main-app-483b436873410ddc.js" async=""></script><script src="/_next/static/chunks/212-170855bed19f67f3.js" async=""></script><script src="/_next/static/chunks/app/layout-c2fa8035998e48d5.js" async=""></script><script src="/_next/static/chunks/1d2d5650-810a3ca1ae20cab5.js" async=""></script><script src="/_next/static/chunks/307-d7a99f9a4a436ec3.js" async=""></script><script src="/_next/static/chunks/app/page-31317ef1fb0ba31f.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-47520cdc55be63e7.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[59391,[\"212\",\"static/chunks/212-170855bed19f67f3.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"ThemeProvider\"]\n3:I[26967,[\"212\",\"static/chunks/212-170855bed19f67f3.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"AppI18nProvider\"]\n4:I[66048,[],\"\"]\n5:I[29926,[],\"\"]\n6:I[57917,[],\"ClientPageRoot\"]\n7:I[11704,[\"560\",\"static/chunks/1d2d5650-810a3ca1ae20cab5.js\",\"307\",\"static/chunks/307-d7a99f9a4a436ec3.js\",\"212\",\"static/chunks/212-170855bed19f67f3.js\",\"974\",\"static/chunks/app/page-31317ef1fb0ba31f.js\"],\"default\"]\na:I[11789,[],\"OutletBoundary\"]\nc:I[21528,[],\"AsyncMetadataOutlet\"]\ne:I[11789,[],\"ViewportBoundary\"]\n10:I[11789,[],\"MetadataBoundary\"]\n11:\"$Sreact.suspense\"\n13:I[79836,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/66df341f99220f5c.css\",\"style\"]\n:HL[\"/_next/static/css/659eccb5db697b76.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"iSrc6429pyTB0lK-EE5oH\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/66df341f99220f5c.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[89592,[],\"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>
1
+ <!DOCTYPE html><!--NE1IhKMHm2l1nBV_EMjhV--><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/66df341f99220f5c.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-47520cdc55be63e7.js"/><script src="/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js" async=""></script><script src="/_next/static/chunks/635-3dd0043ed0520b9d.js" async=""></script><script src="/_next/static/chunks/main-app-483b436873410ddc.js" async=""></script><script src="/_next/static/chunks/212-170855bed19f67f3.js" async=""></script><script src="/_next/static/chunks/app/layout-c2fa8035998e48d5.js" async=""></script><script src="/_next/static/chunks/1d2d5650-810a3ca1ae20cab5.js" async=""></script><script src="/_next/static/chunks/307-d7a99f9a4a436ec3.js" async=""></script><script src="/_next/static/chunks/app/page-f6e0adceb730c1a7.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-47520cdc55be63e7.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[59391,[\"212\",\"static/chunks/212-170855bed19f67f3.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"ThemeProvider\"]\n3:I[26967,[\"212\",\"static/chunks/212-170855bed19f67f3.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"AppI18nProvider\"]\n4:I[66048,[],\"\"]\n5:I[29926,[],\"\"]\n6:I[57917,[],\"ClientPageRoot\"]\n7:I[11704,[\"560\",\"static/chunks/1d2d5650-810a3ca1ae20cab5.js\",\"307\",\"static/chunks/307-d7a99f9a4a436ec3.js\",\"212\",\"static/chunks/212-170855bed19f67f3.js\",\"974\",\"static/chunks/app/page-f6e0adceb730c1a7.js\"],\"default\"]\na:I[11789,[],\"OutletBoundary\"]\nc:I[21528,[],\"AsyncMetadataOutlet\"]\ne:I[11789,[],\"ViewportBoundary\"]\n10:I[11789,[],\"MetadataBoundary\"]\n11:\"$Sreact.suspense\"\n13:I[79836,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/66df341f99220f5c.css\",\"style\"]\n:HL[\"/_next/static/css/659eccb5db697b76.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"NE1IhKMHm2l1nBV-EMjhV\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/66df341f99220f5c.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[89592,[],\"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>
@@ -4,7 +4,7 @@
4
4
  4:I[66048,[],""]
5
5
  5:I[29926,[],""]
6
6
  6:I[57917,[],"ClientPageRoot"]
7
- 7:I[11704,["560","static/chunks/1d2d5650-810a3ca1ae20cab5.js","307","static/chunks/307-d7a99f9a4a436ec3.js","212","static/chunks/212-170855bed19f67f3.js","974","static/chunks/app/page-31317ef1fb0ba31f.js"],"default"]
7
+ 7:I[11704,["560","static/chunks/1d2d5650-810a3ca1ae20cab5.js","307","static/chunks/307-d7a99f9a4a436ec3.js","212","static/chunks/212-170855bed19f67f3.js","974","static/chunks/app/page-f6e0adceb730c1a7.js"],"default"]
8
8
  a:I[11789,[],"OutletBoundary"]
9
9
  c:I[21528,[],"AsyncMetadataOutlet"]
10
10
  e:I[11789,[],"ViewportBoundary"]
@@ -14,7 +14,7 @@ e:I[11789,[],"ViewportBoundary"]
14
14
  :HL["/_next/static/media/e4af272ccee01ff0-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
15
15
  :HL["/_next/static/css/66df341f99220f5c.css","style"]
16
16
  :HL["/_next/static/css/659eccb5db697b76.css","style"]
17
- 0:{"P":null,"b":"iSrc6429pyTB0lK-EE5oH","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/66df341f99220f5c.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}
17
+ 0:{"P":null,"b":"NE1IhKMHm2l1nBV-EMjhV","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/66df341f99220f5c.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"}]]
@@ -0,0 +1,97 @@
1
+ # AgentCore Runtime Mode
2
+
3
+ Halo server as an **Amazon Bedrock AgentCore Runtime** container — a fourth
4
+ way to run halo (server / CLI / desktop / AgentCore). One env var flips it:
5
+ `HALO_RUNTIME_MODE=agentcore`. Demo package: `packages/agentcore-demo/`
6
+ (Dockerfile, chat frontend, CDK stack, auth Lambdas — its README carries the
7
+ operational gotchas; this doc covers how the mode works inside the server).
8
+
9
+ ```
10
+ Browser (static frontend, S3 + CloudFront)
11
+ │ /api/login, /api/verify → CloudFront /api/* behavior → API GW → auth Lambda (DDB users, JWT)
12
+ │ /api/ws-presign → presign Lambda (SigV4-signs the AgentCore WS URL)
13
+ │ wss (presigned URL, 5-min TTL)
14
+ ▼
15
+ AgentCore Runtime (per-session microVM, auth terminated here)
16
+ │ X-Amzn-Bedrock-AgentCore-Runtime-Session-Id = user UUID
17
+ ▼
18
+ halo server :8080 (HALO_RUNTIME_MODE=agentcore)
19
+ ├── GET /ping Healthy | HealthyBusy (any running agent session)
20
+ ├── POST /invocations {"input":{"prompt"}} → full assistant message
21
+ └── WS /ws streaming frames (primary path)
22
+ ```
23
+
24
+ ## What the mode changes (packages/server/src/index.ts)
25
+
26
+ `config.server.runtimeMode === 'agentcore'` (env `HALO_RUNTIME_MODE`) skips:
27
+
28
+ - **Password/JWT gate** — AgentCore terminates auth upstream (SigV4 presign /
29
+ OAuth); the container is only reachable through the runtime.
30
+ - **Single-instance lock** — one microVM per session; many server processes
31
+ coexist by design.
32
+ - **Channels, cron, evolution, archive daemon** — meaningless in an ephemeral
33
+ per-session microVM; sessions are driven only through the AgentCore surface.
34
+
35
+ Everything else (agent loop, tools, skills, sqlite persistence) is the normal
36
+ server. The adapter itself is `packages/server/src/routes/agentcore.ts`,
37
+ mounted at the root (the AgentCore contract paths live outside `/api`).
38
+
39
+ ## Per-user workspace isolation
40
+
41
+ `userWorkspace(base, runtimeSessionId)` maps each runtime session id to
42
+ `<HALO_WORKSPACE>/users/<sanitized-id>/` — an isolated workspace with its own
43
+ `.halo/` (sqlite + session files). The id chain:
44
+
45
+ DDB `agentcore-demo-users` UUID → login response → frontend uses it as
46
+ sessionId → presign Lambda signs it into
47
+ `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` → adapter routes the workspace.
48
+
49
+ So **1 user = 1 runtime session id = 1 workspace**: users never see each
50
+ other's history; reconnecting with the same id resumes the same conversation.
51
+ `HALO_WORKSPACE` points at an EFS mount (`/mnt/efs`) — microVMs are ephemeral,
52
+ EFS makes the workspaces survive session termination and image rollouts.
53
+
54
+ ## WS protocol quirks (why /init exists)
55
+
56
+ The AgentCore WS proxy only forwards **client frames containing `inputText`**
57
+ and cannot push server frames on connect. Hence:
58
+
59
+ - Frontend sends `{"inputText":"/init"}` after open; the server special-cases
60
+ it (also accepts `{"type":"init"}`) and replies with a `history` frame —
61
+ a full snapshot, which the frontend renders by rebuild-from-scratch (not
62
+ append), keyed by a signature so identical snapshots skip re-rendering.
63
+ - Frames without `inputText` are silently ignored server-side — the frontend
64
+ uses `{type:'ping'}` every 30s purely to keep the proxy from cutting the
65
+ socket.
66
+ - `/session switch` sends a `{type:'switch'}` frame → frontend clears, then
67
+ the follow-up history frame rebuilds.
68
+
69
+ ## Session lifecycle (the part everyone gets wrong)
70
+
71
+ - Idle timeout (`idleRuntimeSessionTimeout`) terminates a session whose
72
+ `/ping` reports `Healthy`; `maxLifetime` (8h) force-terminates even busy
73
+ ones; failed health checks kill immediately.
74
+ - **An open WebSocket = an in-flight invocation** — the session never counts
75
+ as idle while a socket is open. The 30s frontend keepalive therefore keeps
76
+ the whole session warm; effective idle timeout ≈ "after the tab closes".
77
+ - `/ping` returns `HealthyBusy` whenever any agent session is running
78
+ (`registry.list().some(sm => sm.hasRunningSessions())`) — the official
79
+ keep-alive for long tool chains with no open connection.
80
+ - Termination is cheap: data is on EFS, next connect cold-starts (~3s) and
81
+ history reloads. `stop-runtime-session` kills one session on demand; there
82
+ is **no list/get-runtime-sessions API** (observe via CloudWatch `Sessions`
83
+ metric + runtime log filtering).
84
+
85
+ ## Ops crib sheet
86
+
87
+ - CloudWatch logs capture container **stdout**; halo's logger drops
88
+ sub-threshold lines before stdout — set `HALO_LOG_LEVEL=info` or the log
89
+ group stays near-empty.
90
+ - `update-agent-runtime` **replaces the whole config** — omit
91
+ `--filesystem-configurations` and the EFS mount silently disappears.
92
+ Fetch-modify-send, and strip `requireServiceS3Endpoint` (rejected on
93
+ newer runtimes).
94
+ - VPC mode has no public IP: private subnets need `0.0.0.0/0 → NAT` or all
95
+ egress (Bedrock included) hangs → opaque 502s.
96
+ - Full operational detail + deploy walkthrough:
97
+ `packages/agentcore-demo/README.md`.
@@ -109,7 +109,7 @@ Sandbox is whitelist-cp from main workspace (only: `INSTRUCTIONS.md`, `INDEX.md`
109
109
  ### Phase B — Dry-run + Fix Loop
110
110
 
111
111
  ```
112
- timeout 600 halo cli -a <patch.testScenario.agentId> -n -w <runDir>/sandbox \
112
+ timeout 1800 halo cli -a <patch.testScenario.agentId> -n -w <runDir>/sandbox \
113
113
  --access workspace <patch.testScenario.testMessage>
114
114
  ```
115
115
 
@@ -174,7 +174,7 @@ The wrapper clears any stale ABORT.md (from a crashed previous attempt) at the s
174
174
  For each source_run_id:
175
175
 
176
176
  1. Read `testScenario` from source run's `patch.md`.
177
- 2. Run `timeout 600 halo cli -a <agentId> -n -w <applyDir>/sandbox <testMessage>` against merged sandbox.
177
+ 2. Run `timeout 1800 halo cli -a <agentId> -n -w <applyDir>/sandbox <testMessage>` against merged sandbox.
178
178
  3. Spawn `__score__` with regress-mode brief, write `regress/<runId>/score.json`.
179
179
  4. Any score with `lint < 50` or `behavior < 50` → regression, abort with `failed`.
180
180
 
@@ -34,10 +34,11 @@ Halo 服务器运行态的**街景式**像素可视化:侧视、剖面的像素
34
34
  | 🪜 楼梯间 | 大楼左侧,真的逐层爬上爬下(没有传送) |
35
35
  | 💺 工作层 | 每层 3 张工位(按 agent 名固定)+ 1 个技能站 + 随机小设施(盆栽/小书架/白板/售货机);干活时屏幕滚代码 |
36
36
  | 🍵 公共层 | 冰箱(偶尔开门漏光)、水吧(水壶冒汽)、书报架、沙发,外加街机/鱼缸/猫爬架三选一;右侧伸出**阳台**(烟灰桶+盆栽)——抽烟点 |
37
- | 🚬 习惯 | 每个 session 由 id hash 派生 **3 个固定偏好**(抽烟/咖啡/泡茶/翻冰箱/看书/街机/看鱼/撸猫/打电话/下楼遛弯/沙发),摸鱼时偏好权重 ~4×——"这个会话就爱在阳台抽烟"是稳定人设,不是随机骰子 |
38
- | 🧭 就近原则 | 市民**生在自己的归属层**(自己的工位层;子代理用父代理的工位层),不再从大堂爬上来。休息活动以**归属层**为锚点就近找公共层(每 3 层就有),不会为了倒杯茶爬通天,**也不会随时间往低层漂**——12 楼的人永远回 F11 休息,不会一路沉到大堂。高层会话不下大堂:`下楼遛弯` 只有 F5 及以下才真下街,F6+ 改在就近休息层放风 |
37
+ | 🚬 习惯 | 每个 session 由 id hash 派生 **3 个固定偏好**(抽烟/咖啡/泡茶/翻冰箱/看书/街机/看鱼/撸猫/打电话/下楼遛弯/沙发/工位打游戏/工位刷手机),摸鱼时偏好权重 ~4×——"这个会话就爱在阳台抽烟"是稳定人设,不是随机骰子(工位打游戏/工位刷手机不挪窝,就在自己座位上摸鱼;面板文案里"工位刷手机"沿用了"打电话"那条文案) |
38
+ | 🧭 就近原则 | 市民**生在自己的归属层**(自己的工位层;子代理不论嵌套多深都解析到所在会话树的**根会话**工位层,不再掉进大堂)。休息活动以**归属层**为锚点找最近的公共层,不会为了倒杯茶爬通天,**也不会随时间往低层漂**——12 楼的人永远回 F11 休息,不会一路沉到大堂。街机/鱼缸/猫爬架这类稀有设施只在归属层 **±4 层**的漫游带内找最近一个,带外直接放弃改走最近的休息层;`下楼遛弯` 同样按漫游带算——归属层在 F3 及以下才真下街,F4+ 改在带内最近的休息层放风 |
39
+ | 📌 稳定工位 | 每个**根会话**的工位楼层只在**第一次出现**时分配一次(选最低空位),离开释放、回来复用——不会因为 `/api/show/state` 按 `updated_at DESC` 排序每次轮询都重排,否则摸鱼回来就会撞上"工位换人"的错觉 |
39
40
  | 💡 灯光 | 有人在的楼层灯亮(running 或 idle 都算——摸鱼也开灯),只有彻底 stopped 的层才熄;天黑后街灯/霓虹/远景窗火全亮 |
40
- | ↳ 小一号 | 子代理:生在并聚在父代理那层桌边,不占工位 |
41
+ | ↳ 小一号 | 子代理:生在所在会话树的**根会话**工位层(而不是直接父代理那层),干活时聚在**直接**父代理桌边,不占工位 |
41
42
 
42
43
  进出有戏:新会话直接出现在自己的工位层(从本层楼梯口走到工位,不再从街上爬通天);结束的走出门沿街远去淡出。
43
44
 
@@ -51,7 +52,8 @@ Halo 服务器运行态的**街景式**像素可视化:侧视、剖面的像素
51
52
  - **上下文仪表**:`134k / 200k (67%)` —— 分母是该会话的**真实** `maxContextTokens`
52
53
  - **委派链**:父/子会话可点击跳转,每行带 token 数和消息数
53
54
  - **点楼层**:该层的会话委派关系树(缩进、可点跳转)——工作层看"谁在这层干活、
54
- 带着哪些子代理",公共层看"谁在摸鱼"
55
+ 带着哪些子代理",公共层看"谁在摸鱼";**工位在这层的人即使正外出休息也照样算在这层**
56
+ (按归属工位层统计,不是按人此刻站在哪),点楼层永远能看全常驻名单
55
57
  - 点大楼:全员 roster + token 合计;点技能站:谁在用
56
58
 
57
59
  ## 街景与载具
@@ -77,6 +79,9 @@ Halo 服务器运行态的**街景式**像素可视化:侧视、剖面的像素
77
79
  - **纯净模式(zen)**:淡出所有 HUD(统计/动态/图例/缩放条/面板),只留街景,适合大屏常驻展示
78
80
  - **多语言(i18n)**:中文 / English 一键切;`?lang=en`/`?lang=zh` 直达,选择存 localStorage。
79
81
  运行时文案走 `t()`,静态 DOM 走 `data-i18n` 属性填充
82
+ - **背景音乐**:🎵/🔇 HUD 按钮(挨着 中/EN),纯 Web Audio 程序生成、零素材,
83
+ 默认开、偏好存 localStorage;受浏览器自动播放策略限制,AudioContext 在首次
84
+ 用户手势后才创建,关掉时真正 suspend(零 CPU),音频图与画面 rAF 循环完全独立
80
85
 
81
86
  ## 调试参数
82
87
 
@@ -107,10 +112,18 @@ js/
107
112
  ## 服务端配套
108
113
 
109
114
  - `GET /api/show/session?ws=&id=` — 只读会话详情:裁剪后的消息日志 + 真实
110
- token 上限。鉴权同 `/api/show/state`(x-token;非 full 只能看自己 workspace)
115
+ token 上限。鉴权同 `/api/show/state`(x-token;`full`/`observer` 可跨
116
+ workspace 读取,其余 accessLevel 只能看自己 workspace)
111
117
  - `/api/show/state` — 无活跃 UIState 的会话(idle/stopped/重启后)token
112
118
  从会话文件头读取(mtime 缓存,不随轮询刷盘),不显示 0;含 `messageCount` 字段
113
119
 
120
+ > **`observer` token 的权限面**:除了 `/show/state` 的聚合计数外,`observer`
121
+ > 还能调 `/show/session` 读取**任意 workspace** 任意会话的明细——最近 40 条
122
+ > 消息(每条截断到 600 字,tool input 截到 200 字)。它是"看板/监控"用的
123
+ > 全局只读角色,但这条 transcript 读取能力不止是计数;签发 observer token 时
124
+ > 要知道它相当于给了跨 workspace 的会话内容只读权限,不要把它当成纯粹的
125
+ > "数字仪表盘"凭证随手发放。
126
+
114
127
  ## 性能设计
115
128
 
116
129
  原则:一个纯只读可视化页面,轮询**不得对服务端会话生命周期产生任何副作用**,
@@ -296,8 +296,8 @@ general: # built-in declarer (the server itself
296
296
  max_message_slice: 800
297
297
  summarize_timeout_sec: 300
298
298
  sandbox:
299
- hidden_dirs: "~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker"
300
- hidden_files: "~/.npmrc,~/.bash_history,~/.gitconfig"
299
+ hidden_dirs: "~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh"
300
+ hidden_files: "~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc"
301
301
  logging:
302
302
  level: warn
303
303
 
@@ -352,8 +352,8 @@ A value of the form `<<ENV_NAME>>` is replaced at read time with `process.env.EN
352
352
  | `general.limits.tool_result_ui_chars` | 65536 | Per-tool-result cap on the content **stored for UI display** (admin/web chat panel). Far larger than the LLM cap so a normal command's full output stays visible, but bounded so a multi-MB `cat` can't bloat the session file / WS payload / browser render; excess truncated with a marker pointing at `file_read` |
353
353
  | `general.limits.ws_event_buffer` | 5000 | Events buffered per detached WS session before oldest are dropped on reattach |
354
354
  | `general.limits.terminal_scrollback_bytes` | 50000 | Off-screen scrollback bytes retained per detached persistent terminal |
355
- | `general.sandbox.hidden_dirs` | `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker` | bwrap tmpfs overlay (Linux only) |
356
- | `general.sandbox.hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig` | bwrap /dev/null bind (Linux only) |
355
+ | `general.sandbox.hidden_dirs` | `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh` | bwrap tmpfs overlay (Linux only) |
356
+ | `general.sandbox.hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc` | bwrap /dev/null bind (Linux only) |
357
357
  | `general.logging.level` | warn | `debug` / `info` / `warn` / `error` |
358
358
 
359
359
  Schema source: [packages/server/src/settings-schema.ts](../../../packages/server/src/settings-schema.ts) `generalSection()`.
@@ -170,10 +170,8 @@ node server.js
170
170
 
171
171
  ### Security model
172
172
 
173
- - `HALO_TOKEN` and `HALO_API` are server-side only, never exposed to the browser
174
- - Frontend uses same-origin relative paths (`/chat`, `/history`, etc.)
175
- - web-demo server proxies all requests to Halo, injecting the token in `x-token` header
176
- - Session auth: random hex token stored in `x-session` header + localStorage
173
+ - **Proxy mode (default)**: `HALO_TOKEN` and `HALO_API` are server-side only, never exposed to the browser; frontend uses same-origin relative paths (`/chat`, `/history`, etc.); web-demo server proxies all requests to Halo, injecting the token in `x-token` header. Session auth: HMAC token in `x-session` header + localStorage. All proxy routes including `GET /file` are gated by the auth middleware.
174
+ - **Direct-connect mode (opt-in)**: the gear panel takes a halo server URL + web-channel token; the browser then calls `<server>/api/web/*` directly with `x-token`, bypassing the proxy (no password step). The token is stored in that browser's localStorage by explicit user choice — the UI says so next to the field. One slot; clearing it returns to proxy mode. Works because the server CORS reflects any origin and allowlists `x-token`.
177
175
 
178
176
  ### Proxy routes
179
177
 
@@ -183,17 +181,21 @@ node server.js
183
181
  | `POST /stop` | `POST /api/web/stop` |
184
182
  | `GET /history` | `GET /api/web/history` |
185
183
  | `GET /subscribe` | `GET /api/web/subscribe` |
184
+ | `GET /file` | `GET /api/web/file` |
186
185
 
187
186
  ### Features
188
187
 
189
- - SSE streaming with tool call display (collapsible)
190
- - Auto-reconnect: if session is running on page load, subscribes to live stream
188
+ - Markdown rendering for assistant text (marked@12, gfm + breaks) with typewriter streaming; user text stays escaped plain text
189
+ - SSE streaming with collapsible thinking / tool-call step blocks
190
+ - Auto-reconnect: if session is running on page load, subscribes to live stream; history renders rebuild-from-scratch (never append)
191
191
  - Message queue: can send while agent is running, messages queue and send sequentially
192
- - Slash commands (`/help`, `/session`, `/agent`, `/skill`, `/workspace`)
193
- - Image upload (file picker + camera capture)
192
+ - Slash commands (`/help`, `/session`, `/agent`, `/skill`, `/workspace`), session/agent switcher menus
193
+ - Image upload + camera capture (camera button is mobile-only — the `capture` attribute is a no-op on desktop, where it would duplicate the file picker)
194
194
  - Voice recording (webm audio saved to workspace, path given to agent)
195
195
  - Stop/interrupt button
196
196
  - i18n: auto-detects browser language (`navigator.language`), toggle button in header, persists to `localStorage`
197
+ - Mobile-first layout: 100dvh-safe, safe-area insets, ≥44px touch targets, bottom-sheet menus/settings on small widths, visual language shared with `packages/agentcore-demo`
198
+ - Page load needs internet for two CDN deps (tailwindcss, marked) — no longer fully self-contained
197
199
 
198
200
  ## Integration guide (custom frontend)
199
201
 
@@ -222,7 +222,7 @@ File: `packages/server/src/routes/web.ts`
222
222
  | POST | `/api/web/stop` | Stop running task → `{stopped: boolean}` |
223
223
  | GET | `/api/web/history` | Active session history → `{sessionId, messages[], running}` |
224
224
  | GET | `/api/web/subscribe` | Reconnect SSE to running session |
225
- | GET | `/api/web/file?path=` | Inline-serve a workspace-relative file (image / video / pdf etc.). Path-traversal-checked against the token's bound workspace. |
225
+ | GET | `/api/web/file?path=` | Inline-serve a workspace-relative file (image / video / pdf etc.). Path-traversal-checked against the token's bound workspace: the lexical check is re-verified against the realpath'd root, so a symlink inside the workspace pointing outside it returns 403; a dangling symlink (target doesn't exist) returns 404. |
226
226
 
227
227
  See [design/web.md](../design/web.md).
228
228
 
@@ -238,6 +238,8 @@ runtime so the frontend can render rooms (workspaces) + characters (sessions).
238
238
  | GET | `/api/show/state` | `full` or `observer` token → every known workspace; otherwise the account's own. Returns `{ serverTime, uptime, accessLevel, skills[], workspaces[] }` |
239
239
  | GET | `/api/show/session?ws=&id=` | Inspector-panel detail for a single session. Trimmed message log (last 40, content/tool I/O capped) plus `contextTokens` / `outputTokens` / `maxContextTokens` / `isRunning`. Non-`full`/`observer` tokens may only read their own workspace. |
240
240
 
241
+ `observer` is more than an aggregate-counts role: past `/show/state`, it can also call `/show/session` to read **any workspace's** session transcript (last 40 messages, content truncated to 600 chars, tool input to 200 chars). Mint it knowing it grants cross-workspace transcript read access, not just dashboard counters.
242
+
241
243
  Each `workspace` = `{ path, key, label, counts{running,idle,stopped}, totalSessions, skills[], sessions[] }`; each `session` = `{ id, parentId, depth, agentName, description, status, lastTool, activeSkill, contextTokens, outputTokens, messageCount, updatedAt }`. `lastTool` / `activeSkill` come from the live in-memory UI log (empty when the session isn't loaded). Sessions per room are capped (`totalSessions` reports the true total). Frontend: `halo-city/` at repo root.
242
244
 
243
245
  **Read-only by construction — degraded snapshots.** Both endpoints resolve a workspace's runtime via `registry.peek()` — in-memory lookup only, **never** `getOrCreate`: constructing a SessionManager has write side effects (boot orphan-reconciliation batch-stops live sub-session rows; `.halo/` scaffolding), so a pure visualization poll must not trigger it. When no live SessionManager exists in this process, the handler opens that workspace's `.halo/halo.db` **read-only** (cached connection; missing db/tables silently degrade to an empty room) and projects rows / the persisted session file into the same wire shape. Sessions not in memory therefore return a **persisted snapshot**: `status` / token counts / `messageCount` may lag behind the last persistence point, and the live-only signals (`lastTool` / `activeSkill`) are empty by definition. The degraded `/api/show/session` path reports `maxContextTokens: 0` (the real cap needs a live agent config; the city inspector hides the meter when the cap is unknown).
@@ -152,6 +152,14 @@ This installs the `halo` binary on `$PATH`. Subcommands available:
152
152
  1. `halo upgrade` — bumps the on-disk npm package
153
153
  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.
154
154
 
155
+ ### Release checklist (before `npm publish`)
156
+
157
+ 1. **Bump version** in root `package.json` (and any workspace `package.json` that mirrors it).
158
+ 2. **Update `CHANGELOG.md`**: rename `[Unreleased]` → `[x.y.z] - YYYY-MM-DD`, add a fresh empty `[Unreleased]` section above it.
159
+ 3. **Bump `TEMPLATE_VERSION`** in `packages/server/src/init.ts` if any file under `templates/` was touched.
160
+ 4. **Build admin**: `pnpm --filter @turmind/halo-admin build` — verify `admin/out/monaco/vs/loader.js` exists.
161
+ 5. Commit, tag `vx.y.z`, push, then publish.
162
+
155
163
  The published package contains a single bundled JS entry (~620 KB), all built-in templates (agents / skills / prompts / models), bundled platform docs, and the admin Web UI static export. Total install footprint ≈ 120 MB after npm dedupes shared deps.
156
164
 
157
165
  ### Non-interactive (Docker / CI) details
@@ -196,3 +204,5 @@ server {
196
204
  }
197
205
  }
198
206
  ```
207
+
208
+ Behind a reverse proxy, set `general.server.trust_proxy: true` in `settings.yaml` (global scope only). Without it, brute-force rate-limiting / lockout resolves the client IP from the direct socket address — which behind a proxy is the proxy's own IP, so every client collapses into one bucket and can't be told apart. Only enable this when the proxy in front is one you control and it rewrites `x-forwarded-for` itself; otherwise a client can forge the header and bypass lockouts. Default is `false` (direct-connect deployments).
@@ -108,8 +108,9 @@ settings.yaml only (no env override):
108
108
  - `general.compact.max_summary_input` (default 15000) — local compaction fallback input cap
109
109
  - `general.compact.max_message_slice` (default 800) — local compaction per-message cap
110
110
  - `general.compact.summarize_timeout_sec` (default 300) — self-compact timeout
111
- - `general.sandbox.hidden_dirs` (default `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker`) — bwrap tmpfs overlays, scope: global
112
- - `general.sandbox.hidden_files` (default `~/.npmrc,~/.bash_history,~/.gitconfig`) — bwrap /dev/null binds, scope: global
111
+ - `general.server.trust_proxy` (default `false`) — trust `x-forwarded-for` for client IP resolution, scope: global (enable only behind a reverse proxy you control)
112
+ - `general.sandbox.hidden_dirs` (default `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh`) — bwrap tmpfs overlays, scope: global
113
+ - `general.sandbox.hidden_files` (default `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc`) — bwrap /dev/null binds, scope: global
113
114
  - `general.logging.level` (default `warn`) — log level: debug | info | warn | error
114
115
 
115
116
  `<provider-id>.secrets.*` (server-only, hard-rejected by `{{}}` substitution; declared in `models/<provider-id>.yaml` `secrets:`):
@@ -137,10 +137,10 @@ Configured in `settings.yaml` under `general.sandbox`:
137
137
 
138
138
  | Setting | Default | Method |
139
139
  |---|---|---|
140
- | `hidden_dirs` | `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker` | `--tmpfs` overlay (empty directory) |
141
- | `hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig` | `--ro-bind /dev/null` (empty file) |
140
+ | `hidden_dirs` | `~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh` | `--tmpfs` overlay (empty directory) |
141
+ | `hidden_files` | `~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc` | `--ro-bind /dev/null` (empty file) |
142
142
 
143
- Changes take effect immediately — `config.ts` reads settings.yaml via an mtime-watched lazy cache, so the next `shell_exec` reads the latest values. A workspace-level `settings.yaml` can override these the same way it overrides any other key (no global-scope lock).
143
+ Changes take effect immediately — `config.ts` reads settings.yaml via an mtime-watched lazy cache, so the next `shell_exec` reads the latest values. These keys are `globalOnly` in the schema — a workspace `settings.yaml` cannot override them, since they define the security boundary agents run inside.
144
144
 
145
145
  `/tmp` is not in the hidden list — it receives a standalone `--tmpfs` mount for process isolation (each bwrap invocation gets its own empty `/tmp`), not for hiding secrets.
146
146
 
@@ -172,6 +172,7 @@ Start a sub-agent session asynchronously. When the sub-agent finishes, its **wra
172
172
  | `agent_id` | string | yes | Agent ID (e.g. `"coder"`) |
173
173
  | `message` | string | yes | Task description |
174
174
  | `system_prompt_context` | string | no | Extra context prepended to the initial message |
175
+ | `title` | string | no | Session title shown in the admin sidebar. When omitted, auto-generated from the task message (first 60 chars of `description`). |
175
176
  | `working_dir` | string | no | Sub-agent's focus directory (absolute or workspace-relative; default: project root). It's persistent session identity (stored in the DB, restored on resume), so the directory-chain `.halo/INSTRUCTIONS.md` along the path root→dir is baked into the sub-agent's **system prompt every turn** (it never forgets the directory's rules), and the prompt is tagged with this focus. Does **not** change where tools run. |
176
177
 
177
178
  **Output (JSON string)**
@@ -134,6 +134,8 @@ Environment variables:
134
134
 
135
135
  The demo holds your token server-side and exposes its own password-based session — the browser never sees the halo token. Open `http://localhost:9528` to use it.
136
136
 
137
+ Alternatively, the gear icon (header / login screen) opens **direct-connect mode**: enter any halo server's URL + a web-channel token and the browser talks to that server's `/api/web/*` directly, skipping the proxy and its password. Handy for pointing one deployed web-demo at several halo servers. The token is kept in that browser's localStorage — only use it on devices you trust; clear it in the same panel to return to proxy mode.
138
+
137
139
  ## Security notes
138
140
 
139
141
  - **Tokens are unhashed** in `~/.halo/secrets/channels/channels.db`. Restrict that file to the user that runs halo
@@ -55,6 +55,7 @@ VS Code-style highlight selection (no checkboxes):
55
55
  - Selected files/folders drag onto a target dir
56
56
  - Multi-file drag supported
57
57
  - Each file goes through `POST /api/files/rename` with `{oldPath, newPath}`
58
+ - **Spring-loaded expand** (applies to both tree-internal drags and OS-file drops): a collapsed folder does **not** expand the moment a drag passes over it — it unfolds only after the drag hovers on it for ~600ms (VSCode/Finder behaviour; the timer cancels on drag-leave). Dropping into a collapsed folder expands it immediately to show the result
58
59
 
59
60
  ### Right-click menu
60
61
 
@@ -7,6 +7,8 @@ Global + workspace configuration. The shape mirrors VSCode's `contributes.config
7
7
  ```yaml
8
8
  # ~/.halo/secrets/settings.yaml — values only
9
9
  general: # built-in declarer (server itself)
10
+ server:
11
+ trust_proxy: false # true only behind a reverse proxy you control — see below
10
12
  session:
11
13
  max_queue_size: 256
12
14
  max_nesting_depth: 16
@@ -15,8 +17,8 @@ general: # built-in declarer (server itself)
15
17
  max_summary_input: 15000
16
18
  ...
17
19
  sandbox:
18
- hidden_dirs: "~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker"
19
- hidden_files: "~/.npmrc,~/.bash_history,~/.gitconfig"
20
+ hidden_dirs: "~/.halo/secrets,~/.aws,~/.ssh,~/.gnupg,~/.docker,~/.config/gh"
21
+ hidden_files: "~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc"
20
22
  writable_dirs: "" # e.g. ~/.kiro,~/.local/share/kiro-cli — rw bind-mounts in bwrap; ignored for readonly sessions
21
23
  logging:
22
24
  level: warn
@@ -91,7 +93,11 @@ secrets: []
91
93
 
92
94
  ### General — built-in
93
95
 
94
- Declared in [packages/server/src/settings-schema.ts](../../../packages/server/src/settings-schema.ts) `generalSection()`. The server itself is the implicit declarer. Keys: `language`, `agent.*`, `session.*`, `compact.*`, `sandbox.*`, `logging.*`, `evolution.*`, `limits.*`.
96
+ Declared in [packages/server/src/settings-schema.ts](../../../packages/server/src/settings-schema.ts) `generalSection()`. The server itself is the implicit declarer. Keys: `language`, `agent.*`, `server.*`, `session.*`, `compact.*`, `sandbox.*`, `logging.*`, `evolution.*`, `limits.*`.
97
+
98
+ `server.trust_proxy` (boolean, default `false`, `globalOnly`): whether the brute-force rate limiter (`middleware/brute-force.ts` `getClientIp`) trusts the `x-forwarded-for` header for client IP resolution. Direct-connect deployments leave it `false` and get the socket address. Behind a reverse proxy (nginx / Cloudflare / etc.), set it to `true` so the real client IP is honored instead of the proxy's — but only when that proxy is one you control and rewrites the header itself, otherwise a client can forge XFF to dodge lockouts.
99
+
100
+ `sandbox.hidden_dirs` / `sandbox.hidden_files` / `sandbox.writable_dirs` are `globalOnly` — they define the security boundary agents run inside, so a workspace `settings.yaml` cannot override them (a workspace overriding them could lift its own sandbox constraints).
95
101
 
96
102
  `evolution.*` controls the self-evolution subsystem (see [plans/self-evolution.md](../plans/self-evolution.md)). All `evolution.*` keys are `globalOnly` — they live in `~/.halo/secrets/settings.yaml` only, not workspace settings. Notable knobs: `evolution.level` (`L0` = off, `L1` = human + LLM assist), `evolution.max_concurrent_run` / `max_concurrent_apply` (wrapper concurrency caps), `evolution.run_timeout_minutes` / `apply_timeout_minutes` (heartbeat timeouts), `evolution.max_attempts` (per-row retry cap), `evolution.triggers.pre_compact` (snapshot session before compaction).
97
103
 
@@ -106,6 +112,7 @@ Declared in [packages/server/src/settings-schema.ts](../../../packages/server/sr
106
112
  | `description_zh` | no | Chinese description (UI picks based on lang) |
107
113
  | `default` | no | Placeholder shown when the value is unset; supports `<<ENV>>` |
108
114
  | `secret` | no | `true` → masked in API responses + password input in UI |
115
+ | `globalOnly` | no | `true` → read from global settings only; workspace overrides are ignored at runtime. UI disables the workspace input and shows a "global only" hint |
109
116
 
110
117
  ## Scope: global vs. workspace
111
118