@usejarvis/brain 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 (266) hide show
  1. package/LICENSE +153 -0
  2. package/README.md +278 -0
  3. package/bin/jarvis.ts +413 -0
  4. package/package.json +74 -0
  5. package/scripts/ensure-bun.cjs +8 -0
  6. package/src/actions/README.md +421 -0
  7. package/src/actions/app-control/desktop-controller.test.ts +26 -0
  8. package/src/actions/app-control/desktop-controller.ts +438 -0
  9. package/src/actions/app-control/interface.ts +64 -0
  10. package/src/actions/app-control/linux.ts +273 -0
  11. package/src/actions/app-control/macos.ts +54 -0
  12. package/src/actions/app-control/sidecar-launcher.test.ts +23 -0
  13. package/src/actions/app-control/sidecar-launcher.ts +286 -0
  14. package/src/actions/app-control/windows.ts +44 -0
  15. package/src/actions/browser/cdp.ts +138 -0
  16. package/src/actions/browser/chrome-launcher.ts +252 -0
  17. package/src/actions/browser/session.ts +437 -0
  18. package/src/actions/browser/stealth.ts +49 -0
  19. package/src/actions/index.ts +20 -0
  20. package/src/actions/terminal/executor.ts +157 -0
  21. package/src/actions/terminal/wsl-bridge.ts +126 -0
  22. package/src/actions/test.ts +93 -0
  23. package/src/actions/tools/agents.ts +321 -0
  24. package/src/actions/tools/builtin.ts +846 -0
  25. package/src/actions/tools/commitments.ts +192 -0
  26. package/src/actions/tools/content.ts +217 -0
  27. package/src/actions/tools/delegate.ts +147 -0
  28. package/src/actions/tools/desktop.test.ts +55 -0
  29. package/src/actions/tools/desktop.ts +305 -0
  30. package/src/actions/tools/goals.ts +376 -0
  31. package/src/actions/tools/local-tools-guard.ts +20 -0
  32. package/src/actions/tools/registry.ts +171 -0
  33. package/src/actions/tools/research.ts +111 -0
  34. package/src/actions/tools/sidecar-list.ts +57 -0
  35. package/src/actions/tools/sidecar-route.ts +105 -0
  36. package/src/actions/tools/workflows.ts +216 -0
  37. package/src/agents/agent.ts +132 -0
  38. package/src/agents/delegation.ts +107 -0
  39. package/src/agents/hierarchy.ts +113 -0
  40. package/src/agents/index.ts +19 -0
  41. package/src/agents/messaging.ts +125 -0
  42. package/src/agents/orchestrator.ts +576 -0
  43. package/src/agents/role-discovery.ts +61 -0
  44. package/src/agents/sub-agent-runner.ts +307 -0
  45. package/src/agents/task-manager.ts +151 -0
  46. package/src/authority/approval-delivery.ts +59 -0
  47. package/src/authority/approval.ts +196 -0
  48. package/src/authority/audit.ts +158 -0
  49. package/src/authority/authority.test.ts +519 -0
  50. package/src/authority/deferred-executor.ts +103 -0
  51. package/src/authority/emergency.ts +66 -0
  52. package/src/authority/engine.ts +297 -0
  53. package/src/authority/index.ts +12 -0
  54. package/src/authority/learning.ts +111 -0
  55. package/src/authority/tool-action-map.ts +74 -0
  56. package/src/awareness/analytics.ts +466 -0
  57. package/src/awareness/awareness.test.ts +332 -0
  58. package/src/awareness/capture-engine.ts +305 -0
  59. package/src/awareness/context-graph.ts +130 -0
  60. package/src/awareness/context-tracker.ts +349 -0
  61. package/src/awareness/index.ts +25 -0
  62. package/src/awareness/intelligence.ts +321 -0
  63. package/src/awareness/ocr-engine.ts +88 -0
  64. package/src/awareness/service.ts +528 -0
  65. package/src/awareness/struggle-detector.ts +342 -0
  66. package/src/awareness/suggestion-engine.ts +476 -0
  67. package/src/awareness/types.ts +201 -0
  68. package/src/cli/autostart.ts +241 -0
  69. package/src/cli/deps.ts +449 -0
  70. package/src/cli/doctor.ts +230 -0
  71. package/src/cli/helpers.ts +401 -0
  72. package/src/cli/onboard.ts +580 -0
  73. package/src/comms/README.md +329 -0
  74. package/src/comms/auth-error.html +48 -0
  75. package/src/comms/channels/discord.ts +228 -0
  76. package/src/comms/channels/signal.ts +56 -0
  77. package/src/comms/channels/telegram.ts +316 -0
  78. package/src/comms/channels/whatsapp.ts +60 -0
  79. package/src/comms/channels.test.ts +173 -0
  80. package/src/comms/desktop-notify.ts +114 -0
  81. package/src/comms/example.ts +129 -0
  82. package/src/comms/index.ts +129 -0
  83. package/src/comms/streaming.ts +142 -0
  84. package/src/comms/voice.test.ts +152 -0
  85. package/src/comms/voice.ts +291 -0
  86. package/src/comms/websocket.test.ts +409 -0
  87. package/src/comms/websocket.ts +473 -0
  88. package/src/config/README.md +387 -0
  89. package/src/config/index.ts +6 -0
  90. package/src/config/loader.test.ts +137 -0
  91. package/src/config/loader.ts +142 -0
  92. package/src/config/types.ts +260 -0
  93. package/src/daemon/README.md +232 -0
  94. package/src/daemon/agent-service-interface.ts +9 -0
  95. package/src/daemon/agent-service.ts +600 -0
  96. package/src/daemon/api-routes.ts +2119 -0
  97. package/src/daemon/background-agent-service.ts +396 -0
  98. package/src/daemon/background-agent.test.ts +78 -0
  99. package/src/daemon/channel-service.ts +201 -0
  100. package/src/daemon/commitment-executor.ts +297 -0
  101. package/src/daemon/event-classifier.ts +239 -0
  102. package/src/daemon/event-coalescer.ts +123 -0
  103. package/src/daemon/event-reactor.ts +214 -0
  104. package/src/daemon/health.ts +220 -0
  105. package/src/daemon/index.ts +1004 -0
  106. package/src/daemon/llm-settings.ts +316 -0
  107. package/src/daemon/observer-service.ts +150 -0
  108. package/src/daemon/pid.ts +98 -0
  109. package/src/daemon/research-queue.ts +155 -0
  110. package/src/daemon/services.ts +175 -0
  111. package/src/daemon/ws-service.ts +788 -0
  112. package/src/goals/accountability.ts +240 -0
  113. package/src/goals/awareness-bridge.ts +185 -0
  114. package/src/goals/estimator.ts +185 -0
  115. package/src/goals/events.ts +28 -0
  116. package/src/goals/goals.test.ts +400 -0
  117. package/src/goals/integration.test.ts +329 -0
  118. package/src/goals/nl-builder.test.ts +220 -0
  119. package/src/goals/nl-builder.ts +256 -0
  120. package/src/goals/rhythm.test.ts +177 -0
  121. package/src/goals/rhythm.ts +275 -0
  122. package/src/goals/service.test.ts +135 -0
  123. package/src/goals/service.ts +348 -0
  124. package/src/goals/types.ts +106 -0
  125. package/src/goals/workflow-bridge.ts +96 -0
  126. package/src/integrations/google-api.ts +134 -0
  127. package/src/integrations/google-auth.ts +175 -0
  128. package/src/llm/README.md +291 -0
  129. package/src/llm/anthropic.ts +386 -0
  130. package/src/llm/gemini.ts +371 -0
  131. package/src/llm/index.ts +19 -0
  132. package/src/llm/manager.ts +153 -0
  133. package/src/llm/ollama.ts +307 -0
  134. package/src/llm/openai.ts +350 -0
  135. package/src/llm/provider.test.ts +231 -0
  136. package/src/llm/provider.ts +60 -0
  137. package/src/llm/test.ts +87 -0
  138. package/src/observers/README.md +278 -0
  139. package/src/observers/calendar.ts +113 -0
  140. package/src/observers/clipboard.ts +136 -0
  141. package/src/observers/email.ts +109 -0
  142. package/src/observers/example.ts +58 -0
  143. package/src/observers/file-watcher.ts +124 -0
  144. package/src/observers/index.ts +159 -0
  145. package/src/observers/notifications.ts +197 -0
  146. package/src/observers/observers.test.ts +203 -0
  147. package/src/observers/processes.ts +225 -0
  148. package/src/personality/README.md +61 -0
  149. package/src/personality/adapter.ts +196 -0
  150. package/src/personality/index.ts +20 -0
  151. package/src/personality/learner.ts +209 -0
  152. package/src/personality/model.ts +132 -0
  153. package/src/personality/personality.test.ts +236 -0
  154. package/src/roles/README.md +252 -0
  155. package/src/roles/authority.ts +119 -0
  156. package/src/roles/example-usage.ts +198 -0
  157. package/src/roles/index.ts +42 -0
  158. package/src/roles/loader.ts +143 -0
  159. package/src/roles/prompt-builder.ts +194 -0
  160. package/src/roles/test-multi.ts +102 -0
  161. package/src/roles/test-role.yaml +77 -0
  162. package/src/roles/test-utils.ts +93 -0
  163. package/src/roles/test.ts +106 -0
  164. package/src/roles/tool-guide.ts +190 -0
  165. package/src/roles/types.ts +36 -0
  166. package/src/roles/utils.ts +200 -0
  167. package/src/scripts/google-setup.ts +168 -0
  168. package/src/sidecar/connection.ts +179 -0
  169. package/src/sidecar/index.ts +6 -0
  170. package/src/sidecar/manager.ts +542 -0
  171. package/src/sidecar/protocol.ts +85 -0
  172. package/src/sidecar/rpc.ts +161 -0
  173. package/src/sidecar/scheduler.ts +136 -0
  174. package/src/sidecar/types.ts +112 -0
  175. package/src/sidecar/validator.ts +144 -0
  176. package/src/vault/README.md +110 -0
  177. package/src/vault/awareness.ts +341 -0
  178. package/src/vault/commitments.ts +299 -0
  179. package/src/vault/content-pipeline.ts +260 -0
  180. package/src/vault/conversations.ts +173 -0
  181. package/src/vault/entities.ts +180 -0
  182. package/src/vault/extractor.test.ts +356 -0
  183. package/src/vault/extractor.ts +345 -0
  184. package/src/vault/facts.ts +190 -0
  185. package/src/vault/goals.ts +477 -0
  186. package/src/vault/index.ts +87 -0
  187. package/src/vault/keychain.ts +99 -0
  188. package/src/vault/observations.ts +115 -0
  189. package/src/vault/relationships.ts +178 -0
  190. package/src/vault/retrieval.test.ts +126 -0
  191. package/src/vault/retrieval.ts +227 -0
  192. package/src/vault/schema.ts +658 -0
  193. package/src/vault/settings.ts +38 -0
  194. package/src/vault/vectors.ts +92 -0
  195. package/src/vault/workflows.ts +403 -0
  196. package/src/workflows/auto-suggest.ts +290 -0
  197. package/src/workflows/engine.ts +366 -0
  198. package/src/workflows/events.ts +24 -0
  199. package/src/workflows/executor.ts +207 -0
  200. package/src/workflows/nl-builder.ts +198 -0
  201. package/src/workflows/nodes/actions/agent-task.ts +73 -0
  202. package/src/workflows/nodes/actions/calendar-action.ts +85 -0
  203. package/src/workflows/nodes/actions/code-execution.ts +73 -0
  204. package/src/workflows/nodes/actions/discord.ts +77 -0
  205. package/src/workflows/nodes/actions/file-write.ts +73 -0
  206. package/src/workflows/nodes/actions/gmail.ts +69 -0
  207. package/src/workflows/nodes/actions/http-request.ts +117 -0
  208. package/src/workflows/nodes/actions/notification.ts +85 -0
  209. package/src/workflows/nodes/actions/run-tool.ts +55 -0
  210. package/src/workflows/nodes/actions/send-message.ts +82 -0
  211. package/src/workflows/nodes/actions/shell-command.ts +76 -0
  212. package/src/workflows/nodes/actions/telegram.ts +60 -0
  213. package/src/workflows/nodes/builtin.ts +119 -0
  214. package/src/workflows/nodes/error/error-handler.ts +37 -0
  215. package/src/workflows/nodes/error/fallback.ts +47 -0
  216. package/src/workflows/nodes/error/retry.ts +82 -0
  217. package/src/workflows/nodes/logic/delay.ts +42 -0
  218. package/src/workflows/nodes/logic/if-else.ts +41 -0
  219. package/src/workflows/nodes/logic/loop.ts +90 -0
  220. package/src/workflows/nodes/logic/merge.ts +38 -0
  221. package/src/workflows/nodes/logic/race.ts +40 -0
  222. package/src/workflows/nodes/logic/switch.ts +59 -0
  223. package/src/workflows/nodes/logic/template-render.ts +53 -0
  224. package/src/workflows/nodes/logic/variable-get.ts +37 -0
  225. package/src/workflows/nodes/logic/variable-set.ts +59 -0
  226. package/src/workflows/nodes/registry.ts +99 -0
  227. package/src/workflows/nodes/transform/aggregate.ts +99 -0
  228. package/src/workflows/nodes/transform/csv-parse.ts +70 -0
  229. package/src/workflows/nodes/transform/json-parse.ts +63 -0
  230. package/src/workflows/nodes/transform/map-filter.ts +84 -0
  231. package/src/workflows/nodes/transform/regex-match.ts +89 -0
  232. package/src/workflows/nodes/triggers/calendar.ts +33 -0
  233. package/src/workflows/nodes/triggers/clipboard.ts +32 -0
  234. package/src/workflows/nodes/triggers/cron.ts +40 -0
  235. package/src/workflows/nodes/triggers/email.ts +40 -0
  236. package/src/workflows/nodes/triggers/file-change.ts +45 -0
  237. package/src/workflows/nodes/triggers/git.ts +46 -0
  238. package/src/workflows/nodes/triggers/manual.ts +23 -0
  239. package/src/workflows/nodes/triggers/poll.ts +81 -0
  240. package/src/workflows/nodes/triggers/process.ts +44 -0
  241. package/src/workflows/nodes/triggers/screen-event.ts +37 -0
  242. package/src/workflows/nodes/triggers/webhook.ts +39 -0
  243. package/src/workflows/safe-eval.ts +139 -0
  244. package/src/workflows/template.ts +118 -0
  245. package/src/workflows/triggers/cron.ts +311 -0
  246. package/src/workflows/triggers/manager.ts +285 -0
  247. package/src/workflows/triggers/observer-bridge.ts +172 -0
  248. package/src/workflows/triggers/poller.ts +201 -0
  249. package/src/workflows/triggers/screen-condition.ts +218 -0
  250. package/src/workflows/triggers/triggers.test.ts +740 -0
  251. package/src/workflows/triggers/webhook.ts +191 -0
  252. package/src/workflows/types.ts +133 -0
  253. package/src/workflows/variables.ts +72 -0
  254. package/src/workflows/workflows.test.ts +383 -0
  255. package/src/workflows/yaml.ts +104 -0
  256. package/ui/dist/index-j75njzc1.css +1199 -0
  257. package/ui/dist/index-p2zh407q.js +80603 -0
  258. package/ui/dist/index.html +13 -0
  259. package/ui/public/openwakeword/models/embedding_model.onnx +0 -0
  260. package/ui/public/openwakeword/models/hey_jarvis_v0.1.onnx +0 -0
  261. package/ui/public/openwakeword/models/melspectrogram.onnx +0 -0
  262. package/ui/public/openwakeword/models/silero_vad.onnx +0 -0
  263. package/ui/public/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
  264. package/ui/public/ort/ort-wasm-simd-threaded.jsep.wasm +0 -0
  265. package/ui/public/ort/ort-wasm-simd-threaded.mjs +59 -0
  266. package/ui/public/ort/ort-wasm-simd-threaded.wasm +0 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Google OAuth2 Authentication
