@polderlabs/bizar 4.3.0 → 4.4.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.
Files changed (57) hide show
  1. package/bizar-dash/src/server/opencode-sdk.mjs +72 -0
  2. package/bizar-dash/src/server/routes/background.mjs +92 -0
  3. package/bizar-dash/src/server/routes/chat.mjs +300 -123
  4. package/bizar-dash/src/server/routes/opencode-session-detail.mjs +26 -0
  5. package/bizar-dash/src/server/routes/tasks.mjs +59 -1
  6. package/bizar-dash/src/server/task-delegator.mjs +154 -8
  7. package/bizar-dash/src/server/tasks-store.mjs +50 -2
  8. package/bizar-dash/src/web/components/background/AttachButton.tsx +96 -0
  9. package/bizar-dash/src/web/components/background/TmuxAttachCard.tsx +122 -0
  10. package/bizar-dash/src/web/components/chat/AgentNode.tsx +127 -0
  11. package/bizar-dash/src/web/components/chat/AgentTree.tsx +80 -0
  12. package/bizar-dash/src/web/components/chat/ChatComposer.tsx +76 -0
  13. package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +144 -0
  14. package/bizar-dash/src/web/components/chat/ChatRail.tsx +387 -0
  15. package/bizar-dash/src/web/components/chat/ChatThread.tsx +28 -7
  16. package/bizar-dash/src/web/components/chat/JumpToLatest.tsx +58 -0
  17. package/bizar-dash/src/web/components/chat/MessageBlock.tsx +260 -0
  18. package/bizar-dash/src/web/components/chat/SessionRowMenu.tsx +206 -0
  19. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +33 -5
  20. package/bizar-dash/src/web/components/chat/_legacy.ts +30 -0
  21. package/bizar-dash/src/web/components/chat/index.ts +11 -0
  22. package/bizar-dash/src/web/components/chat/useChat.ts +345 -167
  23. package/bizar-dash/src/web/components/tasks/BacklogPanel.css +109 -0
  24. package/bizar-dash/src/web/components/tasks/BacklogPanel.tsx +209 -0
  25. package/bizar-dash/src/web/styles/chat.css +1536 -133
  26. package/bizar-dash/src/web/views/BackgroundAgents.tsx +3 -0
  27. package/bizar-dash/src/web/views/Chat.tsx +147 -57
  28. package/bizar-dash/src/web/views/Tasks.tsx +23 -1
  29. package/cli/bg.mjs +94 -71
  30. package/cli/bin.mjs +36 -8
  31. package/cli/service-controller.mjs +587 -0
  32. package/cli/service-controller.test.mjs +92 -0
  33. package/cli/service.mjs +162 -14
  34. package/config/agents/baldr.md +2 -0
  35. package/config/agents/browser-harness.md +2 -0
  36. package/config/agents/forseti.md +2 -0
  37. package/config/agents/frigg.md +2 -0
  38. package/config/agents/heimdall.md +2 -0
  39. package/config/agents/hermod.md +2 -0
  40. package/config/agents/mimir.md +2 -0
  41. package/config/agents/odin.md +2 -0
  42. package/config/agents/quick.md +2 -0
  43. package/config/agents/semble-search.md +2 -0
  44. package/config/agents/thor.md +2 -0
  45. package/config/agents/tyr.md +2 -0
  46. package/config/agents/vidarr.md +2 -0
  47. package/config/agents/vor.md +2 -0
  48. package/config/opencode.json.template +1 -0
  49. package/install.sh +448 -787
  50. package/package.json +2 -2
  51. package/packages/sdk/package.json +20 -0
  52. package/packages/sdk/src/client.ts +5 -0
  53. package/packages/sdk/src/errors.ts +11 -2
  54. package/packages/sdk/src/index.ts +19 -0
  55. package/packages/sdk/src/opencode-events.ts +134 -0
  56. package/packages/sdk/src/opencode-types.ts +66 -0
  57. package/packages/sdk/src/opencode.ts +335 -0
