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