zubo 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 (222) hide show
  1. package/.github/workflows/ci.yml +35 -0
  2. package/README.md +149 -0
  3. package/bun.lock +216 -0
  4. package/desktop/README.md +57 -0
  5. package/desktop/package.json +12 -0
  6. package/desktop/src-tauri/Cargo.toml +25 -0
  7. package/desktop/src-tauri/build.rs +3 -0
  8. package/desktop/src-tauri/icons/README.md +17 -0
  9. package/desktop/src-tauri/icons/icon.png +0 -0
  10. package/desktop/src-tauri/src/main.rs +189 -0
  11. package/desktop/src-tauri/tauri.conf.json +68 -0
  12. package/docs/ROADMAP.md +490 -0
  13. package/migrations/001_init.sql +9 -0
  14. package/migrations/002_memory.sql +33 -0
  15. package/migrations/003_cron.sql +24 -0
  16. package/migrations/004_usage.sql +12 -0
  17. package/migrations/005_secrets.sql +8 -0
  18. package/migrations/006_agents.sql +1 -0
  19. package/migrations/007_workflows.sql +22 -0
  20. package/migrations/008_proactive.sql +24 -0
  21. package/migrations/009_uploads.sql +9 -0
  22. package/migrations/010_observability.sql +22 -0
  23. package/migrations/011_api_keys.sql +7 -0
  24. package/migrations/012_indexes.sql +5 -0
  25. package/migrations/013_budget.sql +11 -0
  26. package/migrations/014_usage_session_idx.sql +2 -0
  27. package/package.json +39 -0
  28. package/site/404.html +156 -0
  29. package/site/CNAME +1 -0
  30. package/site/docs/agents.html +294 -0
  31. package/site/docs/api.html +446 -0
  32. package/site/docs/channels.html +345 -0
  33. package/site/docs/cli.html +238 -0
  34. package/site/docs/config.html +1034 -0
  35. package/site/docs/index.html +433 -0
  36. package/site/docs/integrations.html +381 -0
  37. package/site/docs/memory.html +254 -0
  38. package/site/docs/security.html +375 -0
  39. package/site/docs/skills.html +322 -0
  40. package/site/docs.css +412 -0
  41. package/site/index.html +638 -0
  42. package/site/install.sh +98 -0
  43. package/site/logo.svg +1 -0
  44. package/site/og-image.png +0 -0
  45. package/site/robots.txt +4 -0
  46. package/site/script.js +361 -0
  47. package/site/sitemap.xml +63 -0
  48. package/site/skills.html +532 -0
  49. package/site/style.css +1686 -0
  50. package/src/agent/agents.ts +159 -0
  51. package/src/agent/compaction.ts +53 -0
  52. package/src/agent/context.ts +18 -0
  53. package/src/agent/delegate.ts +118 -0
  54. package/src/agent/loop.ts +318 -0
  55. package/src/agent/prompts.ts +111 -0
  56. package/src/agent/session.ts +87 -0
  57. package/src/agent/teams.ts +116 -0
  58. package/src/agent/workflow-executor.ts +192 -0
  59. package/src/agent/workflow.ts +175 -0
  60. package/src/channels/adapter.ts +21 -0
  61. package/src/channels/dashboard.html.ts +2969 -0
  62. package/src/channels/discord.ts +137 -0
  63. package/src/channels/optional-deps.d.ts +17 -0
  64. package/src/channels/router.ts +199 -0
  65. package/src/channels/signal.ts +133 -0
  66. package/src/channels/slack.ts +101 -0
  67. package/src/channels/telegram.ts +102 -0
  68. package/src/channels/utils.ts +18 -0
  69. package/src/channels/webchat.ts +1797 -0
  70. package/src/channels/whatsapp.ts +119 -0
  71. package/src/config/loader.ts +22 -0
  72. package/src/config/paths.ts +43 -0
  73. package/src/config/schema.ts +121 -0
  74. package/src/db/connection.ts +20 -0
  75. package/src/db/export.ts +148 -0
  76. package/src/db/migrations.ts +42 -0
  77. package/src/index.ts +261 -0
  78. package/src/llm/claude.ts +193 -0
  79. package/src/llm/factory.ts +115 -0
  80. package/src/llm/failover.ts +101 -0
  81. package/src/llm/openai-compat.ts +409 -0
  82. package/src/llm/provider.ts +83 -0
  83. package/src/llm/smart-router.ts +241 -0
  84. package/src/logs.ts +53 -0
  85. package/src/memory/chunker.ts +58 -0
  86. package/src/memory/document-parser.ts +115 -0
  87. package/src/memory/embedder.ts +235 -0
  88. package/src/memory/engine.ts +170 -0
  89. package/src/memory/fts-index.ts +55 -0
  90. package/src/memory/hybrid-search.ts +72 -0
  91. package/src/memory/store.ts +56 -0
  92. package/src/memory/vector-index.ts +72 -0
  93. package/src/model.ts +118 -0
  94. package/src/registry/cli.ts +43 -0
  95. package/src/registry/client.ts +54 -0
  96. package/src/registry/installer.ts +67 -0
  97. package/src/scheduler/briefing.ts +71 -0
  98. package/src/scheduler/cron.ts +258 -0
  99. package/src/scheduler/heartbeat.ts +58 -0
  100. package/src/scheduler/memory-triggers.ts +100 -0
  101. package/src/scheduler/natural-cron.ts +163 -0
  102. package/src/scheduler/proactive.ts +25 -0
  103. package/src/scheduler/recipes.ts +110 -0
  104. package/src/secrets/store.ts +64 -0
  105. package/src/setup.ts +413 -0
  106. package/src/skills.ts +293 -0
  107. package/src/start.ts +373 -0
  108. package/src/status.ts +165 -0
  109. package/src/tools/builtin/connect-service.ts +205 -0
  110. package/src/tools/builtin/cron.ts +126 -0
  111. package/src/tools/builtin/datetime.ts +36 -0
  112. package/src/tools/builtin/delegate-task.ts +81 -0
  113. package/src/tools/builtin/delegate.ts +42 -0
  114. package/src/tools/builtin/diagnose.ts +41 -0
  115. package/src/tools/builtin/google-oauth.ts +379 -0
  116. package/src/tools/builtin/manage-agents.ts +149 -0
  117. package/src/tools/builtin/manage-skills.ts +294 -0
  118. package/src/tools/builtin/manage-teams.ts +89 -0
  119. package/src/tools/builtin/manage-triggers.ts +94 -0
  120. package/src/tools/builtin/manage-workflows.ts +119 -0
  121. package/src/tools/builtin/memory-search.ts +38 -0
  122. package/src/tools/builtin/memory-write.ts +30 -0
  123. package/src/tools/builtin/run-workflow.ts +36 -0
  124. package/src/tools/builtin/secrets.ts +122 -0
  125. package/src/tools/builtin/skill-registry.ts +75 -0
  126. package/src/tools/builtin-integrations/api-helpers.ts +26 -0
  127. package/src/tools/builtin-integrations/github/github_issues/SKILL.md +56 -0
  128. package/src/tools/builtin-integrations/github/github_issues/handler.ts +108 -0
  129. package/src/tools/builtin-integrations/github/github_prs/SKILL.md +57 -0
  130. package/src/tools/builtin-integrations/github/github_prs/handler.ts +113 -0
  131. package/src/tools/builtin-integrations/github/github_repos/SKILL.md +37 -0
  132. package/src/tools/builtin-integrations/github/github_repos/handler.ts +88 -0
  133. package/src/tools/builtin-integrations/google/gmail/SKILL.md +51 -0
  134. package/src/tools/builtin-integrations/google/gmail/handler.ts +125 -0
  135. package/src/tools/builtin-integrations/google/google_calendar/SKILL.md +35 -0
  136. package/src/tools/builtin-integrations/google/google_calendar/handler.ts +105 -0
  137. package/src/tools/builtin-integrations/google/google_docs/SKILL.md +35 -0
  138. package/src/tools/builtin-integrations/google/google_docs/handler.ts +108 -0
  139. package/src/tools/builtin-integrations/google/google_drive/SKILL.md +39 -0
  140. package/src/tools/builtin-integrations/google/google_drive/handler.ts +106 -0
  141. package/src/tools/builtin-integrations/google/google_sheets/SKILL.md +36 -0
  142. package/src/tools/builtin-integrations/google/google_sheets/handler.ts +116 -0
  143. package/src/tools/builtin-integrations/jira/jira_boards/SKILL.md +21 -0
  144. package/src/tools/builtin-integrations/jira/jira_boards/handler.ts +74 -0
  145. package/src/tools/builtin-integrations/jira/jira_issues/SKILL.md +28 -0
  146. package/src/tools/builtin-integrations/jira/jira_issues/handler.ts +140 -0
  147. package/src/tools/builtin-integrations/linear/linear_issues/SKILL.md +30 -0
  148. package/src/tools/builtin-integrations/linear/linear_issues/handler.ts +75 -0
  149. package/src/tools/builtin-integrations/linear/linear_projects/SKILL.md +21 -0
  150. package/src/tools/builtin-integrations/linear/linear_projects/handler.ts +43 -0
  151. package/src/tools/builtin-integrations/notion/notion_databases/SKILL.md +39 -0
  152. package/src/tools/builtin-integrations/notion/notion_databases/handler.ts +83 -0
  153. package/src/tools/builtin-integrations/notion/notion_pages/SKILL.md +43 -0
  154. package/src/tools/builtin-integrations/notion/notion_pages/handler.ts +130 -0
  155. package/src/tools/builtin-integrations/notion/notion_search/SKILL.md +27 -0
  156. package/src/tools/builtin-integrations/notion/notion_search/handler.ts +69 -0
  157. package/src/tools/builtin-integrations/slack/slack_messages/SKILL.md +42 -0
  158. package/src/tools/builtin-integrations/slack/slack_messages/handler.ts +72 -0
  159. package/src/tools/builtin-integrations/twitter/twitter_posts/SKILL.md +24 -0
  160. package/src/tools/builtin-integrations/twitter/twitter_posts/handler.ts +133 -0
  161. package/src/tools/builtin-skills/file-read/SKILL.md +26 -0
  162. package/src/tools/builtin-skills/file-read/handler.ts +66 -0
  163. package/src/tools/builtin-skills/file-write/SKILL.md +30 -0
  164. package/src/tools/builtin-skills/file-write/handler.ts +64 -0
  165. package/src/tools/builtin-skills/http-request/SKILL.md +34 -0
  166. package/src/tools/builtin-skills/http-request/handler.ts +87 -0
  167. package/src/tools/builtin-skills/shell/SKILL.md +26 -0
  168. package/src/tools/builtin-skills/shell/handler.ts +96 -0
  169. package/src/tools/builtin-skills/url-fetch/SKILL.md +26 -0
  170. package/src/tools/builtin-skills/url-fetch/handler.ts +37 -0
  171. package/src/tools/builtin-skills/web-search/SKILL.md +26 -0
  172. package/src/tools/builtin-skills/web-search/handler.ts +50 -0
  173. package/src/tools/executor.ts +205 -0
  174. package/src/tools/integration-installer.ts +106 -0
  175. package/src/tools/permissions.ts +45 -0
  176. package/src/tools/registry.ts +39 -0
  177. package/src/tools/sandbox-runner.ts +56 -0
  178. package/src/tools/sandbox.ts +82 -0
  179. package/src/tools/skill-installer.ts +52 -0
  180. package/src/tools/skill-loader.ts +259 -0
  181. package/src/types/optional-deps.d.ts +23 -0
  182. package/src/util/auth.ts +121 -0
  183. package/src/util/costs.ts +59 -0
  184. package/src/util/error-buffer.ts +32 -0
  185. package/src/util/google-tokens.ts +180 -0
  186. package/src/util/logger.ts +73 -0
  187. package/src/util/perf-collector.ts +35 -0
  188. package/src/util/rate-limiter.ts +70 -0
  189. package/src/util/tokens.ts +17 -0
  190. package/src/voice/stt.ts +57 -0
  191. package/src/voice/tts.ts +103 -0
  192. package/tests/agent/session.test.ts +109 -0
  193. package/tests/agent-loop.test.ts +54 -0
  194. package/tests/auth.test.ts +89 -0
  195. package/tests/channels.test.ts +67 -0
  196. package/tests/compaction.test.ts +44 -0
  197. package/tests/config.test.ts +51 -0
  198. package/tests/costs.test.ts +19 -0
  199. package/tests/cron.test.ts +55 -0
  200. package/tests/db/export.test.ts +219 -0
  201. package/tests/executor.test.ts +144 -0
  202. package/tests/export.test.ts +137 -0
  203. package/tests/helpers/mock-llm.ts +34 -0
  204. package/tests/helpers/test-db.ts +74 -0
  205. package/tests/integration/chat-flow.test.ts +48 -0
  206. package/tests/integrations.test.ts +97 -0
  207. package/tests/memory/engine.test.ts +114 -0
  208. package/tests/memory-engine.test.ts +57 -0
  209. package/tests/permissions.test.ts +21 -0
  210. package/tests/rate-limiter.test.ts +70 -0
  211. package/tests/registry.test.ts +67 -0
  212. package/tests/router.test.ts +36 -0
  213. package/tests/session.test.ts +58 -0
  214. package/tests/skill-loader.test.ts +44 -0
  215. package/tests/tokens.test.ts +30 -0
  216. package/tests/tools/executor.test.ts +130 -0
  217. package/tests/util/auth.test.ts +75 -0
  218. package/tests/util/rate-limiter.test.ts +73 -0
  219. package/tests/voice.test.ts +60 -0
  220. package/tests/webchat.test.ts +88 -0
  221. package/tests/workflow.test.ts +38 -0
  222. package/tsconfig.json +16 -0