package/cli/service.mjs CHANGED
@@ -2,16 +2,22 @@
2
2
  /**
3
3
  * cli/service.mjs
4
4
  *
5
- * v3.0.0 — Background service daemon for Bizar.
5
+ * v4.4.0 — Background service daemon for Bizar.
6
6
  *
7
7
  * Subcommands:
8
- * start — spawn the service detached, return immediately
9
- * stop — kill the service
10
- * status — show running state
11
- * logs — tail the service log
8
+ * start — spawn the service detached, return immediately
9
+ * stop — kill the service
10
+ * status — show running state
11
+ * logs — tail the service log
12
+ * follow / tail — tail the service log live
13
+ * install — register with systemd / launchd / scheduled task
14
+ * uninstall — unregister the OS-level autostart
15
+ * install --force — re-install even when the unit matches
16
+ * uninstall --force — re-uninstall even when not present
12
17
  *
13
18
  * The service:
14
19
  * - Watches per-project schedules
20
+ * - Ticks the task backlog (Stream B's task-delegator) when present
15
21
  * - Fires due schedules (interval / cron / once)
16
22
  * - Records every run in schedules.json
17
23
  * - Logs to ~/.config/bizar/service.log
@@ -143,18 +149,37 @@ function stopService() {
143
149
  }
144
150
 
145
151
  function serviceStatus() {
152
+ // v4.4.0 — surface both the old PID-file status (so a manually-started
153
+ // session is still observable) and the OS-level registration status
154
+ // (systemd / launchd / schtasks). Process stays responsive with the
155
+ // unconditional `await import`.
146
156
  const pid = readPid();
147
- if (!pid) {
148
- console.log('Bizar service: stopped (no PID file)');
149
- return;
150
- }
151
- if (!isAlive(pid)) {
152
- console.log(`Bizar service: stopped (stale PID file: ${pid})`);
157
+ const alive = pid && isAlive(pid);
158
+ if (pid && !alive) {
153
159
  removePidFile();
154
- return;
155
160
  }
156
- console.log(`Bizar service: running (pid ${pid})`);
157
- console.log(`Log: ${LOG_FILE}`);
161
+ (async () => {
162
+ let unitInfo = null;
163
+ try {
164
+ const { serviceStatus: ctrlStatus } = await import('./service-controller.mjs');
165
+ unitInfo = ctrlStatus();
166
+ } catch {
167
+ /* service-controller optional */
168
+ }
169
+ if (alive) {
170
+ console.log(`Bizar service: running (pid ${pid})`);
171
+ console.log(`Log: ${LOG_FILE}`);
172
+ } else {
173
+ console.log('Bizar service: stopped (no live PID)');
174
+ }
175
+ if (unitInfo) {
176
+ const unitBit = unitInfo.unitPath
177
+ ? `${unitInfo.installed ? 'installed' : 'partial'} (${unitInfo.unitPath})`
178
+ : 'not registered';
179
+ const runBit = unitInfo.running ? 'running' : 'not running';
180
+ console.log(`Bizar OS-level unit: ${unitBit}, ${runBit}`);
181
+ }
182
+ })();
158
183
  }
159
184
 
160
185
  function tailLogs(follow) {
@@ -205,6 +230,10 @@ async function daemonLoop() {
205
230
  const candidates = [
206
231
  // v4.0.0 primary: <repo>/bizar-dash/src/server/schedules-runner.mjs
207
232
  join(__dirname, '..', 'bizar-dash', 'src', 'server', 'schedules-runner.mjs'),
233
+ // v4.4.0 sibling: <repo>/bizar-dash/src/server/task-delegator.mjs
234
+ // Exported under the same runner module shape (default export) so the
235
+ // service can call .tickBacklog() alongside .schedulesRunner.tick().
236
+ join(__dirname, '..', 'bizar-dash', 'src', 'server', 'task-delegator.mjs'),
208
237
  // Legacy fallbacks — users with @polderlabs/bizar-dash still installed:
209
238
  join(HOME, '.npm-global', 'lib', 'node_modules', '@polderlabs', 'bizar-dash', 'src', 'server', 'schedules-runner.mjs'),
210
239
  ];
@@ -235,6 +264,37 @@ async function daemonLoop() {
235
264
  }
236
265
  }
237
266
  }
