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,213 +0,0 @@
1
- // Unit tests for the messaging `ToolHandler`s that Bazilion exposes as pi
2
- // customTools. Called directly here (not through the LLM loop) because the
3
- // tests are about tool *behaviour* — DB side effects, polling, error paths —
4
- // not about round-tripping through pi's agent loop.
5
-
6
- import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'
7
- import { tmpdir } from 'node:os'
8
- import { join } from 'node:path'
9
- import type { ResolvedAgent } from '@bazilion/api-types'
10
- import { afterEach, beforeEach, expect, test } from 'vitest'
11
- import {
12
- agentRepo,
13
- type BazilionDb,
14
- createProfile,
15
- messageRepo,
16
- openInMemoryDb,
17
- type Paths,
18
- registerGroup,
19
- resolveAgent,
20
- resolvePaths,
21
- runMigrations,
22
- spawnAgent,
23
- } from '../../src/core/index.ts'
24
- import { messagingTools } from '../../src/runtime/tools/messaging.ts'
25
- import type { ToolHandler } from '../../src/runtime/tools/types.ts'
26
- import type { MessagingHost } from '../../src/runtime/worker/ipc-protocol.ts'
27
-
28
- function dbHost(db: BazilionDb): MessagingHost {
29
- return {
30
- agentExists: (id) => agentRepo.get(db, id) !== null,
31
- sendMessage: (input) => ({ messageId: messageRepo.send(db, input).id }),
32
- listInbox: (id, opts) => messageRepo.listInbox(db, id, opts),
33
- markRead: (id) => messageRepo.markRead(db, id),
34
- findReplies: (id, replyTo) => messageRepo.findReplies(db, id, replyTo),
35
- }
36
- }
37
-
38
- interface Env {
39
- home: string
40
- paths: Paths
41
- db: BazilionDb
42
- groupId: string
43
- cleanup(): void
44
- }
45
-
46
- function makeEnv(): Env {
47
- const home = mkdtempSync(join(tmpdir(), 'bazilion-msg-'))
48
- const paths = resolvePaths(home)
49
- for (const d of [
50
- paths.profilesDir,
51
- paths.agentsDir,
52
- paths.skillsDir,
53
- paths.logsDir,
54
- paths.groupsDir,
55
- ]) {
56
- mkdirSync(d, { recursive: true })
57
- }
58
- const db = openInMemoryDb()
59
- runMigrations(db)
60
- const g = registerGroup(db, { id: 'test-group', name: 'test' }, paths)
61
- return {
62
- home,
63
- paths,
64
- db,
65
- groupId: g.id,
66
- cleanup() {
67
- db.close()
68
- rmSync(home, { recursive: true, force: true })
69
- },
70
- }
71
- }
72
-
73
- function makeAgent(env: Env, name: string): ResolvedAgent {
74
- const a = spawnAgent(env.db, env.paths, {
75
- profileId: 'p',
76
- name,
77
- groupId: env.groupId,
78
- })
79
- return resolveAgent(env.db, env.paths, a.id)
80
- }
81
-
82
- function findHandler(handlers: ToolHandler[], name: string): ToolHandler {
83
- const h = handlers.find((x) => x.def.name === name)
84
- if (!h) throw new Error(`no handler: ${name}`)
85
- return h
86
- }
87
-
88
- let env: Env
89
- beforeEach(() => {
90
- env = makeEnv()
91
- createProfile(env.db, env.paths, {
92
- id: 'p',
93
- defaultModel: 'mock:m',
94
- })
95
- })
96
- afterEach(() => env.cleanup())
97
-
98
- test('send_message tool inserts a row addressed to the recipient', async () => {
99
- const a = makeAgent(env, 'A')
100
- const b = makeAgent(env, 'B')
101
- const handlers = messagingTools(dbHost(env.db), a.agent.id)
102
- const send = findHandler(handlers, 'send_message')
103
-
104
- await send.invoke({ to: b.agent.id, text: 'hi B, this is A' })
105
-
106
- const inbox = messageRepo.listInbox(env.db, b.agent.id)
107
- expect(inbox).toHaveLength(1)
108
- expect(inbox[0]?.fromAgentId).toBe(a.agent.id)
109
- const payload = JSON.parse(inbox[0]?.payload ?? '{}')
110
- expect(payload.text).toBe('hi B, this is A')
111
- })
112
-
113
- test('read_inbox returns unread messages and marks them read', async () => {
114
- const a = makeAgent(env, 'A')
115
- const b = makeAgent(env, 'B')
116
-
117
- messageRepo.send(env.db, {
118
- from: a.agent.id,
119
- to: b.agent.id,
120
- payload: JSON.stringify({ text: 'first' }),
121
- })
122
- messageRepo.send(env.db, {
123
- from: a.agent.id,
124
- to: b.agent.id,
125
- payload: JSON.stringify({ text: 'second' }),
126
- })
127
-
128
- const handlers = messagingTools(dbHost(env.db), b.agent.id)
129
- const read = findHandler(handlers, 'read_inbox')
130
- const result = await read.invoke({})
131
- expect(result).toContain('first')
132
- expect(result).toContain('second')
133
-
134
- // After read, inbox shows nothing (unreadOnly default)
135
- const after = messageRepo.listInbox(env.db, b.agent.id, { unreadOnly: true })
136
- expect(after).toHaveLength(0)
137
- })
138
-
139
- test('send_message + read_inbox round-trip preserves reply_to pointer', async () => {
140
- const a = makeAgent(env, 'A')
141
- const b = makeAgent(env, 'B')
142
-
143
- // A sends B a question
144
- const aHandlers = messagingTools(dbHost(env.db), a.agent.id)
145
- const aSend = findHandler(aHandlers, 'send_message')
146
- await aSend.invoke({ to: b.agent.id, text: 'B, what is 2+2?' })
147
-
148
- const bInbox = messageRepo.listInbox(env.db, b.agent.id)
149
- expect(bInbox).toHaveLength(1)
150
- const originalMsgId = bInbox[0]?.id
151
- expect(originalMsgId).toBeDefined()
152
-
153
- // B replies with reply_to pointer
154
- const bHandlers = messagingTools(dbHost(env.db), b.agent.id)
155
- const bSend = findHandler(bHandlers, 'send_message')
156
- await bSend.invoke({ to: a.agent.id, text: '4', reply_to: originalMsgId })
157
-
158
- // A can see B's reply in its own inbox
159
- const aInbox = messageRepo.listInbox(env.db, a.agent.id)
160
- expect(aInbox).toHaveLength(1)
161
- expect(aInbox[0]?.replyTo).toBe(originalMsgId)
162
- const payload = JSON.parse(aInbox[0]?.payload ?? '{}')
163
- expect(payload.text).toBe('4')
164
- })
165
-
166
- test('wait_for_reply resolves when a reply arrives', async () => {
167
- const a = makeAgent(env, 'A')
168
- const b = makeAgent(env, 'B')
169
-
170
- const sent = messageRepo.send(env.db, {
171
- from: a.agent.id,
172
- to: b.agent.id,
173
- payload: JSON.stringify({ text: 'ping' }),
174
- })
175
-
176
- const reply = setTimeout(() => {
177
- messageRepo.send(env.db, {
178
- from: b.agent.id,
179
- to: a.agent.id,
180
- payload: JSON.stringify({ text: 'pong' }),
181
- replyTo: sent.id,
182
- })
183
- }, 100)
184
-
185
- const handlers = messagingTools(dbHost(env.db), a.agent.id)
186
- const wait = findHandler(handlers, 'wait_for_reply')
187
- const result = await wait.invoke({ message_id: sent.id, timeout_ms: 2000, poll_ms: 50 })
188
- clearTimeout(reply)
189
-
190
- expect(result).toContain('pong')
191
- })
192
-
193
- test('wait_for_reply times out cleanly when no reply arrives', async () => {
194
- const a = makeAgent(env, 'A')
195
- const b = makeAgent(env, 'B')
196
- const sent = messageRepo.send(env.db, {
197
- from: a.agent.id,
198
- to: b.agent.id,
199
- payload: JSON.stringify({ text: 'ping' }),
200
- })
201
-
202
- const handlers = messagingTools(dbHost(env.db), a.agent.id)
203
- const wait = findHandler(handlers, 'wait_for_reply')
204
- const result = await wait.invoke({ message_id: sent.id, timeout_ms: 200, poll_ms: 50 })
205
- expect(result).toContain('no reply within')
206
- })
207
-
208
- test('send_message rejects unknown recipient', async () => {
209
- const a = makeAgent(env, 'A')
210
- const handlers = messagingTools(dbHost(env.db), a.agent.id)
211
- const send = findHandler(handlers, 'send_message')
212
- await expect(send.invoke({ to: 'nope', text: 'hi' })).rejects.toThrow()
213
- })
@@ -1,65 +0,0 @@
1
- import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'
2
-
3
- export interface MockServer {
4
- url: string
5
- stop(): Promise<void>
6
- }
7
-
8
- /**
9
- * Starts a node:http server on an ephemeral port that adapts Node's
10
- * (req, res) to the Fetch-API Request/Response shape, then dispatches to the
11
- * provided handler. Lets tests keep Request/Response handler code.
12
- */
13
- export function startMockServer(
14
- handler: (req: Request) => Response | Promise<Response>,
15
- ): Promise<MockServer> {
16
- const server = createServer((nodeReq: IncomingMessage, nodeRes: ServerResponse) => {
17
- const chunks: Buffer[] = []
18
- nodeReq.on('data', (c: Buffer) => chunks.push(c))
19
- nodeReq.on('end', async () => {
20
- try {
21
- const bodyBuf = chunks.length > 0 ? Buffer.concat(chunks) : undefined
22
- const addr = server.address()
23
- const hostHeader =
24
- nodeReq.headers.host ??
25
- (addr && typeof addr !== 'string' ? `localhost:${addr.port}` : 'localhost')
26
- const url = `http://${hostHeader}${nodeReq.url ?? '/'}`
27
- const headerEntries: [string, string][] = []
28
- for (const [k, v] of Object.entries(nodeReq.headers)) {
29
- if (typeof v === 'string') headerEntries.push([k, v])
30
- else if (Array.isArray(v)) headerEntries.push([k, v.join(', ')])
31
- }
32
- const method = nodeReq.method ?? 'GET'
33
- const req = new Request(url, {
34
- method,
35
- headers: headerEntries,
36
- body: method === 'GET' || method === 'HEAD' ? undefined : bodyBuf,
37
- })
38
- const res = await handler(req)
39
- const outHeaders: Record<string, string> = {}
40
- res.headers.forEach((v, k) => {
41
- outHeaders[k] = v
42
- })
43
- nodeRes.writeHead(res.status, outHeaders)
44
- if (res.body) {
45
- nodeRes.end(Buffer.from(await res.arrayBuffer()))
46
- } else {
47
- nodeRes.end()
48
- }
49
- } catch (err) {
50
- nodeRes.writeHead(500, { 'content-type': 'text/plain' })
51
- nodeRes.end(`mock handler error: ${(err as Error).message}`)
52
- }
53
- })
54
- })
55
- return new Promise((resolve) => {
56
- server.listen(0, () => {
57
- const addr = server.address()
58
- if (!addr || typeof addr === 'string') throw new Error('unexpected server address')
59
- resolve({
60
- url: `http://localhost:${addr.port}`,
61
- stop: () => new Promise((r) => server.close(() => r())),
62
- })
63
- })
64
- })
65
- }
@@ -1,116 +0,0 @@
1
- import { randomBytes } from 'node:crypto'
2
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
- import { afterEach, beforeEach, expect, test } from 'vitest'
6
- import {
7
- type BazilionDb,
8
- openDb,
9
- openSecrets,
10
- resolvePaths,
11
- runMigrations,
12
- } from '../../src/core/index.ts'
13
- import {
14
- clearCredentials,
15
- getStatus,
16
- hasCredentials,
17
- loadAccessToken,
18
- OPENAI_CODEX_SECRET_KEY,
19
- saveLoginCredentials,
20
- } from '../../src/runtime/auth/openai-codex.ts'
21
-
22
- // These tests exercise the storage + expiry logic directly. Pi-ai's network
23
- // calls (login flow + token refresh) are out of scope and not hit — the
24
- // refresh path is driven by a shimmed stored refresh token so we can assert
25
- // behaviour before expiry without the wire.
26
-
27
- let home: string
28
- let db: BazilionDb
29
- let authToken: string
30
-
31
- beforeEach(() => {
32
- home = mkdtempSync(join(tmpdir(), 'bazilion-oauth-test-'))
33
- const paths = resolvePaths(home)
34
- mkdirSync(paths.home, { recursive: true })
35
- // Stamp out an auth.json with a fake bootstrap token — secretsRepo derives
36
- // its PBKDF2 key from this string, so encryption round-trip works.
37
- authToken = randomBytes(24).toString('hex')
38
- writeFileSync(paths.authFile, JSON.stringify({ token: authToken }))
39
- db = openDb(paths.db)
40
- runMigrations(db)
41
- })
42
- afterEach(() => {
43
- db.close()
44
- rmSync(home, { recursive: true, force: true })
45
- })
46
-
47
- function fakeAccessJwt(accountId: string): string {
48
- // A minimal three-part JWT so decodeAccountId can extract the chatgpt_account_id
49
- // from the JSON claim. Signature isn't validated locally.
50
- const header = Buffer.from(JSON.stringify({ alg: 'none', typ: 'JWT' })).toString('base64url')
51
- const payload = Buffer.from(
52
- JSON.stringify({ 'https://api.openai.com/auth': { chatgpt_account_id: accountId } }),
53
- ).toString('base64url')
54
- return `${header}.${payload}.sig`
55
- }
56
-
57
- test('hasCredentials / getStatus return disconnected on a fresh home', () => {
58
- expect(hasCredentials(db, authToken)).toBe(false)
59
- const status = getStatus(db, authToken)
60
- expect(status).toEqual({ connected: false, expiresAt: null, accountId: null })
61
- })
62
-
63
- test('saveLoginCredentials persists and getStatus reflects it', () => {
64
- saveLoginCredentials(db, authToken, {
65
- refresh: 'refresh-token',
66
- access: fakeAccessJwt('acct-abc'),
67
- expires: Date.now() + 30 * 60_000,
68
- })
69
- expect(hasCredentials(db, authToken)).toBe(true)
70
- const status = getStatus(db, authToken)
71
- expect(status.connected).toBe(true)
72
- expect(status.accountId).toBe('acct-abc')
73
- expect(status.expiresAt).toBeGreaterThan(Date.now())
74
- })
75
-
76
- test('loadAccessToken returns the stored access when still fresh', async () => {
77
- const access = fakeAccessJwt('acct-abc')
78
- saveLoginCredentials(db, authToken, {
79
- refresh: 'refresh-token',
80
- access,
81
- expires: Date.now() + 30 * 60_000,
82
- })
83
- await expect(loadAccessToken(db, authToken)).resolves.toBe(access)
84
- })
85
-
86
- test('loadAccessToken throws a helpful error when not configured', async () => {
87
- await expect(loadAccessToken(db, authToken)).rejects.toThrow(/not configured/)
88
- })
89
-
90
- test('clearCredentials wipes the stored blob', () => {
91
- saveLoginCredentials(db, authToken, {
92
- refresh: 'r',
93
- access: fakeAccessJwt('x'),
94
- expires: Date.now() + 60_000,
95
- })
96
- expect(hasCredentials(db, authToken)).toBe(true)
97
- clearCredentials(db, authToken)
98
- expect(hasCredentials(db, authToken)).toBe(false)
99
- })
100
-
101
- test('credential blob is stored under OPENAI_CODEX_SECRET_KEY and decrypts to JSON', () => {
102
- const access = fakeAccessJwt('acct-1')
103
- saveLoginCredentials(db, authToken, { refresh: 'r1', access, expires: 1000 })
104
- const secrets = openSecrets(db, authToken)
105
- const raw = secrets.get(OPENAI_CODEX_SECRET_KEY)
106
- expect(raw).toBeTruthy()
107
- const parsed = JSON.parse(raw as string)
108
- expect(parsed).toEqual({ refresh: 'r1', access, expires: 1000 })
109
- })
110
-
111
- test('malformed blob is treated as disconnected', () => {
112
- const secrets = openSecrets(db, authToken)
113
- secrets.set(OPENAI_CODEX_SECRET_KEY, 'not-json{')
114
- expect(hasCredentials(db, authToken)).toBe(false)
115
- expect(getStatus(db, authToken).connected).toBe(false)
116
- })
@@ -1,306 +0,0 @@
1
- import {
2
- createAssistantMessageEventStream,
3
- registerApiProvider,
4
- unregisterApiProviders,
5
- } from '@mariozechner/pi-ai'
6
- import { afterEach, beforeEach, expect, test } from 'vitest'
7
- import { piProvider } from '../../src/runtime/providers/pi-adapter.ts'
8
- import {
9
- createProviderRegistry,
10
- loadProviderConfigFromEnv,
11
- } from '../../src/runtime/providers/registry.ts'
12
-
13
- // These tests cover the pi-ai adapter's semantics — message/tool/response
14
- // conversion and delta plumbing — by registering a fake pi `Api` that returns
15
- // canned event streams. Wire-format correctness (SSE framing, provider-specific
16
- // request shapes) is pi-ai's responsibility and covered by its own test suite.
17
-
18
- const FAKE_API = 'bazilion-test-fake' as const
19
- const TEST_SRC = 'bazilion-tests'
20
-
21
- beforeEach(() => {
22
- unregisterApiProviders(TEST_SRC)
23
- })
24
- afterEach(() => {
25
- unregisterApiProviders(TEST_SRC)
26
- })
27
-
28
- function registerFakeApi(build: () => ReturnType<typeof createAssistantMessageEventStream>): void {
29
- registerApiProvider(
30
- {
31
- api: FAKE_API,
32
- stream: () => build(),
33
- streamSimple: () => build(),
34
- },
35
- TEST_SRC,
36
- )
37
- }
38
-
39
- function cannedAssistantText(text: string): ReturnType<typeof createAssistantMessageEventStream> {
40
- const stream = createAssistantMessageEventStream()
41
- const assistant = {
42
- role: 'assistant' as const,
43
- content: [{ type: 'text' as const, text }],
44
- api: FAKE_API,
45
- provider: 'fake',
46
- model: 'm',
47
- usage: {
48
- input: 10,
49
- output: 5,
50
- cacheRead: 0,
51
- cacheWrite: 0,
52
- totalTokens: 15,
53
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
54
- },
55
- stopReason: 'stop' as const,
56
- timestamp: Date.now(),
57
- }
58
- // Push chunked deltas to exercise the delta path, then a terminal done event.
59
- queueMicrotask(() => {
60
- stream.push({ type: 'start', partial: assistant })
61
- stream.push({ type: 'text_start', contentIndex: 0, partial: assistant })
62
- stream.push({
63
- type: 'text_delta',
64
- contentIndex: 0,
65
- delta: text.slice(0, 3),
66
- partial: assistant,
67
- })
68
- stream.push({ type: 'text_delta', contentIndex: 0, delta: text.slice(3), partial: assistant })
69
- stream.push({ type: 'text_end', contentIndex: 0, content: text, partial: assistant })
70
- stream.push({ type: 'done', reason: 'stop', message: assistant })
71
- stream.end(assistant)
72
- })
73
- return stream
74
- }
75
-
76
- function cannedToolCall(
77
- id: string,
78
- name: string,
79
- args: Record<string, unknown>,
80
- ): ReturnType<typeof createAssistantMessageEventStream> {
81
- const stream = createAssistantMessageEventStream()
82
- const assistant = {
83
- role: 'assistant' as const,
84
- content: [{ type: 'toolCall' as const, id, name, arguments: args }],
85
- api: FAKE_API,
86
- provider: 'fake',
87
- model: 'm',
88
- usage: {
89
- input: 0,
90
- output: 0,
91
- cacheRead: 0,
92
- cacheWrite: 0,
93
- totalTokens: 0,
94
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
95
- },
96
- stopReason: 'toolUse' as const,
97
- timestamp: Date.now(),
98
- }
99
- queueMicrotask(() => {
100
- stream.push({ type: 'start', partial: assistant })
101
- stream.push({ type: 'done', reason: 'toolUse', message: assistant })
102
- stream.end(assistant)
103
- })
104
- return stream
105
- }
106
-
107
- function cannedError(message: string): ReturnType<typeof createAssistantMessageEventStream> {
108
- const stream = createAssistantMessageEventStream()
109
- const assistant = {
110
- role: 'assistant' as const,
111
- content: [],
112
- api: FAKE_API,
113
- provider: 'fake',
114
- model: 'm',
115
- usage: {
116
- input: 0,
117
- output: 0,
118
- cacheRead: 0,
119
- cacheWrite: 0,
120
- totalTokens: 0,
121
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
122
- },
123
- stopReason: 'error' as const,
124
- errorMessage: message,
125
- timestamp: Date.now(),
126
- }
127
- queueMicrotask(() => {
128
- stream.push({ type: 'error', reason: 'error', error: assistant })
129
- stream.end(assistant)
130
- })
131
- return stream
132
- }
133
-
134
- test('piProvider surfaces text, usage, and stopReason from a completed stream', async () => {
135
- registerFakeApi(() => cannedAssistantText('hello there'))
136
- const provider = piProvider({
137
- providerName: 'bazilion-fake',
138
- fallbackApi: FAKE_API,
139
- apiKey: 'k',
140
- })
141
- const res = await provider.chat({
142
- model: 'm',
143
- messages: [{ role: 'user', content: 'hi' }],
144
- })
145
- expect(res.content).toBe('hello there')
146
- expect(res.toolCalls).toEqual([])
147
- expect(res.stopReason).toBe('stop')
148
- expect(res.usage).toEqual({ promptTokens: 10, completionTokens: 5 })
149
- })
150
-
151
- test('piProvider calls onDelta for each streamed chunk', async () => {
152
- registerFakeApi(() => cannedAssistantText('abcdef'))
153
- const provider = piProvider({
154
- providerName: 'bazilion-fake',
155
- fallbackApi: FAKE_API,
156
- apiKey: 'k',
157
- })
158
- const deltas: string[] = []
159
- const res = await provider.chat({
160
- model: 'm',
161
- messages: [{ role: 'user', content: 'hi' }],
162
- onDelta: (d) => deltas.push(d),
163
- })
164
- expect(deltas.join('')).toBe('abcdef')
165
- expect(res.content).toBe('abcdef')
166
- })
167
-
168
- test('piProvider extracts toolCalls with JSON-stringified arguments', async () => {
169
- registerFakeApi(() => cannedToolCall('call_1', 'memory_write', { key: 'x.md', content: 'y' }))
170
- const provider = piProvider({
171
- providerName: 'bazilion-fake',
172
- fallbackApi: FAKE_API,
173
- apiKey: 'k',
174
- })
175
- const res = await provider.chat({
176
- model: 'm',
177
- messages: [{ role: 'user', content: 'remember' }],
178
- tools: [
179
- {
180
- name: 'memory_write',
181
- description: 'write memory',
182
- parameters: { type: 'object', properties: { key: { type: 'string' } } },
183
- },
184
- ],
185
- })
186
- expect(res.stopReason).toBe('tool_use')
187
- expect(res.toolCalls).toHaveLength(1)
188
- expect(res.toolCalls[0]?.name).toBe('memory_write')
189
- expect(JSON.parse(res.toolCalls[0]?.arguments ?? '{}')).toEqual({
190
- key: 'x.md',
191
- content: 'y',
192
- })
193
- })
194
-
195
- test('piProvider surfaces errorMessage as a thrown Error', async () => {
196
- registerFakeApi(() => cannedError('upstream blew up'))
197
- const provider = piProvider({
198
- providerName: 'bazilion-fake',
199
- fallbackApi: FAKE_API,
200
- apiKey: 'k',
201
- })
202
- await expect(
203
- provider.chat({ model: 'm', messages: [{ role: 'user', content: 'hi' }] }),
204
- ).rejects.toThrow(/upstream blew up/)
205
- })
206
-
207
- // --- registry ---
208
-
209
- test('registry resolves all 16 provider:model strings', () => {
210
- const reg = createProviderRegistry({
211
- anthropic: { apiKey: 'k' },
212
- openai: { apiKey: 'k' },
213
- google: { apiKey: 'k' },
214
- azureOpenai: { apiKey: 'k' },
215
- bedrock: {},
216
- googleVertex: {},
217
- mistral: { apiKey: 'k' },
218
- groq: { apiKey: 'k' },
219
- cerebras: { apiKey: 'k' },
220
- xai: { apiKey: 'k' },
221
- zai: { apiKey: 'k' },
222
- huggingface: { apiKey: 'k' },
223
- openrouter: { apiKey: 'k' },
224
- vercelAiGateway: { apiKey: 'k' },
225
- })
226
- for (const p of [
227
- 'anthropic',
228
- 'openai',
229
- 'google',
230
- 'google-vertex',
231
- 'azure-openai',
232
- 'bedrock',
233
- 'mistral',
234
- 'groq',
235
- 'cerebras',
236
- 'xai',
237
- 'zai',
238
- 'huggingface',
239
- 'openrouter',
240
- 'vercel-ai-gateway',
241
- 'lmstudio',
242
- 'ollama',
243
- ]) {
244
- const r = reg.resolve(`${p}:some-model`)
245
- expect(r.provider.name).toBe(p)
246
- expect(r.model).toBe('some-model')
247
- }
248
- })
249
-
250
- test('registry exposes lmstudio and ollama without any env config', () => {
251
- const reg = createProviderRegistry({})
252
- const lm = reg.resolve('lmstudio:llama-3-8b')
253
- expect(lm.provider.name).toBe('lmstudio')
254
- const ol = reg.resolve('ollama:llama2')
255
- expect(ol.provider.name).toBe('ollama')
256
- })
257
-
258
- test('registry rejects unknown names, malformed strings, and unconfigured providers', () => {
259
- const reg = createProviderRegistry({})
260
- expect(() => reg.resolve('martian:foo')).toThrow(/unknown provider/)
261
- expect(() => reg.resolve('not-a-model')).toThrow(/expected "provider:model"/)
262
- expect(() => reg.resolve('anthropic:m')).toThrow(/not configured/)
263
- expect(() => reg.resolve('groq:m')).toThrow(/GROQ_API_KEY/)
264
- })
265
-
266
- test('loadProviderConfigFromEnv picks up every standard env var', () => {
267
- const config = loadProviderConfigFromEnv({
268
- ANTHROPIC_API_KEY: 'a',
269
- OPENAI_API_KEY: 'o',
270
- GEMINI_API_KEY: 'g',
271
- GROQ_API_KEY: 'gr',
272
- CEREBRAS_API_KEY: 'ce',
273
- XAI_API_KEY: 'x',
274
- MISTRAL_API_KEY: 'mi',
275
- HF_TOKEN: 'hf',
276
- OPENROUTER_API_KEY: 'or',
277
- AI_GATEWAY_API_KEY: 'vg',
278
- } as NodeJS.ProcessEnv)
279
- expect(config.anthropic?.apiKey).toBe('a')
280
- expect(config.openai?.apiKey).toBe('o')
281
- expect(config.google?.apiKey).toBe('g')
282
- expect(config.groq?.apiKey).toBe('gr')
283
- expect(config.cerebras?.apiKey).toBe('ce')
284
- expect(config.xai?.apiKey).toBe('x')
285
- expect(config.mistral?.apiKey).toBe('mi')
286
- expect(config.huggingface?.apiKey).toBe('hf')
287
- expect(config.openrouter?.apiKey).toBe('or')
288
- expect(config.vercelAiGateway?.apiKey).toBe('vg')
289
- })
290
-
291
- test('loadProviderConfigFromEnv recognizes Bedrock auth via AWS_PROFILE', () => {
292
- const cfg = loadProviderConfigFromEnv({ AWS_PROFILE: 'default' } as NodeJS.ProcessEnv)
293
- expect(cfg.bedrock).toBeDefined()
294
- })
295
-
296
- test('loadProviderConfigFromEnv requires both project and location for Vertex', () => {
297
- expect(
298
- loadProviderConfigFromEnv({ GOOGLE_CLOUD_PROJECT: 'p' } as NodeJS.ProcessEnv).googleVertex,
299
- ).toBeUndefined()
300
- expect(
301
- loadProviderConfigFromEnv({
302
- GOOGLE_CLOUD_PROJECT: 'p',
303
- GOOGLE_CLOUD_LOCATION: 'us-central1',
304
- } as NodeJS.ProcessEnv).googleVertex,
305
- ).toBeDefined()
306
- })