@polderlabs/bizar-plugin 0.6.1 → 0.6.2
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/index.ts +90 -40
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -125,7 +125,7 @@ import { SettingsStore } from "./src/settings.js";
|
|
|
125
125
|
import { parseSlashCommand } from "./src/commands.js";
|
|
126
126
|
import { createPlanActionTool } from "./src/tools/plan-action.js";
|
|
127
127
|
import { createWaitForFeedbackTool } from "./src/tools/wait-for-feedback.js";
|
|
128
|
-
import {
|
|
128
|
+
import { stripInlineThinkBlocks } from "./src/reasoning-clean.js";
|
|
129
129
|
|
|
130
130
|
// v0.5.0 — visual plan wiring: side-effect executor + plan-fs
|
|
131
131
|
import { executeSideEffect, type ExecuteOptions } from "./src/commands-impl.js";
|
|
@@ -700,6 +700,47 @@ async function listPlanSlugs(worktree: string, logger: Logger): Promise<string[]
|
|
|
700
700
|
* delegates to the runtime context and the supporting modules.
|
|
701
701
|
*/
|
|
702
702
|
function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
703
|
+
// ────────────────────────────────────────────────────────────────────
|
|
704
|
+
// v0.6.2 — Reasoning directive
|
|
705
|
+
// ────────────────────────────────────────────────────────────────────
|
|
706
|
+
// Some reasoning models (notably MiniMax M3 via OpenRouter) emit
|
|
707
|
+
// their chain-of-thought in BOTH the structured `reasoning` /
|
|
708
|
+
// `reasoning_details` field AND inline as `` blocks inside
|
|
709
|
+
// `message.content`. opencode's openrouter SDK extracts the structured
|
|
710
|
+
// reasoning correctly and renders it as a separate "Thought" panel,
|
|
711
|
+
// but it does NOT strip the inline blocks from `content`, so the user
|
|
712
|
+
// sees the same thinking text twice — once in the proper panel and
|
|
713
|
+
// again as visible message text below it.
|
|
714
|
+
//
|
|
715
|
+
// The opencode plugin API in this version does NOT trigger a
|
|
716
|
+
// `config` hook (the `wrap-fetch` workaround from v0.6.1 is dead
|
|
717
|
+
// code in current builds), so we cannot post-process the response
|
|
718
|
+
// stream. The only working hooks that can help are:
|
|
719
|
+
//
|
|
720
|
+
// 1. `experimental.chat.system.transform` — runs every turn; we
|
|
721
|
+
// push a directive telling the model to put thinking in the
|
|
722
|
+
// structured field only.
|
|
723
|
+
// 2. `experimental.chat.messages.transform` — runs before each
|
|
724
|
+
// request; we strip `` blocks from previous assistant
|
|
725
|
+
// messages so the model sees clean history and is less likely
|
|
726
|
+
// to keep emitting inline ``.
|
|
727
|
+
//
|
|
728
|
+
// Neither fixes the CURRENT response (the model has already
|
|
729
|
+
// returned), but together they strongly reduce — and in many cases
|
|
730
|
+
// eliminate — the duplication on subsequent turns.
|
|
731
|
+
const REASONING_DIRECTIVE_MARKER = "BIZAR_REASONING_DIRECTIVE_v0.6.2";
|
|
732
|
+
const REASONING_DIRECTIVE = [
|
|
733
|
+
REASONING_DIRECTIVE_MARKER,
|
|
734
|
+
"",
|
|
735
|
+
"When reasoning is enabled for this conversation, output your thinking",
|
|
736
|
+
"ONLY in the model's structured reasoning field. Do NOT emit `` blocks",
|
|
737
|
+
"inline inside your message content — the opencode host extracts the",
|
|
738
|
+
"reasoning field and renders it as a separate, collapsable \"Thought\"",
|
|
739
|
+
"panel. If you also emit the same text inline, the user will see your",
|
|
740
|
+
"thinking twice (once in the panel and once as visible message body).",
|
|
741
|
+
"Keep the actual response text in the normal content stream.",
|
|
742
|
+
].join(" ");
|
|
743
|
+
|
|
703
744
|
// Build the 7 tools. We always register them; if the serve child is
|
|
704
745
|
// not available, the background tools return a clear error. The
|
|
705
746
|
// bizar_get_plan_comments, bizar_plan_action, and
|
|
@@ -707,7 +748,7 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
707
748
|
// work regardless of the serve child's state.
|
|
708
749
|
//
|
|
709
750
|
// v0.4.0 — added `bizar_plan_action` (CRUD on the v2 canvas) and
|
|
710
|
-
//
|
|
751
|
+
// bizar_wait_for_feedback (poll until feedback). Both are pure
|
|
711
752
|
// file I/O — no serve child required.
|
|
712
753
|
//
|
|
713
754
|
// v0.5.0 — renamed `bizarre_*` → `bizar_*` (single `r`) to match
|
|
@@ -756,36 +797,57 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
756
797
|
};
|
|
757
798
|
|
|
758
799
|
return {
|
|
759
|
-
//
|
|
760
|
-
//
|
|
761
|
-
//
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
|
|
800
|
+
// Push a persistent system-prompt directive that tells reasoning
|
|
801
|
+
// models to put their thinking in the structured reasoning field
|
|
802
|
+
// (rendered as a separate "Thought" panel by opencode) rather than
|
|
803
|
+
// also emitting it inline as `` blocks in the content. The openrouter
|
|
804
|
+
// SDK does not strip the inline `` blocks, so without this
|
|
805
|
+
// directive the user sees the reasoning twice — once in the proper
|
|
806
|
+
// panel and once as visible message text.
|
|
807
|
+
//
|
|
808
|
+
// We push the directive only when the marker is absent so we don't
|
|
809
|
+
// append it on every turn.
|
|
810
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
811
|
+
const sessionID = input.sessionID;
|
|
812
|
+
if (!sessionID) return;
|
|
813
|
+
if (!output.system.some((s) => s.includes(REASONING_DIRECTIVE_MARKER))) {
|
|
814
|
+
output.system.push(REASONING_DIRECTIVE);
|
|
815
|
+
}
|
|
816
|
+
// §3.1, §5.4 — handoff injection point. We push a single string
|
|
817
|
+
// onto `output.system` if a pending injection is queued for this
|
|
818
|
+
// session.
|
|
819
|
+
const pending = ctx.pendingInjections.get(sessionID);
|
|
820
|
+
if (pending) {
|
|
821
|
+
output.system.push(pending);
|
|
822
|
+
ctx.pendingInjections.delete(sessionID);
|
|
823
|
+
}
|
|
824
|
+
},
|
|
825
|
+
|
|
826
|
+
// Before the model is called, strip `` blocks from
|
|
827
|
+
// any text content in previous assistant messages. This keeps the
|
|
828
|
+
// model's view of its own history clean of the duplicated thinking
|
|
829
|
+
// it emitted earlier, reducing the chance it will keep emitting
|
|
830
|
+
// inline `` on subsequent turns.
|
|
831
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
765
832
|
try {
|
|
766
|
-
const
|
|
767
|
-
if (!
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
if (!
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
original as Parameters<typeof wrapFetchForReasoningCleanup>[0],
|
|
781
|
-
{ debug, providers: [name] },
|
|
782
|
-
);
|
|
783
|
-
(prov.options.fetch as { __bizarReasoningClean?: boolean }).__bizarReasoningClean = true;
|
|
784
|
-
debug(`wrapped provider.fetch for ${name}`);
|
|
833
|
+
const messages = (output as { messages?: unknown }).messages;
|
|
834
|
+
if (!Array.isArray(messages)) return;
|
|
835
|
+
for (const msg of messages) {
|
|
836
|
+
if (!msg || typeof msg !== "object") continue;
|
|
837
|
+
const m = msg as { role?: unknown; parts?: unknown };
|
|
838
|
+
if (m.role !== "assistant") continue;
|
|
839
|
+
if (!Array.isArray(m.parts)) continue;
|
|
840
|
+
for (const part of m.parts) {
|
|
841
|
+
if (!part || typeof part !== "object") continue;
|
|
842
|
+
const p = part as { type?: unknown; text?: unknown };
|
|
843
|
+
if (p.type === "text" && typeof p.text === "string" && p.text.includes("<think>")) {
|
|
844
|
+
p.text = stripInlineThinkBlocks(p.text);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
785
847
|
}
|
|
786
848
|
} catch (err) {
|
|
787
849
|
ctx.logger.warn(
|
|
788
|
-
`bizar:
|
|
850
|
+
`bizar: messages.transform failed (passing through): ${
|
|
789
851
|
err instanceof Error ? err.message : String(err)
|
|
790
852
|
}`,
|
|
791
853
|
);
|
|
@@ -1102,18 +1164,6 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
1102
1164
|
}
|
|
1103
1165
|
},
|
|
1104
1166
|
|
|
1105
|
-
// §3.1, §5.4 — handoff injection point. We push a single string onto
|
|
1106
|
-
// `output.system` if a pending injection is queued for this session.
|
|
1107
|
-
"experimental.chat.system.transform": async (input, output) => {
|
|
1108
|
-
const sessionID = input.sessionID;
|
|
1109
|
-
if (!sessionID) return;
|
|
1110
|
-
const pending = ctx.pendingInjections.get(sessionID);
|
|
1111
|
-
if (pending) {
|
|
1112
|
-
output.system.push(pending);
|
|
1113
|
-
ctx.pendingInjections.delete(sessionID);
|
|
1114
|
-
}
|
|
1115
|
-
},
|
|
1116
|
-
|
|
1117
1167
|
// v0.4.2 — register the 4 background tools.
|
|
1118
1168
|
tool: tools,
|
|
1119
1169
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Bizar opencode plugin — loop detection, status reporting, handoff signal, background agents, and slash commands + visual plan flow for subagent activity",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|