267
+ // v4.4.0 — Probe a sibling `task-delegator.mjs` separately so the backlog
268
+ // tick works regardless of which file won the race above. ESM namespace
269
+ // objects are non-extensible, so we cannot mutate `runner` to attach a
270
+ // `taskDelegator` property — instead, capture the imported module in a
271
+ // local binding and reference it from `tick()` below.
272
+ let taskDelegator = null;
273
+ if (runner && runner.taskDelegator && typeof runner.taskDelegator.tickBacklog === 'function') {
274
+ // The runner already exposes taskDelegator (e.g. task-delegator.mjs
275
+ // resolved as the runner). Use it.
276
+ taskDelegator = runner.taskDelegator;
277
+ } else {
278
+ const tdCandidates = [
279
+ join(__dirname, '..', 'bizar-dash', 'src', 'server', 'task-delegator.mjs'),
280
+ join(process.cwd(), 'node_modules', '@polderlabs', 'bizar-dash', 'src', 'server', 'task-delegator.mjs'),
281
+ join(__dirname, '..', 'node_modules', '@polderlabs', 'bizar-dash', 'src', 'server', 'task-delegator.mjs'),
282
+ ];
283
+ for (const c of tdCandidates) {
284
+ if (existsSync(c)) {
285
+ try {
286
+ const td = await import(c);
287
+ if (td && td.taskDelegator && typeof td.taskDelegator.tickBacklog === 'function') {
288
+ taskDelegator = td.taskDelegator;
289
+ logLine(`using task-delegator at ${c}`);
290
+ break;
291
+ }
292
+ } catch (err) {
293
+ logLine(`failed to import task-delegator at ${c}: ${err.message}`);
294
+ }
295
+ }
296
+ }
297
+ }
238
298
  if (!runner) {
239
299
  logLine('FATAL: could not locate @polderlabs/bizar-dash/schedules-runner. Service exiting.');
240
300
  removePidFile();
@@ -261,6 +321,22 @@ async function daemonLoop() {
261
321
  } catch (err) {
262
322
  logLine(`tick error: ${err.message}`);
263
323
  }
324
+ // v4.4.0 — Drive the task backlog. The local binding is a closure
325
+ // capture, not an attached property, because ESM namespace objects
326
+ // are non-extensible. The check is intentionally lazy — if Stream B's
327
+ // task-delegator hasn't landed yet, this is a no-op.
328
+ try {
329
+ if (taskDelegator && typeof taskDelegator.tickBacklog === 'function') {
330
+ const r = await taskDelegator.tickBacklog({ logLine });
331
+ if (r && (r.promoted || r.dispatched)) {
332
+ const promoted = r.promoted || 0;
333
+ const dispatched = r.dispatched || 0;
334
+ logLine(`backlog: ${promoted} promoted, ${dispatched} dispatched`);
335
+ }
336
+ }
337
+ } catch (err) {
338
+ logLine(`backlog tick error: ${err.message}`);
339
+ }
264
340
  };
265
341
 
266
342
  // Run immediately, then on a 5s interval
@@ -269,6 +345,13 @@ async function daemonLoop() {
269
345
  }
270
346
 
