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,221 +0,0 @@
1
- // Subprocess entrypoint for whole-run isolation — no SQLite handle inside.
2
- //
3
- // Reads a `WorkerInput` JSON object from stdin describing the turn to run:
4
- // the pre-resolved agent record, enabled-provider set, and message text.
5
- // The daemon does all DB-backed lookups before spawning us.
6
- //
7
- // Live messaging (the `send_message` / `read_inbox` / `wait_for_reply` tools)
8
- // is delegated back to the daemon over Node IPC: each tool invocation sends
9
- // a `{type: 'rpc', ...}` message and awaits the matching reply. The parent
10
- // dispatches the request through its own DB-backed `MessagingHost`.
11
- //
12
- // Ownership split now reads:
13
- // - daemon: SQLite handle, agent resolution, secrets envelope, scheduler,
14
- // run-cancel registry, messaging RPC dispatch.
15
- // - pi: conversation transcript, compaction entries, tool execution,
16
- // provider retries.
17
- // - this worker: glue between the two — runs `session.prompt(message)`,
18
- // translates `AgentSessionEvent`s into Bazilion `SessionEvent`s, emits
19
- // them as NDJSON `ChatFrame`s on stdout.
20
- //
21
- // Cancellation: SIGTERM from the parent → `session.abort()` → pi aborts the
22
- // current prompt → `agent_end` fires with error → translator emits an
23
- // `error` event so the client renders "cancelled" identically to other
24
- // failures.
25
-
26
- import { randomUUID } from 'node:crypto'
27
- import { join } from 'node:path'
28
- import type { ChatFrame, ResolvedAgent } from '@bazilion/api-types'
29
- import { resolvePaths } from '../../core/index.ts'
30
- import { qmdBackend } from '../memory/qmd.ts'
31
- import { piMessagesToProviderView, translatePiEvent } from '../pi/events.ts'
32
- import { createBazilionSession } from '../pi/session.ts'
33
- import type { IpcReply, IpcRequest, MessagingHost, RpcMethod } from './ipc-protocol.ts'
34
-
35
- interface WorkerInput {
36
- agent: ResolvedAgent
37
- message: string
38
- enabledProviders: string[]
39
- /** Pre-fetched API key for OAuth providers; undefined for env-key ones. */
40
- apiKey?: string
41
- }
42
-
43
- function emit(frame: ChatFrame): void {
44
- process.stdout.write(`${JSON.stringify(frame)}\n`)
45
- }
46
-
47
- async function readInput(): Promise<WorkerInput> {
48
- const chunks: Buffer[] = []
49
- for await (const c of process.stdin) chunks.push(c as Buffer)
50
- const raw = Buffer.concat(chunks).toString('utf8').trim()
51
- if (!raw) throw new Error('worker: empty stdin input')
52
- const parsed = JSON.parse(raw) as WorkerInput
53
- if (
54
- !parsed.agent ||
55
- typeof parsed.message !== 'string' ||
56
- !Array.isArray(parsed.enabledProviders)
57
- ) {
58
- throw new Error('worker: stdin must be {agent, message, enabledProviders}')
59
- }
60
- return parsed
61
- }
62
-
63
- /**
64
- * Build a `MessagingHost` that proxies every method through the IPC channel
65
- * back to the parent. Each call gets a fresh correlation id and waits for the
66
- * matching `rpc-reply`. The promise rejects when the channel disconnects mid-
67
- * request — that path only fires during shutdown, where the LLM loop has
68
- * already been aborted, so the unresolved tool call doesn't matter.
69
- */
70
- function createIpcMessagingHost(): MessagingHost {
71
- type Pending = { resolve: (value: unknown) => void; reject: (err: Error) => void }
72
- const pending = new Map<string, Pending>()
73
-
74
- process.on('message', (msg: unknown) => {
75
- if (!isIpcReply(msg)) return
76
- const entry = pending.get(msg.id)
77
- if (!entry) return
78
- pending.delete(msg.id)
79
- if (msg.ok) entry.resolve(msg.result)
80
- else entry.reject(new Error(msg.error))
81
- })
82
-
83
- process.on('disconnect', () => {
84
- for (const [, entry] of pending) entry.reject(new Error('worker IPC channel disconnected'))
85
- pending.clear()
86
- })
87
-
88
- function call<T>(method: RpcMethod, args: unknown): Promise<T> {
89
- if (!process.send) {
90
- return Promise.reject(new Error('worker: no IPC channel — daemon must spawn with stdio:ipc'))
91
- }
92
- const id = randomUUID()
93
- // The wire format is uniform "method + args"; the parent's dispatcher
94
- // re-narrows on `method`. TS can't follow that union construction here,
95
- // so we cast through unknown rather than push the discrimination through
96
- // every call site.
97
- const message = { type: 'rpc', id, method, args } as unknown as IpcRequest
98
- return new Promise<T>((resolve, reject) => {
99
- pending.set(id, {
100
- resolve: (v) => resolve(v as T),
101
- reject,
102
- })
103
- // process.send is fire-and-forget; the callback only signals serialization
104
- // failure. Treat it as a reject to surface the bug rather than hang.
105
- process.send?.(message, undefined, undefined, (err) => {
106
- if (err) {
107
- pending.delete(id)
108
- reject(err)
109
- }
110
- })
111
- })
112
- }
113
-
114
- return {
115
- agentExists: (agentId) => call<boolean>('agentExists', { agentId }),
116
- sendMessage: (input) => call<{ messageId: string }>('sendMessage', input),
117
- listInbox: (agentId, opts) => call('listInbox', { agentId, unreadOnly: opts.unreadOnly }),
118
- markRead: async (messageId) => {
119
- await call<null>('markRead', { messageId })
120
- },
121
- findReplies: (agentId, replyTo) => call('findReplies', { agentId, replyTo }),
122
- }
123
- }
124
-
125
- function isIpcReply(msg: unknown): msg is IpcReply {
126
- if (!msg || typeof msg !== 'object') return false
127
- const m = msg as Record<string, unknown>
128
- return m.type === 'rpc-reply' && typeof m.id === 'string' && typeof m.ok === 'boolean'
129
- }
130
-
131
- async function main(): Promise<void> {
132
- // Install signal handlers FIRST so a SIGTERM that arrives during setup
133
- // doesn't kill us with the Node default (exit-by-signal, no frame emitted).
134
- // Pre-session, we just exit cleanly with a synthetic `cancelled` event;
135
- // post-session, we delegate to `session.abort()` so pi unwinds the
136
- // provider fetch and surfaces its own `error` event.
137
- let aborted = false
138
- let abortSession: (() => void) | null = null
139
- const onSignal = (): void => {
140
- aborted = true
141
- if (abortSession) {
142
- abortSession()
143
- } else {
144
- try {
145
- emit({ kind: 'event', event: { type: 'error', error: 'cancelled' } })
146
- } catch {}
147
- process.exit(0)
148
- }
149
- }
150
- process.on('SIGTERM', onSignal)
151
- process.on('SIGINT', onSignal)
152
-
153
- const { agent, message, enabledProviders, apiKey } = await readInput()
154
-
155
- // Path resolution still happens in the worker — `resolvePaths()` only reads
156
- // the `BAZILION_HOME` env var the daemon hands down. No DB, no filesystem
157
- // probe of the live tree.
158
- const paths = resolvePaths()
159
-
160
- const memory = qmdBackend(join(agent.group.path, 'memory'))
161
- await memory.init()
162
-
163
- const messagingHost = createIpcMessagingHost()
164
-
165
- const { session, dispose } = await createBazilionSession({
166
- agent,
167
- paths,
168
- env: process.env,
169
- memory,
170
- enabledProviders: new Set(enabledProviders),
171
- messagingHost,
172
- apiKey,
173
- })
174
-
175
- abortSession = (): void => {
176
- void session.abort()
177
- }
178
-
179
- const unsubscribe = session.subscribe((piEvent) => {
180
- for (const ev of translatePiEvent(piEvent)) {
181
- emit({ kind: 'event', event: ev })
182
- }
183
- })
184
-
185
- try {
186
- await session.prompt(message)
187
- await session.agent.waitForIdle()
188
-
189
- emit({
190
- kind: 'done',
191
- messages: piMessagesToProviderView(session.agent.state.messages),
192
- })
193
- } catch (err) {
194
- if (!aborted) {
195
- emit({ kind: 'event', event: { type: 'error', error: (err as Error).message } })
196
- }
197
- emit({ kind: 'fatal', error: (err as Error).message })
198
- process.exitCode = 1
199
- } finally {
200
- unsubscribe()
201
- process.off('SIGTERM', onSignal)
202
- process.off('SIGINT', onSignal)
203
- dispose()
204
- // Detach from the parent's IPC channel so the worker's event loop can
205
- // exit. `process.on('message', …)` registered above otherwise keeps
206
- // the loop alive even after the turn finishes — Node treats an open
207
- // IPC handle as a live ref the same way it treats an open socket.
208
- try {
209
- process.disconnect?.()
210
- } catch {
211
- // already disconnected (parent closed first) — fine
212
- }
213
- }
214
- }
215
-
216
- main().catch((err) => {
217
- try {
218
- emit({ kind: 'fatal', error: (err as Error)?.message ?? String(err) })
219
- } catch {}
220
- process.exit(1)
221
- })
@@ -1,75 +0,0 @@
1
- // Worker ↔ daemon IPC protocol.
2
- //
3
- // The daemon is the single owner of `~/.bazilion`. Worker subprocesses run
4
- // agent turns without their own SQLite handle: anything that needs DB access
5
- // during a turn (currently the `send_message` / `read_inbox` /
6
- // `wait_for_reply` tools) is delegated to the parent over Node's built-in
7
- // `process.send` / `child.send` IPC channel (set up via `stdio: 'ipc'`).
8
- //
9
- // Wire format: one JSON object per `send`, with a stable `type` discriminator
10
- // and a correlation `id` for matching replies. The shape stays narrow on
11
- // purpose — only the messaging tools talk over IPC. Agent resolution,
12
- // provider gate, and merged env are pre-computed in the daemon and handed to
13
- // the worker on stdin (see `WorkerInput` in `worker/entry.ts`).
14
-
15
- import type { Message } from '@bazilion/api-types'
16
-
17
- export type RpcMethod = 'agentExists' | 'sendMessage' | 'listInbox' | 'markRead' | 'findReplies'
18
-
19
- export interface AgentExistsArgs {
20
- agentId: string
21
- }
22
-
23
- export interface SendMessageArgs {
24
- from: string
25
- to: string
26
- payload: string
27
- replyTo: string | null
28
- }
29
-
30
- export interface ListInboxArgs {
31
- agentId: string
32
- unreadOnly: boolean
33
- }
34
-
35
- export interface MarkReadArgs {
36
- messageId: string
37
- }
38
-
39
- export interface FindRepliesArgs {
40
- agentId: string
41
- replyTo: string
42
- }
43
-
44
- export type RpcArgs =
45
- | { method: 'agentExists'; args: AgentExistsArgs }
46
- | { method: 'sendMessage'; args: SendMessageArgs }
47
- | { method: 'listInbox'; args: ListInboxArgs }
48
- | { method: 'markRead'; args: MarkReadArgs }
49
- | { method: 'findReplies'; args: FindRepliesArgs }
50
-
51
- export type RpcResult =
52
- | { method: 'agentExists'; value: boolean }
53
- | { method: 'sendMessage'; value: { messageId: string } }
54
- | { method: 'listInbox'; value: Message[] }
55
- | { method: 'markRead'; value: null }
56
- | { method: 'findReplies'; value: Message[] }
57
-
58
- export type IpcRequest = { type: 'rpc'; id: string } & RpcArgs
59
-
60
- export type IpcReply =
61
- | { type: 'rpc-reply'; id: string; ok: true; result: unknown }
62
- | { type: 'rpc-reply'; id: string; ok: false; error: string }
63
-
64
- /**
65
- * The host-side surface the worker delegates messaging operations to. The
66
- * daemon implements this against `messageRepo` / `agentRepo`; tests can
67
- * provide an in-memory shim.
68
- */
69
- export interface MessagingHost {
70
- agentExists(agentId: string): boolean | Promise<boolean>
71
- sendMessage(input: SendMessageArgs): { messageId: string } | Promise<{ messageId: string }>
72
- listInbox(agentId: string, opts: { unreadOnly: boolean }): Message[] | Promise<Message[]>
73
- markRead(messageId: string): void | Promise<void>
74
- findReplies(agentId: string, replyTo: string): Message[] | Promise<Message[]>
75
- }
@@ -1,249 +0,0 @@
1
- // Parent-side spawn helper for whole-run subprocess isolation.
2
- //
3
- // `spawnWorkerTurn` launches `./entry.ts` and feeds it a `WorkerInput` JSON
4
- // blob on stdin describing the turn to run (pre-resolved agent record,
5
- // enabled-provider set, message text). The worker no longer opens its own
6
- // SQLite handle — the daemon is the sole owner of `~/.bazilion`.
7
- //
8
- // Two channels run between parent and child:
9
- // - stdout (NDJSON): the worker emits `ChatFrame`s; we line-parse and
10
- // yield each one to the caller.
11
- // - IPC (Node `stdio: 'ipc'`): the worker calls back into the parent for
12
- // anything that needs DB access during the turn (today: the messaging
13
- // tools `send_message` / `read_inbox` / `wait_for_reply`). We dispatch
14
- // each `IpcRequest` through the injected `MessagingHost` and reply with
15
- // `child.send`.
16
- //
17
- // Cancellation: wire an AbortSignal via `opts.signal`. On abort we send
18
- // SIGTERM to the child — the child has a signal handler that calls
19
- // `session.abort()`, which aborts the provider fetch and surfaces a final
20
- // `error` SessionEvent before the worker exits cleanly. If the child doesn't
21
- // exit within `killGraceMs`, we SIGKILL it.
22
-
23
- import { type ChildProcess, spawn } from 'node:child_process'
24
- import { createRequire } from 'node:module'
25
- import { fileURLToPath, pathToFileURL } from 'node:url'
26
- import type { ChatFrame, ResolvedAgent } from '@bazilion/api-types'
27
- import type { IpcReply, IpcRequest, MessagingHost } from './ipc-protocol.ts'
28
-
29
- const DEFAULT_KILL_GRACE_MS = 3_000
30
-
31
- const entryPath = fileURLToPath(new URL('./entry.ts', import.meta.url))
32
-
33
- // Node 22.12+ (the project's minimum) runs `.ts` files directly via
34
- // `--experimental-strip-types`, which just strips type annotations without
35
- // any transform / loader plumbing. We prefer it over tsx for worker spawns
36
- // because it shaves ~1s off the subprocess boot (no tsx module to resolve,
37
- // no TypeScript compiler to initialize per turn). tsx remains available as
38
- // a fallback for environments where strip-types is disabled — detected at
39
- // spawn time by probing `process.features.typescript`.
40
- //
41
- // `--no-warnings` silences Node's "ExperimentalWarning: Type Stripping"
42
- // banner that would otherwise appear on every child start.
43
- function workerSpawnArgs(): string[] {
44
- // `process.features.typescript` is 'strip' on builds where strip-types is
45
- // enabled, 'transform' on nightly/experimental, undefined otherwise. Cast
46
- // through unknown because the typing of ProcessFeatures doesn't yet
47
- // include this field in our @types/node.
48
- const tsFeature = (process.features as unknown as Record<string, unknown>).typescript
49
- if (typeof tsFeature === 'string' || tsFeature === true) {
50
- return ['--experimental-strip-types', '--no-warnings', entryPath]
51
- }
52
- return ['--import', tsxImportSpecifier(), entryPath]
53
- }
54
-
55
- let cachedTsxImport: string | null = null
56
- function tsxImportSpecifier(): string {
57
- if (cachedTsxImport) return cachedTsxImport
58
- // `require.resolve('tsx')` returns the absolute path to tsx's loader.mjs —
59
- // the ESM module that hooks the runtime. Passing it to `node --import` as a
60
- // file:// URL is the most portable way to activate tsx in a subprocess: it
61
- // avoids CWD-sensitive bare-specifier resolution, and works regardless of
62
- // where the caller lives in a pnpm-hoisted workspace.
63
- const req = createRequire(import.meta.url)
64
- cachedTsxImport = pathToFileURL(req.resolve('tsx')).href
65
- return cachedTsxImport
66
- }
67
-
68
- export interface WorkerTurnSpec {
69
- /** Pre-resolved agent record — the worker never queries the DB itself. */
70
- agent: ResolvedAgent
71
- /** First user-message text for this turn. */
72
- message: string
73
- /**
74
- * Names of providers the user has enabled in /config. Empty array means
75
- * no per-provider gating configured (all providers pass).
76
- */
77
- enabledProviders: string[]
78
- /**
79
- * Pre-fetched API key for the agent's provider. Required for OAuth-backed
80
- * providers (`openai-codex`) — the worker has no DB handle to read the
81
- * secrets table itself. Omit for env-key providers; `pi/session.ts` then
82
- * derives the key from `process.env`.
83
- */
84
- apiKey?: string
85
- }
86
-
87
- export interface SpawnWorkerOpts {
88
- /** Abort to kill the in-flight worker. */
89
- signal?: AbortSignal
90
- /** ms between SIGTERM and fallback SIGKILL (default 3000). */
91
- killGraceMs?: number
92
- /** Override env passed to the child. Defaults to `process.env`. */
93
- env?: NodeJS.ProcessEnv
94
- /**
95
- * Daemon-side implementation of the messaging tools the worker calls back
96
- * into via IPC. Omit only when the caller knows the agent will not invoke
97
- * any of `send_message` / `read_inbox` / `wait_for_reply` — passing it
98
- * costs nothing for turns that don't use messaging.
99
- */
100
- messagingHost?: MessagingHost
101
- }
102
-
103
- export async function* spawnWorkerTurn(
104
- spec: WorkerTurnSpec,
105
- opts: SpawnWorkerOpts = {},
106
- ): AsyncGenerator<ChatFrame, void, void> {
107
- const child = spawn(process.execPath, workerSpawnArgs(), {
108
- env: opts.env ?? process.env,
109
- stdio: ['pipe', 'pipe', 'inherit', 'ipc'],
110
- })
111
-
112
- if (opts.messagingHost) attachIpcHandler(child, opts.messagingHost)
113
-
114
- child.stdin?.write(JSON.stringify(spec))
115
- child.stdin?.end()
116
-
117
- const grace = opts.killGraceMs ?? DEFAULT_KILL_GRACE_MS
118
- let killTimer: NodeJS.Timeout | null = null
119
- const onAbort = (): void => {
120
- if (child.exitCode !== null || child.signalCode !== null) return
121
- try {
122
- child.kill('SIGTERM')
123
- } catch {
124
- // process may have already exited between our check and the kill
125
- }
126
- killTimer = setTimeout(() => {
127
- if (child.exitCode === null && child.signalCode === null) {
128
- try {
129
- child.kill('SIGKILL')
130
- } catch {}
131
- }
132
- }, grace)
133
- killTimer.unref()
134
- }
135
- if (opts.signal?.aborted) onAbort()
136
- else opts.signal?.addEventListener('abort', onAbort)
137
-
138
- const waitForExit: Promise<{ code: number | null; signal: NodeJS.Signals | null }> = new Promise(
139
- (resolve) => {
140
- if (child.exitCode !== null || child.signalCode !== null) {
141
- resolve({ code: child.exitCode, signal: child.signalCode })
142
- return
143
- }
144
- child.once('close', (code, signal) => resolve({ code, signal }))
145
- },
146
- )
147
-
148
- let emittedFatal = false
149
-
150
- try {
151
- let buf = ''
152
- if (!child.stdout) throw new Error('worker spawn: stdout pipe missing')
153
- for await (const chunk of child.stdout) {
154
- buf += (chunk as Buffer).toString('utf8')
155
- let idx = buf.indexOf('\n')
156
- while (idx !== -1) {
157
- const line = buf.slice(0, idx)
158
- buf = buf.slice(idx + 1)
159
- if (line.trim()) {
160
- const frame = parseFrame(line)
161
- if (frame.kind === 'fatal') emittedFatal = true
162
- yield frame
163
- }
164
- idx = buf.indexOf('\n')
165
- }
166
- }
167
- if (buf.trim()) {
168
- const frame = parseFrame(buf)
169
- if (frame.kind === 'fatal') emittedFatal = true
170
- yield frame
171
- }
172
-
173
- const exit = await waitForExit
174
- // Child exited without emitting a fatal frame but with non-zero status —
175
- // surface that so the caller doesn't silently swallow a crash. Cancelled
176
- // turns produce an `error` SessionEvent via the worker's signal handler
177
- // and exit cleanly with code 0; only truly unexpected exits hit this.
178
- if (!emittedFatal && exit.code !== 0 && exit.code !== null) {
179
- yield {
180
- kind: 'fatal',
181
- error: `worker exited with code ${exit.code}${exit.signal ? ` (${exit.signal})` : ''}`,
182
- }
183
- } else if (!emittedFatal && exit.signal && exit.code === null) {
184
- yield { kind: 'fatal', error: `worker killed by ${exit.signal}` }
185
- }
186
- } finally {
187
- opts.signal?.removeEventListener('abort', onAbort)
188
- if (killTimer) clearTimeout(killTimer)
189
- try {
190
- child.disconnect()
191
- } catch {
192
- // already disconnected (child closed first) — fine
193
- }
194
- }
195
- }
196
-
197
- function parseFrame(line: string): ChatFrame {
198
- try {
199
- return JSON.parse(line) as ChatFrame
200
- } catch {
201
- return { kind: 'fatal', error: `worker emitted malformed frame: ${line.slice(0, 200)}` }
202
- }
203
- }
204
-
205
- function attachIpcHandler(child: ChildProcess, host: MessagingHost): void {
206
- child.on('message', (msg: unknown) => {
207
- if (!isIpcRequest(msg)) return
208
- void dispatch(msg, host).then((reply) => {
209
- try {
210
- child.send?.(reply)
211
- } catch {
212
- // child may have exited between request and reply — drop silently
213
- }
214
- })
215
- })
216
- }
217
-
218
- function isIpcRequest(msg: unknown): msg is IpcRequest {
219
- if (!msg || typeof msg !== 'object') return false
220
- const m = msg as Record<string, unknown>
221
- return m.type === 'rpc' && typeof m.id === 'string' && typeof m.method === 'string'
222
- }
223
-
224
- async function dispatch(req: IpcRequest, host: MessagingHost): Promise<IpcReply> {
225
- try {
226
- let result: unknown
227
- switch (req.method) {
228
- case 'agentExists':
229
- result = await host.agentExists(req.args.agentId)
230
- break
231
- case 'sendMessage':
232
- result = await host.sendMessage(req.args)
233
- break
234
- case 'listInbox':
235
- result = await host.listInbox(req.args.agentId, { unreadOnly: req.args.unreadOnly })
236
- break
237
- case 'markRead':
238
- await host.markRead(req.args.messageId)
239
- result = null
240
- break
241
- case 'findReplies':
242
- result = await host.findReplies(req.args.agentId, req.args.replyTo)
243
- break
244
- }
245
- return { type: 'rpc-reply', id: req.id, ok: true, result }
246
- } catch (err) {
247
- return { type: 'rpc-reply', id: req.id, ok: false, error: (err as Error).message }
248
- }
249
- }