@polderlabs/bizar 4.9.0 → 5.0.1
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/bizar-dash/dist/assets/icons-Bo0iH9EC.js +701 -0
- package/bizar-dash/dist/assets/icons-Bo0iH9EC.js.map +1 -0
- package/bizar-dash/dist/assets/{index-DU61awG3.js → index-DmpSFPJY.js} +1 -1
- package/bizar-dash/dist/assets/{index-DU61awG3.js.map → index-DmpSFPJY.js.map} +1 -1
- package/bizar-dash/dist/assets/main-C1cpttnv.js +19 -0
- package/bizar-dash/dist/assets/main-C1cpttnv.js.map +1 -0
- package/bizar-dash/dist/assets/main-DTkNlLrw.css +1 -0
- package/bizar-dash/dist/assets/markdown-DIquRulQ.js +29 -0
- package/bizar-dash/dist/assets/markdown-DIquRulQ.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-BtxQJftK.js +2 -0
- package/bizar-dash/dist/assets/mobile-BtxQJftK.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-DD-FZrTC.js +1 -0
- package/bizar-dash/dist/assets/{mobile-CL5uUQEC.js.map → mobile-DD-FZrTC.js.map} +1 -1
- package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js +40 -0
- package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js.map +1 -0
- package/bizar-dash/dist/index.html +6 -3
- package/bizar-dash/dist/mobile.html +5 -2
- package/bizar-dash/node_modules/.package-lock.json +6 -0
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/package-lock.json +6 -0
- package/bizar-dash/skills/eval/SKILL.md +237 -0
- package/bizar-dash/src/server/api.mjs +35 -0
- package/bizar-dash/src/server/auth.mjs +155 -1
- package/bizar-dash/src/server/diagnostics-store.mjs +452 -2
- package/bizar-dash/src/server/eval-store.mjs +226 -0
- package/bizar-dash/src/server/eval.mjs +347 -0
- package/bizar-dash/src/server/memory-store.mjs +46 -0
- package/bizar-dash/src/server/ocr.mjs +55 -0
- package/bizar-dash/src/server/plugins/registry.mjs +363 -0
- package/bizar-dash/src/server/plugins/sandbox.mjs +655 -0
- package/bizar-dash/src/server/plugins/store.mjs +659 -0
- package/bizar-dash/src/server/providers-store.mjs +11 -4
- package/bizar-dash/src/server/routes/_shared.mjs +2 -2
- package/bizar-dash/src/server/routes/clipboard.mjs +173 -0
- package/bizar-dash/src/server/routes/doctor.mjs +71 -0
- package/bizar-dash/src/server/routes/env-vars.mjs +67 -1
- package/bizar-dash/src/server/routes/eval.mjs +147 -0
- package/bizar-dash/src/server/routes/memory.mjs +3 -0
- package/bizar-dash/src/server/routes/ocr.mjs +182 -0
- package/bizar-dash/src/server/routes/opencode-session-detail.mjs +61 -42
- package/bizar-dash/src/server/routes/plugins.mjs +220 -0
- package/bizar-dash/src/server/routes/schedules.mjs +55 -0
- package/bizar-dash/src/server/routes/users.mjs +84 -0
- package/bizar-dash/src/server/routes/voice.mjs +131 -0
- package/bizar-dash/src/server/routes/workspaces.mjs +204 -0
- package/bizar-dash/src/server/serve-info.mjs +172 -0
- package/bizar-dash/src/server/voice-store.mjs +202 -0
- package/bizar-dash/src/server/voice-transcribe.mjs +72 -0
- package/bizar-dash/src/server/workspaces.mjs +626 -0
- package/bizar-dash/src/web/App.tsx +48 -11
- package/bizar-dash/src/web/components/AutosaveField.tsx +50 -0
- package/bizar-dash/src/web/components/DoctorPanel.tsx +160 -0
- package/bizar-dash/src/web/components/EnvVarManager.tsx +245 -62
- package/bizar-dash/src/web/components/InviteDialog.tsx +205 -0
- package/bizar-dash/src/web/components/ScheduleTemplateCard.tsx +71 -0
- package/bizar-dash/src/web/components/ScreenshotCapture.tsx +138 -0
- package/bizar-dash/src/web/components/ScreenshotOCR.tsx +42 -0
- package/bizar-dash/src/web/components/SettingsNav.tsx +101 -0
- package/bizar-dash/src/web/components/Sidebar.tsx +25 -1
- package/bizar-dash/src/web/components/StatusBadge.tsx +33 -2
- package/bizar-dash/src/web/components/Topbar.tsx +11 -0
- package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +182 -0
- package/bizar-dash/src/web/components/VoiceRecorder.tsx +104 -0
- package/bizar-dash/src/web/components/WorkspaceSelector.tsx +158 -0
- package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +64 -0
- package/bizar-dash/src/web/components/chat/Composer.tsx +1 -1
- package/bizar-dash/src/web/components/chat/useChat.ts +118 -1
- package/bizar-dash/src/web/hooks/useAutosave.ts +107 -0
- package/bizar-dash/src/web/lib/types.ts +134 -0
- package/bizar-dash/src/web/styles/chat.css +8 -5
- package/bizar-dash/src/web/styles/main.css +77 -2
- package/bizar-dash/src/web/styles/memory.css +82 -0
- package/bizar-dash/src/web/styles/settings.css +265 -0
- package/bizar-dash/src/web/views/Chat.tsx +15 -1
- package/bizar-dash/src/web/views/Doctor.tsx +317 -0
- package/bizar-dash/src/web/views/Memory.tsx +17 -2
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +1 -0
- package/bizar-dash/src/web/views/Schedules.tsx +94 -12
- package/bizar-dash/src/web/views/Settings.tsx +78 -45
- package/bizar-dash/src/web/views/Workspace.tsx +294 -0
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +114 -133
- package/bizar-dash/src/web/views/memory/FromScreenshotPanel.tsx +23 -0
- package/bizar-dash/src/web/views/memory/VaultFromClipboardPanel.tsx +101 -0
- package/bizar-dash/src/web/views/settings/AgentSection.tsx +23 -12
- package/bizar-dash/src/web/views/settings/EnvVarsSection.tsx +5 -6
- package/bizar-dash/src/web/views/settings/GeneralSection.tsx +38 -15
- package/bizar-dash/src/web/views/settings/MemorySection.tsx +92 -8
- package/bizar-dash/src/web/views/settings/WorkspacesSection.tsx +189 -0
- package/bizar-dash/tests/autosave.test.tsx +276 -0
- package/bizar-dash/tests/bundle-analysis.test.mjs +5 -2
- package/bizar-dash/tests/chat-composer.test.tsx +140 -0
- package/bizar-dash/tests/clipboard.test.mjs +147 -0
- package/bizar-dash/tests/components/doctor-panel.test.tsx +105 -0
- package/bizar-dash/tests/components/screenshot-ocr.test.tsx +75 -0
- package/bizar-dash/tests/components/workspace-selector.test.tsx +73 -0
- package/bizar-dash/tests/deploy-templates.test.mjs +100 -0
- package/bizar-dash/tests/diagnostics-store.test.mjs +206 -0
- package/bizar-dash/tests/env-vars-extended.test.mjs +190 -0
- package/bizar-dash/tests/eval/fixtures.test.mjs +141 -0
- package/bizar-dash/tests/eval/report.test.mjs +284 -0
- package/bizar-dash/tests/eval/runner.test.mjs +471 -0
- package/bizar-dash/tests/layout-spacing.test.mjs +101 -0
- package/bizar-dash/tests/memory-default-vault.test.mjs +98 -0
- package/bizar-dash/tests/minimax-bar.test.tsx +113 -0
- package/bizar-dash/tests/minimax-models.test.mjs +123 -0
- package/bizar-dash/tests/ocr.test.mjs +87 -0
- package/bizar-dash/tests/opencode-sessions-detail.test.mjs +158 -0
- package/bizar-dash/tests/plugins-registry.test.mjs +387 -0
- package/bizar-dash/tests/plugins-sandbox.test.mjs +374 -0
- package/bizar-dash/tests/plugins-store.test.mjs +455 -0
- package/bizar-dash/tests/routes-doctor.test.mjs +159 -0
- package/bizar-dash/tests/schedules-templates.test.mjs +144 -0
- package/bizar-dash/tests/settings-layout.test.tsx +129 -0
- package/bizar-dash/tests/settings-mode-wiring.test.tsx +151 -0
- package/bizar-dash/tests/settings-nav.test.tsx +126 -0
- package/bizar-dash/tests/users.test.mjs +108 -0
- package/bizar-dash/tests/voice-recorder.test.tsx +95 -0
- package/bizar-dash/tests/voice-store.test.mjs +148 -0
- package/bizar-dash/tests/voice-transcribe.test.mjs +87 -0
- package/bizar-dash/tests/workspaces.test.mjs +527 -0
- package/cli/bin.mjs +72 -2
- package/cli/commands/clip.mjs +146 -0
- package/cli/commands/deploy/cloudflare.mjs +250 -0
- package/cli/commands/deploy/docker.mjs +221 -0
- package/cli/commands/deploy/fly.mjs +161 -0
- package/cli/commands/deploy/vercel.mjs +225 -0
- package/cli/commands/deploy.mjs +240 -0
- package/cli/commands/eval.mjs +378 -0
- package/cli/commands/marketplace.mjs +64 -0
- package/cli/commands/minimax.mjs +5 -2
- package/cli/commands/ocr.mjs +165 -0
- package/cli/commands/plugin.mjs +358 -0
- package/cli/commands/voice.mjs +211 -0
- package/cli/commands/workspace.mjs +247 -0
- package/cli/tests/minimax-cli.test.mjs +79 -0
- package/config/agents/frigg.md +1 -1
- package/config/agents/heimdall.md +1 -1
- package/config/agents/mimir.md +1 -1
- package/config/agents/quick.md +1 -1
- package/config/agents/semble-search.md +1 -1
- package/config/agents/vor.md +1 -1
- package/config/opencode.json +28 -21
- package/config/opencode.json.template +8 -1
- package/package.json +12 -8
- package/plugins/bizar/index.ts +77 -0
- package/plugins/bizar/src/compaction.d.mts +48 -0
- package/plugins/bizar/src/compaction.mjs +192 -0
- package/plugins/bizar/tests/compaction.test.ts +264 -0
- package/templates/deploy/cloudflare/README.md +32 -0
- package/templates/deploy/cloudflare/functions-index.template.js +15 -0
- package/templates/deploy/cloudflare/wrangler.toml.template +9 -0
- package/templates/deploy/docker/.env.template +16 -0
- package/templates/deploy/docker/README.md +58 -0
- package/templates/deploy/docker/docker-compose.template.yml +23 -0
- package/templates/deploy/fly/README.md +35 -0
- package/templates/deploy/fly/fly.toml.template +28 -0
- package/templates/deploy/vercel/README.md +29 -0
- package/templates/deploy/vercel/api-index.template.js +18 -0
- package/templates/deploy/vercel/vercel.json.template +16 -0
- package/templates/eval-fixtures/README.md +58 -0
- package/templates/eval-fixtures/code-search-basic.json +28 -0
- package/templates/eval-fixtures/latency-bounds.json +16 -0
- package/templates/eval-fixtures/regression-suite.json +79 -0
- package/templates/eval-fixtures/response-format.json +30 -0
- package/templates/eval-fixtures/tool-call-correctness.json +24 -0
- package/templates/plugin-template/README.md +121 -0
- package/templates/plugin-template/index.js +66 -0
- package/templates/plugin-template/plugin.json +42 -0
- package/templates/plugin-template/tests/plugin.test.js +83 -0
- package/templates/schedules/daily-backup.json +12 -0
- package/templates/schedules/daily-cleanup.json +12 -0
- package/templates/schedules/hourly-health-check.json +12 -0
- package/templates/schedules/webhook-on-push.json +13 -0
- package/templates/schedules/weekly-digest.json +13 -0
- package/bizar-dash/dist/assets/main-DaC1Lc6q.js +0 -366
- package/bizar-dash/dist/assets/main-DaC1Lc6q.js.map +0 -1
- package/bizar-dash/dist/assets/main-DfmIfOUS.css +0 -1
- package/bizar-dash/dist/assets/mobile-CL5uUQEC.js +0 -1
- package/bizar-dash/dist/assets/mobile-D5WTWvuh.js +0 -338
- package/bizar-dash/dist/assets/mobile-D5WTWvuh.js.map +0 -1
- package/bizar-dash/src/web/views/settings/BackupSection.tsx +0 -16
- package/bizar-dash/src/web/views/settings/ProvidersSection.tsx +0 -16
- package/bizar-dash/src/web/views/settings/SkillsSection.tsx +0 -16
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/workspace.mjs
|
|
3
|
+
*
|
|
4
|
+
* v5.0.0 — Workspace management CLI — talks to the running dashboard's HTTP API.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
|
|
11
|
+
const HOME = homedir();
|
|
12
|
+
|
|
13
|
+
// ── Config dir helper ──────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
function getBizarConfigDir() {
|
|
16
|
+
if (process.platform === 'win32') {
|
|
17
|
+
return process.env.APPDATA
|
|
18
|
+
? join(process.env.APPDATA, 'bizar')
|
|
19
|
+
: join(homedir(), '.config', 'bizar');
|
|
20
|
+
}
|
|
21
|
+
return process.env.XDG_CONFIG_HOME
|
|
22
|
+
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
23
|
+
: join(homedir(), '.config', 'bizar');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getDashboardPort() {
|
|
27
|
+
const portFile = join(getBizarConfigDir(), 'dashboard.port');
|
|
28
|
+
try {
|
|
29
|
+
const port = parseInt(readFileSync(portFile, 'utf8').trim(), 10);
|
|
30
|
+
if (Number.isFinite(port) && port > 0) return port;
|
|
31
|
+
} catch {
|
|
32
|
+
/* fall through */
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── API helpers ───────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
async function postJson(baseUrl, path, body) {
|
|
40
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'Content-Type': 'application/json' },
|
|
43
|
+
body: JSON.stringify(body || {}),
|
|
44
|
+
});
|
|
45
|
+
const text = await res.text();
|
|
46
|
+
let json = null;
|
|
47
|
+
try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
const msg = json?.message || json?.error || text || `HTTP ${res.status}`;
|
|
50
|
+
throw new Error(`${path} failed: ${msg}`);
|
|
51
|
+
}
|
|
52
|
+
return json;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function getJson(baseUrl, path) {
|
|
56
|
+
const res = await fetch(`${baseUrl}${path}`);
|
|
57
|
+
const text = await res.text();
|
|
58
|
+
let json = null;
|
|
59
|
+
try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const msg = json?.message || json?.error || text || `HTTP ${res.status}`;
|
|
62
|
+
throw new Error(`${path} failed: ${msg}`);
|
|
63
|
+
}
|
|
64
|
+
return json;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function delJson(baseUrl, path) {
|
|
68
|
+
const res = await fetch(`${baseUrl}${path}`, { method: 'DELETE' });
|
|
69
|
+
const text = await res.text();
|
|
70
|
+
let json = null;
|
|
71
|
+
try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const msg = json?.message || json?.error || text || `HTTP ${res.status}`;
|
|
74
|
+
throw new Error(`${path} failed: ${msg}`);
|
|
75
|
+
}
|
|
76
|
+
return json;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Help ───────────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export function showWorkspaceHelp() {
|
|
82
|
+
console.log(`
|
|
83
|
+
bizar workspace — Manage workspaces (via the dashboard's HTTP API)
|
|
84
|
+
|
|
85
|
+
Usage:
|
|
86
|
+
bizar workspace list List all workspaces
|
|
87
|
+
bizar workspace create <name> Create a new workspace
|
|
88
|
+
bizar workspace switch <id> Set active workspace
|
|
89
|
+
bizar workspace invite <email> [role] Generate invite URL (role: admin|editor|viewer)
|
|
90
|
+
bizar workspace accept <token> Accept an invite
|
|
91
|
+
bizar workspace members List workspace members
|
|
92
|
+
bizar workspace remove-member <userId> Remove a member
|
|
93
|
+
|
|
94
|
+
Description:
|
|
95
|
+
Subcommands call the running dashboard's HTTP API. If no dashboard is
|
|
96
|
+
reachable, you'll be told to run \`bizar dash start\` first.
|
|
97
|
+
|
|
98
|
+
Workspaces let you share access with team members. Each workspace has
|
|
99
|
+
its own members, settings, and data.
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
bizar workspace list
|
|
103
|
+
bizar workspace create "Engineering"
|
|
104
|
+
bizar workspace invite alice@example.com editor
|
|
105
|
+
bizar workspace accept abc123def456
|
|
106
|
+
bizar workspace members
|
|
107
|
+
bizar workspace remove-member usr_abc123
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Command runner ───────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export async function runWorkspaceCommand(args) {
|
|
114
|
+
const sub = args[0];
|
|
115
|
+
const positional = args.slice(1).filter((a) => !a.startsWith('-'));
|
|
116
|
+
const flags = args.slice(1).filter((a) => a.startsWith('-'));
|
|
117
|
+
|
|
118
|
+
if (!sub || sub === '--help' || sub === '-h' || flags.includes('--help') || flags.includes('-h')) {
|
|
119
|
+
showWorkspaceHelp();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const port = getDashboardPort();
|
|
124
|
+
if (!port) {
|
|
125
|
+
console.error(chalk.red(' ✗ Dashboard is not running (no port file).'));
|
|
126
|
+
console.error(chalk.dim(' Start it first: `bizar dash start --bg`'));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const baseUrl = `http://127.0.0.1:${port}`;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
if (sub === 'list') {
|
|
133
|
+
const r = await getJson(baseUrl, '/api/workspaces');
|
|
134
|
+
const workspaces = r.workspaces || [];
|
|
135
|
+
if (workspaces.length === 0) {
|
|
136
|
+
console.log(chalk.dim(' (no workspaces)'));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
console.log(chalk.bold(' Workspaces:'));
|
|
140
|
+
for (const { workspace, role } of workspaces) {
|
|
141
|
+
const badge = role === 'admin' ? chalk.green('admin') : role === 'editor' ? chalk.yellow('editor') : chalk.blue('viewer');
|
|
142
|
+
console.log(` ${workspace.id.padEnd(20)} ${workspace.name.padEnd(30)} ${badge}`);
|
|
143
|
+
}
|
|
144
|
+
} else if (sub === 'create') {
|
|
145
|
+
const name = positional[0];
|
|
146
|
+
if (!name) {
|
|
147
|
+
console.error(chalk.red(' ✗ Missing workspace name. Usage: bizar workspace create <name>'));
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
const r = await postJson(baseUrl, '/api/workspaces', { name });
|
|
151
|
+
console.log(chalk.green(` ✓ Created workspace "${r.workspace.name}" (${r.workspace.id})`));
|
|
152
|
+
} else if (sub === 'switch') {
|
|
153
|
+
const wsId = positional[0];
|
|
154
|
+
if (!wsId) {
|
|
155
|
+
console.error(chalk.red(' ✗ Missing workspace id. Usage: bizar workspace switch <id>'));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
// Verify workspace exists
|
|
159
|
+
try {
|
|
160
|
+
await getJson(baseUrl, `/api/workspaces/${wsId}`);
|
|
161
|
+
} catch {
|
|
162
|
+
console.error(chalk.red(` ✗ Workspace not found: ${wsId}`));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
console.log(chalk.green(` ✓ Switched to workspace ${wsId}`));
|
|
166
|
+
console.log(chalk.dim(' Note: Run `bizar workspace list` to see workspace ids'));
|
|
167
|
+
} else if (sub === 'invite') {
|
|
168
|
+
const email = positional[0];
|
|
169
|
+
if (!email) {
|
|
170
|
+
console.error(chalk.red(' ✗ Missing email. Usage: bizar workspace invite <email> [role]'));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const role = positional[1] || 'editor';
|
|
174
|
+
if (!['admin', 'editor', 'viewer'].includes(role)) {
|
|
175
|
+
console.error(chalk.red(` ✗ Invalid role: ${role}. Must be admin, editor, or viewer.`));
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
// Get first workspace id (or use BIZAR_WORKSPACE_ID env)
|
|
179
|
+
const wsId = process.env.BIZAR_WORKSPACE_ID;
|
|
180
|
+
if (!wsId) {
|
|
181
|
+
console.error(chalk.red(' ✗ Set BIZAR_WORKSPACE_ID env var or run from a workspace context.'));
|
|
182
|
+
console.error(chalk.dim(' Run `bizar workspace list` to find your workspace id.'));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
const r = await postJson(baseUrl, `/api/workspaces/${wsId}/invites`, { email, role });
|
|
186
|
+
console.log(chalk.green(` ✓ Invite created for ${email} as ${role}`));
|
|
187
|
+
console.log(chalk.dim(' Invite URL:'));
|
|
188
|
+
console.log(` ${r.url}`);
|
|
189
|
+
} else if (sub === 'accept') {
|
|
190
|
+
const token = positional[0];
|
|
191
|
+
if (!token) {
|
|
192
|
+
console.error(chalk.red(' ✗ Missing invite token. Usage: bizar workspace accept <token>'));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
// For CLI acceptance, we need user info - prompt or use env
|
|
196
|
+
const email = process.env.BIZAR_USER_EMAIL;
|
|
197
|
+
const name = process.env.BIZAR_USER_NAME || 'CLI User';
|
|
198
|
+
if (!email) {
|
|
199
|
+
console.error(chalk.red(' ✗ Set BIZAR_USER_EMAIL env var with your email address.'));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const r = await postJson(baseUrl, `/api/invites/${token}/accept`, { email, name });
|
|
203
|
+
console.log(chalk.green(` ✓ Joined workspace "${r.workspace.name}" as ${r.role}`));
|
|
204
|
+
} else if (sub === 'members') {
|
|
205
|
+
const wsId = process.env.BIZAR_WORKSPACE_ID;
|
|
206
|
+
if (!wsId) {
|
|
207
|
+
console.error(chalk.red(' ✗ Set BIZAR_WORKSPACE_ID env var or run from a workspace context.'));
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
const r = await getJson(baseUrl, `/api/workspaces/${wsId}`);
|
|
211
|
+
const members = r.members || [];
|
|
212
|
+
if (members.length === 0) {
|
|
213
|
+
console.log(chalk.dim(' (no members)'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
console.log(chalk.bold(` Members of ${r.workspace.name}:`));
|
|
217
|
+
for (const m of members) {
|
|
218
|
+
const badge = m.role === 'admin' ? chalk.green('admin') : m.role === 'editor' ? chalk.yellow('editor') : chalk.blue('viewer');
|
|
219
|
+
console.log(` ${m.userId.padEnd(20)} ${(m.name || m.email).padEnd(20)} ${badge}`);
|
|
220
|
+
}
|
|
221
|
+
} else if (sub === 'remove-member') {
|
|
222
|
+
const userId = positional[0];
|
|
223
|
+
if (!userId) {
|
|
224
|
+
console.error(chalk.red(' ✗ Missing user id. Usage: bizar workspace remove-member <userId>'));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
const wsId = process.env.BIZAR_WORKSPACE_ID;
|
|
228
|
+
if (!wsId) {
|
|
229
|
+
console.error(chalk.red(' ✗ Set BIZAR_WORKSPACE_ID env var.'));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
await delJson(baseUrl, `/api/workspaces/${wsId}/members/${userId}`);
|
|
233
|
+
console.log(chalk.green(` ✓ Removed member ${userId}`));
|
|
234
|
+
} else {
|
|
235
|
+
console.error(chalk.red(` ✗ Unknown workspace subcommand: ${sub}`));
|
|
236
|
+
showWorkspaceHelp();
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error(chalk.red(` ✗ ${err.message}`));
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function run(name, args, isHelpRequest) {
|
|
246
|
+
await runWorkspaceCommand(args);
|
|
247
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/tests/minimax-cli.test.mjs
|
|
3
|
+
*
|
|
4
|
+
* Tests for MiniMax CLI bar rendering.
|
|
5
|
+
* Verifies bar shows CONSUMED quota as filled blocks (not remaining).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { test, describe } from 'node:test';
|
|
9
|
+
import assert from 'node:assert';
|
|
10
|
+
|
|
11
|
+
// ── Bar rendering helper (mirrors minimax.mjs logic) ──────────────────────────
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render a quota bar string.
|
|
15
|
+
* @param {number} remainingPct - percentage of quota remaining (0-100)
|
|
16
|
+
* @returns {string} bar string like '██░░░░░░░░░░░░░░░░░' (20 chars total)
|
|
17
|
+
*/
|
|
18
|
+
function renderBar(remainingPct) {
|
|
19
|
+
const consumed = 100 - remainingPct;
|
|
20
|
+
const filled = Math.round(consumed / 5);
|
|
21
|
+
const empty = 20 - filled;
|
|
22
|
+
return '█'.repeat(filled) + '░'.repeat(empty);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── Tests ───────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
describe('MiniMax CLI bar rendering', () => {
|
|
28
|
+
|
|
29
|
+
test('91% remaining → 9% consumed → 2 filled blocks (9/5 rounds to 2)', () => {
|
|
30
|
+
const bar = renderBar(91);
|
|
31
|
+
const filledCount = (bar.match(/█/g) || []).length;
|
|
32
|
+
assert.strictEqual(filledCount, 2, `Expected 2 filled blocks for 91% remaining, got ${filledCount}`);
|
|
33
|
+
assert.strictEqual(bar.length, 20, `Bar should be exactly 20 chars`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('100% remaining → 0% consumed → 0 filled blocks', () => {
|
|
37
|
+
const bar = renderBar(100);
|
|
38
|
+
const filledCount = (bar.match(/█/g) || []).length;
|
|
39
|
+
assert.strictEqual(filledCount, 0, `Expected 0 filled blocks for 100% remaining`);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('0% remaining → 100% consumed → 20 filled blocks', () => {
|
|
43
|
+
const bar = renderBar(0);
|
|
44
|
+
const filledCount = (bar.match(/█/g) || []).length;
|
|
45
|
+
assert.strictEqual(filledCount, 20, `Expected 20 filled blocks for 0% remaining`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('75% remaining → 25% consumed → 5 filled blocks', () => {
|
|
49
|
+
const bar = renderBar(75);
|
|
50
|
+
const filledCount = (bar.match(/█/g) || []).length;
|
|
51
|
+
assert.strictEqual(filledCount, 5, `Expected 5 filled blocks for 75% remaining`);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('25% remaining → 75% consumed → 15 filled blocks', () => {
|
|
55
|
+
const bar = renderBar(25);
|
|
56
|
+
const filledCount = (bar.match(/█/g) || []).length;
|
|
57
|
+
assert.strictEqual(filledCount, 15, `Expected 15 filled blocks for 25% remaining`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('bar is always exactly 20 characters', () => {
|
|
61
|
+
for (const remaining of [0, 10, 25, 50, 75, 90, 100]) {
|
|
62
|
+
const bar = renderBar(remaining);
|
|
63
|
+
assert.strictEqual(bar.length, 20, `Bar for ${remaining}% remaining should be 20 chars, got ${bar.length}`);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('filled blocks increase as remaining decreases', () => {
|
|
68
|
+
const bars = [100, 91, 75, 50, 25, 10, 0].map(r => ({
|
|
69
|
+
remaining: r,
|
|
70
|
+
filled: (renderBar(r).match(/█/g) || []).length
|
|
71
|
+
}));
|
|
72
|
+
for (let i = 1; i < bars.length; i++) {
|
|
73
|
+
assert.ok(
|
|
74
|
+
bars[i].filled >= bars[i-1].filled,
|
|
75
|
+
`${bars[i].remaining}% remaining (${bars[i].filled} blocks) should have >= filled blocks than ${bars[i-1].remaining}% remaining (${bars[i-1].filled} blocks)`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
package/config/agents/frigg.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Frigg — Read-only codebase Q&A. Answers questions about the project with file references, never modifies anything. Routes to no one.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model:
|
|
4
|
+
model: minimax/MiniMax-M2.7
|
|
5
5
|
color: "#f472b6"
|
|
6
6
|
permission:
|
|
7
7
|
read: allow
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Heimdall — Simple, routine, and deterministic tasks using DeepSeek. Quick edits, mechanical work, file operations. The ever-watchful guardian.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model:
|
|
4
|
+
model: minimax/MiniMax-M2.7
|
|
5
5
|
color: "#10b981"
|
|
6
6
|
permission:
|
|
7
7
|
read: allow
|
package/config/agents/mimir.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Mimir — Deep codebase research and exploration. Uses Semble as primary search tool. Architecture analysis, pattern discovery, documentation research, and project initialization.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model:
|
|
4
|
+
model: minimax/MiniMax-M2.7
|
|
5
5
|
color: "#06b6d4"
|
|
6
6
|
permission:
|
|
7
7
|
read: allow
|
package/config/agents/quick.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Quick (quick) — fast single-shot tasks. No delegation, no parallel streams. Use for small edits, mechanical changes, one-shot questions. Routes to no one.
|
|
3
3
|
mode: primary
|
|
4
|
-
model: minimax/MiniMax-M2.7
|
|
4
|
+
model: minimax/MiniMax-M2.7-Flash
|
|
5
5
|
color: "#22d3ee"
|
|
6
6
|
permission:
|
|
7
7
|
read: allow
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Code search agent for exploring any codebase. Use for finding code by intent, locating implementations, understanding how something works, or discovering related code. Prefer over Bash/Read for any semantic or exploratory question.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model:
|
|
4
|
+
model: minimax/MiniMax-M2.7
|
|
5
5
|
color: "#64748b"
|
|
6
6
|
permission:
|
|
7
7
|
read: allow
|
package/config/agents/vor.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Vör — Asks clarifying questions for ambiguous or incomplete requests. Reads project context first, then asks one targeted, project-specific question.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model:
|
|
4
|
+
model: minimax/MiniMax-M2.7
|
|
5
5
|
color: "#a78bfa"
|
|
6
6
|
permission:
|
|
7
7
|
read: allow
|
package/config/opencode.json
CHANGED
|
@@ -37,7 +37,14 @@
|
|
|
37
37
|
"loopThresholdWarn": 5,
|
|
38
38
|
"loopThresholdEscalate": 8,
|
|
39
39
|
"loopThresholdBlock": 12,
|
|
40
|
-
"loopWindowSize": 10
|
|
40
|
+
"loopWindowSize": 10,
|
|
41
|
+
"compaction": {
|
|
42
|
+
"auto": true,
|
|
43
|
+
"threshold": 0.5,
|
|
44
|
+
"preserve_recent": 10,
|
|
45
|
+
"strategy": "summarize",
|
|
46
|
+
"model": "minimax/MiniMax-M3-Reasoning"
|
|
47
|
+
}
|
|
41
48
|
}]
|
|
42
49
|
],
|
|
43
50
|
"tools": {
|
|
@@ -66,9 +73,9 @@
|
|
|
66
73
|
}
|
|
67
74
|
},
|
|
68
75
|
"vor": {
|
|
69
|
-
"description": "Vör — Tier 2 clarifier. Reads project context first and asks targeted, project-specific questions when requests are ambiguous. Backed by
|
|
76
|
+
"description": "Vör — Tier 2 clarifier. Reads project context first and asks targeted, project-specific questions when requests are ambiguous. Backed by MiniMax M2.7. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
70
77
|
"mode": "subagent",
|
|
71
|
-
"model": "
|
|
78
|
+
"model": "minimax/MiniMax-M2.7",
|
|
72
79
|
"color": "#8b5cf6",
|
|
73
80
|
"permission": {
|
|
74
81
|
"read": "allow",
|
|
@@ -77,9 +84,9 @@
|
|
|
77
84
|
}
|
|
78
85
|
},
|
|
79
86
|
"frigg": {
|
|
80
|
-
"description": "Frigg — Tier 1 primary. Read-only Q&A agent that answers codebase questions with file:line citations, never edits. Backed by
|
|
87
|
+
"description": "Frigg — Tier 1 primary. Read-only Q&A agent that answers codebase questions with file:line citations, never edits. Backed by MiniMax M2.7. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
81
88
|
"mode": "primary",
|
|
82
|
-
"model": "
|
|
89
|
+
"model": "minimax/MiniMax-M2.7",
|
|
83
90
|
"color": "#06b6d4",
|
|
84
91
|
"permission": {
|
|
85
92
|
"read": "allow",
|
|
@@ -93,9 +100,9 @@
|
|
|
93
100
|
}
|
|
94
101
|
},
|
|
95
102
|
"quick": {
|
|
96
|
-
"description": "Quick — Tier 1 primary. Fast single-shot escape hatch for small edits, mechanical changes, and one-shot questions. No delegation, no parallel streams. Backed by MiniMax M2.7. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
103
|
+
"description": "Quick — Tier 1 primary. Fast single-shot escape hatch for small edits, mechanical changes, and one-shot questions. No delegation, no parallel streams. Backed by MiniMax M2.7 Flash. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
97
104
|
"mode": "primary",
|
|
98
|
-
"model": "minimax/MiniMax-M2.7",
|
|
105
|
+
"model": "minimax/MiniMax-M2.7-Flash",
|
|
99
106
|
"color": "#22d3ee",
|
|
100
107
|
"permission": {
|
|
101
108
|
"read": "allow",
|
|
@@ -112,9 +119,9 @@
|
|
|
112
119
|
}
|
|
113
120
|
},
|
|
114
121
|
"mimir": {
|
|
115
|
-
"description": "Mimir — Tier 2 research. Dedicated codebase exploration agent who uses Semble as primary search tool for deep analysis, pattern discovery, and documentation research. Backed by
|
|
122
|
+
"description": "Mimir — Tier 2 research. Dedicated codebase exploration agent who uses Semble as primary search tool for deep analysis, pattern discovery, and documentation research. Backed by MiniMax M2.7. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
116
123
|
"mode": "subagent",
|
|
117
|
-
"model": "
|
|
124
|
+
"model": "minimax/MiniMax-M2.7",
|
|
118
125
|
"color": "#0ea5e9",
|
|
119
126
|
"permission": {
|
|
120
127
|
"read": "allow",
|
|
@@ -130,9 +137,9 @@
|
|
|
130
137
|
}
|
|
131
138
|
},
|
|
132
139
|
"heimdall": {
|
|
133
|
-
"description": "Heimdall — Tier 2 mechanical. Handles simple, routine, deterministic engineering tasks with speed and precision using
|
|
140
|
+
"description": "Heimdall — Tier 2 mechanical. Handles simple, routine, deterministic engineering tasks with speed and precision using MiniMax M2.7. The ever-watchful guardian. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
134
141
|
"mode": "subagent",
|
|
135
|
-
"model": "
|
|
142
|
+
"model": "minimax/MiniMax-M2.7",
|
|
136
143
|
"color": "#10b981",
|
|
137
144
|
"permission": {
|
|
138
145
|
"read": "allow",
|
|
@@ -249,9 +256,9 @@
|
|
|
249
256
|
}
|
|
250
257
|
},
|
|
251
258
|
"semble-search": {
|
|
252
|
-
"description": "semble-search — Tier 2 research. Code search agent for exploring any codebase using Semble's semantic search. For finding code by intent, locating implementations, and understanding how things work. Backed by
|
|
259
|
+
"description": "semble-search — Tier 2 research. Code search agent for exploring any codebase using Semble's semantic search. For finding code by intent, locating implementations, and understanding how things work. Backed by MiniMax M2.7. Operates inside BizarHarness alongside 13 sibling agents.",
|
|
253
260
|
"mode": "subagent",
|
|
254
|
-
"model": "
|
|
261
|
+
"model": "minimax/MiniMax-M2.7",
|
|
255
262
|
"color": "#0ea5e9",
|
|
256
263
|
"permission": {
|
|
257
264
|
"bash": "allow",
|
|
@@ -312,15 +319,15 @@
|
|
|
312
319
|
},
|
|
313
320
|
"provider": {
|
|
314
321
|
"minimax": {
|
|
322
|
+
"options": {
|
|
323
|
+
"baseURL": "https://api.minimax.io/v1",
|
|
324
|
+
"apiKey": "{env:MiniMax_API_KEY}"
|
|
325
|
+
},
|
|
315
326
|
"models": {
|
|
316
|
-
"MiniMax-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
},
|
|
320
|
-
"MiniMax-M2.7": {
|
|
321
|
-
"interleaved": { "field": "reasoning_details" },
|
|
322
|
-
"reasoning": true
|
|
323
|
-
}
|
|
327
|
+
"MiniMax-M2.7-Flash": { "name": "MiniMax M2.7 Flash", "interleaved": { "field": "reasoning_details" }, "reasoning": true },
|
|
328
|
+
"MiniMax-M2.7": { "name": "MiniMax M2.7", "interleaved": { "field": "reasoning_details" }, "reasoning": true },
|
|
329
|
+
"MiniMax-M3": { "name": "MiniMax M3", "interleaved": { "field": "reasoning_details" }, "reasoning": true },
|
|
330
|
+
"MiniMax-M3-Reasoning": { "name": "MiniMax M3 Reasoning", "interleaved": { "field": "reasoning_details" }, "reasoning": true }
|
|
324
331
|
}
|
|
325
332
|
}
|
|
326
333
|
}
|
|
@@ -28,7 +28,14 @@
|
|
|
28
28
|
"loopThresholdWarn": 5,
|
|
29
29
|
"loopThresholdEscalate": 8,
|
|
30
30
|
"loopThresholdBlock": 12,
|
|
31
|
-
"loopWindowSize": 10
|
|
31
|
+
"loopWindowSize": 10,
|
|
32
|
+
"compaction": {
|
|
33
|
+
"auto": true,
|
|
34
|
+
"threshold": 0.5,
|
|
35
|
+
"preserve_recent": 10,
|
|
36
|
+
"strategy": "summarize",
|
|
37
|
+
"model": "minimax/MiniMax-M3-Reasoning"
|
|
38
|
+
}
|
|
32
39
|
}]
|
|
33
40
|
],
|
|
34
41
|
"tools": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"cors": "^2.8.6",
|
|
56
56
|
"croner": "^10.0.1",
|
|
57
57
|
"express": "^4.22.0",
|
|
58
|
+
"formidable": "^3.5.0",
|
|
58
59
|
"fuse.js": "^7.0.0",
|
|
59
60
|
"inquirer": "^12.0.0",
|
|
60
61
|
"lucide-react": "^0.460.0",
|
|
@@ -65,8 +66,15 @@
|
|
|
65
66
|
"react-dom": "^18.3.0",
|
|
66
67
|
"react-markdown": "^9.0.0",
|
|
67
68
|
"remark-gfm": "^4.0.0",
|
|
69
|
+
"tesseract.js": "^5.1.1",
|
|
68
70
|
"ws": "^8.18.0",
|
|
69
|
-
"zod": "4.1.8"
|
|
71
|
+
"zod": "4.1.8",
|
|
72
|
+
"@opentelemetry/api": "^1.9.1",
|
|
73
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.52.1",
|
|
74
|
+
"@opentelemetry/resources": "^1.30.1",
|
|
75
|
+
"@opentelemetry/sdk-node": "^0.52.1",
|
|
76
|
+
"@opentelemetry/sdk-trace-node": "^1.30.1",
|
|
77
|
+
"@opentelemetry/semantic-conventions": "^1.41.1"
|
|
70
78
|
},
|
|
71
79
|
"peerDependencies": {
|
|
72
80
|
"@opencode-ai/plugin": ">=1.17.0"
|
|
@@ -78,12 +86,6 @@
|
|
|
78
86
|
},
|
|
79
87
|
"devDependencies": {
|
|
80
88
|
"@opencode-ai/plugin": "^1.17.7",
|
|
81
|
-
"@opentelemetry/api": "^1.9.0",
|
|
82
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
|
83
|
-
"@opentelemetry/resources": "^1.25.0",
|
|
84
|
-
"@opentelemetry/sdk-node": "^0.52.0",
|
|
85
|
-
"@opentelemetry/sdk-trace-node": "^1.25.0",
|
|
86
|
-
"@opentelemetry/semantic-conventions": "^1.25.0",
|
|
87
89
|
"@testing-library/jest-dom": "^6.9.1",
|
|
88
90
|
"@testing-library/react": "^16.3.2",
|
|
89
91
|
"@testing-library/user-event": "^14.6.1",
|
|
@@ -93,7 +95,9 @@
|
|
|
93
95
|
"@types/react": "^18.3.0",
|
|
94
96
|
"@types/react-dom": "^18.3.0",
|
|
95
97
|
"@vitejs/plugin-react": "^4.3.0",
|
|
98
|
+
"js-yaml": "^5.2.1",
|
|
96
99
|
"jsdom": "^25.0.1",
|
|
100
|
+
"smol-toml": "^1.7.0",
|
|
97
101
|
"typescript": "^5.6.0",
|
|
98
102
|
"vite": "^5.0.0",
|
|
99
103
|
"vitest": "^4.1.9"
|
package/plugins/bizar/index.ts
CHANGED
|
@@ -143,6 +143,18 @@ import { executeSideEffect, type ExecuteOptions } from "./src/commands-impl.js";
|
|
|
143
143
|
import { join as pathJoin } from "node:path";
|
|
144
144
|
import { homedir } from "node:os";
|
|
145
145
|
|
|
146
|
+
// v0.6.3 — Compaction gate. Configures the compaction threshold from
|
|
147
|
+
// `rawOptions.compaction.threshold` (default 0.5) and wires two opencode
|
|
148
|
+
// hooks: `experimental.session.compacting` (push a Bizar-policy note onto
|
|
149
|
+
// the compaction prompt) and `experimental.compaction.autocontinue`
|
|
150
|
+
// (skip the synthetic "continue" turn when our policy decides to compact).
|
|
151
|
+
// The pure decision logic lives in `src/compaction.mjs`.
|
|
152
|
+
import {
|
|
153
|
+
getCompactionThreshold,
|
|
154
|
+
setCompactionThreshold,
|
|
155
|
+
resetCompactionDefaults,
|
|
156
|
+
} from "./src/compaction.mjs";
|
|
157
|
+
|
|
146
158
|
// --- Env-var constants (per spec §8) -------------------------------------
|
|
147
159
|
|
|
148
160
|
/** `BIZAR_SERVE_PORT` — default 0 (random). */
|
|
@@ -402,6 +414,29 @@ async function init(
|
|
|
402
414
|
const logger = createLogger(input.client as unknown as Parameters<typeof createLogger>[0]);
|
|
403
415
|
loggerHandle = logger;
|
|
404
416
|
|
|
417
|
+
// v0.6.3 — Compaction gate. Read the user-configured threshold from
|
|
418
|
+
// `rawOptions.compaction.threshold` (default 0.5). Invalid values are
|
|
419
|
+
// caught by `setCompactionThreshold` and logged; we fall back to the
|
|
420
|
+
// module default rather than crashing init (per spec §8.1: any init
|
|
421
|
+
// error must not break the plugin).
|
|
422
|
+
resetCompactionDefaults();
|
|
423
|
+
const compactionCfg = (rawOptions as Record<string, unknown> | undefined)
|
|
424
|
+
?.compaction as { threshold?: unknown } | undefined;
|
|
425
|
+
if (compactionCfg && typeof compactionCfg.threshold === "number") {
|
|
426
|
+
try {
|
|
427
|
+
setCompactionThreshold(compactionCfg.threshold);
|
|
428
|
+
logger.info(
|
|
429
|
+
`bizar: compaction threshold set to ${compactionCfg.threshold} (from plugin options)`,
|
|
430
|
+
);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
logger.warn(
|
|
433
|
+
`bizar: invalid compaction threshold ${String(compactionCfg.threshold)}; using default ${getCompactionThreshold()}: ${
|
|
434
|
+
err instanceof Error ? err.message : String(err)
|
|
435
|
+
}`,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
405
440
|
// §6.4 — refuse to start if logDir or stateDir is inside a secret dir.
|
|
406
441
|
const offending = findOffendingPath(options);
|
|
407
442
|
if (offending !== null) {
|
|
@@ -1412,6 +1447,48 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
1412
1447
|
// v0.4.2 — register the 4 background tools.
|
|
1413
1448
|
tool: tools,
|
|
1414
1449
|
|
|
1450
|
+
// v0.6.3 — Compaction gate. Called by opencode BEFORE it starts a
|
|
1451
|
+
// session compaction. We append a one-line Bizar policy note so the
|
|
1452
|
+
// model summarises with our threshold in mind (the default opencode
|
|
1453
|
+
// compaction prompt doesn't know about our 50% trigger). The threshold
|
|
1454
|
+
// itself was set during `init()` from `rawOptions.compaction.threshold`.
|
|
1455
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
1456
|
+
try {
|
|
1457
|
+
const threshold = getCompactionThreshold();
|
|
1458
|
+
output.context.push(
|
|
1459
|
+
`Bizar compaction policy: this compaction was triggered at the configured ${threshold} ratio of context usage. Preserve technical decisions, file paths, error messages, and unresolved questions verbatim in the summary; compress conversational filler.`,
|
|
1460
|
+
);
|
|
1461
|
+
} catch (err) {
|
|
1462
|
+
ctx.logger.warn(
|
|
1463
|
+
`bizar: experimental.session.compacting failed (passing through): ${
|
|
1464
|
+
err instanceof Error ? err.message : String(err)
|
|
1465
|
+
}`,
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
|
|
1470
|
+
// v0.6.3 — Compaction auto-continue gate. Called by opencode AFTER
|
|
1471
|
+
// compaction succeeds and BEFORE it injects a synthetic user "continue"
|
|
1472
|
+
// message. We disable auto-continue when compaction was actually
|
|
1473
|
+
// triggered (so the user sees the compacted state and can drive the
|
|
1474
|
+
// next step), and let auto-continue proceed otherwise. We can't read
|
|
1475
|
+
// the live usage ratio here, so we use the configured threshold as a
|
|
1476
|
+
// proxy: if it's been lowered below 1.0, the policy is "compact
|
|
1477
|
+
// early", and we want the user in the loop.
|
|
1478
|
+
"experimental.compaction.autocontinue": async (_input, output) => {
|
|
1479
|
+
try {
|
|
1480
|
+
if (getCompactionThreshold() < 1.0) {
|
|
1481
|
+
output.enabled = false;
|
|
1482
|
+
}
|
|
1483
|
+
} catch (err) {
|
|
1484
|
+
ctx.logger.warn(
|
|
1485
|
+
`bizar: experimental.compaction.autocontinue failed (passing through): ${
|
|
1486
|
+
err instanceof Error ? err.message : String(err)
|
|
1487
|
+
}`,
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
|
|
1415
1492
|
// v0.4.2 — dispose hook. Opencode calls this when the plugin is
|
|
1416
1493
|
// being torn down. We do a best-effort cleanup similar to the
|
|
1417
1494
|
// signal trap, but we do NOT call `process.exit` — that's the
|