kaizen-agent 1.21.0 → 1.22.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.
Files changed (2) hide show
  1. package/cli/web.ts +15 -6
  2. package/package.json +1 -1
package/cli/web.ts CHANGED
@@ -18,10 +18,17 @@ const NOTES_JS = join(dirname(import.meta.dir), "web", "notebook.js");
18
18
  const DEFAULT_PORT = 7420;
19
19
  // Read once at start: a board keeps the code it started with, so this is what it
20
20
  // runs, not what is installed now. `/` sends it; `current()` compares the two.
21
- const VERSION = (() => {
21
+ const installed = () => {
22
22
  try { return JSON.parse(readText(join(dirname(import.meta.dir), "package.json"))).version as string; }
23
23
  catch { return "unknown"; }
24
- })();
24
+ };
25
+ const VERSION = installed();
26
+ // An upgrade replaces the page files under a running board but not its code, and a
27
+ // page newer than its server sends ops the server does not know ("unknown op").
28
+ // So the pages are kept as they were at start, and read live (dev edits) only while
29
+ // the installed version is still the one this board runs.
30
+ const AT_START = new Map([PAGE, NOTES_PAGE, NOTES_JS].map((f) => [f, existsSync(f) ? readText(f) : null]));
31
+ const served = (f: string) => installed() === VERSION && existsSync(f) ? readText(f) : AT_START.get(f) ?? null;
25
32
 
26
33
  // A board answers at url and runs the installed version. A board from before the
27
34
  // version header sends none, so it counts as stale too.
@@ -211,17 +218,19 @@ export async function web(repoDir: string, opts: Opts = {}) {
211
218
  const path = url.pathname;
212
219
 
213
220
  if (req.method === "GET" && path === "/") {
214
- if (!existsSync(PAGE)) return new Response("web/board.html missing", { status: 500 });
221
+ const page = served(PAGE);
222
+ if (page === null) return new Response("web/board.html missing", { status: 500 });
215
223
  // The page gets the notify rule from state.ts rather than keeping its own copy.
216
- return new Response(readText(PAGE).replace("/*notifier*/", () => notifier.toString()), {
224
+ return new Response(page.replace("/*notifier*/", () => notifier.toString()), {
217
225
  headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "x-kaizen-version": VERSION },
218
226
  });
219
227
  }
220
228
 
221
229
  if (req.method === "GET" && (path === "/notebook" || path === "/notebook.js")) {
222
230
  const [f, type] = path === "/notebook" ? [NOTES_PAGE, "text/html; charset=utf-8"] : [NOTES_JS, "application/javascript; charset=utf-8"];
223
- if (!existsSync(f)) return new Response(`${path} missing; run bun run build:web`, { status: 500 });
224
- return new Response(Bun.file(f), { headers: { "content-type": type, "cache-control": "no-store" } });
231
+ const body = served(f);
232
+ if (body === null) return new Response(`${path} missing; run bun run build:web`, { status: 500 });
233
+ return new Response(body, { headers: { "content-type": type, "cache-control": "no-store" } });
225
234
  }
226
235
 
227
236
  // Every note of one project, text included: search, tags and backlinks are the page's.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "Staged workflow for AI agents — code, writing, research, operations: plan, human approval, build, independent review, bounded fix loop.",
5
5
  "type": "module",
6
6
  "bin": {