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