@turmind/halo 1.1.3 → 1.1.4

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><!--oDvBPFdNAW2vHdHcxQ0lw--><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/0d0ca7b3c8cc5562.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-fc1574554e7b1108.js"/><script src="/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js" async=""></script><script src="/_next/static/chunks/635-d7fd95bff8674116.js" async=""></script><script src="/_next/static/chunks/main-app-483b436873410ddc.js" async=""></script><script src="/_next/static/chunks/212-70bd2971392bde84.js" async=""></script><script src="/_next/static/chunks/app/layout-c2fa8035998e48d5.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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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-fc1574554e7b1108.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-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"ThemeProvider\"]\n3:I[26967,[\"212\",\"static/chunks/212-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"AppI18nProvider\"]\n4:I[66048,[],\"\"]\n5:I[29926,[],\"\"]\n6:I[11789,[],\"OutletBoundary\"]\n8:I[21528,[],\"AsyncMetadataOutlet\"]\na:I[11789,[],\"ViewportBoundary\"]\nc:I[11789,[],\"MetadataBoundary\"]\nd:\"$Sreact.suspense\"\nf:I[79836,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/0d0ca7b3c8cc5562.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"oDvBPFdNAW2vHdHcxQ0lw\",\"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/0d0ca7b3c8cc5562.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[89592,[],\"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>
1
+ <!DOCTYPE html><!--PuVQROsIREQUt1b6EFWJR--><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/0d0ca7b3c8cc5562.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-fc1574554e7b1108.js"/><script src="/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js" async=""></script><script src="/_next/static/chunks/635-d7fd95bff8674116.js" async=""></script><script src="/_next/static/chunks/main-app-483b436873410ddc.js" async=""></script><script src="/_next/static/chunks/212-70bd2971392bde84.js" async=""></script><script src="/_next/static/chunks/app/layout-c2fa8035998e48d5.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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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-fc1574554e7b1108.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-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"ThemeProvider\"]\n3:I[26967,[\"212\",\"static/chunks/212-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"AppI18nProvider\"]\n4:I[66048,[],\"\"]\n5:I[29926,[],\"\"]\n6:I[11789,[],\"OutletBoundary\"]\n8:I[21528,[],\"AsyncMetadataOutlet\"]\na:I[11789,[],\"ViewportBoundary\"]\nc:I[11789,[],\"MetadataBoundary\"]\nd:\"$Sreact.suspense\"\nf:I[79836,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/0d0ca7b3c8cc5562.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"PuVQROsIREQUt1b6EFWJR\",\"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/0d0ca7b3c8cc5562.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[89592,[],\"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>
@@ -1 +1 @@
1
- <!DOCTYPE html><!--oDvBPFdNAW2vHdHcxQ0lw--><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/0d0ca7b3c8cc5562.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-fc1574554e7b1108.js"/><script src="/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js" async=""></script><script src="/_next/static/chunks/635-d7fd95bff8674116.js" async=""></script><script src="/_next/static/chunks/main-app-483b436873410ddc.js" async=""></script><script src="/_next/static/chunks/212-70bd2971392bde84.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/438-866a1a20e9efa78a.js" async=""></script><script src="/_next/static/chunks/app/page-71c576815b4a30d9.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-fc1574554e7b1108.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-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"ThemeProvider\"]\n3:I[26967,[\"212\",\"static/chunks/212-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"AppI18nProvider\"]\n4:I[66048,[],\"\"]\n5:I[29926,[],\"\"]\n6:I[57917,[],\"ClientPageRoot\"]\n7:I[61089,[\"560\",\"static/chunks/1d2d5650-810a3ca1ae20cab5.js\",\"438\",\"static/chunks/438-866a1a20e9efa78a.js\",\"212\",\"static/chunks/212-70bd2971392bde84.js\",\"974\",\"static/chunks/app/page-71c576815b4a30d9.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/0d0ca7b3c8cc5562.css\",\"style\"]\n:HL[\"/_next/static/css/659eccb5db697b76.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"oDvBPFdNAW2vHdHcxQ0lw\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/0d0ca7b3c8cc5562.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><!--PuVQROsIREQUt1b6EFWJR--><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/0d0ca7b3c8cc5562.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-fc1574554e7b1108.js"/><script src="/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js" async=""></script><script src="/_next/static/chunks/635-d7fd95bff8674116.js" async=""></script><script src="/_next/static/chunks/main-app-483b436873410ddc.js" async=""></script><script src="/_next/static/chunks/212-70bd2971392bde84.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/438-866a1a20e9efa78a.js" async=""></script><script src="/_next/static/chunks/app/page-71c576815b4a30d9.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-fc1574554e7b1108.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-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"ThemeProvider\"]\n3:I[26967,[\"212\",\"static/chunks/212-70bd2971392bde84.js\",\"177\",\"static/chunks/app/layout-c2fa8035998e48d5.js\"],\"AppI18nProvider\"]\n4:I[66048,[],\"\"]\n5:I[29926,[],\"\"]\n6:I[57917,[],\"ClientPageRoot\"]\n7:I[61089,[\"560\",\"static/chunks/1d2d5650-810a3ca1ae20cab5.js\",\"438\",\"static/chunks/438-866a1a20e9efa78a.js\",\"212\",\"static/chunks/212-70bd2971392bde84.js\",\"974\",\"static/chunks/app/page-71c576815b4a30d9.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/0d0ca7b3c8cc5562.css\",\"style\"]\n:HL[\"/_next/static/css/659eccb5db697b76.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"PuVQROsIREQUt1b6EFWJR\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/0d0ca7b3c8cc5562.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>
@@ -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/0d0ca7b3c8cc5562.css","style"]
16
16
  :HL["/_next/static/css/659eccb5db697b76.css","style"]
