@polderlabs/bizar 3.23.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/README.md +72 -2
  2. package/bizar-dash/.bizar/activity.log +3 -0
  3. package/bizar-dash/.bizar/graph/.graphify_analysis.json +1540 -0
  4. package/bizar-dash/.bizar/graph/.graphify_labels.json +1 -0
  5. package/bizar-dash/.bizar/graph/GRAPH_REPORT.md +404 -0
  6. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/0661b816358db84dfdb7f10443db8a61152fd20b3fee950caf8fbf32920a1790.json +1 -0
  7. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/16da06d774142a6e5d74829ad982531286a6193ef798de8f593db029e123ec32.json +1 -0
  8. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/27e5697c01254c69c64c5a33896669073b40afb5ba88375102ae77fa5404136d.json +1 -0
  9. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/532997898027254aa4c0932243dc71d2f55888181232b65974961934997dc85b.json +1 -0
  10. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/84e3451a64c7f99f0cd6a88b0748d7426780486f53ab4935b7476b28c08f8293.json +1 -0
  11. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/88e5ea5f3e78fa69353fe9e6a8d1a05391d54605e25600cb125531eb5a6432ff.json +1 -0
  12. package/bizar-dash/.bizar/graph/cache/stat-index.json +1 -0
  13. package/bizar-dash/.bizar/graph/graph.html +307 -0
  14. package/bizar-dash/.bizar/graph/graph.json +40267 -0
  15. package/bizar-dash/.bizar/graph/manifest.json +762 -0
  16. package/bizar-dash/.graphifyignore +40 -0
  17. package/bizar-dash/.obsidian/README.md +14 -0
  18. package/bizar-dash/.obsidian/daily/2026-06-25.md +9 -0
  19. package/bizar-dash/.obsidian/vault.json +7 -0
  20. package/bizar-dash/.od-skills/web-prototype-e1429c1737/SKILL.md +103 -0
  21. package/bizar-dash/.od-skills/web-prototype-e1429c1737/assets/template.html +338 -0
  22. package/bizar-dash/.od-skills/web-prototype-e1429c1737/example.html +81 -0
  23. package/bizar-dash/.od-skills/web-prototype-e1429c1737/open-design.json +172 -0
  24. package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/checklist.md +44 -0
  25. package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/layouts.md +247 -0
  26. package/bizar-dash/CHANGELOG.md +474 -0
  27. package/bizar-dash/DESIGN.md +136 -0
  28. package/bizar-dash/dist/assets/main-B3RgW6FS.js +331 -0
  29. package/bizar-dash/dist/assets/main-B3RgW6FS.js.map +1 -0
  30. package/bizar-dash/dist/assets/main-DlJ8wgLR.css +1 -0
  31. package/bizar-dash/dist/assets/mobile-CsZQAswA.css +1 -0
  32. package/bizar-dash/dist/assets/mobile-DbxIw8BG.js +2 -0
  33. package/bizar-dash/dist/assets/mobile-DbxIw8BG.js.map +1 -0
  34. package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js +354 -0
  35. package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js.map +1 -0
  36. package/bizar-dash/dist/index.html +40 -0
  37. package/bizar-dash/dist/mobile.html +22 -0
  38. package/bizar-dash/plans/debug-plan/comments.json +1 -0
  39. package/bizar-dash/plans/debug-plan/meta.json +10 -0
  40. package/bizar-dash/plans/debug-plan/plan.json +174 -0
  41. package/bizar-dash/scripts/smoke-bg-retry.mjs +246 -0
  42. package/bizar-dash/src/cli/dashboard-ports.mjs +488 -0
  43. package/bizar-dash/src/cli.mjs +522 -0
  44. package/bizar-dash/src/server/activity-log.mjs +174 -0
  45. package/bizar-dash/src/server/agents-store.mjs +465 -0
  46. package/bizar-dash/src/server/api.mjs +138 -0
  47. package/bizar-dash/src/server/artifact-template.html +810 -0
  48. package/bizar-dash/src/server/artifacts-store.mjs +733 -0
  49. package/bizar-dash/src/server/auth.mjs +381 -0
  50. package/bizar-dash/src/server/background-store.mjs +447 -0
  51. package/bizar-dash/src/server/bg-poller.mjs +305 -0
  52. package/bizar-dash/src/server/bg-retry.mjs +574 -0
  53. package/bizar-dash/src/server/browser.mjs +40 -0
  54. package/bizar-dash/src/server/diagnostics-store.mjs +148 -0
  55. package/bizar-dash/src/server/dialog-poller.mjs +105 -0
  56. package/bizar-dash/src/server/dialog-store.mjs +177 -0
  57. package/bizar-dash/src/server/glyphs/mdx-compiler.mjs +583 -0
  58. package/bizar-dash/src/server/lib/path-safe.mjs +283 -0
  59. package/bizar-dash/src/server/memory-git.mjs +249 -0
  60. package/bizar-dash/src/server/memory-schema.mjs +145 -0
  61. package/bizar-dash/src/server/memory-secrets.mjs +84 -0
  62. package/bizar-dash/src/server/memory-store.mjs +424 -0
  63. package/bizar-dash/src/server/mod-security.mjs +398 -0
  64. package/bizar-dash/src/server/mods-loader.mjs +1067 -0
  65. package/bizar-dash/src/server/notifications-store.mjs +243 -0
  66. package/bizar-dash/src/server/obsidian-store.mjs +318 -0
  67. package/bizar-dash/src/server/opencode-runner.mjs +279 -0
  68. package/bizar-dash/src/server/pair-store.mjs +138 -0
  69. package/bizar-dash/src/server/projects-store.mjs +364 -0
  70. package/bizar-dash/src/server/providers-store.mjs +789 -0
  71. package/bizar-dash/src/server/routes/_shared.mjs +415 -0
  72. package/bizar-dash/src/server/routes/activity.mjs +110 -0
  73. package/bizar-dash/src/server/routes/agents.mjs +134 -0
  74. package/bizar-dash/src/server/routes/artifacts.mjs +279 -0
  75. package/bizar-dash/src/server/routes/auth.mjs +69 -0
  76. package/bizar-dash/src/server/routes/background.mjs +165 -0
  77. package/bizar-dash/src/server/routes/chat.mjs +485 -0
  78. package/bizar-dash/src/server/routes/config.mjs +129 -0
  79. package/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
  80. package/bizar-dash/src/server/routes/dialogs.mjs +86 -0
  81. package/bizar-dash/src/server/routes/fs.mjs +429 -0
  82. package/bizar-dash/src/server/routes/history.mjs +78 -0
  83. package/bizar-dash/src/server/routes/memory.mjs +499 -0
  84. package/bizar-dash/src/server/routes/misc.mjs +89 -0
  85. package/bizar-dash/src/server/routes/mods.mjs +359 -0
  86. package/bizar-dash/src/server/routes/notifications.mjs +54 -0
  87. package/bizar-dash/src/server/routes/obsidian.mjs +239 -0
  88. package/bizar-dash/src/server/routes/opencode-sessions.mjs +55 -0
  89. package/bizar-dash/src/server/routes/overview.mjs +91 -0
  90. package/bizar-dash/src/server/routes/pair.mjs +67 -0
  91. package/bizar-dash/src/server/routes/projects.mjs +175 -0
  92. package/bizar-dash/src/server/routes/providers.mjs +47 -0
  93. package/bizar-dash/src/server/routes/schedules.mjs +104 -0
  94. package/bizar-dash/src/server/routes/settings.mjs +51 -0
  95. package/bizar-dash/src/server/routes/skills.mjs +76 -0
  96. package/bizar-dash/src/server/routes/tasks.mjs +468 -0
  97. package/bizar-dash/src/server/routes/themes.mjs +51 -0
  98. package/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
  99. package/bizar-dash/src/server/routes-v2/events.mjs +107 -0
  100. package/bizar-dash/src/server/routes-v2/health.mjs +21 -0
  101. package/bizar-dash/src/server/routes-v2/index.mjs +90 -0
  102. package/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
  103. package/bizar-dash/src/server/schedules-runner.mjs +369 -0
  104. package/bizar-dash/src/server/schedules-store.mjs +268 -0
  105. package/bizar-dash/src/server/search-store.mjs +220 -0
  106. package/bizar-dash/src/server/serve-info.mjs +684 -0
  107. package/bizar-dash/src/server/server.mjs +796 -0
  108. package/bizar-dash/src/server/settings-store.mjs +45 -0
  109. package/bizar-dash/src/server/skills-store.mjs +300 -0
  110. package/bizar-dash/src/server/state.mjs +437 -0
  111. package/bizar-dash/src/server/tailscale-store.mjs +113 -0
  112. package/bizar-dash/src/server/task-delegator.mjs +824 -0
  113. package/bizar-dash/src/server/tasks-store.mjs +508 -0
  114. package/bizar-dash/src/server/tui.mjs +923 -0
  115. package/bizar-dash/src/server/update-store.mjs +168 -0
  116. package/bizar-dash/src/server/v2-auth-file.mjs +99 -0
  117. package/bizar-dash/src/server/v2-event-bus.mjs +128 -0
  118. package/bizar-dash/src/server/watcher.mjs +81 -0
  119. package/bizar-dash/src/server/yaml.mjs +238 -0
  120. package/bizar-dash/src/web/App.tsx +718 -0
  121. package/bizar-dash/src/web/MobileApp.tsx +408 -0
  122. package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +95 -0
  123. package/bizar-dash/src/web/components/ArtifactListDialog.tsx +55 -0
  124. package/bizar-dash/src/web/components/ArtifactViewer.tsx +143 -0
  125. package/bizar-dash/src/web/components/AuditDialog.tsx +79 -0
  126. package/bizar-dash/src/web/components/BgStatusBadge.tsx +32 -0
  127. package/bizar-dash/src/web/components/Button.tsx +56 -0
  128. package/bizar-dash/src/web/components/CanvasContextMenu.tsx +68 -0
  129. package/bizar-dash/src/web/components/Card.tsx +40 -0
  130. package/bizar-dash/src/web/components/CollapsibleSection.tsx +72 -0
  131. package/bizar-dash/src/web/components/CommandDialog.tsx +66 -0
  132. package/bizar-dash/src/web/components/EmptyState.tsx +30 -0
  133. package/bizar-dash/src/web/components/FileBrowser.tsx +633 -0
  134. package/bizar-dash/src/web/components/HelpDialog.tsx +55 -0
  135. package/bizar-dash/src/web/components/KillConfirmDialog.tsx +88 -0
  136. package/bizar-dash/src/web/components/Modal.tsx +215 -0
  137. package/bizar-dash/src/web/components/Notifications.tsx +241 -0
  138. package/bizar-dash/src/web/components/SearchModal.tsx +224 -0
  139. package/bizar-dash/src/web/components/Sidebar.tsx +66 -0
  140. package/bizar-dash/src/web/components/Spinner.tsx +19 -0
  141. package/bizar-dash/src/web/components/StatusBadge.tsx +25 -0
  142. package/bizar-dash/src/web/components/Tag.tsx +28 -0
  143. package/bizar-dash/src/web/components/Toast.tsx +150 -0
  144. package/bizar-dash/src/web/components/Topbar.tsx +368 -0
  145. package/bizar-dash/src/web/components/VisualPlanDialog.tsx +58 -0
  146. package/bizar-dash/src/web/components/chat/AgentChip.tsx +68 -0
  147. package/bizar-dash/src/web/components/chat/ChatBubble.tsx +60 -0
  148. package/bizar-dash/src/web/components/chat/ChatThread.tsx +88 -0
  149. package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +50 -0
  150. package/bizar-dash/src/web/components/chat/Composer.tsx +164 -0
  151. package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +54 -0
  152. package/bizar-dash/src/web/components/chat/EmptyState.tsx +21 -0
  153. package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +18 -0
  154. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +32 -0
  155. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +119 -0
  156. package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +19 -0
  157. package/bizar-dash/src/web/components/chat/MessageBubble.tsx +60 -0
  158. package/bizar-dash/src/web/components/chat/SessionList.tsx +153 -0
  159. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +9 -0
  160. package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +55 -0
  161. package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +36 -0
  162. package/bizar-dash/src/web/components/chat/index.ts +20 -0
  163. package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +18 -0
  164. package/bizar-dash/src/web/components/chat/useChat.ts +219 -0
  165. package/bizar-dash/src/web/components/chat/useSlashCommands.ts +47 -0
  166. package/bizar-dash/src/web/index.html +37 -0
  167. package/bizar-dash/src/web/lib/api.ts +193 -0
  168. package/bizar-dash/src/web/lib/markdown.tsx +59 -0
  169. package/bizar-dash/src/web/lib/types.ts +668 -0
  170. package/bizar-dash/src/web/lib/utils.ts +114 -0
  171. package/bizar-dash/src/web/lib/ws.ts +176 -0
  172. package/bizar-dash/src/web/main.tsx +34 -0
  173. package/bizar-dash/src/web/mobile/MobileApp.tsx +278 -0
  174. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +38 -0
  175. package/bizar-dash/src/web/mobile/MobileTopbar.tsx +115 -0
  176. package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +182 -0
  177. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +64 -0
  178. package/bizar-dash/src/web/mobile/components/MobileModal.tsx +123 -0
  179. package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +273 -0
  180. package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +374 -0
  181. package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +439 -0
  182. package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +143 -0
  183. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +180 -0
  184. package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +175 -0
  185. package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +191 -0
  186. package/bizar-dash/src/web/mobile/views/MobileMods.tsx +149 -0
  187. package/bizar-dash/src/web/mobile/views/MobileMore.tsx +121 -0
  188. package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +137 -0
  189. package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +26 -0
  190. package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +685 -0
  191. package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +172 -0
  192. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +388 -0
  193. package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +153 -0
  194. package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +564 -0
  195. package/bizar-dash/src/web/mobile.html +20 -0
  196. package/bizar-dash/src/web/mobile.tsx +13 -0
  197. package/bizar-dash/src/web/styles/chat.css +198 -0
  198. package/bizar-dash/src/web/styles/glyphs.css +960 -0
  199. package/bizar-dash/src/web/styles/main.css +8855 -0
  200. package/bizar-dash/src/web/styles/mobile-chat.css +5 -0
  201. package/bizar-dash/src/web/styles/mobile.css +1960 -0
  202. package/bizar-dash/src/web/views/Activity.tsx +1203 -0
  203. package/bizar-dash/src/web/views/Agents.tsx +701 -0
  204. package/bizar-dash/src/web/views/Artifacts.tsx +1480 -0
  205. package/bizar-dash/src/web/views/BackgroundAgents.tsx +450 -0
  206. package/bizar-dash/src/web/views/Chat.tsx +190 -0
  207. package/bizar-dash/src/web/views/Config.tsx +1603 -0
  208. package/bizar-dash/src/web/views/History.tsx +335 -0
  209. package/bizar-dash/src/web/views/ModView.tsx +207 -0
  210. package/bizar-dash/src/web/views/Mods.tsx +693 -0
  211. package/bizar-dash/src/web/views/Overview.tsx +812 -0
  212. package/bizar-dash/src/web/views/Providers.tsx +302 -0
  213. package/bizar-dash/src/web/views/Schedules.tsx +802 -0
  214. package/bizar-dash/src/web/views/Settings.tsx +1787 -0
  215. package/bizar-dash/src/web/views/Skills.tsx +431 -0
  216. package/bizar-dash/src/web/views/Tasks.tsx +1523 -0
  217. package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +613 -0
  218. package/bizar-dash/src/web/views/glyphs/components.tsx +1098 -0
  219. package/bizar-dash/templates/mod/FORMAT.md +76 -0
  220. package/bizar-dash/templates/mod/hello-mod/README.md +19 -0
  221. package/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
  222. package/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
  223. package/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
  224. package/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
  225. package/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
  226. package/bizar-dash/tests/dashboard-ports.test.mjs +138 -0
  227. package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +90 -0
  228. package/bizar-dash/tests/memory-git.test.mjs +138 -0
  229. package/bizar-dash/tests/memory-schema.test.mjs +198 -0
  230. package/bizar-dash/tests/memory-secrets.test.mjs +121 -0
  231. package/bizar-dash/tests/memory-store.test.mjs +390 -0
  232. package/bizar-dash/tests/memory-sync.test.mjs +141 -0
  233. package/bizar-dash/tests/mod-instructions.node.test.mjs +188 -0
  234. package/bizar-dash/tests/mod-security.test.mjs +273 -0
  235. package/bizar-dash/tests/mod-upgrade.node.test.mjs +357 -0
  236. package/bizar-dash/tests/no-agent-browser.node.test.mjs +98 -0
  237. package/bizar-dash/tests/obsidian-back-compat.test.mjs +199 -0
  238. package/bizar-dash/tests/opencode-runner.test.mjs +172 -0
  239. package/bizar-dash/tests/path-safe.test.mjs +108 -0
  240. package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +333 -0
  241. package/bizar-dash/tests/smoke-v2.mjs +212 -0
  242. package/bizar-dash/tests/strip-thinking.test.mjs +175 -0
  243. package/bizar-dash/tests/submit-feedback.test.mjs +353 -0
  244. package/bizar-dash/tests/tmux-wrap.test.mjs +145 -0
  245. package/bizar-dash/tests/yaml.test.mjs +128 -0
  246. package/bizar-dash/tsconfig.json +23 -0
  247. package/bizar-dash/vite.config.ts +27 -0
  248. package/cli/atomic.mjs +73 -0
  249. package/cli/bin.mjs +43 -46
  250. package/cli/copy.mjs +3 -0
  251. package/cli/dev-link.mjs +17 -1
  252. package/cli/init.mjs +135 -1
  253. package/cli/install.mjs +10 -23
  254. package/cli/memory-constants.mjs +59 -0
  255. package/cli/memory.mjs +757 -0
  256. package/cli/service.mjs +5 -6
  257. package/config/skills/obsidian/SKILL.md +213 -65
  258. package/install.sh +48 -18
  259. package/package.json +49 -14
  260. package/packages/sdk/LICENSE +21 -0
  261. package/packages/sdk/README.md +80 -0
  262. package/packages/sdk/src/client.ts +183 -0
  263. package/packages/sdk/src/errors.ts +120 -0
  264. package/packages/sdk/src/events.ts +153 -0
  265. package/packages/sdk/src/index.ts +63 -0
  266. package/packages/sdk/src/types.ts +176 -0
  267. package/packages/sdk/src/version.ts +4 -0
  268. package/packages/sdk/tests/client.test.ts +217 -0
  269. package/packages/sdk/tests/errors.test.ts +108 -0
  270. package/packages/sdk/tests/events.test.ts +139 -0
  271. package/packages/sdk/tests/fixtures/fetch-mock.ts +152 -0
  272. package/packages/sdk/tests/fixtures/sse-mock.ts +30 -0
  273. package/packages/sdk/tsconfig.json +25 -0
  274. package/packages/sdk/vitest.config.ts +9 -0
  275. package/plugins/bizar/LICENSE +21 -0
  276. package/plugins/bizar/README.md +448 -0
  277. package/plugins/bizar/dist/index.js +29901 -0
  278. package/plugins/bizar/index.ts +1484 -0
  279. package/plugins/bizar/scripts/check-forbidden-imports.sh +33 -0
  280. package/plugins/bizar/src/background-state.ts +551 -0
  281. package/plugins/bizar/src/background.ts +1250 -0
  282. package/plugins/bizar/src/commands-impl.ts +464 -0
  283. package/plugins/bizar/src/commands.ts +1127 -0
  284. package/plugins/bizar/src/dashboard-client.ts +233 -0
  285. package/plugins/bizar/src/event-stream.ts +606 -0
  286. package/plugins/bizar/src/fingerprint.ts +120 -0
  287. package/plugins/bizar/src/handoff.ts +79 -0
  288. package/plugins/bizar/src/http-client.ts +467 -0
  289. package/plugins/bizar/src/key-rotation.ts +218 -0
  290. package/plugins/bizar/src/logger.ts +144 -0
  291. package/plugins/bizar/src/loop.ts +176 -0
  292. package/plugins/bizar/src/opencode-runner.ts +390 -0
  293. package/plugins/bizar/src/options.ts +421 -0
  294. package/plugins/bizar/src/plan-fs.ts +323 -0
  295. package/plugins/bizar/src/reasoning-clean.ts +454 -0
  296. package/plugins/bizar/src/report.ts +178 -0
  297. package/plugins/bizar/src/research-prompt.ts +35 -0
  298. package/plugins/bizar/src/serve-info.ts +228 -0
  299. package/plugins/bizar/src/serve.ts +496 -0
  300. package/plugins/bizar/src/settings.ts +349 -0
  301. package/plugins/bizar/src/state.ts +298 -0
  302. package/plugins/bizar/src/tools/bg-collect.ts +104 -0
  303. package/plugins/bizar/src/tools/bg-get-comments.ts +239 -0
  304. package/plugins/bizar/src/tools/bg-kill.ts +87 -0
  305. package/plugins/bizar/src/tools/bg-spawn.ts +401 -0
  306. package/plugins/bizar/src/tools/bg-status.ts +99 -0
  307. package/plugins/bizar/src/tools/plan-action.ts +767 -0
  308. package/plugins/bizar/src/tools/read-glyph-feedback.ts +170 -0
  309. package/plugins/bizar/src/tools/wait-for-feedback.ts +402 -0
  310. package/plugins/bizar/tests/attach-handler-bug.test.ts +169 -0
  311. package/plugins/bizar/tests/background-state.test.ts +277 -0
  312. package/plugins/bizar/tests/background.test.ts +402 -0
  313. package/plugins/bizar/tests/block.test.ts +195 -0
  314. package/plugins/bizar/tests/canonical-key-order.test.ts +75 -0
  315. package/plugins/bizar/tests/commands-impl.test.ts +442 -0
  316. package/plugins/bizar/tests/commands.test.ts +548 -0
  317. package/plugins/bizar/tests/config.test.ts +122 -0
  318. package/plugins/bizar/tests/dashboard-client.test.ts +159 -0
  319. package/plugins/bizar/tests/dispose.test.ts +336 -0
  320. package/plugins/bizar/tests/event-stream.test.ts +409 -0
  321. package/plugins/bizar/tests/event.test.ts +262 -0
  322. package/plugins/bizar/tests/fingerprint.test.ts +162 -0
  323. package/plugins/bizar/tests/http-client.test.ts +404 -0
  324. package/plugins/bizar/tests/init-helpers.test.ts +203 -0
  325. package/plugins/bizar/tests/integration/slash-command.test.ts +348 -0
  326. package/plugins/bizar/tests/integration/tool-routing.test.ts +314 -0
  327. package/plugins/bizar/tests/key-rotation.test.ts +396 -0
  328. package/plugins/bizar/tests/loop.test.ts +397 -0
  329. package/plugins/bizar/tests/options.test.ts +276 -0
  330. package/plugins/bizar/tests/reasoning-clean.test.ts +422 -0
  331. package/plugins/bizar/tests/serve.test.ts +339 -0
  332. package/plugins/bizar/tests/settings.test.ts +351 -0
  333. package/plugins/bizar/tests/stall-think.test.ts +750 -0
  334. package/plugins/bizar/tests/state.test.ts +276 -0
  335. package/plugins/bizar/tests/tools/bg-collect.test.ts +337 -0
  336. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +485 -0
  337. package/plugins/bizar/tests/tools/bg-kill.test.ts +235 -0
  338. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +147 -0
  339. package/plugins/bizar/tests/tools/bg-spawn.test.ts +311 -0
  340. package/plugins/bizar/tests/tools/bg-status.test.ts +217 -0
  341. package/plugins/bizar/tests/tools/opencode-runner.test.ts +115 -0
  342. package/plugins/bizar/tests/tools/plan-action.test.ts +599 -0
  343. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +253 -0
  344. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +390 -0
  345. package/plugins/bizar/tests/update-deadlock.test.ts +145 -0
  346. package/plugins/bizar/tsconfig.json +29 -0
