@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,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/bizar/src/tools/bg-report-progress.ts
|
|
3
|
+
*
|
|
4
|
+
* v5.x — `bizar_report_progress` tool. Lets a running background agent
|
|
5
|
+
* push a structured progress update (step/total/message) into the
|
|
6
|
+
* instance state. The dashboard reads `progress` and renders a
|
|
7
|
+
* progress bar; `progressMessage` shows the free-form status line.
|
|
8
|
+
*
|
|
9
|
+
* Available to ALL agents (the body of a bg agent calls it; not just
|
|
10
|
+
* Odin). The `bizar_*` prefix is reserved; the auth filter is "all",
|
|
11
|
+
* matching the "tools all agents can call" pattern of
|
|
12
|
+
* `bizar_status`.
|
|
13
|
+
*/
|
|
14
|
+
import { tool } from "@opencode-ai/plugin";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
import type { InstanceManager } from "../background.js";
|
|
18
|
+
import type { Logger } from "../logger.js";
|
|
19
|
+
|
|
20
|
+
export interface BgReportProgressDeps {
|
|
21
|
+
instanceManager: InstanceManager;
|
|
22
|
+
logger: Logger;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createBgReportProgressTool(deps: BgReportProgressDeps) {
|
|
26
|
+
return tool({
|
|
27
|
+
description:
|
|
28
|
+
"Report progress on the current background instance. " +
|
|
29
|
+
"Available to all agents. step/total are integers; message is a " +
|
|
30
|
+
"free-form status hint shown next to the progress bar.",
|
|
31
|
+
args: {
|
|
32
|
+
instanceId: z
|
|
33
|
+
.string()
|
|
34
|
+
.min(1)
|
|
35
|
+
.describe("Instance id (the one returned by bizar_spawn_background)."),
|
|
36
|
+
step: z
|
|
37
|
+
.number()
|
|
38
|
+
.int()
|
|
39
|
+
.min(0)
|
|
40
|
+
.describe("Current step (>=0)."),
|
|
41
|
+
total: z
|
|
42
|
+
.number()
|
|
43
|
+
.int()
|
|
44
|
+
.positive()
|
|
45
|
+
.describe("Total step count (>0)."),
|
|
46
|
+
message: z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Optional human-readable status."),
|
|
50
|
+
indeterminate: z
|
|
51
|
+
.boolean()
|
|
52
|
+
.optional()
|
|
53
|
+
.default(false)
|
|
54
|
+
.describe("Set true to show an indeterminate (unknown) progress."),
|
|
55
|
+
},
|
|
56
|
+
execute: async (rawArgs) => {
|
|
57
|
+
const args = rawArgs as {
|
|
58
|
+
instanceId: string;
|
|
59
|
+
step: number;
|
|
60
|
+
total: number;
|
|
61
|
+
message?: string;
|
|
62
|
+
indeterminate?: boolean;
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
const result = await deps.instanceManager.updateProgress(
|
|
66
|
+
args.instanceId,
|
|
67
|
+
args.step,
|
|
68
|
+
args.total,
|
|
69
|
+
args.message,
|
|
70
|
+
Boolean(args.indeterminate),
|
|
71
|
+
);
|
|
72
|
+
if (!result.ok) {
|
|
73
|
+
return {
|
|
74
|
+
output: JSON.stringify({
|
|
75
|
+
error: result.error || "progress_failed",
|
|
76
|
+
instanceId: args.instanceId,
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
output: JSON.stringify({
|
|
82
|
+
instanceId: args.instanceId,
|
|
83
|
+
progress: result.progress,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
} catch (err: unknown) {
|
|
87
|
+
deps.logger.warn(
|
|
88
|
+
`bizar: report-progress(${args.instanceId}) threw: ${
|
|
89
|
+
err instanceof Error ? err.message : String(err)
|
|
90
|
+
}`,
|
|
91
|
+
);
|
|
92
|
+
return {
|
|
93
|
+
output: JSON.stringify({
|
|
94
|
+
error: `report-progress threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
95
|
+
instanceId: args.instanceId,
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/bizar/src/tools/bg-resume.ts
|
|
3
|
+
*
|
|
4
|
+
* v5.x — `bizar_resume` tool. Sends SIGCONT to a paused background
|
|
5
|
+
* agent's subprocess. Companion to {@link ./bg-pause.ts}.
|
|
6
|
+
*
|
|
7
|
+
* Odin-only.
|
|
8
|
+
*/
|
|
9
|
+
import { tool } from "@opencode-ai/plugin";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
import type { InstanceManager } from "../background.js";
|
|
13
|
+
import type { Logger } from "../logger.js";
|
|
14
|
+
|
|
15
|
+
export interface BgResumeDeps {
|
|
16
|
+
instanceManager: InstanceManager;
|
|
17
|
+
logger: Logger;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createBgResumeTool(deps: BgResumeDeps) {
|
|
21
|
+
return tool({
|
|
22
|
+
description:
|
|
23
|
+
"Resume a paused background agent by sending SIGCONT to its subprocess. " +
|
|
24
|
+
"Only Odin may resume. No-op on already-running instances. POSIX only.",
|
|
25
|
+
args: {
|
|
26
|
+
instanceId: z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1)
|
|
29
|
+
.describe("Instance id returned by bizar_spawn_background."),
|
|
30
|
+
},
|
|
31
|
+
execute: async (rawArgs, ctx) => {
|
|
32
|
+
if (ctx.agent !== "odin") {
|
|
33
|
+
return {
|
|
34
|
+
output: JSON.stringify({
|
|
35
|
+
error:
|
|
36
|
+
"Only Odin can resume background agents. Use bizar_status to inspect or ask Odin to resume.",
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const args = rawArgs as { instanceId: string };
|
|
41
|
+
try {
|
|
42
|
+
const result = await deps.instanceManager.resume(args.instanceId);
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
return {
|
|
45
|
+
output: JSON.stringify({
|
|
46
|
+
error: result.error || "resume failed",
|
|
47
|
+
instanceId: args.instanceId,
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
output: JSON.stringify({
|
|
53
|
+
instanceId: args.instanceId,
|
|
54
|
+
status: "running",
|
|
55
|
+
}),
|
|
56
|
+
};
|
|
57
|
+
} catch (err: unknown) {
|
|
58
|
+
deps.logger.warn(
|
|
59
|
+
`bizar: resume(${args.instanceId}) threw: ${
|
|
60
|
+
err instanceof Error ? err.message : String(err)
|
|
61
|
+
}`,
|
|
62
|
+
);
|
|
63
|
+
return {
|
|
64
|
+
output: JSON.stringify({
|
|
65
|
+
error: `resume threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
66
|
+
instanceId: args.instanceId,
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/bizar/src/tools/bg-send-message.ts
|
|
3
|
+
*
|
|
4
|
+
* v5.x — `bizar_send_message` tool. Mid-flight redirect for a
|
|
5
|
+
* background agent — "steer" the running instance with a new
|
|
6
|
+
* instruction.
|
|
7
|
+
*
|
|
8
|
+
* ────────────────────────────────────────────────────────────────
|
|
9
|
+
* v0.8.0 DESIGN NOTE — why this tool is a structured no-op
|
|
10
|
+
* ────────────────────────────────────────────────────────────────
|
|
11
|
+
* In the opencode 1.17 `serve` HTTP API (used by bg-spawn up to v0.7)
|
|
12
|
+
* you could `POST /session/{id}/prompt` to send a follow-up message
|
|
13
|
+
* to a running session — the loop would pick it up mid-run and start
|
|
14
|
+
* a new turn. That's the "real" steer path.
|
|
15
|
+
*
|
|
16
|
+
* In v0.8.0 the plugin switched to `opencode run`, which is a
|
|
17
|
+
* one-shot CLI: it accepts the prompt on the command line, runs the
|
|
18
|
+
* agent loop to completion, and exits. There is no documented HTTP /
|
|
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.
|
|
21
|
+
*
|
|
22
|
+
* Two paths to a workable steer:
|
|
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.
|
|
44
|
+
*/
|
|
45
|
+
import { tool } from "@opencode-ai/plugin";
|
|
46
|
+
import { z } from "zod";
|
|
47
|
+
|
|
48
|
+
import type { InstanceManager } from "../background.js";
|
|
49
|
+
import type { Logger } from "../logger.js";
|
|
50
|
+
|
|
51
|
+
export interface BgSendMessageDeps {
|
|
52
|
+
instanceManager: InstanceManager;
|
|
53
|
+
logger: Logger;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createBgSendMessageTool(deps: BgSendMessageDeps) {
|
|
57
|
+
return tool({
|
|
58
|
+
description:
|
|
59
|
+
"Send a follow-up message to a running background agent. " +
|
|
60
|
+
"v0.8.0 LIMITATION: opencode run is a one-shot CLI and does not " +
|
|
61
|
+
"support mid-flight prompting. This tool returns an `unavailable_in_subprocess_mode` " +
|
|
62
|
+
"error and points the caller to the dashboard's steer endpoint, " +
|
|
63
|
+
"which performs kill+restart-with-appended-prompt.",
|
|
64
|
+
args: {
|
|
65
|
+
instanceId: z
|
|
66
|
+
.string()
|
|
67
|
+
.min(1)
|
|
68
|
+
.describe("Instance id returned by bizar_spawn_background."),
|
|
69
|
+
message: z
|
|
70
|
+
.string()
|
|
71
|
+
.min(1)
|
|
72
|
+
.describe("The follow-up instruction to send."),
|
|
73
|
+
},
|
|
74
|
+
execute: async (rawArgs, ctx) => {
|
|
75
|
+
const args = rawArgs as { instanceId: string; message: string };
|
|
76
|
+
// Verify the instance exists so we don't silently accept bogus ids.
|
|
77
|
+
const inst = await deps.instanceManager.get(args.instanceId);
|
|
78
|
+
if (!inst) {
|
|
79
|
+
return {
|
|
80
|
+
output: JSON.stringify({
|
|
81
|
+
error: "instance_not_found",
|
|
82
|
+
instanceId: args.instanceId,
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
deps.logger.debug(
|
|
87
|
+
`bizar: sendMessage(${args.instanceId}) — mid-flight steer unavailable in v0.8.0 subprocess mode`,
|
|
88
|
+
);
|
|
89
|
+
void ctx;
|
|
90
|
+
return {
|
|
91
|
+
output: JSON.stringify({
|
|
92
|
+
error: "unavailable_in_subprocess_mode",
|
|
93
|
+
message:
|
|
94
|
+
"v0.8.0 bg agents run as `opencode run` subprocesses and cannot accept a follow-up prompt mid-flight. " +
|
|
95
|
+
"Use the dashboard's `POST /api/background/<id>/steer` (Steer button on instance detail) which performs " +
|
|
96
|
+
"kill+restart with the new instruction appended to the original prompt. " +
|
|
97
|
+
"Or, if running headlessly, you can call `bizar_kill` followed by `bizar_spawn_background` " +
|
|
98
|
+
"with a combined prompt yourself.",
|
|
99
|
+
instanceId: args.instanceId,
|
|
100
|
+
dashboardHint: `POST /api/background/${args.instanceId}/steer`,
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -245,6 +245,10 @@ export function createBgSpawnTool(deps: BgSpawnDeps) {
|
|
|
245
245
|
persistent: args.persistent ?? false,
|
|
246
246
|
maxRestarts: args.maxRestarts ?? 3,
|
|
247
247
|
restartCount: 0,
|
|
248
|
+
// v5.x — progress reporting seed. Agents update via
|
|
249
|
+
// `bizar_report_progress`; the dashboard renders the bar.
|
|
250
|
+
progress: 0,
|
|
251
|
+
toolCalls: [],
|
|
248
252
|
};
|
|
249
253
|
const addRes = await deps.instanceManager.add(draft);
|
|
250
254
|
if (addRes === "cap_reached") {
|
|
@@ -333,14 +337,17 @@ export function createBgSpawnTool(deps: BgSpawnDeps) {
|
|
|
333
337
|
const { onExit } = await import("../opencode-runner.js");
|
|
334
338
|
onExit(spawnRes.processId, (status) => {
|
|
335
339
|
// Map runner states to BackgroundStatus. The runner reports
|
|
336
|
-
// "starting" | "running" | "done" | "failed" | "killed";
|
|
337
|
-
// BackgroundStatus has "pending" | "running" | "done" |
|
|
338
|
-
// "failed" | "killed" | "timed_out". "starting"
|
|
339
|
-
// "running" (in-flight, no terminal state).
|
|
340
|
-
|
|
340
|
+
// "starting" | "running" | "paused" | "done" | "failed" | "killed";
|
|
341
|
+
// BackgroundStatus has "pending" | "running" | "paused" | "done" |
|
|
342
|
+
// "failed" | "killed" | "timed_out" | "steered". "starting"
|
|
343
|
+
// maps to "running" (in-flight, no terminal state). "paused"
|
|
344
|
+
// is preserved so pause/resume survives subprocess reconnects.
|
|
345
|
+
const mapped: "pending" | "running" | "paused" | "done" | "failed" | "killed" =
|
|
341
346
|
status.state === "starting" || status.state === "running"
|
|
342
347
|
? "running"
|
|
343
|
-
: status.state
|
|
348
|
+
: status.state === "paused"
|
|
349
|
+
? "paused"
|
|
350
|
+
: status.state;
|
|
344
351
|
|
|
345
352
|
const update: Parameters<InstanceManager["update"]>[1] = {
|
|
346
353
|
status: mapped,
|
|
@@ -95,5 +95,15 @@ function toViewShape(inst: import("../background-state.js").BackgroundState): In
|
|
|
95
95
|
if (inst.interventionAt !== undefined) v.interventionAt = inst.interventionAt;
|
|
96
96
|
if (inst.interventionReason !== undefined) v.interventionReason = inst.interventionReason;
|
|
97
97
|
}
|
|
98
|
+
// v5.x — extended dashboard surface.
|
|
99
|
+
if (Array.isArray(inst.toolCalls) && inst.toolCalls.length > 0) {
|
|
100
|
+
v.toolCalls = inst.toolCalls.slice();
|
|
101
|
+
}
|
|
102
|
+
if (typeof inst.progress === "number") v.progress = inst.progress;
|
|
103
|
+
if (inst.progressMessage !== undefined) v.progressMessage = inst.progressMessage;
|
|
104
|
+
if (inst.processId !== undefined) v.processId = inst.processId;
|
|
105
|
+
if (inst.runnerState !== undefined) v.runnerState = inst.runnerState;
|
|
106
|
+
if (Array.isArray(inst.tags) && inst.tags.length > 0) v.tags = inst.tags.slice();
|
|
107
|
+
if (inst.pausedAt !== undefined) v.pausedAt = inst.pausedAt;
|
|
98
108
|
return v;
|
|
99
109
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/bizar/src/tools/memory-list.ts
|
|
3
|
+
*
|
|
4
|
+
* `bizar_memory_list` tool — list notes in the Bizar Memory vault.
|
|
5
|
+
*
|
|
6
|
+
* Calls GET /api/memory/notes?prefix=... on the dashboard server.
|
|
7
|
+
* Returns an array of { path, title, updated, type } objects.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { tool } from "@opencode-ai/plugin";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
|
|
16
|
+
import type { Logger } from "../logger.js";
|
|
17
|
+
|
|
18
|
+
export interface MemoryListDeps {
|
|
19
|
+
worktree: string;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function resolveDashboardPort(): Promise<number | null> {
|
|
24
|
+
const portFile = join(homedir(), ".config", "bizar", "dashboard.port");
|
|
25
|
+
if (existsSync(portFile)) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(portFile, "utf8").trim();
|
|
28
|
+
const n = parseInt(raw, 10);
|
|
29
|
+
if (Number.isFinite(n) && n > 0 && n <= 65535) return n;
|
|
30
|
+
} catch {
|
|
31
|
+
// fall through
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const envPort = process.env.BIZAR_DASHBOARD_PORT;
|
|
35
|
+
if (envPort) {
|
|
36
|
+
const n = parseInt(envPort, 10);
|
|
37
|
+
if (Number.isFinite(n) && n > 0 && n <= 65535) return n;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createMemoryListTool(deps: MemoryListDeps) {
|
|
43
|
+
return tool({
|
|
44
|
+
description:
|
|
45
|
+
"List notes in the Bizar Memory vault, optionally filtered by path prefix. " +
|
|
46
|
+
"Returns { notes: [{ path, title, updated, type }] }. " +
|
|
47
|
+
"Available to all agents.",
|
|
48
|
+
args: {
|
|
49
|
+
prefix: z
|
|
50
|
+
.string()
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Optional vault-relative path prefix to filter results " +
|
|
53
|
+
"(e.g. 'decisions/' lists all notes under decisions/)."),
|
|
54
|
+
limit: z
|
|
55
|
+
.number()
|
|
56
|
+
.int()
|
|
57
|
+
.positive()
|
|
58
|
+
.optional()
|
|
59
|
+
.default(50)
|
|
60
|
+
.describe("Max notes to return (default 50)."),
|
|
61
|
+
},
|
|
62
|
+
execute: async (rawArgs) => {
|
|
63
|
+
const args = rawArgs as { prefix?: string; limit?: number };
|
|
64
|
+
const { logger } = deps;
|
|
65
|
+
|
|
66
|
+
const port = await resolveDashboardPort();
|
|
67
|
+
if (!port) {
|
|
68
|
+
return {
|
|
69
|
+
output: JSON.stringify({
|
|
70
|
+
error: "dashboard_not_running",
|
|
71
|
+
message: "The Bizar dashboard is not running. Start it with `bizar dash start`.",
|
|
72
|
+
notes: [],
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const params = new URLSearchParams();
|
|
78
|
+
if (args.prefix) {
|
|
79
|
+
params.set("prefix", args.prefix);
|
|
80
|
+
}
|
|
81
|
+
const limit = Math.min(args.limit ?? 50, 200);
|
|
82
|
+
params.set("limit", String(limit));
|
|
83
|
+
|
|
84
|
+
const url = `http://127.0.0.1:${port}/api/memory/notes?${params}`;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(url, { method: "GET" });
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const text = await res.text().catch(() => "");
|
|
90
|
+
logger.warn(`bizar: memory-list HTTP ${res.status}: ${text.slice(0, 200)}`);
|
|
91
|
+
return {
|
|
92
|
+
output: JSON.stringify({
|
|
93
|
+
error: "list_failed",
|
|
94
|
+
message: `HTTP ${res.status}: ${text.slice(0, 200)}`,
|
|
95
|
+
notes: [],
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const data = (await res.json()) as {
|
|
101
|
+
notes?: Array<{
|
|
102
|
+
relPath?: string;
|
|
103
|
+
frontmatter?: { title?: string; type?: string };
|
|
104
|
+
mtime?: number;
|
|
105
|
+
}>;
|
|
106
|
+
count?: number;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const notes = (data.notes ?? []).map((n) => ({
|
|
110
|
+
path: n.relPath ?? "",
|
|
111
|
+
title: n.frontmatter?.title ?? null,
|
|
112
|
+
updated: n.mtime ? new Date(n.mtime).toISOString() : null,
|
|
113
|
+
type: n.frontmatter?.type ?? null,
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
output: JSON.stringify({
|
|
118
|
+
notes,
|
|
119
|
+
count: notes.length,
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
} catch (err) {
|
|
123
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
124
|
+
logger.warn(`bizar: memory-list failed: ${msg}`);
|
|
125
|
+
return {
|
|
126
|
+
output: JSON.stringify({
|
|
127
|
+
error: "list_failed",
|
|
128
|
+
message: msg,
|
|
129
|
+
notes: [],
|
|
130
|
+
}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/bizar/src/tools/memory-read.ts
|
|
3
|
+
*
|
|
4
|
+
* `bizar_memory_read` tool — read a single note from the Bizar Memory vault.
|
|
5
|
+
*
|
|
6
|
+
* Calls GET /api/memory/notes/:path on the dashboard server.
|
|
7
|
+
*
|
|
8
|
+
* The `path` is relative to the vault root (e.g. "decisions/foo.md"
|
|
9
|
+
* or "projects/myproj/sessions/2025-07-06.md").
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { tool } from "@opencode-ai/plugin";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
|
|
18
|
+
import type { Logger } from "../logger.js";
|
|
19
|
+
|
|
20
|
+
export interface MemoryReadDeps {
|
|
21
|
+
worktree: string;
|
|
22
|
+
logger: Logger;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function resolveDashboardPort(): Promise<number | null> {
|
|
26
|
+
const portFile = join(homedir(), ".config", "bizar", "dashboard.port");
|
|
27
|
+
if (existsSync(portFile)) {
|
|
28
|
+
try {
|
|
29
|
+
const raw = readFileSync(portFile, "utf8").trim();
|
|
30
|
+
const n = parseInt(raw, 10);
|
|
31
|
+
if (Number.isFinite(n) && n > 0 && n <= 65535) return n;
|
|
32
|
+
} catch {
|
|
33
|
+
// fall through
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const envPort = process.env.BIZAR_DASHBOARD_PORT;
|
|
37
|
+
if (envPort) {
|
|
38
|
+
const n = parseInt(envPort, 10);
|
|
39
|
+
if (Number.isFinite(n) && n > 0 && n <= 65535) return n;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createMemoryReadTool(deps: MemoryReadDeps) {
|
|
45
|
+
return tool({
|
|
46
|
+
description:
|
|
47
|
+
"Read a single note from the Bizar Memory vault. " +
|
|
48
|
+
"The path is relative to the vault root (e.g. 'decisions/foo.md'). " +
|
|
49
|
+
"Returns { path, content, frontmatter, mtime } or { error } on failure. " +
|
|
50
|
+
"Available to all agents.",
|
|
51
|
+
args: {
|
|
52
|
+
path: z
|
|
53
|
+
.string()
|
|
54
|
+
.min(1)
|
|
55
|
+
.describe("Vault-relative path of the note to read (e.g. 'decisions/foo.md')."),
|
|
56
|
+
},
|
|
57
|
+
execute: async (rawArgs) => {
|
|
58
|
+
const args = rawArgs as { path: string };
|
|
59
|
+
const { logger } = deps;
|
|
60
|
+
|
|
61
|
+
// Basic path safety: no absolute paths, no traversal above vault root
|
|
62
|
+
const normalized = args.path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
63
|
+
if (normalized.includes("..") || normalized.startsWith(".")) {
|
|
64
|
+
return {
|
|
65
|
+
output: JSON.stringify({
|
|
66
|
+
error: "invalid_path",
|
|
67
|
+
message: "Path must be a simple relative vault path (no '..' or absolute paths).",
|
|
68
|
+
path: args.path,
|
|
69
|
+
}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const port = await resolveDashboardPort();
|
|
74
|
+
if (!port) {
|
|
75
|
+
return {
|
|
76
|
+
output: JSON.stringify({
|
|
77
|
+
error: "dashboard_not_running",
|
|
78
|
+
message: "The Bizar dashboard is not running. Start it with `bizar dash start`.",
|
|
79
|
+
path: args.path,
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const url = `http://127.0.0.1:${port}/api/memory/notes/${encodeURIComponent(normalized)}`;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(url, { method: "GET" });
|
|
88
|
+
|
|
89
|
+
if (res.status === 404) {
|
|
90
|
+
return {
|
|
91
|
+
output: JSON.stringify({
|
|
92
|
+
error: "not_found",
|
|
93
|
+
message: `Note not found at path: ${normalized}`,
|
|
94
|
+
path: normalized,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
const text = await res.text().catch(() => "");
|
|
101
|
+
logger.warn(`bizar: memory-read(${normalized}) HTTP ${res.status}: ${text.slice(0, 200)}`);
|
|
102
|
+
return {
|
|
103
|
+
output: JSON.stringify({
|
|
104
|
+
error: "read_failed",
|
|
105
|
+
message: `HTTP ${res.status}: ${text.slice(0, 200)}`,
|
|
106
|
+
path: normalized,
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const data = (await res.json()) as {
|
|
112
|
+
relPath?: string;
|
|
113
|
+
frontmatter?: Record<string, unknown>;
|
|
114
|
+
body?: string;
|
|
115
|
+
raw?: string;
|
|
116
|
+
mtime?: number;
|
|
117
|
+
size?: number;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
output: JSON.stringify({
|
|
122
|
+
path: data.relPath ?? normalized,
|
|
123
|
+
content: data.body ?? "",
|
|
124
|
+
frontmatter: data.frontmatter ?? {},
|
|
125
|
+
mtime: data.mtime ?? null,
|
|
126
|
+
size: data.size ?? null,
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
} catch (err) {
|
|
130
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
131
|
+
logger.warn(`bizar: memory-read(${normalized}) failed: ${msg}`);
|
|
132
|
+
return {
|
|
133
|
+
output: JSON.stringify({
|
|
134
|
+
error: "read_failed",
|
|
135
|
+
message: msg,
|
|
136
|
+
path: normalized,
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|