@polderlabs/bizar 5.4.0 → 5.5.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/bizar-dash/dist/assets/{EnvVarsSection-DqvB8ZcJ.js → EnvVarsSection-L413CQEI.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-DqvB8ZcJ.js.map → EnvVarsSection-L413CQEI.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-DRXP2ERQ.js → MobileChat-0kGQb1S6.js} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-DRXP2ERQ.js.map → MobileChat-0kGQb1S6.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js +1 -0
- package/bizar-dash/dist/assets/{MobileSettings-CVW9VhEo.js.map → MobileSettings-Cq1oFIdO.js.map} +1 -1
- package/bizar-dash/dist/assets/{Toast-CcZVVN4i.js → Toast-C6f1zFJ2.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-CcZVVN4i.js.map → Toast-C6f1zFJ2.js.map} +1 -1
- package/bizar-dash/dist/assets/{icons-OMEGx5KA.js → icons-BsWXWkEF.js} +121 -126
- package/bizar-dash/dist/assets/icons-BsWXWkEF.js.map +1 -0
- package/bizar-dash/dist/assets/main-BklYy02e.css +1 -0
- package/bizar-dash/dist/assets/main-CBTgYSeG.js +18 -0
- package/bizar-dash/dist/assets/main-CBTgYSeG.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-CsaN4CxH.js +1 -0
- package/bizar-dash/dist/assets/{mobile-XN2P7YfM.js.map → mobile-CsaN4CxH.js.map} +1 -1
- package/bizar-dash/dist/assets/{mobile-layout-D6V-7WnA.css → mobile-layout-CJnZNLy3.css} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-iFBdjEE4.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-iFBdjEE4.js.map +1 -0
- package/bizar-dash/dist/assets/{useSlashCommands-iyoW95UB.js → useSlashCommands-DGJcC9iQ.js} +2 -2
- package/bizar-dash/dist/assets/{useSlashCommands-iyoW95UB.js.map → useSlashCommands-DGJcC9iQ.js.map} +1 -1
- package/bizar-dash/dist/index.html +8 -8
- package/bizar-dash/dist/mobile.html +4 -4
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/backup-store.mjs +6 -1
- package/bizar-dash/src/server/bg-spawner.mjs +574 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +242 -0
- package/bizar-dash/src/server/memory-store.mjs +43 -3
- package/bizar-dash/src/server/routes/background.mjs +143 -0
- package/bizar-dash/src/server/routes/lightrag.mjs +10 -0
- package/bizar-dash/src/server/routes/memory.mjs +8 -0
- package/bizar-dash/src/server/server.mjs +22 -0
- package/bizar-dash/src/web/App.tsx +3 -0
- package/bizar-dash/src/web/MobileApp.tsx +1 -10
- package/bizar-dash/src/web/components/InstallConfirmDialog.tsx +1 -1
- package/bizar-dash/src/web/components/SettingsNav.tsx +1 -10
- package/bizar-dash/src/web/lib/types.ts +39 -1
- package/bizar-dash/src/web/styles/main.css +43 -0
- package/bizar-dash/src/web/styles/mobile-layout.css +7 -5
- package/bizar-dash/src/web/styles/settings.css +46 -0
- package/bizar-dash/src/web/views/BackgroundAgents.tsx +399 -30
- package/bizar-dash/src/web/views/Settings.tsx +12 -12
- package/bizar-dash/src/web/views/SpawnAgentModal.tsx +221 -0
- package/bizar-dash/tests/BackgroundAgents.test.tsx +160 -0
- package/bizar-dash/tests/background-pause-resume.test.mjs +46 -0
- package/bizar-dash/tests/background-spawn-from-ui.test.mjs +36 -0
- package/bizar-dash/tests/background-steer.test.mjs +42 -0
- package/bizar-dash/tests/lightrag-startup-hook.test.mjs +206 -0
- package/bizar-dash/tests/memory-auto-reindex.test.mjs +196 -0
- package/bizar-dash/tests/memory-cli.test.mjs +1 -1
- package/bizar-dash/tests/memory-default-vault.test.mjs +90 -3
- package/bizar-dash/tests/memory-lightrag-extended.test.mjs +131 -0
- package/bizar-dash/tests/mobile-layout.test.tsx +36 -0
- package/bizar-dash/tests/settings-layout.test.tsx +3 -3
- package/bizar-dash/tests/settings-nav.test.tsx +7 -5
- package/cli/bin.mjs +16 -0
- package/cli/commands/deploy/docker.mjs +2 -1
- package/cli/commands/lightrag.mjs +171 -0
- package/cli/commands/service.mjs +7 -0
- package/cli/commands/util.mjs +8 -1
- package/cli/init.mjs +1 -1
- package/cli/post-install-smoke.mjs +230 -0
- package/cli/provision.mjs +177 -0
- package/cli/provision.test.mjs +180 -0
- package/cli/service-controller.mjs +378 -28
- package/cli/service-controller.test.mjs +100 -0
- package/cli/service-env.mjs +139 -0
- package/cli/service.mjs +23 -0
- package/config/agents/_shared/AGENT_BASELINE.md +6 -6
- package/install.sh +87 -6
- package/package.json +1 -1
- package/plugins/bizar/index.ts +147 -0
- package/plugins/bizar/src/background-state.ts +84 -5
- package/plugins/bizar/src/background.ts +388 -10
- package/plugins/bizar/src/hooks/memory-inject.ts +247 -0
- package/plugins/bizar/src/hooks/memory-write-on-end.ts +188 -0
- package/plugins/bizar/src/opencode-runner.ts +87 -1
- package/plugins/bizar/src/tools/bg-pause.ts +77 -0
- package/plugins/bizar/src/tools/bg-report-progress.ts +101 -0
- package/plugins/bizar/src/tools/bg-resume.ts +72 -0
- package/plugins/bizar/src/tools/bg-send-message.ts +105 -0
- package/plugins/bizar/src/tools/bg-spawn.ts +13 -6
- package/plugins/bizar/src/tools/bg-status.ts +10 -0
- package/plugins/bizar/src/tools/memory-list.ts +135 -0
- package/plugins/bizar/src/tools/memory-read.ts +142 -0
- package/plugins/bizar/src/tools/memory-search.ts +228 -0
- package/plugins/bizar/src/tools/memory-write.ts +183 -0
- package/plugins/bizar/tests/memory-write-on-end.test.ts +92 -0
- package/plugins/bizar/tests/tools/bg-pause.test.ts +64 -0
- package/plugins/bizar/tests/tools/bg-report-progress.test.ts +82 -0
- package/plugins/bizar/tests/tools/bg-resume.test.ts +43 -0
- package/plugins/bizar/tests/tools/bg-send-message.test.ts +46 -0
- package/bizar-dash/dist/assets/MobileSettings-CVW9VhEo.js +0 -1
- package/bizar-dash/dist/assets/icons-OMEGx5KA.js.map +0 -1
- package/bizar-dash/dist/assets/main-CMAGZouj.js +0 -16
- package/bizar-dash/dist/assets/main-CMAGZouj.js.map +0 -1
- package/bizar-dash/dist/assets/main-O2tZVdHm.css +0 -1
- package/bizar-dash/dist/assets/mobile-XN2P7YfM.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-DqTxvXZp.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-DqTxvXZp.js.map +0 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/service-env.mjs
|
|
3
|
+
*
|
|
4
|
+
* v5.x — Central env file builder for the Bizar service.
|
|
5
|
+
*
|
|
6
|
+
* Produces the content for `~/.config/bizar/service.env` (mode 0600),
|
|
7
|
+
* which is sourced by the systemd/launchd service and the headroom companion.
|
|
8
|
+
*
|
|
9
|
+
* All BIZAR_* vars are written here so the service has a consistent view
|
|
10
|
+
* of the environment. Values already present in `process.env` are honoured
|
|
11
|
+
* (never overwritten); everything else gets a safe default.
|
|
12
|
+
*
|
|
13
|
+
* Public API:
|
|
14
|
+
* buildServiceEnvFile({ bizarHome, repoPath, options })
|
|
15
|
+
* → string (the env file content, ready to write)
|
|
16
|
+
* defaultBizarEnv()
|
|
17
|
+
* → Record<string, string> (all defaults merged)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { execSync } from 'node:child_process';
|
|
21
|
+
import { existsSync } from 'node:fs';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
|
|
25
|
+
/** Default values for every BIZAR_* env var. */
|
|
26
|
+
export function defaultBizarEnv() {
|
|
27
|
+
const home = homedir();
|
|
28
|
+
return {
|
|
29
|
+
BIZAR_HOME: join(home, '.config', 'bizar'),
|
|
30
|
+
BIZAR_REPO: '',
|
|
31
|
+
BIZAR_DASHBOARD_PORT: '4097',
|
|
32
|
+
BIZAR_DASHBOARD_HOST: '127.0.0.1',
|
|
33
|
+
BIZAR_LOG_LEVEL: 'info',
|
|
34
|
+
BIZAR_HEADROOM_AUTOSTART: '1',
|
|
35
|
+
BIZAR_LIGHTRAG_AUTOSTART: '1',
|
|
36
|
+
BIZAR_MEMORY_VAULT: join(home, '.bizar_memory'),
|
|
37
|
+
OPENCODE_SERVER_PASSWORD: '',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generate a fresh `OPENCODE_SERVER_PASSWORD` using the platform's
|
|
43
|
+
* secure random source.
|
|
44
|
+
*/
|
|
45
|
+
function generatePassword() {
|
|
46
|
+
try {
|
|
47
|
+
if (process.platform === 'win32') {
|
|
48
|
+
// PowerShell
|
|
49
|
+
const out = execSync(
|
|
50
|
+
'powershell -Command "[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))"',
|
|
51
|
+
{ encoding: 'utf8', timeout: 5000 },
|
|
52
|
+
);
|
|
53
|
+
return out.trim().replace(/\n/g, '');
|
|
54
|
+
}
|
|
55
|
+
// Linux/macOS
|
|
56
|
+
const out = execSync('openssl rand -hex 32', { encoding: 'utf8', timeout: 5000 });
|
|
57
|
+
return out.trim();
|
|
58
|
+
} catch {
|
|
59
|
+
// Fallback: rough approximation using /dev/urandom
|
|
60
|
+
try {
|
|
61
|
+
const { readFileSync } = require?.('node:fs') ?? (() => { throw new Error('no fs'); })();
|
|
62
|
+
const fd = require?.('node:fs')?.openSync?.('/dev/urandom', 'r');
|
|
63
|
+
if (fd !== undefined) {
|
|
64
|
+
const buf = Buffer.alloc(32);
|
|
65
|
+
require('node:fs').readSync(fd, buf, 0, 32);
|
|
66
|
+
require('node:fs').closeSync?.(fd);
|
|
67
|
+
return buf.toString('hex');
|
|
68
|
+
}
|
|
69
|
+
} catch { /* ignore */ }
|
|
70
|
+
// Last-resort: use Date.now() + PID — not great but better than nothing
|
|
71
|
+
return `${Date.now()}-${process.pid}`.split('').reverse().join('').slice(0, 64);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build the service.env file content.
|
|
77
|
+
*
|
|
78
|
+
* @param {object} opts
|
|
79
|
+
* @param {string} [opts.bizarHome] – default: ~/.config/bizar
|
|
80
|
+
* @param {string} [opts.repoPath] – absolute path to the repo root
|
|
81
|
+
* @param {boolean} [opts.headroomAutoStart] – default: true
|
|
82
|
+
* @param {boolean} [opts.lightragAutoStart] – default: true
|
|
83
|
+
* @returns {string} env file content (does NOT write to disk)
|
|
84
|
+
*/
|
|
85
|
+
export function buildServiceEnvFile({ bizarHome, repoPath, headroomAutoStart = true, lightragAutoStart = true } = {}) {
|
|
86
|
+
const home = homedir();
|
|
87
|
+
const defaults = defaultBizarEnv();
|
|
88
|
+
|
|
89
|
+
const env = {
|
|
90
|
+
BIZAR_HOME: bizarHome || defaults.BIZAR_HOME,
|
|
91
|
+
BIZAR_REPO: repoPath || process.env.BIZAR_REPO || '',
|
|
92
|
+
BIZAR_DASHBOARD_PORT: process.env.BIZAR_DASHBOARD_PORT || defaults.BIZAR_DASHBOARD_PORT,
|
|
93
|
+
BIZAR_DASHBOARD_HOST: process.env.BIZAR_DASHBOARD_HOST || defaults.BIZAR_DASHBOARD_HOST,
|
|
94
|
+
BIZAR_LOG_LEVEL: process.env.BIZAR_LOG_LEVEL || defaults.BIZAR_LOG_LEVEL,
|
|
95
|
+
BIZAR_HEADROOM_AUTOSTART: String(
|
|
96
|
+
process.env.BIZAR_HEADROOM_AUTOSTART !== undefined
|
|
97
|
+
? (process.env.BIZAR_HEADROOM_AUTOSTART === '1' || process.env.BIZAR_HEADROOM_AUTOSTART === 'true' ? '1' : '0')
|
|
98
|
+
: (headroomAutoStart ? '1' : '0')
|
|
99
|
+
),
|
|
100
|
+
BIZAR_LIGHTRAG_AUTOSTART: String(
|
|
101
|
+
process.env.BIZAR_LIGHTRAG_AUTOSTART !== undefined
|
|
102
|
+
? (process.env.BIZAR_LIGHTRAG_AUTOSTART === '1' || process.env.BIZAR_LIGHTRAG_AUTOSTART === 'true' ? '1' : '0')
|
|
103
|
+
: (lightragAutoStart ? '1' : '0')
|
|
104
|
+
),
|
|
105
|
+
BIZAR_MEMORY_VAULT: process.env.BIZAR_MEMORY_VAULT || defaults.BIZAR_MEMORY_VAULT,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Always add ~/.local/bin to PATH for uv tool binaries (lightrag-server, etc.)
|
|
109
|
+
const localBin = join(home, '.local', 'bin');
|
|
110
|
+
const existingPath = process.env.PATH || '/usr/local/bin:/usr/bin:/bin';
|
|
111
|
+
env.PATH = existingPath.includes(localBin) ? existingPath : `${localBin}:${existingPath}`;
|
|
112
|
+
|
|
113
|
+
// OPENCODE_SERVER_PASSWORD: honour existing env, otherwise generate fresh
|
|
114
|
+
const password = process.env.OPENCODE_SERVER_PASSWORD
|
|
115
|
+
|| process.env.BIZAR_REDACT_PASSWORD === '1' ? ''
|
|
116
|
+
: (process.env.OPENCODE_SERVER_PASSWORD || generatePassword());
|
|
117
|
+
if (password) {
|
|
118
|
+
env.OPENCODE_SERVER_PASSWORD = password;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const lines = [
|
|
122
|
+
'# Generated by bizar service install — edit with care.',
|
|
123
|
+
`# Created: ${new Date().toISOString()}`,
|
|
124
|
+
`BIZAR_HOME=${env.BIZAR_HOME}`,
|
|
125
|
+
`BIZAR_REPO=${env.BIZAR_REPO}`,
|
|
126
|
+
`PATH=${env.PATH}`,
|
|
127
|
+
`BIZAR_DASHBOARD_PORT=${env.BIZAR_DASHBOARD_PORT}`,
|
|
128
|
+
`BIZAR_DASHBOARD_HOST=${env.BIZAR_DASHBOARD_HOST}`,
|
|
129
|
+
`BIZAR_LOG_LEVEL=${env.BIZAR_LOG_LEVEL}`,
|
|
130
|
+
`BIZAR_HEADROOM_AUTOSTART=${env.BIZAR_HEADROOM_AUTOSTART}`,
|
|
131
|
+
`BIZAR_LIGHTRAG_AUTOSTART=${env.BIZAR_LIGHTRAG_AUTOSTART}`,
|
|
132
|
+
`BIZAR_MEMORY_VAULT=${env.BIZAR_MEMORY_VAULT}`,
|
|
133
|
+
];
|
|
134
|
+
if (password) {
|
|
135
|
+
lines.push(`OPENCODE_SERVER_PASSWORD=${password}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return lines.join('\n') + '\n';
|
|
139
|
+
}
|
package/cli/service.mjs
CHANGED
|
@@ -377,6 +377,24 @@ export async function runService(sub, _rest) {
|
|
|
377
377
|
}
|
|
378
378
|
return;
|
|
379
379
|
}
|
|
380
|
+
if (sub === 'restart') {
|
|
381
|
+
// v5.x — issue #7. Used by `bizar update` to make sure the service
|
|
382
|
+
// picks up the freshly-installed binary without leaving the old one
|
|
383
|
+
// running with stale inodes.
|
|
384
|
+
const { restartService } = await import('./service-controller.mjs');
|
|
385
|
+
const force = Array.isArray(_rest) && (_rest.includes('--force') || _rest.includes('-f'));
|
|
386
|
+
const dryRun = Array.isArray(_rest) && (_rest.includes('--dry-run'));
|
|
387
|
+
const r = restartService({ force, dryRun });
|
|
388
|
+
if (r.ok) {
|
|
389
|
+
console.log('[bizar-service] restarted.');
|
|
390
|
+
if (r.unitPath) console.log(`[bizar-service] unit: ${r.unitPath}`);
|
|
391
|
+
if (r.note) console.log(`[bizar-service] ${r.note}`);
|
|
392
|
+
} else {
|
|
393
|
+
console.error(`[bizar-service] restart failed: ${r.error}`);
|
|
394
|
+
process.exitCode = 1;
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
380
398
|
if (sub === 'uninstall') {
|
|
381
399
|
const { uninstallService } = await import('./service-controller.mjs');
|
|
382
400
|
const force = Array.isArray(_rest) && (_rest.includes('--force') || _rest.includes('-f'));
|
|
@@ -400,6 +418,7 @@ export async function runService(sub, _rest) {
|
|
|
400
418
|
bizar service logs
|
|
401
419
|
bizar service follow
|
|
402
420
|
bizar service install [--force] [--dry-run]
|
|
421
|
+
bizar service restart [--force] [--dry-run]
|
|
403
422
|
bizar service uninstall [--force]
|
|
404
423
|
|
|
405
424
|
Description:
|
|
@@ -408,6 +427,10 @@ export async function runService(sub, _rest) {
|
|
|
408
427
|
user login. install is idempotent — when the on-disk unit matches
|
|
409
428
|
the desired content, it returns without restarting the service.
|
|
410
429
|
Pass --force to overwrite or to drop a stale registration.
|
|
430
|
+
|
|
431
|
+
restart stops the daemon, reinstalls the unit, and starts it again.
|
|
432
|
+
Used by \`bizar update\` to make sure the service picks up the new
|
|
433
|
+
code without leaving the old binary running.
|
|
411
434
|
`);
|
|
412
435
|
}
|
|
413
436
|
|
|
@@ -223,10 +223,10 @@ Bizar stores long-term memory in a **project memory vault** — Markdown files m
|
|
|
223
223
|
|
|
224
224
|
### Session Start
|
|
225
225
|
|
|
226
|
-
1. Run `
|
|
227
|
-
2. Read project-level index entries: `
|
|
228
|
-
3. Read the most recent session summaries: `
|
|
229
|
-
4. If a relevant decision note exists, read it by path with `
|
|
226
|
+
1. Run `bizar_memory_search({ query: "<topic>" })` to find relevant prior context (the plugin's session-start hook also auto-injects relevant memory at session creation).
|
|
227
|
+
2. Read project-level index entries: `bizar_memory_search({ query: "project_index" })` or browse notes with `bizar_memory_list({})`.
|
|
228
|
+
3. Read the most recent session summaries: `bizar_memory_search({ query: "session_summary" })`.
|
|
229
|
+
4. If a relevant decision note exists, read it by path with `bizar_memory_read({ path: "projects/<projectId>/decisions/<name>.md" })`.
|
|
230
230
|
|
|
231
231
|
### During Work
|
|
232
232
|
|
|
@@ -388,7 +388,7 @@ The following rules apply to every agent at all times. They are the single sourc
|
|
|
388
388
|
- Bizar does not have a single knowledge cutoff shared by all models. Subagents may run on DeepSeek V4 Flash (opencode-zen, free tier) or MiniMax M2.7 / M3, each with their own training window.
|
|
389
389
|
- For facts that change quickly (current positions, prices, breaking news) or anything that could have changed recently, **search before answering**: use `websearch` and `webfetch` or delegate to `@mimir` for deep research.
|
|
390
390
|
- For stable technical knowledge (language semantics, well-established APIs, mathematical truths), answer directly without search.
|
|
391
|
-
- Default to running `
|
|
391
|
+
- Default to running `bizar_memory_search({ query: "<topic>" })` at session start to retrieve prior project context before answering anything project-specific. (The plugin also auto-injects relevant memory via the session-start hook — agents don't need to manually call this if the hook is active.)
|
|
392
392
|
- When formulating date-sensitive queries, use the actual current date (Bizar's opencode environment provides this). Do not hardcode years.
|
|
393
393
|
- Do not over-rely on memory; if uncertain, search. Confabulating costs the user more than searching.
|
|
394
394
|
|
|
@@ -629,7 +629,7 @@ Rules:
|
|
|
629
629
|
The memory vault uses three namespaces: `projects/<projectId>/` (project-specific), `global/bizar/` (cross-project), and `users/<userId>/` (personal). Run `bizar memory status` from the project root to see the active mode and resolved path. The user has been working on this project — their notes contain the real context, the gotchas, the failed approaches, the preferred patterns. **Read the relevant vault entries before making any non-trivial decision.**
|
|
630
630
|
|
|
631
631
|
**When to read:**
|
|
632
|
-
- At the start of every session:
|
|
632
|
+
- At the start of every session: the plugin's session-start hook auto-injects relevant memory context. You can also call `bizar_memory_search({ query: "<topic>" })` explicitly.
|
|
633
633
|
- Before any non-trivial implementation decision: check for ADRs or design notes.
|
|
634
634
|
- When you're about to suggest something the user has already tried.
|
|
635
635
|
- When the codebase feels like it's working around something you don't understand.
|
package/install.sh
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# install.sh — Cross-platform BizarHarness installer (
|
|
3
|
+
# install.sh — Cross-platform BizarHarness installer (v5.x — thin shell wrapper).
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
5
|
+
# v5.x — Updated for issue #7. The install/update flow now:
|
|
6
|
+
# 1. On first install: registers the system service that auto-starts
|
|
7
|
+
# the dashboard (`bizar service install`).
|
|
8
|
+
# 2. On update: stops the running service before files are replaced
|
|
9
|
+
# (`bizar service stop` → npm upgrade → `bizar service restart`).
|
|
10
|
+
#
|
|
11
|
+
# Almost all logic still lives in `cli/provision.mjs:runProvision`, which
|
|
12
|
+
# is shared between `bizar install` and `bizar update`. This bash script
|
|
13
|
+
# exists only for the platform-specific steps that need sudo + a system
|
|
14
|
+
# package manager:
|
|
9
15
|
#
|
|
10
16
|
# - Linux: ensure node is installed (apt/dnf/pacman/zypper), install uv,
|
|
11
17
|
# python3.12, jq, gh, browser-harness, Chrome runtime libs.
|
|
@@ -20,7 +26,7 @@
|
|
|
20
26
|
# --non-interactive skip prompts (CI safe)
|
|
21
27
|
# --dry-run print actions, make no changes
|
|
22
28
|
# --force overwrite existing files
|
|
23
|
-
# --update this is a re-install
|
|
29
|
+
# --update this is a re-install
|
|
24
30
|
# --mode=install|update|install-only-system
|
|
25
31
|
# install = full flow (the default)
|
|
26
32
|
# update = alias for install (provisioner auto-detects)
|
|
@@ -145,6 +151,21 @@ ensure_node() {
|
|
|
145
151
|
action "Installing Node.js via zypper..."
|
|
146
152
|
dry $SUDO zypper install -y nodejs20 npm20
|
|
147
153
|
;;
|
|
154
|
+
alpine)
|
|
155
|
+
action "Installing Node.js via apk..."
|
|
156
|
+
dry $SUDO apk add --no-cache nodejs npm
|
|
157
|
+
;;
|
|
158
|
+
nixos)
|
|
159
|
+
action "Detected NixOS — Node.js must be installed via nix-shell"
|
|
160
|
+
warn "Run the following, then re-run this installer:"
|
|
161
|
+
warn " nix-shell -p nodejs python3 jq gh git"
|
|
162
|
+
warn " node cli/provision.mjs"
|
|
163
|
+
exit 0
|
|
164
|
+
;;
|
|
165
|
+
void)
|
|
166
|
+
action "Installing Node.js via xbps..."
|
|
167
|
+
dry $SUDO xbps-install -S nodejs
|
|
168
|
+
;;
|
|
148
169
|
*)
|
|
149
170
|
err "Unsupported distro: $ID"
|
|
150
171
|
err "Install Node.js 18+ manually: https://nodejs.org/"
|
|
@@ -168,6 +189,29 @@ check_deps() {
|
|
|
168
189
|
fi
|
|
169
190
|
}
|
|
170
191
|
|
|
192
|
+
install_lightrag() {
|
|
193
|
+
# LightRAG is optional but recommended. Install via uv tool.
|
|
194
|
+
# uv tools install to ~/.local/bin/ — ensure that's on PATH.
|
|
195
|
+
if [ -d "$HOME/.local/bin" ] && [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
196
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
197
|
+
fi
|
|
198
|
+
if have_cmd lightrag-server; then
|
|
199
|
+
note "LightRAG $(lightrag-server --version 2>/dev/null || echo 'server') present"
|
|
200
|
+
return 0
|
|
201
|
+
fi
|
|
202
|
+
action "Installing LightRAG via uv tool..."
|
|
203
|
+
if dry uv tool install "lightrag-hku[api]" 2>&1; then
|
|
204
|
+
note "LightRAG installed"
|
|
205
|
+
# Verify reachable
|
|
206
|
+
if [ -f "$HOME/.local/bin/lightrag-server" ]; then
|
|
207
|
+
note "LightRAG binary found at ~/.local/bin/"
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
warn "LightRAG install failed — the memory graph will not be available"
|
|
211
|
+
warn " Install later: uv tool install \"lightrag-hku[api]\""
|
|
212
|
+
fi
|
|
213
|
+
}
|
|
214
|
+
|
|
171
215
|
install_missing_deps_linux() {
|
|
172
216
|
. /etc/os-release 2>/dev/null || return 0
|
|
173
217
|
sudo_if_needed
|
|
@@ -206,7 +250,32 @@ install_missing_deps_linux() {
|
|
|
206
250
|
note "uv installed via astral.sh"
|
|
207
251
|
fi
|
|
208
252
|
;;
|
|
253
|
+
alpine)
|
|
254
|
+
action "Installing uv, python3, jq, gh via apk..."
|
|
255
|
+
dry $SUDO apk add --no-cache python3 py3-pip jq git curl
|
|
256
|
+
if ! have_cmd uv; then
|
|
257
|
+
dry curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
258
|
+
note "uv installed via astral.sh"
|
|
259
|
+
fi
|
|
260
|
+
;;
|
|
261
|
+
void)
|
|
262
|
+
action "Installing uv, python3, jq, gh via xbps..."
|
|
263
|
+
dry $SUDO xbps-install -S python3 python3-pip jq git curl
|
|
264
|
+
if ! have_cmd uv; then
|
|
265
|
+
dry curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
266
|
+
note "uv installed via astral.sh"
|
|
267
|
+
fi
|
|
268
|
+
;;
|
|
269
|
+
nixos)
|
|
270
|
+
warn "NixOS detected — LightRAG and other uv tools require manual setup"
|
|
271
|
+
warn " After nix-shell: nix-shell -p nodejs python3 jq gh git"
|
|
272
|
+
warn " Then run: uv tool install \"lightrag-hku[api]\""
|
|
273
|
+
warn " And re-run this installer: node cli/provision.mjs"
|
|
274
|
+
return 0
|
|
275
|
+
;;
|
|
209
276
|
esac
|
|
277
|
+
# LightRAG via uv tool (requires uv to be on PATH)
|
|
278
|
+
install_lightrag
|
|
210
279
|
}
|
|
211
280
|
|
|
212
281
|
install_missing_deps_macos() {
|
|
@@ -222,6 +291,8 @@ install_missing_deps_macos() {
|
|
|
222
291
|
fi
|
|
223
292
|
action "Installing uv, python3.12, jq, gh via brew..."
|
|
224
293
|
dry brew install uv jq gh 2>/dev/null || true
|
|
294
|
+
# LightRAG via uv tool (requires uv to be on PATH)
|
|
295
|
+
install_lightrag
|
|
225
296
|
}
|
|
226
297
|
|
|
227
298
|
# ── Service registration (delegated to Node) ────────────────────────────────
|
|
@@ -230,6 +301,12 @@ install_service() {
|
|
|
230
301
|
# The unified provisioner does this via `cli/service-controller.mjs`.
|
|
231
302
|
# We still call it from here because the bash script may run standalone
|
|
232
303
|
# (e.g. for first-boot provisioning before the npm package is set up).
|
|
304
|
+
#
|
|
305
|
+
# v5.x — issue #7: On first install, register the service so the
|
|
306
|
+
# dashboard auto-starts at login. On update, the running service is
|
|
307
|
+
# stopped by the provisioner before files are replaced, then restarted
|
|
308
|
+
# by the provisioner after — see cli/provision.mjs. This function
|
|
309
|
+
# covers the FIRST install path only.
|
|
233
310
|
local bin="$REPO_DIR/cli/bin.mjs"
|
|
234
311
|
if [ ! -f "$bin" ]; then
|
|
235
312
|
warn "cli/bin.mjs not found — service registration deferred"
|
|
@@ -240,6 +317,10 @@ install_service() {
|
|
|
240
317
|
dim " would run: node $bin service install"
|
|
241
318
|
return 0
|
|
242
319
|
fi
|
|
320
|
+
if [ "$UPDATE_MODE" -eq 1 ]; then
|
|
321
|
+
dim " update mode: service restart is handled by the Node provisioner"
|
|
322
|
+
return 0
|
|
323
|
+
fi
|
|
243
324
|
if ! node "$bin" service install 2>&1; then
|
|
244
325
|
warn "service registration had issues — see output above"
|
|
245
326
|
dim " manual command: node $bin service install"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
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": {
|
package/plugins/bizar/index.ts
CHANGED
|
@@ -119,8 +119,18 @@ import { createBgSpawnTool } from "./src/tools/bg-spawn.js";
|
|
|
119
119
|
import { createBgStatusTool } from "./src/tools/bg-status.js";
|
|
120
120
|
import { createBgCollectTool } from "./src/tools/bg-collect.js";
|
|
121
121
|
import { createBgKillTool } from "./src/tools/bg-kill.js";
|
|
122
|
+
import { createBgPauseTool } from "./src/tools/bg-pause.js";
|
|
123
|
+
import { createBgResumeTool } from "./src/tools/bg-resume.js";
|
|
124
|
+
import { createBgReportProgressTool } from "./src/tools/bg-report-progress.js";
|
|
125
|
+
import { createBgSendMessageTool } from "./src/tools/bg-send-message.js";
|
|
122
126
|
import { createBgGetCommentsTool } from "./src/tools/bg-get-comments.js";
|
|
123
127
|
import { createOpenKbTool } from "./src/tools/open-kb.js";
|
|
128
|
+
import { createMemorySearchTool } from "./src/tools/memory-search.js";
|
|
129
|
+
import { createMemoryReadTool } from "./src/tools/memory-read.js";
|
|
130
|
+
import { createMemoryWriteTool } from "./src/tools/memory-write.js";
|
|
131
|
+
import { createMemoryListTool } from "./src/tools/memory-list.js";
|
|
132
|
+
import { createMemoryInject, popMemoryContext } from "./src/hooks/memory-inject.js";
|
|
133
|
+
import { createMemoryWriteOnEnd } from "./src/hooks/memory-write-on-end.js";
|
|
124
134
|
|
|
125
135
|
// v0.4.0 — visual plan flow: settings, slash commands, plan tools
|
|
126
136
|
import { SettingsStore } from "./src/settings.js";
|
|
@@ -360,10 +370,25 @@ interface RuntimeContext {
|
|
|
360
370
|
seenMessageIds: Map<string, Set<string>>;
|
|
361
371
|
/** sessionID → pending system-transform message, set at warn/escalate. */
|
|
362
372
|
pendingInjections: Map<string, string>;
|
|
373
|
+
/** §Memory — sessionIDs that have already received memory context. */
|
|
374
|
+
injectedSessions: Set<string>;
|
|
363
375
|
/** v0.7.0-alpha.1 — Dashboard publisher (or null if disabled/not started).
|
|
364
376
|
* Used by the `event` hook to forward opencode session lifecycle
|
|
365
377
|
* events to the v2 dashboard via the @polderlabs/bizar-sdk. */
|
|
366
378
|
dashboardPublisher: DashboardPublisher | null;
|
|
379
|
+
/** §Memory — injects relevant memory context at session start. */
|
|
380
|
+
memoryInject: (sessionID: string, firstMessage: string) => Promise<void>;
|
|
381
|
+
/** §Memory — writes session summary to memory vault on session end. */
|
|
382
|
+
memoryWriteOnEnd: (sessionID: string, info: {
|
|
383
|
+
sessionID: string;
|
|
384
|
+
agent: string;
|
|
385
|
+
startedAt: number;
|
|
386
|
+
endedAt: number;
|
|
387
|
+
status: "idle" | "error" | "killed";
|
|
388
|
+
error?: string;
|
|
389
|
+
}, conversationPreview: string) => Promise<void>;
|
|
390
|
+
/** §Memory — session start timestamps for duration calculation on session end. */
|
|
391
|
+
sessionStartTimes: Map<string, number>;
|
|
367
392
|
}
|
|
368
393
|
|
|
369
394
|
/**
|
|
@@ -652,6 +677,22 @@ let bgAvailable = false;
|
|
|
652
677
|
|
|
653
678
|
installSignalHandlers(logger, instanceManager, serve, stream, options.stateDir);
|
|
654
679
|
|
|
680
|
+
// §Memory — compute injectedSessions and memoryInject early so they can
|
|
681
|
+
// be embedded directly in the ctx object literal (avoids TS2741).
|
|
682
|
+
const injectedSessions = new Set<string>();
|
|
683
|
+
const sessionStartTimes = new Map<string, number>();
|
|
684
|
+
const { memoryInject } = createMemoryInject({
|
|
685
|
+
injectedSessions,
|
|
686
|
+
worktree: input.worktree,
|
|
687
|
+
logger,
|
|
688
|
+
enabled: true, // TODO: gate on settings.memory.injectOnSessionStart
|
|
689
|
+
});
|
|
690
|
+
const { memoryWriteOnEnd } = createMemoryWriteOnEnd({
|
|
691
|
+
worktree: input.worktree,
|
|
692
|
+
logger,
|
|
693
|
+
enabled: true, // TODO: gate on settings.memory.writeOnSessionEnd
|
|
694
|
+
});
|
|
695
|
+
|
|
655
696
|
const ctx: RuntimeContext = {
|
|
656
697
|
logger,
|
|
657
698
|
options,
|
|
@@ -663,7 +704,11 @@ let bgAvailable = false;
|
|
|
663
704
|
directory: input.directory,
|
|
664
705
|
seenMessageIds: new Map(),
|
|
665
706
|
pendingInjections: new Map(),
|
|
707
|
+
injectedSessions,
|
|
666
708
|
dashboardPublisher,
|
|
709
|
+
memoryInject,
|
|
710
|
+
memoryWriteOnEnd,
|
|
711
|
+
sessionStartTimes,
|
|
667
712
|
};
|
|
668
713
|
|
|
669
714
|
return buildHooks(ctx, { instanceManager, bgAvailable });
|
|
@@ -983,6 +1028,25 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
983
1028
|
worktree: ctx.worktree,
|
|
984
1029
|
logger: ctx.logger,
|
|
985
1030
|
}),
|
|
1031
|
+
|
|
1032
|
+
// §Memory — Bizar Memory tools. Available to all agents; no serve
|
|
1033
|
+
// child required. Worktree is passed for context (vault discovery).
|
|
1034
|
+
bizar_memory_search: createMemorySearchTool({
|
|
1035
|
+
worktree: ctx.worktree,
|
|
1036
|
+
logger: ctx.logger,
|
|
1037
|
+
}),
|
|
1038
|
+
bizar_memory_read: createMemoryReadTool({
|
|
1039
|
+
worktree: ctx.worktree,
|
|
1040
|
+
logger: ctx.logger,
|
|
1041
|
+
}),
|
|
1042
|
+
bizar_memory_write: createMemoryWriteTool({
|
|
1043
|
+
worktree: ctx.worktree,
|
|
1044
|
+
logger: ctx.logger,
|
|
1045
|
+
}),
|
|
1046
|
+
bizar_memory_list: createMemoryListTool({
|
|
1047
|
+
worktree: ctx.worktree,
|
|
1048
|
+
logger: ctx.logger,
|
|
1049
|
+
}),
|
|
986
1050
|
};
|
|
987
1051
|
const tools = bg.instanceManager
|
|
988
1052
|
? {
|
|
@@ -992,6 +1056,10 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
992
1056
|
// src/opencode-runner.ts). The serve child is still
|
|
993
1057
|
// available for the dashboard's v2 protocol and for any
|
|
994
1058
|
// TUI/web client that wants to attach to it.
|
|
1059
|
+
//
|
|
1060
|
+
// v5.x — added pause/resume/steer/progress tools for
|
|
1061
|
+
// dashboard integration. See tools/bg-{pause,resume,
|
|
1062
|
+
// report-progress,send-message}.ts.
|
|
995
1063
|
bizar_spawn_background: createBgSpawnTool({
|
|
996
1064
|
instanceManager: bg.instanceManager,
|
|
997
1065
|
worktree: ctx.worktree,
|
|
@@ -1009,6 +1077,23 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
1009
1077
|
instanceManager: bg.instanceManager,
|
|
1010
1078
|
logger: ctx.logger,
|
|
1011
1079
|
}),
|
|
1080
|
+
bizar_pause: createBgPauseTool({
|
|
1081
|
+
instanceManager: bg.instanceManager,
|
|
1082
|
+
logger: ctx.logger,
|
|
1083
|
+
}),
|
|
1084
|
+
bizar_resume: createBgResumeTool({
|
|
1085
|
+
instanceManager: bg.instanceManager,
|
|
1086
|
+
logger: ctx.logger,
|
|
1087
|
+
}),
|
|
1088
|
+
bizar_send_message: createBgSendMessageTool({
|
|
1089
|
+
instanceManager: bg.instanceManager,
|
|
1090
|
+
logger: ctx.logger,
|
|
1091
|
+
}),
|
|
1092
|
+
// Available to ALL agents (the body of a bg agent calls it).
|
|
1093
|
+
bizar_report_progress: createBgReportProgressTool({
|
|
1094
|
+
instanceManager: bg.instanceManager,
|
|
1095
|
+
logger: ctx.logger,
|
|
1096
|
+
}),
|
|
1012
1097
|
}
|
|
1013
1098
|
: {
|
|
1014
1099
|
...basePlanTools,
|
|
@@ -1040,6 +1125,14 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
1040
1125
|
output.system.push(pending);
|
|
1041
1126
|
ctx.pendingInjections.delete(sessionID);
|
|
1042
1127
|
}
|
|
1128
|
+
|
|
1129
|
+
// §Memory — inject relevant memory context at the start of the
|
|
1130
|
+
// session (first turn only). `popMemoryContext` is idempotent
|
|
1131
|
+
// (marks session as injected on first call).
|
|
1132
|
+
const memCtx = popMemoryContext(sessionID);
|
|
1133
|
+
if (memCtx) {
|
|
1134
|
+
output.system.push(memCtx);
|
|
1135
|
+
}
|
|
1043
1136
|
},
|
|
1044
1137
|
|
|
1045
1138
|
// Before the model is called, strip `` blocks from
|
|
@@ -1150,7 +1243,61 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
1150
1243
|
});
|
|
1151
1244
|
ctx.pendingInjections.delete(sessionID);
|
|
1152
1245
|
ctx.seenMessageIds.delete(sessionID);
|
|
1246
|
+
ctx.injectedSessions.delete(sessionID);
|
|
1247
|
+
ctx.sessionStartTimes.delete(sessionID);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// §Memory — on session start, fetch relevant memory context and
|
|
1251
|
+
// queue it for injection via `popMemoryContext` in the transform hook.
|
|
1252
|
+
if (type === "session.created") {
|
|
1253
|
+
const ev2 = ev as {
|
|
1254
|
+
properties?: {
|
|
1255
|
+
sessionID?: string;
|
|
1256
|
+
startedAt?: number;
|
|
1257
|
+
[k: string]: unknown;
|
|
1258
|
+
};
|
|
1259
|
+
messageText?: string;
|
|
1260
|
+
firstMessage?: string;
|
|
1261
|
+
};
|
|
1262
|
+
// Record start time for session-end duration calculation.
|
|
1263
|
+
const startedAt = typeof ev2.properties?.startedAt === "number"
|
|
1264
|
+
? ev2.properties.startedAt
|
|
1265
|
+
: Date.now();
|
|
1266
|
+
ctx.sessionStartTimes.set(sessionID, startedAt);
|
|
1267
|
+
// Extract the first user message text — different opencode versions
|
|
1268
|
+
// surface this in different places; try several keys.
|
|
1269
|
+
const firstMessage =
|
|
1270
|
+
typeof ev2.messageText === "string"
|
|
1271
|
+
? ev2.messageText
|
|
1272
|
+
: typeof ev2.firstMessage === "string"
|
|
1273
|
+
? ev2.firstMessage
|
|
1274
|
+
: typeof ev.properties?.messageText === "string"
|
|
1275
|
+
? ev.properties.messageText
|
|
1276
|
+
: "";
|
|
1277
|
+
void ctx.memoryInject(sessionID, firstMessage);
|
|
1153
1278
|
}
|
|
1279
|
+
|
|
1280
|
+
// §Memory — on session end, write an automated session summary to the
|
|
1281
|
+
// memory vault. Fire-and-forget — failures are logged but never throw.
|
|
1282
|
+
if (type === "session.idle" || type === "session.error") {
|
|
1283
|
+
const startedAt = ctx.sessionStartTimes.get(sessionID) ?? Date.now();
|
|
1284
|
+
const endedAt = Date.now();
|
|
1285
|
+
const errorProp = (ev as { properties?: { error?: string } }).properties?.error;
|
|
1286
|
+
void ctx.memoryWriteOnEnd(
|
|
1287
|
+
sessionID,
|
|
1288
|
+
{
|
|
1289
|
+
sessionID,
|
|
1290
|
+
agent: (ev as { properties?: { agent?: string } }).properties?.agent ?? "unknown",
|
|
1291
|
+
startedAt,
|
|
1292
|
+
endedAt,
|
|
1293
|
+
status: type === "session.idle" ? "idle" : "error",
|
|
1294
|
+
error: type === "session.error" ? (errorProp ?? "unknown") : undefined,
|
|
1295
|
+
},
|
|
1296
|
+
"",
|
|
1297
|
+
);
|
|
1298
|
+
ctx.sessionStartTimes.delete(sessionID);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1154
1301
|
// Other event types are no-ops on the hook side. The state file
|
|
1155
1302
|
// is updated by `chat.message` and `tool.execute.before/after`.
|
|
1156
1303
|
} catch (err) {
|