@polderlabs/bizar 6.2.4 → 6.3.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 (332) hide show
  1. package/.claude/CLAUDE.md +15 -0
  2. package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/agents/baldr.md +49 -0
  7. package/.claude/agents/forseti.md +51 -0
  8. package/.claude/agents/frigg.md +42 -0
  9. package/.claude/agents/heimdall.md +33 -0
  10. package/.claude/agents/hermod.md +53 -0
  11. package/.claude/agents/mimir.md +49 -0
  12. package/.claude/agents/odin.md +287 -0
  13. package/.claude/agents/quick.md +34 -0
  14. package/.claude/agents/semble-search.md +50 -0
  15. package/.claude/agents/thor.md +53 -0
  16. package/.claude/agents/tyr.md +56 -0
  17. package/.claude/agents/vidarr.md +54 -0
  18. package/.claude/agents/vor.md +53 -0
  19. package/.claude/commands/audit.md +25 -0
  20. package/.claude/commands/bizar.md +22 -0
  21. package/.claude/commands/explain.md +17 -0
  22. package/.claude/commands/init.md +29 -0
  23. package/.claude/commands/learn.md +44 -0
  24. package/.claude/commands/plan.md +35 -0
  25. package/.claude/commands/plow-through.md +50 -0
  26. package/.claude/commands/pr-review.md +49 -0
  27. package/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/commands/team.md +132 -0
  30. package/.claude/commands/test.md +62 -0
  31. package/.claude/commands/validate.md +68 -0
  32. package/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/hooks/README.md +92 -0
  34. package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/settings.json +116 -0
  41. package/.claude/skills/9router/SKILL.md +80 -0
  42. package/.claude/skills/9router-chat/SKILL.md +73 -0
  43. package/.claude/skills/9router-embeddings/SKILL.md +69 -0
  44. package/.claude/skills/9router-image/SKILL.md +86 -0
  45. package/.claude/skills/9router-stt/SKILL.md +79 -0
  46. package/.claude/skills/9router-tts/SKILL.md +80 -0
  47. package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  48. package/.claude/skills/9router-web-search/SKILL.md +91 -0
  49. package/.claude/skills/bizar/README.md +9 -0
  50. package/.claude/skills/bizar/SKILL.md +450 -0
  51. package/.claude/skills/cpp-coding-standards/README.md +28 -0
  52. package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  53. package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  54. package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  55. package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  56. package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  57. package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  58. package/.claude/skills/cpp-testing/README.md +28 -0
  59. package/.claude/skills/cpp-testing/SKILL.md +304 -0
  60. package/.claude/skills/cpp-testing/references/coverage.md +370 -0
  61. package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  62. package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  63. package/.claude/skills/cpp-testing/references/mocking.md +364 -0
  64. package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  65. package/.claude/skills/cubesandbox/SKILL.md +148 -0
  66. package/.claude/skills/embedded-esp-idf/README.md +41 -0
  67. package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  68. package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  69. package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  70. package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  71. package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  72. package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  73. package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  74. package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  75. package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  76. package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  77. package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  78. package/.claude/skills/glyph/SKILL.md +163 -0
  79. package/.claude/skills/harness-engineering/SKILL.md +143 -0
  80. package/.claude/skills/lightrag/SKILL.md +81 -0
  81. package/.claude/skills/memory-protocol/SKILL.md +105 -0
  82. package/.claude/skills/obsidian/SKILL.md +306 -0
  83. package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  84. package/.claude/skills/self-improvement/SKILL.md +64 -0
  85. package/README.md +87 -59
  86. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-B58aiJiE.js} +1 -1
  87. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-B58aiJiE.js.map} +1 -1
  88. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js → MobileChat-BJrqwVDd.js} +1 -1
  89. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-BJrqwVDd.js.map} +1 -1
  90. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js → MobileSettings-CEQNJNLJ.js} +1 -1
  91. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-CEQNJNLJ.js.map} +1 -1
  92. package/bizar-dash/dist/assets/{main-DYiZqMrn.js → main-IvfQAOfy.js} +1 -1
  93. package/bizar-dash/dist/assets/{main-DYiZqMrn.js.map → main-IvfQAOfy.js.map} +1 -1
  94. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js → markdown-tOLaD6nm.js} +1 -1
  95. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-tOLaD6nm.js.map} +1 -1
  96. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js → mobile-DYCHcUpq.js} +1 -1
  97. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DYCHcUpq.js.map} +1 -1
  98. package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js → mobile-layout-CBHjpwsb.js} +2 -2
  99. package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js.map → mobile-layout-CBHjpwsb.js.map} +1 -1
  100. package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js → useSlashCommands-Bd7_FA6U.js} +2 -2
  101. package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js.map → useSlashCommands-Bd7_FA6U.js.map} +1 -1
  102. package/bizar-dash/dist/assets/{vendor-CeHGtduv.js → vendor-C843201K.js} +12 -12
  103. package/bizar-dash/dist/assets/vendor-C843201K.js.map +1 -0
  104. package/bizar-dash/dist/index.html +6 -6
  105. package/bizar-dash/dist/mobile.html +2 -2
  106. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  107. package/bizar-dash/src/server/api.mjs +4 -4
  108. package/bizar-dash/src/server/background-store.mjs +15 -16
  109. package/bizar-dash/src/server/bg-poller.mjs +25 -35
  110. package/bizar-dash/src/server/bg-retry.mjs +66 -194
  111. package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  112. package/bizar-dash/src/server/claude-info.mjs +412 -0
  113. package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
  114. package/bizar-dash/src/server/claude-sdk.mjs +191 -0
  115. package/bizar-dash/src/server/providers-store.mjs +22 -49
  116. package/bizar-dash/src/server/routes/background.mjs +24 -20
  117. package/bizar-dash/src/server/routes/chat.mjs +223 -326
  118. package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  119. package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  120. package/bizar-dash/src/server/routes/tasks.mjs +20 -28
  121. package/bizar-dash/src/server/task-delegator.mjs +76 -77
  122. package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
  123. package/bizar-dash/src/web/views/Chat.tsx +8 -8
  124. package/cli/bin.mjs +31 -18
  125. package/cli/commands/claude-cmd.mjs +348 -0
  126. package/cli/commands/install.mjs +12 -11
  127. package/cli/commands/sandbox.mjs +220 -0
  128. package/cli/commands/validate.mjs +252 -288
  129. package/cli/dev-link.test.mjs +11 -11
  130. package/cli/doctor.mjs +131 -213
  131. package/cli/install.mjs +33 -37
  132. package/cli/install.test.mjs +9 -9
  133. package/cli/provision-claude.mjs +893 -0
  134. package/cli/provision.mjs +330 -226
  135. package/cli/provision.test.mjs +102 -0
  136. package/cli/utils.mjs +72 -58
  137. package/config/AGENTS.md +28 -28
  138. package/config/agents/_shared/AGENT_BASELINE.md +104 -684
  139. package/config/skills/bizar/SKILL.md +197 -0
  140. package/config/skills/cubesandbox/SKILL.md +148 -0
  141. package/config/skills/harness-engineering/SKILL.md +142 -0
  142. package/install.sh +66 -44
  143. package/package.json +19 -11
  144. package/packages/sdk/ARCHITECTURE.md +40 -36
  145. package/packages/sdk/package-lock.json +32 -0
  146. package/packages/sdk/package.json +15 -5
  147. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  148. package/packages/sdk/src/fingerprint.ts +0 -0
  149. package/packages/sdk/src/index.ts +38 -73
  150. package/packages/sdk/src/mcp/bin.ts +51 -0
  151. package/packages/sdk/src/mcp/server.ts +498 -0
  152. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  153. package/packages/sdk/tests/sdk.test.mjs +148 -0
  154. package/packages/sdk/vitest.config.ts +1 -1
  155. package/scripts/bh-full-e2e.mjs +166 -363
  156. package/scripts/check-deps.mjs +28 -46
  157. package/scripts/mirror-claude-md.sh +78 -0
  158. package/scripts/test-in-container.sh +157 -0
  159. package/templates/clean-state-checklist.md +3 -3
  160. package/templates/sprint-contract.md +2 -2
  161. package/bizar-dash/.bizar/activity.log +0 -3
  162. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  163. package/bizar-dash/.omx/state/session.json +0 -10
  164. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  165. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  166. package/bizar-dash/bizar-design/canvas.html +0 -281
  167. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  168. package/bizar-dash/bizar-design/components.css +0 -1665
  169. package/bizar-dash/bizar-design/components.html +0 -557
  170. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  171. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  172. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  173. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  174. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  175. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  176. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  177. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  178. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  179. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  180. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  181. package/bizar-dash/bizar-design/icons.svg +0 -44
  182. package/bizar-dash/bizar-design/index.html +0 -192
  183. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  184. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  185. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  186. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  187. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  188. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  189. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  190. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  191. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  192. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  193. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  194. package/bizar-dash/bizar-design/prototype.js +0 -114
  195. package/bizar-dash/bizar-design/tokens.css +0 -106
  196. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  197. package/bizar-dash/node_modules/.package-lock.json +0 -6
  198. package/bizar-dash/package-lock.json +0 -6
  199. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  200. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  201. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  202. package/cli/commands/cline-cmd.mjs +0 -289
  203. package/cli/commands/validate.test.mjs +0 -344
  204. package/cli/doctor.test.mjs +0 -350
  205. package/config/cline.json.template +0 -342
  206. package/packages/sdk/src/client.ts +0 -188
  207. package/packages/sdk/src/cline-events.ts +0 -134
  208. package/packages/sdk/src/cline-types.ts +0 -66
  209. package/packages/sdk/src/cline.ts +0 -339
  210. package/packages/sdk/src/errors.ts +0 -129
  211. package/packages/sdk/src/events.ts +0 -153
  212. package/packages/sdk/src/types.ts +0 -176
  213. package/packages/sdk/tests/client.test.ts +0 -217
  214. package/packages/sdk/tests/errors.test.ts +0 -108
  215. package/packages/sdk/tests/events.test.ts +0 -139
  216. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  217. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  218. package/plugins/bizar/ARCHITECTURE.md +0 -142
  219. package/plugins/bizar/CONSTRAINTS.md +0 -67
  220. package/plugins/bizar/LICENSE +0 -21
  221. package/plugins/bizar/README.md +0 -448
  222. package/plugins/bizar/index.ts +0 -872
  223. package/plugins/bizar/package.json +0 -39
  224. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  225. package/plugins/bizar/src/background-state.ts +0 -641
  226. package/plugins/bizar/src/background.ts +0 -1806
  227. package/plugins/bizar/src/cline-runner.ts +0 -203
  228. package/plugins/bizar/src/clineruntime.ts +0 -227
  229. package/plugins/bizar/src/commands-impl.ts +0 -151
  230. package/plugins/bizar/src/commands.ts +0 -1799
  231. package/plugins/bizar/src/compaction.d.mts +0 -48
  232. package/plugins/bizar/src/compaction.mjs +0 -192
  233. package/plugins/bizar/src/dashboard-client.ts +0 -233
  234. package/plugins/bizar/src/event-stream.ts +0 -606
  235. package/plugins/bizar/src/fingerprint.ts +0 -120
  236. package/plugins/bizar/src/handoff.ts +0 -79
  237. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  238. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  239. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  240. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  241. package/plugins/bizar/src/http-client.ts +0 -467
  242. package/plugins/bizar/src/key-rotation.ts +0 -218
  243. package/plugins/bizar/src/logger.ts +0 -144
  244. package/plugins/bizar/src/loop-engineering.ts +0 -241
  245. package/plugins/bizar/src/loop.ts +0 -176
  246. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  247. package/plugins/bizar/src/odin.ts +0 -227
  248. package/plugins/bizar/src/options.ts +0 -470
  249. package/plugins/bizar/src/plan-fs.ts +0 -323
  250. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  251. package/plugins/bizar/src/report.ts +0 -178
  252. package/plugins/bizar/src/research-prompt.ts +0 -35
  253. package/plugins/bizar/src/serve-info.ts +0 -228
  254. package/plugins/bizar/src/serve.ts +0 -496
  255. package/plugins/bizar/src/settings.ts +0 -349
  256. package/plugins/bizar/src/state.ts +0 -298
  257. package/plugins/bizar/src/tool-discipline.ts +0 -105
  258. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  259. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  260. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  261. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  262. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  263. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  264. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  265. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  266. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  267. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  268. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  269. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  270. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  271. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  272. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  273. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  274. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  275. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  276. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  277. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  278. package/plugins/bizar/src/tools/team-status.ts +0 -76
  279. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  280. package/plugins/bizar/src/trajectory.ts +0 -104
  281. package/plugins/bizar/tests/README.md +0 -99
  282. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  283. package/plugins/bizar/tests/background-state.test.ts +0 -277
  284. package/plugins/bizar/tests/background.test.ts +0 -402
  285. package/plugins/bizar/tests/block.test.ts +0 -195
  286. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  287. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  288. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  289. package/plugins/bizar/tests/commands.test.ts +0 -584
  290. package/plugins/bizar/tests/compaction.test.ts +0 -264
  291. package/plugins/bizar/tests/config.test.ts +0 -128
  292. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  293. package/plugins/bizar/tests/dispose.test.ts +0 -336
  294. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  295. package/plugins/bizar/tests/event.test.ts +0 -262
  296. package/plugins/bizar/tests/fingerprint.test.ts +0 -162
  297. package/plugins/bizar/tests/http-client.test.ts +0 -404
  298. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  299. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  300. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  301. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  302. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  303. package/plugins/bizar/tests/loop.test.ts +0 -397
  304. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  305. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  306. package/plugins/bizar/tests/odin.test.ts +0 -125
  307. package/plugins/bizar/tests/options.test.ts +0 -329
  308. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  309. package/plugins/bizar/tests/safety.test.ts +0 -256
  310. package/plugins/bizar/tests/serve.test.ts +0 -339
  311. package/plugins/bizar/tests/settings.test.ts +0 -351
  312. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  313. package/plugins/bizar/tests/state.test.ts +0 -276
  314. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  315. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  316. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  317. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  318. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  319. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  320. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  321. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  322. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  323. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  324. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  325. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  326. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  327. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  328. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  329. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  330. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  331. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  332. package/plugins/bizar/tsconfig.json +0 -29
