bazilion 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/README.md +53 -152
  2. package/dist/cli.js +2730 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/daemon.js +5888 -0
  5. package/dist/daemon.js.map +1 -0
  6. package/dist/worker.js +1830 -0
  7. package/dist/worker.js.map +1 -0
  8. package/package.json +57 -28
  9. package/.understand-anything/.understandignore +0 -25
  10. package/.understand-anything/fingerprints.json +0 -14267
  11. package/.understand-anything/knowledge-graph.json +0 -18128
  12. package/.understand-anything/meta.json +0 -6
  13. package/CLAUDE.md +0 -164
  14. package/apps/cli/package.json +0 -21
  15. package/apps/cli/src/auth-file.ts +0 -18
  16. package/apps/cli/src/client.ts +0 -37
  17. package/apps/cli/src/columnize.ts +0 -32
  18. package/apps/cli/src/commands/agent.ts +0 -574
  19. package/apps/cli/src/commands/auth.ts +0 -110
  20. package/apps/cli/src/commands/backup.ts +0 -135
  21. package/apps/cli/src/commands/completion.ts +0 -155
  22. package/apps/cli/src/commands/config.ts +0 -95
  23. package/apps/cli/src/commands/doctor.ts +0 -131
  24. package/apps/cli/src/commands/group.ts +0 -132
  25. package/apps/cli/src/commands/inbox.ts +0 -82
  26. package/apps/cli/src/commands/login.ts +0 -73
  27. package/apps/cli/src/commands/memory.ts +0 -106
  28. package/apps/cli/src/commands/profile.ts +0 -259
  29. package/apps/cli/src/commands/provider.ts +0 -170
  30. package/apps/cli/src/commands/send.ts +0 -24
  31. package/apps/cli/src/commands/serve.ts +0 -89
  32. package/apps/cli/src/commands/skill.ts +0 -120
  33. package/apps/cli/src/commands/token.ts +0 -148
  34. package/apps/cli/src/commands/trigger.ts +0 -129
  35. package/apps/cli/src/commands/uninstall.ts +0 -156
  36. package/apps/cli/src/index.ts +0 -196
  37. package/apps/cli/src/paths.ts +0 -12
  38. package/apps/cli/test/agent.test.ts +0 -213
  39. package/apps/cli/test/backup.test.ts +0 -95
  40. package/apps/cli/test/chat.test.ts +0 -539
  41. package/apps/cli/test/columnize.test.ts +0 -29
  42. package/apps/cli/test/completion.test.ts +0 -45
  43. package/apps/cli/test/config-page.test.ts +0 -151
  44. package/apps/cli/test/group.test.ts +0 -74
  45. package/apps/cli/test/helpers.ts +0 -70
  46. package/apps/cli/test/inbox-autodeliver.test.ts +0 -143
  47. package/apps/cli/test/inbox.test.ts +0 -147
  48. package/apps/cli/test/memory.test.ts +0 -69
  49. package/apps/cli/test/profile.test.ts +0 -110
  50. package/apps/cli/test/send.test.ts +0 -30
  51. package/apps/cli/test/server-fixture.ts +0 -212
  52. package/apps/cli/test/session-head.test.ts +0 -45
  53. package/apps/cli/test/skill.test.ts +0 -128
  54. package/apps/cli/test/token.test.ts +0 -109
  55. package/apps/cli/test/trigger.test.ts +0 -245
  56. package/apps/cli/tsconfig.json +0 -4
  57. package/apps/daemon/package.json +0 -31
  58. package/apps/daemon/src/app.ts +0 -41
  59. package/apps/daemon/src/core/agent/archive.ts +0 -8
  60. package/apps/daemon/src/core/agent/delete.ts +0 -30
  61. package/apps/daemon/src/core/agent/resolve.ts +0 -31
  62. package/apps/daemon/src/core/agent/spawn.ts +0 -95
  63. package/apps/daemon/src/core/agent/unarchive.ts +0 -11
  64. package/apps/daemon/src/core/availableModels.ts +0 -58
  65. package/apps/daemon/src/core/db/client.ts +0 -113
  66. package/apps/daemon/src/core/db/migrate.ts +0 -41
  67. package/apps/daemon/src/core/db/migrations/0001_init.sql +0 -179
  68. package/apps/daemon/src/core/group/delete.ts +0 -21
  69. package/apps/daemon/src/core/group/register.ts +0 -68
  70. package/apps/daemon/src/core/index.ts +0 -71
  71. package/apps/daemon/src/core/paths.ts +0 -56
  72. package/apps/daemon/src/core/profile/create.ts +0 -78
  73. package/apps/daemon/src/core/profile/delete.ts +0 -26
  74. package/apps/daemon/src/core/profile/identity.ts +0 -70
  75. package/apps/daemon/src/core/profile/load.ts +0 -45
  76. package/apps/daemon/src/core/profile/seed.ts +0 -74
  77. package/apps/daemon/src/core/profile/templates.ts +0 -60
  78. package/apps/daemon/src/core/profile/update.ts +0 -57
  79. package/apps/daemon/src/core/profile/validate.ts +0 -9
  80. package/apps/daemon/src/core/repos/agents.ts +0 -202
  81. package/apps/daemon/src/core/repos/config.ts +0 -78
  82. package/apps/daemon/src/core/repos/groups.ts +0 -55
  83. package/apps/daemon/src/core/repos/messages.ts +0 -127
  84. package/apps/daemon/src/core/repos/profiles.ts +0 -83
  85. package/apps/daemon/src/core/repos/providerModels.ts +0 -58
  86. package/apps/daemon/src/core/repos/providerState.ts +0 -37
  87. package/apps/daemon/src/core/repos/secrets.ts +0 -145
  88. package/apps/daemon/src/core/repos/skillMeta.ts +0 -49
  89. package/apps/daemon/src/core/repos/triggers.ts +0 -101
  90. package/apps/daemon/src/core/repos/webTokens.ts +0 -87
  91. package/apps/daemon/src/core/secrets.ts +0 -65
  92. package/apps/daemon/src/core/services.ts +0 -264
  93. package/apps/daemon/src/core/skills/discover.ts +0 -28
  94. package/apps/daemon/src/core/skills/import.ts +0 -136
  95. package/apps/daemon/src/core/skills/parse.ts +0 -52
  96. package/apps/daemon/src/core/skills/resolve.ts +0 -50
  97. package/apps/daemon/src/index.ts +0 -45
  98. package/apps/daemon/src/lib/agent-cancel.ts +0 -48
  99. package/apps/daemon/src/lib/agent-id.ts +0 -13
  100. package/apps/daemon/src/lib/agent-turn.ts +0 -56
  101. package/apps/daemon/src/lib/api-key.ts +0 -56
  102. package/apps/daemon/src/lib/auth.ts +0 -41
  103. package/apps/daemon/src/lib/cron.ts +0 -93
  104. package/apps/daemon/src/lib/ctx.ts +0 -80
  105. package/apps/daemon/src/lib/messaging-host.ts +0 -34
  106. package/apps/daemon/src/lib/middleware-auth.ts +0 -52
  107. package/apps/daemon/src/lib/scheduler.ts +0 -294
  108. package/apps/daemon/src/routes/agents.ts +0 -772
  109. package/apps/daemon/src/routes/auth-login.ts +0 -193
  110. package/apps/daemon/src/routes/config.ts +0 -267
  111. package/apps/daemon/src/routes/groups.ts +0 -133
  112. package/apps/daemon/src/routes/messages.ts +0 -29
  113. package/apps/daemon/src/routes/misc.ts +0 -239
  114. package/apps/daemon/src/routes/profiles.ts +0 -197
  115. package/apps/daemon/src/routes/skills.ts +0 -123
  116. package/apps/daemon/src/routes/triggers.ts +0 -29
  117. package/apps/daemon/src/runtime/auth/openai-codex.ts +0 -121
  118. package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +0 -31
  119. package/apps/daemon/src/runtime/index.ts +0 -77
  120. package/apps/daemon/src/runtime/memory/files.ts +0 -103
  121. package/apps/daemon/src/runtime/memory/qmd.ts +0 -152
  122. package/apps/daemon/src/runtime/memory/types.ts +0 -16
  123. package/apps/daemon/src/runtime/pi/events.ts +0 -173
  124. package/apps/daemon/src/runtime/pi/session.ts +0 -536
  125. package/apps/daemon/src/runtime/pi/tools.ts +0 -85
  126. package/apps/daemon/src/runtime/providers/catalog.ts +0 -145
  127. package/apps/daemon/src/runtime/providers/pi-adapter.ts +0 -272
  128. package/apps/daemon/src/runtime/providers/registry.ts +0 -374
  129. package/apps/daemon/src/runtime/providers/retry.ts +0 -176
  130. package/apps/daemon/src/runtime/providers/types.ts +0 -33
  131. package/apps/daemon/src/runtime/session/prompt.ts +0 -83
  132. package/apps/daemon/src/runtime/tools/bootstrap.ts +0 -22
  133. package/apps/daemon/src/runtime/tools/home.ts +0 -114
  134. package/apps/daemon/src/runtime/tools/memory.ts +0 -81
  135. package/apps/daemon/src/runtime/tools/messaging.ts +0 -127
  136. package/apps/daemon/src/runtime/tools/registry.ts +0 -29
  137. package/apps/daemon/src/runtime/tools/types.ts +0 -13
  138. package/apps/daemon/src/runtime/tools/web-extract.ts +0 -110
  139. package/apps/daemon/src/runtime/tools/web-ssrf.ts +0 -245
  140. package/apps/daemon/src/runtime/tools/web.ts +0 -273
  141. package/apps/daemon/src/runtime/worker/entry.ts +0 -221
  142. package/apps/daemon/src/runtime/worker/ipc-protocol.ts +0 -75
  143. package/apps/daemon/src/runtime/worker/spawn.ts +0 -249
  144. package/apps/daemon/test/core/agents.test.ts +0 -319
  145. package/apps/daemon/test/core/available-models.test.ts +0 -63
  146. package/apps/daemon/test/core/config.test.ts +0 -99
  147. package/apps/daemon/test/core/groups.test.ts +0 -63
  148. package/apps/daemon/test/core/helpers.ts +0 -50
  149. package/apps/daemon/test/core/identity.test.ts +0 -85
  150. package/apps/daemon/test/core/migrations.test.ts +0 -83
  151. package/apps/daemon/test/core/profiles.test.ts +0 -182
  152. package/apps/daemon/test/core/provider-models.test.ts +0 -57
  153. package/apps/daemon/test/core/provider-state.test.ts +0 -36
  154. package/apps/daemon/test/core/skill-meta.test.ts +0 -45
  155. package/apps/daemon/test/core/skills.test.ts +0 -271
  156. package/apps/daemon/test/core/triggers.test.ts +0 -182
  157. package/apps/daemon/test/core/web-tokens.test.ts +0 -79
  158. package/apps/daemon/test/cron.test.ts +0 -90
  159. package/apps/daemon/test/runtime/heartbeat.test.ts +0 -34
  160. package/apps/daemon/test/runtime/memory-qmd.test.ts +0 -97
  161. package/apps/daemon/test/runtime/memory.test.ts +0 -78
  162. package/apps/daemon/test/runtime/messaging.test.ts +0 -213
  163. package/apps/daemon/test/runtime/mock-server.ts +0 -65
  164. package/apps/daemon/test/runtime/openai-codex-auth.test.ts +0 -116
  165. package/apps/daemon/test/runtime/providers.test.ts +0 -306
  166. package/apps/daemon/test/runtime/retry.test.ts +0 -191
  167. package/apps/daemon/test/runtime/session-head.test.ts +0 -90
  168. package/apps/daemon/test/runtime/tools-home.test.ts +0 -102
  169. package/apps/daemon/test/runtime/tools-web.test.ts +0 -206
  170. package/apps/daemon/tsconfig.json +0 -4
  171. package/apps/mobile/README.md +0 -60
  172. package/apps/mobile/app/_layout.tsx +0 -58
  173. package/apps/mobile/app/agents/[id]/chat.tsx +0 -486
  174. package/apps/mobile/app/agents/[id]/index.tsx +0 -166
  175. package/apps/mobile/app/agents/index.tsx +0 -212
  176. package/apps/mobile/app/index.tsx +0 -21
  177. package/apps/mobile/app/pair.tsx +0 -226
  178. package/apps/mobile/app/settings.tsx +0 -419
  179. package/apps/mobile/app.json +0 -49
  180. package/apps/mobile/assets/adaptive-icon.png +0 -0
  181. package/apps/mobile/assets/favicon.png +0 -0
  182. package/apps/mobile/assets/icon.png +0 -0
  183. package/apps/mobile/assets/splash-icon.png +0 -0
  184. package/apps/mobile/babel.config.js +0 -6
  185. package/apps/mobile/metro.config.js +0 -28
  186. package/apps/mobile/package.json +0 -44
  187. package/apps/mobile/src/auth.ts +0 -66
  188. package/apps/mobile/src/pair-url.ts +0 -48
  189. package/apps/mobile/src/theme-context.tsx +0 -88
  190. package/apps/mobile/src/theme.ts +0 -135
  191. package/apps/mobile/test/pair-url.test.ts +0 -46
  192. package/apps/mobile/tsconfig.json +0 -23
  193. package/apps/web/components.json +0 -25
  194. package/apps/web/package.json +0 -39
  195. package/apps/web/public/baziu.svg +0 -8
  196. package/apps/web/src/components/AgentTabs.tsx +0 -45
  197. package/apps/web/src/components/BaziuLogo.tsx +0 -21
  198. package/apps/web/src/components/ChatPane.tsx +0 -1033
  199. package/apps/web/src/components/ConfigTabs.tsx +0 -29
  200. package/apps/web/src/components/CopyButton.tsx +0 -68
  201. package/apps/web/src/components/CreateGroupDialog.tsx +0 -127
  202. package/apps/web/src/components/FieldRow.tsx +0 -94
  203. package/apps/web/src/components/Footer.tsx +0 -10
  204. package/apps/web/src/components/PawIcon.tsx +0 -15
  205. package/apps/web/src/components/Sidebar.tsx +0 -287
  206. package/apps/web/src/components/SpawnDialog.tsx +0 -129
  207. package/apps/web/src/components/ThemeToggle.tsx +0 -75
  208. package/apps/web/src/components/TopNav.tsx +0 -34
  209. package/apps/web/src/components/ui/button.tsx +0 -67
  210. package/apps/web/src/components/ui/card.tsx +0 -103
  211. package/apps/web/src/components/ui/checkbox.tsx +0 -31
  212. package/apps/web/src/components/ui/dialog.tsx +0 -168
  213. package/apps/web/src/components/ui/input.tsx +0 -19
  214. package/apps/web/src/components/ui/label.tsx +0 -22
  215. package/apps/web/src/components/ui/radio-group.tsx +0 -44
  216. package/apps/web/src/components/ui/select.tsx +0 -192
  217. package/apps/web/src/components/ui/separator.tsx +0 -26
  218. package/apps/web/src/components/ui/table.tsx +0 -116
  219. package/apps/web/src/components/ui/tabs.tsx +0 -88
  220. package/apps/web/src/components/ui/textarea.tsx +0 -18
  221. package/apps/web/src/lib/auth.ts +0 -50
  222. package/apps/web/src/lib/daemon-client.ts +0 -34
  223. package/apps/web/src/lib/md.ts +0 -45
  224. package/apps/web/src/lib/utils.ts +0 -6
  225. package/apps/web/src/lib/wire-constants.ts +0 -27
  226. package/apps/web/src/routeTree.gen.ts +0 -408
  227. package/apps/web/src/router.tsx +0 -20
  228. package/apps/web/src/routes/__root.tsx +0 -123
  229. package/apps/web/src/routes/agents/$id/inbox.tsx +0 -207
  230. package/apps/web/src/routes/agents/$id/index.tsx +0 -527
  231. package/apps/web/src/routes/agents/$id/triggers.tsx +0 -239
  232. package/apps/web/src/routes/agents/index.tsx +0 -265
  233. package/apps/web/src/routes/api/$.ts +0 -88
  234. package/apps/web/src/routes/config/index.tsx +0 -315
  235. package/apps/web/src/routes/config/services.tsx +0 -49
  236. package/apps/web/src/routes/config/tokens.tsx +0 -192
  237. package/apps/web/src/routes/groups/$id/index.tsx +0 -153
  238. package/apps/web/src/routes/groups/$id/memory.tsx +0 -321
  239. package/apps/web/src/routes/groups/index.tsx +0 -191
  240. package/apps/web/src/routes/index.tsx +0 -133
  241. package/apps/web/src/routes/login.tsx +0 -63
  242. package/apps/web/src/routes/profiles/$id.tsx +0 -549
  243. package/apps/web/src/routes/profiles/index.tsx +0 -458
  244. package/apps/web/src/routes/skills/index.tsx +0 -297
  245. package/apps/web/src/routes/welcome.tsx +0 -61
  246. package/apps/web/src/styles.css +0 -449
  247. package/apps/web/tsconfig.json +0 -25
  248. package/apps/web/vite.config.ts +0 -25
  249. package/biome.json +0 -23
  250. package/docs/agent-engine.md +0 -219
  251. package/docs/architecture.md +0 -627
  252. package/docs/backlog/README.md +0 -42
  253. package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +0 -125
  254. package/docs/openclaw-reference.md +0 -210
  255. package/packages/api-types/package.json +0 -11
  256. package/packages/api-types/src/entities.ts +0 -146
  257. package/packages/api-types/src/events.ts +0 -55
  258. package/packages/api-types/src/index.ts +0 -488
  259. package/packages/api-types/src/memory.ts +0 -15
  260. package/packages/client/package.json +0 -13
  261. package/packages/client/src/index.ts +0 -117
  262. package/pnpm-workspace.yaml +0 -3
  263. package/tsconfig.base.json +0 -23
  264. package/tsconfig.json +0 -11
  265. package/vitest.config.ts +0 -22