@@ -0,0 +1,490 @@
1
+ # Zubo Roadmap
2
+
3
+ > What makes OpenClaw a 145k-star project: multi-LLM with failover, 14+ channels
4
+ > through one agent, a skills ecosystem, local-first gateway, and progressive
5
+ > onboarding. Zubo takes the same ideas but stays lean — no gateway daemon,
6
+ > no monorepo, just a single Bun process you can run anywhere.
7
+
8
+ ---
9
+
10
+ ## Phase 1 — Multi-LLM Provider System
11
+
12
+ **Goal:** Use any LLM — Claude, GPT, Ollama, LM Studio, Groq, Together — through
13
+ one config. Swap models without touching code.
14
+
15
+ ### 1.1 OpenAI-Compatible Provider
16
+
17
+ The OpenAI chat completions API is the lingua franca. Ollama, LM Studio, Together,
18
+ Groq, and OpenRouter all expose it. One provider covers them all.
19
+
20
+ **Files:**
21
+
22
+ | File | Action |
23
+ |---|---|
24
+ | `src/llm/provider.ts` | Add `providerName` field to interface |
25
+ | `src/llm/openai-compat.ts` | **New.** OpenAI-compatible provider (no SDK — raw `fetch`) |
26
+ | `src/llm/claude.ts` | Add `providerName = "anthropic"` |
27
+
28
+ **`src/llm/openai-compat.ts` design:**
29
+
30
+ ```ts
31
+ export class OpenAICompatProvider implements LlmProvider {
32
+ providerName: string;
33
+
34
+ constructor(opts: {
35
+ name: string; // "openai" | "ollama" | "groq" | "lmstudio" | ...
36
+ baseUrl: string; // "https://api.openai.com/v1" or "http://localhost:11434/v1"
37
+ apiKey: string; // or "ollama" for local
38
+ model: string; // "gpt-4o" | "llama3.3" | "qwen2.5-coder:32b"
39
+ maxTokens?: number;
40
+ streaming?: boolean; // default true, false for Ollama tool-use bug
41
+ })
42
+
43
+ async chat(request: LlmRequest): Promise<LlmResponse> {
44
+ // POST /chat/completions
45
+ // Map LlmToolDef[] → OpenAI function format
46
+ // Map response → LlmResponse (text + tool_use blocks)
47
+ }
48
+ }
49
+ ```
50
+
51
+ **Why raw fetch instead of the OpenAI SDK:** Fewer deps, works with every
52
+ OpenAI-compatible endpoint, and we control retry/timeout behavior.
53
+
54
+ ### 1.2 Provider Factory + Config
55
+
56
+ Decide which provider to instantiate from config alone.
57
+
58
+ **Files:**
59
+
60
+ | File | Action |
61
+ |---|---|
62
+ | `src/config/schema.ts` | New `providers` + `activeModel` fields |
63
+ | `src/llm/factory.ts` | **New.** `createProvider(config) → LlmProvider` |
64
+ | `src/start.ts` | Use factory instead of hardcoded `new ClaudeProvider()` |
65
+ | `src/setup.ts` | Ask which provider during setup |
66
+
67
+ **Config shape:**
68
+
69
+ ```jsonc
70
+ {
71
+ // ... existing fields ...
72
+
73
+ "providers": {
74
+ "anthropic": {
75
+ "apiKey": "sk-ant-...",
76
+ "model": "claude-sonnet-4-5-20250929"
77
+ },
78
+ "openai": {
79
+ "apiKey": "sk-...",
80
+ "baseUrl": "https://api.openai.com/v1",
81
+ "model": "gpt-4o"
82
+ },
83
+ "ollama": {
84
+ "baseUrl": "http://localhost:11434/v1",
85
+ "apiKey": "ollama",
86
+ "model": "qwen2.5-coder:32b",
87
+ "streaming": false
88
+ },
89
+ "groq": {
90
+ "apiKey": "gsk_...",
91
+ "baseUrl": "https://api.groq.com/openai/v1",
92
+ "model": "llama-3.3-70b-versatile"
93
+ }
94
+ },
95
+
96
+ "activeProvider": "anthropic",
97
+
98
+ "failover": ["openai", "ollama"] // optional fallback chain
99
+ }
100
+ ```
101
+
102
+ **`src/llm/factory.ts` logic:**
103
+
104
+ ```
105
+ 1. Look up config.providers[config.activeProvider]
106
+ 2. If provider name is "anthropic" → new ClaudeProvider(...)
107
+ 3. Anything else → new OpenAICompatProvider({ name, baseUrl, apiKey, model })
108
+ 4. Wrap in FailoverProvider if config.failover exists
109
+ ```
110
+
111
+ ### 1.3 Failover Wrapper
112
+
113
+ **File:** `src/llm/failover.ts` (new)
114
+
115
+ ```ts
116
+ export class FailoverProvider implements LlmProvider {
117
+ constructor(
118
+ private primary: LlmProvider,
119
+ private fallbacks: LlmProvider[],
120
+ ) {}
121
+
122
+ async chat(request: LlmRequest): Promise<LlmResponse> {
123
+ try {
124
+ return await this.primary.chat(request);
125
+ } catch (err) {
126
+ logger.warn(`Primary provider failed, trying fallback`, { error: err.message });
127
+ for (const fb of this.fallbacks) {
128
+ try { return await fb.chat(request); } catch {}
129
+ }
130
+ throw err; // all failed
131
+ }
132
+ }
133
+ }
134
+ ```
135
+
136
+ ### 1.4 Ollama Auto-Discovery (optional, nice-to-have)
137
+
138
+ **File:** `src/llm/ollama-discover.ts` (new)
139
+
140
+ ```
141
+ GET http://localhost:11434/api/tags → list models
142
+ GET http://localhost:11434/api/show { name } → check tool support
143
+ Auto-populate config.providers.ollama.model with best available
144
+ ```
145
+
146
+ Run during `bun run setup` if user picks Ollama — show available models and let
147
+ them choose.
148
+
149
+ ### 1.5 `bun run model` CLI command
150
+
151
+ Quick model switching without editing config.json:
152
+
153
+ ```bash
154
+ bun run model # show current model
155
+ bun run model ollama/llama3.3 # switch active provider+model
156
+ bun run model --list # list configured providers
157
+ ```
158
+
159
+ **File:** `src/model.ts` (new) + wire in `index.ts`
160
+
161
+ ---
162
+
163
+ ## Phase 2 — More Channels
164
+
165
+ **Goal:** Same agent on WhatsApp, Discord, and a local web UI. One memory,
166
+ one personality, many surfaces.
167
+
168
+ ### 2.1 Channel Architecture Refinement
169
+
170
+ Current `ChannelAdapter` interface is already clean. We add:
171
+
172
+ **Files:**
173
+
174
+ | File | Action |
175
+ |---|---|
176
+ | `src/channels/adapter.ts` | Add `channelName`, `isGroup` to InboundMessage |
177
+ | `src/config/schema.ts` | Per-channel config section |
178
+
179
+ ### 2.2 Discord Channel
180
+
181
+ **Dep:** `discord.js` or lighter `@discordjs/rest` + gateway
182
+
183
+ **File:** `src/channels/discord.ts` (new)
184
+
185
+ - Bot token from config
186
+ - Listen for mentions + DMs
187
+ - Group mode: only respond when @mentioned
188
+ - Message splitting for 2000-char limit
189
+
190
+ ### 2.3 WhatsApp via Baileys
191
+
192
+ **Dep:** `@whiskeysockets/baileys` (no official API needed)
193
+
194
+ **File:** `src/channels/whatsapp.ts` (new)
195
+
196
+ - QR code pairing during setup
197
+ - Store auth state in `~/.zubo/channels/whatsapp/`
198
+ - Handle text messages, ignore media for now
199
+
200
+ ### 2.4 WebChat (local HTTP UI)
201
+
202
+ **No deps.** Bun's built-in HTTP server + a single HTML file.
203
+
204
+ **Files:**
205
+ - `src/channels/webchat.ts` — HTTP server + SSE for streaming
206
+ - `src/channels/webchat.html` — Minimal chat UI (inline, no build step)
207
+
208
+ Serves on `http://localhost:3000`. Good for testing without Telegram.
209
+
210
+ ### 2.5 Channel Router Improvements
211
+
212
+ **File:** `src/channels/router.ts`
213
+
214
+ - Register multiple adapters (not just one)
215
+ - Route replies to correct channel by `sessionKey` prefix
216
+ - Cross-channel session access (read Telegram history from WebChat)
217
+
218
+ ---
219
+
220
+ ## Phase 3 — Skills / Plugin System
221
+
222
+ **Goal:** Let users drop a folder into `~/.zubo/workspace/skills/` and the agent
223
+ gains new abilities. No code changes, no restart.
224
+
225
+ ### 3.1 Skill Format
226
+
227
+ ```
228
+ ~/.zubo/workspace/skills/
229
+ web-search/
230
+ SKILL.md # Description, when to use, tool schema
231
+ handler.ts # Bun-loadable module exporting tool handler
232
+ weather/
233
+ SKILL.md
234
+ handler.ts
235
+ ```
236
+
237
+ **SKILL.md example:**
238
+
239
+ ```markdown
240
+ # Web Search
241
+
242
+ Search the web for current information.
243
+
244
+ ## Tool: web_search
245
+ - query (string, required): Search query
246
+
247
+ ## When to use
248
+ When the user asks about current events, prices, news, or anything
249
+ that requires up-to-date information beyond your training data.
250
+ ```
251
+
252
+ **handler.ts contract:**
253
+
254
+ ```ts
255
+ export default async function(input: Record<string, unknown>): Promise<string> {
256
+ const { query } = input as { query: string };
257
+ // ... do the thing ...
258
+ return JSON.stringify(results);
259
+ }
260
+ ```
261
+
262
+ ### 3.2 Skill Loader
263
+
264
+ **File:** `src/tools/skill-loader.ts` (new)
265
+
266
+ ```
267
+ 1. Scan ~/.zubo/workspace/skills/*/
268
+ 2. Parse each SKILL.md for tool name + schema + description
269
+ 3. Dynamic import() each handler.ts
270
+ 4. Register via existing registerTool()
271
+ ```
272
+
273
+ Called at startup. Hot-reload on file change is a future nice-to-have.
274
+
275
+ ### 3.3 Built-in Skills to Ship
276
+
277
+ | Skill | What it does |
278
+ |---|---|
279
+ | `web-search` | DuckDuckGo/Brave Search API |
280
+ | `url-fetch` | Fetch URL, convert to markdown, summarize |
281
+ | `shell` | Run shell command (with confirmation) |
282
+ | `file-read` | Read file from disk |
283
+ | `file-write` | Write file to disk |
284
+ | `http-request` | Generic HTTP request (GET/POST) |
285
+
286
+ These live in `src/tools/builtin/` but follow the same SKILL.md pattern
287
+ so users can override them.
288
+
289
+ ---
290
+
291
+ ## Phase 4 — Agent Improvements
292
+
293
+ **Goal:** Smarter agent loop — thinking, streaming, multi-agent delegation.
294
+
295
+ ### 4.1 Streaming Responses
296
+
297
+ Stream tokens to the channel as they arrive instead of waiting for completion.
298
+
299
+ **Files:**
300
+ - `src/llm/provider.ts` — Add `chatStream()` returning `AsyncIterable<LlmChunk>`
301
+ - `src/llm/claude.ts` — Implement streaming via Anthropic SDK
302
+ - `src/llm/openai-compat.ts` — Implement SSE parsing
303
+ - `src/agent/loop.ts` — Stream-aware loop
304
+ - `src/channels/telegram.ts` — Edit message in-place as tokens arrive
305
+
306
+ ### 4.2 Extended Thinking / Reasoning
307
+
308
+ For models that support it (Claude with extended thinking, DeepSeek-R1, QwQ):
309
+
310
+ - Detect `thinking` blocks in response
311
+ - Don't send thinking to user (unless asked)
312
+ - Use thinking for better tool-use decisions
313
+ - Config toggle: `thinking: true/false` per provider
314
+
315
+ ### 4.3 Cost + Token Tracking
316
+
317
+ **File:** `src/agent/usage.ts` (new)
318
+
319
+ - Track per-message input/output tokens
320
+ - Track per-session cumulative cost
321
+ - Store in DB (new `usage` table)
322
+ - Surface in `bun run status`
323
+
324
+ ```sql
325
+ CREATE TABLE usage (
326
+ id INTEGER PRIMARY KEY,
327
+ session_key TEXT,
328
+ provider TEXT,
329
+ model TEXT,
330
+ input_tokens INTEGER,
331
+ output_tokens INTEGER,
332
+ cost_usd REAL,
333
+ created_at TEXT DEFAULT (datetime('now'))
334
+ );
335
+ ```
336
+
337
+ ### 4.4 Context Window Awareness
338
+
339
+ Different models have different context windows. Compaction should respect this.
340
+
341
+ **File:** `src/agent/compaction.ts`
342
+
343
+ - Provider reports `contextWindow` size
344
+ - Compaction targets 66% of that (not hardcoded 100k)
345
+ - Small local models (8k context) get aggressive compaction
346
+
347
+ ---
348
+
349
+ ## Phase 5 — Proactive Agent + Automation
350
+
351
+ **Goal:** Zubo doesn't just respond — it acts on its own.
352
+
353
+ ### 5.1 Webhook Inbound
354
+
355
+ **File:** `src/scheduler/webhooks.ts` (new)
356
+
357
+ - Bun HTTP server on configurable port
358
+ - Routes like `POST /webhook/:name` → trigger agent with payload
359
+ - Use case: GitHub push → Zubo summarizes commit
360
+ - Use case: IFTTT/Zapier integration
361
+
362
+ ### 5.2 Agent-Managed Cron
363
+
364
+ Let the agent create/edit/delete cron jobs via tools (not just DB):
365
+
366
+ **New tools:**
367
+ - `cron_create` — Create a scheduled task
368
+ - `cron_list` — List all cron jobs
369
+ - `cron_delete` — Remove a job
370
+
371
+ Now the user can say "remind me every Monday at 9am to review PRs" and the
372
+ agent handles it end-to-end.
373
+
374
+ ### 5.3 Daily Digest
375
+
376
+ A built-in cron job (opt-in) that:
377
+
378
+ 1. Reads recent memories from the past 24h
379
+ 2. Checks calendar/weather (if skills installed)
380
+ 3. Composes a morning briefing
381
+ 4. Sends to user's primary channel
382
+
383
+ ---
384
+
385
+ ## Phase 6 — Security + Permissions
386
+
387
+ ### 6.1 Tool Approval
388
+
389
+ Some tools are dangerous (shell, file-write). Before executing:
390
+
391
+ ```
392
+ Agent wants to run: shell("rm -rf /tmp/old-data")
393
+ [Approve] [Deny] [Always allow shell]
394
+ ```
395
+
396
+ **File:** `src/tools/permissions.ts` (new)
397
+
398
+ - Permission levels: `auto` (memory_search), `confirm` (shell), `deny`
399
+ - Per-tool config in `~/.zubo/workspace/PERMISSIONS.md` or config.json
400
+ - Channel adapters render approval buttons (Telegram inline keyboard)
401
+
402
+ ### 6.2 Message Pairing / Auth
403
+
404
+ For channels where anyone can message the bot:
405
+
406
+ - First message from unknown user → pairing code
407
+ - User must confirm in a trusted channel (or CLI)
408
+ - Prevents prompt injection from strangers
409
+
410
+ ---
411
+
412
+ ## Phase 7 — Companion Interfaces
413
+
414
+ ### 7.1 TUI (Terminal UI)
415
+
416
+ **File:** `src/channels/tui.ts` (new)
417
+
418
+ Interactive terminal chat using Bun's readline or `ink`:
419
+
420
+ ```bash
421
+ bun run chat
422
+ ```
423
+
424
+ Full chat experience without any external service. Best for testing and
425
+ local-only use. Essential for Ollama-only setups where you don't need Telegram.
426
+
427
+ ### 7.2 Web Dashboard
428
+
429
+ **Files:**
430
+ - `src/dashboard/server.ts` — Bun HTTP server
431
+ - `src/dashboard/index.html` — Single-page app
432
+
433
+ Shows:
434
+ - Live conversation view
435
+ - Memory browser (search + edit)
436
+ - Cron job management
437
+ - Provider/model status
438
+ - Usage/cost charts
439
+
440
+ ---
441
+
442
+ ## Implementation Priority
443
+
444
+ What to build first, based on impact vs. effort:
445
+
446
+ ```
447
+ HIGH IMPACT, LOW EFFORT (do first)
448
+ ├── Phase 1.1 OpenAI-compatible provider ★★★★★
449
+ ├── Phase 1.2 Provider factory + config ★★★★★
450
+ ├── Phase 1.5 `bun run model` command ★★★★
451
+ ├── Phase 7.1 TUI / `bun run chat` ★★★★
452
+ ├── Phase 4.3 Cost + token tracking ★★★★
453
+ └── Phase 5.2 Agent-managed cron tools ★★★★
454
+
455
+ HIGH IMPACT, MEDIUM EFFORT (do second)
456
+ ├── Phase 1.3 Failover wrapper ★★★★
457
+ ├── Phase 3.1 Skill format + loader ★★★★
458
+ ├── Phase 3.3 web-search + url-fetch skills ★★★★
459
+ ├── Phase 2.4 WebChat (local HTTP UI) ★★★★
460
+ ├── Phase 4.1 Streaming responses ★★★
461
+ └── Phase 4.4 Context window awareness ★★★
462
+
463
+ MEDIUM IMPACT, MEDIUM EFFORT (do third)
464
+ ├── Phase 2.2 Discord channel ★★★
465
+ ├── Phase 2.3 WhatsApp channel ★★★
466
+ ├── Phase 5.1 Webhook inbound ★★★
467
+ ├── Phase 6.1 Tool approval / permissions ★★★
468
+ └── Phase 5.3 Daily digest ★★★
469
+
470
+ NICE TO HAVE (do when stable)
471
+ ├── Phase 1.4 Ollama auto-discovery ★★
472
+ ├── Phase 4.2 Extended thinking ★★
473
+ ├── Phase 6.2 Message pairing / auth ★★
474
+ └── Phase 7.2 Web dashboard ★★
475
+ ```
476
+
477
+ ---
478
+
479
+ ## Next Concrete Step
480
+
481
+ **Phase 1.1 + 1.2**: Multi-LLM support. One session of work:
482
+
483
+ 1. Create `src/llm/openai-compat.ts` — OpenAI-compatible provider
484
+ 2. Create `src/llm/factory.ts` — Provider factory
485
+ 3. Update `src/config/schema.ts` — New provider config fields
486
+ 4. Update `src/start.ts` — Use factory
487
+ 5. Update `src/setup.ts` — Provider selection in wizard
488
+ 6. Test with Ollama locally
489
+
490
+ This single change makes Zubo usable with any model, which unlocks everything else.
@@ -0,0 +1,9 @@
1
+ CREATE TABLE IF NOT EXISTS sessions (
2
+ id TEXT PRIMARY KEY,
3
+ channel TEXT NOT NULL,
4
+ user_id TEXT NOT NULL,
5
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
6
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
7
+ );
8
+
9
+ CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
@@ -0,0 +1,33 @@
1
+ CREATE TABLE IF NOT EXISTS memory_chunks (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ source_file TEXT NOT NULL,
4
+ chunk_index INTEGER NOT NULL,
5
+ content TEXT NOT NULL,
6
+ embedding BLOB,
7
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
8
+ UNIQUE(source_file, chunk_index)
9
+ );
10
+
11
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
12
+ content,
13
+ source_file,
14
+ content='memory_chunks',
15
+ content_rowid='id'
16
+ );
17
+
18
+ CREATE TRIGGER IF NOT EXISTS memory_chunks_ai AFTER INSERT ON memory_chunks BEGIN
19
+ INSERT INTO memory_fts(rowid, content, source_file)
20
+ VALUES (new.id, new.content, new.source_file);
21
+ END;
22
+
23
+ CREATE TRIGGER IF NOT EXISTS memory_chunks_ad AFTER DELETE ON memory_chunks BEGIN
24
+ INSERT INTO memory_fts(memory_fts, rowid, content, source_file)
25
+ VALUES ('delete', old.id, old.content, old.source_file);
26
+ END;
27
+
28
+ CREATE TRIGGER IF NOT EXISTS memory_chunks_au AFTER UPDATE ON memory_chunks BEGIN
29
+ INSERT INTO memory_fts(memory_fts, rowid, content, source_file)
30
+ VALUES ('delete', old.id, old.content, old.source_file);
31
+ INSERT INTO memory_fts(rowid, content, source_file)
32
+ VALUES (new.id, new.content, new.source_file);
33
+ END;
@@ -0,0 +1,24 @@
1
+ CREATE TABLE IF NOT EXISTS cron_jobs (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ name TEXT NOT NULL UNIQUE,
4
+ schedule TEXT NOT NULL,
5
+ task TEXT NOT NULL,
6
+ enabled INTEGER NOT NULL DEFAULT 1,
7
+ last_run TEXT,
8
+ next_run TEXT,
9
+ retry_count INTEGER NOT NULL DEFAULT 0,
10
+ max_retries INTEGER NOT NULL DEFAULT 3,
11
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS cron_logs (
15
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
16
+ job_id INTEGER NOT NULL REFERENCES cron_jobs(id),
17
+ status TEXT NOT NULL,
18
+ output TEXT,
19
+ error TEXT,
20
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
21
+ finished_at TEXT
22
+ );
23
+
24
+ CREATE INDEX IF NOT EXISTS idx_cron_logs_job ON cron_logs(job_id);
@@ -0,0 +1,12 @@
1
+ CREATE TABLE IF NOT EXISTS usage (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ session_id TEXT,
4
+ provider TEXT NOT NULL,
5
+ model TEXT NOT NULL,
6
+ input_tokens INTEGER NOT NULL DEFAULT 0,
7
+ output_tokens INTEGER NOT NULL DEFAULT 0,
8
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
9
+ );
10
+
11
+ CREATE INDEX IF NOT EXISTS idx_usage_provider ON usage(provider);
12
+ CREATE INDEX IF NOT EXISTS idx_usage_created ON usage(created_at);
@@ -0,0 +1,8 @@
1
+ CREATE TABLE secrets (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ name TEXT NOT NULL UNIQUE,
4
+ value TEXT NOT NULL,
5
+ service TEXT,
6
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
7
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
8
+ );
@@ -0,0 +1 @@
1
+ ALTER TABLE cron_jobs ADD COLUMN agent TEXT;
@@ -0,0 +1,22 @@
1
+ CREATE TABLE IF NOT EXISTS workflow_executions (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ workflow_name TEXT NOT NULL,
4
+ input TEXT,
5
+ status TEXT NOT NULL DEFAULT 'running',
6
+ result TEXT,
7
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
8
+ completed_at TEXT
9
+ );
10
+
11
+ CREATE TABLE IF NOT EXISTS workflow_step_logs (
12
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ execution_id INTEGER NOT NULL REFERENCES workflow_executions(id),
14
+ step_name TEXT NOT NULL,
15
+ agent_name TEXT NOT NULL,
16
+ task TEXT NOT NULL,
17
+ status TEXT NOT NULL DEFAULT 'pending',
18
+ output TEXT,
19
+ started_at TEXT,
20
+ completed_at TEXT,
21
+ duration_ms INTEGER
22
+ );
@@ -0,0 +1,24 @@
1
+ CREATE TABLE IF NOT EXISTS memory_triggers (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ pattern TEXT NOT NULL,
4
+ action TEXT NOT NULL,
5
+ schedule TEXT NOT NULL DEFAULT 'once',
6
+ last_fired TEXT,
7
+ enabled INTEGER NOT NULL DEFAULT 1,
8
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
9
+ );
10
+
11
+ CREATE TABLE IF NOT EXISTS user_preferences (
12
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ key TEXT NOT NULL UNIQUE,
14
+ value TEXT NOT NULL,
15
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
16
+ );
17
+
18
+ CREATE TABLE IF NOT EXISTS proactive_log (
19
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
20
+ type TEXT NOT NULL,
21
+ message TEXT NOT NULL,
22
+ channel TEXT,
23
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
24
+ );
@@ -0,0 +1,9 @@
1
+ CREATE TABLE IF NOT EXISTS uploads (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ filename TEXT NOT NULL,
4
+ original_name TEXT NOT NULL,
5
+ mime_type TEXT NOT NULL,
6
+ size_bytes INTEGER NOT NULL,
7
+ chunk_count INTEGER NOT NULL DEFAULT 0,
8
+ uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
9
+ );
@@ -0,0 +1,22 @@
1
+ -- Enhance usage table with response time and cost
2
+ ALTER TABLE usage ADD COLUMN response_time_ms INTEGER;
3
+ ALTER TABLE usage ADD COLUMN cost_usd REAL;
4
+
5
+ -- Tool execution metrics
6
+ CREATE TABLE IF NOT EXISTS tool_metrics (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ tool_name TEXT NOT NULL,
9
+ session_id TEXT,
10
+ duration_ms INTEGER NOT NULL,
11
+ success INTEGER NOT NULL DEFAULT 1,
12
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
13
+ );
14
+
15
+ -- Performance snapshots
16
+ CREATE TABLE IF NOT EXISTS perf_snapshots (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ rss_mb REAL,
19
+ heap_mb REAL,
20
+ db_size_mb REAL,
21
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
22
+ );
@@ -0,0 +1,7 @@
1
+ CREATE TABLE IF NOT EXISTS api_keys (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ label TEXT NOT NULL DEFAULT '',
4
+ key_hash TEXT NOT NULL UNIQUE,
5
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
6
+ last_used_at TEXT
7
+ );
@@ -0,0 +1,5 @@
1
+ -- Performance indexes for dashboard analytics queries
2
+ CREATE INDEX IF NOT EXISTS idx_usage_provider_model ON usage(provider, model);
3
+ CREATE INDEX IF NOT EXISTS idx_tool_metrics_name ON tool_metrics(tool_name);
4
+ CREATE INDEX IF NOT EXISTS idx_tool_metrics_created ON tool_metrics(created_at);
5
+ CREATE INDEX IF NOT EXISTS idx_memory_chunks_source ON memory_chunks(source_file);
@@ -0,0 +1,11 @@
1
+ -- Budget configuration
2
+ CREATE TABLE IF NOT EXISTS budget_config (
3
+ id INTEGER PRIMARY KEY CHECK (id = 1), -- singleton row
4
+ daily_limit_usd REAL,
5
+ monthly_limit_usd REAL,
6
+ alert_threshold REAL DEFAULT 0.8, -- alert at 80% of budget
7
+ paused INTEGER NOT NULL DEFAULT 0, -- 1 = budget exceeded, agent paused
8
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
9
+ );
10
+
11
+ INSERT OR IGNORE INTO budget_config (id) VALUES (1);
@@ -0,0 +1,2 @@
1
+ -- Index on usage.session_id for analytics queries using COUNT(DISTINCT session_id)
2
+ CREATE INDEX IF NOT EXISTS idx_usage_session_id ON usage(session_id);