@polderlabs/bizar 5.5.0 → 5.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bizar-dash/dist/assets/{EnvVarsSection-L413CQEI.js → EnvVarsSection-DoIzqrlb.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-L413CQEI.js.map → EnvVarsSection-DoIzqrlb.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-0kGQb1S6.js → MobileChat-CoUCUsbr.js} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-0kGQb1S6.js.map → MobileChat-CoUCUsbr.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-BeO04g52.js +1 -0
- package/bizar-dash/dist/assets/MobileSettings-BeO04g52.js.map +1 -0
- package/bizar-dash/dist/assets/{Toast-C6f1zFJ2.js → Toast-D9nv5N6m.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-C6f1zFJ2.js.map → Toast-D9nv5N6m.js.map} +1 -1
- package/bizar-dash/dist/assets/{icons-BsWXWkEF.js → icons-Btipv2pp.js} +100 -90
- package/bizar-dash/dist/assets/icons-Btipv2pp.js.map +1 -0
- package/bizar-dash/dist/assets/main-DHXPuddY.js +18 -0
- package/bizar-dash/dist/assets/main-DHXPuddY.js.map +1 -0
- package/bizar-dash/dist/assets/{main-BklYy02e.css → main-UG4jKK-F.css} +1 -1
- package/bizar-dash/dist/assets/mobile-H5KH9cTL.js +1 -0
- package/bizar-dash/dist/assets/{mobile-CsaN4CxH.js.map → mobile-H5KH9cTL.js.map} +1 -1
- package/bizar-dash/dist/assets/{mobile-layout-iFBdjEE4.js → mobile-layout-BqHxVb6_.js} +2 -2
- package/bizar-dash/dist/assets/{mobile-layout-iFBdjEE4.js.map → mobile-layout-BqHxVb6_.js.map} +1 -1
- package/bizar-dash/dist/assets/{useSlashCommands-DGJcC9iQ.js → useSlashCommands-C24-O1e7.js} +2 -2
- package/bizar-dash/dist/assets/{useSlashCommands-DGJcC9iQ.js.map → useSlashCommands-C24-O1e7.js.map} +1 -1
- package/bizar-dash/dist/index.html +7 -7
- package/bizar-dash/dist/mobile.html +3 -3
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/bg-spawner.mjs +522 -305
- package/bizar-dash/src/server/headroom.mjs +3 -1
- package/bizar-dash/src/server/memory-lightrag.mjs +26 -2
- package/bizar-dash/src/server/memory-store.mjs +74 -28
- package/bizar-dash/src/server/opencode-sdk.mjs +63 -3
- package/bizar-dash/src/server/plugins/registry.mjs +1 -1
- package/bizar-dash/src/server/routes/activity.mjs +85 -0
- package/bizar-dash/src/server/routes/background.mjs +31 -13
- package/bizar-dash/src/server/routes/memory.mjs +91 -38
- package/bizar-dash/src/server/routes/opencode-session-detail.mjs +1 -1
- package/bizar-dash/src/server/server.mjs +4 -1
- package/bizar-dash/src/web/components/Sidebar.tsx +0 -1
- package/bizar-dash/src/web/styles/main.css +52 -13
- package/bizar-dash/src/web/styles/memory.css +17 -0
- package/bizar-dash/src/web/styles/settings.css +6 -56
- package/bizar-dash/src/web/views/Marketplace.tsx +192 -31
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +157 -7
- package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +11 -0
- package/bizar-dash/src/web/views/settings/MemorySection.tsx +65 -11
- package/bizar-dash/tests/background-sdk-session.test.mjs +94 -0
- package/bizar-dash/tests/background-session-events.test.mjs +74 -0
- package/bizar-dash/tests/background-steer-sdk.test.mjs +71 -0
- package/bizar-dash/tests/background-steer.test.mjs +25 -7
- package/bizar-dash/tests/memory-namespace.test.mjs +2 -2
- package/bizar-dash/tests/memory-status-root.test.mjs +144 -0
- package/bizar-dash/tests/memory-store.test.mjs +5 -4
- package/bizar-dash/tests/memory-vault-config-endpoint.test.mjs +173 -0
- package/bizar-dash/tests/settings-mode-wiring.test.tsx +1 -1
- package/bizar-dash/tests/settings-nav.test.tsx +0 -1
- package/bizar-dash/tests/views/Marketplace.test.tsx +200 -0
- package/bizar-dash/tests/views/Memory.test.tsx +274 -0
- package/bizar-dash/tests/views/sidebar.test.tsx +116 -0
- package/bizar-dash/tests/views/topbar.test.tsx +87 -0
- package/cli/commands/marketplace.mjs +1 -1
- package/cli/memory.mjs +1 -1
- package/package.json +1 -1
- package/plugins/bizar/src/background-state.ts +11 -0
- package/plugins/bizar/src/background.ts +212 -34
- package/plugins/bizar/src/opencode-runner.ts +80 -353
- package/plugins/bizar/src/tools/bg-send-message.ts +160 -61
- package/plugins/bizar/src/tools/bg-spawn.ts +235 -156
- package/plugins/bizar/tests/tools/bg-send-message.test.ts +84 -11
- package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +223 -0
- package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js.map +0 -1
- package/bizar-dash/dist/assets/icons-BsWXWkEF.js.map +0 -1
- package/bizar-dash/dist/assets/main-CBTgYSeG.js +0 -18
- package/bizar-dash/dist/assets/main-CBTgYSeG.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-CsaN4CxH.js +0 -1
|
@@ -1,66 +1,139 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* plugins/bizar/src/tools/bg-send-message.ts
|
|
3
3
|
*
|
|
4
|
-
* v5.
|
|
5
|
-
* background agent — "steer" the running instance with a new
|
|
6
|
-
* instruction.
|
|
4
|
+
* v5.5.1 — `bizar_send_message` tool. TRUE mid-flight steer.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* a new turn. That's the "real" steer path.
|
|
6
|
+
* In v5.5.0 this tool returned `unavailable_in_subprocess_mode` because
|
|
7
|
+
* `opencode run` is a one-shot CLI. v5.5.1 switched bg agents to
|
|
8
|
+
* long-lived opencode serve SDK sessions, so this tool now delegates
|
|
9
|
+
* to `POST /api/background/<id>/steer` on the dashboard. The dashboard
|
|
10
|
+
* calls `sdk.sessions.prompt()` on the live session, which the
|
|
11
|
+
* opencode serve child picks up mid-loop as the next user turn.
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* IPC channel for sending a follow-up user message mid-run. The
|
|
20
|
-
* subprocess doesn't expose one and we don't own the opencode source.
|
|
13
|
+
* Wire contract (mirrors `bizar-dash/src/server/routes/background.mjs`):
|
|
14
|
+
* - Request: `POST /api/background/:id/steer` with body `{ message }`.
|
|
15
|
+
* - Response: `{ ok, mode: 'true_midflight', newInstanceId: null, instanceId, steerCount }`.
|
|
21
16
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* 1. **Switch back to HTTP server mode** — every bg agent would
|
|
25
|
-
* start its own `opencode serve` + a server-side streaming
|
|
26
|
-
* prompt. Heavyweight (extra processes, double the memory) and
|
|
27
|
-
* a 1-2 sprint piece of work.
|
|
28
|
-
*
|
|
29
|
-
* 2. **Kill + restart with appended prompt** — graceful shutdown
|
|
30
|
-
* of the current subprocess, spin up a new one whose prompt is
|
|
31
|
-
* `(original) + "\n\n[STEERED " + ISO timestamp + "]\n" + msg`.
|
|
32
|
-
* Cheaper; matches the user's mental model of "send new
|
|
33
|
-
* instruction"; implemented in the dashboard at
|
|
34
|
-
* `POST /api/background/:id/steer`.
|
|
35
|
-
*
|
|
36
|
-
* We document the constraint here. The tool itself returns the
|
|
37
|
-
* concrete error and the recommended path so the agent can either
|
|
38
|
-
* (a) ask Odin to use the dashboard steer endpoint or (b) call
|
|
39
|
-
* `bizar_kill` + `bizar_spawn_background` with a combined prompt as
|
|
40
|
-
* a backup.
|
|
41
|
-
*
|
|
42
|
-
* In a future v0.9.x this tool will be promoted to the real
|
|
43
|
-
* implementation once opencode run supports mid-flight prompting.
|
|
17
|
+
* Errors surface as `{ ok: false, error }` so callers can react.
|
|
44
18
|
*/
|
|
45
19
|
import { tool } from "@opencode-ai/plugin";
|
|
46
20
|
import { z } from "zod";
|
|
47
21
|
|
|
48
22
|
import type { InstanceManager } from "../background.js";
|
|
49
23
|
import type { Logger } from "../logger.js";
|
|
24
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import { homedir } from "node:os";
|
|
50
27
|
|
|
51
28
|
export interface BgSendMessageDeps {
|
|
52
29
|
instanceManager: InstanceManager;
|
|
53
30
|
logger: Logger;
|
|
31
|
+
/**
|
|
32
|
+
* Optional injection point for tests. When provided, the dashboard
|
|
33
|
+
* HTTP call is short-circuited and the test function is called with
|
|
34
|
+
* the resolved request shape.
|
|
35
|
+
*/
|
|
36
|
+
_dashboardPost?: (url: string, init: { headers: Record<string, string>; body: string }) => Promise<{ ok: boolean; status: number; json: () => Promise<unknown> }>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// --- Dashboard URL + token (mirrors bg-spawn.ts) ------------------------
|
|
40
|
+
|
|
41
|
+
function resolveDashboardUrl(): string {
|
|
42
|
+
const fromEnv = process.env.BIZAR_DASHBOARD_URL;
|
|
43
|
+
if (fromEnv && fromEnv.trim()) return fromEnv.trim().replace(/\/+$/, "");
|
|
44
|
+
const port = process.env.BIZAR_DASHBOARD_PORT;
|
|
45
|
+
if (port && /^\d+$/.test(port)) return `http://127.0.0.1:${port}`;
|
|
46
|
+
return "http://127.0.0.1:4098";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const DEFAULT_DASHBOARD_AUTH_PATHS = [
|
|
50
|
+
join(homedir(), ".config", "bizar", "dashboard-secret"),
|
|
51
|
+
join(homedir(), ".cache", "bizarharness", "dash-auth.json"),
|
|
52
|
+
join(homedir(), ".cache", "bizar", "dash-auth.json"),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
function readDashboardToken(): string {
|
|
56
|
+
for (const candidate of DEFAULT_DASHBOARD_AUTH_PATHS) {
|
|
57
|
+
try {
|
|
58
|
+
if (!existsSync(candidate)) continue;
|
|
59
|
+
const text = readFileSync(candidate, "utf-8").trim();
|
|
60
|
+
if (text && text.length >= 16) return text;
|
|
61
|
+
} catch {
|
|
62
|
+
/* ignore */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const candidate of DEFAULT_DASHBOARD_AUTH_PATHS) {
|
|
66
|
+
try {
|
|
67
|
+
if (!existsSync(candidate)) continue;
|
|
68
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf-8")) as { password?: unknown };
|
|
69
|
+
if (typeof parsed.password === "string" && parsed.password.length >= 16) {
|
|
70
|
+
return parsed.password;
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
/* ignore */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function postJsonToDashboard(
|
|
80
|
+
url: string,
|
|
81
|
+
body: unknown,
|
|
82
|
+
override?: BgSendMessageDeps["_dashboardPost"],
|
|
83
|
+
): Promise<unknown> {
|
|
84
|
+
if (override) {
|
|
85
|
+
const res = await override(url, {
|
|
86
|
+
headers: { "content-type": "application/json" },
|
|
87
|
+
body: JSON.stringify(body),
|
|
88
|
+
});
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
const text = await res.json().catch(() => ({}));
|
|
91
|
+
const err = new Error(
|
|
92
|
+
`dashboard HTTP ${res.status}: ${JSON.stringify(text).slice(0, 200)}`,
|
|
93
|
+
);
|
|
94
|
+
(err as Error & { httpStatus?: number }).httpStatus = res.status;
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
return res.json();
|
|
98
|
+
}
|
|
99
|
+
const token = readDashboardToken();
|
|
100
|
+
const headers: Record<string, string> = {
|
|
101
|
+
"content-type": "application/json",
|
|
102
|
+
accept: "application/json",
|
|
103
|
+
};
|
|
104
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
105
|
+
|
|
106
|
+
const ac = new AbortController();
|
|
107
|
+
const timer = setTimeout(() => ac.abort(), 8_000);
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(url, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers,
|
|
112
|
+
body: JSON.stringify(body),
|
|
113
|
+
signal: ac.signal,
|
|
114
|
+
});
|
|
115
|
+
if (!res.ok) {
|
|
116
|
+
const text = await res.text().catch(() => "");
|
|
117
|
+
const err = new Error(
|
|
118
|
+
`dashboard HTTP ${res.status}: ${text.slice(0, 200)}`,
|
|
119
|
+
);
|
|
120
|
+
(err as Error & { httpStatus?: number }).httpStatus = res.status;
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
return res.json();
|
|
124
|
+
} finally {
|
|
125
|
+
clearTimeout(timer);
|
|
126
|
+
}
|
|
54
127
|
}
|
|
55
128
|
|
|
56
129
|
export function createBgSendMessageTool(deps: BgSendMessageDeps) {
|
|
57
130
|
return tool({
|
|
58
131
|
description:
|
|
59
132
|
"Send a follow-up message to a running background agent. " +
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
133
|
+
"v5.5.1: TRUE mid-flight steer via the opencode serve SDK. " +
|
|
134
|
+
"The same instance keeps running; the new message becomes the next user turn " +
|
|
135
|
+
"of the running opencode session. No kill+respawn, no [STEERED <ts>] marker, " +
|
|
136
|
+
"no loss of agent context.",
|
|
64
137
|
args: {
|
|
65
138
|
instanceId: z
|
|
66
139
|
.string()
|
|
@@ -71,9 +144,9 @@ export function createBgSendMessageTool(deps: BgSendMessageDeps) {
|
|
|
71
144
|
.min(1)
|
|
72
145
|
.describe("The follow-up instruction to send."),
|
|
73
146
|
},
|
|
74
|
-
execute: async (rawArgs,
|
|
147
|
+
execute: async (rawArgs, _ctx) => {
|
|
75
148
|
const args = rawArgs as { instanceId: string; message: string };
|
|
76
|
-
// Verify the instance exists so we don't silently accept bogus ids.
|
|
149
|
+
// 1. Verify the instance exists so we don't silently accept bogus ids.
|
|
77
150
|
const inst = await deps.instanceManager.get(args.instanceId);
|
|
78
151
|
if (!inst) {
|
|
79
152
|
return {
|
|
@@ -83,23 +156,49 @@ export function createBgSendMessageTool(deps: BgSendMessageDeps) {
|
|
|
83
156
|
}),
|
|
84
157
|
};
|
|
85
158
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
159
|
+
|
|
160
|
+
// 2. v5.5.1 — delegate to the dashboard. The dashboard owns the
|
|
161
|
+
// opencode SDK and calls `sdk.sessions.prompt({...})` on the
|
|
162
|
+
// live session, which the opencode serve child picks up
|
|
163
|
+
// mid-loop as the next user turn.
|
|
164
|
+
const dashboardUrl = `${resolveDashboardUrl()}/api/background/${encodeURIComponent(args.instanceId)}/steer`;
|
|
165
|
+
try {
|
|
166
|
+
const data = (await postJsonToDashboard(
|
|
167
|
+
dashboardUrl,
|
|
168
|
+
{ message: args.message },
|
|
169
|
+
deps._dashboardPost,
|
|
170
|
+
)) as { ok: boolean; mode?: string; steerCount?: number; error?: string };
|
|
171
|
+
if (!data || data.ok !== true) {
|
|
172
|
+
return {
|
|
173
|
+
output: JSON.stringify({
|
|
174
|
+
error: data?.error || "steer_failed",
|
|
175
|
+
instanceId: args.instanceId,
|
|
176
|
+
}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
deps.logger.info(
|
|
180
|
+
`bizar: sendMessage(${args.instanceId}) — TRUE mid-flight steer #${data.steerCount ?? "?"} accepted`,
|
|
181
|
+
);
|
|
182
|
+
return {
|
|
183
|
+
output: JSON.stringify({
|
|
184
|
+
ok: true,
|
|
185
|
+
mode: data.mode || "true_midflight",
|
|
186
|
+
instanceId: args.instanceId,
|
|
187
|
+
steerCount: data.steerCount ?? 0,
|
|
188
|
+
message:
|
|
189
|
+
"Steer delivered to the running opencode session. The agent will pick up your message as the next user turn — no kill+respawn was performed, no context was lost.",
|
|
190
|
+
}),
|
|
191
|
+
};
|
|
192
|
+
} catch (err: unknown) {
|
|
193
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
194
|
+
deps.logger.warn(`bizar: sendMessage(${args.instanceId}) failed: ${msg}`);
|
|
195
|
+
return {
|
|
196
|
+
output: JSON.stringify({
|
|
197
|
+
error: `steer_failed: ${msg}`,
|
|
198
|
+
instanceId: args.instanceId,
|
|
199
|
+
}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
103
202
|
},
|
|
104
203
|
});
|
|
105
|
-
}
|
|
204
|
+
}
|