3
+ *
4
+ * Manages OAuth2 tokens for Google APIs (Gmail, Calendar).
5
+ * Uses raw fetch() — no googleapis package needed.
6
+ * Tokens stored at ~/.jarvis/google-tokens.json
7
+ */
8
+
9
+ import path from 'node:path';
10
+ import os from 'node:os';
11
+ import { readFileSync, existsSync } from 'node:fs';
12
+
13
+ const TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token';
14
+ const AUTH_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth';
15
+
16
+ export type GoogleTokens = {
17
+ access_token: string;
18
+ refresh_token: string;
19
+ expiry_date: number;
20
+ token_type: string;
21
+ };
22
+
23
+ export class GoogleAuth {
24
+ private clientId: string;
25
+ private clientSecret: string;
26
+ private tokens: GoogleTokens | null = null;
27
+ private tokensPath: string;
28
+ private redirectUri: string;
29
+
30
+ constructor(
31
+ clientId: string,
32
+ clientSecret: string,
33
+ opts?: { tokensPath?: string; redirectUri?: string }
34
+ ) {
35
+ this.clientId = clientId;
36
+ this.clientSecret = clientSecret;
37
+ this.tokensPath = opts?.tokensPath ?? path.join(os.homedir(), '.jarvis', 'google-tokens.json');
38
+ this.redirectUri = opts?.redirectUri ?? 'http://localhost:3142/api/auth/google/callback';
39
+ this.loadTokens();
40
+ }
41
+
42
+ /**
43
+ * Load saved tokens from disk.
44
+ */
45
+ loadTokens(): GoogleTokens | null {
46
+ try {
47
+ if (!existsSync(this.tokensPath)) return null;
48
+ const text = readFileSync(this.tokensPath, 'utf-8');
49
+ const data = JSON.parse(text);
50
+ if (data.access_token && data.refresh_token) {
51
+ this.tokens = data as GoogleTokens;
52
+ return this.tokens;
53
+ }
54
+ } catch {
55
+ // No tokens file or invalid
56
+ }
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * Save tokens to disk.
62
+ */
63
+ async saveTokens(tokens: GoogleTokens): Promise<void> {
64
+ this.tokens = tokens;
65
+ await Bun.write(this.tokensPath, JSON.stringify(tokens, null, 2));
66
+ }
67
+
68
+ /**
69
+ * Check if we have valid tokens.
70
+ */
71
+ isAuthenticated(): boolean {
72
+ return this.tokens !== null && !!this.tokens.refresh_token;
73
+ }
74
+
75
+ /**
76
+ * Get a valid access token. Auto-refreshes if expired.
77
+ */
78
+ async getAccessToken(): Promise<string> {
79
+ if (!this.tokens) {
80
+ throw new Error('Not authenticated. Run: bun run src/scripts/google-setup.ts');
81
+ }
82
+
83
+ // Check if token is expired (with 5 min buffer)
84
+ if (this.tokens.expiry_date && Date.now() > this.tokens.expiry_date - 5 * 60_000) {
85
+ await this.refreshAccessToken();
86
+ }
87
+
88
+ return this.tokens.access_token;
89
+ }
90
+
91
+ /**
92
+ * Generate OAuth2 consent URL.
93
+ */
94
+ getAuthUrl(scopes: string[]): string {
95
+ const params = new URLSearchParams({
96
+ client_id: this.clientId,
97
+ redirect_uri: this.redirectUri,
98
+ response_type: 'code',
99
+ scope: scopes.join(' '),
100
+ access_type: 'offline',
101
+ prompt: 'consent',
102
+ });
103
+ return `${AUTH_ENDPOINT}?${params.toString()}`;
104
+ }
105
+
106
+ /**
107
+ * Exchange authorization code for tokens.
108
+ */
109
+ async exchangeCode(code: string): Promise<GoogleTokens> {
110
+ const resp = await fetch(TOKEN_ENDPOINT, {
111
+ method: 'POST',
112
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
113
+ body: new URLSearchParams({
114
+ code,
115
+ client_id: this.clientId,
116
+ client_secret: this.clientSecret,
117
+ redirect_uri: this.redirectUri,
118
+ grant_type: 'authorization_code',
119
+ }),
120
+ });
121
+
122
+ if (!resp.ok) {
123
+ const err = await resp.text();
124
+ throw new Error(`Token exchange failed: ${err}`);
125
+ }
126
+
127
+ const data = await resp.json() as any;
128
+
129
+ const tokens: GoogleTokens = {
130
+ access_token: data.access_token,
131
+ refresh_token: data.refresh_token,
132
+ expiry_date: Date.now() + (data.expires_in ?? 3600) * 1000,
133
+ token_type: data.token_type ?? 'Bearer',
134
+ };
135
+
136
+ await this.saveTokens(tokens);
137
+ return tokens;
138
+ }
139
+
140
+ /**
141
+ * Refresh the access token using the refresh token.
142
+ */
143
+ private async refreshAccessToken(): Promise<void> {
144
+ if (!this.tokens?.refresh_token) {
145
+ throw new Error('No refresh token available');
146
+ }
147
+
148
+ const resp = await fetch(TOKEN_ENDPOINT, {
149
+ method: 'POST',
150
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
151
+ body: new URLSearchParams({
152
+ refresh_token: this.tokens.refresh_token,
153
+ client_id: this.clientId,
154
+ client_secret: this.clientSecret,
155
+ grant_type: 'refresh_token',
156
+ }),
157
+ });
158
+
159
+ if (!resp.ok) {
160
+ const err = await resp.text();
161
+ throw new Error(`Token refresh failed: ${err}`);
162
+ }
163
+
164
+ const data = await resp.json() as any;
165
+
166
+ this.tokens = {
167
+ ...this.tokens,
168
+ access_token: data.access_token,
169
+ expiry_date: Date.now() + (data.expires_in ?? 3600) * 1000,
170
+ };
171
+
172
+ await this.saveTokens(this.tokens);
173
+ console.log('[GoogleAuth] Token refreshed successfully');
174
+ }
175
+ }
@@ -0,0 +1,291 @@
1
+ # J.A.R.V.I.S. LLM Provider System
2
+
3
+ A unified abstraction layer for multiple LLM providers with automatic fallback support.
4
+
5
+ ## Features
6
+
7
+ - **Unified Interface**: Single API for Anthropic Claude, OpenAI GPT, and Ollama (local models)
8
+ - **Automatic Fallback**: Seamlessly switches to backup providers if primary fails
9
+ - **Streaming Support**: Real-time response streaming for all providers
10
+ - **Tool Calling**: Unified tool/function calling across providers
11
+ - **Zero Dependencies**: Uses native `fetch` API (no SDKs required)
12
+ - **Type-Safe**: Full TypeScript types for all operations
13
+
14
+ ## Supported Providers
15
+
16
+ ### 1. Anthropic Claude
17
+ - **Models**: Claude Opus 4.6, Sonnet 4.5, Claude 3.5, Claude 3
18
+ - **Default**: `claude-sonnet-4-5-20250929`
19
+ - **Features**: Text generation, streaming, tool use
20
+ - **API**: https://api.anthropic.com/v1/messages
21
+
22
+ ### 2. OpenAI GPT
23
+ - **Models**: GPT-4o, GPT-4 Turbo, GPT-3.5 Turbo
24
+ - **Default**: `gpt-4o`
25
+ - **Features**: Text generation, streaming, function calling
26
+ - **API**: https://api.openai.com/v1/chat/completions
27
+
28
+ ### 3. Ollama (Local)
29
+ - **Models**: Llama 3, Llama 2, Mistral, Mixtral, CodeLlama, etc.
30
+ - **Default**: `llama3`
31
+ - **Features**: Local inference, streaming, tool use
32
+ - **API**: http://localhost:11434/api/chat
33
+
34
+ ## Usage
35
+
36
+ ### Basic Setup
37
+
38
+ ```typescript
39
+ import { LLMManager, AnthropicProvider, OpenAIProvider, OllamaProvider } from './llm/index.ts';
40
+
41
+ const manager = new LLMManager();
42
+
43
+ // Register providers
44
+ const anthropic = new AnthropicProvider('sk-ant-...', 'claude-sonnet-4-5-20250929');
45
+ manager.registerProvider(anthropic);
46
+
47
+ const openai = new OpenAIProvider('sk-...', 'gpt-4o');
48
+ manager.registerProvider(openai);
49
+
50
+ const ollama = new OllamaProvider('http://localhost:11434', 'llama3');
51
+ manager.registerProvider(ollama);
52
+
53
+ // Set primary and fallbacks
54
+ manager.setPrimary('anthropic');
55
+ manager.setFallbackChain(['openai', 'ollama']);
56
+ ```
57
+
58
+ ### Simple Chat
59
+
60
+ ```typescript
61
+ const messages = [
62
+ { role: 'system', content: 'You are a helpful assistant.' },
63
+ { role: 'user', content: 'What is TypeScript?' },
64
+ ];
65
+
66
+ const response = await manager.chat(messages);
67
+ console.log(response.content);
68
+ console.log('Tokens:', response.usage);
69
+ console.log('Model:', response.model);
70
+ ```
71
+
72
+ ### Streaming Responses
73
+
74
+ ```typescript
75
+ for await (const event of manager.stream(messages)) {
76
+ if (event.type === 'text') {
77
+ process.stdout.write(event.text);
78
+ } else if (event.type === 'done') {
79
+ console.log('\nCompleted!');
80
+ console.log('Total tokens:', event.response.usage);
81
+ } else if (event.type === 'error') {
82
+ console.error('Error:', event.error);
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Tool Calling
88
+
89
+ ```typescript
90
+ const tools = [
91
+ {
92
+ name: 'get_weather',
93
+ description: 'Get the current weather in a location',
94
+ parameters: {
95
+ type: 'object',
96
+ properties: {
97
+ location: { type: 'string', description: 'City name' },
98
+ unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
99
+ },
100
+ required: ['location'],
101
+ },
102
+ },
103
+ ];
104
+
105
+ const messages = [
106
+ { role: 'user', content: 'What is the weather in Paris?' },
107
+ ];
108
+
109
+ const response = await manager.chat(messages, { tools });
110
+
111
+ if (response.tool_calls.length > 0) {
112
+ for (const call of response.tool_calls) {
113
+ console.log('Tool:', call.name);
114
+ console.log('Arguments:', call.arguments);
115
+ // Execute tool and continue conversation...
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Advanced Options
121
+
122
+ ```typescript
123
+ const response = await manager.chat(messages, {
124
+ model: 'claude-opus-4-6', // Override default model
125
+ temperature: 0.7, // Control randomness (0-1)
126
+ max_tokens: 2000, // Limit response length
127
+ tools: [...], // Tool definitions
128
+ });
129
+ ```
130
+
131
+ ## Configuration
132
+
133
+ Load from `~/.jarvis/config.yaml`:
134
+
135
+ ```typescript
136
+ import { loadConfig } from './config/index.ts';
137
+
138
+ const config = await loadConfig();
139
+
140
+ // Auto-configure providers from config
141
+ if (config.llm.anthropic?.api_key) {
142
+ const anthropic = new AnthropicProvider(
143
+ config.llm.anthropic.api_key,
144
+ config.llm.anthropic.model
145
+ );
146
+ manager.registerProvider(anthropic);
147
+ }
148
+
149
+ manager.setPrimary(config.llm.primary);
150
+ manager.setFallbackChain(config.llm.fallback);
151
+ ```
152
+
153
+ ## Provider-Specific Usage
154
+
155
+ ### Direct Provider Access
156
+
157
+ ```typescript
158
+ const provider = manager.getProvider('anthropic');
159
+ if (provider) {
160
+ const response = await provider.chat(messages);
161
+ }
162
+ ```
163
+
164
+ ### List Available Models
165
+
166
+ ```typescript
167
+ const anthropic = new AnthropicProvider('sk-ant-...');
168
+ const models = await anthropic.listModels();
169
+ console.log('Available models:', models);
170
+ ```
171
+
172
+ ## Response Types
173
+
174
+ ### LLMResponse
175
+
176
+ ```typescript
177
+ {
178
+ content: string; // Generated text
179
+ tool_calls: LLMToolCall[]; // Tool calls made (if any)
180
+ usage: {
181
+ input_tokens: number; // Prompt tokens
182
+ output_tokens: number; // Completion tokens
183
+ };
184
+ model: string; // Model used
185
+ finish_reason: 'stop' | 'tool_use' | 'length' | 'error';
186
+ }
187
+ ```
188
+
189
+ ### LLMStreamEvent
190
+
191
+ ```typescript
192
+ // Text delta
193
+ { type: 'text'; text: string }
194
+
195
+ // Tool call completed
196
+ { type: 'tool_call'; tool_call: LLMToolCall }
197
+
198
+ // Stream finished
199
+ { type: 'done'; response: LLMResponse }
200
+
201
+ // Error occurred
202
+ { type: 'error'; error: string }
203
+ ```
204
+
205
+ ## Error Handling
206
+
207
+ The manager automatically tries fallback providers on failure:
208
+
209
+ ```typescript
210
+ try {
211
+ const response = await manager.chat(messages);
212
+ } catch (err) {
213
+ // All providers failed
214
+ console.error('All LLM providers failed:', err.message);
215
+ }
216
+ ```
217
+
218
+ Individual provider errors are logged but don't throw unless all providers fail.
219
+
220
+ ## Testing
221
+
222
+ Run the test file:
223
+
224
+ ```bash
225
+ bun run src/llm/test.ts
226
+ ```
227
+
228
+ Make sure you have a valid config at `~/.jarvis/config.yaml` with API keys set.
229
+
230
+ ## Implementation Details
231
+
232
+ ### Message Conversion
233
+
234
+ Each provider has different message formats:
235
+ - **Anthropic**: Separates system message, converts roles
236
+ - **OpenAI**: Standard chat format
237
+ - **Ollama**: Chat API format
238
+
239
+ The abstraction layer handles all conversions automatically.
240
+
241
+ ### Tool Calling Formats
242
+
243
+ - **Anthropic**: `tools` array with `input_schema`
244
+ - **OpenAI**: `tools` array with `function` wrapper
245
+ - **Ollama**: OpenAI-compatible function calling
246
+
247
+ All converted to unified `LLMToolCall` format in responses.
248
+
249
+ ### Streaming Implementation
250
+
251
+ - **Anthropic**: Server-Sent Events (SSE) with `data: {...}` format
252
+ - **OpenAI**: SSE with delta chunks
253
+ - **Ollama**: Newline-delimited JSON streaming
254
+
255
+ All providers return the same `LLMStreamEvent` types.
256
+
257
+ ## Architecture
258
+
259
+ ```
260
+ LLMManager
261
+ ├── AnthropicProvider (implements LLMProvider)
262
+ ├── OpenAIProvider (implements LLMProvider)
263
+ └── OllamaProvider (implements LLMProvider)
264
+
265
+ Each provider:
266
+ - Implements chat() for single responses
267
+ - Implements stream() for streaming responses
268
+ - Implements listModels() for available models
269
+ - Handles provider-specific API formats
270
+ - Converts to/from unified types
271
+ ```
272
+
273
+ ## Best Practices
274
+
275
+ 1. **Always use LLMManager**: Don't instantiate providers directly unless needed
276
+ 2. **Set up fallbacks**: Configure multiple providers for reliability
277
+ 3. **Handle stream errors**: Check for `error` events in stream loops
278
+ 4. **Use config system**: Load API keys from config, never hardcode
279
+ 5. **Monitor usage**: Track token usage from responses for cost control
280
+ 6. **Test locally first**: Use Ollama for development before hitting paid APIs
281
+
282
+ ## Future Enhancements
283
+
284
+ - [ ] Response caching
285
+ - [ ] Rate limiting
286
+ - [ ] Usage tracking/analytics
287
+ - [ ] Model capability detection
288
+ - [ ] Automatic model selection based on task
289
+ - [ ] Retry with exponential backoff
290
+ - [ ] Cost estimation per request
291
+ - [ ] Multi-turn conversation helpers