bazilion 0.0.0 → 0.1.1
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 +6460 -0
- package/dist/daemon.js.map +1 -0
- package/dist/worker.js +2176 -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
package/docs/architecture.md
DELETED
|
@@ -1,627 +0,0 @@
|
|
|
1
|
-
# Bazilion Architecture — Components & Communication
|
|
2
|
-
|
|
3
|
-
Comprehensive engineer-to-engineer reference for every component in the monorepo and how they talk to each other.
|
|
4
|
-
|
|
5
|
-
For the LLM turn loop itself, see `agent-engine.md`. This doc is about the system *around* the engine: the DB, the daemon, the CLI, the subprocess boundary, and the flows that stitch them together.
|
|
6
|
-
|
|
7
|
-
The HTTP API lives in a standalone Hono daemon (`apps/daemon`, port 4321); the web UI is a TanStack Start app (`apps/web`, port 4322) that calls it. A group is a collaboration context (one filesystem root, one USER.md, one roster, one shared memory) and an agent belongs to exactly one group. Pi's session JSONL files are the canonical transcript — there is no separate `runs` / `events` audit layer. Config + secrets live in two SQLite tables; the only remaining file at the bazilion home root besides `bazilion.db` is `auth.json` (the bootstrap bearer used by both the daemon and the CLI). Workers spawned per turn don't hold a SQLite handle of their own — agent resolution happens in the daemon and live messaging tools round-trip via Node IPC.
|
|
8
|
-
|
|
9
|
-
## 0. Topology at a glance
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
┌────────────────────────┐
|
|
13
|
-
terminal ──▶ │ apps/cli (citty) │
|
|
14
|
-
│ ─ src/index.ts │──── HTTP + bearer ─────────┐
|
|
15
|
-
│ ─ src/client.ts │ │
|
|
16
|
-
└────────────────────────┘ │
|
|
17
|
-
│
|
|
18
|
-
browser ─────────────▶ ┌────────────────────────────┐ │
|
|
19
|
-
│ apps/web (TanStack Start) │ │
|
|
20
|
-
│ 127.0.0.1:4322 │ │
|
|
21
|
-
│ ─ __root.tsx auth gate │ │
|
|
22
|
-
│ ─ /api/$ catch-all proxy │── HTTP + bearer
|
|
23
|
-
│ (cookie → bearer) │ │
|
|
24
|
-
└────────────────────────────┘ │
|
|
25
|
-
▼
|
|
26
|
-
┌───────────────────────────────────────────────┐
|
|
27
|
-
│ apps/daemon (Hono on @hono/node-server) │
|
|
28
|
-
│ 127.0.0.1:4321 │
|
|
29
|
-
│ ─ src/app.ts (route mounts) │
|
|
30
|
-
│ ─ src/lib/middleware-auth.ts (auth + gate) │
|
|
31
|
-
│ ─ src/routes/{agents,groups,profiles, │
|
|
32
|
-
│ skills,triggers,messages,config, │
|
|
33
|
-
│ auth-login,misc}.ts │
|
|
34
|
-
│ ─ src/lib/{ctx, agent-cancel, scheduler, │
|
|
35
|
-
│ agent-turn, api-key, messaging-host, │
|
|
36
|
-
│ auth, cron, agent-id}.ts │
|
|
37
|
-
└───────────┬───────────────────────────────────┘
|
|
38
|
-
│ in-proc
|
|
39
|
-
▼
|
|
40
|
-
┌───────────────────────────────────────────────┐
|
|
41
|
-
│ apps/daemon/src/core (pure data layer) │
|
|
42
|
-
│ ─ db/{client, migrate, migrations/0001…} │
|
|
43
|
-
│ ─ repos/* profile/* agent/* group/* skills/* │
|
|
44
|
-
│ ─ paths · services · secrets │
|
|
45
|
-
│ ─ availableModels │
|
|
46
|
-
└──────────────────┬────────────────────────────┘
|
|
47
|
-
│ reads/writes
|
|
48
|
-
▼
|
|
49
|
-
┌───────────────────────────────────────────────┐
|
|
50
|
-
│ ~/.bazilion │
|
|
51
|
-
│ bazilion.db (SQLite WAL) │
|
|
52
|
-
│ auth.json {token, remote?} │
|
|
53
|
-
│ groups/<slug>/{memory,…} │
|
|
54
|
-
│ agents/<id>/{sessions/*.jsonl, *.md} │
|
|
55
|
-
│ profiles/<id>/ skills/<name>/ logs/ │
|
|
56
|
-
└───────────────────────────────────────────────┘
|
|
57
|
-
▲
|
|
58
|
-
│ fork (subprocess per turn,
|
|
59
|
-
│ stdio: pipe/pipe/inherit/ipc)
|
|
60
|
-
│
|
|
61
|
-
┌──────────────────────────────────────────────┴─────────────────────────────┐
|
|
62
|
-
│ apps/daemon/src/runtime (spawned per-turn, NO DB handle) │
|
|
63
|
-
│ node --import <tsx-loader> apps/daemon/src/runtime/worker/entry.ts │
|
|
64
|
-
│ ─ worker/{entry, spawn, ipc-protocol} │
|
|
65
|
-
│ ─ pi/{session, tools, events} ← bridge to pi-coding-agent │
|
|
66
|
-
│ ─ session/{prompt, frame} │
|
|
67
|
-
│ ─ providers/{pi-adapter, registry, retry, catalog, types} │
|
|
68
|
-
│ ─ tools/{registry, memory, messaging, home, web, web-ssrf, │
|
|
69
|
-
│ web-extract, bootstrap} │
|
|
70
|
-
│ ─ memory/{qmd, files, types} ─ auth/openai-codex │
|
|
71
|
-
└────────────────────────────────────────────────────────────────────────────┘
|
|
72
|
-
│
|
|
73
|
-
▼
|
|
74
|
-
Upstream LLM providers via pi-ai
|
|
75
|
-
(anthropic, openai, google, groq, bedrock,
|
|
76
|
-
lmstudio, ollama, openai-codex/OAuth, …)
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Three processes during a chat: the **web UI** (long-lived Vite/Node), the **daemon** (long-lived Hono, sole DB owner), and the **worker** (short-lived, one per turn — no DB handle, talks back via IPC). The CLI is a fourth process that talks straight to the daemon over HTTP.
|
|
80
|
-
|
|
81
|
-
Wire boundaries:
|
|
82
|
-
- **CLI ↔ daemon, web UI ↔ daemon**: HTTP with bearer auth (CLI) or cookie translated to bearer by web's `/api/$` reverse proxy (browser). Streaming endpoints emit NDJSON.
|
|
83
|
-
- **Daemon ↔ worker**: stdin (one JSON line: `{agent, message, enabledProviders, apiKey?}`), stdout NDJSON `ChatFrame`, plus a Node IPC channel (fd 3) for the worker's messaging tool callbacks (`process.send({type:'rpc', method, args, id})` ↔ `child.send({type:'rpc-reply', id, ok, result|error})`).
|
|
84
|
-
|
|
85
|
-
The `ChatFrame` shape is the same across the worker→daemon boundary and the daemon→client boundary — no translation. Worker stdout → daemon HTTP body → CLI stdout / browser parser.
|
|
86
|
-
|
|
87
|
-
## 1. `packages/api-types` — the anti-drift ring
|
|
88
|
-
|
|
89
|
-
**Hermetic** — zero deps, no `node:*` imports, no daemon code. Owns the canonical type definitions for everything that crosses the HTTP/IPC wire. Four modules:
|
|
90
|
-
|
|
91
|
-
1. **`entities.ts`** — entity shapes that originate in the DB but cross the wire: `Agent`, `Profile`, `Group`, `Message`, `SkillMeta`, `WebToken`, `AgentTrigger`, `LoadedProfile`, `ResolvedAgent`, `OpenAICodexStatus`, …
|
|
92
|
-
2. **`events.ts`** — chat/provider wire events: `ChatFrame`, `SessionEvent`, `ProviderMessage`, `ToolCall`, `ToolDef`.
|
|
93
|
-
3. **`memory.ts`** — memory wire types (`MemoryEntry`, `MemoryHit`, …).
|
|
94
|
-
4. **`index.ts`** — request/response envelopes (`SpawnAgentRequest`, `UpdateProfileRequest`, `ChatRequest`, `RegisterGroupRequest`, `SetGroupUserMdRequest`, `ImportSkillsRequest`/`Response`, `ProviderTestRequest`/`Response`, `HealthReport`, `ChatContextResponse`, `ApiError`, `ServiceCard`, `ProviderConfigResponse`, …) plus `PROFILE_FILES` (the whitelist of editable profile markdown filenames) and the matching `ProfileFileName` string-literal union.
|
|
95
|
-
|
|
96
|
-
The flow has reversed compared to earlier shapes: the daemon imports its entity/wire types **from** `@bazilion/api-types`, not the other way around. That's what keeps `apps/web`, `apps/mobile`, and `@bazilion/client` from ever reaching Node-only code (`node:sqlite`, undici, pi-ai, the worker spawner). Nothing here executes; it's a compile-time contract.
|
|
97
|
-
|
|
98
|
-
## 1b. `packages/client` — cross-origin HTTP client
|
|
99
|
-
|
|
100
|
-
One file: `src/index.ts`. Pure `fetch` + `TextDecoder` + an NDJSON stream async generator. No `node:*` imports, no node-only deps, so it works in Node (the CLI), React Native (the mobile app), and anywhere else with a real `fetch`. Exports `createClient({serverUrl, token})`, `BazilionClient` (the typed `{get, post, put, patch, del, postMultipart, stream}` surface), `ApiClientError`. `token` is `string | (() => string | Promise<string>)` so OAuth refresh / mobile keychain reads plug in without rebuilding the client.
|
|
101
|
-
|
|
102
|
-
`apps/cli/src/client.ts` wraps `createClient` with `loadClientConfig()` (reads `~/.bazilion/auth.json` + `BAZILION_SERVER`/`BAZILION_TOKEN` env). The mobile app uses `clientFor(creds)` (`apps/mobile/src/auth.ts`) which loads the bearer from `expo-secure-store`. The browser does **not** use this package — it talks to its own server (`apps/web`) same-origin via relative `/api/*` URLs; the web's `/api/$` catch-all then translates the cookie to a bearer header for the daemon.
|
|
103
|
-
|
|
104
|
-
## 2. `apps/daemon/src/core` — pure data layer
|
|
105
|
-
|
|
106
|
-
**Rule**: no LLM, no network, no process spawning. Everything here is pure functions, DB queries, and filesystem I/O on `~/.bazilion`. Lives inside the daemon — not a separate package.
|
|
107
|
-
|
|
108
|
-
### 2.1 Paths — `paths.ts`
|
|
109
|
-
|
|
110
|
-
`resolvePaths(home?)` returns the `Paths` struct: `home`, `db`, `authFile`, `profilesDir`, `agentsDir`, `skillsDir`, `groupsDir`, `logsDir`, plus `profileDir(id)` / `agentDir(id)` / `groupDir(slug)` / `skillDir(name)` computed helpers. Override `home` via `$BAZILION_HOME`, default `~/.bazilion`. Every other core + runtime module takes `Paths` as input — no one recomputes paths independently. **There is no `configFile` field anymore** — the previous `config.json` + `secrets.enc` pair was collapsed into DB tables.
|
|
111
|
-
|
|
112
|
-
### 2.2 DB client — `db/client.ts` + `db/migrate.ts`
|
|
113
|
-
|
|
114
|
-
Uses Node 22's built-in `node:sqlite` (`DatabaseSync`). No `better-sqlite3`, no `bun:sqlite`.
|
|
115
|
-
|
|
116
|
-
**`openDb(path)`** applies `PRAGMA journal_mode=WAL` and `foreign_keys=ON`. WAL was originally what made the worker↔server concurrency work; today the worker doesn't open the DB at all, so WAL mostly buys us "daemon can read while it writes" semantics for in-process operations.
|
|
117
|
-
|
|
118
|
-
**`QueryableDatabase`** wraps `DatabaseSync` with a **prepared-statement cache** keyed by SQL string, and exposes a typed `QueryStmt<Row, Params>` with `get() / all() / run()`. The wrapper also implements manual `BEGIN / COMMIT / ROLLBACK` in a `transaction()` method because `node:sqlite` has no callable transaction wrapper.
|
|
119
|
-
|
|
120
|
-
**`BazilionDb`** is the public type: `{ raw: QueryableDatabase; close(): void }`. Everything downstream (repos, runtime helpers) takes `BazilionDb`, not the raw sqlite handle.
|
|
121
|
-
|
|
122
|
-
**`runMigrations(db)`** is idempotent. It creates a `schema_migrations(version, applied_at)` bookkeeping table, loads numbered `*.sql` files from `db/migrations/` in lexical order, and runs each unapplied file inside a transaction. Called from `apps/daemon/src/lib/ctx.ts:bootstrap()` at daemon startup — the daemon eagerly initializes the context so the bootstrap message + auth.json land before the HTTP port binds. A daemon started against an older schema self-heals on the next boot.
|
|
123
|
-
|
|
124
|
-
### 2.3 Schema (migrations)
|
|
125
|
-
|
|
126
|
-
`0001_init.sql` is the consolidated authoritative baseline (the project is alpha; the prior chain has been collapsed multiple times rather than maintaining ALTER history). Live tables:
|
|
127
|
-
|
|
128
|
-
| Table | Purpose |
|
|
129
|
-
|---|---|
|
|
130
|
-
| `groups` | Collaboration contexts (`id` (slug, PK), `name`, `user_md`, `created_at`). One filesystem root per group, derived from `paths.groupDir(id)` at read time — there is no `path` column. `user_md` is the per-group human-context block injected into every member's prompt. |
|
|
131
|
-
| `profiles` | Agent templates (`id`, `name`, `dir`, `default_model`, `skills_mode IN ('all','selected')`, timestamps). No `memory_backend` column anymore (memory is per-group, not per-profile). |
|
|
132
|
-
| `profile_default_skills` | `(profile_id, skill_name)` — seed skills for `selected` mode. |
|
|
133
|
-
| `agents` | Running/archived agent instances (`id`, `profile_id`, `name`, `model_override`, `reasoning_level`, `status`, `dir`, `group_id` `ON DELETE RESTRICT`, timestamps). One agent → one group. |
|
|
134
|
-
| `agent_skills` | `(agent_id, skill_name)` — per-agent skill attachments. Cascade on agent delete. |
|
|
135
|
-
| `agent_triggers` | Per-agent wake-ups (`kind='interval' \| 'cron'`, `interval_sec`, `cron_expr`, `message`, `last_fired_at`, `enabled`). |
|
|
136
|
-
| `messages` | Inter-agent mailbox (`id`, `from_agent_id`, `to_agent_id`, `payload`, `reply_to`, `read_at`). FKs do **not** cascade; `agent/delete.ts` nulls inbound `reply_to` and purges rows manually before removing the agent. |
|
|
137
|
-
| `skill_meta` | Import provenance — `(name PK, source, imported_at)`. No trust column — see "Skill model" in CLAUDE.md. |
|
|
138
|
-
| `web_tokens` | Per-token access records (hashed). The `bootstrap` row's plaintext lives in `auth.json`; revoking it returns 409 (would lock the operator out). |
|
|
139
|
-
| `provider_models` | Curated model list per provider (what shows up in dropdowns). |
|
|
140
|
-
| `provider_state` | Per-provider enabled flag. |
|
|
141
|
-
| `secrets` | `(key PK, envelope, updated_at)` — AES-256-GCM blobs. PBKDF2 key derived from `auth.json:token`. |
|
|
142
|
-
| `config` | `(key PK, value, updated_at)` — plaintext for env-var-shaped config that doesn't need confidentiality (server URLs, region slugs). `CONFIG_KEYS` allowlist enforced in the repo on writes. |
|
|
143
|
-
|
|
144
|
-
**Dropped during the alpha**: `runs`, `events` (audit layer for chat turns — pi's session JSONL is now the canonical record), `chat_messages` column on `agents` (pi owns the transcript), `memory_backend` column on `profiles` (memory is per-group), `path` column on `groups` (derived from slug + paths), `agents.chat_messages` (same migration as runs/events).
|
|
145
|
-
|
|
146
|
-
### 2.4 Repos — `repos/*.ts`
|
|
147
|
-
|
|
148
|
-
One module per table, each exporting narrow operations. Nothing in a repo opens a DB handle; they all take `db: BazilionDb`.
|
|
149
|
-
|
|
150
|
-
| File | Scope |
|
|
151
|
-
|---|---|
|
|
152
|
-
| `agents.ts` | Insert / get (with name + UUID-prefix fallback) / list / `resolveId(prefix)` / archive / unarchive / update (name / model / reasoning) / setGroup / attach+detach skill. |
|
|
153
|
-
| `profiles.ts` | CRUD + `profile_default_skills` set ops. |
|
|
154
|
-
| `groups.ts` | CRUD (`insert / get / list / remove`, all taking `paths` to derive the path field) + `setUserMd`. No `getByPath` — slug is the unique key. |
|
|
155
|
-
| `messages.ts` | `send({from, to, payload, replyTo?}) / get / listInbox(agentId, {unread}) / markRead / findReplies(msgId) / drainUnreadForAgent(agentId)` (txn used by the auto-deliver scheduler). |
|
|
156
|
-
| `triggers.ts` | Insert / list per agent / listEnabled (all agents) / markFired (used by scheduler) / setEnabled / delete. |
|
|
157
|
-
| `skillMeta.ts` | `get / listAll / upsert / remove` — import provenance only. |
|
|
158
|
-
| `webTokens.ts` | Insert (hashes), findActiveByToken, markUsed, revoke, list, delete. |
|
|
159
|
-
| `providerModels.ts` | List / listAll / replace / remove per provider. |
|
|
160
|
-
| `providerState.ts` | `isEnabled / setEnabled / listEnabled` — returns a `Set<string>` consumed by the provider registry. |
|
|
161
|
-
| `secrets.ts` | `openSecrets(db, password) → SecretsStore` — per-row AES-GCM, password derived from `auth.json:token`. |
|
|
162
|
-
| `config.ts` | `openConfig(db) → ConfigStore`, plus `CONFIG_KEYS` and `isConfigKey`. |
|
|
163
|
-
|
|
164
|
-
### 2.5 Domain ops — `agent/`, `profile/`, `skills/`, `group/`
|
|
165
|
-
|
|
166
|
-
Higher-level than repos; they combine DB writes with filesystem operations.
|
|
167
|
-
|
|
168
|
-
**`agent/`**:
|
|
169
|
-
- `spawn.ts:spawnAgent(db, paths, input)` — allocates UUID, creates `agents/<id>/{sessions/}`, copies profile markdown templates (`SOUL.md`, `IDENTITY.md`, `BOOTSTRAP.md`, `AGENTS.md`, `TOOLS.md`, `HEARTBEAT.md`) so the agent can diverge per-instance, writes `agent.json`, picks the group (caller-supplied or the seeded `default`), inserts into `agents` with `group_id`, attaches skills (honoring `profile.skillsMode`). **Does not create a per-agent `memory/` dir** — memory is at the group level now.
|
|
170
|
-
- `resolve.ts:resolveAgent(db, paths, id)` — joins agent + profile + group + skills into a `ResolvedAgent` (the type the runtime consumes). Accepts UUID prefixes and unique names.
|
|
171
|
-
- `archive.ts` / `unarchive.ts` — flip `status` + enforce state transitions.
|
|
172
|
-
- `delete.ts:deleteAgent(db, id)` — tx: null out inbound `messages.reply_to` pointers, purge messages where the agent is sender or recipient, then `agentRepo.remove` which cascades skills.
|
|
173
|
-
|
|
174
|
-
**`profile/`**:
|
|
175
|
-
- `create.ts` — validates slug, creates `profiles/<id>/`, writes template files, inserts profile + default_skills.
|
|
176
|
-
- `load.ts:loadProfile(db, id)` — returns `LoadedProfile` = the profile row + the parsed files (including `parseIdentityMarkdown`) + `defaultSkills[]` + `skillsMode`.
|
|
177
|
-
- `update.ts` — patch name / default_model / skills_mode / default skills.
|
|
178
|
-
- `delete.ts` — refuses while any non-archived agents reference the profile, removes the dir.
|
|
179
|
-
- `seed.ts:seedDefaults(db, paths, {model})` — idempotent bootstrap: creates the `default` group at `~/.bazilion/groups/default/` + a `default` profile wired to `model` with `skillsMode: 'all'`. `ensureSetupSeeded(db, paths)` is the call-site-safe wrapper that exits early when the default profile already exists. Called on the 0→1 available-models threshold (see §6.7).
|
|
180
|
-
- `validate.ts` — slug regex.
|
|
181
|
-
- `templates.ts` — the markdown strings used for the six profile files.
|
|
182
|
-
- `identity.ts` — structured parser for IDENTITY.md (pulls out `name`, `context`, etc.).
|
|
183
|
-
|
|
184
|
-
**`group/`**:
|
|
185
|
-
- `register.ts:registerGroup(db, input, paths)` — validates slug, materializes `paths.groupDir(id)` either as a fresh real directory (default) or a symlink to `input.link` (the "agents working on my existing project tree" path), creates the `memory/` subdir, inserts the row.
|
|
186
|
-
- `delete.ts:deleteGroup(db, paths, id)` — refuses while members exist (`agents.group_id` FK is `RESTRICT`), removes the row + the on-disk slot.
|
|
187
|
-
|
|
188
|
-
**`skills/`**:
|
|
189
|
-
- `discover.ts:discoverSkills(paths)` — walks `skillsDir`, returns `[{name, dir, skillFile}]` for each SKILL.md.
|
|
190
|
-
- `parse.ts:parseSkillFile(path)` — YAML frontmatter (`name`, `description`, `tags`) + markdown body.
|
|
191
|
-
- `import.ts:importSkills(db, paths, input)` — local path or `.zip` blob. Zip-slip guard on every entry. Copies into `skillsDir`, records `skill_meta` row with source + timestamp.
|
|
192
|
-
- `resolve.ts:resolveAgentSkills(db, paths, agentId)` — honors `profile.skillsMode === 'all'` (discover + attach everything) vs `'selected'` (use `profile_default_skills`).
|
|
193
|
-
|
|
194
|
-
### 2.6 Services, secrets, config
|
|
195
|
-
|
|
196
|
-
**`services.ts`** — static registry (`SERVICES`) of every provider and supporting service (Brave Search, SearXNG) the user might configure. Each `ServiceDef` has `id`, `displayName`, `category: 'provider' | 'service'`, `hint`, and a `fields[]` array where each field knows its env-var name, `kind: 'secret' | 'config'`, label, placeholder, description. **This single array drives**:
|
|
197
|
-
- the `/config` page UI (cards + forms),
|
|
198
|
-
- the env-var → store dispatch (`isConfigKey` → `config` table vs `secrets` table),
|
|
199
|
-
- the provider registry's "what hint do I show when this isn't set" error.
|
|
200
|
-
|
|
201
|
-
**`secrets.ts`** — top-level helpers tying the auth file to the secrets repo. `readAuthFile(authFile)` parses `~/.bazilion/auth.json` (`{token, remote?}`) and throws when the file is missing or malformed (callers treat that as "bazilion not initialized"). `mergeSecretsIntoEnv(db, password, env=process.env)` returns a fresh env object combining (lowest → highest precedence): the `config` table → the `secrets` table (decrypted with `password`) → `process.env`. Each request / each worker spawn gets a clean snapshot.
|
|
202
|
-
|
|
203
|
-
**`repos/secrets.ts`** — AES-256-GCM with PBKDF2 (100k iter, SHA-256). Envelope JSON stores `{salt, iv, tag, ciphertext}`. The `SecretsStore` exposes `get / set / remove / has / list (preview: first 6 chars + "…") / getAll`. Each row is encrypted independently with a fresh salt; the password is the `auth.json:token`.
|
|
204
|
-
|
|
205
|
-
**`repos/config.ts`** — companion plaintext store. `CONFIG_KEYS` derived from `SERVICES` (every field with `kind='config'`: URLs, project IDs, region slugs). `isConfigKey(key)` routes writes to the right store. The repo enforces the allowlist on `set`.
|
|
206
|
-
|
|
207
|
-
### 2.7 Available models
|
|
208
|
-
|
|
209
|
-
**`availableModels.ts:isSetupComplete(db)`** returns true iff at least one enabled provider has ≥1 curated model. The web middleware gates every non-API route to `/welcome` until this flips. `listAvailableModels(db)` and `groupAvailableModels(db)` produce flat + grouped views for dropdowns.
|
|
210
|
-
|
|
211
|
-
### 2.8 Public surface — `index.ts`
|
|
212
|
-
|
|
213
|
-
Flat barrel: `openDb`, `runMigrations`, `resolvePaths`, `spawnAgent`, `resolveAgent`, `agentRepo`, `messageRepo`, `profileRepo`, `triggerRepo`, `skillMetaRepo`, `webTokenRepo`, `groupRepo`, `providerStateRepo`, `providerModelRepo`, the profile/skill/group domain ops (`createProfile`, `updateProfile`, `deleteProfile`, `loadProfile`, `registerGroup`, `deleteGroup`, `discoverSkills`, `importSkills`, `resolveAgentSkills`, …), `mergeSecretsIntoEnv`, `readAuthFile`, `openSecrets`, `openConfig`, `isSetupComplete`, `SERVICES`, `findFieldByEnvVar`.
|
|
214
|
-
|
|
215
|
-
`apps/daemon/src/lib` (HTTP routes, middleware, lifecycle glue) and `apps/daemon/src/runtime` (LLM/tool stack) import from this flat namespace. `apps/cli` keeps a tiny local copy of just the path/auth helpers it needs for filesystem-level commands (`uninstall`, `backup`, `login`, `token show-local`) — it never opens the daemon's DB. `apps/web` never reaches into the daemon's source at all; every data access goes through HTTP.
|
|
216
|
-
|
|
217
|
-
## 3. `apps/daemon/src/runtime` — the LLM side
|
|
218
|
-
|
|
219
|
-
See `agent-engine.md` for the full turn-loop walkthrough. Structural summary here:
|
|
220
|
-
|
|
221
|
-
- **`worker/{entry,spawn,ipc-protocol}.ts`** — subprocess boundary. `spawn.ts:spawnWorkerTurn` is the parent-side generator. `entry.ts` is the child script — reads stdin → runs the turn → emits NDJSON on stdout → on exit calls `process.disconnect()` so the IPC handle doesn't pin the event loop. `ipc-protocol.ts` declares the `MessagingHost` interface, the `IpcRequest`/`IpcReply` shapes, and the `RpcMethod` union (`agentExists`/`sendMessage`/`listInbox`/`markRead`/`findReplies`). Stdio: `['pipe','pipe','inherit','ipc']`. Communication: stdin (1 JSON line in: agent + message + enabledProviders + apiKey) → stdout (NDJSON `ChatFrame`s) + stderr (inherited) + IPC fd (worker → daemon RPC for messaging tools). SIGTERM triggers the child's internal `AbortController`; 3s grace before SIGKILL.
|
|
222
|
-
- **`pi/`** — the pi-coding-agent bridge. `session.ts:createBazilionSession` builds a pi `AgentSession` for an agent (loads/creates the JSONL session file, wires the tool list, picks a provider). Takes `enabledProviders: Set<string>`, optional `messagingHost: MessagingHost`, optional `apiKey: string` (pre-fetched OAuth token), and optional `refreshApiKey: (provider) => Promise<string>` (mid-turn refresher; daemon-side only). `tools.ts:createBazilionCustomTools` adapts Bazilion's `ToolHandler` shape to pi's `ToolDefinition` shape and composes the Bazilion-specific tool list (memory, home, web, bootstrap, optional messaging via the host) — it deliberately **excludes** file-IO tools because pi's own `createCodingTools(cwd, …)` provides the richer `read`/`bash`/`edit`/`write`/`grep`/`find`/`ls` set. `events.ts` translates pi's session events back into Bazilion `SessionEvent`s for downstream NDJSON emission.
|
|
223
|
-
- **`session/`** — `prompt.ts` (compose the system prompt from the agent's markdown files + the group block + USER.md), `frame.ts` (the `ChatFrame` type).
|
|
224
|
-
- **`providers/`** — `pi-adapter.ts` (pi-ai → Bazilion `Provider` adapter), `registry.ts` (provider registration + `enabledSet` gate + model-string parser, takes optional `oauth: {db, authToken}` to pick up `openai-codex` credentials), `retry.ts` (uniform transient-error retry with "no retry once streamed" invariant), `types.ts` (`Provider`, `ProviderRequest`, `ProviderResponse`, `ToolCall`, `ReasoningLevel`, `StopReason`).
|
|
225
|
-
- **`tools/`** — `registry.ts` (Map-backed dispatcher) + one file per tool category:
|
|
226
|
-
- `memory.ts` — `memory_write / memory_read / memory_search / memory_list` (qmd BM25 over markdown files in `<group.path>/memory/`). Tool descriptions explicitly call out group-shared scope and direct personal notes to `home_write IDENTITY.md`.
|
|
227
|
-
- `home.ts` — `home_read / home_write / home_list`. Scope: `agents/<id>/` with a hard whitelist of identity files (`SOUL.md`, `IDENTITY.md`, `BOOTSTRAP.md`, `AGENTS.md`, `TOOLS.md`, `HEARTBEAT.md`). No path arg, no traversal. `BOOTSTRAP.md` is read-only — its lifecycle belongs to `bootstrap_done`.
|
|
228
|
-
- `messaging.ts` — `send_message / read_inbox / wait_for_reply`. Takes a `MessagingHost` (not a DB handle) — the worker's host proxies via Node IPC, the daemon's host calls repos directly.
|
|
229
|
-
- `web.ts` + `web-ssrf.ts` + `web-extract.ts` — `web_search` (Brave → SearXNG fallback) + `web_fetch` (Readability + markdown, SSRF guard, 15-min LRU cache, UA spoof, 20s timeout, 3 max redirects).
|
|
230
|
-
- `bootstrap.ts` — `bootstrap_done` (deletes `BOOTSTRAP.md` after onboarding).
|
|
231
|
-
- File-IO (`read`/`bash`/`edit`/`write`/`grep`/`find`/`ls`) comes from pi-coding-agent's own `createCodingTools(cwd, …)` — `cwd` is the agent's group directory.
|
|
232
|
-
- **`memory/`** — `qmd.ts` (BM25 via `@tobilu/qmd`, one `.qmd-index.sqlite` per group memory dir, `storeCache` module-scope Map dedupes per-process), `files.ts` (zero-deps substring fallback, currently dormant), `types.ts`.
|
|
233
|
-
- **`auth/openai-codex.ts`** — OAuth storage/refresh for the ChatGPT-backed `openai-codex` provider. Credentials blob lives under secrets-table key `OPENAI_CODEX_OAUTH`. `loadAccessToken(db, authToken)` is what the provider registry hands pi-ai as its `apiKey` supplier.
|
|
234
|
-
|
|
235
|
-
## 4. `apps/daemon` — the HTTP server (Hono)
|
|
236
|
-
|
|
237
|
-
Hono on `@hono/node-server`, bound to `127.0.0.1:4321` by default (`HOST` / `PORT` env override). **Only the daemon holds the long-lived DB handle.** Boot via `bazilion serve`, which runs `node --import tsx/esm apps/daemon/src/index.ts`.
|
|
238
|
-
|
|
239
|
-
### 4.1 App composition — `src/app.ts`
|
|
240
|
-
|
|
241
|
-
Auth + first-run middleware mounted on `*`, then route families mounted under their resource roots:
|
|
242
|
-
|
|
243
|
-
```
|
|
244
|
-
app.use('*', authMiddleware) // src/lib/middleware-auth.ts
|
|
245
|
-
app.route('/api/agents', agentsRouter)
|
|
246
|
-
app.route('/api/groups', groupsRouter)
|
|
247
|
-
app.route('/api/profiles', profilesRouter)
|
|
248
|
-
app.route('/api/skills', skillsRouter)
|
|
249
|
-
app.route('/api/triggers', triggersRouter)
|
|
250
|
-
app.route('/api/messages', messagesRouter)
|
|
251
|
-
app.route('/api/config', configRouter)
|
|
252
|
-
app.route('/api', miscRouter) // /health, /backup, /tokens, /tokens/:id
|
|
253
|
-
app.route('/api', authRouter) // /auth/openai*, /providers/test, /login
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
`src/index.ts` calls `serve()` from `@hono/node-server`, installs SIGINT/SIGTERM handlers that gracefully close the HTTP server, and exits.
|
|
257
|
-
|
|
258
|
-
### 4.2 Per-process singletons
|
|
259
|
-
|
|
260
|
-
**`src/lib/ctx.ts:getCtx()`** returns `{db, paths, authToken}`.
|
|
261
|
-
- Initializes `{db, paths}` once per process. Lazy: the first request pays the cost.
|
|
262
|
-
- Runs migrations on open.
|
|
263
|
-
- Reads `auth.json` once and caches the bootstrap token in `authToken` — used as the PBKDF2 seed for the secrets table on every `mergeSecretsIntoEnv` call.
|
|
264
|
-
- Kicks off the scheduler on first call (unless `BAZILION_SCHEDULER=off`).
|
|
265
|
-
|
|
266
|
-
**`src/lib/agent-cancel.ts`** — `Map<agentId, AbortController>`, pinned to `globalThis[Symbol.for('bazilion.agent-cancel.registry')]` so module reloads don't split the map. Exports `registerAgent`, `unregisterAgent`, `cancelAgent`, `isActiveAgent`. Replaces the older runId-keyed registry — runIds are gone now that the runs table is gone.
|
|
267
|
-
|
|
268
|
-
**`src/lib/scheduler.ts`** — `setInterval` tick loop pinned to `globalThis[Symbol.for('bazilion.scheduler')]`. Unrefed so tests can exit. See `agent-engine.md` §8 for the firing rules.
|
|
269
|
-
|
|
270
|
-
**`src/lib/messaging-host.ts`** — `createDbMessagingHost(db)` returns a `MessagingHost` backed by repos. Used by both the in-process compact/context routes and the worker IPC handler in `worker/spawn.ts`.
|
|
271
|
-
|
|
272
|
-
**`src/lib/api-key.ts`** — `resolveAgentApiKey(db, authToken, agent, opts?)` — the helper every session-creating call site uses to pre-fetch an `apiKey` (and optionally a `refreshApiKey`) for the agent's provider. For env-key providers it returns `{}` (pi pulls from the merged env). For `openai-codex` it fetches the OAuth access token from the secrets table; daemon-side callers pass `withRefresher: true` so pi can swap an expired JWT mid-turn, worker callers omit the refresher and live with the initial token's lifetime.
|
|
273
|
-
|
|
274
|
-
### 4.3 Middleware — `src/lib/middleware-auth.ts`
|
|
275
|
-
|
|
276
|
-
Two sequential gates on every request:
|
|
277
|
-
|
|
278
|
-
1. **Auth gate** — reads `Authorization: Bearer <t>` first, falls back to the `bz_token` cookie via `hono/cookie`. Both routes through `isValidToken(t)` in `src/lib/auth.ts`, which queries `web_tokens.findActiveByToken(t)` and bumps `last_used_at` on a hit. The bootstrap token (auth.json) is just one row in `web_tokens` with label `bootstrap` — same lookup path. Public paths (`/api/login`, `/api/health`) skip auth.
|
|
279
|
-
2. **First-run gate** — `isSetupComplete(db)` (see §2.7). Before setup is done, allow only the setup-open prefixes: `/api/config`, `/api/auth`, plus the public paths above. Everything else returns `409 setup incomplete`.
|
|
280
|
-
|
|
281
|
-
The web UI also enforces a setup gate client-side (its `__root.tsx` `beforeLoad` redirects to `/welcome` until setup is done) but the daemon's gate is the load-bearing one.
|
|
282
|
-
|
|
283
|
-
### 4.4 HTTP API surface — `src/routes/*.ts`
|
|
284
|
-
|
|
285
|
-
Grouped by resource. Request/response shapes all live in `@bazilion/api-types`. URL params resolve through `src/lib/agent-id.ts:resolveAgentIdParam()` so prefixes/names expand to full UUIDs before hitting tables keyed by full id.
|
|
286
|
-
|
|
287
|
-
**Agents** (`routes/agents.ts`):
|
|
288
|
-
|
|
289
|
-
| Method | Path | Purpose |
|
|
290
|
-
|---|---|---|
|
|
291
|
-
| GET | `/api/agents?includeArchived` | List agents. |
|
|
292
|
-
| POST | `/api/agents` | Spawn new agent from a profile (accepts `groupId`). |
|
|
293
|
-
| GET | `/api/agents/:id` | Full resolved agent (profile, group, skills). |
|
|
294
|
-
| PATCH | `/api/agents/:id` | Edit name / model override / reasoning level. |
|
|
295
|
-
| PATCH | `/api/agents/:id/group` | Move to a different group. |
|
|
296
|
-
| POST | `/api/agents/:id/chat` | **Streaming NDJSON** — the main turn endpoint. Calls `runAgentTurn()`. |
|
|
297
|
-
| POST | `/api/agents/:id/cancel` | Abort the in-flight turn for this agent (keyed by agentId). 409 if the agent is idle. |
|
|
298
|
-
| POST | `/api/agents/:id/chat/reset` | Drop the agent's pi session(s) so the next turn starts with an empty transcript. |
|
|
299
|
-
| GET | `/api/agents/:id/chat/context` | `ChatContextResponse` — system prompt / tools / skills / group / history breakdown. `?detail=1` or `?json=1` includes every entry. |
|
|
300
|
-
| POST | `/api/agents/:id/chat/compact` | Pi-style compaction: summarize the head, preserve `keepTail` entries (default 10) verbatim, persist a compaction marker. |
|
|
301
|
-
| POST | `/api/agents/:id/chat/truncate` | Keep first N entries. |
|
|
302
|
-
| POST | `/api/agents/:id/archive` / `unarchive` | Status flip. |
|
|
303
|
-
| DELETE | `/api/agents/:id` | Hard delete (via `agent/delete.ts`). |
|
|
304
|
-
| GET / POST | `/api/agents/:id/messages` | Inbox list / send (accepts `replyTo`). |
|
|
305
|
-
| GET / POST | `/api/agents/:id/skills` | List attached / attach. |
|
|
306
|
-
| DELETE | `/api/agents/:id/skills/:name` | Detach. |
|
|
307
|
-
| GET / POST | `/api/agents/:id/triggers` | List / create. |
|
|
308
|
-
| GET | `/api/agents/:id/sessions/messages` | SSR transcript replay (the JSONL contents rendered as `ProviderMessage[]`). |
|
|
309
|
-
| GET | `/api/agents/:id/sessions/head` | Latest session metadata for the stale-banner poller. |
|
|
310
|
-
| GET / POST / DELETE / PATCH | `/api/agents/:id/memory*` | qmd-backed memory CRUD + search (`/search`, `/[...key]`). Note: writes to the **group's** shared memory, not a per-agent store. |
|
|
311
|
-
|
|
312
|
-
**Groups** (`routes/groups.ts`): `GET / POST /api/groups`, `GET / DELETE /api/groups/:id`, `PUT /api/groups/:id/user-md`. POST body is `{id (slug), name?, link?}` — no `path` field; the daemon picks `paths.groupDir(id)`.
|
|
313
|
-
|
|
314
|
-
**Messages** (`routes/messages.ts`): `GET / PATCH /api/messages/:id` — detail + mark-read.
|
|
315
|
-
|
|
316
|
-
**Profiles** (`routes/profiles.ts`): list, create (POST, seeds markdown files), get, update (PATCH: name, default_model, skills_mode, default_skills), delete. Profile-file editing: `GET / PUT /api/profiles/:id/files/:file` where `:file` is whitelisted to `PROFILE_FILES`. `GET /api/profiles/_/templates` returns the default SOUL/IDENTITY/BOOTSTRAP/AGENTS/TOOLS/HEARTBEAT strings.
|
|
317
|
-
|
|
318
|
-
**Skills** (`routes/skills.ts`): `GET /api/skills` (discovered + parsed + metadata), `POST /api/skills/import` (multipart zip or JSON body with path/source), `DELETE /api/skills/:name`.
|
|
319
|
-
|
|
320
|
-
**Triggers** (`routes/triggers.ts`): `PATCH / DELETE /api/triggers/:id` (enable/disable + patch).
|
|
321
|
-
|
|
322
|
-
**Config** (`routes/config.ts`):
|
|
323
|
-
- `GET /api/config/providers` — provider cards: per-provider `{ enabled, configured, curatedModels, liveModels?, envHints }`. Live-models lookup hits the provider's `/v1/models` when possible.
|
|
324
|
-
- `GET /api/config/services` — same shape for non-LLM services (Brave, SearXNG).
|
|
325
|
-
- `GET /api/config/available-models` — provider-grouped curated models for dropdowns.
|
|
326
|
-
- `PUT /api/config/fields/:envVar` — dispatch to `config` or `secrets` table via `isConfigKey`.
|
|
327
|
-
- `PUT /api/config/providers/:name/enabled` — set `provider_state.enabled`.
|
|
328
|
-
- `PUT /api/config/providers/:name/models` — replace the curated list. **Both this and the enabled toggle call `ensureSetupSeeded(db, paths)` after the write** — that's what fires the 0→1 seed.
|
|
329
|
-
|
|
330
|
-
**Auth** (`routes/auth-login.ts`): `POST /api/login` (token exchange, sets `bz_token` cookie), `GET / PUT / DELETE /api/auth/openai` (Codex OAuth credentials in the secrets table), `POST /api/auth/openai/login` (server-side browser kickoff), `POST /api/providers/test`, `GET /api/auth/me` (auth+setup probe used by web middleware).
|
|
331
|
-
|
|
332
|
-
**Misc** (`routes/misc.ts`): `GET /api/health` (`HealthReport`), `GET /api/backup` (tar.gz of `~/.bazilion`), `GET / POST / DELETE /api/tokens[/:id]`. **`DELETE /api/tokens/:id` refuses to revoke the bootstrap row** by hashing `getCtx().authToken` and rejecting the matching id with 409 — locking the operator out of their own daemon would be a footgun.
|
|
333
|
-
|
|
334
|
-
### 4.5 Chat streaming path in detail
|
|
335
|
-
|
|
336
|
-
```
|
|
337
|
-
POST /api/agents/:id/chat body = {message}
|
|
338
|
-
│
|
|
339
|
-
▼
|
|
340
|
-
routes/agents.ts: resolveAgentIdParam() // prefix → UUID
|
|
341
|
-
│
|
|
342
|
-
▼
|
|
343
|
-
runAgentTurn(id, message) [apps/daemon/src/lib/agent-turn.ts]
|
|
344
|
-
│
|
|
345
|
-
│ resolveAgent(db, paths, id) // joins agent+profile+group+skills
|
|
346
|
-
│ enabledProviders = providerStateRepo.listEnabled(db)
|
|
347
|
-
│ env = mergeSecretsIntoEnv(db, authToken) // process.env > secrets > config
|
|
348
|
-
│ apiKey = resolveAgentApiKey(db, authToken, agent) // OAuth pre-fetch for openai-codex
|
|
349
|
-
│ messagingHost = createDbMessagingHost(db) // for the IPC handler
|
|
350
|
-
│ registerAgent(agentId, controller) // cancel registry (keyed by agentId)
|
|
351
|
-
│
|
|
352
|
-
▼
|
|
353
|
-
spawnWorkerTurn(spec, opts) [apps/daemon/src/runtime/worker/spawn.ts]
|
|
354
|
-
│
|
|
355
|
-
│ stdio: pipe/pipe/inherit/ipc
|
|
356
|
-
│ stdin: JSON.stringify({agent, message, enabledProviders, apiKey})
|
|
357
|
-
│ child.on('message', ...) → dispatch via messagingHost → child.send(reply)
|
|
358
|
-
▼
|
|
359
|
-
node --import <tsx-loader-URL> apps/daemon/src/runtime/worker/entry.ts
|
|
360
|
-
│
|
|
361
|
-
│ (full turn — see agent-engine.md §2–§5)
|
|
362
|
-
│
|
|
363
|
-
│ worker tool calls for messaging:
|
|
364
|
-
│ process.send({type:'rpc', id, method:'sendMessage', args})
|
|
365
|
-
│ ← child.send({type:'rpc-reply', id, ok:true, result})
|
|
366
|
-
│
|
|
367
|
-
▼
|
|
368
|
-
stdout NDJSON:
|
|
369
|
-
{kind:'event', event:{type:'user_message',...}}
|
|
370
|
-
{kind:'event', event:{type:'assistant_delta', delta}}
|
|
371
|
-
... more deltas ...
|
|
372
|
-
{kind:'event', event:{type:'assistant_message', text}}
|
|
373
|
-
{kind:'event', event:{type:'tool_call', ...}}
|
|
374
|
-
{kind:'event', event:{type:'tool_result', ...}}
|
|
375
|
-
{kind:'done', messages:[...]}
|
|
376
|
-
│
|
|
377
|
-
▼
|
|
378
|
-
agent-turn.ts line-buffers stdout → yields ChatFrame
|
|
379
|
-
In finally: unregisterAgent(agentId)
|
|
380
|
-
│
|
|
381
|
-
▼
|
|
382
|
-
route serializes each frame as "{...}\n" and writes to the Hono streamed body
|
|
383
|
-
│
|
|
384
|
-
▼
|
|
385
|
-
client (CLI or browser via web's /api/$ proxy): line-buffered NDJSON parser
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
Cancellation: `POST /api/agents/:id/cancel` → `cancelAgent(id)` → the stored `controller.abort()` → `spawn.ts`'s abort handler `child.kill('SIGTERM')` → child's signal handler aborts its internal controller → pi unwinds the provider fetch → translator emits `{type:'error', error:'cancelled'}` → child emits `done` → parent `unregisterAgent`. There is no per-run row to update — pi's session JSONL records the partial assistant message + the error event as the last entries on the branch.
|
|
389
|
-
|
|
390
|
-
## 4b. `apps/web` — the browser UI (TanStack Start)
|
|
391
|
-
|
|
392
|
-
React 19 + Vite 7 + Tailwind v4 + shadcn/ui. File-based routes via `@tanstack/react-router`; `src/router.tsx` exports `getRouter`. Server fns use `.inputValidator()`. Bound to `127.0.0.1:4322` by default (`WEB_HOST` / `WEB_PORT`).
|
|
393
|
-
|
|
394
|
-
**Daemon-only client** — `apps/web` never reaches into daemon source; the only daemon-facing types it imports are wire shapes from `@bazilion/api-types`. Every loader is a `createServerFn` handler that calls `apps/web/src/lib/daemon-client.ts`, which reads the request's `bz_token` cookie via `getCookie` from `@tanstack/react-start/server` and forwards it as `Authorization: Bearer …` to the daemon (`http://127.0.0.1:4321`, overridable via `BAZILION_DAEMON`).
|
|
395
|
-
|
|
396
|
-
**`daemon-client.ts` is server-only** — Vite's import-protection rejects `@tanstack/react-start/server` in any module that ends up in the client bundle. Client-safe wire constants live in `apps/web/src/lib/wire-constants.ts` (`DEFAULT_GROUP_ID`, `DEFAULT_PROFILE_ID`, `REASONING_LEVELS`).
|
|
397
|
-
|
|
398
|
-
Browser fetches hit relative `/api/*` URLs. The `/api/$` catch-all (`src/routes/api/$.ts`, using `createFileRoute` with `server.handlers`) is a streaming-capable reverse proxy: it pulls the cookie, stamps it as a bearer header, forwards to the daemon, and streams the response back. That's how the chat NDJSON stream survives the trip browser → web server → daemon → worker.
|
|
399
|
-
|
|
400
|
-
**Markdown rendering**: chat output goes through `marked` + DOMPurify (`apps/web/src/lib/md.ts`); the rendered HTML lands in divs with class `md-content`. Typography rules for that class live in `apps/web/src/styles.css` (headings, lists, code, pre, blockquote, table). Without those rules, Tailwind preflight strips list markers + header sizes etc., so chat reads as flat plain text.
|
|
401
|
-
|
|
402
|
-
Routes (under `src/routes/`):
|
|
403
|
-
|
|
404
|
-
| Route | Purpose |
|
|
405
|
-
|---|---|
|
|
406
|
-
| `__root.tsx` | Root layout + `beforeLoad` auth/setup gate. |
|
|
407
|
-
| `index.tsx` | Homepage with sidebar + spawn dropdown. |
|
|
408
|
-
| `login.tsx` | Token paste form. |
|
|
409
|
-
| `welcome.tsx` | First-run setup landing. |
|
|
410
|
-
| `agents/index.tsx` + `agents/$id/{index,memory,inbox,triggers}.tsx` | Agents UI. |
|
|
411
|
-
| `profiles/{index,$id}.tsx` | Profile list + 2-tab editor (basics / skills). After-save `router.invalidate()` keeps `useLoaderData()` fresh on tab toggles. |
|
|
412
|
-
| `groups/{index,$id}.tsx` | Group list (slug + optional `--link` target form) + per-group USER.md editor + member roster. |
|
|
413
|
-
| `skills/index.tsx` | Skill library (import card + installed list). |
|
|
414
|
-
| `config/{index,services,tokens}.tsx` | Provider config / service config / token management. The bootstrap row in `tokens.tsx` is badged `auth.json` and has its revoke button hidden. |
|
|
415
|
-
| `api/$.ts` | The cookie→bearer reverse proxy. |
|
|
416
|
-
|
|
417
|
-
## 5. `apps/cli` — the command-line client
|
|
418
|
-
|
|
419
|
-
Citty-based. Two "modes":
|
|
420
|
-
|
|
421
|
-
- **Direct mode** (no HTTP): `uninstall`, `serve`, `login`, `backup`, `token show-local`. These operate on the filesystem directly so they work when the daemon isn't running. The CLI never opens `bazilion.db`.
|
|
422
|
-
- **Client mode** (HTTP): everything else. Talks to the daemon via `src/client.ts` (which wraps `@bazilion/client`).
|
|
423
|
-
|
|
424
|
-
### 5.1 Entry — `src/index.ts`
|
|
425
|
-
|
|
426
|
-
Registers the subcommand tree: `serve · login · profile · group · agent · skill · memory · provider · send · inbox · config · doctor · backup · trigger · token · auth · uninstall · completion`. Custom `printTopLevelHelp()` renders a grouped layout (setup / catalog / agents / ops / remote / shell) instead of citty's default flat help.
|
|
427
|
-
|
|
428
|
-
Top-level error handler catches `ApiClientError` subclasses (401 token mismatch, 403 origin mismatch) and low-level network errors (`ECONNREFUSED`, `ENOTFOUND`) with friendly hints ("is the daemon running? `bazilion serve`").
|
|
429
|
-
|
|
430
|
-
### 5.2 Client — `src/client.ts`
|
|
431
|
-
|
|
432
|
-
`loadClientConfig()` resolves, in order:
|
|
433
|
-
1. `$BAZILION_SERVER + $BAZILION_TOKEN` — for remote daemons.
|
|
434
|
-
2. `auth.json:remote.{server,token}` — set by `bazilion login`.
|
|
435
|
-
3. `http://127.0.0.1:4321` + `auth.json:token` — local default.
|
|
436
|
-
|
|
437
|
-
The result is wrapped with `createClient({serverUrl, token})` from `@bazilion/client`, which returns `{ get, post, put, patch, del, postMultipart, stream }`. All requests send `Authorization: Bearer <token>` + `Origin: <serverUrl>`.
|
|
438
|
-
|
|
439
|
-
`stream(method, path, body)` is an async generator: fetches the endpoint, parses NDJSON line-by-line from the response body, yields `T` per line. This is what `bazilion agent chat` uses.
|
|
440
|
-
|
|
441
|
-
### 5.3 Commands
|
|
442
|
-
|
|
443
|
-
| File | Subcommands | Endpoint(s) |
|
|
444
|
-
|---|---|---|
|
|
445
|
-
| `serve.ts` | `serve [--port N] [--host H]` | (direct) — spawns `apps/daemon/src/index.ts` under `node --import tsx/esm`. On first run the daemon auto-bootstraps `~/.bazilion` (mkdir, migrate, mint bootstrap token to `auth.json`) before binding the port. |
|
|
446
|
-
| `uninstall.ts` | `uninstall` | (direct) — two-tier teardown (data vs full). `--all` removes `auth.json`, `logs/`, `skills/` in addition to the data tier. |
|
|
447
|
-
| `login.ts` | `login` | (direct) — writes `auth.json:remote` (or `--clear` to remove it). |
|
|
448
|
-
| `token.ts` | `create / list / revoke / show-local` | `/api/tokens/*`. `show-local` reads the bootstrap token from local `auth.json`. |
|
|
449
|
-
| `agent.ts` | `spawn / edit / list / show / chat / archive / unarchive / delete / move / cancel / skill / chat-{reset,trim,context,compact} / session-head` | `/api/agents*` — `chat` drains NDJSON from `/api/agents/:id/chat`; `cancel` POSTs to `/api/agents/:id/cancel`. |
|
|
450
|
-
| `profile.ts` | `create / list / show / edit / update / delete` | `/api/profiles*`. |
|
|
451
|
-
| `group.ts` | `add [--link] / list / rm / user-md {show,set,clear}` | `/api/groups*`. |
|
|
452
|
-
| `skill.ts` | `list / import / rm` | `/api/skills*` (import uses multipart). |
|
|
453
|
-
| `provider.ts` | `list / enable / disable / models / models-set / test` | `/api/config/providers*`, `/api/providers/test`. |
|
|
454
|
-
| `config.ts` | `get / set` | `/api/config/fields/:envVar`. |
|
|
455
|
-
| `auth.ts` | `openai login / logout / status` | `/api/auth/openai*` (login runs the OAuth loopback flow client-side, then PUTs credentials). |
|
|
456
|
-
| `send.ts` | `send` | `POST /api/agents/:id/messages`. |
|
|
457
|
-
| `inbox.ts` | `list / show / read` | `/api/agents/:id/messages`, `/api/messages/:id`. |
|
|
458
|
-
| `trigger.ts` | `add / list / rm / enable / disable / update` | `/api/agents/:id/triggers`, `/api/triggers/:id`. |
|
|
459
|
-
| `memory.ts` | `list / read / write / search / rm` | `/api/agents/:id/memory*` (writes to the group's shared store). |
|
|
460
|
-
| `backup.ts` | `create / restore` | `/api/backup` (download) and tar -xzf into `--home` (restore is direct, refuses while a daemon is reachable). |
|
|
461
|
-
| `doctor.ts` | `doctor` | `/api/health` + local checks. |
|
|
462
|
-
| `completion.ts` | `completion <shell>` | (direct) — prints bash/zsh/fish completion script. |
|
|
463
|
-
|
|
464
|
-
## 6. Cross-cutting flows
|
|
465
|
-
|
|
466
|
-
### 6.1 Serve + first-run bootstrap
|
|
467
|
-
|
|
468
|
-
```
|
|
469
|
-
bazilion serve [--port N] [--host H]
|
|
470
|
-
└─ spawn('node', ['--import', 'tsx/esm', 'apps/daemon/src/index.ts'])
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
The daemon's `apps/daemon/src/index.ts` eagerly calls `getCtx()` before `serve()` so the bootstrap message + `auth.json` land before the HTTP port binds:
|
|
474
|
-
|
|
475
|
-
```
|
|
476
|
-
getCtx()
|
|
477
|
-
└─ bootstrap()
|
|
478
|
-
└─ resolvePaths() → mkdir ~/.bazilion + {profiles,agents,skills,groups,logs}
|
|
479
|
-
└─ openDb + runMigrations
|
|
480
|
-
└─ if auth.json missing:
|
|
481
|
-
webTokenRepo.create(db, 'bootstrap') → randomBytes(24).toString('hex')
|
|
482
|
-
writeFile auth.json {token} → mode 0600
|
|
483
|
-
└─ cache authToken in getCtx() for PBKDF2-seeding the secrets table
|
|
484
|
-
└─ startScheduler (unless BAZILION_SCHEDULER=off)
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
Idempotent: existing installs short-circuit the mkdir + token-mint steps. The migration runner picks up new files on every boot.
|
|
488
|
-
|
|
489
|
-
The web UI is **not** booted by `serve` — run it separately with `cd apps/web && pnpm dev`.
|
|
490
|
-
|
|
491
|
-
### 6.2 Login / auth
|
|
492
|
-
|
|
493
|
-
**Local**: the daemon's first-run bootstrap mints the bootstrap row + writes its plaintext to `auth.json:token` (see §6.1); every subsequent CLI call reads it and sends `Authorization: Bearer <token>`. Daemon validates against `web_tokens.findActiveByToken`.
|
|
494
|
-
|
|
495
|
-
**Remote**: `bazilion login --server https://host --token <token>` writes `{remote:{server,token}}` to `auth.json`, or the user exports `$BAZILION_SERVER` + `$BAZILION_TOKEN`. `client.ts` picks those up.
|
|
496
|
-
|
|
497
|
-
**Browser**: user POSTs `/api/login` (via the web's catch-all proxy) with the token (from terminal output or `/config`). On success, the daemon sets `bz_token=<t>; HttpOnly; SameSite=Lax`. The cookie auto-attaches to every relative `/api/*` fetch; the proxy translates it to a bearer header before talking to the daemon.
|
|
498
|
-
|
|
499
|
-
**Mobile**: pairing via `bazilion token create <label> --qr` mints a token + emits `bazilion://pair?server=<url>&token=<t>` as a QR code. The mobile app stores the credentials in `expo-secure-store` and uses `@bazilion/client` directly with the bearer.
|
|
500
|
-
|
|
501
|
-
### 6.3 Chat turn (CLI)
|
|
502
|
-
|
|
503
|
-
```
|
|
504
|
-
bazilion agent chat <id> # interactive REPL
|
|
505
|
-
↓ reads user line
|
|
506
|
-
client.stream('POST', '/api/agents/:id/chat', {message})
|
|
507
|
-
↓ HTTP NDJSON body
|
|
508
|
-
server: runAgentTurn → resolve + pre-fetch apiKey → spawnWorkerTurn → node worker entry
|
|
509
|
-
↓ stdout NDJSON (worker → daemon over the parent's stdout pipe)
|
|
510
|
-
plus IPC RPC for messaging tools (worker → daemon over fd 3)
|
|
511
|
-
server: pipe to response body
|
|
512
|
-
↓
|
|
513
|
-
client: yield ChatFrame, pretty-print deltas/tool_calls/tool_results
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
### 6.4 Chat turn (web UI)
|
|
517
|
-
|
|
518
|
-
```
|
|
519
|
-
ChatPane.tsx (React) onSubmit
|
|
520
|
-
↓ fetch('/api/agents/:id/chat', {method:'POST', body:{message}})
|
|
521
|
-
↓ web's /api/$ catch-all proxies to daemon (cookie → bearer, body streams through)
|
|
522
|
-
↓ same daemon path as CLI — runAgentTurn → subprocess → NDJSON
|
|
523
|
-
↓ ReadableStream consumer parses + renders deltas via marked + DOMPurify (md-content typography in styles.css)
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
### 6.5 Scheduler fire
|
|
527
|
-
|
|
528
|
-
```
|
|
529
|
-
setInterval(5s) tick
|
|
530
|
-
↓
|
|
531
|
-
triggerRepo.listEnabled(db)
|
|
532
|
-
↓ filter isDue(t, now, cronCache)
|
|
533
|
-
↓ per due trigger:
|
|
534
|
-
firing.add(t.id)
|
|
535
|
-
triggerRepo.markFired(t.id) ← before the run; restart-safe
|
|
536
|
-
for await frame of runAgentTurn(t.agentId, t.message):
|
|
537
|
-
(drain, discard, log 'fatal')
|
|
538
|
-
firing.delete(t.id)
|
|
539
|
-
```
|
|
540
|
-
Same worker-spawn path as HTTP. Pi's session JSONL records the turn; there is no separate runs/events row.
|
|
541
|
-
|
|
542
|
-
### 6.6 Provider test
|
|
543
|
-
|
|
544
|
-
```
|
|
545
|
-
bazilion provider test --model anthropic:claude-opus-4-6 --message "ping"
|
|
546
|
-
↓
|
|
547
|
-
POST /api/providers/test {model, message?}
|
|
548
|
-
↓
|
|
549
|
-
server:
|
|
550
|
-
env = mergeSecretsIntoEnv(db, authToken)
|
|
551
|
-
reg = createProviderRegistry(loadProviderConfigFromEnv(env, {db, authToken}),
|
|
552
|
-
{enabledSet: providerStateRepo.listEnabled(db)})
|
|
553
|
-
{provider, model} = reg.resolve(modelString)
|
|
554
|
-
resp = await provider.chat({system:'', messages:[{role:'user',content:message}]})
|
|
555
|
-
↓
|
|
556
|
-
{content, usage}
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### 6.7 First-run seed (the 0→1 moment)
|
|
560
|
-
|
|
561
|
-
```
|
|
562
|
-
user configures ANTHROPIC_API_KEY via /config
|
|
563
|
-
↓ PUT /api/config/fields/ANTHROPIC_API_KEY → openSecrets(db, authToken).set(...)
|
|
564
|
-
user enables the provider
|
|
565
|
-
↓ PUT /api/config/providers/anthropic/enabled {enabled:true}
|
|
566
|
-
→ providerStateRepo.setEnabled('anthropic', true)
|
|
567
|
-
→ ensureSetupSeeded(db, paths)
|
|
568
|
-
→ isSetupComplete(db)? still false (no curated model) → noop
|
|
569
|
-
user curates a model
|
|
570
|
-
↓ PUT /api/config/providers/anthropic/models {models:['claude-opus-4-6']}
|
|
571
|
-
→ providerModelRepo.replace('anthropic', [...])
|
|
572
|
-
→ ensureSetupSeeded(db, paths)
|
|
573
|
-
→ isSetupComplete(db)? NOW true
|
|
574
|
-
→ seedDefaults(db, paths, {model:'anthropic:claude-opus-4-6'})
|
|
575
|
-
→ registerGroup({id:'default', name:'Default'}, paths)
|
|
576
|
-
(mkdir paths.groupDir('default') + memory/)
|
|
577
|
-
→ createProfile({id:'default', name:'Default',
|
|
578
|
-
defaultModel:..., skillsMode:'all'})
|
|
579
|
-
↓
|
|
580
|
-
middleware: isSetupComplete → true → /welcome redirect stops
|
|
581
|
-
homepage: sidebar shows "+ new ▾" dropdown with the 'default' profile floated to top
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### 6.8 Inter-agent messaging (worker via IPC)
|
|
585
|
-
|
|
586
|
-
**Send from inside a turn**:
|
|
587
|
-
```
|
|
588
|
-
agent-A's worker invokes send_message({to:'agent-B', text:'…'})
|
|
589
|
-
↓
|
|
590
|
-
messagingTools (runtime, MessagingHost interface)
|
|
591
|
-
↓
|
|
592
|
-
process.send({type:'rpc', id, method:'sendMessage', args:{from, to, payload, replyTo}})
|
|
593
|
-
↓ (over fd 3 IPC)
|
|
594
|
-
daemon: child.on('message') dispatches via createDbMessagingHost(db)
|
|
595
|
-
↓
|
|
596
|
-
messageRepo.send(db, ...) → SQLite row inserted
|
|
597
|
-
↓
|
|
598
|
-
child.send({type:'rpc-reply', id, ok:true, result:{messageId}})
|
|
599
|
-
↓
|
|
600
|
-
worker awaits the reply in createIpcMessagingHost; tool returns "sent message <id>"
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
**Read** (`read_inbox`) and **wait** (`wait_for_reply`) follow the same IPC pattern.
|
|
604
|
-
|
|
605
|
-
**From outside** (web UI or CLI):
|
|
606
|
-
```
|
|
607
|
-
bazilion send <from> <to> "text"
|
|
608
|
-
↓ POST /api/agents/:to/messages {from, text, replyTo?}
|
|
609
|
-
↓ messageRepo.send(...) — daemon-side, no IPC needed
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
**Inbox reads**: `GET /api/agents/:id/messages?unread=1`, `GET /api/messages/:id`, `PATCH /api/messages/:id {read:true}`. CLI `inbox list/show/read` wraps these.
|
|
613
|
-
|
|
614
|
-
## 7. Invariants you can rely on
|
|
615
|
-
|
|
616
|
-
- **Daemon is sole owner of `~/.bazilion`**: workers don't open `bazilion.db`. The CLI never opens it at all — its handful of direct-mode commands (`uninstall`, `backup` restore, `login`, `token show-local`) touch only the filesystem. The web UI never touches the DB either.
|
|
617
|
-
- **One owner of LLM traffic**: the worker subprocess. The daemon never calls `provider.chat` in its own event loop.
|
|
618
|
-
- **One agent, one group**: enforced by `agents.group_id NOT NULL REFERENCES groups(id) ON DELETE RESTRICT`. To delete a group with members, move them first.
|
|
619
|
-
- **Memory is group-shared**: every agent in the group reads + writes the same qmd index at `<group.path>/memory/`.
|
|
620
|
-
- **`apps/web` never reaches into daemon source**: every data access goes through HTTP, including SSR loaders. Wire shapes are imported from `@bazilion/api-types`.
|
|
621
|
-
- **`daemon-client.ts` is server-only**: any module ending up in the client bundle imports from `wire-constants.ts` instead.
|
|
622
|
-
- **Env vars win over stored secrets**: for debugging / override, a shell export always takes precedence over the `secrets` and `config` tables.
|
|
623
|
-
- **Bootstrap token can't be revoked**: `DELETE /api/tokens/:id` rejects (409) when the requested id matches `getCtx().authToken`'s hash.
|
|
624
|
-
- **HMR-safe singletons**: `agent-cancel`, `scheduler` are pinned on `globalThis[Symbol.for(...)]` so dev-mode module reloads don't double them.
|
|
625
|
-
- **Pi owns the transcript**: there is no `chat_messages` blob, no `runs`/`events` audit layer. Conversation state lives in `~/.bazilion/agents/<id>/sessions/<sessionId>.jsonl` under pi-coding-agent's `SessionManager`.
|
|
626
|
-
- **Worker IPC is fd 3**: the worker spawns with `stdio: ['pipe', 'pipe', 'inherit', 'ipc']`. Stdin = turn spec. Stdout = NDJSON `ChatFrame`. Stderr = inherited. fd 3 = JSON RPC for messaging tools.
|
|
627
|
-
- **CLI ↔ web parity**: every endpoint has both a CLI command and a web UI surface. If you add one without the other, you haven't shipped the feature.
|