bazilion 0.0.0 → 0.1.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 (265) hide show
  1. package/README.md +53 -152
  2. package/dist/cli.js +2730 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/daemon.js +5888 -0
  5. package/dist/daemon.js.map +1 -0
  6. package/dist/worker.js +1830 -0
  7. package/dist/worker.js.map +1 -0
  8. package/package.json +57 -28
  9. package/.understand-anything/.understandignore +0 -25
  10. package/.understand-anything/fingerprints.json +0 -14267
  11. package/.understand-anything/knowledge-graph.json +0 -18128
  12. package/.understand-anything/meta.json +0 -6
  13. package/CLAUDE.md +0 -164
  14. package/apps/cli/package.json +0 -21
  15. package/apps/cli/src/auth-file.ts +0 -18
  16. package/apps/cli/src/client.ts +0 -37
  17. package/apps/cli/src/columnize.ts +0 -32
  18. package/apps/cli/src/commands/agent.ts +0 -574
  19. package/apps/cli/src/commands/auth.ts +0 -110
  20. package/apps/cli/src/commands/backup.ts +0 -135
  21. package/apps/cli/src/commands/completion.ts +0 -155
  22. package/apps/cli/src/commands/config.ts +0 -95
  23. package/apps/cli/src/commands/doctor.ts +0 -131
  24. package/apps/cli/src/commands/group.ts +0 -132
  25. package/apps/cli/src/commands/inbox.ts +0 -82
  26. package/apps/cli/src/commands/login.ts +0 -73
  27. package/apps/cli/src/commands/memory.ts +0 -106
  28. package/apps/cli/src/commands/profile.ts +0 -259
  29. package/apps/cli/src/commands/provider.ts +0 -170
  30. package/apps/cli/src/commands/send.ts +0 -24
  31. package/apps/cli/src/commands/serve.ts +0 -89
  32. package/apps/cli/src/commands/skill.ts +0 -120
  33. package/apps/cli/src/commands/token.ts +0 -148
  34. package/apps/cli/src/commands/trigger.ts +0 -129
  35. package/apps/cli/src/commands/uninstall.ts +0 -156
  36. package/apps/cli/src/index.ts +0 -196
  37. package/apps/cli/src/paths.ts +0 -12
  38. package/apps/cli/test/agent.test.ts +0 -213
  39. package/apps/cli/test/backup.test.ts +0 -95
  40. package/apps/cli/test/chat.test.ts +0 -539
  41. package/apps/cli/test/columnize.test.ts +0 -29
  42. package/apps/cli/test/completion.test.ts +0 -45
  43. package/apps/cli/test/config-page.test.ts +0 -151
  44. package/apps/cli/test/group.test.ts +0 -74
  45. package/apps/cli/test/helpers.ts +0 -70
  46. package/apps/cli/test/inbox-autodeliver.test.ts +0 -143
  47. package/apps/cli/test/inbox.test.ts +0 -147
  48. package/apps/cli/test/memory.test.ts +0 -69
  49. package/apps/cli/test/profile.test.ts +0 -110
  50. package/apps/cli/test/send.test.ts +0 -30
  51. package/apps/cli/test/server-fixture.ts +0 -212
  52. package/apps/cli/test/session-head.test.ts +0 -45
  53. package/apps/cli/test/skill.test.ts +0 -128
  54. package/apps/cli/test/token.test.ts +0 -109
  55. package/apps/cli/test/trigger.test.ts +0 -245
  56. package/apps/cli/tsconfig.json +0 -4
  57. package/apps/daemon/package.json +0 -31
  58. package/apps/daemon/src/app.ts +0 -41
  59. package/apps/daemon/src/core/agent/archive.ts +0 -8
  60. package/apps/daemon/src/core/agent/delete.ts +0 -30
  61. package/apps/daemon/src/core/agent/resolve.ts +0 -31
  62. package/apps/daemon/src/core/agent/spawn.ts +0 -95
  63. package/apps/daemon/src/core/agent/unarchive.ts +0 -11
  64. package/apps/daemon/src/core/availableModels.ts +0 -58
  65. package/apps/daemon/src/core/db/client.ts +0 -113
  66. package/apps/daemon/src/core/db/migrate.ts +0 -41
  67. package/apps/daemon/src/core/db/migrations/0001_init.sql +0 -179
  68. package/apps/daemon/src/core/group/delete.ts +0 -21
  69. package/apps/daemon/src/core/group/register.ts +0 -68
  70. package/apps/daemon/src/core/index.ts +0 -71
  71. package/apps/daemon/src/core/paths.ts +0 -56
  72. package/apps/daemon/src/core/profile/create.ts +0 -78
  73. package/apps/daemon/src/core/profile/delete.ts +0 -26
  74. package/apps/daemon/src/core/profile/identity.ts +0 -70
  75. package/apps/daemon/src/core/profile/load.ts +0 -45
  76. package/apps/daemon/src/core/profile/seed.ts +0 -74
  77. package/apps/daemon/src/core/profile/templates.ts +0 -60
  78. package/apps/daemon/src/core/profile/update.ts +0 -57
  79. package/apps/daemon/src/core/profile/validate.ts +0 -9
  80. package/apps/daemon/src/core/repos/agents.ts +0 -202
  81. package/apps/daemon/src/core/repos/config.ts +0 -78
  82. package/apps/daemon/src/core/repos/groups.ts +0 -55
  83. package/apps/daemon/src/core/repos/messages.ts +0 -127
  84. package/apps/daemon/src/core/repos/profiles.ts +0 -83
  85. package/apps/daemon/src/core/repos/providerModels.ts +0 -58
  86. package/apps/daemon/src/core/repos/providerState.ts +0 -37
  87. package/apps/daemon/src/core/repos/secrets.ts +0 -145
  88. package/apps/daemon/src/core/repos/skillMeta.ts +0 -49
  89. package/apps/daemon/src/core/repos/triggers.ts +0 -101
  90. package/apps/daemon/src/core/repos/webTokens.ts +0 -87
  91. package/apps/daemon/src/core/secrets.ts +0 -65
  92. package/apps/daemon/src/core/services.ts +0 -264
  93. package/apps/daemon/src/core/skills/discover.ts +0 -28
  94. package/apps/daemon/src/core/skills/import.ts +0 -136
  95. package/apps/daemon/src/core/skills/parse.ts +0 -52
  96. package/apps/daemon/src/core/skills/resolve.ts +0 -50
  97. package/apps/daemon/src/index.ts +0 -45
  98. package/apps/daemon/src/lib/agent-cancel.ts +0 -48
  99. package/apps/daemon/src/lib/agent-id.ts +0 -13
  100. package/apps/daemon/src/lib/agent-turn.ts +0 -56
  101. package/apps/daemon/src/lib/api-key.ts +0 -56
  102. package/apps/daemon/src/lib/auth.ts +0 -41
  103. package/apps/daemon/src/lib/cron.ts +0 -93
  104. package/apps/daemon/src/lib/ctx.ts +0 -80
  105. package/apps/daemon/src/lib/messaging-host.ts +0 -34
  106. package/apps/daemon/src/lib/middleware-auth.ts +0 -52
  107. package/apps/daemon/src/lib/scheduler.ts +0 -294
  108. package/apps/daemon/src/routes/agents.ts +0 -772
  109. package/apps/daemon/src/routes/auth-login.ts +0 -193
  110. package/apps/daemon/src/routes/config.ts +0 -267
  111. package/apps/daemon/src/routes/groups.ts +0 -133
  112. package/apps/daemon/src/routes/messages.ts +0 -29
  113. package/apps/daemon/src/routes/misc.ts +0 -239
  114. package/apps/daemon/src/routes/profiles.ts +0 -197
  115. package/apps/daemon/src/routes/skills.ts +0 -123
  116. package/apps/daemon/src/routes/triggers.ts +0 -29
  117. package/apps/daemon/src/runtime/auth/openai-codex.ts +0 -121
  118. package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +0 -31
  119. package/apps/daemon/src/runtime/index.ts +0 -77
  120. package/apps/daemon/src/runtime/memory/files.ts +0 -103
  121. package/apps/daemon/src/runtime/memory/qmd.ts +0 -152
  122. package/apps/daemon/src/runtime/memory/types.ts +0 -16
  123. package/apps/daemon/src/runtime/pi/events.ts +0 -173
  124. package/apps/daemon/src/runtime/pi/session.ts +0 -536
  125. package/apps/daemon/src/runtime/pi/tools.ts +0 -85
  126. package/apps/daemon/src/runtime/providers/catalog.ts +0 -145
  127. package/apps/daemon/src/runtime/providers/pi-adapter.ts +0 -272
  128. package/apps/daemon/src/runtime/providers/registry.ts +0 -374
  129. package/apps/daemon/src/runtime/providers/retry.ts +0 -176
  130. package/apps/daemon/src/runtime/providers/types.ts +0 -33
  131. package/apps/daemon/src/runtime/session/prompt.ts +0 -83
  132. package/apps/daemon/src/runtime/tools/bootstrap.ts +0 -22
  133. package/apps/daemon/src/runtime/tools/home.ts +0 -114
  134. package/apps/daemon/src/runtime/tools/memory.ts +0 -81
  135. package/apps/daemon/src/runtime/tools/messaging.ts +0 -127
  136. package/apps/daemon/src/runtime/tools/registry.ts +0 -29
  137. package/apps/daemon/src/runtime/tools/types.ts +0 -13
  138. package/apps/daemon/src/runtime/tools/web-extract.ts +0 -110
  139. package/apps/daemon/src/runtime/tools/web-ssrf.ts +0 -245
  140. package/apps/daemon/src/runtime/tools/web.ts +0 -273
  141. package/apps/daemon/src/runtime/worker/entry.ts +0 -221
  142. package/apps/daemon/src/runtime/worker/ipc-protocol.ts +0 -75
  143. package/apps/daemon/src/runtime/worker/spawn.ts +0 -249
  144. package/apps/daemon/test/core/agents.test.ts +0 -319
  145. package/apps/daemon/test/core/available-models.test.ts +0 -63
  146. package/apps/daemon/test/core/config.test.ts +0 -99
  147. package/apps/daemon/test/core/groups.test.ts +0 -63
  148. package/apps/daemon/test/core/helpers.ts +0 -50
  149. package/apps/daemon/test/core/identity.test.ts +0 -85
  150. package/apps/daemon/test/core/migrations.test.ts +0 -83
  151. package/apps/daemon/test/core/profiles.test.ts +0 -182
  152. package/apps/daemon/test/core/provider-models.test.ts +0 -57
  153. package/apps/daemon/test/core/provider-state.test.ts +0 -36
  154. package/apps/daemon/test/core/skill-meta.test.ts +0 -45
  155. package/apps/daemon/test/core/skills.test.ts +0 -271
  156. package/apps/daemon/test/core/triggers.test.ts +0 -182
  157. package/apps/daemon/test/core/web-tokens.test.ts +0 -79
  158. package/apps/daemon/test/cron.test.ts +0 -90
  159. package/apps/daemon/test/runtime/heartbeat.test.ts +0 -34
  160. package/apps/daemon/test/runtime/memory-qmd.test.ts +0 -97
  161. package/apps/daemon/test/runtime/memory.test.ts +0 -78
  162. package/apps/daemon/test/runtime/messaging.test.ts +0 -213
  163. package/apps/daemon/test/runtime/mock-server.ts +0 -65
  164. package/apps/daemon/test/runtime/openai-codex-auth.test.ts +0 -116
  165. package/apps/daemon/test/runtime/providers.test.ts +0 -306
  166. package/apps/daemon/test/runtime/retry.test.ts +0 -191
  167. package/apps/daemon/test/runtime/session-head.test.ts +0 -90
  168. package/apps/daemon/test/runtime/tools-home.test.ts +0 -102
  169. package/apps/daemon/test/runtime/tools-web.test.ts +0 -206
  170. package/apps/daemon/tsconfig.json +0 -4
  171. package/apps/mobile/README.md +0 -60
  172. package/apps/mobile/app/_layout.tsx +0 -58
  173. package/apps/mobile/app/agents/[id]/chat.tsx +0 -486
  174. package/apps/mobile/app/agents/[id]/index.tsx +0 -166
  175. package/apps/mobile/app/agents/index.tsx +0 -212
  176. package/apps/mobile/app/index.tsx +0 -21
  177. package/apps/mobile/app/pair.tsx +0 -226
  178. package/apps/mobile/app/settings.tsx +0 -419
  179. package/apps/mobile/app.json +0 -49
  180. package/apps/mobile/assets/adaptive-icon.png +0 -0
  181. package/apps/mobile/assets/favicon.png +0 -0
  182. package/apps/mobile/assets/icon.png +0 -0
  183. package/apps/mobile/assets/splash-icon.png +0 -0
  184. package/apps/mobile/babel.config.js +0 -6
  185. package/apps/mobile/metro.config.js +0 -28
  186. package/apps/mobile/package.json +0 -44
  187. package/apps/mobile/src/auth.ts +0 -66
  188. package/apps/mobile/src/pair-url.ts +0 -48
  189. package/apps/mobile/src/theme-context.tsx +0 -88
  190. package/apps/mobile/src/theme.ts +0 -135
  191. package/apps/mobile/test/pair-url.test.ts +0 -46
  192. package/apps/mobile/tsconfig.json +0 -23
  193. package/apps/web/components.json +0 -25
  194. package/apps/web/package.json +0 -39
  195. package/apps/web/public/baziu.svg +0 -8
  196. package/apps/web/src/components/AgentTabs.tsx +0 -45
  197. package/apps/web/src/components/BaziuLogo.tsx +0 -21
  198. package/apps/web/src/components/ChatPane.tsx +0 -1033
  199. package/apps/web/src/components/ConfigTabs.tsx +0 -29
  200. package/apps/web/src/components/CopyButton.tsx +0 -68
  201. package/apps/web/src/components/CreateGroupDialog.tsx +0 -127
  202. package/apps/web/src/components/FieldRow.tsx +0 -94
  203. package/apps/web/src/components/Footer.tsx +0 -10
  204. package/apps/web/src/components/PawIcon.tsx +0 -15
  205. package/apps/web/src/components/Sidebar.tsx +0 -287
  206. package/apps/web/src/components/SpawnDialog.tsx +0 -129
  207. package/apps/web/src/components/ThemeToggle.tsx +0 -75
  208. package/apps/web/src/components/TopNav.tsx +0 -34
  209. package/apps/web/src/components/ui/button.tsx +0 -67
  210. package/apps/web/src/components/ui/card.tsx +0 -103
  211. package/apps/web/src/components/ui/checkbox.tsx +0 -31
  212. package/apps/web/src/components/ui/dialog.tsx +0 -168
  213. package/apps/web/src/components/ui/input.tsx +0 -19
  214. package/apps/web/src/components/ui/label.tsx +0 -22
  215. package/apps/web/src/components/ui/radio-group.tsx +0 -44
  216. package/apps/web/src/components/ui/select.tsx +0 -192
  217. package/apps/web/src/components/ui/separator.tsx +0 -26
  218. package/apps/web/src/components/ui/table.tsx +0 -116
  219. package/apps/web/src/components/ui/tabs.tsx +0 -88
  220. package/apps/web/src/components/ui/textarea.tsx +0 -18
  221. package/apps/web/src/lib/auth.ts +0 -50
  222. package/apps/web/src/lib/daemon-client.ts +0 -34
  223. package/apps/web/src/lib/md.ts +0 -45
  224. package/apps/web/src/lib/utils.ts +0 -6
  225. package/apps/web/src/lib/wire-constants.ts +0 -27
  226. package/apps/web/src/routeTree.gen.ts +0 -408
  227. package/apps/web/src/router.tsx +0 -20
  228. package/apps/web/src/routes/__root.tsx +0 -123
  229. package/apps/web/src/routes/agents/$id/inbox.tsx +0 -207
  230. package/apps/web/src/routes/agents/$id/index.tsx +0 -527
  231. package/apps/web/src/routes/agents/$id/triggers.tsx +0 -239
  232. package/apps/web/src/routes/agents/index.tsx +0 -265
  233. package/apps/web/src/routes/api/$.ts +0 -88
  234. package/apps/web/src/routes/config/index.tsx +0 -315
  235. package/apps/web/src/routes/config/services.tsx +0 -49
  236. package/apps/web/src/routes/config/tokens.tsx +0 -192
  237. package/apps/web/src/routes/groups/$id/index.tsx +0 -153
  238. package/apps/web/src/routes/groups/$id/memory.tsx +0 -321
  239. package/apps/web/src/routes/groups/index.tsx +0 -191
  240. package/apps/web/src/routes/index.tsx +0 -133
  241. package/apps/web/src/routes/login.tsx +0 -63
  242. package/apps/web/src/routes/profiles/$id.tsx +0 -549
  243. package/apps/web/src/routes/profiles/index.tsx +0 -458
  244. package/apps/web/src/routes/skills/index.tsx +0 -297
  245. package/apps/web/src/routes/welcome.tsx +0 -61
  246. package/apps/web/src/styles.css +0 -449
  247. package/apps/web/tsconfig.json +0 -25
  248. package/apps/web/vite.config.ts +0 -25
  249. package/biome.json +0 -23
  250. package/docs/agent-engine.md +0 -219
  251. package/docs/architecture.md +0 -627
  252. package/docs/backlog/README.md +0 -42
  253. package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +0 -125
  254. package/docs/openclaw-reference.md +0 -210
  255. package/packages/api-types/package.json +0 -11
  256. package/packages/api-types/src/entities.ts +0 -146
  257. package/packages/api-types/src/events.ts +0 -55
  258. package/packages/api-types/src/index.ts +0 -488
  259. package/packages/api-types/src/memory.ts +0 -15
  260. package/packages/client/package.json +0 -13
  261. package/packages/client/src/index.ts +0 -117
  262. package/pnpm-workspace.yaml +0 -3
  263. package/tsconfig.base.json +0 -23
  264. package/tsconfig.json +0 -11
  265. package/vitest.config.ts +0 -22