271
347
  export async function runService(sub, _rest) {
348
+ if (sub === '_daemon') {
349
+ // Internal entrypoint used by the systemd / launchd / schtasks unit.
350
+ // The OS-level unit invokes `node cli/bin.mjs service _daemon` so this
351
+ // branch makes the dispatch round-trip work.
352
+ await daemonLoop();
353
+ return;
354
+ }
272
355
  if (sub === 'start') {
273
356
  await startService();
274
357
  return;
@@ -289,6 +372,34 @@ export async function runService(sub, _rest) {
289
372
  tailLogs(true);
290
373
  return;
291
374
  }
375
+ if (sub === 'install') {
376
+ const { installService } = await import('./service-controller.mjs');
377
+ const force = Array.isArray(_rest) && (_rest.includes('--force') || _rest.includes('-f'));
378
+ const dryRun = Array.isArray(_rest) && (_rest.includes('--dry-run'));
379
+ const r = installService({ force, dryRun });
380
+ if (r.ok) {
381
+ console.log(`[bizar-service] installed${r.alreadyInstalled ? ' (already)' : ''}.`);
382
+ if (r.unitPath) console.log(`[bizar-service] unit: ${r.unitPath}`);
383
+ if (r.note) console.log(`[bizar-service] ${r.note}`);
384
+ } else {
385
+ console.error(`[bizar-service] install failed: ${r.error}`);
386
+ process.exitCode = 1;
387
+ }
388
+ return;
389
+ }
390
+ if (sub === 'uninstall') {
391
+ const { uninstallService } = await import('./service-controller.mjs');
392
+ const force = Array.isArray(_rest) && (_rest.includes('--force') || _rest.includes('-f'));
393
+ const r = uninstallService();
394
+ if (r.ok) {
395
+ console.log(`[bizar-service] uninstalled.`);
396
+ if (r.note) console.log(`[bizar-service] ${r.note}`);
397
+ } else {
398
+ console.error(`[bizar-service] uninstall failed: ${r.error}`);
399
+ process.exitCode = 1;
400
+ }
401
+ return;
402
+ }
292
403
  console.log(`
293
404
  bizar service — Manage the background service daemon
294
405
 
@@ -298,6 +409,15 @@ export async function runService(sub, _rest) {
298
409
  bizar service status
299
410
  bizar service logs
300
411
  bizar service follow
412
+ bizar service install [--force] [--dry-run]
413
+ bizar service uninstall [--force]
414
+
415
+ Description:
416
+ install / uninstall register the daemon with systemd (Linux),
417
+ launchd (macOS), or a scheduled task (Windows) so it autostarts at
418
+ user login. install is idempotent — when the on-disk unit matches
419
+ the desired content, it returns without restarting the service.
420
+ Pass --force to overwrite or to drop a stale registration.
301
421
  `);
302
422
  }
303
423
 
@@ -316,6 +436,32 @@ if (isMain) {
316
436
  tailLogs(false);
317
437
  } else if (sub === 'start') {
318
438
  await startService();
439
+ } else if (sub === 'install') {
440
+ const { installService } = await import('./service-controller.mjs');
441
+ const flags = process.argv.slice(3);
442
+ const force = flags.includes('--force') || flags.includes('-f');
443
+ const dryRun = flags.includes('--dry-run');
444
+ const r = installService({ force, dryRun });
445
+ if (r.ok) {
446
+ console.log(`[bizar-service] installed${r.alreadyInstalled ? ' (already)' : ''}.`);
447
+ if (r.unitPath) console.log(`[bizar-service] unit: ${r.unitPath}`);
448
+ if (r.note) console.log(`[bizar-service] ${r.note}`);
449
+ } else {
450
+ console.error(`[bizar-service] install failed: ${r.error}`);
451
+ process.exit(1);
452
+ }
453
+ } else if (sub === 'uninstall') {
454
+ const { uninstallService } = await import('./service-controller.mjs');
455
+ const flags = process.argv.slice(3);
456
+ const force = flags.includes('--force') || flags.includes('-f');
457
+ const r = uninstallService();
458
+ if (r.ok) {
459
+ console.log(`[bizar-service] uninstalled.`);
460
+ if (r.note) console.log(`[bizar-service] ${r.note}`);
461
+ } else {
462
+ console.error(`[bizar-service] uninstall failed: ${r.error}`);
463
+ process.exit(1);
464
+ }
319
465
  } else {
320
466
  console.log(`
321
467
  bizar service — Manage the background service daemon
@@ -326,6 +472,8 @@ if (isMain) {
326
472
  node cli/service.mjs status
327
473
  node cli/service.mjs logs
328
474
  node cli/service.mjs follow
475
+ node cli/service.mjs install [--force] [--dry-run]
476
+ node cli/service.mjs uninstall [--force]
329
477
  `);
330
478
  }
331
479
  }
@@ -56,3 +56,5 @@ You are Baldr — the beautiful. You create design plans. You do NOT implement c
56
56
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
57
57
 
58
58
  Your unique rule: you plan, Thor and Tyr implement. If asked to write code, refuse and tell the user to route the implementation to @odin.
59
+
60
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -68,3 +68,5 @@ You are browser-harness — the silent observer. You drive a real browser via CD
68
68
  The baseline's `.bizar/` maintenance duty (§10) does **not** apply to you.
69
69
 
70
70
  If a code change is needed, refuse and tell the user to dispatch @odin for the implementation.
