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.
- package/README.md +53 -152
- package/dist/cli.js +2730 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.js +5888 -0
- package/dist/daemon.js.map +1 -0
- package/dist/worker.js +1830 -0
- package/dist/worker.js.map +1 -0
- package/package.json +57 -28
- package/.understand-anything/.understandignore +0 -25
- package/.understand-anything/fingerprints.json +0 -14267
- package/.understand-anything/knowledge-graph.json +0 -18128
- package/.understand-anything/meta.json +0 -6
- package/CLAUDE.md +0 -164
- package/apps/cli/package.json +0 -21
- package/apps/cli/src/auth-file.ts +0 -18
- package/apps/cli/src/client.ts +0 -37
- package/apps/cli/src/columnize.ts +0 -32
- package/apps/cli/src/commands/agent.ts +0 -574
- package/apps/cli/src/commands/auth.ts +0 -110
- package/apps/cli/src/commands/backup.ts +0 -135
- package/apps/cli/src/commands/completion.ts +0 -155
- package/apps/cli/src/commands/config.ts +0 -95
- package/apps/cli/src/commands/doctor.ts +0 -131
- package/apps/cli/src/commands/group.ts +0 -132
- package/apps/cli/src/commands/inbox.ts +0 -82
- package/apps/cli/src/commands/login.ts +0 -73
- package/apps/cli/src/commands/memory.ts +0 -106
- package/apps/cli/src/commands/profile.ts +0 -259
- package/apps/cli/src/commands/provider.ts +0 -170
- package/apps/cli/src/commands/send.ts +0 -24
- package/apps/cli/src/commands/serve.ts +0 -89
- package/apps/cli/src/commands/skill.ts +0 -120
- package/apps/cli/src/commands/token.ts +0 -148
- package/apps/cli/src/commands/trigger.ts +0 -129
- package/apps/cli/src/commands/uninstall.ts +0 -156
- package/apps/cli/src/index.ts +0 -196
- package/apps/cli/src/paths.ts +0 -12
- package/apps/cli/test/agent.test.ts +0 -213
- package/apps/cli/test/backup.test.ts +0 -95
- package/apps/cli/test/chat.test.ts +0 -539
- package/apps/cli/test/columnize.test.ts +0 -29
- package/apps/cli/test/completion.test.ts +0 -45
- package/apps/cli/test/config-page.test.ts +0 -151
- package/apps/cli/test/group.test.ts +0 -74
- package/apps/cli/test/helpers.ts +0 -70
- package/apps/cli/test/inbox-autodeliver.test.ts +0 -143
- package/apps/cli/test/inbox.test.ts +0 -147
- package/apps/cli/test/memory.test.ts +0 -69
- package/apps/cli/test/profile.test.ts +0 -110
- package/apps/cli/test/send.test.ts +0 -30
- package/apps/cli/test/server-fixture.ts +0 -212
- package/apps/cli/test/session-head.test.ts +0 -45
- package/apps/cli/test/skill.test.ts +0 -128
- package/apps/cli/test/token.test.ts +0 -109
- package/apps/cli/test/trigger.test.ts +0 -245
- package/apps/cli/tsconfig.json +0 -4
- package/apps/daemon/package.json +0 -31
- package/apps/daemon/src/app.ts +0 -41
- package/apps/daemon/src/core/agent/archive.ts +0 -8
- package/apps/daemon/src/core/agent/delete.ts +0 -30
- package/apps/daemon/src/core/agent/resolve.ts +0 -31
- package/apps/daemon/src/core/agent/spawn.ts +0 -95
- package/apps/daemon/src/core/agent/unarchive.ts +0 -11
- package/apps/daemon/src/core/availableModels.ts +0 -58
- package/apps/daemon/src/core/db/client.ts +0 -113
- package/apps/daemon/src/core/db/migrate.ts +0 -41
- package/apps/daemon/src/core/db/migrations/0001_init.sql +0 -179
- package/apps/daemon/src/core/group/delete.ts +0 -21
- package/apps/daemon/src/core/group/register.ts +0 -68
- package/apps/daemon/src/core/index.ts +0 -71
- package/apps/daemon/src/core/paths.ts +0 -56
- package/apps/daemon/src/core/profile/create.ts +0 -78
- package/apps/daemon/src/core/profile/delete.ts +0 -26
- package/apps/daemon/src/core/profile/identity.ts +0 -70
- package/apps/daemon/src/core/profile/load.ts +0 -45
- package/apps/daemon/src/core/profile/seed.ts +0 -74
- package/apps/daemon/src/core/profile/templates.ts +0 -60
- package/apps/daemon/src/core/profile/update.ts +0 -57
- package/apps/daemon/src/core/profile/validate.ts +0 -9
- package/apps/daemon/src/core/repos/agents.ts +0 -202
- package/apps/daemon/src/core/repos/config.ts +0 -78
- package/apps/daemon/src/core/repos/groups.ts +0 -55
- package/apps/daemon/src/core/repos/messages.ts +0 -127
- package/apps/daemon/src/core/repos/profiles.ts +0 -83
- package/apps/daemon/src/core/repos/providerModels.ts +0 -58
- package/apps/daemon/src/core/repos/providerState.ts +0 -37
- package/apps/daemon/src/core/repos/secrets.ts +0 -145
- package/apps/daemon/src/core/repos/skillMeta.ts +0 -49
- package/apps/daemon/src/core/repos/triggers.ts +0 -101
- package/apps/daemon/src/core/repos/webTokens.ts +0 -87
- package/apps/daemon/src/core/secrets.ts +0 -65
- package/apps/daemon/src/core/services.ts +0 -264
- package/apps/daemon/src/core/skills/discover.ts +0 -28
- package/apps/daemon/src/core/skills/import.ts +0 -136
- package/apps/daemon/src/core/skills/parse.ts +0 -52
- package/apps/daemon/src/core/skills/resolve.ts +0 -50
- package/apps/daemon/src/index.ts +0 -45
- package/apps/daemon/src/lib/agent-cancel.ts +0 -48
- package/apps/daemon/src/lib/agent-id.ts +0 -13
- package/apps/daemon/src/lib/agent-turn.ts +0 -56
- package/apps/daemon/src/lib/api-key.ts +0 -56
- package/apps/daemon/src/lib/auth.ts +0 -41
- package/apps/daemon/src/lib/cron.ts +0 -93
- package/apps/daemon/src/lib/ctx.ts +0 -80
- package/apps/daemon/src/lib/messaging-host.ts +0 -34
- package/apps/daemon/src/lib/middleware-auth.ts +0 -52
- package/apps/daemon/src/lib/scheduler.ts +0 -294
- package/apps/daemon/src/routes/agents.ts +0 -772
- package/apps/daemon/src/routes/auth-login.ts +0 -193
- package/apps/daemon/src/routes/config.ts +0 -267
- package/apps/daemon/src/routes/groups.ts +0 -133
- package/apps/daemon/src/routes/messages.ts +0 -29
- package/apps/daemon/src/routes/misc.ts +0 -239
- package/apps/daemon/src/routes/profiles.ts +0 -197
- package/apps/daemon/src/routes/skills.ts +0 -123
- package/apps/daemon/src/routes/triggers.ts +0 -29
- package/apps/daemon/src/runtime/auth/openai-codex.ts +0 -121
- package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +0 -31
- package/apps/daemon/src/runtime/index.ts +0 -77
- package/apps/daemon/src/runtime/memory/files.ts +0 -103
- package/apps/daemon/src/runtime/memory/qmd.ts +0 -152
- package/apps/daemon/src/runtime/memory/types.ts +0 -16
- package/apps/daemon/src/runtime/pi/events.ts +0 -173
- package/apps/daemon/src/runtime/pi/session.ts +0 -536
- package/apps/daemon/src/runtime/pi/tools.ts +0 -85
- package/apps/daemon/src/runtime/providers/catalog.ts +0 -145
- package/apps/daemon/src/runtime/providers/pi-adapter.ts +0 -272
- package/apps/daemon/src/runtime/providers/registry.ts +0 -374
- package/apps/daemon/src/runtime/providers/retry.ts +0 -176
- package/apps/daemon/src/runtime/providers/types.ts +0 -33
- package/apps/daemon/src/runtime/session/prompt.ts +0 -83
- package/apps/daemon/src/runtime/tools/bootstrap.ts +0 -22
- package/apps/daemon/src/runtime/tools/home.ts +0 -114
- package/apps/daemon/src/runtime/tools/memory.ts +0 -81
- package/apps/daemon/src/runtime/tools/messaging.ts +0 -127
- package/apps/daemon/src/runtime/tools/registry.ts +0 -29
- package/apps/daemon/src/runtime/tools/types.ts +0 -13
- package/apps/daemon/src/runtime/tools/web-extract.ts +0 -110
- package/apps/daemon/src/runtime/tools/web-ssrf.ts +0 -245
- package/apps/daemon/src/runtime/tools/web.ts +0 -273
- package/apps/daemon/src/runtime/worker/entry.ts +0 -221
- package/apps/daemon/src/runtime/worker/ipc-protocol.ts +0 -75
- package/apps/daemon/src/runtime/worker/spawn.ts +0 -249
- package/apps/daemon/test/core/agents.test.ts +0 -319
- package/apps/daemon/test/core/available-models.test.ts +0 -63
- package/apps/daemon/test/core/config.test.ts +0 -99
- package/apps/daemon/test/core/groups.test.ts +0 -63
- package/apps/daemon/test/core/helpers.ts +0 -50
- package/apps/daemon/test/core/identity.test.ts +0 -85
- package/apps/daemon/test/core/migrations.test.ts +0 -83
- package/apps/daemon/test/core/profiles.test.ts +0 -182
- package/apps/daemon/test/core/provider-models.test.ts +0 -57
- package/apps/daemon/test/core/provider-state.test.ts +0 -36
- package/apps/daemon/test/core/skill-meta.test.ts +0 -45
- package/apps/daemon/test/core/skills.test.ts +0 -271
- package/apps/daemon/test/core/triggers.test.ts +0 -182
- package/apps/daemon/test/core/web-tokens.test.ts +0 -79
- package/apps/daemon/test/cron.test.ts +0 -90
- package/apps/daemon/test/runtime/heartbeat.test.ts +0 -34
- package/apps/daemon/test/runtime/memory-qmd.test.ts +0 -97
- package/apps/daemon/test/runtime/memory.test.ts +0 -78
- package/apps/daemon/test/runtime/messaging.test.ts +0 -213
- package/apps/daemon/test/runtime/mock-server.ts +0 -65
- package/apps/daemon/test/runtime/openai-codex-auth.test.ts +0 -116
- package/apps/daemon/test/runtime/providers.test.ts +0 -306
- package/apps/daemon/test/runtime/retry.test.ts +0 -191
- package/apps/daemon/test/runtime/session-head.test.ts +0 -90
- package/apps/daemon/test/runtime/tools-home.test.ts +0 -102
- package/apps/daemon/test/runtime/tools-web.test.ts +0 -206
- package/apps/daemon/tsconfig.json +0 -4
- package/apps/mobile/README.md +0 -60
- package/apps/mobile/app/_layout.tsx +0 -58
- package/apps/mobile/app/agents/[id]/chat.tsx +0 -486
- package/apps/mobile/app/agents/[id]/index.tsx +0 -166
- package/apps/mobile/app/agents/index.tsx +0 -212
- package/apps/mobile/app/index.tsx +0 -21
- package/apps/mobile/app/pair.tsx +0 -226
- package/apps/mobile/app/settings.tsx +0 -419
- package/apps/mobile/app.json +0 -49
- package/apps/mobile/assets/adaptive-icon.png +0 -0
- package/apps/mobile/assets/favicon.png +0 -0
- package/apps/mobile/assets/icon.png +0 -0
- package/apps/mobile/assets/splash-icon.png +0 -0
- package/apps/mobile/babel.config.js +0 -6
- package/apps/mobile/metro.config.js +0 -28
- package/apps/mobile/package.json +0 -44
- package/apps/mobile/src/auth.ts +0 -66
- package/apps/mobile/src/pair-url.ts +0 -48
- package/apps/mobile/src/theme-context.tsx +0 -88
- package/apps/mobile/src/theme.ts +0 -135
- package/apps/mobile/test/pair-url.test.ts +0 -46
- package/apps/mobile/tsconfig.json +0 -23
- package/apps/web/components.json +0 -25
- package/apps/web/package.json +0 -39
- package/apps/web/public/baziu.svg +0 -8
- package/apps/web/src/components/AgentTabs.tsx +0 -45
- package/apps/web/src/components/BaziuLogo.tsx +0 -21
- package/apps/web/src/components/ChatPane.tsx +0 -1033
- package/apps/web/src/components/ConfigTabs.tsx +0 -29
- package/apps/web/src/components/CopyButton.tsx +0 -68
- package/apps/web/src/components/CreateGroupDialog.tsx +0 -127
- package/apps/web/src/components/FieldRow.tsx +0 -94
- package/apps/web/src/components/Footer.tsx +0 -10
- package/apps/web/src/components/PawIcon.tsx +0 -15
- package/apps/web/src/components/Sidebar.tsx +0 -287
- package/apps/web/src/components/SpawnDialog.tsx +0 -129
- package/apps/web/src/components/ThemeToggle.tsx +0 -75
- package/apps/web/src/components/TopNav.tsx +0 -34
- package/apps/web/src/components/ui/button.tsx +0 -67
- package/apps/web/src/components/ui/card.tsx +0 -103
- package/apps/web/src/components/ui/checkbox.tsx +0 -31
- package/apps/web/src/components/ui/dialog.tsx +0 -168
- package/apps/web/src/components/ui/input.tsx +0 -19
- package/apps/web/src/components/ui/label.tsx +0 -22
- package/apps/web/src/components/ui/radio-group.tsx +0 -44
- package/apps/web/src/components/ui/select.tsx +0 -192
- package/apps/web/src/components/ui/separator.tsx +0 -26
- package/apps/web/src/components/ui/table.tsx +0 -116
- package/apps/web/src/components/ui/tabs.tsx +0 -88
- package/apps/web/src/components/ui/textarea.tsx +0 -18
- package/apps/web/src/lib/auth.ts +0 -50
- package/apps/web/src/lib/daemon-client.ts +0 -34
- package/apps/web/src/lib/md.ts +0 -45
- package/apps/web/src/lib/utils.ts +0 -6
- package/apps/web/src/lib/wire-constants.ts +0 -27
- package/apps/web/src/routeTree.gen.ts +0 -408
- package/apps/web/src/router.tsx +0 -20
- package/apps/web/src/routes/__root.tsx +0 -123
- package/apps/web/src/routes/agents/$id/inbox.tsx +0 -207
- package/apps/web/src/routes/agents/$id/index.tsx +0 -527
- package/apps/web/src/routes/agents/$id/triggers.tsx +0 -239
- package/apps/web/src/routes/agents/index.tsx +0 -265
- package/apps/web/src/routes/api/$.ts +0 -88
- package/apps/web/src/routes/config/index.tsx +0 -315
- package/apps/web/src/routes/config/services.tsx +0 -49
- package/apps/web/src/routes/config/tokens.tsx +0 -192
- package/apps/web/src/routes/groups/$id/index.tsx +0 -153
- package/apps/web/src/routes/groups/$id/memory.tsx +0 -321
- package/apps/web/src/routes/groups/index.tsx +0 -191
- package/apps/web/src/routes/index.tsx +0 -133
- package/apps/web/src/routes/login.tsx +0 -63
- package/apps/web/src/routes/profiles/$id.tsx +0 -549
- package/apps/web/src/routes/profiles/index.tsx +0 -458
- package/apps/web/src/routes/skills/index.tsx +0 -297
- package/apps/web/src/routes/welcome.tsx +0 -61
- package/apps/web/src/styles.css +0 -449
- package/apps/web/tsconfig.json +0 -25
- package/apps/web/vite.config.ts +0 -25
- package/biome.json +0 -23
- package/docs/agent-engine.md +0 -219
- package/docs/architecture.md +0 -627
- package/docs/backlog/README.md +0 -42
- package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +0 -125
- package/docs/openclaw-reference.md +0 -210
- package/packages/api-types/package.json +0 -11
- package/packages/api-types/src/entities.ts +0 -146
- package/packages/api-types/src/events.ts +0 -55
- package/packages/api-types/src/index.ts +0 -488
- package/packages/api-types/src/memory.ts +0 -15
- package/packages/client/package.json +0 -13
- package/packages/client/src/index.ts +0 -117
- package/pnpm-workspace.yaml +0 -3
- package/tsconfig.base.json +0 -23
- package/tsconfig.json +0 -11
- 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
|
-
}
|