@@ -1,319 +0,0 @@
1
- import { existsSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import { afterEach, beforeEach, expect, test } from 'vitest'
4
- import { archiveAgent } from '../../src/core/agent/archive.ts'
5
- import { deleteAgent } from '../../src/core/agent/delete.ts'
6
- import { resolveAgent } from '../../src/core/agent/resolve.ts'
7
- import { spawnAgent } from '../../src/core/agent/spawn.ts'
8
- import { unarchiveAgent } from '../../src/core/agent/unarchive.ts'
9
- import { registerGroup } from '../../src/core/group/register.ts'
10
- import { createProfile } from '../../src/core/profile/create.ts'
11
- import * as agentRepo from '../../src/core/repos/agents.ts'
12
- import * as messageRepo from '../../src/core/repos/messages.ts'
13
- import { makeTestEnv, type TestEnv } from './helpers.ts'
14
-
15
- let env: TestEnv
16
- beforeEach(() => {
17
- env = makeTestEnv()
18
- })
19
- afterEach(() => env.cleanup())
20
-
21
- function seedProfile() {
22
- return createProfile(env.db, env.paths, {
23
- id: 'base',
24
- defaultModel: 'anthropic:claude-opus-4-6',
25
- defaultSkills: ['skill-a'],
26
- })
27
- }
28
-
29
- function spawn(input: Partial<Parameters<typeof spawnAgent>[2]> & { profileId?: string } = {}) {
30
- return spawnAgent(env.db, env.paths, {
31
- profileId: input.profileId ?? 'base',
32
- groupId: input.groupId ?? env.groupId,
33
- ...input,
34
- })
35
- }
36
-
37
- test('spawnAgent creates dir, copies templates, inserts row, attaches default skills', () => {
38
- seedProfile()
39
- const agent = spawn({ name: 'A1' })
40
-
41
- expect(agent.profileId).toBe('base')
42
- expect(agent.status).toBe('idle')
43
- expect(agent.groupId).toBe(env.groupId)
44
- expect(existsSync(agent.dir)).toBe(true)
45
- expect(existsSync(join(agent.dir, 'SOUL.md'))).toBe(true)
46
- expect(existsSync(join(agent.dir, 'IDENTITY.md'))).toBe(true)
47
- expect(existsSync(join(agent.dir, 'BOOTSTRAP.md'))).toBe(true)
48
- expect(existsSync(join(agent.dir, 'agent.json'))).toBe(true)
49
- // Memory now lives at the group level (`groups/<slug>/memory/`), shared
50
- // by all member agents. The agent's private home only carries identity
51
- // files + sessions/.
52
- expect(existsSync(join(agent.dir, 'sessions'))).toBe(true)
53
- expect(existsSync(join(agent.dir, 'memory'))).toBe(false)
54
-
55
- expect(agentRepo.listAttachedSkills(env.db, agent.id)).toEqual(['skill-a'])
56
- })
57
-
58
- test('spawnAgent copies optional AGENTS/TOOLS/HEARTBEAT files when the profile seeded them', () => {
59
- createProfile(env.db, env.paths, {
60
- id: 'felix',
61
- defaultModel: 'm',
62
- templates: {
63
- agents: '# AGENTS\n- peer: hello\n',
64
- tools: '# TOOLS\n- ripgrep before grep\n',
65
- heartbeat: '# HEARTBEAT\n- check calendar\n',
66
- },
67
- })
68
- const agent = spawn({ profileId: 'felix' })
69
- expect(existsSync(join(agent.dir, 'AGENTS.md'))).toBe(true)
70
- expect(existsSync(join(agent.dir, 'TOOLS.md'))).toBe(true)
71
- expect(existsSync(join(agent.dir, 'HEARTBEAT.md'))).toBe(true)
72
- })
73
-
74
- test('spawnAgent does not create optional AGENTS/TOOLS/HEARTBEAT files when the profile lacks them', () => {
75
- seedProfile()
76
- const agent = spawn()
77
- expect(existsSync(join(agent.dir, 'AGENTS.md'))).toBe(false)
78
- expect(existsSync(join(agent.dir, 'TOOLS.md'))).toBe(false)
79
- expect(existsSync(join(agent.dir, 'HEARTBEAT.md'))).toBe(false)
80
- })
81
-
82
- test('spawning many agents from one profile produces independent instances', () => {
83
- seedProfile()
84
- const a = spawn()
85
- const b = spawn()
86
- expect(a.id).not.toBe(b.id)
87
- expect(a.dir).not.toBe(b.dir)
88
- expect(agentRepo.list(env.db).length).toBe(2)
89
- })
90
-
91
- test('spawnAgent places the agent in the requested group', () => {
92
- seedProfile()
93
- const g = registerGroup(env.db, { id: 'extra' }, env.paths)
94
-
95
- const agent = spawn({ groupId: g.id })
96
- expect(agent.groupId).toBe('extra')
97
- })
98
-
99
- test('spawnAgent rejects an unknown group', () => {
100
- seedProfile()
101
- expect(() => spawn({ groupId: 'no-such-group' })).toThrow(/group "no-such-group" does not exist/)
102
- })
103
-
104
- test('agentRepo.setGroup moves an agent to a different group', () => {
105
- seedProfile()
106
- const g2 = registerGroup(env.db, { id: 'g2' }, env.paths)
107
- const agent = spawn()
108
- expect(agent.groupId).toBe(env.groupId)
109
-
110
- agentRepo.setGroup(env.db, agent.id, g2.id)
111
- expect(agentRepo.get(env.db, agent.id)?.groupId).toBe('g2')
112
- })
113
-
114
- test('attaching and detaching skills on the fly', () => {
115
- seedProfile()
116
- const agent = spawn()
117
-
118
- agentRepo.attachSkill(env.db, agent.id, 'extra')
119
- expect(agentRepo.listAttachedSkills(env.db, agent.id).sort()).toEqual(['extra', 'skill-a'])
120
-
121
- agentRepo.detachSkill(env.db, agent.id, 'skill-a')
122
- expect(agentRepo.listAttachedSkills(env.db, agent.id)).toEqual(['extra'])
123
- })
124
-
125
- test('archiveAgent sets status and timestamp', () => {
126
- seedProfile()
127
- const agent = spawn()
128
- archiveAgent(env.db, agent.id)
129
- const after = agentRepo.get(env.db, agent.id)
130
- expect(after?.status).toBe('archived')
131
- expect(after?.archivedAt).not.toBeNull()
132
- })
133
-
134
- test('list excludes archived by default', () => {
135
- seedProfile()
136
- const a = spawn()
137
- spawn()
138
- archiveAgent(env.db, a.id)
139
-
140
- expect(agentRepo.list(env.db).length).toBe(1)
141
- expect(agentRepo.list(env.db, { includeArchived: true }).length).toBe(2)
142
- })
143
-
144
- test('unarchiveAgent flips status back to idle and clears archivedAt', () => {
145
- seedProfile()
146
- const agent = spawn()
147
- archiveAgent(env.db, agent.id)
148
- expect(agentRepo.get(env.db, agent.id)?.status).toBe('archived')
149
-
150
- unarchiveAgent(env.db, agent.id)
151
- const after = agentRepo.get(env.db, agent.id)
152
- expect(after?.status).toBe('idle')
153
- expect(after?.archivedAt).toBeNull()
154
- })
155
-
156
- test('unarchiveAgent throws on a non-archived agent', () => {
157
- seedProfile()
158
- const agent = spawn()
159
- expect(() => unarchiveAgent(env.db, agent.id)).toThrow(/not archived/)
160
- })
161
-
162
- test('unarchiveAgent throws on an unknown id', () => {
163
- seedProfile()
164
- expect(() => unarchiveAgent(env.db, 'ghost-id')).toThrow()
165
- })
166
-
167
- test('deleteAgent removes the DB row and the on-disk directory', () => {
168
- seedProfile()
169
- const agent = spawn()
170
- expect(existsSync(agent.dir)).toBe(true)
171
-
172
- deleteAgent(env.db, agent.id)
173
- expect(agentRepo.get(env.db, agent.id)).toBeNull()
174
- expect(existsSync(agent.dir)).toBe(false)
175
- })
176
-
177
- test('deleteAgent cascades to attachments, messages, runs', () => {
178
- seedProfile()
179
- const a = spawn()
180
- const b = spawn()
181
- agentRepo.attachSkill(env.db, a.id, 'extra')
182
-
183
- // Side table: a message tied to the agent
184
- messageRepo.send(env.db, {
185
- from: b.id,
186
- to: a.id,
187
- payload: JSON.stringify({ text: 'hi' }),
188
- })
189
-
190
- // Sanity: the rows exist before delete
191
- expect(agentRepo.listAttachedSkills(env.db, a.id).length).toBeGreaterThan(0)
192
- expect(messageRepo.listInbox(env.db, a.id).length).toBe(1)
193
-
194
- deleteAgent(env.db, a.id)
195
-
196
- expect(agentRepo.get(env.db, a.id)).toBeNull()
197
- expect(agentRepo.listAttachedSkills(env.db, a.id)).toEqual([])
198
- expect(messageRepo.listInbox(env.db, a.id)).toEqual([])
199
-
200
- // Agent b is untouched
201
- expect(agentRepo.get(env.db, b.id)).not.toBeNull()
202
- })
203
-
204
- test('deleteAgent throws on an unknown id', () => {
205
- seedProfile()
206
- expect(() => deleteAgent(env.db, 'ghost-id')).toThrow()
207
- })
208
-
209
- test('agentRepo.get resolves an unambiguous UUID prefix (≥4 chars)', () => {
210
- seedProfile()
211
- const agent = spawn()
212
- // Full UUID still works
213
- expect(agentRepo.get(env.db, agent.id)?.id).toBe(agent.id)
214
- // First 8 chars (git-style) resolves
215
- const prefix = agent.id.slice(0, 8)
216
- expect(agentRepo.get(env.db, prefix)?.id).toBe(agent.id)
217
- // <4 chars never resolves
218
- expect(agentRepo.get(env.db, agent.id.slice(0, 3))).toBeNull()
219
- // Non-existing prefix still null
220
- expect(agentRepo.get(env.db, 'zzzzzzzz')).toBeNull()
221
- })
222
-
223
- test('agentRepo.get resolves by exact name when unique', () => {
224
- seedProfile()
225
- const a = spawn({ name: 'alpha' })
226
- const b = spawn({ name: 'beta' })
227
-
228
- expect(agentRepo.get(env.db, 'alpha')?.id).toBe(a.id)
229
- expect(agentRepo.get(env.db, 'beta')?.id).toBe(b.id)
230
- expect(agentRepo.get(env.db, 'nobody')).toBeNull()
231
- })
232
-
233
- test('agentRepo.get returns null for an ambiguous name', () => {
234
- seedProfile()
235
- spawn({ name: 'twin' })
236
- spawn({ name: 'twin' })
237
- expect(agentRepo.get(env.db, 'twin')).toBeNull()
238
- })
239
-
240
- test('agentRepo.get prefers exact name over prefix collision', () => {
241
- seedProfile()
242
- // An agent whose name is a hex string that is ALSO a valid UUID prefix of
243
- // some other agent's id. The name-match should win so users can rename
244
- // agents to anything and have lookup behave predictably.
245
- const id = 'deadbeef-0000-0000-0000-000000000000'
246
- agentRepo.insert(env.db, {
247
- id,
248
- profileId: 'base',
249
- name: 'A-with-hex-prefix-name',
250
- modelOverride: null,
251
- reasoningLevel: 'medium',
252
- status: 'idle',
253
- dir: '/tmp/none-hex',
254
- groupId: env.groupId,
255
- })
256
- const b = spawn({ name: 'deadbeef' })
257
-
258
- // Lookup "deadbeef": exact name on agent b wins, even though it matches a
259
- // (the id starts with deadbeef).
260
- expect(agentRepo.get(env.db, 'deadbeef')?.id).toBe(b.id)
261
- // But the full id for a still resolves
262
- expect(agentRepo.get(env.db, id)?.id).toBe(id)
263
- })
264
-
265
- test('agentRepo.get returns null for ambiguous prefix', () => {
266
- seedProfile()
267
- // Insert two agents manually with IDs that share a 4-char prefix; spawnAgent
268
- // uses random UUIDs so we bypass it.
269
- agentRepo.insert(env.db, {
270
- id: 'abcd1111-aaaa-bbbb-cccc-dddddddddddd',
271
- profileId: 'base',
272
- name: 'A',
273
- modelOverride: null,
274
- reasoningLevel: 'medium',
275
- status: 'idle',
276
- dir: '/tmp/none-a',
277
- groupId: env.groupId,
278
- })
279
- agentRepo.insert(env.db, {
280
- id: 'abcd2222-aaaa-bbbb-cccc-dddddddddddd',
281
- profileId: 'base',
282
- name: 'B',
283
- modelOverride: null,
284
- reasoningLevel: 'medium',
285
- status: 'idle',
286
- dir: '/tmp/none-b',
287
- groupId: env.groupId,
288
- })
289
- expect(agentRepo.get(env.db, 'abcd')).toBeNull() // ambiguous
290
- expect(agentRepo.get(env.db, 'abcd1')?.name).toBe('A') // unique
291
- expect(agentRepo.get(env.db, 'abcd2')?.name).toBe('B')
292
- })
293
-
294
- test('resolveAgent accepts a UUID prefix', () => {
295
- seedProfile()
296
- const agent = spawn()
297
- const resolved = resolveAgent(env.db, env.paths, agent.id.slice(0, 8))
298
- expect(resolved.agent.id).toBe(agent.id)
299
- })
300
-
301
- test('resolveAgent assembles profile, skills, group, and model', () => {
302
- seedProfile()
303
- const g = registerGroup(env.db, { id: 'rg' }, env.paths)
304
- const agent = spawn({ groupId: g.id, modelOverride: 'openai:gpt-5' })
305
-
306
- const resolved = resolveAgent(env.db, env.paths, agent.id)
307
- expect(resolved.model).toBe('openai:gpt-5')
308
- expect(resolved.profile.id).toBe('base')
309
- expect(resolved.group.id).toBe('rg')
310
- expect(resolved.group.userMd).toBe('')
311
- expect(resolved.skills).toEqual(['skill-a'])
312
- })
313
-
314
- test('resolveAgent uses profile default model when no override', () => {
315
- seedProfile()
316
- const agent = spawn()
317
- const resolved = resolveAgent(env.db, env.paths, agent.id)
318
- expect(resolved.model).toBe('anthropic:claude-opus-4-6')
319
- })
@@ -1,63 +0,0 @@
1
- import { afterEach, beforeEach, expect, test } from 'vitest'
2
- import {
3
- type BazilionDb,
4
- groupAvailableModels,
5
- listAvailableModels,
6
- openInMemoryDb,
7
- providerModelRepo,
8
- providerStateRepo,
9
- runMigrations,
10
- } from '../../src/core/index.ts'
11
-
12
- let db: BazilionDb
13
-
14
- beforeEach(() => {
15
- db = openInMemoryDb()
16
- runMigrations(db)
17
- })
18
- afterEach(() => {
19
- db.close()
20
- })
21
-
22
- test('empty DB yields empty lists', () => {
23
- expect(listAvailableModels(db)).toEqual([])
24
- expect(groupAvailableModels(db)).toEqual([])
25
- })
26
-
27
- test('only enabled providers with curated models surface', () => {
28
- providerStateRepo.setEnabled(db, 'anthropic', true)
29
- providerStateRepo.setEnabled(db, 'openai', true)
30
- providerStateRepo.setEnabled(db, 'groq', false)
31
- providerStateRepo.setEnabled(db, 'lmstudio', true)
32
-
33
- providerModelRepo.replace(db, 'anthropic', ['claude-opus-4-6', 'claude-sonnet-4-6'])
34
- providerModelRepo.replace(db, 'openai', ['gpt-4o'])
35
- // openai but disabled-then-enabled — still has its curated entries
36
- providerModelRepo.replace(db, 'groq', ['llama-3.3-70b']) // disabled → omitted
37
- // lmstudio is enabled but has no curated models → omitted from groups
38
-
39
- const list = listAvailableModels(db)
40
- expect(list.map((m) => m.value).sort()).toEqual([
41
- 'anthropic:claude-opus-4-6',
42
- 'anthropic:claude-sonnet-4-6',
43
- 'openai:gpt-4o',
44
- ])
45
-
46
- const groups = groupAvailableModels(db)
47
- expect(groups).toEqual([
48
- { provider: 'anthropic', models: ['claude-opus-4-6', 'claude-sonnet-4-6'] },
49
- { provider: 'openai', models: ['gpt-4o'] },
50
- ])
51
- })
52
-
53
- test('disabling a provider makes its curated models disappear from the list', () => {
54
- providerStateRepo.setEnabled(db, 'anthropic', true)
55
- providerModelRepo.replace(db, 'anthropic', ['claude-opus-4-6'])
56
- expect(listAvailableModels(db)).toHaveLength(1)
57
-
58
- providerStateRepo.setEnabled(db, 'anthropic', false)
59
- expect(listAvailableModels(db)).toHaveLength(0)
60
-
61
- providerStateRepo.setEnabled(db, 'anthropic', true)
62
- expect(listAvailableModels(db)).toHaveLength(1)
63
- })
@@ -1,99 +0,0 @@
1
- import { mkdtempSync, rmSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { afterEach, beforeEach, expect, test } from 'vitest'
5
- import { type BazilionDb, openInMemoryDb } from '../../src/core/db/client.ts'
6
- import { runMigrations } from '../../src/core/db/migrate.ts'
7
- import { CONFIG_KEYS, isConfigKey, openConfig } from '../../src/core/repos/config.ts'
8
- import { openSecrets } from '../../src/core/repos/secrets.ts'
9
- import { mergeSecretsIntoEnv } from '../../src/core/secrets.ts'
10
-
11
- interface Env {
12
- home: string
13
- db: BazilionDb
14
- authToken: string
15
- cleanup(): void
16
- }
17
-
18
- function makeEnv(): Env {
19
- const home = mkdtempSync(join(tmpdir(), 'bazilion-config-'))
20
- const db = openInMemoryDb()
21
- runMigrations(db)
22
- return {
23
- home,
24
- db,
25
- authToken: 'test-token-abc123',
26
- cleanup() {
27
- db.close()
28
- rmSync(home, { recursive: true, force: true })
29
- },
30
- }
31
- }
32
-
33
- let env: Env
34
-
35
- beforeEach(() => {
36
- env = makeEnv()
37
- })
38
- afterEach(() => {
39
- env.cleanup()
40
- })
41
-
42
- test('isConfigKey identifies URL-type keys and rejects secret-shaped ones', () => {
43
- for (const k of CONFIG_KEYS) expect(isConfigKey(k)).toBe(true)
44
- expect(isConfigKey('ANTHROPIC_API_KEY')).toBe(false)
45
- expect(isConfigKey('OPENAI_API_KEY')).toBe(false)
46
- expect(isConfigKey('BRAVE_API_KEY')).toBe(false)
47
- expect(isConfigKey('random-junk')).toBe(false)
48
- })
49
-
50
- test('openConfig round-trips LMSTUDIO_URL through the config table', () => {
51
- const config = openConfig(env.db)
52
- config.set('LMSTUDIO_URL', 'http://192.168.1.50:1234/v1')
53
- expect(config.get('LMSTUDIO_URL')).toBe('http://192.168.1.50:1234/v1')
54
-
55
- // Re-opening (a fresh repo over the same db) yields the same value.
56
- const reopened = openConfig(env.db)
57
- expect(reopened.get('LMSTUDIO_URL')).toBe('http://192.168.1.50:1234/v1')
58
- })
59
-
60
- test('openConfig rejects unknown keys to prevent accidental secret leaks', () => {
61
- const config = openConfig(env.db)
62
- expect(() => config.set('ANTHROPIC_API_KEY', 'sk-abc')).toThrow(/not a known config key/)
63
- expect(() => config.set('random-thing', 'x')).toThrow(/not a known config key/)
64
- })
65
-
66
- test('openConfig.remove clears one key without affecting siblings', () => {
67
- const config = openConfig(env.db)
68
- config.set('LMSTUDIO_URL', 'http://x')
69
- config.set('OLLAMA_URL', 'http://y')
70
- config.remove('LMSTUDIO_URL')
71
- expect(config.get('LMSTUDIO_URL')).toBeUndefined()
72
- expect(config.get('OLLAMA_URL')).toBe('http://y')
73
- })
74
-
75
- test('openSecrets round-trips an encrypted value with the bootstrap token', () => {
76
- const secrets = openSecrets(env.db, env.authToken)
77
- secrets.set('ANTHROPIC_API_KEY', 'sk-from-secrets')
78
- expect(secrets.get('ANTHROPIC_API_KEY')).toBe('sk-from-secrets')
79
-
80
- // A different password fails to decrypt — get returns undefined rather
81
- // than throwing.
82
- const wrongPass = openSecrets(env.db, 'wrong-token')
83
- expect(wrongPass.get('ANTHROPIC_API_KEY')).toBeUndefined()
84
- })
85
-
86
- test('mergeSecretsIntoEnv layers process env over secrets over config', () => {
87
- const config = openConfig(env.db)
88
- config.set('LMSTUDIO_URL', 'http://from-config')
89
-
90
- const secrets = openSecrets(env.db, env.authToken)
91
- secrets.set('ANTHROPIC_API_KEY', 'sk-from-secrets')
92
-
93
- // process env beats both.
94
- const merged = mergeSecretsIntoEnv(env.db, env.authToken, {
95
- ANTHROPIC_API_KEY: 'sk-from-env',
96
- } as NodeJS.ProcessEnv)
97
- expect(merged.LMSTUDIO_URL).toBe('http://from-config')
98
- expect(merged.ANTHROPIC_API_KEY).toBe('sk-from-env')
99
- })
@@ -1,63 +0,0 @@
1
- import { existsSync, lstatSync, mkdtempSync, symlinkSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { afterEach, beforeEach, expect, test } from 'vitest'
5
- import { registerGroup } from '../../src/core/group/register.ts'
6
- import * as groupRepo from '../../src/core/repos/groups.ts'
7
- import { makeTestEnv, type TestEnv } from './helpers.ts'
8
-
9
- let env: TestEnv
10
- beforeEach(() => {
11
- env = makeTestEnv()
12
- })
13
- afterEach(() => env.cleanup())
14
-
15
- test('registerGroup creates a real directory under groups/<slug>/', () => {
16
- const g = registerGroup(env.db, { id: 'g1' }, env.paths)
17
- expect(g.id).toBe('g1')
18
- expect(g.path).toBe(env.paths.groupDir('g1'))
19
- expect(g.userMd).toBe('')
20
- expect(existsSync(g.path)).toBe(true)
21
- expect(lstatSync(g.path).isSymbolicLink()).toBe(false)
22
- expect(existsSync(join(g.path, 'memory'))).toBe(true)
23
- // +1 over the auto-seeded test-group from makeTestEnv.
24
- expect(groupRepo.list(env.db, env.paths).length).toBe(2)
25
- })
26
-
27
- test('registerGroup with --link materializes the slot as a symlink', () => {
28
- const target = mkdtempSync(join(tmpdir(), 'bazilion-link-'))
29
- const g = registerGroup(env.db, { id: 'linked', link: target }, env.paths)
30
- expect(g.path).toBe(env.paths.groupDir('linked'))
31
- expect(lstatSync(g.path).isSymbolicLink()).toBe(true)
32
- // memory subdir gets created via the symlink — ends up inside the target.
33
- expect(existsSync(join(target, 'memory'))).toBe(true)
34
- })
35
-
36
- test('registerGroup --link fails when target does not exist', () => {
37
- expect(() =>
38
- registerGroup(env.db, { id: 'broken', link: '/tmp/no-such-bazilion-target-x9' }, env.paths),
39
- ).toThrow(/does not exist/)
40
- })
41
-
42
- test('registerGroup refuses duplicate slug', () => {
43
- registerGroup(env.db, { id: 'first' }, env.paths)
44
- expect(() => registerGroup(env.db, { id: 'first' }, env.paths)).toThrow(/already registered/)
45
- })
46
-
47
- test('registerGroup refuses if the on-disk slot already exists', () => {
48
- // Materialize the slot before the registry call — should bail out cleanly.
49
- const slot = env.paths.groupDir('preexisting')
50
- symlinkSync(env.home, slot, 'dir')
51
- expect(() => registerGroup(env.db, { id: 'preexisting' }, env.paths)).toThrow(/slot already/)
52
- })
53
-
54
- test('registerGroup rejects invalid slug', () => {
55
- expect(() => registerGroup(env.db, { id: 'Bad Id' }, env.paths)).toThrow(/invalid slug/)
56
- })
57
-
58
- test('setUserMd updates the stored value, read back via get', () => {
59
- const g = registerGroup(env.db, { id: 'with-user-md' }, env.paths)
60
- groupRepo.setUserMd(env.db, g.id, 'hi, call me Pat')
61
- const reloaded = groupRepo.get(env.db, g.id, env.paths)
62
- expect(reloaded?.userMd).toBe('hi, call me Pat')
63
- })
@@ -1,50 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { type BazilionDb, openInMemoryDb } from '../../src/core/db/client.ts'
5
- import { runMigrations } from '../../src/core/db/migrate.ts'
6
- import { registerGroup } from '../../src/core/group/register.ts'
7
- import { type Paths, resolvePaths } from '../../src/core/paths.ts'
8
-
9
- // `process.env.BAZILION_HOME` is consumed by `resolvePaths`. Some tests pass a
10
- // home explicitly; other code paths reach for the env. Either works.
11
-
12
- export interface TestEnv {
13
- home: string
14
- paths: Paths
15
- db: BazilionDb
16
- /**
17
- * A group registered automatically so spawn/resolve always have a valid
18
- * id to target. Named `test-group` — tests that need more can register
19
- * extras.
20
- */
21
- groupId: string
22
- cleanup: () => void
23
- }
24
-
25
- export function makeTestEnv(): TestEnv {
26
- const home = mkdtempSync(join(tmpdir(), 'bazilion-test-'))
27
- const paths = resolvePaths(home)
28
- for (const d of [
29
- paths.profilesDir,
30
- paths.agentsDir,
31
- paths.skillsDir,
32
- paths.logsDir,
33
- paths.groupsDir,
34
- ]) {
35
- mkdirSync(d, { recursive: true })
36
- }
37
- const db = openInMemoryDb()
38
- runMigrations(db)
39
- const group = registerGroup(db, { id: 'test-group', name: 'test' }, paths)
40
- return {
41
- home,
42
- paths,
43
- db,
44
- groupId: group.id,
45
- cleanup() {
46
- db.close()
47
- rmSync(home, { recursive: true, force: true })
48
- },
49
- }
50
- }
@@ -1,85 +0,0 @@
1
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { afterEach, beforeEach, expect, test } from 'vitest'
5
- import {
6
- identityHasValues,
7
- loadIdentityFromFile,
8
- parseIdentityMarkdown,
9
- } from '../../src/core/profile/identity.ts'
10
-
11
- let dir: string
12
- beforeEach(() => {
13
- dir = mkdtempSync(join(tmpdir(), 'bazilion-identity-'))
14
- })
15
- afterEach(() => rmSync(dir, { recursive: true, force: true }))
16
-
17
- test('parseIdentityMarkdown extracts name/emoji/vibe from bullet list', () => {
18
- const md = `# IDENTITY
19
-
20
- - **Name:** Felix
21
- - **Vibe:** cat with a clipboard
22
- - **Emoji:** 🐾
23
- - **Creature:** familiar
24
- `
25
- const parsed = parseIdentityMarkdown(md)
26
- expect(parsed).toEqual({
27
- name: 'Felix',
28
- vibe: 'cat with a clipboard',
29
- emoji: '🐾',
30
- creature: 'familiar',
31
- })
32
- expect(identityHasValues(parsed)).toBe(true)
33
- })
34
-
35
- test('parseIdentityMarkdown strips placeholder prompts', () => {
36
- const md = `- Name: pick something you like
37
- - Emoji: 🦉
38
- - Theme: your signature - pick one that feels right`
39
- const parsed = parseIdentityMarkdown(md)
40
- expect(parsed).toEqual({ emoji: '🦉' })
41
- })
42
-
43
- test('parseIdentityMarkdown ignores unrelated labels and bare text', () => {
44
- const md = `# Felix
45
-
46
- Random prose without a colon.
47
-
48
- - Name: Bazyl
49
- - Mission: this is not a known label
50
- - not-even-a-label
51
- `
52
- const parsed = parseIdentityMarkdown(md)
53
- expect(parsed).toEqual({ name: 'Bazyl' })
54
- })
55
-
56
- test('parseIdentityMarkdown strips markdown emphasis on labels', () => {
57
- // Label regex drops every *_; value regex only touches edges, after the
58
- // colon-adjacent space has been preserved.
59
- const md = `- **Name:** Quill
60
- - _Vibe_: cheeky`
61
- const parsed = parseIdentityMarkdown(md)
62
- expect(parsed.name).toBe('Quill')
63
- expect(parsed.vibe).toBe('cheeky')
64
- })
65
-
66
- test('identityHasValues is false for empty / placeholder-only identities', () => {
67
- expect(identityHasValues(parseIdentityMarkdown('# heading\n'))).toBe(false)
68
- expect(identityHasValues({})).toBe(false)
69
- })
70
-
71
- test('loadIdentityFromFile returns null for missing file', () => {
72
- expect(loadIdentityFromFile(join(dir, 'ghost.md'))).toBeNull()
73
- })
74
-
75
- test('loadIdentityFromFile returns null when nothing parses', () => {
76
- const path = join(dir, 'IDENTITY.md')
77
- writeFileSync(path, '# Just prose, no key:value pairs\n')
78
- expect(loadIdentityFromFile(path)).toBeNull()
79
- })
80
-
81
- test('loadIdentityFromFile returns parsed identity when the file has values', () => {
82
- const path = join(dir, 'IDENTITY.md')
83
- writeFileSync(path, '- Name: Ada\n- Emoji: ✨\n')
84
- expect(loadIdentityFromFile(path)).toEqual({ name: 'Ada', emoji: '✨' })
85
- })