17
- 0:{"P":null,"b":"oDvBPFdNAW2vHdHcxQ0lw","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/0d0ca7b3c8cc5562.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":"PuVQROsIREQUt1b6EFWJR","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/0d0ca7b3c8cc5562.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"}]]
@@ -52,7 +52,7 @@ Each fire:
52
52
  1. **Concurrency guard**: if the same `jobId` is already running in this process (in-memory `_inflight` set), insert a `cron_runs` row with `status='skipped'`, `failureReason='previous run still in progress'`, broadcast, and bail without spawning. Applies to both scheduled fires and manual run-now clicks. Two overlapping cli children would double-write the same `cron-<jobId>` on-disk session state; SessionManager's per-session lock is in-process and can't see across cli children, so this set is the cheapest place to enforce serialization. The set itself is in-memory, but its restart blind spot is covered: the boot-time orphan sweep (below) re-enters stale jobIds before croner re-arms.
53
53
  2. Insert a `cron_runs` row with `status='running'` up front (UI sees it mid-flight); broadcast `cron:run_changed`.
54
54
  3. Check `job.workspacePath` exists — fail loudly if the workspace was moved/deleted.
55
- 4. Spawn `halo cli -a <agent> -s cron-<jobId> -w <workspace>`, prompt on **stdin** (avoids Windows argv length limits). The stable `cron-<jobId>` session id means each fire *resumes the same session*, so the conversation accumulates and is reviewable in the admin Sessions tab. The child's pid is written back onto the `running` row (`cron_runs.pid`, nullable) — this is what makes the orphan sweep possible after a server crash.
55
+ 4. Spawn `halo cli -a <agent> -s cron-<jobId> -w <workspace>`, prompt on **stdin** (avoids Windows argv length limits). The stable `cron-<jobId>` session id means each fire *resumes the same session*, so the conversation accumulates and is reviewable in the admin Sessions tab. The child's pid is written back onto the `running` row (`cron_runs.pid`, nullable) — this is what makes the orphan sweep possible after a server crash. Note the **dual-writer layout** this creates: the cli child and the server share the workspace's session files. The server side is view-only for these sessions — admin subscribes seed a UIState from disk, and the WS detach-save skips it via the dirty gate (see [session.md](session.md#ws-disconnect-resilience)); without that gate a frozen server snapshot would overwrite the cli child's newer messages (the cron-session UI-log truncation incident). Live streaming of a cli-driven session into the admin is out of scope — events don't cross processes; the Sessions tab re-reads the file on open.
56
56
  5. Tee stdout + stderr live to `~/.halo/global/logs/cron/<runId>.log`; capture stdout to memory for dispatch.
57
57
  6. Enforce a **per-job timeout** (`job.timeoutSec ?? 3600`, the nullable `cron_jobs.timeout_sec` validated 60–21600 at every write surface; a change takes effect on the job's next fire, no croner re-instantiation needed) via a Node timer (cross-platform, not the Linux-only `timeout(1)`), with a **two-phase kill**: SIGTERM the direct child first — the cli has a SIGTERM handler that stops the harness, repairs + flushes the session, kills its own shell_exec process groups, and exits 143 — then, if it's still alive after a 30s grace, SIGKILL the entire descendant tree (POSIX ppid-walk over one `ps` snapshot; `taskkill /T /F` on Windows) so no grandchild survives to double-write the workspace on the next fire. A ppid walk, not a process-group kill: the cli sandbox spawns shell_exec workers `detached` into their own groups, so `kill(-pid)` would miss them. Timed-out runs still report exit code 124. Generous because overlap-of-the-same-job is already blocked up-front in step 1; this is just the long-stop reaper for a truly stuck child.
58
58
  7. Classify: exit 124 → `timeout`; non-zero → `failed`; zero but empty stdout → `failed` ("nothing to dispatch"); else `succeeded`.
@@ -69,6 +69,7 @@ Gates, in order:
69
69
 
70
70
  1. Root only (`parentId === null`), row carries `goalSessionId`, goal status is `running`.
71
71
  2. **Subtree-quiet gate** (inherited from `tryReportToParent`): no active children in the db **and** an empty message queue. A W that dispatched executors and idled while they run does NOT end the round — their reports wake W first.
72
+ - **Aborted-round marker**: the report body is `finalOutput || output || '(no output)'`; when W's turn was killed by an unrecoverable error (`turnError` set — retry budget exhausted / account error), it is prefixed with `[WORKER ABORTED: … Error: <text> … judge this as an interrupted round, do not score it as a completed result …]`. Without it G scored mid-turn fragments (or a literal "(no output)") as real rounds — same defect as the sub-agent auto-report, same prescription (see [session.md](session.md#session-lifecycle)): prefix, don't suppress (skipping delivery would stall the loop), prepended *before* the truncation cap so it always survives. The marker rides every downstream delivery path (question-stop / halt headers describe the stop, the marker describes the round's shape — both true), and an aborted round still counts toward round accounting and the no-progress hash: three identical aborts trip the breaker, which is the right read for a worker dying the same way thrice.
72
73
  3. **Spec tamper gate**: sha256 of `GOAL_SPEC.md` must equal the `specHash` stamped at attach. Changed or missing → halt (`spec-tampered`), back-pointer cleared, G instructed to write a halt diagnosis. Never silently restore.
73
74
  4. **Question-stop**: a report containing `<NEED_INPUT>` is delivered with a question-stop header and consumes **neither** the round counter nor the no-progress budget — the worker is waiting, not failing. G triages: answerable from spec + scene → `goal_decide` + relay; genuine user-sovereignty fork → park to the user.
74
75
  5. **Round accounting**: `round++`; `noProgress` increments when the report hash is unchanged from last round, else resets (a deterministic proxy for hard-stuck — semantic no-progress is G's judging duty).
@@ -139,6 +139,8 @@ Hierarchical encoding: `root_id>child_segment>grandchild_segment`.
139
139
 
140
140
  Both true → sets `stoppedAt` + emits `agent_done` + `querySession`s the result back to the parent. The parent's `querySession` clears its own `stoppedAt`, handles the report, and may trigger its own `tryReportToParent` — bubbling up.
141
141
 
142
+ **Abnormal-termination marker**: when the reported turn was killed by an unrecoverable error (retry budget exhausted / account-level failure), the session's `turnError` field holds the error text and the auto-report is prefixed with an explicit `[SUB-AGENT ABORTED: … Error: <text> … Re-dispatch with query_session(…) to resume.]` block. Without it the parent LLM consumed mid-turn fragments (or a literal "(no output)") as completed reports — the cron-era "sub-agent reported without finishing" incident, root-caused to a Bedrock h2 hang (`TimeoutError: http2 request did not get a response`, now also in the transient-transport retry list). Prefix, don't suppress: skipping the report would leave the parent waiting forever, the partial trace has diagnostic value, and `stoppedAt` is stamped as usual so the child never shows as falsely running. The marker is prepended *before* the truncation cap so it can never be sliced off. (`deliverGoalRound` has the same `finalOutput || output` shape without an error dimension — known gap, unfixed.)
143
+
142
144
  The full finally-chain order is `tryReportToParent → deliverGoalRound → releaseSession`: after the parent-report check, goal-bound **root** sessions get their round report delivered to the goal session (fire-and-forget; a no-op for everyone else — see [goal-mode.md](goal-mode.md#the-delivery-point-delivergoalround)), then the session is released.
143
145
 
144
146
  - **Sibling-status injection for root** (`siblingStatusSuffix`): `tryReportToParent` early-returns for root (`parentId === null`) since there's no parent to bubble up to — so root never learns whether its *other* children are still running, and the root LLM could wrap up early after consuming just one child's report. When root consumes a child report (`querySession` idle branch + `drainQueue`), a sibling-status line is appended to the message fed to the LLM. "All sub-agents done" requires **both** no sibling running in the DB (`parentId = root AND stoppedAt IS NULL`) **and** an empty in-memory `messageQueue` — a child can be stopped while its report is still queued, so the DB check alone would falsely declare completion. The reporting child needs no identity exclusion: it stamped `stoppedAt` before the report was delivered, so `stoppedAt IS NULL` already excludes it (unless re-dispatched a new task, which clears `stoppedAt` — then it correctly counts as running). The line carries per-child `created` + `last active` timestamps so a capable model can tell a freshly dispatched sibling from an original-batch leftover. Mid-tier parents are excluded by design — their `tryReportToParent` bubble-up already gates them on a fully-drained subtree.
@@ -303,6 +305,7 @@ Frontend network issues don't affect the backend:
303
305
  2. **Grace period**: fixed `config.timeout.sessionGrace` (5 min default) — a single `setTimeout`, no auto-extension
304
306
  3. **Event buffering**: the detached handler uses `bufferDetachedNotification` to buffer structural events in `pendingEvents[]`. All state (messageLog / tokens / etc.) lives in SessionManager's UIState, not duplicated in the handler.
305
307
  4. **Reconnect**: a `subscribe` with the same sessionId loads UIState from SessionManager, replays `pendingEvents`, and — if still running — resumes live streaming
308
+ 5. **Detach-save dirty gate**: every WS write-back path (disconnect cleanup, grace expiry, session switch/clear, backgroundSaves) funnels through `saveSession()`, which skips any UIState that isn't **dirty** — i.e. this process never reduced an event / appended a notification / replaced the log since the last successful persist (`SessionUIStore.isUIStateDirty`). A clean state is either a pure disk seed (built by `prepareForView` to *view* a session another process is driving — e.g. a cron `halo cli` child appending to the same file) or already flushed; writing it back would clobber the fresher file with a frozen snapshot. This was the cron-session UI-log truncation incident: admin viewed a cron session, closed the tab, and the grace-expiry save erased the cli child's final minutes of messages (`rawMessages`/`output` survived only via `saveSessionToFile`'s read-merge). The flag is cleared on successful `persistUIState` (kept on failure so a later save retries) and on `dropUIState`/purge/`prepareForView` re-seed so a stale flag never leaks onto a fresh disk copy.
306
309
 
307
310
  ## Resilient execution loop
308
311
 
package/dist/index.js CHANGED
@@ -2339,6 +2339,13 @@ function ensureHaloHome(haloHome2) {
2339
2339
  fs7.rmSync(staleWsSkill, { recursive: true, force: true });
2340
2340
  console.log("[Init] Removed stale `ws` skill (renamed to `workspace`)");
2341
2341
  }
2342
+ for (const staleId of ["nova-web-search", "openai-web-search"]) {
2343
+ const staleDir = path8.join(globalDir2, "skills", staleId);
2344
+ if (fs7.existsSync(staleDir)) {
2345
+ fs7.rmSync(staleDir, { recursive: true, force: true });
2346
+ console.log(`[Init] Removed stale \`${staleId}\` skill (merged into \`web-search\`)`);
2347
+ }
2348
+ }
2342
2349
  const docsSrc = resolveDocsSource();
2343
2350
  if (!docsSrc) {
2344
2351
  console.log(`[Init] No docs source found \u2014 skipping platform docs seed. Searched: ${DOCS_SRC_CANDIDATES.join(", ")}`);
@@ -2400,7 +2407,7 @@ var init_init = __esm({
2400
2407
  path8.resolve(__dirname, "..", "bundled-docs"),
2401
2408
  path8.resolve(__dirname, "..", "..", "..", ".halo", "docs")
2402
2409
  ];
2403
- TEMPLATE_VERSION = 50;
2410
+ TEMPLATE_VERSION = 52;
2404
2411
  VERSION_FILE = ".template-version";
2405
2412
  SKIP_NAMES = /* @__PURE__ */ new Set([".DS_Store", "schema.sql", "__pycache__", ".pytest_cache"]);
2406
2413
  BUILTIN_AGENT_IDS = /* @__PURE__ */ new Set([
@@ -2434,7 +2441,7 @@ var init_init = __esm({
2434
2441
  // Capability skills, model-invoked only (user-invocable: false — no slash
2435
2442
  // command). Preinstalled because they're broadly useful out of the box.
2436
2443
  "aws-knowledge",
2437
- "nova-web-search",
2444
+ "web-search",
2438
2445
  // The agent's visual "face": teaches it that `.halo/canvas/self.html`
2439
2446
  // (seeded per-workspace, see ensureWorkspaceHalo) is a live self-portrait
2440
2447
  // it can drive in real time by emitting `<<<SHOW: …js… >>>`, which the admin
@@ -3324,7 +3331,12 @@ async function deliverGoalRound(host, worker) {
3324
3331
  const activeChildren = db.select({ id: agentSessions.id }).from(agentSessions).where(and2(eq2(agentSessions.parentId, worker.id), isNull(agentSessions.stoppedAt), isNull(agentSessions.archivedAt))).all();
3325
3332
  if (activeChildren.length > 0 || worker.messageQueue.length > 0)
3326
3333
  return;
3327
- const report = worker.finalOutput || worker.output || "(no output)";
3334
+ let report = worker.finalOutput || worker.output || "(no output)";
3335
+ if (worker.turnError) {
3336
+ report = `[WORKER ABORTED: this round was terminated by an unrecoverable error, NOT a normal wrap-up. Error: ${worker.turnError}. The text below is a partial trace of the aborted turn \u2014 judge this as an interrupted round, do not score it as a completed result. Re-dispatch with query_session to let the worker resume.]
3337
+
3338
+ ${report}`;
3339
+ }
3328
3340
  const caps = state.caps;
3329
3341
  const currentSpecHash = hashFileOrNull(goalSpecPath(host.workspaceRoot, state.goalId));
3330
3342
  if (!currentSpecHash || currentSpecHash !== state.specHash) {
@@ -5127,6 +5139,17 @@ var init_session_ui_store = __esm({
5127
5139
  /** Last activity (event reduced / view built) per uiStates key — the idle
5128
5140
  * sweep's eviction clock. Invariant: every uiStates key has an entry. */
5129
5141
  uiStateTouched = /* @__PURE__ */ new Map();
5142
+ /** Roots whose in-memory UIState holds local mutations (event reduced in,
5143
+ * notification appended, log replaced) **not yet persisted** — marked on
5144
+ * every mutation, cleared once persistUIState lands the snapshot. A state
5145
+ * not in this set adds nothing to what's on disk: either it's a pure disk
5146
+ * seed (built to *view* a session another process may be driving — a cron
5147
+ * `halo cli` child shares the workspace's session files and keeps appending
5148
+ * after the seed), or its mutations were already flushed. The WS
5149
+ * detach/switch saves gate on `isUIStateDirty`: writing a clean state back
5150
+ * would overwrite the other process's newer messages with this process's
5151
+ * frozen snapshot (the cron-session UI-log truncation incident). */
5152
+ uiStateDirty = /* @__PURE__ */ new Set();
5130
5153
  constructor(host) {
5131
5154
  this.host = host;
5132
5155
  this.db = host.getDb();
@@ -5223,6 +5246,7 @@ var init_session_ui_store = __esm({
5223
5246
  reduceIntoUIState(rootId, event) {
5224
5247
  try {
5225
5248
  const state = this.ensureUIState(rootId);
5249
+ this.uiStateDirty.add(rootId);
5226
5250
  const result = applyEvent(state, event);
5227
5251
  if (result.subSessionSave) {
5228
5252
  this.persistSubSession(state, rootId, result.subSessionSave);
@@ -5319,6 +5343,7 @@ var init_session_ui_store = __esm({
5319
5343
  archiveCount: archive?.count,
5320
5344
  archivedUserDelta: archive?.userDelta
5321
5345
  });
5346
+ this.uiStateDirty.delete(rootId);
5322
5347
  } catch (err) {
5323
5348
  console.error(`[SessionUIStore] persistUIState failed for ${rootId}: ${err instanceof Error ? err.message : String(err)}`);
5324
5349
  }
@@ -5438,6 +5463,14 @@ var init_session_ui_store = __esm({
5438
5463
  getCachedUIState(rootSessionId) {
5439
5464
  return this.uiStates.get(rootSessionId) ?? null;
5440
5465
  }
5466
+ /** Whether the cached UIState holds local mutations not yet persisted.
5467
+ * False for a state that was only seeded from disk (view of a session
5468
+ * another process drives) or whose mutations already flushed — writing
5469
+ * such a state back is at best a no-op and at worst overwrites a fresher
5470
+ * file (see uiStateDirty). */
5471
+ isUIStateDirty(rootSessionId) {
5472
+ return this.uiStateDirty.has(rootSessionId);
5473
+ }
5441
5474
  /**
5442
5475
  * Get the UIState for a session, restoring it from disk if it isn't
5443
5476
  * in memory yet. Returns null only when the session id doesn't exist
@@ -5470,6 +5503,7 @@ var init_session_ui_store = __esm({
5470
5503
  timestamp: Date.now(),
5471
5504
  agentName
5472
5505
  });
5506
+ this.uiStateDirty.add(rootId);
5473
5507
  this.persistUIState(rootId, state);
5474
5508
  }
5475
5509
  /**
@@ -5483,6 +5517,7 @@ var init_session_ui_store = __esm({
5483
5517
  state.streamBuffer = "";
5484
5518
  state.turnToolCalls = [];
5485
5519
  state.turnContentBlocks = [];
5520
+ this.uiStateDirty.add(rootId);
5486
5521
  this.persistUIState(rootId, state);
5487
5522
  }
5488
5523
  /**
@@ -5504,6 +5539,7 @@ var init_session_ui_store = __esm({
5504
5539
  this.uiStates.delete(sessionId);
5505
5540
  this.uiStateProjectPaths.delete(sessionId);
5506
5541
  this.uiStateTouched.delete(sessionId);
5542
+ this.uiStateDirty.delete(sessionId);
5507
5543
  }
5508
5544
  /**
5509
5545
  * Evict UIStates whose tree has been idle past the TTL. This is THE eviction
@@ -5560,8 +5596,10 @@ var init_session_ui_store = __esm({
5560
5596
  * session evolving elsewhere would freeze on the first snapshot.
5561
5597
  */
5562
5598
  prepareForView(sessionId, selfDriven) {
5563
- if (!selfDriven)
5599
+ if (!selfDriven) {
5564
5600
  this.uiStates.delete(sessionId);
5601
+ this.uiStateDirty.delete(sessionId);
5602
+ }
5565
5603
  this.uiStateProjectPaths.set(sessionId, this.host.workspaceRoot);
5566
5604
  return this.ensureUIState(sessionId);
5567
5605
  }
@@ -5571,6 +5609,7 @@ var init_session_ui_store = __esm({
5571
5609
  this.uiStates.delete(id);
5572
5610
  this.uiStateProjectPaths.delete(id);
5573
5611
  this.uiStateTouched.delete(id);
5612
+ this.uiStateDirty.delete(id);
5574
5613
  const timer = this.persistTimers.get(id);
5575
5614
  if (timer) {
5576
5615
  clearTimeout(timer);
@@ -10392,6 +10431,7 @@ var init_session_manager = __esm({
10392
10431
  draftReset,
10393
10432
  output: "",
10394
10433
  finalOutput: "",
10434
+ turnError: null,
10395
10435
  promise: null,
10396
10436
  abortController: null,
10397
10437
  messageQueue: [],
@@ -10695,6 +10735,13 @@ var init_session_manager = __esm({
10695
10735
  getCachedUIState(rootSessionId) {
10696
10736
  return this.uiStore.getCachedUIState(rootSessionId);
10697
10737
  }
10738
+ /** Whether a root's cached UIState holds local mutations not yet persisted.
10739
+ * The WS save paths gate on this — a clean state (pure disk seed, or
10740
+ * already flushed) must not be written back over a file that another
10741
+ * process (cron `halo cli` child) may have advanced since. */
10742
+ isUIStateDirty(rootSessionId) {
10743
+ return this.uiStore.isUIStateDirty(rootSessionId);
10744
+ }
10698
10745
  getUIState(rootSessionId) {
10699
10746
  return this.uiStore.getUIState(rootSessionId);
10700
10747
  }
@@ -10802,6 +10849,7 @@ var init_session_manager = __esm({
10802
10849
  const maxRetries = config.agent.maxRetries;
10803
10850
  session.output = "";
10804
10851
  session.finalOutput = "";
10852
+ session.turnError = null;
10805
10853
  for (let attempt = 0; attempt < maxRetries; attempt++) {
10806
10854
  session.abortController = new AbortController();
10807
10855
  const signal = session.abortController.signal;
@@ -10858,6 +10906,7 @@ var init_session_manager = __esm({
10858
10906
  if (msg.includes("insufficient balance") || msg.includes("suspended") || msg.includes("invalid api key") || msg.includes("Invalid API Key") || msg.includes("Unauthorized") || msg.includes("authentication")) {
10859
10907
  console.error(`[SessionManager] Session ${session.id} account error: ${msg}`);
10860
10908
  this.emitEvent(session.id, { type: "error", error: msg, agentName: session.agentName, taskId: session.parentId ? session.id : void 0 });
10909
+ session.turnError = msg;
10861
10910
  resultText = `Error: ${msg}`;
10862
10911
  break;
10863
10912
  }
@@ -10883,7 +10932,7 @@ var init_session_manager = __esm({
10883
10932
  continue;
10884
10933
  }
10885
10934
  }
10886
- if (msg === "fetch failed" || msg === "Model request timed out" || msg.includes("UND_ERR_HEADERS_TIMEOUT") || msg.includes("HeadersTimeoutError") || msg.includes("socket hang up") || msg.includes("ECONNRESET") || msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("EAI_AGAIN")) {
10935
+ if (msg === "fetch failed" || msg === "Model request timed out" || msg.includes("http2 request did not get a response") || msg.includes("UND_ERR_HEADERS_TIMEOUT") || msg.includes("HeadersTimeoutError") || msg.includes("socket hang up") || msg.includes("ECONNRESET") || msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("EAI_AGAIN")) {
10887
10936
  if (attempt + 1 < maxRetries) {
10888
10937
  const delay = 1e3 * Math.pow(2, attempt) + Math.random() * 500;
10889
10938
  console.debug(`[SessionManager] Session ${session.id} transient network error, retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries}): ${msg}`);
@@ -10926,6 +10975,7 @@ var init_session_manager = __esm({
10926
10975
  const errDetail = [errName, httpStatus ? `HTTP ${httpStatus}` : ""].filter(Boolean).join(" ");
10927
10976
  console.error(`[SessionManager] Session ${session.id} error (attempt ${attempt + 1}/${maxRetries})${errDetail ? ` [${errDetail}]` : ""}: ${msg}`);
10928
10977
  this.emitEvent(session.id, { type: "error", error: msg, agentName: session.agentName, taskId: session.parentId ? session.id : void 0 });
10978
+ session.turnError = errName ? `${errName}: ${msg}` : msg;
10929
10979
  resultText = `Error: ${errName ? `${errName}: ` : ""}${msg}`;
10930
10980
  break;
10931
10981
  }
@@ -10991,7 +11041,12 @@ var init_session_manager = __esm({
10991
11041
  return;
10992
11042
  }
10993
11043
  this.db.update(agentSessions).set({ stoppedAt: Date.now() }).where(eq6(agentSessions.id, session.id)).run();
10994
- const result = session.finalOutput || session.output || "(no output)";
11044
+ let result = session.finalOutput || session.output || "(no output)";
11045
+ if (session.turnError) {
11046
+ result = `[SUB-AGENT ABORTED: the last turn was terminated by an unrecoverable error, NOT completed. Error: ${session.turnError}. The text below is a partial trace of the aborted turn \u2014 do not treat it as a finished result. Re-dispatch with query_session("${session.id}", ...) to let it resume.]
11047
+
11048
+ ${result}`;
11049
+ }
10995
11050
  console.debug(`[SessionManager] Auto-report: ${session.id} \u2192 parent ${session.parentId} \u2014 result: ${result.slice(0, 150)}`);
10996
11051
  const reportCap = config.limits.autoReportMax;
10997
11052
  const truncatedReport = result.length > reportCap ? result.slice(0, reportCap) + `
@@ -21552,6 +21607,8 @@ function setupWebSocketHandler(deps) {
21552
21607
  const state = getState(client);
21553
21608
  if (!state)
21554
21609
  return;
21610
+ if (!client.sessionManager.isUIStateDirty(client.sessionId))
21611
+ return;
21555
21612
  const projectPath = resolveProjectPath2(client.projectId);
21556
21613
  const snapshot = createSaveSnapshot(state);
21557
21614
  if (snapshot.length === 0)
@@ -29087,7 +29144,7 @@ var init_dist2 = __esm({
29087
29144
  init_git_ssh();
29088
29145
  __dirname3 = path64.dirname(fileURLToPath3(import.meta.url));
29089
29146
  PORT = config.server.port;
29090
- HALO_VERSION = "1.1.3";
29147
+ HALO_VERSION = "1.1.4";
29091
29148
  PROJECT_ROOT = findProjectRoot();
29092
29149
  HALO_HOME4 = path64.join(homedir24(), ".halo");
29093
29150
  GIT_SHA = readGitSha();
@@ -29989,7 +30046,7 @@ import path65 from "node:path";
29989
30046
  import fs58 from "node:fs";
29990
30047
  import { homedir as homedir25 } from "node:os";
29991
30048
  import { parseArgs as parseArgs2 } from "node:util";
29992
- var VERSION = "1.1.3";
30049
+ var VERSION = "1.1.4";
29993
30050
  var HELP_TOP = `Usage: halo <command> [options]
29994
30051
 
29995
30052
  Commands:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turmind/halo",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Halo — multi-agent collaborative workspace. CLI + TUI + HTTP/WS server in one package.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@ skills:
45
45
  - send-file
46
46
  - self
47
47
  - aws-knowledge
48
- - nova-web-search
48
+ - web-search
49
49
  - cron
50
50
  - acp
51
51
  - halo
@@ -35,6 +35,34 @@ secrets:
35
35
  # thinking.default → effort selected when Thinking is on (adaptive)
36
36
  # thinking.defaultBudgetTokens → budget selected when Thinking is on (manual)
37
37
  models:
38
+ - id: global.anthropic.claude-fable-5-1
39
+ displayName: Claude Fable 5.1
40
+ maxOutputTokens: 128000
41
+ contextWindow: 1000000
42
+ capabilities:
43
+ image: true
44
+ video: false
45
+ audio: false
46
+ promptCaching:
47
+ default: 1h
48
+ ttlPresets:
49
+ - { value: 5m, label: 5min }
50
+ - { value: 1h, label: 1hour }
51
+ thinking:
52
+ # Fable 5.1 — Mythos-class flagship refresh. Adaptive effort set
53
+ # confirmed by an invalid-value probe against Bedrock (2026-09-02):
54
+ # low / medium / high / xhigh / max, same as Fable 5. 1M context,
55
+ # 128K output. Smoke-tested on invoke (xhigh).
56
+ mode: adaptive
57
+ defaultEnabled: true
58
+ default: high # flagship-of-flagships — high by default; xhigh/max on demand
59
+ effortPresets:
60
+ - { value: low, label: Low }
61
+ - { value: medium, label: Medium }
62
+ - { value: high, label: High }
63
+ - { value: xhigh, label: XHigh }
64
+ - { value: max, label: Max }
65
+
38
66
  - id: global.anthropic.claude-fable-5
39
67
  displayName: Claude Fable 5
40
68
  maxOutputTokens: 128000
@@ -46,7 +46,7 @@ simply absent. Consequences you must apply:
46
46
  global skill for one workspace, copy the WHOLE folder to workspace scope
47
47
  (including resource files — remember: no per-file fallback), then edit.
48
48
  - **Built-in skills** (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`,
49
- `self`, `aws-knowledge`, `nova-web-search`): their global copies are force re-seeded on every server
49
+ `self`, `aws-knowledge`, `web-search`): their global copies are force re-seeded on every server
50
50
  start — direct edits to those global files are lost. Customize at workspace
51
51
  scope instead.
52
52
  - **Disabled state** lives per-workspace in the `disabled_items` DB table, not
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: web-search
3
+ description: Real-time web search with two gears. Fast (default, Amazon Nova grounding, ~3s, cheap) — current events, news, prices, any routine lookup. Deep (--deep, OpenAI GPT-5.6 web_search via Bedrock Mantle, ~20-40s, token-expensive) — multi-round retrieval with first-hand sources and per-claim citations, for verifying facts behind important decisions or double-checking weak/contradictory fast results. Default to fast; escalate to deep only when the question deserves depth.
4
+ user-invocable: false
5
+ ---
6
+ # Web Search
7
+
8
+ One skill, two gears:
9
+
10
+ | Gear | Engine | Speed / cost | Use for |
11
+ |---|---|---|---|
12
+ | **fast** (default) | Amazon Nova 2 Lite `nova_grounding` | ~3 s, ~500 tokens | News, quotes, prices, routine lookups — the everyday default |
13
+ | **deep** (`--deep`) | OpenAI GPT-5.6 `web_search` (Bedrock Mantle) | ~20-40 s, 10-40k tokens | Fact verification before important conclusions; second opinion when fast results look weak, self-contradictory, or conflict with data you hold |
14
+
15
+ Deep gear does multi-round real retrieval (bilingual query rewriting, `site:`-scoped
16
+ searches against official sites), leans toward authoritative first-hand sources
17
+ (official announcements, news agencies), and attaches a citation to every claim —
18
+ **expensive and slow, but thorough and it doesn't make things up**.
19
+
20
+ ## Usage
21
+
22
+ ```bash
23
+ # fast (default) — everyday lookups
24
+ python3 ~/.halo/global/skills/web-search/search.py "What are the latest news about the China A-share market today?"
25
+
26
+ # deep — verify facts that must be right
27
+ python3 ~/.halo/global/skills/web-search/search.py --deep "What did the US Federal Reserve announce most recently about interest rates?"
28
+ ```
29
+
30
+ - Output: answer text + a `Sources:` URL list (gear/model/token usage on stderr).
31
+ - Deep gear default model is `luna` (reasoning effort=max — retrieval planning
32
+ maxed out). `--deep --model sol` is carpet-bombing research (auto-paired with
33
+ effort=xhigh; ~3x more search queries, roughly double the tokens) — reserve it
34
+ for "profile one stock / one topic exhaustively" jobs.
35
+ - Full natural-language questions beat keyword stubs in both gears
36
+ (`"RTX 5080"` ✗ → `"Tell me about the NVIDIA RTX 5080, including specs and price."` ✓).
37
+ English works best; Chinese works too — deep gear rewrites queries bilingually.
38
+ - Search output is still a lead, not gospel: for critical figures, `web_fetch`
39
+ the cited source URL and confirm before acting on it.
40
+ - Auth is automatic in both gears (machine AWS credentials — no API key to
41
+ configure or rotate). Deep gear needs the `aws-bedrock-token-generator` pip
42
+ package (one-time: `pip3 install --user aws-bedrock-token-generator`).
43
+ - A deep call can take 40 s — be patient in shell_exec. One clear question per
44
+ call; don't fan out parallel calls.
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """Web search with two gears.
3
+
4
+ fast (default): Amazon Nova 2 Lite + nova_grounding — ~3 s, ~500 tokens.
5
+ deep (--deep): OpenAI GPT-5.6 on Bedrock Mantle + server-side web_search —
6
+ multi-round retrieval, first-hand sources, ~20-40 s, 10-40k tokens.
7
+
8
+ Usage:
9
+ python3 search.py "your question"
10
+ python3 search.py --deep "your question" [--model sol|luna]
11
+
12
+ Auth is automatic in both gears (machine AWS credentials): fast uses boto3's
13
+ default credential chain; deep mints a 12-hour bearer token on every call via
14
+ aws-bedrock-token-generator — no API key to configure or rotate.
15
+ """
16
+ import json
17
+ import sys
18
+ import urllib.error
19
+ import urllib.request
20
+
21
+ MANTLE_URL = "https://bedrock-mantle.us-east-2.api.aws/openai/v1/responses"
22
+ MODELS = {"luna": "openai.gpt-5.6-luna", "sol": "openai.gpt-5.6-sol"}
23
+ # luna (default) runs effort=max to squeeze out its retrieval planning; sol's
24
+ # retrieval is already exhaustive at medium (measured 48 search queries on one
25
+ # question), so it gets xhigh to avoid pointless token doubling — max over
26
+ # xhigh measured only ~6% more reasoning tokens.
27
+ EFFORTS = {"openai.gpt-5.6-luna": "max", "openai.gpt-5.6-sol": "xhigh"}
28
+
29
+
30
+ def fast_search(question: str) -> None:
31
+ import boto3
32
+ from botocore.config import Config
33
+
34
+ bedrock = boto3.client(
35
+ "bedrock-runtime", region_name="us-east-1", config=Config(read_timeout=60)
36
+ )
37
+ response = bedrock.converse(
38
+ modelId="us.amazon.nova-2-lite-v1:0",
39
+ messages=[{"role": "user", "content": [{"text": question}]}],
40
+ toolConfig={"tools": [{"systemTool": {"name": "nova_grounding"}}]},
41
+ )
42
+ answer = ""
43
+ sources: list[str] = []
44
+ for content in response["output"]["message"]["content"]:
45
+ if "text" in content:
46
+ answer += content["text"]
47
+ elif "citationsContent" in content:
48
+ for c in content["citationsContent"].get("citations", []):
49
+ url = c.get("location", {}).get("web", {}).get("url", "")
50
+ if url and url not in sources:
51
+ sources.append(url)
52
+ print(answer)
53
+ if sources:
54
+ print("\nSources:")
55
+ for u in sources:
56
+ print(" -", u)
57
+ usage = response.get("usage", {})
58
+ print(f"\n[gear=fast model=nova-2-lite total_tokens={usage.get('totalTokens')}]", file=sys.stderr)
59
+
60
+
61
+ def deep_search(question: str, model: str) -> None:
62
+ from aws_bedrock_token_generator import provide_token
63
+
64
+ token = provide_token(region="us-east-2")
65
+ body = json.dumps(
66
+ {
67
+ "model": model,
68
+ "input": question,
69
+ "tools": [{"type": "web_search"}],
70
+ "reasoning": {"effort": EFFORTS[model]},
71
+ }
72
+ ).encode()
73
+ req = urllib.request.Request(
74
+ MANTLE_URL,
75
+ data=body,
76
+ headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
77
+ method="POST",
78
+ )
79
+ try:
80
+ with urllib.request.urlopen(req, timeout=180) as resp:
81
+ data = json.loads(resp.read())
82
+ except urllib.error.HTTPError as e:
83
+ print(f"HTTP {e.code}: {e.read().decode(errors='replace')[:2000]}", file=sys.stderr)
84
+ sys.exit(1)
85
+
86
+ texts: list[str] = []
87
+ sources: list[str] = []
88
+ for item in data.get("output", []):
89
+ if item.get("type") == "message":
90
+ for c in item.get("content", []):
91
+ if c.get("type") == "output_text":
92
+ texts.append(c.get("text", ""))
93
+ for a in c.get("annotations", []):
94
+ if a.get("type") == "url_citation":
95
+ u = a.get("url", "")
96
+ if u and u not in sources:
97
+ sources.append(u)
98
+ print("\n".join(texts))
99
+ if sources:
100
+ print("\nSources:")
101
+ for u in sources:
102
+ print(" -", u)
103
+ usage = data.get("usage", {})
104
+ print(f"\n[gear=deep model={model} total_tokens={usage.get('total_tokens')}]", file=sys.stderr)
105
+
106
+
107
+ def main() -> None:
108
+ args = sys.argv[1:]
109
+ deep = False
110
+ model = MODELS["luna"]
111
+ if "--deep" in args:
112
+ deep = True
113
+ args.remove("--deep")
114
+ if "--model" in args:
115
+ i = args.index("--model")
116
+ try:
117
+ model = MODELS[args[i + 1]]
118
+ except (IndexError, KeyError):
119
+ print("--model must be 'luna' or 'sol'", file=sys.stderr)
120
+ sys.exit(2)
121
+ del args[i : i + 2]
122
+ if not deep:
123
+ print("--model only applies with --deep", file=sys.stderr)
124
+ sys.exit(2)
125
+ if not args:
126
+ print('usage: search.py [--deep] "<question>" [--model sol|luna]', file=sys.stderr)
127
+ sys.exit(2)
128
+ question = " ".join(args)
129
+
130
+ if deep:
131
+ deep_search(question, model)
132
+ else:
133
+ fast_search(question)
134
+
135
+
136
+ if __name__ == "__main__":
137
+ main()
@@ -53,7 +53,7 @@ Agents (2):
53
53
  Skills (3):
54
54
  - repo-conventions (workspace)
55
55
  - aws-knowledge (global)
56
- - nova-web-search (global)
56
+ - web-search (global)
57
57
 
58
58
  Instructions: .halo/INSTRUCTIONS.md (global fallback)
59
59
  INDEX.md → 2 docs followed: docs/architecture/auth.md, docs/dev/api.md
@@ -74,7 +74,7 @@ BUILTIN_AGENT_IDS = {
74
74
  }
75
75
  BUILTIN_SKILL_IDS = {
76
76
  "agent", "skill", "workspace", "cron", "send-file", "acp",
77
- "aws-knowledge", "nova-web-search", "self",
77
+ "aws-knowledge", "web-search", "self",
78
78
  }
79
79
 
80
80
  # --- helpers ---------------------------------------------------------------
@@ -1,65 +0,0 @@
1
- ---
2
- name: nova-web-search
3
- description: Real-time web search using Amazon Nova 2 Lite's built-in nova_grounding system tool. Use this skill when the user asks about current events, news, latest updates, prices, or any real-time information. This replaces the web_search tool.
4
- user-invocable: false
5
- ---
6
- # Nova Web Search
7
-
8
- Perform real-time web search using Amazon Nova 2 Lite's built-in `nova_grounding` system tool. Returns a synthesized answer with source URLs.
9
-
10
- ## How to Use
11
-
12
- Run the following Python script via `shell_exec`:
13
-
14
- ```python
15
- import boto3
16
- from botocore.config import Config
17
-
18
- bedrock = boto3.client("bedrock-runtime", region_name="us-east-1", config=Config(read_timeout=60))
19
-
20
- query = "YOUR_QUERY_HERE" # Replace with the actual search query
21
-
22
- response = bedrock.converse(
23
- modelId="us.amazon.nova-2-lite-v1:0",
24
- messages=[{"role": "user", "content": [{"text": query}]}],
25
- toolConfig={"tools": [{"systemTool": {"name": "nova_grounding"}}]}
26
- )
27
-
28
- answer = ""
29
- sources = []
30
- for content in response["output"]["message"]["content"]:
31
- if "text" in content:
32
- answer += content["text"]
33
- elif "citationsContent" in content:
34
- for c in content["citationsContent"].get("citations", []):
35
- url = c.get("location", {}).get("web", {}).get("url", "")
36
- if url and url not in sources:
37
- sources.append(url)
38
-
39
- print(answer)
40
- if sources:
41
- print("\nSources:", sources)
42
- ```
43
-
44
- ## Query Formulation
45
-
46
- Nova grounds better against full natural-language questions than against
47
- keyword stubs — raw keywords return more loosely-related sources, while
48
- a question gives Nova enough context to filter.
49
-
50
- | User Intent | Query Template |
51
- |---|---|
52
- | News / updates / latest developments | `"What are the latest news about X?"` |
53
- | Product / specs / concept lookup | `"Tell me about X, including specs, price, and latest updates."` |
54
- | General search | `"Search for information about X."` |
55
-
56
- **Examples:**
57
- - ✅ `"What are the latest news about the Middle East conflict?"`
58
- - ✅ `"Tell me about the NVIDIA RTX 5080, including specs and price."`
59
- - ✗ `"RTX 5080"` — terse keywords leave Nova guessing at intent
60
-
61
- ## Requirements
62
-
63
- - Region **must** be `us-east-1`
64
- - English queries yield better results than other languages
65
- - Model ID: `us.amazon.nova-2-lite-v1:0`