@@ -0,0 +1,401 @@
1
+ /**
2
+ * plugins/bizar/src/tools/bg-spawn.ts
3
+ *
4
+ * v0.8.0 — `bizar_spawn_background` tool, refactored to spawn one
5
+ * `opencode run` subprocess per agent (instead of POSTing to a
6
+ * passive `opencode serve` HTTP API). See opencode-runner.ts for the
7
+ * spawning implementation; see ../opencode-runner.ts for the
8
+ * rationale and the wire format we parse.
9
+ *
10
+ * Spec §1, §6.3, §7.1.
11
+ */
12
+ import { tool } from "@opencode-ai/plugin";
13
+ import { z } from "zod";
14
+
15
+ import { generateInstanceId, generateMessageId } from "../background.js";
16
+ import type { InstanceManager } from "../background.js";
17
+ import type { Logger } from "../logger.js";
18
+ import { spawnAgent } from "../opencode-runner.js";
19
+ import { resolve as pathResolve } from "node:path";
20
+ import { homedir } from "node:os";
21
+
22
+ /** Spec §7.3: `timeoutMs` clamped to [1000, 1800000] (1s..30min). */
23
+ const TIMEOUT_MIN_MS = 1000;
24
+ const TIMEOUT_MAX_MS = 1_800_000;
25
+ const TIMEOUT_DEFAULT_MS = 300_000;
26
+
27
+ /**
28
+ * Agents whose `mode` is `primary` and therefore accepted by
29
+ * `opencode run --agent <name>`. opencode 1.17.x silently REJECTS
30
+ * any agent whose `mode` is `subagent` with a "Falling back to default
31
+ * agent" warning — the bg instance then runs odin regardless of what
32
+ * the caller asked for. See `wiki/Troubleshooting.md` for the full
33
+ * postmortem.
34
+ *
35
+ * To invoke a subagent cleanly, `spawnAgent` below routes the spawn
36
+ * through a primary agent (odin) with an explicit delegation prompt
37
+ * that tells odin to call its `task` tool to spawn the requested
38
+ * subagent. Subagent changes here MUST mirror `config/agents/*.md`.
39
+ *
40
+ * Exported for testability — the test asserts the set is in sync with
41
+ * the agent configs.
42
+ */
43
+ // v3.20.11 — kept in sync with config/agents/*.md via the
44
+ // "is in sync with the on-disk agent configs" test below. The set
45
+ // must equal exactly the agents whose frontmatter declares
46
+ // `mode: primary` (currently odin, quick, browser-harness).
47
+ export const PRIMARY_AGENTS: ReadonlySet<string> = new Set([
48
+ "odin",
49
+ "quick",
50
+ "browser-harness",
51
+ ]);
52
+
53
+ /**
54
+ * Decide whether a given agent name needs the delegation wrapper.
55
+ * Pure function, exported for testability.
56
+ */
57
+ export function needsDelegationWrapper(agent: string): boolean {
58
+ return !PRIMARY_AGENTS.has(agent);
59
+ }
60
+
61
+ /**
62
+ * Build the delegation prompt that wraps a subagent request. The
63
+ * wrapper runs as odin; odin must call its `task` tool to spawn the
64
+ * requested subagent and report the subagent's final output verbatim.
65
+ *
66
+ * The prompt is intentionally directive — LLMs are non-deterministic,
67
+ * and a passive "please consider delegating" framing is too easily
68
+ * ignored. The explicit "do not interpret", "do not perform the work
69
+ * yourself", and "report only the subagent's output" constraints give
70
+ * the LLM no room to drift.
71
+ *
72
+ * Exported for testability — the test asserts the prompt is directive
73
+ * enough to override Odin's default routing behavior.
74
+ */
75
+ export function buildDelegationPrompt(requestedAgent: string, userPrompt: string): string {
76
+ return [
77
+ "You are Odin, the BizarHarness router.",
78
+ "",
79
+ "A background agent session has been requested with a SPECIFIC subagent.",
80
+ "Your only job is to delegate to that subagent using the `task` tool. Do NOT",
81
+ "perform the work yourself. Do NOT interpret the user's prompt. Do NOT ask",
82
+ "clarifying questions. Do NOT route to any other agent.",
83
+ "",
84
+ `Requested subagent: ${requestedAgent}`,
85
+ "",
86
+ "Task prompt to pass verbatim to the subagent:",
87
+ "--- BEGIN USER PROMPT ---",
88
+ userPrompt,
89
+ "--- END USER PROMPT ---",
90
+ "",
91
+ `Use the task tool with agent="${requestedAgent}" and the exact prompt above.`,
92
+ "After the subagent finishes, report its final output VERBATIM.",
93
+ "Do not summarize, do not add commentary, do not run any other tools.",
94
+ ].join("\n");
95
+ }
96
+
97
+ /** Mirrors plugins/bizar/src/http-client.ts ModelOverride. */
98
+ export interface ModelOverride {
99
+ providerID: string;
100
+ modelID: string;
101
+ }
102
+
103
+ /**
104
+ * Parse "providerID/modelID" into a ModelOverride. Returns null when
105
+ * the input is empty (use the agent's default) or malformed.
106
+ */
107
+ function parseModel(raw: string | undefined): ModelOverride | null {
108
+ if (raw === undefined || raw === null) return null;
109
+ const trimmed = String(raw).trim();
110
+ if (trimmed === "") return null;
111
+ const idx = trimmed.indexOf("/");
112
+ if (idx <= 0 || idx === trimmed.length - 1) return null;
113
+ const providerID = trimmed.slice(0, idx).trim();
114
+ const modelID = trimmed.slice(idx + 1).trim();
115
+ if (!providerID || !modelID) return null;
116
+ return { providerID, modelID };
117
+ }
118
+
119
+ export interface BgSpawnDeps {
120
+ instanceManager: InstanceManager;
121
+ worktree: string;
122
+ logger: Logger;
123
+ }
124
+
125
+ /**
126
+ * Compute the LogWriter's actual log path for a given instanceId.
127
+ * The plugin's `LogWriter` (../report.ts:147) writes to
128
+ * `${logDir}/${sessionId}.log` where `logDir` defaults to
129
+ * `~/.cache/bizar/logs` (overridable via the `BIZAR_LOG_DIR` env
130
+ * var). The instanceId is what we know at spawn time — the opencode
131
+ * sessionId is generated later by the subprocess and we don't
132
+ * pre-allocate it. We therefore use the instanceId as the log file
133
+ * name and let the runner append to it.
134
+ */
135
+ function buildLogPath(instanceId: string): string {
136
+ const logDir = process.env.BIZAR_LOG_DIR || pathResolve(homedir(), ".cache", "bizar", "logs");
137
+ return pathResolve(logDir, `${instanceId}.log`);
138
+ }
139
+
140
+ /**
141
+ * Build the `bizar_spawn_background` tool. The plugin wires the
142
+ * result into `Hooks.tool`. The `deps` closure carries the
143
+ * per-process state (InstanceManager, worktree, logger).
144
+ */
145
+ export function createBgSpawnTool(deps: BgSpawnDeps) {
146
+ return tool({
147
+ description:
148
+ "Spawn a background agent that runs asynchronously as a separate `opencode run` subprocess. " +
149
+ "Only Odin may call this tool. " +
150
+ "Returns an instanceId immediately (sub-second), then the agent runs to completion in the background. " +
151
+ "Use `bizar_status` / `bizar_collect` / `bizar_kill` to manage the instance. " +
152
+ "Use `bizar_bg_view` (CLI) to watch all running agents in a tmux split window. " +
153
+ "IMPORTANT: do NOT block waiting for the agent. Return control to the user right after spawning.",
154
+ args: {
155
+ agent: z.string().min(1).describe("Agent name to spawn (e.g. 'mimir', 'thor', 'tyr')."),
156
+ prompt: z.string().min(1).describe("User prompt for the background session."),
157
+ model: z
158
+ .string()
159
+ .optional()
160
+ .describe("Optional model override in 'providerID/modelID' format."),
161
+ timeoutMs: z
162
+ .number()
163
+ .int()
164
+ .positive()
165
+ .optional()
166
+ .describe("Collect-time timeout in ms (1s..30min, default 5min)."),
167
+ persistent: z
168
+ .boolean()
169
+ .optional()
170
+ .default(false)
171
+ .describe("When true, auto-restart on terminal failure (up to maxRestarts)."),
172
+ maxRestarts: z
173
+ .number()
174
+ .int()
175
+ .min(1)
176
+ .max(10)
177
+ .optional()
178
+ .default(3)
179
+ .describe("Number of auto-restart attempts before giving up."),
180
+ },
181
+ execute: async (rawArgs, ctx) => {
182
+ // 1. Odin-only (MEDIUM-26).
183
+ if (ctx.agent !== "odin") {
184
+ return {
185
+ output: JSON.stringify({
186
+ error:
187
+ "Only Odin can spawn background agents. Use the task tool for sync work, or ask Odin to spawn a background agent.",
188
+ }),
189
+ };
190
+ }
191
+
192
+ const args = rawArgs as {
193
+ agent: string;
194
+ prompt: string;
195
+ model?: string;
196
+ timeoutMs?: number;
197
+ persistent?: boolean;
198
+ maxRestarts?: number;
199
+ };
200
+
201
+ // 2. Validate the model parameter (LOW-34 / §1.4).
202
+ let modelOverride: ModelOverride | undefined;
203
+ if (args.model !== undefined && args.model !== "") {
204
+ const m = parseModel(args.model);
205
+ if (m === null) {
206
+ return {
207
+ output: JSON.stringify({
208
+ error: `model must be in "providerID/modelID" format (e.g. "opencode/deepseek-v4-flash-free"). Omit to use the agent's default.`,
209
+ }),
210
+ };
211
+ }
212
+ modelOverride = m;
213
+ }
214
+
215
+ // 3. Clamp timeoutMs (MEDIUM-33 / §7.3).
216
+ const requested = args.timeoutMs ?? TIMEOUT_DEFAULT_MS;
217
+ if (requested < TIMEOUT_MIN_MS || requested > TIMEOUT_MAX_MS) {
218
+ return {
219
+ output: JSON.stringify({
220
+ error: `timeoutMs must be between ${TIMEOUT_MIN_MS} (1s) and ${TIMEOUT_MAX_MS} (30min). Got ${requested}.`,
221
+ }),
222
+ };
223
+ }
224
+ const timeoutMs = requested;
225
+
226
+ // 4. Generate the instanceId and seed the manager (track BEFORE
227
+ // the subprocess starts, so a fast-exiting agent is still
228
+ // queryable via bizar_status).
229
+ const instanceId = generateInstanceId();
230
+ const logPath = buildLogPath(instanceId);
231
+ const draft = {
232
+ instanceId,
233
+ sessionId: "", // filled in once the opencode run reports it
234
+ agent: args.agent,
235
+ model: modelOverride
236
+ ? `${modelOverride.providerID}/${modelOverride.modelID}`
237
+ : "agent-default",
238
+ promptPreview: args.prompt.slice(0, 200),
239
+ prompt: args.prompt, // store full prompt for restart support
240
+ parentAgent: ctx.agent,
241
+ logPath,
242
+ timeoutMs,
243
+ toolCallCount: 0,
244
+ // v0.5.5 — persistent auto-restart
245
+ persistent: args.persistent ?? false,
246
+ maxRestarts: args.maxRestarts ?? 3,
247
+ restartCount: 0,
248
+ };
249
+ const addRes = await deps.instanceManager.add(draft);
250
+ if (addRes === "cap_reached") {
251
+ return {
252
+ output: JSON.stringify({
253
+ error: `Max concurrent instances reached. Wait for one to finish or call bizar_kill.`,
254
+ }),
255
+ };
256
+ }
257
+
258
+ // 5. Spawn the opencode run subprocess. The runner returns
259
+ // when the opencode child has reported its session id in
260
+ // the structured log stream (typically <500ms).
261
+ //
262
+ // opencode 1.17.x rejects `--agent <subagent>` with a silent
263
+ // fallback to the default agent. To invoke a subagent we
264
+ // route through a primary wrapper (odin) with a delegation
265
+ // prompt. The instance record still attributes the work to
266
+ // the requested agent — only the opencode process is odin's.
267
+ const messageID = generateMessageId();
268
+ const isPrimary = PRIMARY_AGENTS.has(args.agent);
269
+ const wrapperAgent = isPrimary ? args.agent : "odin";
270
+ const wrapperPrompt = isPrimary
271
+ ? args.prompt
272
+ : buildDelegationPrompt(args.agent, args.prompt);
273
+ let spawnRes: Awaited<ReturnType<typeof spawnAgent>>;
274
+ try {
275
+ spawnRes = await spawnAgent({
276
+ prompt: wrapperPrompt,
277
+ agent: wrapperAgent,
278
+ model: modelOverride,
279
+ worktree: deps.worktree,
280
+ logPath,
281
+ title: `bgr:${args.agent}:${instanceId}:${messageID}`,
282
+ });
283
+ } catch (err: unknown) {
284
+ const msg = err instanceof Error ? err.message : String(err);
285
+ await deps.instanceManager.update(instanceId, {
286
+ status: "failed",
287
+ error: `spawnAgent threw: ${msg}`,
288
+ completedAt: Date.now(),
289
+ });
290
+ return {
291
+ output: JSON.stringify({
292
+ error: `spawn crashed: ${msg}`,
293
+ instanceId,
294
+ sessionId: null,
295
+ status: "failed",
296
+ }),
297
+ };
298
+ }
299
+
300
+ if (!spawnRes.ok || !spawnRes.sessionId) {
301
+ await deps.instanceManager.update(instanceId, {
302
+ status: "failed",
303
+ error: spawnRes.error || "opencode run failed before reporting session id",
304
+ completedAt: Date.now(),
305
+ });
306
+ return {
307
+ output: JSON.stringify({
308
+ error: `spawn failed: ${spawnRes.error || "no session id"}`,
309
+ instanceId,
310
+ sessionId: null,
311
+ status: "failed",
312
+ }),
313
+ };
314
+ }
315
+
316
+ // 6. Persist the sessionId in the instance state. The high-level
317
+ // `status` stays "pending" until the runner reports a terminal
318
+ // state; we record the processId and the runner's
319
+ // intermediate states in the dedicated fields.
320
+ await deps.instanceManager.update(instanceId, {
321
+ sessionId: spawnRes.sessionId,
322
+ status: "running",
323
+ processId: spawnRes.processId,
324
+ runnerState: "running",
325
+ sessionIdAt: Date.now(),
326
+ });
327
+
328
+ // 7. Wire the runner's exit event to the instance state. When
329
+ // the opencode run subprocess exits, the runner updates
330
+ // the status (done / failed / killed) and triggers the
331
+ // persistent auto-restart flow if appropriate.
332
+ if (spawnRes.processId !== undefined) {
333
+ const { onExit } = await import("../opencode-runner.js");
334
+ onExit(spawnRes.processId, (status) => {
335
+ // Map runner states to BackgroundStatus. The runner reports
336
+ // "starting" | "running" | "done" | "failed" | "killed";
337
+ // BackgroundStatus has "pending" | "running" | "done" |
338
+ // "failed" | "killed" | "timed_out". "starting" maps to
339
+ // "running" (in-flight, no terminal state).
340
+ const mapped: "pending" | "running" | "done" | "failed" | "killed" =
341
+ status.state === "starting" || status.state === "running"
342
+ ? "running"
343
+ : status.state;
344
+
345
+ const update: Parameters<InstanceManager["update"]>[1] = {
346
+ status: mapped,
347
+ runnerState: status.state,
348
+ completedAt: status.endedAt ?? Date.now(),
349
+ };
350
+ if (status.exitCode !== undefined) update.exitCode = status.exitCode;
351
+ if (status.error) {
352
+ update.runnerError = status.error;
353
+ update.error = status.error;
354
+ }
355
+ if (status.endedAt !== undefined) update.runnerEndedAt = status.endedAt;
356
+ // Best-effort: if the InstanceManager has gone away (e.g.
357
+ // the plugin restarted), the update silently no-ops.
358
+ deps.instanceManager
359
+ .update(instanceId, update)
360
+ .then(() => {
361
+ // Persistent auto-restart: only for natural failures,
362
+ // not for explicit kills or successes.
363
+ if (status.state === "failed") {
364
+ return deps.instanceManager.maybeAutoRestart(instanceId);
365
+ }
366
+ return undefined;
367
+ })
368
+ .catch((err: unknown) => {
369
+ deps.logger.warn(
370
+ `bizar: bg-spawn exit update failed for ${instanceId}: ${
371
+ err instanceof Error ? err.message : String(err)
372
+ }`,
373
+ );
374
+ });
375
+ });
376
+ }
377
+
378
+ // 8. Return the spawn result. v0.8.0 message: the agent is
379
+ // running in the background; Odin should return control to
380
+ // the user immediately and not block waiting.
381
+ return {
382
+ output: JSON.stringify({
383
+ instanceId,
384
+ sessionId: spawnRes.sessionId,
385
+ processId: spawnRes.processId,
386
+ status: "running",
387
+ message:
388
+ "Background agent started. It will run to completion in a separate `opencode run` subprocess. " +
389
+ "Use `bizar_status <instanceId>` to check progress, `bizar_collect <instanceId>` to wait for the result, " +
390
+ "or `bizar_kill <instanceId>` to stop it. Run `bizar bg view` in another terminal to watch all running agents live.",
391
+ nextSteps: [
392
+ "Tell the user the agent is running and approximately how long they should expect to wait",
393
+ "If the user wants the result now, call `bizar_collect <instanceId>` (with a reasonable timeout)",
394
+ "If the user wants to stop the agent, call `bizar_kill <instanceId>`",
395
+ "Do NOT block waiting for the result unless the user explicitly asked for it",
396
+ ],
397
+ }),
398
+ };
399
+ },
400
+ });
401
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * bg-status.ts
3
+ *
4
+ * `bizar_status` tool (v0.4.2 spec §7.1).
5
+ *
6
+ * Read-only — available to ALL agents (Vör, Frigg, Quick, Odin) per
7
+ * §6.3. Returns a snapshot of the in-memory instance list, optionally
8
+ * filtered by `instanceId`. The result is the `InstanceView` shape
9
+ * (subset of `BackgroundState`); see `background.ts` for the type.
10
+ *
11
+ * Returns:
12
+ * `Array<{ instanceId, agent, status, startedAt, toolCallCount,
13
+ * promptPreview, resultPreview?, error?, parentAgent,
14
+ * parentInstanceId? }>`
15
+ */
16
+
17
+ import { tool } from "@opencode-ai/plugin";
18
+ import { z } from "zod";
19
+
20
+ import type { InstanceManager, InstanceView } from "../background.js";
21
+ import type { Logger } from "../logger.js";
22
+
23
+ export interface BgStatusDeps {
24
+ instanceManager: InstanceManager;
25
+ logger: Logger;
26
+ }
27
+
28
+ /**
29
+ * Build the `bizar_status` tool. The plugin wires the result into
30
+ * `Hooks.tool`. The `deps` closure carries the InstanceManager.
31
+ */
32
+ export function createBgStatusTool(deps: BgStatusDeps) {
33
+ return tool({
34
+ description:
35
+ "List background instances. Read-only; available to all agents. " +
36
+ "Pass an instanceId to inspect one instance.",
37
+ args: {
38
+ instanceId: z
39
+ .string()
40
+ .optional()
41
+ .describe("Optional instanceId. If omitted, all instances are returned."),
42
+ },
43
+ execute: async (rawArgs) => {
44
+ const args = rawArgs as { instanceId?: string };
45
+ try {
46
+ if (args.instanceId) {
47
+ const inst = await deps.instanceManager.get(args.instanceId);
48
+ if (!inst) {
49
+ return {
50
+ output: JSON.stringify({ error: `instance not found`, instanceId: args.instanceId }),
51
+ };
52
+ }
53
+ const view: InstanceView = toViewShape(inst);
54
+ return { output: JSON.stringify([view]) };
55
+ }
56
+ const list = await deps.instanceManager.list();
57
+ return { output: JSON.stringify(list) };
58
+ } catch (err: unknown) {
59
+ deps.logger.warn(
60
+ `bizar: status failed: ${err instanceof Error ? err.message : String(err)}`,
61
+ );
62
+ return {
63
+ output: JSON.stringify({ error: `status failed: ${err instanceof Error ? err.message : String(err)}` }),
64
+ };
65
+ }
66
+ },
67
+ });
68
+ }
69
+
70
+ // --- Helpers --------------------------------------------------------------
71
+
72
+ function toViewShape(inst: import("../background-state.js").BackgroundState): InstanceView {
73
+ const v: InstanceView = {
74
+ instanceId: inst.instanceId,
75
+ agent: inst.agent,
76
+ status: inst.status,
77
+ startedAt: inst.startedAt,
78
+ toolCallCount: inst.toolCallCount,
79
+ promptPreview: inst.promptPreview,
80
+ parentAgent: inst.parentAgent,
81
+ sessionId: inst.sessionId,
82
+ // v0.3.0 — always surface lastEventAt so callers can compute the
83
+ // current "freshness" of an instance (now - lastEventAt).
84
+ lastEventAt: inst.lastEventAt,
85
+ };
86
+ if (inst.completedAt !== undefined) v.completedAt = inst.completedAt;
87
+ if (inst.resultPreview !== undefined) v.resultPreview = inst.resultPreview;
88
+ if (inst.error !== undefined) v.error = inst.error;
89
+ if (inst.parentInstanceId !== undefined) v.parentInstanceId = inst.parentInstanceId;
90
+ // v0.3.0 — only surface intervention metadata when at least one
91
+ // intervention has actually been sent.
92
+ const interventionCount = inst.interventionCount ?? 0;
93
+ if (interventionCount > 0) {
94
+ v.interventionCount = interventionCount;
95
+ if (inst.interventionAt !== undefined) v.interventionAt = inst.interventionAt;
96
+ if (inst.interventionReason !== undefined) v.interventionReason = inst.interventionReason;
97
+ }
98
+ return v;
99
+ }