bazilion 0.4.0 → 0.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.
@@ -8,7 +8,6 @@
8
8
  -- agents), they don't replace them.
9
9
  --
10
10
  -- Spawn semantics live in apps/daemon/src/core/profile-group/spawn.ts.
11
- -- See docs/backlog/todo/BAZ-002-profile-groups.md for the full spec.
12
11
 
13
12
  CREATE TABLE profile_groups (
14
13
  id TEXT PRIMARY KEY, -- slug, e.g. "platform-team"
@@ -0,0 +1,39 @@
1
+ -- Backfill USER.md content into pre-existing groups.
2
+ --
3
+ -- Before this BAZ, groups.user_md was born as '' and stayed empty unless an
4
+ -- operator hand-edited it, so the "About the User" section of every system
5
+ -- prompt was generic boilerplate. New groups now seed DEFAULT_USER_MD at
6
+ -- insert time (core/repos/groups.ts); this one-shot UPDATE gives the same
7
+ -- starter content to groups that already exist with an empty user_md.
8
+ --
9
+ -- The literal below MUST be kept in sync with DEFAULT_USER_MD in
10
+ -- core/profile/templates.ts — SQL can't import it. Two copies, one source of
11
+ -- truth; flag in review if they drift.
12
+ --
13
+ -- Tradeoff: the column can't distinguish "never set"
14
+ -- from "explicitly cleared", so an operator who deliberately set user_md to ''
15
+ -- sees it replaced. bazilion is alpha; the upside (every install gets the new
16
+ -- template) beats the downside (a power-user re-clears one file). Idempotent:
17
+ -- the WHERE clause matches nothing once every row is non-empty.
18
+
19
+ UPDATE groups SET user_md = '# USER.md — About Your Human
20
+
21
+ _Learn about the person you''re helping. Update via `user_md_write` as you go
22
+ (read with `user_md_get` first for the etag)._
23
+
24
+ - **Name:**
25
+ - **What to call them:**
26
+ - **Pronouns:** _(optional)_
27
+ - **Timezone:**
28
+ - **Notes:**
29
+
30
+ ## Context
31
+
32
+ _(What do they care about? What projects are they working on? What''s their
33
+ working style? Build this picture over time, a little each session.)_
34
+
35
+ ---
36
+
37
+ The more you know, the better you can help. But remember — you''re learning
38
+ about a person, not building a dossier. Respect the difference.
39
+ ' WHERE user_md = '';
package/dist/worker.js CHANGED
@@ -2,22 +2,29 @@
2
2
 
3
3
  // ../daemon/src/runtime/worker/entry.ts
4
4
  import { randomUUID as randomUUID6 } from "crypto";
5
- import { join as join14 } from "path";
5
+ import { join as join16 } from "path";
6
6
 
7
7
  // ../daemon/src/core/agent/delete.ts
8
8
  import { existsSync, rmSync } from "fs";
9
9
 
10
+ // ../daemon/src/core/agent/resolve.ts
11
+ import { join } from "path";
12
+
13
+ // ../daemon/src/core/profile/identity.ts
14
+ import { readFileSync } from "fs";
15
+
10
16
  // ../daemon/src/core/agent/spawn.ts
11
17
  import { randomUUID } from "crypto";
12
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
13
- import { join as join4 } from "path";
18
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
19
+ import { join as join6 } from "path";
14
20
 
15
21
  // ../daemon/src/core/profile/load.ts
16
22
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
17
- import { join } from "path";
23
+ import { join as join2 } from "path";
18
24
 
19
- // ../daemon/src/core/profile/identity.ts
20
- import { readFileSync } from "fs";
25
+ // ../daemon/src/core/profile/seed.ts
26
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
27
+ import { join as join4 } from "path";
21
28
 
22
29
  // ../daemon/src/core/group/register.ts
23
30
  import { existsSync as existsSync3, mkdirSync, statSync, symlinkSync } from "fs";
@@ -25,56 +32,56 @@ import { resolve } from "path";
25
32
 
26
33
  // ../daemon/src/core/profile/create.ts
27
34
  import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
28
- import { join as join2 } from "path";
35
+ import { join as join3 } from "path";
29
36
 
30
37
  // ../daemon/src/core/skills/discover.ts
31
- import { existsSync as existsSync4, readdirSync } from "fs";
32
- import { join as join3 } from "path";
38
+ import { existsSync as existsSync5, readdirSync } from "fs";
39
+ import { join as join5 } from "path";
33
40
 
34
41
  // ../daemon/src/core/db/client.ts
35
42
  import { DatabaseSync } from "node:sqlite";
36
43
 
37
44
  // ../daemon/src/core/db/migrate.ts
38
- import { readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
39
- import { dirname, join as join5 } from "path";
45
+ import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
46
+ import { dirname, join as join7 } from "path";
40
47
  import { fileURLToPath } from "url";
41
- var migrationsDir = join5(dirname(fileURLToPath(import.meta.url)), "migrations");
48
+ var migrationsDir = join7(dirname(fileURLToPath(import.meta.url)), "migrations");
42
49
 
43
50
  // ../daemon/src/core/paths.ts
44
51
  import { homedir } from "os";
45
- import { join as join6 } from "path";
52
+ import { join as join8 } from "path";
46
53
  function resolvePaths(home) {
47
- const root = home ?? process.env.BAZILION_HOME ?? join6(homedir(), ".bazilion");
54
+ const root = home ?? process.env.BAZILION_HOME ?? join8(homedir(), ".bazilion");
48
55
  return {
49
56
  home: root,
50
- db: join6(root, "bazilion.db"),
51
- authFile: join6(root, "auth.json"),
52
- profilesDir: join6(root, "profiles"),
53
- agentsDir: join6(root, "agents"),
54
- skillsDir: join6(root, "skills"),
55
- groupsDir: join6(root, "groups"),
56
- logsDir: join6(root, "logs"),
57
+ db: join8(root, "bazilion.db"),
58
+ authFile: join8(root, "auth.json"),
59
+ profilesDir: join8(root, "profiles"),
60
+ agentsDir: join8(root, "agents"),
61
+ skillsDir: join8(root, "skills"),
62
+ groupsDir: join8(root, "groups"),
63
+ logsDir: join8(root, "logs"),
57
64
  profileDir(id) {
58
- return join6(root, "profiles", id);
65
+ return join8(root, "profiles", id);
59
66
  },
60
67
  agentDir(id) {
61
- return join6(root, "agents", id);
68
+ return join8(root, "agents", id);
62
69
  },
63
70
  skillDir(name) {
64
- return join6(root, "skills", name);
71
+ return join8(root, "skills", name);
65
72
  },
66
73
  groupDir(slug) {
67
- return join6(root, "groups", slug);
74
+ return join8(root, "groups", slug);
68
75
  }
69
76
  };
70
77
  }
71
78
 
72
79
  // ../daemon/src/core/profile/delete.ts
73
- import { existsSync as existsSync5, rmSync as rmSync2 } from "fs";
80
+ import { existsSync as existsSync6, rmSync as rmSync2 } from "fs";
74
81
 
75
82
  // ../daemon/src/core/profile/update.ts
76
- import { writeFileSync as writeFileSync3 } from "fs";
77
- import { join as join7 } from "path";
83
+ import { writeFileSync as writeFileSync4 } from "fs";
84
+ import { join as join9 } from "path";
78
85
 
79
86
  // ../daemon/src/core/profile-group/spawn.ts
80
87
  import { readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
@@ -536,29 +543,29 @@ import { randomUUID as randomUUID4 } from "crypto";
536
543
  import { createHash, randomBytes as randomBytes2, randomUUID as randomUUID5 } from "crypto";
537
544
 
538
545
  // ../daemon/src/core/secrets.ts
539
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
546
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
540
547
 
541
548
  // ../daemon/src/core/skills/import.ts
542
- import { cpSync, existsSync as existsSync7, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync2 } from "fs";
549
+ import { cpSync, existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync2 } from "fs";
543
550
  import { tmpdir } from "os";
544
- import { basename, join as join8, resolve as resolve2, sep } from "path";
551
+ import { basename, join as join10, resolve as resolve2, sep } from "path";
545
552
  import AdmZip from "adm-zip";
546
553
 
547
554
  // ../daemon/src/core/skills/parse.ts
548
- import { readFileSync as readFileSync5 } from "fs";
555
+ import { readFileSync as readFileSync6 } from "fs";
549
556
  import { parse as parseYaml } from "yaml";
550
557
 
551
558
  // ../daemon/src/runtime/memory/qmd.ts
552
559
  import {
553
- existsSync as existsSync8,
560
+ existsSync as existsSync9,
554
561
  mkdirSync as mkdirSync4,
555
562
  readdirSync as readdirSync5,
556
- readFileSync as readFileSync6,
563
+ readFileSync as readFileSync7,
557
564
  rmSync as rmSync6,
558
565
  statSync as statSync3,
559
- writeFileSync as writeFileSync4
566
+ writeFileSync as writeFileSync5
560
567
  } from "fs";
561
- import { dirname as dirname2, join as join9 } from "path";
568
+ import { dirname as dirname2, join as join11 } from "path";
562
569
  import { createStore, extractSnippet } from "@tobilu/qmd";
563
570
  var INDEX_FILENAME = ".qmd-index.sqlite";
564
571
  var COLLECTION_NAME = "memory";
@@ -568,7 +575,7 @@ function getStore(dir) {
568
575
  let p = storeCache.get(dir);
569
576
  if (!p) {
570
577
  p = createStore({
571
- dbPath: join9(dir, INDEX_FILENAME),
578
+ dbPath: join11(dir, INDEX_FILENAME),
572
579
  config: {
573
580
  collections: {
574
581
  [COLLECTION_NAME]: { path: dir, pattern: PATTERN }
@@ -583,13 +590,13 @@ function safeKey(root, key) {
583
590
  if (key.includes("..") || key.startsWith("/") || key.includes("\0")) {
584
591
  throw new Error(`unsafe memory key: ${key}`);
585
592
  }
586
- return join9(root, key);
593
+ return join11(root, key);
587
594
  }
588
595
  function walkMd(dir, prefix, out) {
589
- if (!existsSync8(dir)) return;
596
+ if (!existsSync9(dir)) return;
590
597
  for (const e of readdirSync5(dir, { withFileTypes: true })) {
591
598
  if (e.name.startsWith(".")) continue;
592
- const full = join9(dir, e.name);
599
+ const full = join11(dir, e.name);
593
600
  const key = prefix ? `${prefix}/${e.name}` : e.name;
594
601
  if (e.isDirectory()) {
595
602
  walkMd(full, key, out);
@@ -597,7 +604,7 @@ function walkMd(dir, prefix, out) {
597
604
  const stats = statSync3(full);
598
605
  out.push({
599
606
  key,
600
- content: readFileSync6(full, "utf8"),
607
+ content: readFileSync7(full, "utf8"),
601
608
  updatedAt: stats.mtimeMs
602
609
  });
603
610
  }
@@ -612,20 +619,20 @@ function qmdBackend(root) {
612
619
  },
613
620
  async read(key) {
614
621
  const path = safeKey(root, key);
615
- if (!existsSync8(path)) {
622
+ if (!existsSync9(path)) {
616
623
  throw new Error(`memory entry not found: ${key}`);
617
624
  }
618
625
  const stats = statSync3(path);
619
626
  return {
620
627
  key,
621
- content: readFileSync6(path, "utf8"),
628
+ content: readFileSync7(path, "utf8"),
622
629
  updatedAt: stats.mtimeMs
623
630
  };
624
631
  },
625
632
  async write(key, content) {
626
633
  const path = safeKey(root, key);
627
634
  mkdirSync4(dirname2(path), { recursive: true });
628
- writeFileSync4(path, content);
635
+ writeFileSync5(path, content);
629
636
  const stats = statSync3(path);
630
637
  const store = await getStore(root);
631
638
  await store.update();
@@ -645,7 +652,7 @@ function qmdBackend(root) {
645
652
  let content = r.body ?? "";
646
653
  if (!content) {
647
654
  try {
648
- content = readFileSync6(join9(root, key), "utf8");
655
+ content = readFileSync7(join11(root, key), "utf8");
649
656
  } catch {
650
657
  content = "";
651
658
  }
@@ -662,7 +669,7 @@ function qmdBackend(root) {
662
669
  },
663
670
  async remove(key) {
664
671
  const path = safeKey(root, key);
665
- if (existsSync8(path)) rmSync6(path);
672
+ if (existsSync9(path)) rmSync6(path);
666
673
  const store = await getStore(root);
667
674
  await store.update();
668
675
  }
@@ -819,8 +826,8 @@ function piMessagesToProviderView(messages) {
819
826
  }
820
827
 
821
828
  // ../daemon/src/runtime/pi/session.ts
822
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
823
- import { basename as basename3, join as join13 } from "path";
829
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
830
+ import { basename as basename3, join as join15 } from "path";
824
831
  import {
825
832
  AuthStorage,
826
833
  createAgentSession,
@@ -880,8 +887,8 @@ function resolveModel(cfg, modelId) {
880
887
  import { loginOpenAICodex, refreshOpenAICodexToken } from "@earendil-works/pi-ai/oauth";
881
888
 
882
889
  // ../daemon/src/runtime/session/prompt.ts
883
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
884
- import { join as join10 } from "path";
890
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
891
+ import { join as join12 } from "path";
885
892
  var CONTEXT_FILE_ORDER = [
886
893
  "AGENTS.md",
887
894
  "SOUL.md",
@@ -893,9 +900,9 @@ function buildSystemPrompt(agent) {
893
900
  const parts = [];
894
901
  const contextBlocks = [];
895
902
  for (const file of CONTEXT_FILE_ORDER) {
896
- const path = join10(agent.agent.dir, file);
897
- if (!existsSync9(path)) continue;
898
- const content = readFileSync7(path, "utf8").trimEnd();
903
+ const path = join12(agent.agent.dir, file);
904
+ if (!existsSync10(path)) continue;
905
+ const content = readFileSync8(path, "utf8").trimEnd();
899
906
  if (!content) continue;
900
907
  contextBlocks.push(`## ${file}
901
908
 
@@ -906,9 +913,9 @@ ${content}`);
906
913
 
907
914
  ${contextBlocks.join("\n\n")}`);
908
915
  }
909
- const bootstrapPath = join10(agent.agent.dir, "BOOTSTRAP.md");
910
- if (existsSync9(bootstrapPath)) {
911
- const bootstrap = readFileSync7(bootstrapPath, "utf8").trimEnd();
916
+ const bootstrapPath = join12(agent.agent.dir, "BOOTSTRAP.md");
917
+ if (existsSync10(bootstrapPath)) {
918
+ const bootstrap = readFileSync8(bootstrapPath, "utf8").trimEnd();
912
919
  if (bootstrap) {
913
920
  parts.push(
914
921
  [
@@ -980,8 +987,8 @@ This group's USER.md is empty. As you learn STABLE facts about the human (prefer
980
987
  import { Type as Type2 } from "typebox";
981
988
 
982
989
  // ../daemon/src/runtime/tools/bootstrap.ts
983
- import { existsSync as existsSync10, rmSync as rmSync7 } from "fs";
984
- import { join as join11 } from "path";
990
+ import { existsSync as existsSync11, rmSync as rmSync7 } from "fs";
991
+ import { join as join13 } from "path";
985
992
  function bootstrapTool(agentDir) {
986
993
  return {
987
994
  def: {
@@ -990,8 +997,8 @@ function bootstrapTool(agentDir) {
990
997
  parameters: { type: "object", properties: {} }
991
998
  },
992
999
  async invoke() {
993
- const path = join11(agentDir, "BOOTSTRAP.md");
994
- if (existsSync10(path)) {
1000
+ const path = join13(agentDir, "BOOTSTRAP.md");
1001
+ if (existsSync11(path)) {
995
1002
  rmSync7(path);
996
1003
  return "BOOTSTRAP.md removed. Bootstrap is complete.";
997
1004
  }
@@ -1130,7 +1137,7 @@ function browserTools(host, agentId) {
1130
1137
  }
1131
1138
 
1132
1139
  // ../daemon/src/runtime/tools/deliver-file.ts
1133
- import { readFileSync as readFileSync8, statSync as statSync4 } from "fs";
1140
+ import { readFileSync as readFileSync9, statSync as statSync4 } from "fs";
1134
1141
  import { basename as basename2, extname, isAbsolute, resolve as resolve3 } from "path";
1135
1142
  var MAX_DELIVER_BYTES = 25 * 1024 * 1024;
1136
1143
  var MIME = {
@@ -1181,15 +1188,15 @@ function deliverFileTool(cwd, sink) {
1181
1188
  }
1182
1189
  const name = basename2(abs);
1183
1190
  const mimeType = MIME[extname(abs).toLowerCase()] ?? "application/octet-stream";
1184
- sink({ name, mimeType, data: readFileSync8(abs).toString("base64") });
1191
+ sink({ name, mimeType, data: readFileSync9(abs).toString("base64") });
1185
1192
  return `Delivered "${name}" (${mimeType}) to the user.`;
1186
1193
  }
1187
1194
  };
1188
1195
  }
1189
1196
 
1190
1197
  // ../daemon/src/runtime/tools/home.ts
1191
- import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
1192
- import { join as join12 } from "path";
1198
+ import { readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
1199
+ import { join as join14 } from "path";
1193
1200
  var HOME_FILES_READABLE = [
1194
1201
  "IDENTITY.md",
1195
1202
  "SOUL.md",
@@ -1226,9 +1233,9 @@ function homeTools(agentDir) {
1226
1233
  `home_read: "file" must be one of ${HOME_FILES_READABLE.join(", ")}; got "${file}"`
1227
1234
  );
1228
1235
  }
1229
- const path = join12(agentDir, file);
1236
+ const path = join14(agentDir, file);
1230
1237
  try {
1231
- return readFileSync9(path, "utf8");
1238
+ return readFileSync10(path, "utf8");
1232
1239
  } catch (err) {
1233
1240
  const msg = err instanceof Error ? err.message : String(err);
1234
1241
  throw new Error(`home_read: could not read ${file}: ${msg}`);
@@ -1256,8 +1263,8 @@ function homeTools(agentDir) {
1256
1263
  );
1257
1264
  }
1258
1265
  const content = typeof args.content === "string" ? args.content : "";
1259
- const path = join12(agentDir, file);
1260
- writeFileSync5(path, content, "utf8");
1266
+ const path = join14(agentDir, file);
1267
+ writeFileSync6(path, content, "utf8");
1261
1268
  return `wrote ${file} (${Buffer.byteLength(content, "utf8")} bytes)`;
1262
1269
  }
1263
1270
  },
@@ -1270,7 +1277,7 @@ function homeTools(agentDir) {
1270
1277
  async invoke() {
1271
1278
  const entries = [];
1272
1279
  for (const file of HOME_FILES_READABLE) {
1273
- const path = join12(agentDir, file);
1280
+ const path = join14(agentDir, file);
1274
1281
  try {
1275
1282
  const s = statSync5(path);
1276
1283
  entries.push(`${file} (${s.size}b)`);
@@ -2215,8 +2222,8 @@ async function createBazilionSession(opts) {
2215
2222
  });
2216
2223
  }
2217
2224
  const cwd = agent.group.path;
2218
- if (!existsSync11(cwd)) mkdirSync5(cwd, { recursive: true });
2219
- const sessionDir = join13(paths.agentDir(agent.agent.id), "sessions");
2225
+ if (!existsSync12(cwd)) mkdirSync5(cwd, { recursive: true });
2226
+ const sessionDir = join15(paths.agentDir(agent.agent.id), "sessions");
2220
2227
  mkdirSync5(sessionDir, { recursive: true });
2221
2228
  const existing = findMostRecent(sessionDir);
2222
2229
  const sessionManager = existing ? SessionManager.open(existing, sessionDir, cwd) : SessionManager.create(cwd, sessionDir);
@@ -2246,7 +2253,7 @@ async function createBazilionSession(opts) {
2246
2253
  const allowedTools = [...BUILTIN_TOOL_NAMES, ...customTools.map((t) => t.name)];
2247
2254
  const { session } = await createAgentSession({
2248
2255
  cwd,
2249
- agentDir: join13(paths.home, "pi"),
2256
+ agentDir: join15(paths.home, "pi"),
2250
2257
  model,
2251
2258
  thinkingLevel: toPiThinkingLevel(agent.reasoningLevel),
2252
2259
  tools: allowedTools,
@@ -2372,11 +2379,11 @@ function createBazilionResourceLoader(appendSystemPrompt) {
2372
2379
  };
2373
2380
  }
2374
2381
  function findMostRecent(sessionDir) {
2375
- if (!existsSync11(sessionDir)) return null;
2382
+ if (!existsSync12(sessionDir)) return null;
2376
2383
  let newest = null;
2377
2384
  for (const entry of readdirSync7(sessionDir)) {
2378
2385
  if (!entry.endsWith(".jsonl")) continue;
2379
- const path = join13(sessionDir, entry);
2386
+ const path = join15(sessionDir, entry);
2380
2387
  try {
2381
2388
  const s = statSync6(path);
2382
2389
  if (!newest || s.mtimeMs > newest.mtimeMs) newest = { path, mtimeMs: s.mtimeMs };
@@ -2484,7 +2491,7 @@ async function main() {
2484
2491
  process.on("SIGINT", onSignal);
2485
2492
  const { agent, message, enabledProviders, apiKey, browserEnabled, mcpTools, images } = await readInput();
2486
2493
  const paths = resolvePaths();
2487
- const memory = qmdBackend(join14(agent.group.path, "memory"));
2494
+ const memory = qmdBackend(join16(agent.group.path, "memory"));
2488
2495
  await memory.init();
2489
2496
  const ipcCall = createIpcClient();
2490
2497
  const messagingHost = createIpcMessagingHost(ipcCall);