71
+
72
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -56,3 +56,5 @@ Be specific in your corrections: name the file, the line range, the issue, and t
56
56
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
57
57
 
58
58
  Your role-specific override: you never write or edit. You only review. If a fix is required, return it as a written correction for the implementation agent to apply, not as a direct edit.
59
+
60
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -48,3 +48,5 @@ Lead with the direct answer. Use file:line references (`cli/bin.mjs:42`) for eve
48
48
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
49
49
 
50
50
  The baseline's identity / tone / formatting / search / citation rules apply. The baseline's `.bizar/` maintenance duty (§10) does **not** apply to you — that is Heimdall's job.
51
+
52
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -40,3 +40,5 @@ Odin sends you tasks that are:
40
40
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, the full general agent baseline, and your `.bizar/` maintenance duty.
41
41
 
42
42
  Do not duplicate the baseline rules in this file. If a rule changes, update the shared file once and every agent picks it up.
43
+
44
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -60,3 +60,5 @@ When Odin asks for `@hermod /pr-review` or a PR review:
60
60
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
61
61
 
62
62
  Your unique rule: you are the only git writer. All other agents are forbidden from `git commit` / `push` / `merge` / `rebase` / `reset` / `clean` / `stash` / branch-switching `checkout` / `pull --rebase`.
63
+
64
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -56,3 +56,5 @@ You are Mimir — the wise. You are the dedicated research and exploration engin
56
56
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
57
57
 
58
58
  You are the source of truth for `.obsidian/INDEX.md` and `.obsidian/projects/` notes. Other agents read what you write.
59
+
60
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -306,3 +306,5 @@ You are the All-Father. Concise by default, but you are permitted dry humor, a w
306
306
  - You do not flatter. You do not apologize for doing your job.
307
307
  - Match the user's register: terse when they're terse, thorough when they want depth.
308
308
  - When delegating, be specific about what you want. Other agents follow your instructions literally.
309
+
310
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -43,3 +43,5 @@ You do **not** have `task` permission. If work needs a subagent, refuse and tell
43
43
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
44
44
 
45
45
  Keep replies short. The user picked you for speed, not depth.
46
+
47
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -55,3 +55,5 @@ You are the code search specialist. You explore codebases semantically using Sem
55
55
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
56
56
 
57
57
  The baseline's `.bizar/` maintenance duty (§10) does **not** apply to you.
58
+
59
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -60,3 +60,5 @@ When Odin tells you to run the test gate after parallel implementation work:
60
60
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
61
61
 
62
62
  You are forbidden from `git commit` / `push` / `merge` / `rebase` / `reset` / `clean` / `stash` / branch-switching `checkout` / `pull --rebase` — that is @hermod's job.
63
+
64
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -63,3 +63,5 @@ Once the plan is approved, implement and verify. For parallel work, expect to be
63
63
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
64
64
 
65
65
  You are forbidden from `git commit` / `push` / `merge` / `rebase` / `reset` / `clean` / `stash` / branch-switching `checkout` / `pull --rebase` — that is @hermod's job.
66
+
67
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -61,3 +61,5 @@ When asked "why did the lower-tier attempts fail?", you:
61
61
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
62
62
 
63
63
  You are forbidden from `git commit` / `push` / `merge` / `rebase` / `reset` / `clean` / `stash` / branch-switching `checkout` / `pull --rebase` — that is @hermod's job.
64
+
65
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -58,3 +58,5 @@ One short preamble (1-2 sentences) explaining what you found in the codebase tha
58
58
  **Follow `config/agents/_shared/AGENT_BASELINE.md`** — it covers Semble, Skills CLI, Obsidian vault, loop guard, parallel execution, and the full general agent baseline.
59
59
 
60
60
  The baseline's `.bizar/` maintenance duty (§10) does **not** apply to you.
61
+
62
+ Read `.opencode/instructions/bizar-tools.md` before using any Bizar tool.
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "$schema": "https://opencode.ai/config.json",
3
+ "instructions": [".opencode/instructions/bizar-tools.md"],
3
4
  "model": "minimax/MiniMax-M3",
4
5
  "small_model": "minimax/MiniMax-M2.7",
5
6
  "default_agent": "odin",