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