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,539 +0,0 @@
1
- import { createServer, type ServerResponse } from 'node:http'
2
- import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'
3
- import { openDb, resolveAgent, resolvePaths } from '../../daemon/src/core/index.ts'
4
- import { countSessionMessagesForTest, seedSessionForTest } from '../../daemon/src/runtime/index.ts'
5
- import { extractAgentId } from './helpers.ts'
6
- import { startTestServer, type TestServer } from './server-fixture.ts'
7
-
8
- type MockHandler = unknown | ((res: ServerResponse) => void)
9
-
10
- interface MockLlm {
11
- url: string
12
- push(responses: MockHandler[]): void
13
- reset(): void
14
- callCount(): number
15
- stop(): Promise<void>
16
- }
17
-
18
- interface CannedMessage {
19
- role?: string
20
- content?: string | null
21
- tool_calls?: Array<{
22
- id: string
23
- type: string
24
- function: { name: string; arguments: string }
25
- }>
26
- }
27
- interface CannedChoice {
28
- message: CannedMessage
29
- finish_reason?: string
30
- }
31
- interface CannedResponse {
32
- choices: CannedChoice[]
33
- usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number }
34
- }
35
-
36
- /**
37
- * Translate a canned non-streaming OpenAI-shaped response into OpenAI SSE
38
- * chunks and write them to `res`. Pi-ai's openai-completions client always
39
- * runs in `stream: true` mode, so LMStudio-style JSON bodies need to be
40
- * re-chunked into `data: {...}\n\n` lines terminated by `data: [DONE]`.
41
- */
42
- function writeSseFromCanned(res: ServerResponse, canned: CannedResponse): void {
43
- res.writeHead(200, {
44
- 'content-type': 'text/event-stream',
45
- 'cache-control': 'no-cache',
46
- connection: 'keep-alive',
47
- })
48
- const choice = canned.choices[0]
49
- const message = choice?.message ?? {}
50
- const finish = choice?.finish_reason ?? 'stop'
51
-
52
- // Role delta (always first in OpenAI's SSE protocol).
53
- const roleChunk = {
54
- id: 'chatcmpl-mock',
55
- object: 'chat.completion.chunk',
56
- choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }],
57
- }
58
- res.write(`data: ${JSON.stringify(roleChunk)}\n\n`)
59
-
60
- if (typeof message.content === 'string' && message.content.length > 0) {
61
- const textChunk = {
62
- id: 'chatcmpl-mock',
63
- object: 'chat.completion.chunk',
64
- choices: [{ index: 0, delta: { content: message.content }, finish_reason: null }],
65
- }
66
- res.write(`data: ${JSON.stringify(textChunk)}\n\n`)
67
- }
68
-
69
- if (message.tool_calls && message.tool_calls.length > 0) {
70
- const toolChunk = {
71
- id: 'chatcmpl-mock',
72
- object: 'chat.completion.chunk',
73
- choices: [
74
- {
75
- index: 0,
76
- delta: {
77
- tool_calls: message.tool_calls.map((tc, i) => ({
78
- index: i,
79
- id: tc.id,
80
- type: tc.type,
81
- function: { name: tc.function.name, arguments: tc.function.arguments },
82
- })),
83
- },
84
- finish_reason: null,
85
- },
86
- ],
87
- }
88
- res.write(`data: ${JSON.stringify(toolChunk)}\n\n`)
89
- }
90
-
91
- const finalChunk = {
92
- id: 'chatcmpl-mock',
93
- object: 'chat.completion.chunk',
94
- choices: [{ index: 0, delta: {}, finish_reason: finish }],
95
- usage: canned.usage,
96
- }
97
- res.write(`data: ${JSON.stringify(finalChunk)}\n\n`)
98
- res.write('data: [DONE]\n\n')
99
- res.end()
100
- }
101
-
102
- /**
103
- * Start a node:http server that serves LMStudio-style /v1/chat/completions
104
- * responses from an in-memory queue. Push responses before each `agent chat`
105
- * turn — the server consumes them in order. Queue items may be objects
106
- * (OpenAI-shaped non-streaming responses, translated to SSE on the wire) or
107
- * functions `(res) => void` for custom behavior (e.g. a handler that never
108
- * responds, to exercise client-side cancellation).
109
- */
110
- function startLmStudioMock(): Promise<MockLlm> {
111
- const queue: MockHandler[] = []
112
- let calls = 0
113
- const server = createServer((_req, res) => {
114
- calls++
115
- if (queue.length === 0) {
116
- res.writeHead(500, { 'content-type': 'text/plain' })
117
- res.end('mock response queue empty')
118
- return
119
- }
120
- const item = queue.shift()
121
- if (typeof item === 'function') {
122
- ;(item as (res: ServerResponse) => void)(res)
123
- return
124
- }
125
- writeSseFromCanned(res, item as CannedResponse)
126
- })
127
- return new Promise((resolve) => {
128
- server.listen(0, () => {
129
- const addr = server.address()
130
- if (!addr || typeof addr === 'string') throw new Error('unexpected server address')
131
- resolve({
132
- url: `http://localhost:${addr.port}/v1`,
133
- push(items) {
134
- queue.push(...items)
135
- },
136
- reset() {
137
- queue.length = 0
138
- calls = 0
139
- },
140
- callCount: () => calls,
141
- stop: () => new Promise((r) => server.close(() => r())),
142
- })
143
- })
144
- })
145
- }
146
-
147
- let mock: MockLlm
148
- let server: TestServer
149
-
150
- beforeAll(async () => {
151
- mock = await startLmStudioMock()
152
- server = await startTestServer({ LMSTUDIO_URL: mock.url })
153
- })
154
- afterAll(async () => {
155
- await server.stop()
156
- await mock.stop()
157
- })
158
- beforeEach(() => {
159
- mock.reset()
160
- server.reset()
161
- })
162
-
163
- async function spawnLmStudioAgent(): Promise<string> {
164
- await server.cli(['profile', 'create', 'p', '--model', 'lmstudio:test-model'])
165
- const r = await server.cli(['agent', 'spawn', '--profile', 'p'])
166
- return extractAgentId(r.stdout)
167
- }
168
-
169
- test('agent chat --message round-trips with the provider', async () => {
170
- const agentId = await spawnLmStudioAgent()
171
- mock.push([
172
- {
173
- choices: [
174
- {
175
- message: { role: 'assistant', content: 'hello from mock' },
176
- finish_reason: 'stop',
177
- },
178
- ],
179
- },
180
- ])
181
-
182
- const r = await server.cli(['agent', 'chat', agentId, '--message', 'hi'])
183
- expect(r.exitCode).toBe(0)
184
- expect(r.stdout).toContain('hello from mock')
185
- })
186
-
187
- test('memory persists across chat invocations', async () => {
188
- const agentId = await spawnLmStudioAgent()
189
-
190
- // Session 1: agent saves a memory via tool call
191
- mock.push([
192
- {
193
- choices: [
194
- {
195
- message: {
196
- role: 'assistant',
197
- content: null,
198
- tool_calls: [
199
- {
200
- id: 'call_1',
201
- type: 'function',
202
- function: {
203
- name: 'memory_write',
204
- arguments: '{"key":"prefs.md","content":"User likes hiking on Sundays."}',
205
- },
206
- },
207
- ],
208
- },
209
- finish_reason: 'tool_calls',
210
- },
211
- ],
212
- },
213
- {
214
- choices: [
215
- {
216
- message: { role: 'assistant', content: 'Got it, saved.' },
217
- finish_reason: 'stop',
218
- },
219
- ],
220
- },
221
- ])
222
- let r = await server.cli(['agent', 'chat', agentId, '--message', 'I like hiking on Sundays'])
223
- expect(r.exitCode).toBe(0)
224
- expect(r.stdout).toContain('memory_write')
225
- expect(r.stdout).toContain('Got it')
226
-
227
- // Session 2: new chat invocation, agent searches memory and finds it
228
- mock.push([
229
- {
230
- choices: [
231
- {
232
- message: {
233
- role: 'assistant',
234
- content: null,
235
- tool_calls: [
236
- {
237
- id: 'call_2',
238
- type: 'function',
239
- function: { name: 'memory_search', arguments: '{"query":"hiking"}' },
240
- },
241
- ],
242
- },
243
- finish_reason: 'tool_calls',
244
- },
245
- ],
246
- },
247
- {
248
- choices: [
249
- {
250
- message: { role: 'assistant', content: 'You like hiking on Sundays.' },
251
- finish_reason: 'stop',
252
- },
253
- ],
254
- },
255
- ])
256
- r = await server.cli(['agent', 'chat', agentId, '--message', 'what do I like to do on weekends?'])
257
- expect(r.exitCode).toBe(0)
258
- expect(r.stdout).toContain('hiking')
259
- expect(r.stdout).toContain('You like hiking on Sundays')
260
- })
261
-
262
- test('BOOTSTRAP.md is removed when bootstrap_done tool is called', async () => {
263
- const agentId = await spawnLmStudioAgent()
264
- mock.push([
265
- {
266
- choices: [
267
- {
268
- message: {
269
- role: 'assistant',
270
- content: null,
271
- tool_calls: [
272
- {
273
- id: 'call_1',
274
- type: 'function',
275
- function: { name: 'bootstrap_done', arguments: '{}' },
276
- },
277
- ],
278
- },
279
- finish_reason: 'tool_calls',
280
- },
281
- ],
282
- },
283
- {
284
- choices: [
285
- {
286
- message: { role: 'assistant', content: 'Welcome aboard.' },
287
- finish_reason: 'stop',
288
- },
289
- ],
290
- },
291
- ])
292
-
293
- const r = await server.cli(['agent', 'chat', agentId, '--message', 'I am ready'])
294
- expect(r.exitCode).toBe(0)
295
- expect(r.stdout).toContain('bootstrap_done')
296
-
297
- // Verify BOOTSTRAP.md is gone via agent show (which reads the dir path)
298
- const show = await server.cli(['agent', 'show', agentId])
299
- expect(show.exitCode).toBe(0)
300
- const m = show.stdout.match(/dir:\s+(\S+)/)
301
- expect(m?.[1]).toBeDefined()
302
- if (m?.[1]) {
303
- const { existsSync } = await import('node:fs')
304
- const { join } = await import('node:path')
305
- expect(existsSync(join(m[1], 'BOOTSTRAP.md'))).toBe(false)
306
- }
307
- })
308
-
309
- test('failed turn surfaces an error event and leaves the agent ready for a retry', async () => {
310
- // Pi's SessionManager writes entries as they happen (not at turn end), so
311
- // a failed turn doesn't "roll back" a user message the way the old
312
- // pre-pi worker did. The test's concern here is simpler: a non-retryable
313
- // provider error surfaces as an error event, and a fresh follow-up turn
314
- // succeeds cleanly.
315
- const agentId = await spawnLmStudioAgent()
316
-
317
- mock.push([
318
- (res: ServerResponse) => {
319
- res.writeHead(400, { 'content-type': 'application/json' })
320
- res.end(JSON.stringify({ error: { code: 'invalid_request', message: 'bad model' } }))
321
- },
322
- ])
323
-
324
- const failR = await server.cli(['agent', 'chat', agentId, '--message', 'go on'])
325
- expect(failR.stdout).toMatch(/\[error:/)
326
-
327
- mock.push([
328
- {
329
- choices: [
330
- { message: { role: 'assistant', content: 'recovered reply' }, finish_reason: 'stop' },
331
- ],
332
- },
333
- ])
334
- const okR = await server.cli(['agent', 'chat', agentId, '--message', 'go on'])
335
- expect(okR.exitCode).toBe(0)
336
- expect(okR.stdout).toContain('recovered reply')
337
- })
338
-
339
- test('agent chat can be cancelled mid-flight via `agent cancel`', async () => {
340
- const agentId = await spawnLmStudioAgent()
341
-
342
- // The mock's sole handler never responds — the provider's fetch will hang
343
- // until its AbortSignal fires, giving us a window to cancel the turn.
344
- const callCountBefore = mock.callCount()
345
- mock.push([
346
- (_res: ServerResponse) => {
347
- // intentionally left hanging
348
- },
349
- ])
350
-
351
- // Kick off the chat in the background. The CLI subprocess will block
352
- // inside the NDJSON stream until abort.
353
- const chatPromise = server.cli(['agent', 'chat', agentId, '--message', 'hi'])
354
-
355
- // Wait for the worker to actually reach the provider fetch (mock receives a
356
- // request). Cancelling earlier races with worker startup — SIGTERM during
357
- // Node bootstrap has no handler installed and exits with the signal,
358
- // producing "worker killed by SIGTERM" instead of the cancelled-event path.
359
- let providerHit = false
360
- for (let i = 0; i < 100; i++) {
361
- if (mock.callCount() > callCountBefore) {
362
- providerHit = true
363
- break
364
- }
365
- await new Promise((r) => setTimeout(r, 100))
366
- }
367
- if (!providerHit) throw new Error('worker did not reach the provider fetch in time')
368
-
369
- const cancelR = await server.cli(['agent', 'cancel', agentId])
370
- expect(cancelR.exitCode).toBe(0)
371
-
372
- // The chat subprocess should terminate promptly. With the mock still
373
- // hanging, the fetch must have been aborted — otherwise this would time
374
- // out. Pi surfaces aborts as a `cancelled` error or provider-side text
375
- // like "Request was aborted." — accept either.
376
- const chatR = await chatPromise
377
- expect(chatR.stdout + chatR.stderr).toMatch(/cancelled|aborted/i)
378
- })
379
-
380
- // --- chat-reset + chat-trim ---
381
-
382
- // Seed a canonical pi session file on disk with `n` synthetic user/assistant
383
- // messages. The on-disk JSONL is the authoritative source of conversation
384
- // state after migration 0004 dropped `chat_messages`; hitting tests at this
385
- // layer keeps them faithful to what a real worker writes. See
386
- // `seedSessionForTest` in runtime for the underlying pi SessionManager
387
- // append calls.
388
- function seedChatHistory(home: string, agentId: string, n: number): void {
389
- const paths = resolvePaths(home)
390
- const db = openDb(paths.db)
391
- try {
392
- const resolved = resolveAgent(db, paths, agentId)
393
- const messages = Array.from({ length: n }, (_, i) =>
394
- i % 2 === 0
395
- ? { role: 'user' as const, text: `msg ${i}` }
396
- : { role: 'assistant' as const, text: `reply ${i}` },
397
- )
398
- seedSessionForTest(resolved, paths, messages)
399
- } finally {
400
- db.close()
401
- }
402
- }
403
-
404
- function readChatLen(home: string, agentId: string): number {
405
- const paths = resolvePaths(home)
406
- const db = openDb(paths.db)
407
- try {
408
- const resolved = resolveAgent(db, paths, agentId)
409
- return countSessionMessagesForTest(resolved, paths)
410
- } finally {
411
- db.close()
412
- }
413
- }
414
-
415
- test('agent chat-reset --force wipes history without prompting', async () => {
416
- const agentId = await spawnLmStudioAgent()
417
- seedChatHistory(server.home, agentId, 6)
418
- expect(readChatLen(server.home, agentId)).toBe(6)
419
-
420
- const r = await server.cli(['agent', 'chat-reset', agentId, '--force'])
421
- expect(r.exitCode).toBe(0)
422
- expect(r.stdout).toContain('reset')
423
- expect(readChatLen(server.home, agentId)).toBe(0)
424
- })
425
-
426
- test('agent chat-trim reports the before/after counts from pi branching', async () => {
427
- const agentId = await spawnLmStudioAgent()
428
- seedChatHistory(server.home, agentId, 6)
429
-
430
- const r = await server.cli(['agent', 'chat-trim', agentId, '--keep', '3'])
431
- expect(r.exitCode).toBe(0)
432
- // Pi's `branch()` moves the leaf pointer in-memory; the session file
433
- // keeps the original entries on disk (append-only). The next turn
434
- // will resume from the branched leaf. CLI output is what we assert.
435
- expect(r.stdout).toMatch(/6 → 3/)
436
- })
437
-
438
- test('agent chat-trim rejects non-integer --keep with a clear error', async () => {
439
- const agentId = await spawnLmStudioAgent()
440
- seedChatHistory(server.home, agentId, 4)
441
-
442
- const r = await server.cli(['agent', 'chat-trim', agentId, '--keep', '2.5'])
443
- expect(r.exitCode).not.toBe(0)
444
- expect(r.stderr + r.stdout).toMatch(/non-negative integer/)
445
- // Session file untouched on invalid input
446
- expect(readChatLen(server.home, agentId)).toBe(4)
447
- })
448
-
449
- // --- chat-context + chat-compact ---
450
-
451
- test('agent chat-context reports system prompt, tools, skills, history breakdown', async () => {
452
- const agentId = await spawnLmStudioAgent()
453
- seedChatHistory(server.home, agentId, 6)
454
-
455
- const r = await server.cli(['agent', 'chat-context', agentId])
456
- expect(r.exitCode).toBe(0)
457
- expect(r.stdout).toMatch(/## system prompt/)
458
- expect(r.stdout).toMatch(/## tools/)
459
- expect(r.stdout).toMatch(/count:\s+\d+/)
460
- // 6 seeded message entries, 0 compactions
461
- expect(r.stdout).toMatch(/messages:\s+6/)
462
- expect(r.stdout).toMatch(/compactions:\s+0/)
463
- expect(r.stdout).toMatch(/TOTAL:/)
464
- })
465
-
466
- test('agent chat-context --json emits the structured ChatContextResponse', async () => {
467
- const agentId = await spawnLmStudioAgent()
468
- seedChatHistory(server.home, agentId, 4)
469
-
470
- const r = await server.cli(['agent', 'chat-context', agentId, '--json'])
471
- expect(r.exitCode).toBe(0)
472
- const body = JSON.parse(r.stdout)
473
- expect(body.agentId).toBe(agentId)
474
- expect(typeof body.model).toBe('string')
475
- expect(body.history).toMatchObject({ messageEntries: 4, compactionEntries: 0 })
476
- expect(typeof body.history.chars).toBe('number')
477
- expect(typeof body.history.bytes).toBe('number')
478
- expect(typeof body.history.tokensEstimate).toBe('number')
479
- expect(typeof body.tools.count).toBe('number')
480
- expect(Array.isArray(body.tools.entries)).toBe(true)
481
- expect(typeof body.totals.chars).toBe('number')
482
- })
483
-
484
- test('agent chat-context on an empty history returns zero history entries', async () => {
485
- const agentId = await spawnLmStudioAgent()
486
- const r = await server.cli(['agent', 'chat-context', agentId, '--json'])
487
- expect(r.exitCode).toBe(0)
488
- const body = JSON.parse(r.stdout)
489
- expect(body.history).toMatchObject({
490
- messageEntries: 0,
491
- compactionEntries: 0,
492
- chars: 0,
493
- })
494
- // System prompt + tools still contribute to totals even on a fresh agent.
495
- expect(body.totals.chars).toBeGreaterThan(0)
496
- })
497
-
498
- test('agent chat-compact summarizes the head via pi session.compact', async () => {
499
- const agentId = await spawnLmStudioAgent()
500
- seedChatHistory(server.home, agentId, 10)
501
-
502
- // Pi's session.compact() uses the agent's current model to generate the
503
- // summary — the mock has to answer one LLM call.
504
- mock.push([
505
- {
506
- choices: [
507
- {
508
- message: {
509
- role: 'assistant',
510
- content: 'The user sent several questions; assistant replied each time.',
511
- },
512
- finish_reason: 'stop',
513
- },
514
- ],
515
- },
516
- ])
517
-
518
- const r = await server.cli(['agent', 'chat-compact', agentId, '--force'])
519
- // Pi may be stricter than us about when it bothers to compact; allow
520
- // either a success summary or a "nothing to compact" refusal. The key
521
- // thing is the endpoint runs without throwing.
522
- if (r.exitCode === 0) {
523
- expect(r.stdout).toMatch(/compacted:/)
524
- } else {
525
- // Non-zero exit is acceptable when pi declines to compact a small
526
- // history. Surface the error reason for diagnostics.
527
- expect(r.stderr + r.stdout).toMatch(/(error|nothing)/i)
528
- }
529
- })
530
-
531
- test('agent chat-compact errors out on empty history', async () => {
532
- const agentId = await spawnLmStudioAgent()
533
- const r = await server.cli(['agent', 'chat-compact', agentId, '--force'])
534
- // With no session file at all, pi either refuses or succeeds with a
535
- // trivial empty compaction. We assert the command completes without a
536
- // worker crash — actual exit code depends on pi's behavior for empty
537
- // sessions, which can change between pi releases.
538
- expect(r.stdout + r.stderr).toBeTruthy()
539
- })
@@ -1,29 +0,0 @@
1
- import { expect, test } from 'vitest'
2
- import { columnize } from '../src/columnize.ts'
3
-
4
- test('pads each column to its widest cell', () => {
5
- const out = columnize([
6
- ['a', 'short', 'x'],
7
- ['ab', 'longer-word', 'y'],
8
- ['abc', 'x', 'z'],
9
- ])
10
- expect(out).toEqual(['a short x', 'ab longer-word y', 'abc x z'])
11
- })
12
-
13
- test("doesn't pad the trailing column (keeps `| wc` friendly)", () => {
14
- const out = columnize([
15
- ['a', 'b'],
16
- ['aa', 'bb'],
17
- ])
18
- for (const line of out) {
19
- expect(line.endsWith(' ')).toBe(false)
20
- }
21
- })
22
-
23
- test('empty input returns empty output', () => {
24
- expect(columnize([])).toEqual([])
25
- })
26
-
27
- test('single-row tables still align the first columns', () => {
28
- expect(columnize([['a', 'b', 'c']])).toEqual(['a b c'])
29
- })
@@ -1,45 +0,0 @@
1
- import { afterEach, beforeEach, expect, test } from 'vitest'
2
- import { makeHome, runCli, type TestHome } from './helpers.ts'
3
-
4
- let h: TestHome
5
- beforeEach(() => {
6
- h = makeHome()
7
- })
8
- afterEach(() => h.cleanup())
9
-
10
- test('completion bash prints a sourceable function + complete directive', async () => {
11
- const res = await runCli(['completion', 'bash'], h.home)
12
- expect(res.exitCode).toBe(0)
13
- expect(res.stdout).toContain('_bazilion_completion()')
14
- expect(res.stdout).toContain('complete -F _bazilion_completion bazilion')
15
- // Top-level subcommands are enumerated
16
- expect(res.stdout).toContain('"serve')
17
- expect(res.stdout).toContain('agent')
18
- expect(res.stdout).toContain('backup')
19
- // Nested subcommand case arm exists
20
- expect(res.stdout).toContain('"agent")')
21
- expect(res.stdout).toContain('"agent chat")')
22
- // Flag enumeration (at least one known flag)
23
- expect(res.stdout).toContain('--profile')
24
- })
25
-
26
- test('completion zsh wraps bashcompinit around the bash script', async () => {
27
- const res = await runCli(['completion', 'zsh'], h.home)
28
- expect(res.exitCode).toBe(0)
29
- expect(res.stdout).toContain('bashcompinit')
30
- expect(res.stdout).toContain('_bazilion_completion()')
31
- })
32
-
33
- test('completion fish emits subcommand + guard rules', async () => {
34
- const res = await runCli(['completion', 'fish'], h.home)
35
- expect(res.exitCode).toBe(0)
36
- expect(res.stdout).toContain('complete -c bazilion')
37
- expect(res.stdout).toContain('__fish_use_subcommand')
38
- expect(res.stdout).toContain('__fish_seen_subcommand_from agent')
39
- })
40
-
41
- test('completion rejects an unknown shell', async () => {
42
- const res = await runCli(['completion', 'tcsh'], h.home)
43
- expect(res.exitCode).not.toBe(0)
44
- expect(res.stderr + res.stdout).toMatch(/unsupported shell: tcsh/)
45
- })