@@ -1,872 +0,0 @@
1
- /**
2
- * Bizar plugin — Cline `AgentExtension` entry point (Phase 2 rewrite).
3
- *
4
- * Replaces the legacy OpenCode `Plugin` factory with a Cline
5
- * `AgentPlugin` from `@cline/sdk`. All 17 tools use
6
- * `createTool({ name, description, inputSchema, execute })` directly.
7
- *
8
- * Hook mapping (OpenCode → Cline):
9
- * tool.execute.before → beforeTool
10
- * tool.execute.after → afterTool
11
- * experimental.chat.system.* → beforeModel
12
- * experimental.chat.messages.* → beforeModel
13
- * chat.message → onEvent(message-added)
14
- * event → onEvent(...)
15
- * experimental.session.compacting / autocontinue → onEvent(status-notice)
16
- *
17
- * Architecture:
18
- * - Tools are registered via `api.registerTool()` inside `setup()`.
19
- * - Hooks are defined as a getter that reads from module-level
20
- * `runtimeCtx`. The runtime context is created during `setup()`;
21
- * until then, hooks are a no-op stub.
22
- *
23
- * Migration scope — Phase 3 (follow-up):
24
- * - Replace ServeLifecycle (subprocess spawn) with a ClineCore
25
- * wrapper using `cline.start()` / `cline.send()` / `cline.abort()`.
26
- * - Replace HttpClient (HTTP) with direct ClineCore calls.
27
- * - Replace EventStream (SSE) with `cline.subscribe()`.
28
- */
29
-
30
- import {
31
- type AgentHooks,
32
- type AgentPlugin,
33
- type AgentTool,
34
- type Message,
35
- createTool,
36
- } from "@cline/sdk";
37
- import type {
38
- AgentExtensionApi,
39
- AgentExtensionHooks,
40
- PluginSetupContext,
41
- } from "@cline/shared";
42
- import type { AgentRuntimeEvent } from "@cline/shared";
43
-
44
- import { createLogger, type Logger } from "./src/logger.js";
45
- import { decide, isLogOnlyWarn } from "./src/loop.js";
46
- import { fingerprint } from "./src/fingerprint.js";
47
- import { createDashboardPublisher, type DashboardPublisher } from "./src/dashboard-client.js";
48
- import { StateStore } from "./src/state.js";
49
- import { LogWriter } from "./src/report.js";
50
- import {
51
- normalizeOptions,
52
- readEnvFlags,
53
- findOffendingPath,
54
- type NormalizedOptions,
55
- type EnvFlags,
56
- } from "./src/options.js";
57
-
58
- import { ServeLifecycle } from "./src/serve.js";
59
- import { ClineRuntime } from "./src/clineruntime.js";
60
- import { writeServeInfo, clearServeInfo } from "./src/serve-info.js";
61
- import { HttpClient } from "./src/http-client.js";
62
- import { EventStream } from "./src/event-stream.js";
63
- import { BackgroundStateStore } from "./src/background-state.js";
64
- import { InstanceManager } from "./src/background.js";
65
-
66
- import { createBgSpawnTool } from "./src/tools/bg-spawn.js";
67
- import { createBgStatusTool } from "./src/tools/bg-status.js";
68
- import { createBgCollectTool } from "./src/tools/bg-collect.js";
69
- import { createBgKillTool } from "./src/tools/bg-kill.js";
70
- import { createBgPauseTool } from "./src/tools/bg-pause.js";
71
- import { createBgResumeTool } from "./src/tools/bg-resume.js";
72
- import { createBgReportProgressTool } from "./src/tools/bg-report-progress.js";
73
- import { createBgSendMessageTool } from "./src/tools/bg-send-message.js";
74
- import { createBgGetCommentsTool } from "./src/tools/bg-get-comments.js";
75
-
76
- import { createOpenKbTool } from "./src/tools/open-kb.js";
77
- import { createMemorySearchTool } from "./src/tools/memory-search.js";
78
- import { createMemoryReadTool } from "./src/tools/memory-read.js";
79
- import { createMemoryWriteTool } from "./src/tools/memory-write.js";
80
- import { createMemoryListTool } from "./src/tools/memory-list.js";
81
-
82
- import { createMemoryInject, popMemoryContext } from "./src/hooks/memory-inject.js";
83
- import { createMemoryWriteOnEnd } from "./src/hooks/memory-write-on-end.js";
84
-
85
- import { SettingsStore } from "./src/settings.js";
86
- import { parseSlashCommand } from "./src/commands.js";
87
- import { createPlanActionTool } from "./src/tools/plan-action.js";
88
- import { createWaitForFeedbackTool } from "./src/tools/wait-for-feedback.js";
89
- import { createReadGlyphFeedbackTool } from "./src/tools/read-glyph-feedback.js";
90
- import { createTeamSpawnTool } from "./src/tools/team-spawn.js";
91
- import { createLoopTools } from "./src/tools/loop-engineering.js";
92
- import { createTeamStatusTool } from "./src/tools/team-status.js";
93
- import { createGraphQueryTool, createGraphPathTool, createGraphExplainTool } from "./src/tools/graph-query.js";
94
- import {
95
- createBrowserOpenTool,
96
- createBrowserSnapshotTool,
97
- createBrowserClickTool,
98
- createBrowserFillTool,
99
- createBrowserScreenshotTool,
100
- createBrowserCommandTool,
101
- type AgentBrowserDeps,
102
- } from "./src/tools/agent-browser.js";
103
- import { checkDangerous, getDangerousPatternStats, listDangerousPatterns } from "./src/dangerous-patterns.js";
104
- import { createSkillCurator } from "./src/hooks/skill-curator.js";
105
- import { createMemoryFlushOnCompact } from "./src/hooks/memory-flush-on-compact.js";
106
- import {
107
- stripInlineThinkBlocks,
108
- wrapFetchForReasoningCleanup,
109
- type FetchLike,
110
- } from "./src/reasoning-clean.js";
111
- import {
112
- wrapFetchForKeyRotation,
113
- discoverMiniMaxKeys,
114
- } from "./src/key-rotation.js";
115
- import { executeSideEffect, type ExecuteOptions } from "./src/commands-impl.js";
116
-
117
- import {
118
- getCompactionThreshold,
119
- setCompactionThreshold,
120
- resetCompactionDefaults,
121
- } from "./src/compaction.mjs";
122
-
123
- import { homedir } from "node:os";
124
- import { join as pathJoin } from "node:path";
125
-
126
- // --- Env-var constants --------------------------------------------------
127
-
128
- function readServePort(): number {
129
- const raw = process.env.BIZAR_SERVE_PORT;
130
- if (raw === undefined || raw === "") return 0;
131
- const n = Number(raw);
132
- if (!Number.isFinite(n) || n < 0 || n > 65535) return 0;
133
- return Math.floor(n);
134
- }
135
- function readServeDisabled(): boolean { return process.env.BIZAR_SERVE_DISABLE === "1"; }
136
- function readMaxConcurrent(): number {
137
- const raw = process.env.BIZAR_MAX_CONCURRENT_INSTANCES;
138
- if (raw === undefined || raw === "") return 8;
139
- const n = Number(raw);
140
- if (!Number.isFinite(n) || n < 1) return 8;
141
- return Math.floor(n);
142
- }
143
- function readToolCallCap(): number {
144
- const raw = process.env.BIZAR_BACKGROUND_TOOL_CALL_CAP;
145
- if (raw === undefined || raw === "") return 500;
146
- const n = Number(raw);
147
- if (!Number.isFinite(n) || n < 1) return 500;
148
- return Math.floor(n);
149
- }
150
- function readStallTimeoutMs(): number {
151
- const raw = process.env.BIZAR_STALL_TIMEOUT_MS;
152
- if (raw === undefined || raw === "") return 180_000;
153
- const n = Number(raw);
154
- if (!Number.isFinite(n) || n < 10_000) return 180_000;
155
- return Math.min(Math.floor(n), 600_000);
156
- }
157
- function readThinkingLoopTimeoutMs(): number {
158
- const raw = process.env.BIZAR_THINKING_LOOP_TIMEOUT_MS;
159
- if (raw === undefined || raw === "") return 300_000;
160
- const n = Number(raw);
161
- if (!Number.isFinite(n) || n < 30_000) return 300_000;
162
- return Math.min(Math.floor(n), 900_000);
163
- }
164
- function readMaxInterventions(): number {
165
- const raw = process.env.BIZAR_MAX_INTERVENTIONS;
166
- if (raw === undefined || raw === "") return 1;
167
- const n = Number(raw);
168
- if (!Number.isFinite(n) || n < 1) return 1;
169
- return Math.min(Math.floor(n), 3);
170
- }
171
- function readHttpTimeoutMs(): number {
172
- const raw = process.env.BIZAR_HTTP_TIMEOUT_MS;
173
- if (raw === undefined || raw === "") return 30_000;
174
- const n = Number(raw);
175
- if (!Number.isFinite(n) || n < 1000) return 30_000;
176
- return Math.floor(n);
177
- }
178
-
179
- // --- Shutdown coordination ----------------------------------------------
180
-
181
- let shuttingDown = false;
182
- let instanceManagerHandle: InstanceManager | null = null;
183
- let serveHandle: ServeLifecycle | null = null;
184
- let streamHandle: EventStream | null = null;
185
- let loggerHandle: Logger | null = null;
186
- const signalHandlerRefs = new Map<"SIGTERM" | "SIGINT", () => void>();
187
-
188
- let reasoningCleanInstalled = false;
189
- let keyRotationInstalled = false;
190
-
191
- function installFetchReasoningCleanup(logger: Logger): void {
192
- if (reasoningCleanInstalled) return;
193
- const original = globalThis.fetch;
194
- if (typeof original !== "function") {
195
- logger.warn("bizar: globalThis.fetch is not a function; reasoning-clean wrap skipped");
196
- return;
197
- }
198
- const wrapped = wrapFetchForReasoningCleanup(
199
- original.bind(globalThis) as FetchLike,
200
- { debug: (msg) => logger.debug(msg) },
201
- );
202
- globalThis.fetch = wrapped as typeof globalThis.fetch;
203
- reasoningCleanInstalled = true;
204
- logger.info("bizar: reasoning-clean fetch wrap installed (minimax)");
205
- }
206
-
207
- function installFetchKeyRotation(logger: Logger): void {
208
- if (keyRotationInstalled) return;
209
- const original = globalThis.fetch;
210
- if (typeof original !== "function") {
211
- logger.warn("bizar: globalThis.fetch is not a function; key-rotation wrap skipped");
212
- return;
213
- }
214
- const keys = discoverMiniMaxKeys();
215
- if (keys.length < 2) {
216
- keyRotationInstalled = true;
217
- logger.debug(`bizar: ${keys.length} MiniMax key(s); key-rotation disabled (need >= 2)`);
218
- return;
219
- }
220
- const wrapped = wrapFetchForKeyRotation(original.bind(globalThis) as FetchLike, {
221
- providerId: "minimax",
222
- apiKeys: keys,
223
- debug: (msg) => logger.debug(msg),
224
- });
225
- globalThis.fetch = wrapped as typeof globalThis.fetch;
226
- keyRotationInstalled = true;
227
- logger.info(`bizar: key-rotation fetch wrap installed (minimax, ${keys.length} keys)`);
228
- }
229
-
230
- // --- Runtime context ----------------------------------------------------
231
-
232
- interface RuntimeContext {
233
- logger: Logger;
234
- options: NormalizedOptions;
235
- envFlags: EnvFlags;
236
- stateStore: StateStore;
237
- settingsStore: SettingsStore;
238
- logWriter: LogWriter;
239
- worktree: string;
240
- directory: string;
241
- seenMessageIds: Map<string, Set<string>>;
242
- pendingInjections: Map<string, string>;
243
- injectedSessions: Set<string>;
244
- dashboardPublisher: DashboardPublisher | null;
245
- memoryInject: (sessionID: string, firstMessage: string) => Promise<void>;
246
- memoryWriteOnEnd: (
247
- sessionID: string,
248
- info: { sessionID: string; agent: string; startedAt: number; endedAt: number; status: "idle" | "error" | "killed"; error?: string; },
249
- conversationPreview: string,
250
- ) => Promise<void>;
251
- sessionStartTimes: Map<string, number>;
252
- }
253
-
254
- const REASONING_DIRECTIVE_MARKER = "BIZAR_REASONING_DIRECTIVE_v0.6.2";
255
- const REASONING_DIRECTIVE = [
256
- REASONING_DIRECTIVE_MARKER, "",
257
- "When reasoning is enabled for this conversation, output your thinking",
258
- "ONLY in the model's structured reasoning field. Do NOT emit THINK blocks",
259
- "inline inside your message content — the cline host extracts the",
260
- "reasoning field and renders it as a separate, collapsable \"Thought\"",
261
- "panel. If you also emit the same text inline, the user will see your",
262
- "thinking twice (once in the panel and once as visible message body).",
263
- "Keep the actual response text in the normal content stream.",
264
- ].join(" ");
265
-
266
- // v6.0.1 — Tool-discipline directive (prefer built-in tools, keep bash small).
267
- // See plugins/bizar/src/tool-discipline.ts for the body and rationale.
268
- import {
269
- TOOL_DISCIPLINE_DIRECTIVE,
270
- TOOL_DISCIPLINE_MARKER,
271
- hasToolDiscipline as _hasToolDiscipline,
272
- } from "./src/tool-discipline.js";
273
-
274
- function ensureToolDiscipline(sysParts: string[], baseSys: string): void {
275
- if (_hasToolDiscipline(baseSys)) return;
276
- if (sysParts.some((s) => s.includes(TOOL_DISCIPLINE_MARKER))) return;
277
- sysParts.push(TOOL_DISCIPLINE_DIRECTIVE);
278
- }
279
-
280
- // v6.0.1 — Mistake-recovery callback used by team-spawn and other
281
- // in-process session creators. See plugins/bizar/src/mistake-recovery.ts
282
- // for the rationale (recover from `invalid_tool_call` /
283
- // `tool_execution_failed`; stop on `api_error`).
284
- import { buildMistakeRecovery } from "./src/mistake-recovery.js";
285
-
286
- export function makeMistakeRecoveryCallback(logger: Logger): ReturnType<typeof buildMistakeRecovery> {
287
- return buildMistakeRecovery({
288
- onRecovery: (ctx, guidance) => {
289
- logger.warn(
290
- `bizar: mistake limit reached — continuing with guidance ` +
291
- `(reason=${ctx.reason}, iteration=${ctx.iteration}, ` +
292
- `consecutive=${ctx.consecutiveMistakes}/${ctx.maxConsecutiveMistakes})`,
293
- );
294
- try {
295
- const preview = guidance.replace(/\s+/g, " ").slice(0, 120);
296
- logger.debug(`bizar: recovery guidance preview: ${preview}…`);
297
- } catch {
298
- // preview derivation is best-effort
299
- }
300
- },
301
- });
302
- }
303
-
304
- // Module-level runtime context — set during setup(), read by hooks via getter.
305
- let _runtimeCtx: RuntimeContext | null = null;
306
-
307
- // --- The exported plugin -----------------------------------------------
308
-
309
- const plugin: AgentPlugin = {
310
- name: "bizar",
311
- manifest: { capabilities: ["tools", "hooks"] },
312
- get hooks(): AgentExtensionHooks {
313
- return _runtimeCtx ? buildHooksForCtx(_runtimeCtx) : {};
314
- },
315
- async setup(api: AgentExtensionApi<AgentTool, Message[]>, ctx: PluginSetupContext): Promise<void> {
316
- const { runtimeCtx, tools } = await initRuntime(api, ctx);
317
- _runtimeCtx = runtimeCtx;
318
- for (const t of tools) api.registerTool(t as AgentTool);
319
- },
320
- };
321
-
322
- export default plugin;
323
-
324
- // --- Init (returns runtime context + tools) ----------------------------
325
-
326
- async function initRuntime(
327
- _api: AgentExtensionApi<AgentTool, Message[]>,
328
- ctx: PluginSetupContext,
329
- ): Promise<{ runtimeCtx: RuntimeContext; tools: AgentTool[] }> {
330
- const envFlags = readEnvFlags();
331
- const ctxMeta = (ctx as unknown as { metadata?: Record<string, unknown> }).metadata as Record<string, unknown> | undefined;
332
- const rawOptions = (ctxMeta?.bizar as Record<string, unknown> | undefined) ?? {};
333
- const { options, notes } = normalizeOptions(rawOptions as never);
334
-
335
- const logger = createLogger({
336
- log: (lvl: string, msg: string) => {
337
- const fn = lvl === "error" ? console.error : lvl === "warn" ? console.warn : lvl === "info" ? console.info : console.debug;
338
- fn(`[bizar] ${msg}`);
339
- },
340
- } as unknown as Parameters<typeof createLogger>[0]);
341
- loggerHandle = logger;
342
-
343
- resetCompactionDefaults();
344
- const compactionCfg = (rawOptions as Record<string, unknown>).compaction as { threshold?: unknown } | undefined;
345
- if (compactionCfg && typeof compactionCfg.threshold === "number") {
346
- try { setCompactionThreshold(compactionCfg.threshold); logger.info(`bizar: compaction threshold set to ${compactionCfg.threshold}`); }
347
- catch (err) { logger.warn(`bizar: invalid compaction threshold: ${err instanceof Error ? err.message : String(err)}`); }
348
- }
349
-
350
- const offending = findOffendingPath(options);
351
- if (offending !== null) {
352
- logger.error(`bizar: refusing to start — ${offending.path} inside secret dir (${offending.kind})`);
353
- return { runtimeCtx: emptyRuntimeContext(logger, options, envFlags), tools: [] };
354
- }
355
- if (envFlags.disable) { logger.debug("bizar: disabled via BIZAR_DISABLE=1"); return { runtimeCtx: emptyRuntimeContext(logger, options, envFlags), tools: [] }; }
356
- for (const note of notes) logger.warn(`bizar: ${note}`);
357
-
358
- installFetchReasoningCleanup(logger);
359
- installFetchKeyRotation(logger);
360
-
361
- const worktree = (ctxMeta?.worktree as string | undefined) ?? process.cwd();
362
- const directory = (ctxMeta?.directory as string | undefined) ?? worktree;
363
-
364
- const stateStore = new StateStore(options.stateDir, logger);
365
- const settingsStore = new SettingsStore(options.stateDir, logger);
366
- const logWriter = new LogWriter(options.logDir, options.logRotationBytes, logger);
367
- try {
368
- const deleted = await stateStore.cleanup(7, new Set());
369
- if (deleted > 0) logger.info(`bizar: cleaned up ${deleted} stale session file(s)`);
370
- } catch (err) { logger.warn(`bizar: stale session cleanup failed: ${err instanceof Error ? err.message : String(err)}`); }
371
-
372
- let instanceManager: InstanceManager | null = null;
373
- let serve: ServeLifecycle | null = null;
374
- let stream: EventStream | null = null;
375
- let dashboardPublisher: DashboardPublisher | null = null;
376
- let bgAvailable = false;
377
-
378
- if (readServeDisabled()) {
379
- logger.info("bizar: background agents disabled via BIZAR_SERVE_DISABLE=1");
380
- } else {
381
- try {
382
- // v6.0.0 — In-process Cline mode. The plugin embeds ClineCore
383
- // (via ClineRuntime) instead of spawning a `cline serve` child.
384
- // We pass nulls for serve/http/stream — InstanceManager runs in
385
- // "bg-only mode" and the bg-spawn tool POSTs to the dashboard
386
- // for actual session creation. The dashboard can also use
387
- // ClineCore in-process (see bizar-dash/src/server/bg-spawner.mjs).
388
- const bgStateStore = new BackgroundStateStore(options.stateDir, logger);
389
- bgStateStore.cleanup(7).catch(() => 0);
390
- const maxConcurrent = readMaxConcurrent();
391
- const toolCallCap = readToolCallCap();
392
- const stallTimeoutMs = readStallTimeoutMs();
393
- const thinkingLoopTimeoutMs = readThinkingLoopTimeoutMs();
394
- const maxInterventions = readMaxInterventions();
395
-
396
- instanceManager = new InstanceManager({
397
- stateStore: bgStateStore,
398
- maxConcurrent,
399
- toolCallCap,
400
- logger,
401
- worktree,
402
- serve: null,
403
- http: null,
404
- stream: null,
405
- stallTimeoutMs,
406
- thinkingLoopTimeoutMs,
407
- maxInterventions,
408
- });
409
- instanceManagerHandle = instanceManager;
410
- await instanceManager.rebuildInMemoryMap();
411
- bgAvailable = true;
412
- logger.info(`bizar: background agents ready (in-process ClineCore; cap=${maxConcurrent}, toolCallCap=${toolCallCap})`);
413
- } catch (err) { logger.warn(`bizar: background agents unavailable: ${err instanceof Error ? err.message : String(err)}`); }
414
- }
415
-
416
- installSignalHandlers(logger, instanceManager, serve, stream, options.stateDir);
417
- const injectedSessions = new Set<string>();
418
- const sessionStartTimes = new Map<string, number>();
419
- const { memoryInject } = createMemoryInject({ injectedSessions, worktree, logger, enabled: true });
420
- const { memoryWriteOnEnd } = createMemoryWriteOnEnd({ worktree, logger, enabled: true });
421
-
422
- const runtimeCtx: RuntimeContext = {
423
- logger, options, envFlags, stateStore, settingsStore, logWriter,
424
- worktree, directory,
425
- seenMessageIds: new Map(), pendingInjections: new Map(), injectedSessions,
426
- dashboardPublisher, memoryInject, memoryWriteOnEnd, sessionStartTimes,
427
- };
428
- // v6.0.0 — Try to bring up an in-process ClineRuntime for team tools
429
- // and event-driven features. If ClineCore.create() fails (no
430
- // @cline/core installed, or no provider credentials), we log a
431
- // warning and continue without it — the non-team tools still work.
432
- let clineRuntime: ClineRuntime | null = null;
433
- try {
434
- clineRuntime = new ClineRuntime({
435
- logger,
436
- defaultMaxConsecutiveMistakes: options.clineruntimeMaxConsecutiveMistakes,
437
- defaultOnConsecutiveMistakeLimitReached: makeMistakeRecoveryCallback(logger),
438
- });
439
- await clineRuntime.start();
440
- logger.info(
441
- `bizar: ClineRuntime ready (agent teams + advanced features enabled, ` +
442
- `maxConsecutiveMistakes=${options.clineruntimeMaxConsecutiveMistakes}, ` +
443
- `recovery=continue-on-invalid-tool-call)`,
444
- );
445
- } catch (err) {
446
- clineRuntime = null;
447
- logger.warn(`bizar: ClineRuntime unavailable: ${err instanceof Error ? err.message : String(err)}`);
448
- }
449
-
450
- const tools = buildTools(runtimeCtx, instanceManager, logger, clineRuntime);
451
- return { runtimeCtx, tools };
452
- }
453
-
454
- function emptyRuntimeContext(logger: Logger, options: NormalizedOptions, envFlags: EnvFlags): RuntimeContext {
455
- return {
456
- logger, options, envFlags,
457
- stateStore: new StateStore(options.stateDir, logger),
458
- settingsStore: new SettingsStore(options.stateDir, logger),
459
- logWriter: new LogWriter(options.logDir, options.logRotationBytes, logger),
460
- worktree: process.cwd(), directory: process.cwd(),
461
- seenMessageIds: new Map(), pendingInjections: new Map(), injectedSessions: new Set<string>(),
462
- dashboardPublisher: null,
463
- memoryInject: async () => {},
464
- memoryWriteOnEnd: async () => {},
465
- sessionStartTimes: new Map(),
466
- };
467
- }
468
-
469
- // --- Signal handling (spec §5.3) ----------------------------------------
470
-
471
- function installSignalHandlers(logger: Logger, instanceManager: InstanceManager | null, serve: ServeLifecycle | null, stream: EventStream | null, stateDir: string): void {
472
- const onSignal = async (sig: "SIGTERM" | "SIGINT") => {
473
- if (shuttingDown) return;
474
- shuttingDown = true;
475
- logger.warn(`bizar: received ${sig}; shutting down`);
476
- if (instanceManager !== null) { try { await instanceManager.shutdownAll(); } catch (err) { logger.warn(`shutdownAll failed: ${err instanceof Error ? err.message : String(err)}`); } }
477
- if (stream !== null) { try { await stream.disconnect(); } catch { /* ignore */ } }
478
- if (serve !== null) { try { await serve.stop(); } catch (err) { logger.warn(`serve.stop failed: ${err instanceof Error ? err.message : String(err)}`); } }
479
- clearServeInfo(stateDir, logger);
480
- try { process.exit(0); } catch { /* ignore */ }
481
- };
482
- for (const sig of ["SIGTERM", "SIGINT"] as const) {
483
- const previous = signalHandlerRefs.get(sig);
484
- if (previous) { try { process.removeListener(sig, previous); } catch { /* ignore */ } }
485
- const handler = () => { void onSignal(sig); };
486
- signalHandlerRefs.set(sig, handler);
487
- process.once(sig, handler);
488
- }
489
- }
490
-
491
- function readMessageText(message: Message | undefined): string | null {
492
- if (!message || !Array.isArray(message.content)) return null;
493
- const fragments: string[] = [];
494
- for (const part of message.content) {
495
- const p = part as { type?: string; text?: string };
496
- if (p.type === "text" && typeof p.text === "string") fragments.push(p.text);
497
- }
498
- const joined = fragments.join("\n").trim();
499
- return joined === "" ? null : joined;
500
- }
501
-
502
- async function listPlanSlugs(worktree: string, logger: Logger): Promise<string[]> {
503
- try {
504
- const { readdirSync, statSync } = await import("node:fs");
505
- const { join } = await import("node:path");
506
- const plansDir = join(worktree, "plans");
507
- let entries: string[];
508
- try { entries = readdirSync(plansDir); } catch { return []; }
509
- const slugs: string[] = [];
510
- for (const name of entries) {
511
- try { const stat = statSync(join(plansDir, name)); if (stat.isDirectory()) slugs.push(name); } catch { /* skip */ }
512
- }
513
- slugs.sort();
514
- return slugs;
515
- } catch (err) { logger.debug(`listPlanSlugs failed: ${err instanceof Error ? err.message : String(err)}`); return []; }
516
- }
517
-
518
- function findLastIndex<T>(arr: readonly T[], predicate: (item: T) => boolean): number {
519
- for (let i = arr.length - 1; i >= 0; i--) {
520
- const item = arr[i];
521
- if (item !== undefined && predicate(item)) return i;
522
- }
523
- return -1;
524
- }
525
-
526
- // --- Tools builder ---------------------------------------------------
527
-
528
- function buildTools(ctx: RuntimeContext, instanceManager: InstanceManager | null, _logger: Logger, clineRuntime: ClineRuntime | null = null): AgentTool[] {
529
- const basePlanTools: AgentTool[] = [
530
- createBgGetCommentsTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
531
- createPlanActionTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
532
- createWaitForFeedbackTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
533
- createReadGlyphFeedbackTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
534
- createOpenKbTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
535
- createMemorySearchTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
536
- createMemoryReadTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
537
- createMemoryWriteTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
538
- createMemoryListTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
539
- ];
540
- const bgTools: AgentTool[] = instanceManager
541
- ? [
542
- createBgSpawnTool({ instanceManager, worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
543
- createBgStatusTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
544
- createBgCollectTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
545
- createBgKillTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
546
- createBgPauseTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
547
- createBgResumeTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
548
- createBgSendMessageTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
549
- createBgReportProgressTool({ instanceManager, logger: ctx.logger }) as unknown as AgentTool,
550
- ]
551
- : bgDisabledTools(ctx.logger);
552
- // v6.0.0 — Cline agent teams tools. These need a ClineRuntime, so
553
- // they're only registered if we have one (in-process ClineCore).
554
- const teamTools: AgentTool[] = clineRuntime
555
- ? [
556
- createTeamSpawnTool({ runtime: clineRuntime, logger: ctx.logger }) as unknown as AgentTool,
557
- createTeamStatusTool({ runtime: clineRuntime, logger: ctx.logger }) as unknown as AgentTool,
558
- ]
559
- : [];
560
- // v6.0.0 — Knowledge graph tools (always available; reads .bizar/graph/graph.json).
561
- const graphTools: AgentTool[] = [
562
- createGraphQueryTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
563
- createGraphPathTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
564
- createGraphExplainTool({ worktree: ctx.worktree, logger: ctx.logger }) as unknown as AgentTool,
565
- ];
566
- // v6.0.0 — agent-browser CLI tools (replaces v5.x browser-harness).
567
- // Thin wrappers around the agent-browser Rust CLI; see
568
- // plugins/bizar/src/tools/agent-browser.ts and
569
- // config/agents/agent-browser.md for the browser-primary agent.
570
- const browserDeps: AgentBrowserDeps = { logger: ctx.logger };
571
- const browserTools: AgentTool[] = [
572
- createBrowserOpenTool(browserDeps) as unknown as AgentTool,
573
- createBrowserSnapshotTool(browserDeps) as unknown as AgentTool,
574
- createBrowserClickTool(browserDeps) as unknown as AgentTool,
575
- createBrowserFillTool(browserDeps) as unknown as AgentTool,
576
- createBrowserScreenshotTool(browserDeps) as unknown as AgentTool,
577
- createBrowserCommandTool(browserDeps) as unknown as AgentTool,
578
- ];
579
- // v6.0.0 — Loop-engineering tools (ralph / repl / cron / plan-execute).
580
- // These work standalone (no clineRuntime needed) since state lives on disk.
581
- const loopTools: AgentTool[] = createLoopTools({ logger: ctx.logger }) as unknown as AgentTool[];
582
- return [...basePlanTools, ...bgTools, ...teamTools, ...graphTools, ...browserTools, ...loopTools];
583
- }
584
-
585
- function bgDisabledTools(logger: Logger): AgentTool[] {
586
- const disabled = (name: string): AgentTool => {
587
- return { name, description: `${name} (disabled — background agents are unavailable).`, inputSchema: {}, execute: async () => { logger.debug(`bizar: ${name} called but disabled`); return { ok: false as const, error: "background agents are disabled (cline serve unavailable)." }; } } as unknown as AgentTool;
588
- };
589
- return [disabled("bizar_spawn_background"), disabled("bizar_status"), disabled("bizar_collect"), disabled("bizar_kill"), disabled("bizar_pause"), disabled("bizar_resume"), disabled("bizar_send_message"), disabled("bizar_report_progress")];
590
- }
591
-
592
- // --- Hooks builder ---------------------------------------------------
593
-
594
- function buildHooksForCtx(ctx: RuntimeContext): AgentExtensionHooks {
595
- return {
596
- beforeTool: async (toolCtx) => {
597
- if (ctx.envFlags.disableLoop) return undefined;
598
- const sessionID = (toolCtx.snapshot as { sessionId?: string }).sessionId ?? (toolCtx.snapshot as { agentId?: string }).agentId ?? "";
599
- const tool = toolCtx.tool.name;
600
- if (!sessionID || !tool) return undefined;
601
- const args = toolCtx.input;
602
- // v6.0.0 — Dangerous-patterns approval gate (Hermes + OpenFang pattern).
603
- // Blocks rm -rf, sudo, prompt injection, SSRF, etc. See
604
- // src/dangerous-patterns.ts for the full list. Tool calls with
605
- // `decision: deny` are stopped before they reach the host.
606
- try {
607
- const safety = checkDangerous(args as Record<string, unknown>);
608
- if (safety.decision === "deny") {
609
- ctx.logger.warn(
610
- `bizar: blocked tool '${tool}' — dangerous pattern '${safety.pattern}': ${safety.reason}`,
611
- );
612
- return { stop: true, reason: `dangerous_pattern:${safety.pattern}:${safety.reason}` };
613
- }
614
- } catch {
615
- // safety checks are best-effort; never fail the tool call here
616
- }
617
- return await ctx.stateStore.withLock(sessionID, async () => {
618
- const state = await ctx.stateStore.load(sessionID);
619
- if (state.startedAt === 0) {
620
- state.sessionId = sessionID;
621
- state.startedAt = Date.now();
622
- state.parentAgent = (toolCtx.snapshot as { parentAgentId?: string | null }).parentAgentId ?? null;
623
- }
624
- state.lastActivityAt = Date.now();
625
- state.turnCount += 1;
626
- const fp = fingerprint(tool, args, ctx.worktree);
627
- const now = Date.now();
628
- const decision = decide(state, fp, now, ctx.options);
629
- if (decision.action === "allow") {
630
- state.toolCalls.push({ tool, fingerprint: fp, at: now });
631
- await ctx.stateStore.save(state);
632
- return undefined;
633
- }
634
- if (decision.action === "block") {
635
- state.toolCalls.push({ tool, fingerprint: fp, at: now });
636
- state.blocksTriggered += 1;
637
- await ctx.stateStore.save(state);
638
- return { stop: true, reason: decision.reason };
639
- }
640
- if (decision.action === "warn") {
641
- state.toolCalls.push({ tool, fingerprint: fp, at: now });
642
- state.warningsIssued += 1;
643
- if (isLogOnlyWarn(decision, ctx.options)) ctx.logger.warn(`bizar: ${decision.reason}`);
644
- else { ctx.pendingInjections.set(sessionID, decision.reason); ctx.logger.warn(`bizar: ${decision.reason}`); }
645
- await ctx.stateStore.save(state);
646
- return undefined;
647
- }
648
- state.toolCalls.push({ tool, fingerprint: fp, at: now });
649
- state.warningsIssued += 1;
650
- ctx.pendingInjections.set(sessionID, decision.reason);
651
- ctx.logger.warn(`bizar: ${decision.reason}`);
652
- await ctx.stateStore.save(state);
653
- return undefined;
654
- });
655
- },
656
-
657
- afterTool: async (toolCtx) => {
658
- if (ctx.envFlags.disableLoop && ctx.envFlags.disableLog) return undefined;
659
- const sessionID = (toolCtx.snapshot as { sessionId?: string }).sessionId ?? (toolCtx.snapshot as { agentId?: string }).agentId ?? "";
660
- const tool = toolCtx.tool.name;
661
- if (!sessionID || !tool) return undefined;
662
- const startMs = Date.now();
663
- const ok = !toolCtx.result.isError;
664
- const args = toolCtx.input;
665
- await ctx.stateStore.withLock(sessionID, async () => {
666
- const state = await ctx.stateStore.load(sessionID);
667
- if (state.startedAt === 0) return;
668
- const fp = fingerprint(tool, args, ctx.worktree);
669
- const idx = findLastIndex(state.toolCalls, (c) => c.fingerprint === fp);
670
- if (idx >= 0) { const call = state.toolCalls[idx]; if (call) call.outcome = ok ? "ok" : "error"; }
671
- state.lastActivityAt = Date.now();
672
- await ctx.stateStore.save(state);
673
- });
674
- if (!ctx.envFlags.disableLog) {
675
- const durationMs = Date.now() - startMs;
676
- const fp = fingerprint(tool, args, ctx.worktree);
677
- try { await ctx.logWriter.write({ sessionId: sessionID, agent: null, tool, fingerprint: fp, outcome: ok ? "ok" : "error", durationMs }); }
678
- catch (err) { ctx.logger.warn(`log write failed: ${err instanceof Error ? err.message : String(err)}`); }
679
- }
680
- return undefined;
681
- },
682
-
683
- beforeModel: async (modelCtx) => {
684
- const sessionID = (modelCtx.snapshot as { sessionId?: string }).sessionId ?? (modelCtx.snapshot as { agentId?: string }).agentId ?? "";
685
- if (!sessionID) return undefined;
686
- // v6.0.0 — Pre-compaction memory flush (OpenClaw `flush-plan.ts` pattern).
687
- // When usage crosses the compaction threshold, write a snapshot to the
688
- // memory vault BEFORE the host summarizes the conversation. This closes
689
- // the durability gap where compaction drops context before persistence.
690
- try {
691
- const snap = (modelCtx.snapshot as { usage?: { total?: number; input?: number; output?: number; cached?: number }; maxContext?: number });
692
- if (snap?.usage?.total && snap.maxContext) {
693
- const flusher = createMemoryFlushOnCompact({
694
- worktree: ctx.worktree,
695
- logger: ctx.logger,
696
- enabled: true,
697
- });
698
- await flusher.maybeFlush({
699
- sessionId: sessionID,
700
- usage: snap.usage as { total: number },
701
- maxContext: snap.maxContext,
702
- recentMessages: (modelCtx.request.messages ?? []).slice(-10).map((m) => {
703
- const msg = m as { role?: string; content?: unknown };
704
- let text = '';
705
- if (typeof msg.content === 'string') text = msg.content;
706
- else if (Array.isArray(msg.content)) {
707
- text = msg.content
708
- .filter((p) => p && typeof p === 'object' && (p as { type?: string }).type === 'text')
709
- .map((p) => (p as { text?: string }).text ?? '')
710
- .join('\n');
711
- }
712
- return { role: String(msg.role ?? 'unknown'), content: text };
713
- }),
714
- });
715
- }
716
- } catch {
717
- // best-effort
718
- }
719
- const sysParts: string[] = [];
720
- const baseSys = modelCtx.request.systemPrompt ?? "";
721
- if (!baseSys.includes(REASONING_DIRECTIVE_MARKER)) sysParts.push(REASONING_DIRECTIVE);
722
- ensureToolDiscipline(sysParts, baseSys);
723
- const pending = ctx.pendingInjections.get(sessionID);
724
- if (pending) { sysParts.push(pending); ctx.pendingInjections.delete(sessionID); }
725
- const memCtx = popMemoryContext(sessionID);
726
- if (memCtx) sysParts.push(memCtx);
727
- const cleanedMessages = modelCtx.request.messages.map((m) => {
728
- const msg = m as { role?: string; content?: Array<{ type?: string; text?: string }> };
729
- if (msg.role !== "assistant" || !Array.isArray(msg.content)) return m;
730
- const newContent = msg.content.map((p) => {
731
- if (p.type === "text" && typeof p.text === "string" && p.text.includes("<think>")) return { ...p, text: stripInlineThinkBlocks(p.text) };
732
- return p;
733
- });
734
- return { ...m, content: newContent };
735
- });
736
- return { systemPrompt: [baseSys, ...sysParts].filter(Boolean).join("\n\n") || undefined, messages: cleanedMessages.length > 0 ? cleanedMessages as unknown as readonly never[] : undefined } as unknown as { systemPrompt?: string; messages?: readonly never[] };
737
- },
738
-
739
- onEvent: async (event) => {
740
- try {
741
- if (event.type === "message-added") await handleMessageAdded(event, ctx);
742
- if (event.type === "run-finished" || event.type === "run-failed") await handleRunFinished(event, ctx);
743
- } catch (err) { ctx.logger.warn(`onEvent error: ${err instanceof Error ? err.message : String(err)}`); }
744
- },
745
- };
746
- }
747
-
748
- async function handleMessageAdded(event: Extract<AgentRuntimeEvent, { type: "message-added" }>, ctx: RuntimeContext): Promise<void> {
749
- const message = event.message as unknown as Message;
750
- const sessionID = (event.snapshot as { sessionId?: string }).sessionId ?? (event.snapshot as { agentId?: string }).agentId ?? "";
751
- if (!sessionID) return;
752
- const role = (message as { role?: string }).role;
753
- if (role !== "user") {
754
- if (role === "assistant" && (message as { id?: string }).id) {
755
- ctx.sessionStartTimes.set(sessionID, ctx.sessionStartTimes.get(sessionID) ?? Date.now());
756
- ctx.seenMessageIds.set(sessionID, (ctx.seenMessageIds.get(sessionID) ?? new Set<string>()).add((message as { id?: string }).id as string));
757
- }
758
- return;
759
- }
760
- const messageID = (message as { id?: string }).id;
761
- if (!messageID) return;
762
- const seen = ctx.seenMessageIds.get(sessionID) ?? new Set<string>();
763
- if (seen.has(messageID)) return;
764
- seen.add(messageID);
765
- ctx.seenMessageIds.set(sessionID, seen);
766
- try {
767
- const messageText = readMessageText(message);
768
- if (messageText !== null) {
769
- const currentSettings = await ctx.settingsStore.get();
770
- const availableSlugs = await listPlanSlugs(ctx.worktree, ctx.logger);
771
- const result = parseSlashCommand(messageText, { currentSettings, availablePlanSlugs: availableSlugs, defaultPort: 4321 });
772
- if (result !== null) {
773
- if (result.settingsPatch) await ctx.settingsStore.update(result.settingsPatch);
774
- let finalResponse = result.response;
775
- if (result.sideEffect !== undefined) {
776
- const tools: AgentTool[] = [];
777
- const execOpts: ExecuteOptions = { tools: Object.fromEntries(tools.map((t) => [t.name, t as unknown as ExecuteOptions["tools"][string]])) as unknown as ExecuteOptions["tools"], defaultTemplate: currentSettings.defaultTemplate, defaultPort: 4321 };
778
- try {
779
- const exec = await executeSideEffect(result.sideEffect, { worktree: ctx.worktree, directory: ctx.directory, logger: ctx.logger }, execOpts);
780
- if (exec.responseOverride !== undefined) finalResponse = exec.responseOverride;
781
- else if (exec.responseSuffix !== undefined) finalResponse = `${result.response}${exec.responseSuffix}`;
782
- } catch (execErr: unknown) {
783
- const msg = execErr instanceof Error ? execErr.message : String(execErr);
784
- ctx.logger.warn(`bizar: side-effect crashed: ${msg}`);
785
- finalResponse = `Command failed: ${msg}`;
786
- }
787
- }
788
- if (result.dialog) {
789
- if (!/^dlg_[a-zA-Z0-9_-]{1,64}$/.test(result.dialog.id)) return;
790
- try {
791
- const { mkdir, writeFile } = await import("node:fs/promises");
792
- const dialogDir = pathJoin(homedir(), ".cache", "bizar", "dialogs");
793
- await mkdir(dialogDir, { recursive: true });
794
- await writeFile(pathJoin(dialogDir, `${result.dialog.id}.json`), JSON.stringify({ ...result.dialog, createdAt: new Date().toISOString() }, null, 2));
795
- } catch (dialogErr: unknown) { ctx.logger.warn(`bizar: dialog write failed: ${dialogErr instanceof Error ? dialogErr.message : String(dialogErr)}`); }
796
- return;
797
- }
798
- throw new Error(finalResponse);
799
- }
800
- }
801
- } catch (err) {
802
- const msg = err instanceof Error ? err.message : String(err);
803
- const looksLikeIoError = /(ENOENT|EACCES|EROFS|EISDIR|EPERM|Error:)/.test(msg);
804
- if (!looksLikeIoError) { ctx.logger.warn(`bizar: slash command: ${msg}`); return; }
805
- ctx.logger.warn(`bizar: slash-command failed: ${msg}`);
806
- }
807
- try { const messageText = readMessageText(message); if (messageText) void ctx.memoryInject(sessionID, messageText); }
808
- catch (e) { ctx.logger.debug(`bizar: memory inject skipped: ${e instanceof Error ? e.message : String(e)}`); }
809
- }
810
-
811
- async function handleRunFinished(event: Extract<AgentRuntimeEvent, { type: "run-finished" | "run-failed" }>, ctx: RuntimeContext): Promise<void> {
812
- const sessionID = (event.snapshot as { sessionId?: string }).sessionId ?? (event.snapshot as { agentId?: string }).agentId ?? "";
813
- if (!sessionID) return;
814
- const startedAt = ctx.sessionStartTimes.get(sessionID) ?? Date.now();
815
- const endedAt = Date.now();
816
- const errorMsg = event.type === "run-failed" ? event.error.message : undefined;
817
- void ctx.memoryWriteOnEnd(sessionID, { sessionID, agent: (event.snapshot as { agentRole?: string }).agentRole ?? "unknown", startedAt, endedAt, status: event.type === "run-failed" ? "error" : "idle", error: errorMsg }, "");
818
- ctx.sessionStartTimes.delete(sessionID);
819
- }
820
-
821
- // --- Legacy exports (kept for backwards compat with tests) ----------------
822
-
823
- /**
824
- * Race a promise against a timeout. If `promise` doesn't resolve within
825
- * `ms`, reject with an Error labeled with `label`.
826
- *
827
- * @deprecated Used only by pre-Phase-2 init-helpers tests.
828
- */
829
- export function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {
830
- return new Promise<T>((resolve, reject) => {
831
- const timer = setTimeout(() => reject(new Error(`bizar: ${label} timed out after ${ms}ms`)), ms);
832
- promise.then(
833
- (value) => { clearTimeout(timer); resolve(value); },
834
- (err) => { clearTimeout(timer); reject(err); },
835
- );
836
- });
837
- }
838
-
839
- /**
840
- * Read the set of valid session IDs from the client (with a 1s timeout).
841
- *
842
- * @deprecated Used only by pre-Phase-2 init-helpers tests.
843
- */
844
- interface LegacyPluginInput {
845
- client?: {
846
- session?: {
847
- list?: () => Promise<unknown>;
848
- };
849
- };
850
- }
851
-
852
- export async function readValidSessionIds(input: LegacyPluginInput | unknown): Promise<Set<string>> {
853
- const client = (input as LegacyPluginInput | null | undefined)?.client;
854
- const listFn = client?.session?.list;
855
- if (typeof listFn !== "function") return new Set<string>();
856
- try {
857
- const result = await withTimeout(Promise.resolve().then(() => listFn()), 1000, "client.session.list");
858
- // Normalize: accept either an array or an object with .data
859
- let items: unknown[] = [];
860
- if (Array.isArray(result)) items = result;
861
- else if (result && typeof result === "object" && Array.isArray((result as { data?: unknown }).data)) items = (result as { data: unknown[] }).data;
862
- else return new Set<string>();
863
- const ids = new Set<string>();
864
- for (const item of items) {
865
- if (typeof item === "string") ids.add(item);
866
- else if (item && typeof item === "object" && typeof (item as { id?: unknown }).id === "string") ids.add((item as { id: string }).id);
867
- }
868
- return ids;
869
- } catch {
870
- return new Set<string>();
871
- }
872
- }