@@ -1,58 +0,0 @@
1
- import { DMSans_400Regular, DMSans_500Medium, DMSans_700Bold } from '@expo-google-fonts/dm-sans'
2
- import { DMSerifDisplay_400Regular } from '@expo-google-fonts/dm-serif-display'
3
- import { JetBrainsMono_400Regular } from '@expo-google-fonts/jetbrains-mono'
4
- import { useFonts } from 'expo-font'
5
- import { Stack } from 'expo-router'
6
- import { StatusBar } from 'expo-status-bar'
7
- import { ThemeProvider, useColors, useResolvedScheme } from '@/src/theme-context'
8
- import { fonts } from '@/src/theme'
9
-
10
- export default function RootLayout() {
11
- // Aliases (BaziuDisplay / BaziuBody / …) are what each StyleSheet
12
- // references via theme.fonts.* — keys here are the registered family
13
- // names, values are the asset module IDs imported above.
14
- const [loaded] = useFonts({
15
- BaziuDisplay: DMSerifDisplay_400Regular,
16
- BaziuBody: DMSans_400Regular,
17
- BaziuBodyMedium: DMSans_500Medium,
18
- BaziuBodyBold: DMSans_700Bold,
19
- BaziuMono: JetBrainsMono_400Regular,
20
- })
21
-
22
- // Block render until fonts settle so screens never flash with the
23
- // system fallback font.
24
- if (!loaded) return null
25
-
26
- return (
27
- <ThemeProvider>
28
- <ThemedStack />
29
- </ThemeProvider>
30
- )
31
- }
32
-
33
- // Inner component so it can subscribe to the theme. The Stack needs to
34
- // re-render with new header/background colors when the user toggles.
35
- function ThemedStack() {
36
- const colors = useColors()
37
- const resolved = useResolvedScheme()
38
- return (
39
- <>
40
- <StatusBar style={resolved === 'dark' ? 'light' : 'dark'} />
41
- <Stack
42
- screenOptions={{
43
- headerStyle: { backgroundColor: colors.background },
44
- headerTintColor: colors.foreground,
45
- headerTitleStyle: { fontFamily: fonts.display, fontSize: 18 },
46
- contentStyle: { backgroundColor: colors.background },
47
- }}
48
- >
49
- <Stack.Screen name="index" options={{ headerShown: false }} />
50
- <Stack.Screen name="pair" options={{ title: 'Pair' }} />
51
- <Stack.Screen name="agents/index" options={{ title: 'Agents' }} />
52
- <Stack.Screen name="agents/[id]/index" options={{ title: 'Agent' }} />
53
- <Stack.Screen name="agents/[id]/chat" options={{ title: 'Chat' }} />
54
- <Stack.Screen name="settings" options={{ title: 'Settings' }} />
55
- </Stack>
56
- </>
57
- )
58
- }
@@ -1,486 +0,0 @@
1
- import type { ChatFrame, ProviderMessage, SessionEvent } from '@bazilion/api-types'
2
- import { useHeaderHeight } from '@react-navigation/elements'
3
- import { router, useLocalSearchParams } from 'expo-router'
4
- import { useCallback, useEffect, useMemo, useState } from 'react'
5
- import {
6
- ActivityIndicator,
7
- FlatList,
8
- KeyboardAvoidingView,
9
- Platform,
10
- Pressable,
11
- StyleSheet,
12
- Text,
13
- TextInput,
14
- View,
15
- } from 'react-native'
16
- import Markdown from 'react-native-markdown-display'
17
- import { useSafeAreaInsets } from 'react-native-safe-area-context'
18
- import { clearCredentials, type Credentials, loadCredentials } from '@/src/auth'
19
- import { useColors } from '@/src/theme-context'
20
- import { type Colors, fonts, radii } from '@/src/theme'
21
-
22
- // Display-side rendering item. Derived from the server's ProviderMessage[]
23
- // (history) and SessionEvent stream (live turn). Tool-call/result pairs are
24
- // collapsed into a single tool item so the UI doesn't show them as separate
25
- // rows.
26
- type Item =
27
- | { id: string; kind: 'user'; text: string }
28
- | { id: string; kind: 'assistant'; text: string }
29
- | { id: string; kind: 'tool'; name: string; result?: string; error?: string }
30
- | { id: string; kind: 'error'; text: string }
31
-
32
- let _itemIdSeq = 0
33
- const nextItemId = (): string => `i${++_itemIdSeq}`
34
-
35
- /** Turn the server's flattened ProviderMessage[] (history) into display Items. */
36
- function historyToItems(messages: ProviderMessage[]): Item[] {
37
- const out: Item[] = []
38
- for (const m of messages) {
39
- if (m.role === 'user') {
40
- out.push({ id: nextItemId(), kind: 'user', text: m.content })
41
- } else if (m.role === 'assistant') {
42
- if (m.content) out.push({ id: nextItemId(), kind: 'assistant', text: m.content })
43
- for (const tc of m.toolCalls ?? []) {
44
- out.push({ id: tc.id, kind: 'tool', name: tc.name })
45
- }
46
- } else if (m.role === 'tool') {
47
- // Backfill matching tool item with its result. The tool entry was
48
- // pushed when we saw the assistant's tool_call above; find by id.
49
- const idx = out.findIndex((it) => it.kind === 'tool' && it.id === m.toolCallId)
50
- if (idx >= 0) {
51
- const existing = out[idx]
52
- if (existing && existing.kind === 'tool') {
53
- out[idx] = { ...existing, result: m.content }
54
- }
55
- }
56
- }
57
- }
58
- return out
59
- }
60
-
61
- /**
62
- * Apply a SessionEvent emitted during a turn to the running items list.
63
- * Pure — caller commits the new array via setState.
64
- */
65
- function applyEvent(items: Item[], ev: SessionEvent): Item[] {
66
- switch (ev.type) {
67
- case 'user_message':
68
- // Already appended locally on send; ignore the echo.
69
- return items
70
- case 'assistant_message':
71
- return [...items, { id: nextItemId(), kind: 'assistant', text: ev.text }]
72
- case 'assistant_delta': {
73
- const last = items[items.length - 1]
74
- if (last && last.kind === 'assistant') {
75
- return [...items.slice(0, -1), { ...last, text: last.text + ev.delta }]
76
- }
77
- return [...items, { id: nextItemId(), kind: 'assistant', text: ev.delta }]
78
- }
79
- case 'tool_call':
80
- return [...items, { id: ev.id, kind: 'tool', name: ev.name }]
81
- case 'tool_result':
82
- case 'tool_error': {
83
- const idx = items.findIndex((it) => it.kind === 'tool' && it.id === ev.id)
84
- if (idx < 0) return items
85
- const existing = items[idx]
86
- if (!existing || existing.kind !== 'tool') return items
87
- const updated: Item =
88
- ev.type === 'tool_result'
89
- ? { ...existing, result: ev.result }
90
- : { ...existing, error: ev.error }
91
- return [...items.slice(0, idx), updated, ...items.slice(idx + 1)]
92
- }
93
- case 'error':
94
- return [...items, { id: nextItemId(), kind: 'error', text: ev.error }]
95
- default:
96
- return items
97
- }
98
- }
99
-
100
- export default function ChatScreen() {
101
- const { id } = useLocalSearchParams<{ id: string }>()
102
- const [items, setItems] = useState<Item[]>([])
103
- const [creds, setCreds] = useState<Credentials | null>(null)
104
- const [draft, setDraft] = useState('')
105
- const [loadState, setLoadState] = useState<'loading' | 'ready' | 'error'>('loading')
106
- const [loadError, setLoadError] = useState<string | null>(null)
107
- const [sending, setSending] = useState(false)
108
- const colors = useColors()
109
- const styles = useMemo(() => makeStyles(colors), [colors])
110
- // Real header height (varies with notch / dynamic island / safe-area
111
- // insets) so KeyboardAvoidingView lifts the input box above the keyboard
112
- // by exactly the right amount.
113
- const headerHeight = useHeaderHeight()
114
- // Bottom inset clears the home indicator on iPhones with curved
115
- // corners — without this, the input row gets clipped at rest.
116
- const insets = useSafeAreaInsets()
117
-
118
- // FlatList is `inverted` so the visual bottom is index 0 of the rendered
119
- // data — chronological items get reversed at render time. This makes the
120
- // list anchor itself to the bottom by default; no scrollToEnd dance.
121
- const reversedItems = useMemo(() => [...items].reverse(), [items])
122
-
123
- // History fetch on mount.
124
- useEffect(() => {
125
- let cancelled = false
126
- ;(async () => {
127
- const c = await loadCredentials()
128
- if (!c) {
129
- router.replace('/pair')
130
- return
131
- }
132
- if (cancelled) return
133
- setCreds(c)
134
- try {
135
- const res = await fetch(`${c.server}/api/agents/${id}/sessions/messages`, {
136
- headers: { authorization: `Bearer ${c.token}`, origin: c.server },
137
- })
138
- if (res.status === 401) {
139
- await clearCredentials()
140
- router.replace('/pair')
141
- return
142
- }
143
- if (!res.ok) {
144
- throw new Error(`server returned ${res.status} loading history`)
145
- }
146
- const body = (await res.json()) as { messages: ProviderMessage[] }
147
- if (cancelled) return
148
- setItems(historyToItems(body.messages))
149
- setLoadState('ready')
150
- } catch (err) {
151
- if (cancelled) return
152
- setLoadError(err instanceof Error ? err.message : 'unknown error')
153
- setLoadState('error')
154
- }
155
- })()
156
- return () => {
157
- cancelled = true
158
- }
159
- }, [id])
160
-
161
- const onSend = useCallback(async () => {
162
- if (!creds || sending) return
163
- const text = draft.trim()
164
- if (!text) return
165
-
166
- setDraft('')
167
- setSending(true)
168
- setItems((prev) => [...prev, { id: nextItemId(), kind: 'user', text }])
169
-
170
- try {
171
- const res = await fetch(`${creds.server}/api/agents/${id}/chat`, {
172
- method: 'POST',
173
- headers: {
174
- authorization: `Bearer ${creds.token}`,
175
- origin: creds.server,
176
- 'content-type': 'application/json',
177
- },
178
- body: JSON.stringify({ message: text }),
179
- })
180
- if (res.status === 401) {
181
- await clearCredentials()
182
- router.replace('/pair')
183
- return
184
- }
185
- if (!res.ok) {
186
- const errBody = (await res.json().catch(() => ({ error: res.statusText }))) as {
187
- error?: string
188
- }
189
- throw new Error(errBody.error ?? `server returned ${res.status}`)
190
- }
191
- // Buffered NDJSON: read the whole body, split per line, parse each
192
- // ChatFrame, then apply contained SessionEvents in order. RN's
193
- // ReadableStream support is platform-flaky; buffering trades the
194
- // typing-effect for reliability.
195
- const raw = await res.text()
196
- const frames: ChatFrame[] = raw
197
- .split('\n')
198
- .filter((l) => l.trim())
199
- .map((l) => JSON.parse(l) as ChatFrame)
200
-
201
- let next = items
202
- for (const frame of frames) {
203
- if (frame.kind === 'event') {
204
- next = applyEvent(next, frame.event)
205
- // Apply via setState too so each frame committed visibly when
206
- // the chunk lands. (We defer to the final setState below for
207
- // efficiency; this comment notes intent.)
208
- } else if (frame.kind === 'fatal') {
209
- next = [...next, { id: nextItemId(), kind: 'error', text: frame.error }]
210
- }
211
- }
212
- setItems(next)
213
- } catch (err) {
214
- const message = err instanceof Error ? err.message : 'unknown error'
215
- setItems((prev) => [...prev, { id: nextItemId(), kind: 'error', text: message }])
216
- } finally {
217
- setSending(false)
218
- }
219
- }, [creds, draft, id, items, sending])
220
-
221
- if (loadState === 'loading') {
222
- return (
223
- <View style={styles.centered}>
224
- <ActivityIndicator />
225
- </View>
226
- )
227
- }
228
-
229
- if (loadState === 'error') {
230
- return (
231
- <View style={styles.centered}>
232
- <Text style={styles.errorTitle}>Couldn't load chat</Text>
233
- <Text style={styles.errorBody}>{loadError}</Text>
234
- </View>
235
- )
236
- }
237
-
238
- return (
239
- <KeyboardAvoidingView
240
- style={styles.screen}
241
- behavior={Platform.OS === 'ios' ? 'padding' : undefined}
242
- keyboardVerticalOffset={headerHeight}
243
- >
244
- <FlatList
245
- data={reversedItems}
246
- inverted
247
- keyExtractor={(it) => it.id}
248
- renderItem={({ item }) => <Bubble item={item} />}
249
- contentContainerStyle={styles.listContent}
250
- ListEmptyComponent={
251
- <View style={styles.empty}>
252
- <Text style={styles.emptyText}>Start the conversation.</Text>
253
- </View>
254
- }
255
- />
256
- {sending ? (
257
- <View style={styles.thinking}>
258
- <ActivityIndicator size="small" color={colors.mochaLight} />
259
- <Text style={styles.thinkingText}>thinking…</Text>
260
- </View>
261
- ) : null}
262
- <View style={[styles.inputRow, { paddingBottom: 4 + insets.bottom }]}>
263
- <TextInput
264
- value={draft}
265
- onChangeText={setDraft}
266
- placeholder="Message"
267
- placeholderTextColor={colors.fawn}
268
- style={styles.input}
269
- multiline
270
- editable={!sending}
271
- />
272
- <Pressable
273
- onPress={onSend}
274
- disabled={sending || !draft.trim()}
275
- style={({ pressed }) => [
276
- styles.sendBtn,
277
- (sending || !draft.trim()) && styles.sendBtnDisabled,
278
- pressed && styles.sendBtnPressed,
279
- ]}
280
- >
281
- <Text style={styles.sendBtnText}>Send</Text>
282
- </Pressable>
283
- </View>
284
- </KeyboardAvoidingView>
285
- )
286
- }
287
-
288
- function Bubble({ item }: { item: Item }) {
289
- const colors = useColors()
290
- const styles = useMemo(() => makeStyles(colors), [colors])
291
- const markdownStyles = useMemo(() => makeMarkdownStyles(colors), [colors])
292
- if (item.kind === 'user') {
293
- return (
294
- <View style={[styles.bubbleRow, styles.bubbleRowRight]}>
295
- <View style={[styles.bubble, styles.bubbleUser]}>
296
- <Text style={styles.bubbleUserText}>{item.text}</Text>
297
- </View>
298
- </View>
299
- )
300
- }
301
- if (item.kind === 'assistant') {
302
- return (
303
- <View style={[styles.bubbleRow, styles.bubbleRowLeft]}>
304
- <View style={[styles.bubble, styles.bubbleAssistant]}>
305
- <Markdown style={markdownStyles}>{item.text}</Markdown>
306
- </View>
307
- </View>
308
- )
309
- }
310
- if (item.kind === 'tool') {
311
- const status = item.error ? 'error' : item.result !== undefined ? 'done' : 'running'
312
- return (
313
- <View style={styles.toolRow}>
314
- <Text style={styles.toolLabel}>
315
- ⚙ {item.name} · {status}
316
- </Text>
317
- {item.error ? (
318
- <Text style={styles.toolError} numberOfLines={3}>
319
- {item.error}
320
- </Text>
321
- ) : item.result ? (
322
- <Text style={styles.toolResult} numberOfLines={4}>
323
- {item.result}
324
- </Text>
325
- ) : null}
326
- </View>
327
- )
328
- }
329
- return (
330
- <View style={styles.errorRow}>
331
- <Text style={styles.errorBubbleText}>error: {item.text}</Text>
332
- </View>
333
- )
334
- }
335
-
336
- const makeStyles = (colors: Colors) =>
337
- StyleSheet.create({
338
- screen: { flex: 1, backgroundColor: colors.background },
339
- centered: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24, gap: 12 },
340
- listContent: { padding: 12, paddingBottom: 24, gap: 8 },
341
- empty: { padding: 32, alignItems: 'center' },
342
- emptyText: { color: colors.mochaLight, fontSize: 13, fontFamily: fonts.body },
343
- bubbleRow: { flexDirection: 'row' },
344
- bubbleRowLeft: { justifyContent: 'flex-start' },
345
- bubbleRowRight: { justifyContent: 'flex-end' },
346
- bubble: { maxWidth: '85%', paddingHorizontal: 12, paddingVertical: 8, borderRadius: radii.lg },
347
- bubbleUser: { backgroundColor: colors.sapphire },
348
- bubbleUserText: { color: colors.primaryForeground, fontSize: 15, fontFamily: fonts.body },
349
- bubbleAssistant: { backgroundColor: colors.card },
350
- bubbleAssistantText: { color: colors.foreground, fontSize: 15, fontFamily: fonts.body },
351
- toolRow: {
352
- paddingHorizontal: 12,
353
- paddingVertical: 6,
354
- backgroundColor: colors.ivory,
355
- borderRadius: radii.md,
356
- borderWidth: StyleSheet.hairlineWidth,
357
- borderColor: colors.border,
358
- gap: 4,
359
- },
360
- toolLabel: { fontFamily: fonts.mono, fontSize: 12, color: colors.mocha },
361
- toolResult: { fontFamily: fonts.mono, fontSize: 11, color: colors.mochaLight },
362
- toolError: { fontFamily: fonts.mono, fontSize: 11, color: colors.destructive },
363
- errorRow: {
364
- paddingHorizontal: 12,
365
- paddingVertical: 8,
366
- // 6-char hex + 1A alpha suffix = ~10% opacity; tracks colors.destructive
367
- // across both themes without needing a separate `destructiveBg` token.
368
- backgroundColor: `${colors.destructive}1A`,
369
- borderRadius: radii.md,
370
- },
371
- errorBubbleText: { color: colors.destructive, fontSize: 13, fontFamily: fonts.body },
372
- thinking: {
373
- flexDirection: 'row',
374
- alignItems: 'center',
375
- gap: 8,
376
- paddingHorizontal: 16,
377
- paddingVertical: 6,
378
- },
379
- thinkingText: {
380
- color: colors.mochaLight,
381
- fontSize: 12,
382
- fontStyle: 'italic',
383
- fontFamily: fonts.body,
384
- },
385
- inputRow: {
386
- flexDirection: 'row',
387
- alignItems: 'flex-end',
388
- gap: 8,
389
- paddingHorizontal: 12,
390
- paddingTop: 8,
391
- paddingBottom: 12,
392
- borderTopWidth: StyleSheet.hairlineWidth,
393
- borderTopColor: colors.border,
394
- backgroundColor: colors.background,
395
- },
396
- input: {
397
- flex: 1,
398
- minHeight: 40,
399
- maxHeight: 120,
400
- paddingHorizontal: 12,
401
- paddingVertical: 8,
402
- borderRadius: radii.xl,
403
- backgroundColor: colors.ivory,
404
- fontSize: 15,
405
- color: colors.foreground,
406
- fontFamily: fonts.body,
407
- },
408
- sendBtn: {
409
- paddingHorizontal: 16,
410
- paddingVertical: 10,
411
- borderRadius: radii.xl,
412
- backgroundColor: colors.sapphire,
413
- alignSelf: 'flex-end',
414
- },
415
- sendBtnDisabled: { backgroundColor: colors.mochaLight },
416
- sendBtnPressed: { backgroundColor: colors.sapphireDeep },
417
- sendBtnText: { color: colors.primaryForeground, fontSize: 14, fontFamily: fonts.bodyMedium },
418
- errorTitle: { fontSize: 18, fontFamily: fonts.bodyBold, color: colors.foreground },
419
- errorBody: { color: colors.destructive, textAlign: 'center', fontFamily: fonts.body },
420
- })
421
-
422
- // Style overrides for assistant-bubble markdown. Matches the surrounding
423
- // bubble look (Baziu body font, foreground color) and trims default
424
- // vertical paragraph margins so short replies don't get extra space.
425
- const makeMarkdownStyles = (colors: Colors) =>
426
- StyleSheet.create({
427
- body: { color: colors.foreground, fontSize: 15, fontFamily: fonts.body },
428
- paragraph: { marginTop: 0, marginBottom: 0 },
429
- heading1: {
430
- fontSize: 22,
431
- fontFamily: fonts.display,
432
- color: colors.foreground,
433
- marginTop: 4,
434
- marginBottom: 4,
435
- },
436
- heading2: {
437
- fontSize: 19,
438
- fontFamily: fonts.display,
439
- color: colors.foreground,
440
- marginTop: 4,
441
- marginBottom: 4,
442
- },
443
- heading3: {
444
- fontSize: 17,
445
- fontFamily: fonts.bodyBold,
446
- color: colors.foreground,
447
- marginTop: 4,
448
- marginBottom: 4,
449
- },
450
- strong: { fontFamily: fonts.bodyBold },
451
- code_inline: {
452
- fontFamily: fonts.mono,
453
- fontSize: 13,
454
- backgroundColor: colors.frost,
455
- color: colors.charcoal,
456
- paddingHorizontal: 4,
457
- borderRadius: 4,
458
- },
459
- code_block: {
460
- fontFamily: fonts.mono,
461
- fontSize: 12,
462
- backgroundColor: colors.frost,
463
- color: colors.charcoal,
464
- padding: 8,
465
- borderRadius: radii.sm,
466
- },
467
- fence: {
468
- fontFamily: fonts.mono,
469
- fontSize: 12,
470
- backgroundColor: colors.frost,
471
- color: colors.charcoal,
472
- padding: 8,
473
- borderRadius: radii.sm,
474
- },
475
- link: { color: colors.sapphireDeep },
476
- blockquote: {
477
- backgroundColor: colors.ivory,
478
- borderLeftColor: colors.sapphire,
479
- borderLeftWidth: 3,
480
- paddingLeft: 8,
481
- paddingVertical: 4,
482
- marginVertical: 4,
483
- },
484
- bullet_list: { marginVertical: 2 },
485
- ordered_list: { marginVertical: 2 },
486
- })